Skip to content

Commit

Permalink
fix: able to start prod w wevsocket sources
Browse files Browse the repository at this point in the history
  • Loading branch information
malmen237 committed Sep 11, 2024
1 parent a5007ac commit 474bcb8
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 95 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ MONGODB_URI=${MONGODB_URI:-mongodb://api:password@localhost:27017/live-gui}
# Ateliere Live System Controlleer
LIVE_URL=${LIVE_URL:-https://localhost:8080}
LIVE_CREDENTIALS=${LIVE_CREDENTIALS:-admin:admin}
CONTROL_PANEL_WS==${}
# This ENV variable disables SSL Verification, use if the above LIVE_URL doesn't have a proper certificate
NODE_TLS_REJECT_UNAUTHORIZED=${NODE_TLS_REJECT_UNAUTHORIZED:-1}

Expand Down
2 changes: 1 addition & 1 deletion src/api/ateliereLive/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import WebSocket from 'ws';

function createWebSocket(): Promise<WebSocket> {
return new Promise((resolve, reject) => {
const ws = new WebSocket(`ws://${process.env.AGILE_WEBSOCKET}`);
const ws = new WebSocket(`ws://${process.env.CONTROL_PANEL_WS}`);
ws.on('error', reject);
ws.on('open', () => {
// const send = ws.send.bind(ws);
Expand Down
28 changes: 25 additions & 3 deletions src/api/manager/productions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Db, ObjectId, UpdateResult } from 'mongodb';
import { getDatabase } from '../mongoClient/dbClient';
import { Production, ProductionWithId } from '../../interfaces/production';
import { Log } from '../logger';
import { SourceReference, Type } from '../../interfaces/Source';

export async function getProductions(): Promise<Production[]> {
const db = await getDatabase();
Expand All @@ -29,21 +28,44 @@ export async function setProductionsIsActiveFalse(): Promise<
export async function putProduction(
id: string,
production: Production
): Promise<void> {
): Promise<Production> {
const db = await getDatabase();
const newSourceId = new ObjectId().toString();

const sources = production.sources
? production.sources.flatMap((singleSource) => {
return singleSource._id
? singleSource
: {
_id: newSourceId,
type: singleSource.type,
label: singleSource.label,
input_slot: singleSource.input_slot
};
})
: [];

await db.collection('productions').findOneAndReplace(
{ _id: new ObjectId(id) },
{
name: production.name,
isActive: production.isActive,
sources: production.sources,
sources: sources,
production_settings: production.production_settings
}
);

if (!production.isActive) {
deleteMonitoring(db, id);
}

return {
_id: new ObjectId(id).toString(),
name: production.name,
isActive: production.isActive,
sources: sources,
production_settings: production.production_settings
};
}

export async function postProduction(data: Production): Promise<ObjectId> {
Expand Down
6 changes: 4 additions & 2 deletions src/api/manager/sources.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import inventory from './mocks/inventory.json';
import { Source } from '../../interfaces/Source';
import { ObjectId, OptionalId } from 'mongodb';
import { ObjectId, OptionalId, WithId } from 'mongodb';
import { getDatabase } from '../mongoClient/dbClient';

export function getMockedSources() {
Expand All @@ -21,7 +21,9 @@ export async function getSources() {
const db = await getDatabase();
return await db.collection<Source>('inventory').find().toArray();
}
export async function getSourcesByIds(_ids: string[]) {
export async function getSourcesByIds(
_ids: string[]
): Promise<WithId<Source>[]> {
const db = await getDatabase().catch(() => {
throw new Error("Can't connect to Database");
});
Expand Down
31 changes: 10 additions & 21 deletions src/api/manager/workflow.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SourceWithId } from './../../interfaces/Source';
import {
Production,
ProductionSettings,
Expand Down Expand Up @@ -35,7 +36,7 @@ import {
ResourcesSenderNetworkEndpoint
} from '../../../types/ateliere-live';
import { getSourcesByIds } from './sources';
import { SourceWithId, SourceToPipelineStream } from '../../interfaces/Source';
import { SourceToPipelineStream } from '../../interfaces/Source';
import {
getAvailablePortsForIngest,
getCurrentlyUsedPorts,
Expand Down Expand Up @@ -77,10 +78,6 @@ async function connectIngestSources(
let input_slot = 0;
const sourceToPipelineStreams: SourceToPipelineStream[] = [];

console.log('connectIngestSources - productionSettings', productionSettings);
console.log('connectIngestSources - sources', sources);
console.log('connectIngestSources - usedPorts', usedPorts);

for (const source of sources) {
input_slot = input_slot + 1;
const ingestUuid = await getUuidFromIngestName(
Expand Down Expand Up @@ -464,8 +461,6 @@ export async function startProduction(

await initDedicatedPorts();

console.log('startProduction - production', production);

let streams: SourceToPipelineStream[] = [];
// Try to setup streams from ingest(s) to pipeline(s) start
try {
Expand All @@ -478,19 +473,21 @@ export async function startProduction(
const mediaPlayerSources = production.sources.filter(
(source) => source.type === 'mediaplayer'
);

htmlSources.map((source) => controlPanelWS.createHtml(source.input_slot));
mediaPlayerSources.map((source) =>
controlPanelWS.createMediaplayer(source.input_slot)
);

controlPanelWS.close();

// Nedan behöver göras efter att vi har skapat en produktion
// TODO: Hämta production.sources, för varje html-reference --> create i createHtmlWebSocket, för varje mediaplayer i production.sources skapa en createWebSocket
const sources = await getSourcesByIds(
production.sources
.filter((source) => source._id !== undefined)
.filter(
(source) =>
source._id !== undefined && source.type === 'ingest_source'
)
.map((source) => {
return source._id!.toString();
})
Expand All @@ -502,27 +499,23 @@ export async function startProduction(
throw "Can't get source!";
});

console.log('startProduction - production', production);
// Lookup pipeline UUIDs from pipeline names and insert to production_settings
await insertPipelineUuid(production_settings).catch((error) => {
throw error;
});

console.log('startProduction - production', production);
// Fetch expanded pipeline objects from Ateliere Live
const pipelinesToUsePromises = production_settings.pipelines.map(
(pipeline) => {
return getPipeline(pipeline.pipeline_id!);
}
);
const pipelinesToUse = await Promise.all(pipelinesToUsePromises);
console.log('startProduction - pipelinesToUse', pipelinesToUse);

// Check if pipelines are already in use by another production
const hasAlreadyUsedPipeline = pipelinesToUse.filter((pipeline) =>
isUsed(pipeline)
);
console.log('startProduction - production', production);

if (hasAlreadyUsedPipeline.length > 0) {
Log().error(
Expand All @@ -534,7 +527,6 @@ export async function startProduction(
(p) => p.name
)}`;
}
console.log('startProduction - hasAlreadyUsedPipeline', hasAlreadyUsedPipeline);

const resetPipelinePromises = production_settings.pipelines.map(
(pipeline) => {
Expand All @@ -544,7 +536,6 @@ export async function startProduction(
await Promise.all(resetPipelinePromises).catch((error) => {
throw `Failed to reset pipelines: ${error}`;
});
console.log('startProduction - resetPipelinePromises', resetPipelinePromises);

// Fetch all control panels from Ateliere Live
const allControlPanels = await getControlPanels();
Expand All @@ -558,7 +549,6 @@ export async function startProduction(
const hasAlreadyUsedControlPanel = controlPanelsToUse.filter(
(controlPanel) => controlPanel.outgoing_connections.length > 0
);
console.log('startProduction - hasAlreadyUsedControlPanel', hasAlreadyUsedControlPanel);

if (hasAlreadyUsedControlPanel.length > 0) {
Log().error(
Expand All @@ -585,7 +575,6 @@ export async function startProduction(
return pipeline.uuid;
})
);
console.log('startProduction - stopPipelines', production_settings.pipelines);

streams = await connectIngestSources(
production_settings,
Expand All @@ -610,7 +599,6 @@ export async function startProduction(
error: 'Could not setup streams: Unexpected error occured'
};
}
console.log('startProduction - streams', streams);
return {
ok: false,
value: [
Expand Down Expand Up @@ -774,8 +762,9 @@ export async function startProduction(
);
return {
...source,
stream_uuids: streamsForSource?.map((s) => s.stream_uuid),
input_slot: streamsForSource[0].input_slot
stream_uuids:
streamsForSource?.map((s) => s.stream_uuid) || undefined,
input_slot: source.input_slot
};
}),
isActive: true
Expand Down
92 changes: 33 additions & 59 deletions src/app/production/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use client';

import React, { useEffect, useState, KeyboardEvent } from 'react';
import { PageProps } from '../../../../.next/types/app/production/[id]/page';
import SourceListItem from '../../../components/sourceListItem/SourceListItem';
Expand Down Expand Up @@ -42,7 +43,8 @@ import { useDeleteStream, useCreateStream } from '../../../hooks/streams';
import { MonitoringButton } from '../../../components/button/MonitoringButton';
import { useGetMultiviewPreset } from '../../../hooks/multiviewPreset';
import { useMultiviews } from '../../../hooks/multiviews';
import { v4 as uuidv4 } from 'uuid';
import { useAddSource } from '../../../hooks/sources/useAddSource';
import { useGetFirstEmptySlot } from '../../../hooks/useGetFirstEmptySlot';

export default function ProductionConfiguration({ params }: PageProps) {
const t = useTranslate();
Expand Down Expand Up @@ -91,32 +93,31 @@ export default function ProductionConfiguration({ params }: PageProps) {
const [deleteSourceStatus, setDeleteSourceStatus] =
useState<DeleteSourceStatus>();

// Create source
const [firstEmptySlot] = useGetFirstEmptySlot();
const [addSource] = useAddSource();

useEffect(() => {
refreshPipelines();
refreshControlPanels();
}, [productionSetup?.isActive]);

// TODO: Väldigt lik den för ingest_source --> ändra??
const addSourceToProduction = (type: Type) => {
const newSource: SourceReference = {
_id: uuidv4(),
const input: SourceReference = {
type: type,
label: type === 'html' ? 'HTML Input' : 'Media Player Source',
input_slot: getFirstEmptySlot()
input_slot: firstEmptySlot(productionSetup)
};
setSourceReferenceToAdd(newSource);

if (productionSetup) {
const updatedSetup = addSetupItem(newSource, productionSetup);
if (!productionSetup) return;
addSource(input, productionSetup).then((updatedSetup) => {
if (!updatedSetup) return;
setSourceReferenceToAdd(updatedSetup.sources[0]);
setProductionSetup(updatedSetup);
putProduction(updatedSetup._id.toString(), updatedSetup).then(() => {
refreshProduction();
setAddSourceModal(false);
setSourceReferenceToAdd(undefined);
});
setAddSourceStatus(undefined);
}
refreshProduction();
setAddSourceModal(false);
setSelectedSource(undefined);
});
setAddSourceStatus(undefined);
};

const setSelectedControlPanel = (controlPanel: string[]) => {
Expand Down Expand Up @@ -388,6 +389,8 @@ export default function ProductionConfiguration({ params }: PageProps) {
</li>
);
}

// Adding source to a production, both in setup-mode and in live-mode
function getSourcesToDisplay(
filteredSources: Map<string, SourceWithId>
): React.ReactNode[] {
Expand All @@ -402,52 +405,25 @@ export default function ProductionConfiguration({ params }: PageProps) {
setSelectedSource(source);
setAddSourceModal(true);
} else if (productionSetup) {
const updatedSetup = addSetupItem(
{
_id: source._id.toString(),
type: 'ingest_source',
label: source.ingest_source_name,
input_slot: getFirstEmptySlot()
},
productionSetup
);
if (!updatedSetup) return;
setProductionSetup(updatedSetup);
putProduction(updatedSetup._id.toString(), updatedSetup).then(
() => {
setAddSourceModal(false);
setSelectedSource(undefined);
}
);
const input: SourceReference = {
_id: source._id.toString(),
type: 'ingest_source',
label: source.ingest_source_name,
input_slot: firstEmptySlot(productionSetup)
};
addSource(input, productionSetup).then((updatedSetup) => {
if (!updatedSetup) return;
setProductionSetup(updatedSetup);
setAddSourceModal(false);
setSelectedSource(undefined);
});
}
}}
/>
);
});
}

const getFirstEmptySlot = () => {
if (!productionSetup) throw 'no_production';
let firstEmptySlot = productionSetup.sources.length + 1;
if (productionSetup.sources.length === 0) {
return firstEmptySlot;
}
for (
let i = 0;
i <
productionSetup.sources[productionSetup.sources.length - 1].input_slot;
i++
) {
if (
!productionSetup.sources.some((source) => source.input_slot === i + 1)
) {
firstEmptySlot = i + 1;
break;
}
}
return firstEmptySlot;
};

const handleAddSource = async () => {
setAddSourceStatus(undefined);
if (
Expand All @@ -462,11 +438,10 @@ export default function ProductionConfiguration({ params }: PageProps) {
)
: false)
) {
const firstEmptySlot = getFirstEmptySlot();
const result = await createStream(
selectedSource,
productionSetup,
firstEmptySlot ? firstEmptySlot : productionSetup.sources.length + 1
firstEmptySlot(productionSetup)
);
if (!result.ok) {
if (!result.value) {
Expand All @@ -488,7 +463,7 @@ export default function ProductionConfiguration({ params }: PageProps) {
type: 'ingest_source',
label: selectedSource.name,
stream_uuids: result.value.streams.map((r) => r.stream_uuid),
input_slot: getFirstEmptySlot()
input_slot: firstEmptySlot(productionSetup)
};
const updatedSetup = addSetupItem(sourceToAdd, productionSetup);
if (!updatedSetup) return;
Expand All @@ -506,7 +481,6 @@ export default function ProductionConfiguration({ params }: PageProps) {
}
};

// TODO: HTML och MEDIA PLAYER KÄLLOR TAS INTE BORT
const handleRemoveSource = async () => {
if (
productionSetup &&
Expand Down
Loading

0 comments on commit 474bcb8

Please sign in to comment.