From 80116ace19cb8303f35c206c8774860da20cadd9 Mon Sep 17 00:00:00 2001
From: lucasmaupin
Date: Fri, 1 Nov 2024 14:33:29 +0100
Subject: [PATCH] chore: rebase-main
---
next.config.js | 1 +
.../pipelines/multiviews/multiviews.ts | 35 +-
src/api/ateliereLive/pipelines/pipelines.ts | 33 +-
.../renderingengine/renderingengine.ts | 52 +-
.../ateliereLive/pipelines/streams/streams.ts | 30 +-
src/api/manager/job/syncMonitoring.ts | 15 +-
src/api/manager/productions.ts | 38 +-
src/api/manager/workflow.ts | 233 ++-
src/app/api/manager/productions/[id]/route.ts | 5 +-
.../manager/rendering-engine/html/route.ts | 5 +-
.../manager/rendering-engine/media/route.ts | 5 +-
src/app/api/manager/streams/route.ts | 5 +-
src/app/production/[id]/page.tsx | 1245 +----------------
.../createProduction/CreateProduction.tsx | 41 +-
src/components/dragElement/DragItem.tsx | 17 +-
.../dropDown/ControlPanelDropDown.tsx | 37 +-
src/components/dropDown/DropDown.tsx | 64 +-
.../dropDown/PipelineNameDropDown.tsx | 56 +-
.../layout/DefaultLayout.module.scss | 8 +
src/components/layout/DefaultLayout.tsx | 5 +-
.../modal/ConfigureAlignmentLatencyModal.tsx | 116 +-
.../ConfigureMultiviewButton.tsx | 61 -
.../ConfigureMultiviewModal.tsx | 359 -----
.../ConfigureOutputModal.tsx | 6 +-
.../modal/configureOutputModal/Input.tsx | 2 +-
.../modal/configureOutputModal/Options.tsx | 2 +-
.../PipelineOutputConfig.tsx | 6 +-
.../configureOutputModal/StreamAccordion.tsx | 6 +-
.../Checkbox.tsx | 2 +-
.../MultiviewLayout.tsx | 6 +-
.../MultiviewLayoutSetup.tsx} | 98 +-
.../MultiviewLayoutSetupButton.tsx | 52 +
.../MultiviewSettings.tsx | 19 +-
.../RemoveLayoutButton.tsx | 12 +-
src/components/pipeline/Pipelines.tsx | 12 +-
src/components/production/ProductionPage.tsx | 140 ++
.../ProductionControlConnections.tsx | 104 ++
.../production/header/ProductionHeader.tsx | 70 +
.../monitoring/ProductionMonitoring.tsx | 28 +
.../multiviews/ProductionMultiviews.tsx | 300 ++++
.../outputs/ProductionOutputEdit.tsx | 202 +++
.../production/outputs/ProductionOutputs.tsx | 37 +
.../outputs/ProductionOutputsCard.tsx | 125 ++
.../pipelines/ProductionPipelineCard.tsx | 105 ++
.../pipelines/ProductionPipelines.tsx | 46 +
.../sources/ProductionSourceList.tsx | 93 ++
.../production/sources/ProductionSources.tsx | 688 +++++++++
.../productionsList/ProductionsListItem.tsx | 69 +-
src/components/section/Section.tsx | 37 +
src/components/sourceCard/SourceCard.tsx | 25 +-
src/components/sourceCards/SourceCards.tsx | 52 +-
src/components/sourceList/SourceList.tsx | 2 +-
.../sourceListItem/SourceListItem.tsx | 10 +-
.../startProduction/StartProductionButton.tsx | 76 +-
.../startProduction/presetDropdown.tsx | 81 +-
src/hooks/items/addSetupItem.ts | 20 +-
src/hooks/items/removeSetupItem.ts | 36 -
src/hooks/items/updateSetupItem.ts | 8 +-
src/hooks/multiviewLayout.ts | 2 +-
src/hooks/multiviews.ts | 7 +-
src/hooks/productions.ts | 16 +-
.../renderingEngine/useCreateHtmlSource.tsx | 7 +-
.../renderingEngine/useCreateMediaSource.tsx | 7 +-
.../renderingEngine/useDeleteHtmlSource.tsx | 16 +-
.../renderingEngine/useDeleteMediaSource.tsx | 17 +-
src/hooks/streams.ts | 26 +-
src/hooks/useCheckProductionPipelines.tsx | 15 +-
src/hooks/useCreateInputArray.tsx | 10 +-
src/hooks/useGetFirstEmptySlot.ts | 36 +-
...pdateSourceInputSlotOnMultiviewLayouts.tsx | 36 +-
src/hooks/utils/useEffectNotOnMount.tsx | 17 +
src/interfaces/controlConnections.ts | 2 +-
src/interfaces/pipeline.ts | 7 +-
src/interfaces/production.ts | 14 +-
74 files changed, 2822 insertions(+), 2456 deletions(-)
create mode 100644 src/components/layout/DefaultLayout.module.scss
delete mode 100644 src/components/modal/configureMultiviewModal/ConfigureMultiviewButton.tsx
delete mode 100644 src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx
rename src/components/modal/{configureMultiviewModal/MultiviewLayoutSettings => multiviewLayoutSetup}/Checkbox.tsx (90%)
rename src/components/modal/{configureMultiviewModal/MultiviewLayoutSettings => multiviewLayoutSetup}/MultiviewLayout.tsx (90%)
rename src/components/modal/{configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx => multiviewLayoutSetup/MultiviewLayoutSetup.tsx} (77%)
create mode 100644 src/components/modal/multiviewLayoutSetup/MultiviewLayoutSetupButton.tsx
rename src/components/modal/{configureMultiviewModal => multiviewLayoutSetup}/MultiviewSettings.tsx (94%)
rename src/components/modal/{configureMultiviewModal/MultiviewLayoutSettings => multiviewLayoutSetup}/RemoveLayoutButton.tsx (70%)
create mode 100644 src/components/production/ProductionPage.tsx
create mode 100644 src/components/production/controlConnections/ProductionControlConnections.tsx
create mode 100644 src/components/production/header/ProductionHeader.tsx
create mode 100644 src/components/production/monitoring/ProductionMonitoring.tsx
create mode 100644 src/components/production/multiviews/ProductionMultiviews.tsx
create mode 100644 src/components/production/outputs/ProductionOutputEdit.tsx
create mode 100644 src/components/production/outputs/ProductionOutputs.tsx
create mode 100644 src/components/production/outputs/ProductionOutputsCard.tsx
create mode 100644 src/components/production/pipelines/ProductionPipelineCard.tsx
create mode 100644 src/components/production/pipelines/ProductionPipelines.tsx
create mode 100644 src/components/production/sources/ProductionSourceList.tsx
create mode 100644 src/components/production/sources/ProductionSources.tsx
create mode 100644 src/components/section/Section.tsx
delete mode 100644 src/hooks/items/removeSetupItem.ts
create mode 100644 src/hooks/utils/useEffectNotOnMount.tsx
diff --git a/next.config.js b/next.config.js
index 24756994..17f56ac6 100644
--- a/next.config.js
+++ b/next.config.js
@@ -13,6 +13,7 @@ module.exports = {
images: {
minimumCacheTTL: 0
},
+ reactStrictMode: false,
async headers() {
return [
{
diff --git a/src/api/ateliereLive/pipelines/multiviews/multiviews.ts b/src/api/ateliereLive/pipelines/multiviews/multiviews.ts
index 738d6782..6ae528a6 100644
--- a/src/api/ateliereLive/pipelines/multiviews/multiviews.ts
+++ b/src/api/ateliereLive/pipelines/multiviews/multiviews.ts
@@ -7,7 +7,10 @@ import { getAuthorizationHeader } from '../../utils/authheader';
import { createMultiview } from '../../utils/multiview';
import { getSourcesByIds } from '../../../manager/sources';
import { Log } from '../../../logger';
-import { ProductionSettings } from '../../../../interfaces/production';
+import {
+ Production,
+ ProductionSettings
+} from '../../../../interfaces/production';
import { MultiviewSettings } from '../../../../interfaces/multiview';
import { LIVE_BASE_API_PATH } from '../../../../constants';
@@ -35,14 +38,10 @@ export async function getMultiviewsForPipeline(
}
export async function createMultiviewForPipeline(
- productionSettings: ProductionSettings,
- sourceRefs: SourceReference[]
+ production: Production
): Promise {
- const pipeline = productionSettings.pipelines.find((p) =>
- p.multiviews ? p.multiviews?.length > 0 : undefined
- );
- const multiviewIndexArray = pipeline?.multiviews
- ? pipeline.multiviews.map((p) => p.for_pipeline_idx)
+ const multiviewIndexArray = production?.multiviews
+ ? production.multiviews.map((p) => p.for_pipeline_idx)
: undefined;
const multiviewIndex = multiviewIndexArray?.find((p) => p !== undefined);
@@ -51,22 +50,17 @@ export async function createMultiviewForPipeline(
Log().error(`Did not find a specified pipeline in multiview settings`);
throw `Did not find a specified pipeline in multiview settings`;
}
- if (
- !productionSettings.pipelines[multiviewIndex].multiviews ||
- productionSettings.pipelines[multiviewIndex].multiviews?.length === 0
- ) {
- Log().error(
- `Did not find any multiview settings in pipeline settings for: ${productionSettings.pipelines[multiviewIndex]}`
- );
- throw `Did not find any multiview settings in pipeline settings for: ${productionSettings.pipelines[multiviewIndex]}`;
+ if (!production.multiviews || production.multiviews?.length === 0) {
+ Log().error(`Did not find any multiviews for: ${production.name}`);
+ throw `Did not find any multiviews for: ${production.name}`;
}
const pipelineUUID =
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- productionSettings.pipelines[multiviewIndex].pipeline_id!;
+ production.pipelines[0].pipeline_id!;
const sources = await getSourcesByIds(
- sourceRefs.map((ref) => (ref._id ? ref._id.toString() : ''))
+ production.sources.map((ref) => (ref._id ? ref._id.toString() : ''))
);
- const sourceRefsWithLabels = sourceRefs.map((ref) => {
+ const sourceRefsWithLabels = production.sources.map((ref) => {
const refId = ref._id ? ref._id.toString() : '';
if (!ref.label) {
const source = sources.find((source) => source._id.toString() === refId);
@@ -76,8 +70,7 @@ export async function createMultiviewForPipeline(
});
Log().info(`Creating a multiview for pipeline '${pipelineUUID}' from preset`);
- const multiviewsSettings: MultiviewSettings[] =
- productionSettings.pipelines[multiviewIndex].multiviews ?? [];
+ const multiviewsSettings: MultiviewSettings[] = production.multiviews ?? [];
const createEachMultiviewer = multiviewsSettings.map(
async (singleMultiviewSettings) => {
diff --git a/src/api/ateliereLive/pipelines/pipelines.ts b/src/api/ateliereLive/pipelines/pipelines.ts
index eb2a8089..cfcdced2 100644
--- a/src/api/ateliereLive/pipelines/pipelines.ts
+++ b/src/api/ateliereLive/pipelines/pipelines.ts
@@ -163,8 +163,25 @@ export async function removePipelineStreams(pipeId: string) {
return { status: 200, results };
}
-export async function createPipelineOutputs(pipeline: PipelineSettings) {
- const outputs = pipeline.outputs;
+export async function createPipelineOutputs(
+ pipeline: PipelineSettings,
+ outputsParam: PipelineOutput[]
+) {
+ if (!pipeline.pipeline_id) return;
+ const pipelinesOutputs = await getPipelineOutputs(pipeline.pipeline_id).catch(
+ (error) => {
+ throw error.message;
+ }
+ );
+ const outputsWithStreams = outputsParam.filter(
+ (output) => output.streams?.length
+ );
+ const outputs = outputsWithStreams?.map((output, index) => {
+ return {
+ ...output,
+ uuid: pipelinesOutputs[index]?.uuid
+ };
+ });
if (!outputs) return;
const startOutputStreamsPromises = outputs.map((o) => {
return startPipelineStream(
@@ -176,14 +193,14 @@ export async function createPipelineOutputs(pipeline: PipelineSettings) {
const startedOutputs = await Promise.all(startOutputStreamsPromises).catch(
(error) => {
Log().error(
- `Failed to create outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}'`,
+ `Failed to create outputs for pipeline '${pipeline.pipeline_readable_name}/${pipeline.pipeline_id}'`,
error
);
- throw `Failed to create outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}': ${error.message}`;
+ throw `Failed to create outputs for pipeline '${pipeline.pipeline_readable_name}/${pipeline.pipeline_id}': ${error.message}`;
}
);
Log().info(
- `Outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}' created`
+ `Outputs for pipeline '${pipeline.pipeline_readable_name}/${pipeline.pipeline_id}' created`
);
return startedOutputs;
}
@@ -198,14 +215,14 @@ export async function connectControlPanelToPipeline(
});
const productionControlPanels = controlPanels.filter((c) =>
- control_connection.control_panel_name?.includes(c.name)
+ control_connection.control_panel_ids?.includes(c.uuid)
);
if (!productionControlPanels || productionControlPanels.length === 0) {
Log().error(
- `Did not find control panel: ${control_connection.control_panel_name}`
+ `Did not find control panel: ${control_connection.control_panel_ids}`
);
- throw `Did not find control panel: ${control_connection.control_panel_name}`;
+ throw `Did not find control panel: ${control_connection.control_panel_ids}`;
}
const pipelinesIds = pipelines.map((pipeline) => {
diff --git a/src/api/ateliereLive/pipelines/renderingengine/renderingengine.ts b/src/api/ateliereLive/pipelines/renderingengine/renderingengine.ts
index 68507b9d..9c6e814c 100644
--- a/src/api/ateliereLive/pipelines/renderingengine/renderingengine.ts
+++ b/src/api/ateliereLive/pipelines/renderingengine/renderingengine.ts
@@ -5,7 +5,6 @@ import {
} from '../../../../../types/ateliere-live';
import { LIVE_BASE_API_PATH } from '../../../../constants';
import { MultiviewSettings } from '../../../../interfaces/multiview';
-import { Production } from '../../../../interfaces/production';
import {
HTMLSource,
MediaSource
@@ -17,6 +16,7 @@ import {
getMultiviewsForPipeline,
updateMultiviewForPipeline
} from '../multiviews/multiviews';
+import { PipelineSettings } from '../../../../interfaces/pipeline';
export async function getPipelineHtmlSources(
pipelineUuid: string
@@ -44,20 +44,19 @@ export async function getPipelineHtmlSources(
}
export async function createPipelineHtmlSource(
- production: Production,
+ pipelines: PipelineSettings[],
inputSlot: number,
data: HTMLSource,
source: SourceReference
) {
try {
- const { production_settings } = production;
const htmlResults = [];
- for (let i = 0; i < production_settings.pipelines.length; i++) {
+ for (let i = 0; i < pipelines.length; i++) {
const response = await fetch(
new URL(
LIVE_BASE_API_PATH +
- `/pipelines/${production_settings.pipelines[i].pipeline_id}/renderingengine/html`,
+ `/pipelines/${pipelines[i].pipeline_id}/renderingengine/html`,
process.env.LIVE_URL
),
{
@@ -117,18 +116,16 @@ export async function createPipelineHtmlSource(
}
try {
- if (!production.production_settings.pipelines[0].pipeline_id) {
- Log().error(
- `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`
- );
- throw `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`;
+ if (!pipelines[0].pipeline_id) {
+ Log().error(`Missing pipeline_id for: ${pipelines[0].pipeline_name}`);
+ throw `Missing pipeline_id for: ${pipelines[0].pipeline_name}`;
}
const multiviewsResponse = await getMultiviewsForPipeline(
- production.production_settings.pipelines[0].pipeline_id
+ pipelines[0].pipeline_id
);
const multiviews = multiviewsResponse.filter((multiview) => {
- const pipeline = production.production_settings.pipelines[0];
+ const pipeline = pipelines[0];
const multiviewArray = pipeline.multiviews;
if (Array.isArray(multiviewArray)) {
@@ -146,9 +143,9 @@ export async function createPipelineHtmlSource(
if (multiviews.length === 0 || !multiviews) {
Log().error(
- `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`
+ `No multiview found for pipeline: ${pipelines[0].pipeline_id}`
);
- throw `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`;
+ throw `No multiview found for pipeline: ${pipelines[0].pipeline_id}`;
}
await Promise.all(
@@ -188,7 +185,7 @@ export async function createPipelineHtmlSource(
];
await updateMultiviewForPipeline(
- production.production_settings.pipelines[0].pipeline_id!,
+ pipelines[0].pipeline_id!,
multiview.id,
updatedViews
);
@@ -303,20 +300,19 @@ export async function getPipelineMediaSources(
}
export async function createPipelineMediaSource(
- production: Production,
+ pipelines: PipelineSettings[],
inputSlot: number,
data: MediaSource,
source: SourceReference
) {
try {
- const { production_settings } = production;
const mediaResults = [];
- for (let i = 0; i < production_settings.pipelines.length; i++) {
+ for (let i = 0; i < pipelines.length; i++) {
const response = await fetch(
new URL(
LIVE_BASE_API_PATH +
- `/pipelines/${production_settings.pipelines[i].pipeline_id}/renderingengine/media`,
+ `/pipelines/${pipelines[i].pipeline_id}/renderingengine/media`,
process.env.LIVE_URL
),
{
@@ -373,18 +369,16 @@ export async function createPipelineMediaSource(
}
try {
- if (!production.production_settings.pipelines[0].pipeline_id) {
- Log().error(
- `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`
- );
- throw `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`;
+ if (pipelines[0].pipeline_id) {
+ Log().error(`Missing pipeline_id for: ${pipelines[0].pipeline_name}`);
+ throw `Missing pipeline_id for: ${pipelines[0].pipeline_name}`;
}
const multiviewsResponse = await getMultiviewsForPipeline(
- production.production_settings.pipelines[0].pipeline_id
+ pipelines[0].pipeline_id || ''
);
const multiviews = multiviewsResponse.filter((multiview) => {
- const pipeline = production.production_settings.pipelines[0];
+ const pipeline = pipelines[0];
const multiviewArray = pipeline.multiviews;
if (Array.isArray(multiviewArray)) {
@@ -402,9 +396,9 @@ export async function createPipelineMediaSource(
if (multiviews.length === 0 || !multiviews) {
Log().error(
- `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`
+ `No multiview found for pipeline: ${pipelines[0].pipeline_id}`
);
- throw `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`;
+ throw `No multiview found for pipeline: ${pipelines[0].pipeline_id}`;
}
await Promise.all(
@@ -444,7 +438,7 @@ export async function createPipelineMediaSource(
];
await updateMultiviewForPipeline(
- production.production_settings.pipelines[0].pipeline_id!,
+ pipelines[0].pipeline_id!,
multiview.id,
updatedViews
);
diff --git a/src/api/ateliereLive/pipelines/streams/streams.ts b/src/api/ateliereLive/pipelines/streams/streams.ts
index fc183dad..8f857ab5 100644
--- a/src/api/ateliereLive/pipelines/streams/streams.ts
+++ b/src/api/ateliereLive/pipelines/streams/streams.ts
@@ -6,7 +6,10 @@ import {
SourceWithId
} from '../../../../interfaces/Source';
import { MultiviewSettings } from '../../../../interfaces/multiview';
-import { PipelineStreamSettings } from '../../../../interfaces/pipeline';
+import {
+ PipelineSettings,
+ PipelineStreamSettings
+} from '../../../../interfaces/pipeline';
import { Production } from '../../../../interfaces/production';
import { Result } from '../../../../interfaces/result';
import { Log } from '../../../logger';
@@ -50,15 +53,14 @@ export async function getPipelineStreams(
export async function createStream(
source: SourceWithId,
- production: Production,
+ pipelines: PipelineSettings[],
input_slot: number
): Promise> {
const sourceToPipelineStreams: SourceToPipelineStream[] = [];
try {
const allPipelines = await getPipelines();
- const { production_settings } = production;
const pipelinesToUseCompact = allPipelines.filter((pipeline) =>
- production_settings.pipelines.some((p) => p.pipeline_id === pipeline.uuid)
+ pipelines.some((p) => p.pipeline_id === pipeline.uuid)
);
const usedPorts = await getCurrentlyUsedPorts(
@@ -89,7 +91,7 @@ export async function createStream(
await initDedicatedPorts();
- for (const pipeline of production_settings.pipelines) {
+ for (const pipeline of pipelines) {
const availablePorts = getAvailablePortsForIngest(
source.ingest_name,
usedPorts
@@ -208,18 +210,16 @@ export async function createStream(
}
try {
- if (!production.production_settings.pipelines[0].pipeline_id) {
- Log().error(
- `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`
- );
- throw `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`;
+ if (!pipelines[0].pipeline_id) {
+ Log().error(`Missing pipeline_id for: ${pipelines[0].pipeline_name}`);
+ throw `Missing pipeline_id for: ${pipelines[0].pipeline_name}`;
}
const multiviewsResponse = await getMultiviewsForPipeline(
- production.production_settings.pipelines[0].pipeline_id
+ pipelines[0].pipeline_id
);
const multiviews = multiviewsResponse.filter((multiview) => {
- const pipeline = production.production_settings.pipelines[0];
+ const pipeline = pipelines[0];
const multiviewArray = pipeline.multiviews;
if (Array.isArray(multiviewArray)) {
@@ -237,9 +237,9 @@ export async function createStream(
if (multiviews.length === 0) {
Log().error(
- `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`
+ `No multiview found for pipeline: ${pipelines[0].pipeline_id}`
);
- throw `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`;
+ throw `No multiview found for pipeline: ${pipelines[0].pipeline_id}`;
}
multiviews.map(async (multiview) => {
const views = multiview.layout.views;
@@ -278,7 +278,7 @@ export async function createStream(
];
await updateMultiviewForPipeline(
- production.production_settings.pipelines[0].pipeline_id!,
+ pipelines[0].pipeline_id!,
multiview.id,
updatedViews
);
diff --git a/src/api/manager/job/syncMonitoring.ts b/src/api/manager/job/syncMonitoring.ts
index 7619bfdb..2ab9f2ec 100644
--- a/src/api/manager/job/syncMonitoring.ts
+++ b/src/api/manager/job/syncMonitoring.ts
@@ -40,9 +40,8 @@ export async function updatedMonitoringForProduction(
production: Production
) {
const productionId = production._id;
- const { production_settings } = production;
- const productionPipelineUUIDs = production_settings.pipelines.map(
+ const productionPipelineUUIDs = production.pipelines.map(
(pipeline) => pipeline.pipeline_id!
);
@@ -1230,11 +1229,9 @@ const createMonitoringComponentControlPanels = async (
prevMonitoring?: Monitoring
) => {
const pipelineIndex =
- production.production_settings.control_connection.control_panel_endpoint
- .toPipelineIdx;
+ production.control_connection?.control_panel_endpoint.toPipelineIdx || 0;
- const pipelineId =
- production.production_settings.pipelines[pipelineIndex].pipeline_id;
+ const pipelineId = production.pipelines[pipelineIndex].pipeline_id;
const pipeline = pipelines.find((p) => p.uuid === pipelineId);
@@ -1294,11 +1291,9 @@ const createMonitoringControlPanels = async (
pipelines: ResourcesPipelineResponse[]
) => {
const pipelineIndex =
- production.production_settings.control_connection.control_panel_endpoint
- .toPipelineIdx;
+ production.control_connection?.control_panel_endpoint.toPipelineIdx || 0;
- const pipelineId =
- production.production_settings.pipelines[pipelineIndex].pipeline_id;
+ const pipelineId = production.pipelines[pipelineIndex].pipeline_id;
const pipeline = pipelines.find((p) => p.uuid === pipelineId);
diff --git a/src/api/manager/productions.ts b/src/api/manager/productions.ts
index 716cd340..672d25ad 100644
--- a/src/api/manager/productions.ts
+++ b/src/api/manager/productions.ts
@@ -1,6 +1,6 @@
import { Db, ObjectId, UpdateResult } from 'mongodb';
import { getDatabase } from '../mongoClient/dbClient';
-import { Production, ProductionWithId } from '../../interfaces/production';
+import { Production } from '../../interfaces/production';
import { Log } from '../logger';
export async function getProductions(): Promise {
@@ -10,12 +10,12 @@ export async function getProductions(): Promise {
) as Production[];
}
-export async function getProduction(id: string): Promise {
+export async function getProduction(id: string): Promise {
const db = await getDatabase();
return (await db
.collection('productions')
- .findOne({ _id: new ObjectId(id) })) as ProductionWithId;
+ .findOne({ _id: new ObjectId(id) })) as Production | null;
}
export async function setProductionsIsActiveFalse(): Promise<
@@ -32,14 +32,13 @@ export async function putProduction(
production: Production
): Promise {
const db = await getDatabase();
- const newSourceId = new ObjectId().toString();
const sources = production.sources
? production.sources.flatMap((singleSource) => {
return singleSource._id
? singleSource
: {
- _id: newSourceId,
+ _id: new ObjectId().toString(),
type: singleSource.type,
label: singleSource.label,
input_slot: singleSource.input_slot,
@@ -60,7 +59,11 @@ export async function putProduction(
name: production.name,
isActive: production.isActive,
sources: sources,
- production_settings: production.production_settings
+ preset_name: production.preset_name,
+ pipelines: production.pipelines,
+ control_connection: production.control_connection,
+ outputs: production.outputs,
+ multiviews: production.multiviews
}
);
@@ -73,7 +76,11 @@ export async function putProduction(
name: production.name,
isActive: production.isActive,
sources: sources,
- production_settings: production.production_settings
+ preset_name: production.preset_name,
+ pipelines: production.pipelines,
+ control_connection: production.control_connection,
+ outputs: production.outputs,
+ multiviews: production.multiviews
};
}
@@ -113,7 +120,7 @@ export async function getProductionPipelineSourceAlignment(
return null;
}
- const pipeline = production.production_settings.pipelines.find(
+ const pipeline = production.pipelines.find(
(p) => p.pipeline_id === pipelineId
);
@@ -141,13 +148,12 @@ export async function setProductionPipelineSourceAlignment(
const result = await db.collection('productions').updateOne(
{
_id: new ObjectId(productionId),
- 'production_settings.pipelines.pipeline_id': pipelineId,
- 'production_settings.pipelines.sources.source_id': sourceId
+ 'pipelines.pipeline_id': pipelineId,
+ 'pipelines.sources.source_id': sourceId
},
{
$set: {
- 'production_settings.pipelines.$[p].sources.$[s].settings.alignment_ms':
- alignment_ms
+ 'pipelines.$[p].sources.$[s].settings.alignment_ms': alignment_ms
}
},
{
@@ -182,7 +188,7 @@ export async function getProductionSourceLatency(
return null;
}
- const pipeline = production.production_settings.pipelines.find(
+ const pipeline = production.pipelines.find(
(p) => p.pipeline_id === pipelineId
);
@@ -210,12 +216,12 @@ export async function setProductionPipelineSourceLatency(
const result = await db.collection('productions').updateOne(
{
_id: new ObjectId(productionId),
- 'production_settings.pipelines.pipeline_id': pipelineId,
- 'production_settings.pipelines.sources.source_id': sourceId
+ 'pipelines.pipeline_id': pipelineId,
+ 'pipelines.sources.source_id': sourceId
},
{
$set: {
- 'production_settings.pipelines.$[p].sources.$[s].settings.max_network_latency_ms':
+ 'pipelines.$[p].sources.$[s].settings.max_network_latency_ms':
max_network_latency_ms
}
},
diff --git a/src/api/manager/workflow.ts b/src/api/manager/workflow.ts
index 6520377b..2e9dbaec 100644
--- a/src/api/manager/workflow.ts
+++ b/src/api/manager/workflow.ts
@@ -1,7 +1,6 @@
import { SourceReference, SourceWithId } from './../../interfaces/Source';
import {
Production,
- ProductionSettings,
StartProductionStep,
StopProductionStep
} from '../../interfaces/production';
@@ -61,6 +60,7 @@ import {
deleteHtmlFromPipeline,
deleteMediaFromPipeline
} from '../ateliereLive/pipelines/renderingengine/renderingengine';
+import cloneDeep from 'lodash.clonedeep';
const isUsed = (pipeline: ResourcesPipelineResponse) => {
const hasStreams = pipeline.streams.length > 0;
@@ -81,7 +81,7 @@ const isUsed = (pipeline: ResourcesPipelineResponse) => {
async function connectIngestSources(
productionSources: SourceReference[],
- productionSettings: ProductionSettings,
+ production: Production,
sources: SourceWithId[],
usedPorts: Set
) {
@@ -109,7 +109,7 @@ async function connectIngestSources(
const newAudioMapping = audioSettings?.audio_stream?.audio_mapping;
const audioMapping = newAudioMapping?.length ? newAudioMapping : [[0, 1]];
- for (const pipeline of productionSettings.pipelines) {
+ for (const pipeline of production.pipelines) {
const availablePorts = getAvailablePortsForIngest(
source.ingest_name,
usedPorts
@@ -195,19 +195,16 @@ async function connectIngestSources(
return sourceToPipelineStreams;
}
-async function insertPipelineUuid(productionSettings: ProductionSettings) {
+async function insertPipelineUuid(production: Production) {
const availablePipelines = await getPipelines().catch((error) => {
- Log().error(
- `Failed to get pipeline IDs for '${productionSettings.name}'`,
- error
- );
- throw `Failed to get pipeline IDs for '${productionSettings.name}: ${error.message}'`;
+ Log().error(`Failed to get pipeline IDs for '${production.name}'`, error);
+ throw `Failed to get pipeline IDs for '${production.name}: ${error.message}'`;
});
- for (const pipelinePreset of productionSettings.pipelines) {
+ for (const pipelinePreset of production.pipelines) {
const pipeline = availablePipelines.find(
(p: ResourcesCompactPipelineResponse) =>
- p.name === pipelinePreset.pipeline_name
+ p.uuid === pipelinePreset.pipeline_id
);
if (pipeline) {
pipelinePreset.pipeline_id = pipeline.uuid;
@@ -330,16 +327,14 @@ export async function stopProduction(
success: true
} as StopProductionStep
};
- const pipelineIds = production.production_settings.pipelines.map(
- (p) => p.pipeline_id
- );
+ const pipelineIds = production.pipelines.map((p) => p.pipeline_id);
const productionHasRenderingEngineSources = production.sources.some(
(source) => source.type === 'html' || source.type === 'mediaplayer'
);
if (productionHasRenderingEngineSources) {
- for (const pipeline of production.production_settings.pipelines) {
+ for (const pipeline of production.pipelines) {
const pipelineId = pipeline.pipeline_id;
if (pipelineId) {
const pipelineRenderingEngine = await getPipelineRenderingEngine(
@@ -350,7 +345,7 @@ export async function stopProduction(
const mediaSources = pipelineRenderingEngine.media;
if (htmlSources.length > 0 && htmlSources) {
- for (const pipeline of production.production_settings.pipelines) {
+ for (const pipeline of production.pipelines) {
for (const htmlSource of htmlSources) {
const pipelineId = pipeline.pipeline_id;
if (pipelineId !== undefined) {
@@ -359,9 +354,8 @@ export async function stopProduction(
}
}
}
-
if (mediaSources.length > 0 && mediaSources) {
- for (const pipeline of production.production_settings.pipelines) {
+ for (const pipeline of production.pipelines) {
for (const mediaSource of mediaSources) {
const pipelineId = pipeline.pipeline_id;
if (pipelineId !== undefined) {
@@ -509,9 +503,8 @@ export async function stopProduction(
export async function startProduction(
production: Production
): Promise> {
- const { production_settings } = production;
Log().info(
- `Starting production '${production.name}' with preset '${production_settings.name}'`
+ `Starting production '${production.name}' with preset '${production.preset_name}'`
);
await initDedicatedPorts();
@@ -541,17 +534,15 @@ export async function startProduction(
throw "Can't get source!";
});
- // Lookup pipeline UUIDs from pipeline names and insert to production_settings
- await insertPipelineUuid(production_settings).catch((error) => {
+ // Lookup pipeline UUIDs from pipeline names and insert to production
+ await insertPipelineUuid(production).catch((error) => {
throw error;
});
// Fetch expanded pipeline objects from Ateliere Live
- const pipelinesToUsePromises = production_settings.pipelines.map(
- (pipeline) => {
- return getPipeline(pipeline.pipeline_id!);
- }
- );
+ const pipelinesToUsePromises = production.pipelines.map((pipeline) => {
+ return getPipeline(pipeline.pipeline_id!);
+ });
const pipelinesToUse = await Promise.all(pipelinesToUsePromises);
// Check if pipelines are already in use by another production
@@ -570,11 +561,9 @@ export async function startProduction(
)}`;
}
- const resetPipelinePromises = production_settings.pipelines.map(
- (pipeline) => {
- return resetPipeline(pipeline.pipeline_id!);
- }
- );
+ const resetPipelinePromises = production.pipelines.map((pipeline) => {
+ return resetPipeline(pipeline.pipeline_id!);
+ });
await Promise.all(resetPipelinePromises).catch((error) => {
throw `Failed to reset pipelines: ${error}`;
});
@@ -583,8 +572,8 @@ export async function startProduction(
const allControlPanels = await getControlPanels();
// Check which control panels that should be used by this production
const controlPanelsToUse = allControlPanels.filter((controlPanel) =>
- production.production_settings.control_connection.control_panel_name?.includes(
- controlPanel.name
+ production.control_connection?.control_panel_ids?.includes(
+ controlPanel.uuid
)
);
// Check if control panels are already in use by another production
@@ -606,7 +595,7 @@ export async function startProduction(
// TODO: Is this really neccessary? We have checked that pipeline and controlpanels are not in use by another production
// can they still be in a state where we need to stop them? Removing streams and multiviews might be left to do though..
await stopPipelines(
- production_settings.pipelines.map((pipeline) => pipeline.pipeline_id!)
+ production.pipelines.map((pipeline) => pipeline.pipeline_id!)
).catch((error) => {
throw `Failed to stop pipelines during startup: ${error}`;
});
@@ -619,13 +608,13 @@ export async function startProduction(
);
streams = await connectIngestSources(
production.sources,
- production_settings,
+ production,
sources,
usedPorts
).catch(async (error) => {
Log().error('Stopping pipelines');
await stopPipelines(
- production_settings.pipelines.map((pipeline) => pipeline.pipeline_id!)
+ production.pipelines.map((pipeline) => pipeline.pipeline_id!)
).catch((error) => {
throw `Failed to stop pipelines after production start failure: ${error}`;
});
@@ -653,19 +642,21 @@ export async function startProduction(
// Try to connect control panels and pipeline-to-pipeline connections start
try {
- // TODO: This will re-fetch pipelines from the Ateliere Live API, but we fetched them above into pipelinesToUse
- await connectControlPanelToPipeline(
- production_settings.control_connection,
- production_settings.pipelines
- ).catch(async (error) => {
- Log().error('Stopping pipelines');
- await stopPipelines(
- production_settings.pipelines.map((pipeline) => pipeline.pipeline_id!)
- ).catch((error) => {
- throw `Failed to stop pipelines after production start failure: ${error}`;
+ if (production.control_connection) {
+ // TODO: This will re-fetch pipelines from the Ateliere Live API, but we fetched them above into pipelinesToUse
+ await connectControlPanelToPipeline(
+ production.control_connection,
+ production.pipelines
+ ).catch(async (error) => {
+ Log().error('Stopping pipelines');
+ await stopPipelines(
+ production.pipelines.map((pipeline) => pipeline.pipeline_id!)
+ ).catch((error) => {
+ throw `Failed to stop pipelines after production start failure: ${error}`;
+ });
+ throw error;
});
- throw error;
- });
+ }
} catch (error) {
Log().error('Could not setup control panels');
Log().error(error);
@@ -693,15 +684,15 @@ export async function startProduction(
// Try to setup pipeline outputs start
try {
- for (const pipeline of production_settings.pipelines) {
- await createPipelineOutputs(pipeline);
+ for (const [index, pipeline] of production.pipelines.entries()) {
+ await createPipelineOutputs(pipeline, production.outputs[index]);
}
} catch (e) {
Log().error('Could not setup pipeline outputs');
Log().error(e);
Log().error('Stopping pipelines');
await stopPipelines(
- production_settings.pipelines.map((pipeline) => pipeline.pipeline_id!)
+ production.pipelines.map((pipeline) => pipeline.pipeline_id!)
).catch((error) => {
throw `Failed to stop pipelines after production start failure: ${error}`;
});
@@ -732,7 +723,7 @@ export async function startProduction(
// Try to setup pipeline outputs end
// Try to setup multiviews start
try {
- if (!production.production_settings.pipelines[0].multiviews) {
+ if (!production.multiviews) {
Log().error(
`No multiview settings specified for production: ${production.name}`
);
@@ -740,31 +731,30 @@ export async function startProduction(
}
const runtimeMultiviews = await createMultiviewForPipeline(
- production_settings,
- production.sources
+ production
).catch(async (error) => {
Log().error(
- `Failed to create multiview for pipeline '${production_settings.pipelines[0].pipeline_name}/${production_settings.pipelines[0].pipeline_id}'`,
+ `Failed to create multiview for pipeline '${production.pipelines[0].pipeline_readable_name}/${production.pipelines[0].pipeline_id}'`,
error
);
Log().error('Stopping pipelines');
await stopPipelines(
- production_settings.pipelines.map((pipeline) => pipeline.pipeline_id!)
+ production.pipelines.map((pipeline) => pipeline.pipeline_id!)
).catch((error) => {
throw `Failed to stop pipelines after production start failure: ${error}`;
});
- throw `Failed to create multiview for pipeline '${production_settings.pipelines[0].pipeline_name}/${production_settings.pipelines[0].pipeline_id}': ${error}`;
+ throw `Failed to create multiview for pipeline '${production.pipelines[0].pipeline_readable_name}/${production.pipelines[0].pipeline_id}': ${error}`;
});
runtimeMultiviews.flatMap((runtimeMultiview, index) => {
- const multiview = production.production_settings.pipelines[0].multiviews;
+ const multiview = production.pipelines[0].multiviews;
if (multiview && multiview[index]) {
return (multiview[index].multiview_id = runtimeMultiview.id);
}
});
Log().info(
- `Production '${production.name}' with preset '${production_settings.name}' started`
+ `Production '${production.name}' with preset '${production.name}' started`
);
} catch (e) {
Log().error('Could not start multiviews');
@@ -812,7 +802,7 @@ export async function startProduction(
input_slot: htmlSource.input_slot
};
await createPipelineHtmlSource(
- production,
+ production.pipelines,
htmlSource.input_slot,
htmlData,
htmlSource
@@ -828,7 +818,7 @@ export async function startProduction(
input_slot: mediaSource.input_slot
};
await createPipelineMediaSource(
- production,
+ production.pipelines,
mediaSource.input_slot,
mediaData,
mediaSource
@@ -849,6 +839,7 @@ export async function startProduction(
// Store updated production in database
await putProduction(production._id.toString(), {
...production,
+ isActive: true,
sources: production.sources.map((source) => {
const streamsForSource = streams?.filter(
(stream) => stream.source_id === source._id?.toString()
@@ -860,46 +851,41 @@ export async function startProduction(
input_slot: source.input_slot
};
}),
- production_settings: {
- ...production.production_settings,
- pipelines: await Promise.all(
- production.production_settings.pipelines.map(async (pipeline) => {
- const newSources = await Promise.all(
- sourcesWithId.map(async (source) => {
- const ingestUuid = await getUuidFromIngestName(
- source.ingest_name
- );
- const sourceId = await getSourceIdFromSourceName(
- ingestUuid || '',
- source.ingest_source_name
- );
-
- const currentSettings = pipeline.sources?.find(
- (s) => s.source_id === sourceId
- )?.settings;
-
- return {
- source_id: sourceId || 0,
- settings: {
- alignment_ms:
- currentSettings?.alignment_ms ?? pipeline.alignment_ms,
- max_network_latency_ms:
- currentSettings?.max_network_latency_ms ??
- pipeline.max_network_latency_ms
- }
- };
- })
- );
- return { ...pipeline, sources: newSources };
- })
- )
- },
- isActive: true
+ pipelines: await Promise.all(
+ production.pipelines.map(async (pipeline) => {
+ const newSources = await Promise.all(
+ sourcesWithId.map(async (source) => {
+ const ingestUuid = await getUuidFromIngestName(
+ source.ingest_name
+ );
+ const sourceId = await getSourceIdFromSourceName(
+ ingestUuid || '',
+ source.ingest_source_name
+ );
+ const currentSettings = pipeline.sources?.find(
+ (s) => s.source_id === sourceId
+ )?.settings;
+
+ return {
+ source_id: sourceId || 0,
+ settings: {
+ alignment_ms:
+ currentSettings?.alignment_ms ?? pipeline.alignment_ms,
+ max_network_latency_ms:
+ currentSettings?.max_network_latency_ms ??
+ pipeline.max_network_latency_ms
+ }
+ };
+ })
+ );
+ return { ...pipeline, sources: newSources };
+ })
+ )
}).catch(async (error) => {
Log().error(error);
Log().error('Stopping pipelines');
await stopPipelines(
- production_settings.pipelines.map((pipeline) => pipeline.pipeline_id!)
+ production.pipelines.map((pipeline) => pipeline.pipeline_id!)
).catch((error) => {
throw `Failed to stop pipelines after production start failure: ${error}`;
});
@@ -973,7 +959,7 @@ export async function startProduction(
} catch (error) {
Log().error('Stopping pipelines, error: ', error);
await stopPipelines(
- production_settings.pipelines.map((pipeline) => pipeline.pipeline_id!)
+ production.pipelines.map((pipeline) => pipeline.pipeline_id!)
).catch((error) => {
throw `Failed to stop pipelines after production start failure: ${error}`;
});
@@ -1003,7 +989,7 @@ export async function postMultiviewersOnRunningProduction(
additions: MultiviewSettings[]
) {
try {
- const multiview = production.production_settings.pipelines[0].multiviews;
+ const multiview = production.multiviews;
if (!multiview) {
Log().error(
`No multiview settings specified for production: ${production.name}`
@@ -1011,25 +997,16 @@ export async function postMultiviewersOnRunningProduction(
throw `No multiview settings specified for production: ${production.name}`;
}
- const productionSettings = {
- ...production.production_settings,
- pipelines: production.production_settings.pipelines.map((pipeline) => {
- return {
- ...pipeline,
- multiviews: additions
- };
- })
- };
-
+ const newProduction = cloneDeep(production);
+ newProduction.multiviews = additions;
const runtimeMultiviews = await createMultiviewForPipeline(
- productionSettings,
- production.sources
+ newProduction
).catch(async (error) => {
Log().error(
- `Failed to create multiview for pipeline '${productionSettings.pipelines[0].pipeline_name}/${productionSettings.pipelines[0].pipeline_id}'`,
+ `Failed to create multiview for pipeline '${newProduction.pipelines[0].pipeline_name}/${newProduction.pipelines[0].pipeline_id}'`,
error
);
- throw `Failed to create multiview for pipeline '${productionSettings.pipelines[0].pipeline_name}/${productionSettings.pipelines[0].pipeline_id}': ${error}`;
+ throw `Failed to create multiview for pipeline '${newProduction.pipelines[0].pipeline_name}/${newProduction.pipelines[0].pipeline_id}': ${error}`;
});
const multiviewsWithUpdatedId: MultiviewSettings[] = [
@@ -1044,18 +1021,10 @@ export async function postMultiviewersOnRunningProduction(
await putProduction(production._id.toString(), {
...production,
- production_settings: {
- ...production.production_settings,
- pipelines: production.production_settings.pipelines.map((pipeline) => {
- return {
- ...pipeline,
- multiviews: multiviewsWithUpdatedId
- };
- })
- }
+ multiviews: multiviewsWithUpdatedId
}).catch(async (error) => {
Log().error(
- `Failed to save multiviews for pipeline '${productionSettings.pipelines[0].pipeline_name}/${productionSettings.pipelines[0].pipeline_id}' to database`,
+ `Failed to save multiviews for pipeline '${newProduction.pipelines[0].pipeline_name}/${newProduction.pipelines[0].pipeline_id}' to database`,
error
);
throw error;
@@ -1116,12 +1085,9 @@ export async function putMultiviewersOnRunningProduction(
updates.map(async (multiview) => {
const views = multiview.layout.views;
- if (
- multiview.multiview_id &&
- production.production_settings.pipelines[0].pipeline_id
- ) {
+ if (multiview.multiview_id && production.pipelines[0].pipeline_id) {
await updateMultiviewForPipeline(
- production.production_settings.pipelines[0].pipeline_id,
+ production.pipelines[0].pipeline_id,
multiview.multiview_id,
views
);
@@ -1180,18 +1146,15 @@ export async function deleteMultiviewersOnRunningProduction(
removals: MultiviewSettings[]
) {
try {
- const pipeline = production.production_settings.pipelines.find((p) =>
- p.multiviews ? p.multiviews?.length > 0 : undefined
- );
- const multiviewIndexArray = pipeline?.multiviews
- ? pipeline.multiviews.map((p) => p.for_pipeline_idx)
+ const multiviewIndexArray = production.multiviews
+ ? production.multiviews.map((p) => p.for_pipeline_idx)
: undefined;
const multiviewIndex = multiviewIndexArray?.find((p) => p !== undefined);
const pipelineUUID =
multiviewIndex !== undefined
- ? production.production_settings.pipelines[multiviewIndex].pipeline_id
+ ? production.pipelines[multiviewIndex].pipeline_id
: undefined;
if (!pipelineUUID) return;
diff --git a/src/app/api/manager/productions/[id]/route.ts b/src/app/api/manager/productions/[id]/route.ts
index 5f825f88..54994a69 100644
--- a/src/app/api/manager/productions/[id]/route.ts
+++ b/src/app/api/manager/productions/[id]/route.ts
@@ -25,8 +25,9 @@ export async function GET(
const production = await getProduction(params.id);
const prod = {
...production,
- sources: production.sources.sort((a, b) => a.input_slot - b.input_slot),
- _id: production._id.toString()
+ sources:
+ production?.sources.sort((a, b) => a.input_slot - b.input_slot) || [],
+ _id: production?._id.toString()
};
return new NextResponse(JSON.stringify(prod), { status: 200 });
} catch (error) {
diff --git a/src/app/api/manager/rendering-engine/html/route.ts b/src/app/api/manager/rendering-engine/html/route.ts
index e643fbbe..ee35f93d 100644
--- a/src/app/api/manager/rendering-engine/html/route.ts
+++ b/src/app/api/manager/rendering-engine/html/route.ts
@@ -5,9 +5,10 @@ import { Log } from '../../../../../api/logger';
import { HTMLSource } from '../../../../../interfaces/renderingEngine';
import { Production } from '../../../../../interfaces/production';
import { SourceReference } from '../../../../../interfaces/Source';
+import { PipelineSettings } from '../../../../../interfaces/pipeline';
export type CreateHtmlRequestBody = {
- production: Production;
+ pipelines: PipelineSettings[];
htmlBody: HTMLSource;
inputSlot: number;
source: SourceReference;
@@ -24,7 +25,7 @@ export async function POST(request: NextRequest): Promise {
const createHtmlRequest = data as CreateHtmlRequestBody;
return await createPipelineHtmlSource(
- createHtmlRequest.production,
+ createHtmlRequest.pipelines,
createHtmlRequest.inputSlot,
createHtmlRequest.htmlBody,
createHtmlRequest.source
diff --git a/src/app/api/manager/rendering-engine/media/route.ts b/src/app/api/manager/rendering-engine/media/route.ts
index f21e1c5a..3e7ddaef 100644
--- a/src/app/api/manager/rendering-engine/media/route.ts
+++ b/src/app/api/manager/rendering-engine/media/route.ts
@@ -5,9 +5,10 @@ import { Log } from '../../../../../api/logger';
import { MediaSource } from '../../../../../interfaces/renderingEngine';
import { Production } from '../../../../../interfaces/production';
import { SourceReference } from '../../../../../interfaces/Source';
+import { PipelineSettings } from '../../../../../interfaces/pipeline';
export type CreateMediaRequestBody = {
- production: Production;
+ pipelines: PipelineSettings[];
mediaBody: MediaSource;
inputSlot: number;
source: SourceReference;
@@ -24,7 +25,7 @@ export async function POST(request: NextRequest): Promise {
const createMediaRequest = data as CreateMediaRequestBody;
return await createPipelineMediaSource(
- createMediaRequest.production,
+ createMediaRequest.pipelines,
createMediaRequest.inputSlot,
createMediaRequest.mediaBody,
createMediaRequest.source
diff --git a/src/app/api/manager/streams/route.ts b/src/app/api/manager/streams/route.ts
index 689dd0f6..d89913aa 100644
--- a/src/app/api/manager/streams/route.ts
+++ b/src/app/api/manager/streams/route.ts
@@ -4,9 +4,10 @@ import { SourceWithId } from '../../../../interfaces/Source';
import { Production } from '../../../../interfaces/production';
import { createStream } from '../../../../api/ateliereLive/pipelines/streams/streams';
import { Log } from '../../../../api/logger';
+import { PipelineSettings } from '../../../../interfaces/pipeline';
export type CreateStreamRequestBody = {
source: SourceWithId;
- production: Production;
+ pipelines: PipelineSettings[];
input_slot: number;
};
export async function POST(request: NextRequest): Promise {
@@ -21,7 +22,7 @@ export async function POST(request: NextRequest): Promise {
return await createStream(
createStreamRequest.source,
- createStreamRequest.production,
+ createStreamRequest.pipelines,
createStreamRequest.input_slot
)
.then((response) => {
diff --git a/src/app/production/[id]/page.tsx b/src/app/production/[id]/page.tsx
index 57986804..8122166c 100644
--- a/src/app/production/[id]/page.tsx
+++ b/src/app/production/[id]/page.tsx
@@ -1,1243 +1,6 @@
-'use client';
-import React, {
- useEffect,
- useState,
- KeyboardEvent,
- useContext,
- useMemo
-} from 'react';
-import { PageProps } from '../../../../.next/types/app/production/[id]/page';
-import { AddInput } from '../../../components/addInput/AddInput';
-import { useSources } from '../../../hooks/sources/useSources';
-import {
- AddSourceStatus,
- DeleteSourceStatus,
- SourceReference,
- SourceWithId
-} from '../../../interfaces/Source';
-import {
- useGetProduction,
- usePutProduction,
- useReplaceProductionSourceStreamIds
-} from '../../../hooks/productions';
-import { Production } from '../../../interfaces/production';
-import { updateSetupItem } from '../../../hooks/items/updateSetupItem';
-import { removeSetupItem } from '../../../hooks/items/removeSetupItem';
-import { addSetupItem } from '../../../hooks/items/addSetupItem';
-import HeaderNavigation from '../../../components/headerNavigation/HeaderNavigation';
-import { useGetPresets } from '../../../hooks/presets';
-import { Preset } from '../../../interfaces/preset';
-import SourceCards from '../../../components/sourceCards/SourceCards';
-import { HTML5Backend } from 'react-dnd-html5-backend';
-import { DndProvider } from 'react-dnd';
-import { PresetDropdown } from '../../../components/startProduction/presetDropdown';
-import { StartProductionButton } from '../../../components/startProduction/StartProductionButton';
-import { ConfigureOutputButton } from '../../../components/startProduction/ConfigureOutputButton';
-import toast from 'react-hot-toast';
-import { useTranslate } from '../../../i18n/useTranslate';
-import { usePipelines } from '../../../hooks/pipelines';
-import { useControlPanels } from '../../../hooks/controlPanels';
-import PipelineNameDropDown from '../../../components/dropDown/PipelineNameDropDown';
-import ControlPanelDropDown from '../../../components/dropDown/ControlPanelDropDown';
-import { Pipelines } from '../../../components/pipeline/Pipelines';
-import { AddSourceModal } from '../../../components/modal/AddSourceModal';
-import { RemoveSourceModal } from '../../../components/modal/RemoveSourceModal';
-import { useDeleteStream, useCreateStream } from '../../../hooks/streams';
-import { MonitoringButton } from '../../../components/button/MonitoringButton';
-import { useGetMultiviewLayout } from '../../../hooks/multiviewLayout';
-import { useMultiviews } from '../../../hooks/multiviews';
-import SourceList from '../../../components/sourceList/SourceList';
-import { GlobalContext } from '../../../contexts/GlobalContext';
-import { Select } from '../../../components/select/Select';
-import { useGetFirstEmptySlot } from '../../../hooks/useGetFirstEmptySlot';
-import { ConfigureMultiviewButton } from '../../../components/modal/configureMultiviewModal/ConfigureMultiviewButton';
-import { useUpdateSourceInputSlotOnMultiviewLayouts } from '../../../hooks/useUpdateSourceInputSlotOnMultiviewLayouts';
-import { useCheckProductionPipelines } from '../../../hooks/useCheckProductionPipelines';
-import { ISource } from '../../../hooks/useDragableItems';
-import { useUpdateStream } from '../../../hooks/streams';
-import { usePutProductionPipelineSourceAlignmentAndLatency } from '../../../hooks/productions';
-import { useIngestSourceId } from '../../../hooks/ingests';
-import cloneDeep from 'lodash.clonedeep';
-import {
- useAddMultiviewersOnRunningProduction,
- useRemoveMultiviewersOnRunningProduction,
- useUpdateMultiviewersOnRunningProduction
-} from '../../../hooks/workflow';
-import { MultiviewSettings } from '../../../interfaces/multiview';
-import { CreateHtmlModal } from '../../../components/modal/renderingEngineModals/CreateHtmlModal';
-import { CreateMediaModal } from '../../../components/modal/renderingEngineModals/CreateMediaModal';
-import { useDeleteHtmlSource } from '../../../hooks/renderingEngine/useDeleteHtmlSource';
-import { useDeleteMediaSource } from '../../../hooks/renderingEngine/useDeleteMediaSource';
-import { useCreateHtmlSource } from '../../../hooks/renderingEngine/useCreateHtmlSource';
-import { useCreateMediaSource } from '../../../hooks/renderingEngine/useCreateMediaSource';
-import { useRenderingEngine } from '../../../hooks/renderingEngine/useRenderingEngine';
+import ProductionPage from '../../../components/production/ProductionPage';
-export default function ProductionConfiguration({ params }: PageProps) {
- const t = useTranslate();
-
- //SOURCES
- const [sources] = useSources();
- const [selectedValue, setSelectedValue] = useState(
- t('production.add_other_source_type')
- );
- const [addSourceModal, setAddSourceModal] = useState(false);
- const [removeSourceModal, setRemoveSourceModal] = useState(false);
- const [selectedSource, setSelectedSource] = useState<
- SourceWithId | undefined
- >();
- const [selectedSourceRef, setSelectedSourceRef] = useState<
- SourceReference | undefined
- >();
- const [createStream, loadingCreateStream] = useCreateStream();
- const [deleteStream, loadingDeleteStream] = useDeleteStream();
-
- //PRODUCTION
- const putProduction = usePutProduction();
- const getPresets = useGetPresets();
- const getProduction = useGetProduction();
- const replaceProductionSourceStreamIds =
- useReplaceProductionSourceStreamIds();
- const [configurationName, setConfigurationName] = useState('');
- const [productionSetup, setProductionSetup] = useState();
- const [presets, setPresets] = useState();
- const [selectedPreset, setSelectedPreset] = useState();
- const selectedProductionItems =
- productionSetup?.sources.map((prod) => prod._id) || [];
-
- //MULTIVIEWS
- const getMultiviewLayout = useGetMultiviewLayout();
- const [updateMultiviewViews] = useMultiviews();
- const [updateSourceInputSlotOnMultiviewLayouts, updateMultiviewViewsLoading] =
- useUpdateSourceInputSlotOnMultiviewLayouts();
- const [addMultiviewersOnRunningProduction] =
- useAddMultiviewersOnRunningProduction();
- const [updateMultiviewersOnRunningProduction] =
- useUpdateMultiviewersOnRunningProduction();
- const [removeMultiviewersOnRunningProduction] =
- useRemoveMultiviewersOnRunningProduction();
-
- //FROM LIVE API
- const [pipelines, loadingPipelines, , refreshPipelines] = usePipelines();
- const [controlPanels, loadingControlPanels, , refreshControlPanels] =
- useControlPanels();
-
- //UI STATE
- const [inventoryVisible, setInventoryVisible] = useState(false);
- const [isPresetDropdownHidden, setIsPresetDropdownHidden] = useState(true);
- const [addSourceStatus, setAddSourceStatus] = useState();
- const [deleteSourceStatus, setDeleteSourceStatus] =
- useState();
- const [isHtmlModalOpen, setIsHtmlModalOpen] = useState(false);
- const [isMediaModalOpen, setIsMediaModalOpen] = useState(false);
-
- // Create source
- const [firstEmptySlot] = useGetFirstEmptySlot();
-
- const [updateStream, loading] = useUpdateStream();
- const [getIngestSourceId, ingestSourceIdLoading] = useIngestSourceId();
-
- const putProductionPipelineSourceAlignmentAndLatency =
- usePutProductionPipelineSourceAlignmentAndLatency();
-
- const [checkProductionPipelines] = useCheckProductionPipelines();
-
- // Rendering engine
- const [deleteHtmlSource, deleteHtmlLoading] = useDeleteHtmlSource();
- const [deleteMediaSource, deleteMediaLoading] = useDeleteMediaSource();
- const [createHtmlSource, createHtmlLoading] = useCreateHtmlSource();
- const [createMediaSource, createMediaLoading] = useCreateMediaSource();
- const [getRenderingEngine, renderingEngineLoading] = useRenderingEngine();
-
- const { locked } = useContext(GlobalContext);
-
- const memoizedProduction = useMemo(() => productionSetup, [productionSetup]);
-
- const isAddButtonDisabled =
- (selectedValue !== 'HTML' && selectedValue !== 'Media Player') || locked;
-
- useEffect(() => {
- refreshPipelines();
- refreshControlPanels();
- }, [productionSetup?.isActive]);
-
- const setSelectedControlPanel = (controlPanel: string[]) => {
- setProductionSetup((prevState) => {
- if (!prevState) return;
- putProduction(prevState._id, {
- ...prevState,
- production_settings: {
- ...prevState?.production_settings,
- control_connection: {
- ...prevState?.production_settings?.control_connection,
- control_panel_name: controlPanel
- }
- }
- });
- return {
- ...prevState,
- production_settings: {
- ...prevState?.production_settings,
- control_connection: {
- ...prevState?.production_settings?.control_connection,
- control_panel_name: controlPanel
- }
- }
- };
- });
- };
-
- const setSelectedPipelineName = (
- pipelineIndex: number,
- pipelineName?: string,
- id?: string
- ) => {
- const selectedPresetCopy = cloneDeep(selectedPreset);
- const foundPipeline = selectedPresetCopy?.pipelines[pipelineIndex];
- if (foundPipeline) {
- foundPipeline.outputs = [];
- foundPipeline.pipeline_name = pipelineName;
- }
- setSelectedPreset(selectedPresetCopy);
- setProductionSetup((prevState) => {
- const updatedPipelines = prevState?.production_settings.pipelines;
- if (!updatedPipelines) return;
- updatedPipelines[pipelineIndex].pipeline_name = pipelineName;
- updatedPipelines[pipelineIndex].pipeline_id = id;
- updatedPipelines[pipelineIndex].outputs = [];
- putProduction(prevState._id, {
- ...prevState,
- production_settings: {
- ...prevState?.production_settings,
- pipelines: updatedPipelines
- }
- });
- return {
- ...prevState,
- production_settings: {
- ...prevState?.production_settings,
- pipelines: updatedPipelines
- }
- };
- });
- };
-
- const refreshProduction = () => {
- getProduction(params.id).then((config) => {
- // check if production has pipelines in use or control panels in use, if so update production
- const production = config.isActive
- ? config
- : checkProductionPipelines(config, pipelines);
-
- putProduction(production._id, production);
- setProductionSetup(production);
- setConfigurationName(production.name);
- setSelectedPreset(production.production_settings);
- getPresets().then((presets) => {
- if (!production.production_settings) {
- setPresets(presets);
- } else {
- const presetsExludingProductionSettings = presets.filter(
- (preset) => preset._id !== production?.production_settings._id
- );
- setPresets([
- ...presetsExludingProductionSettings,
- production.production_settings
- ]);
- }
- });
- });
- };
-
- useEffect(() => {
- refreshProduction();
- }, []);
-
- useEffect(() => {
- if (selectedValue === t('production.source')) {
- setInventoryVisible(true);
- }
- }, [selectedValue]);
-
- const updatePreset = (preset: Preset) => {
- if (!productionSetup?._id) return;
-
- const presetMultiviews = preset.pipelines[0].multiviews;
- const productionMultiviews =
- productionSetup.production_settings.pipelines[0].multiviews;
-
- const updatedPreset = {
- ...productionSetup,
- production_settings: {
- ...preset,
- control_connection: {
- ...preset.control_connection,
- control_panel_name:
- productionSetup.production_settings.control_connection
- .control_panel_name
- },
- pipelines: preset.pipelines.map((p, i) => {
- return {
- ...p,
- pipeline_name:
- productionSetup.production_settings.pipelines[i].pipeline_name
- };
- })
- }
- };
-
- if (productionSetup.isActive && presetMultiviews && productionMultiviews) {
- const productionMultiviewsMap = new Map(
- productionMultiviews.map((item) => [item.multiview_id, item])
- );
- const presetMultiviewsMap = new Map(
- presetMultiviews.map((item) => [item.multiview_id, item])
- );
-
- const additions: MultiviewSettings[] = [];
- const updates: MultiviewSettings[] = [];
-
- presetMultiviews.forEach((newItem) => {
- const oldItem = productionMultiviewsMap.get(newItem.multiview_id);
-
- if (!oldItem) {
- additions.push(newItem);
- } else if (JSON.stringify(oldItem) !== JSON.stringify(newItem)) {
- updates.push(newItem);
- }
- });
-
- const removals = productionMultiviews.filter(
- (oldItem) => !presetMultiviewsMap.has(oldItem.multiview_id)
- );
-
- if (additions.length > 0) {
- addMultiviewersOnRunningProduction(
- (productionSetup?._id.toString(), updatedPreset),
- additions
- );
- }
-
- if (updates.length > 0) {
- updateMultiviewersOnRunningProduction(
- (productionSetup?._id.toString(), updatedPreset),
- updates
- );
- }
-
- if (removals.length > 0) {
- removeMultiviewersOnRunningProduction(
- (productionSetup?._id.toString(), updatedPreset),
- removals
- );
- }
- }
-
- putProduction(productionSetup?._id.toString(), updatedPreset).then(() => {
- refreshProduction();
- });
- };
-
- const updateProduction = (id: string, productionSetup: Production) => {
- setProductionSetup(productionSetup);
- putProduction(id, productionSetup);
- };
-
- const handleSetPipelineSourceSettings = async (
- source: ISource,
- sourceId: number,
- data: {
- pipeline_uuid: string;
- stream_uuid: string;
- alignment: number;
- latency: number;
- }[],
- shouldRestart?: boolean,
- streamUuids?: string[]
- ) => {
- if (
- productionSetup?._id &&
- source?.ingest_name &&
- source?.ingest_source_name
- ) {
- data.forEach(({ pipeline_uuid, stream_uuid, alignment, latency }) => {
- putProductionPipelineSourceAlignmentAndLatency(
- productionSetup._id,
- pipeline_uuid,
- source.ingest_name,
- source.ingest_source_name,
- alignment,
- latency
- ).then(() => refreshProduction());
-
- if (productionSetup.isActive) {
- updateStream(stream_uuid, alignment);
- }
-
- const updatedProduction = {
- ...productionSetup,
- productionSettings: {
- ...productionSetup.production_settings,
- pipelines: productionSetup.production_settings.pipelines.map(
- (pipeline) => {
- if (pipeline.pipeline_id === pipeline_uuid) {
- pipeline.sources?.map((source) => {
- if (source.source_id === sourceId) {
- source.settings.alignment_ms = alignment;
- source.settings.max_network_latency_ms = latency;
- }
- });
- }
- return pipeline;
- }
- )
- }
- };
-
- setProductionSetup(updatedProduction);
- });
- }
-
- if (shouldRestart && productionSetup && streamUuids) {
- const sourceToDeleteFrom = productionSetup.sources.find((source) =>
- source.stream_uuids?.includes(streamUuids[0])
- );
- deleteStream(streamUuids, productionSetup, sourceId)
- .then(() => {
- delete sourceToDeleteFrom?.stream_uuids;
- })
- .then(() =>
- setTimeout(async () => {
- const result = await createStream(
- source,
- productionSetup,
- source.input_slot
- );
- if (result.ok) {
- if (result.value.success) {
- const newStreamUuids = result.value.streams.map(
- (r) => r.stream_uuid
- );
- if (sourceToDeleteFrom?._id) {
- replaceProductionSourceStreamIds(
- productionSetup._id,
- sourceToDeleteFrom?._id,
- newStreamUuids
- ).then(() => refreshProduction());
- }
- }
- }
- }, 1500)
- );
- }
- };
-
- const updateMultiview = (
- source: SourceReference,
- updatedSetup: Production
- ) => {
- const pipeline = updatedSetup.production_settings.pipelines[0];
-
- pipeline.multiviews?.map((singleMultiview) => {
- if (
- pipeline.pipeline_id &&
- pipeline.multiviews &&
- singleMultiview.multiview_id
- ) {
- updateMultiviewViews(
- pipeline.pipeline_id,
- updatedSetup,
- source,
- singleMultiview
- );
- }
- });
- };
-
- const updateSource = (
- source: SourceReference,
- productionSetup: Production
- ) => {
- const updatedSetup = updateSetupItem(source, productionSetup);
- setProductionSetup(updatedSetup);
- putProduction(updatedSetup._id.toString(), updatedSetup);
- updateMultiview(source, updatedSetup);
- };
-
- const updateConfigName = (nameChange: string) => {
- if (productionSetup?.name === nameChange) {
- return;
- }
- setConfigurationName(nameChange);
- const updatedSetup = {
- ...productionSetup,
- name: nameChange
- } as Production;
- setProductionSetup(updatedSetup);
- putProduction(updatedSetup._id.toString(), updatedSetup);
- };
-
- async function updateSelectedPreset(preset?: Preset) {
- if (!preset && productionSetup?._id) {
- getPresets().then((presets) => {
- setPresets(presets);
- });
- setSelectedPreset(undefined);
- return;
- }
- if (!preset?.default_multiview_reference) {
- toast.error(t('production.missing_multiview'));
- return;
- }
- const defaultMultiview = await getMultiviewLayout(
- preset?.default_multiview_reference
- );
- setSelectedPreset(preset);
-
- const multiview = {
- layout: defaultMultiview.layout,
- output: defaultMultiview.output,
- name: defaultMultiview.name,
- for_pipeline_idx: 0
- };
- setIsPresetDropdownHidden(true);
- let controlPanelName: string[] = [];
- if (
- productionSetup?.production_settings &&
- productionSetup?.production_settings.control_connection.control_panel_name
- ) {
- // Keep the control panels name array from the current production setup
- controlPanelName =
- productionSetup.production_settings.control_connection
- .control_panel_name!;
- }
-
- const updatedSetup = {
- ...productionSetup,
- production_settings: {
- _id: preset._id,
- name: preset.name,
- control_connection: {
- control_panel_endpoint:
- preset.control_connection.control_panel_endpoint,
- pipeline_control_connections:
- preset.control_connection.pipeline_control_connections,
- control_panel_name: controlPanelName
- },
- pipelines: preset.pipelines
- }
- } as Production;
- updatedSetup.production_settings.pipelines[0].multiviews = [multiview];
- setProductionSetup(updatedSetup);
- }
-
- function addPresetComponent(preset: Preset, index: number) {
- const id = `${preset.name}-${index}-id`;
- return (
- {
- updateSelectedPreset(preset);
- }}
- >
-
-
-
-
- );
- }
-
- const addSourceAction = async (source: SourceWithId) => {
- if (productionSetup && productionSetup.isActive) {
- setSelectedSource(source);
- setAddSourceModal(true);
- } else if (productionSetup) {
- const input: SourceReference = {
- _id: source._id.toString(),
- type: 'ingest_source',
- label: source.ingest_source_name,
- input_slot: firstEmptySlot(productionSetup)
- };
-
- let updatedSetup = addSetupItem(input, productionSetup);
-
- if (!updatedSetup) return;
-
- updatedSetup = await updatePipelinesWithSource(updatedSetup, source);
-
- setProductionSetup(updatedSetup);
- await putProduction(updatedSetup._id.toString(), updatedSetup);
-
- setAddSourceModal(false);
- setSelectedSource(undefined);
- }
- };
-
- const updatePipelinesWithSource = async (
- productionSetup: Production,
- source: SourceWithId
- ): Promise => {
- const updatedPipelines = await Promise.all(
- productionSetup.production_settings.pipelines.map(async (pipeline) => {
- const newSource = {
- source_id: await getIngestSourceId(
- source.ingest_name,
- source.ingest_source_name
- ),
- settings: {
- alignment_ms: pipeline.alignment_ms,
- max_network_latency_ms: pipeline.max_network_latency_ms
- }
- };
-
- const exists = pipeline.sources?.some(
- (s) => s.source_id === newSource.source_id
- );
-
- const updatedSources = exists
- ? pipeline.sources
- : [...(pipeline.sources || []), newSource];
-
- return {
- ...pipeline,
- sources: updatedSources
- };
- })
- );
-
- return {
- ...productionSetup,
- production_settings: {
- ...productionSetup.production_settings,
- pipelines: updatedPipelines
- }
- };
- };
-
- const addHtmlSource = (height: number, width: number, url: string) => {
- if (productionSetup) {
- const sourceToAdd: SourceReference = {
- type: 'html',
- label: `HTML ${firstEmptySlot(productionSetup)}`,
- input_slot: firstEmptySlot(productionSetup),
- html_data: {
- height: height,
- url: url,
- width: width
- }
- };
- const updatedSetup = addSetupItem(sourceToAdd, productionSetup);
- if (!updatedSetup) return;
- setProductionSetup(updatedSetup);
- putProduction(updatedSetup._id.toString(), updatedSetup).then(() => {
- refreshProduction();
- });
-
- if (productionSetup?.isActive && sourceToAdd.html_data) {
- createHtmlSource(
- productionSetup,
- sourceToAdd.input_slot,
- sourceToAdd.html_data,
- sourceToAdd
- );
- }
- }
- };
-
- const addMediaSource = (filename: string) => {
- if (productionSetup) {
- const sourceToAdd: SourceReference = {
- type: 'mediaplayer',
- label: `Media Player ${firstEmptySlot(productionSetup)}`,
- input_slot: firstEmptySlot(productionSetup),
- media_data: {
- filename: filename
- }
- };
- const updatedSetup = addSetupItem(sourceToAdd, productionSetup);
- if (!updatedSetup) return;
- setProductionSetup(updatedSetup);
- putProduction(updatedSetup._id.toString(), updatedSetup).then(() => {
- refreshProduction();
- });
-
- if (productionSetup?.isActive && sourceToAdd.media_data) {
- createMediaSource(
- productionSetup,
- sourceToAdd.input_slot,
- sourceToAdd.media_data,
- sourceToAdd
- );
- }
- }
- };
-
- const isDisabledFunction = (source: SourceWithId): boolean => {
- return selectedProductionItems?.includes(source._id.toString());
- };
-
- const handleOpenModal = (type: 'html' | 'media') => {
- if (type === 'html') {
- setIsHtmlModalOpen(true);
- } else if (type === 'media') {
- setIsMediaModalOpen(true);
- }
- };
-
- const handleAddSource = async () => {
- setAddSourceStatus(undefined);
- if (
- productionSetup &&
- productionSetup.isActive &&
- selectedSource &&
- (Array.isArray(
- productionSetup?.production_settings.pipelines[0].multiviews
- )
- ? productionSetup.production_settings.pipelines[0].multiviews.some(
- (singleMultiview) => singleMultiview?.layout?.views
- )
- : false)
- ) {
- let updatedSetup = productionSetup;
-
- for (
- let i = 0;
- i < productionSetup.production_settings.pipelines.length;
- i++
- ) {
- const pipeline = productionSetup.production_settings.pipelines[i];
-
- if (!pipeline.sources) {
- pipeline.sources = [];
- }
-
- const newSource = {
- source_id: await getIngestSourceId(
- selectedSource.ingest_name,
- selectedSource.ingest_source_name
- ),
- settings: {
- alignment_ms: pipeline.alignment_ms,
- max_network_latency_ms: pipeline.max_network_latency_ms
- }
- };
-
- updatedSetup = {
- ...productionSetup,
- production_settings: {
- ...productionSetup.production_settings,
- pipelines: productionSetup.production_settings.pipelines.map(
- (p, index) => {
- if (index === i) {
- if (!p.sources) {
- p.sources = [];
- }
- p.sources.push(newSource);
- }
- return p;
- }
- )
- }
- } as Production;
- }
-
- const result = await createStream(
- selectedSource,
- updatedSetup,
- firstEmptySlot(productionSetup)
- );
- if (!result.ok) {
- if (!result.value) {
- setAddSourceStatus({
- success: false,
- steps: [{ step: 'add_stream', success: false }]
- });
- } else {
- setAddSourceStatus({
- success: false,
- steps: result.value.steps
- });
- }
- }
- if (result.ok) {
- if (result.value.success) {
- const sourceToAdd: SourceReference = {
- _id: result.value.streams[0].source_id,
- type: 'ingest_source',
- label: selectedSource.name,
- stream_uuids: result.value.streams.map((r) => r.stream_uuid),
- input_slot: firstEmptySlot(productionSetup)
- };
- const updatedSetup = addSetupItem(sourceToAdd, productionSetup);
- if (!updatedSetup) return;
- updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then(
- (result) => {
- if (!result) return;
- setProductionSetup(result);
- updateMultiview(sourceToAdd, result);
- refreshProduction();
- setAddSourceModal(false);
- setSelectedSource(undefined);
- }
- );
- setAddSourceStatus(undefined);
- } else {
- setAddSourceStatus({ success: false, steps: result.value.steps });
- }
- }
- }
- };
-
- const handleRemoveSource = async () => {
- if (productionSetup && productionSetup.isActive && selectedSourceRef) {
- const multiviews =
- productionSetup.production_settings.pipelines[0].multiviews;
-
- if (!multiviews || multiviews.length === 0) return;
-
- const viewToUpdate = multiviews.some((multiview) =>
- multiview.layout.views.find(
- (v) => v.input_slot === selectedSourceRef.input_slot
- )
- );
-
- if (
- selectedSourceRef.stream_uuids &&
- selectedSourceRef.stream_uuids.length > 0
- ) {
- if (!viewToUpdate) {
- if (!productionSetup.production_settings.pipelines[0].pipeline_id)
- return;
-
- const result = await deleteStream(
- selectedSourceRef.stream_uuids,
- productionSetup,
- selectedSourceRef.input_slot
- );
-
- if (!result.ok) {
- if (!result.value) {
- setDeleteSourceStatus({
- success: false,
- steps: [{ step: 'unexpected', success: false }]
- });
- } else {
- setDeleteSourceStatus({ success: false, steps: result.value });
- const didDeleteStream = result.value.some(
- (step) => step.step === 'delete_stream' && step.success
- );
- if (didDeleteStream) {
- const updatedSetup = removeSetupItem(
- selectedSourceRef,
- productionSetup
- );
- if (!updatedSetup) return;
- updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then(
- (result) => {
- if (!result) return;
- setProductionSetup(updatedSetup);
- updateMultiview(selectedSourceRef, result);
- setSelectedSourceRef(undefined);
- }
- );
- return;
- }
- }
- return;
- }
-
- const updatedSetup = removeSetupItem(
- selectedSourceRef,
- productionSetup
- );
-
- if (!updatedSetup) return;
-
- updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then(
- (result) => {
- if (!result) return;
- setProductionSetup(updatedSetup);
- updateMultiview(selectedSourceRef, result);
- setRemoveSourceModal(false);
- setSelectedSourceRef(undefined);
- }
- );
- return;
- }
-
- const result = await deleteStream(
- selectedSourceRef.stream_uuids,
- productionSetup,
- selectedSourceRef.input_slot
- );
-
- if (!result.ok) {
- if (!result.value) {
- setDeleteSourceStatus({
- success: false,
- steps: [{ step: 'unexpected', success: false }]
- });
- } else {
- setDeleteSourceStatus({ success: false, steps: result.value });
- const didDeleteStream = result.value.some(
- (step) => step.step === 'delete_stream' && step.success
- );
- if (didDeleteStream) {
- const updatedSetup = removeSetupItem(
- selectedSourceRef,
- productionSetup
- );
- if (!updatedSetup) return;
- updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then(
- (result) => {
- if (!result) return;
- setProductionSetup(result);
- updateMultiview(selectedSourceRef, result);
- }
- );
- return;
- }
- }
- return;
- }
- }
-
- if (
- selectedSourceRef.type === 'html' ||
- selectedSourceRef.type === 'mediaplayer'
- ) {
- for (
- let i = 0;
- i < productionSetup.production_settings.pipelines.length;
- i++
- ) {
- const pipelineId =
- productionSetup.production_settings.pipelines[i].pipeline_id;
- if (pipelineId) {
- getRenderingEngine(pipelineId);
- if (selectedSourceRef.type === 'html') {
- await deleteHtmlSource(
- pipelineId,
- selectedSourceRef.input_slot,
- productionSetup
- );
- } else if (selectedSourceRef.type === 'mediaplayer') {
- await deleteMediaSource(
- pipelineId,
- selectedSourceRef.input_slot,
- productionSetup
- );
- }
- }
- }
- }
-
- const updatedSetup = removeSetupItem(selectedSourceRef, productionSetup);
-
- if (!updatedSetup) return;
- updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then((result) => {
- if (!result) return;
- setProductionSetup(result);
- updateMultiview(selectedSourceRef, result);
- setRemoveSourceModal(false);
- setSelectedSourceRef(undefined);
- });
- }
- };
-
- const handleAbortAddSource = () => {
- setAddSourceStatus(undefined);
- setAddSourceModal(false);
- setSelectedSource(undefined);
- };
-
- const handleAbortRemoveSource = () => {
- setRemoveSourceModal(false);
- setSelectedSource(undefined);
- setDeleteSourceStatus(undefined);
- };
-
- const hasSelectedPipelines = () => {
- if (!productionSetup?.production_settings?.pipelines?.length) return false;
- let allPipesHaveName = true;
- productionSetup.production_settings.pipelines.forEach((p) => {
- if (!p.pipeline_name) allPipesHaveName = false;
- });
- return allPipesHaveName;
- };
-
- return (
- <>
-
- {
- setConfigurationName(e.target.value);
- }}
- onKeyDown={(e: KeyboardEvent) => {
- if (e.key.includes('Enter')) {
- e.currentTarget.blur();
- }
- }}
- onBlur={() => updateConfigName(configurationName)}
- disabled={locked}
- />
-
-
{
- updateSelectedPreset(undefined);
- }}
- >
- {presets &&
- presets.map((item, index) => {
- return addPresetComponent(item, index);
- })}
-
-
-
-
-
-
-
-
-
setInventoryVisible(false)}
- isDisabledFunc={isDisabledFunction}
- locked={locked}
- />
- {addSourceModal && (selectedSource || selectedSourceRef) && (
-
- )}
-
-
-
- {productionSetup?.sources && sources.size > 0 && (
-
- {
- updateProduction(productionSetup._id, updated);
- }}
- onSourceUpdate={(source: SourceReference) => {
- updateSource(source, productionSetup);
- }}
- onSourceRemoval={async (
- source: SourceReference,
- ingestSource?: ISource
- ) => {
- if (productionSetup && productionSetup.isActive) {
- setSelectedSourceRef(source);
- setRemoveSourceModal(true);
- } else if (productionSetup) {
- const ingestSourceId = ingestSource
- ? await getIngestSourceId(
- ingestSource.ingest_name,
- ingestSource.ingest_source_name
- )
- : undefined;
- const updatedSetup = removeSetupItem(
- {
- _id: source._id,
- type: source.type,
- label: source.label,
- input_slot: source.input_slot
- },
- productionSetup,
- ingestSourceId
- );
- if (!updatedSetup) return;
- setProductionSetup(updatedSetup);
- putProduction(
- updatedSetup._id.toString(),
- updatedSetup
- ).then(() => {
- setRemoveSourceModal(false);
- setSelectedSourceRef(undefined);
- });
- }
- }}
- loading={loading}
- />
- {removeSourceModal && selectedSourceRef && (
-
- )}
-
- )}
-
-
setInventoryVisible(true)}
- disabled={
- productionSetup?.production_settings === undefined ||
- productionSetup.production_settings === null ||
- locked
- }
- />
-
-
-
-
-
- {productionSetup?.production_settings &&
- productionSetup?.production_settings.pipelines.map(
- (pipeline, i) => {
- return (
-
({
- option: pipeline.name,
- id: pipeline.uuid,
- available: pipeline.streams.length === 0
- }))}
- pipelineIndex={i}
- initial={pipeline.pipeline_name}
- setSelectedPipelineName={setSelectedPipelineName}
- />
- );
- }
- )}
- {productionSetup?.production_settings && (
- ({
- option: controlPanel.name,
- available: controlPanel.outgoing_connections?.length === 0
- }))}
- initial={
- productionSetup?.production_settings?.control_connection
- .control_panel_name
- }
- setSelectedControlPanel={setSelectedControlPanel}
- />
- )}
-
-
- {productionSetup && productionSetup.isActive && (
-
-
-
- )}
-
-
setIsHtmlModalOpen(false)}
- onConfirm={addHtmlSource}
- loading={createHtmlLoading}
- />
- setIsMediaModalOpen(false)}
- onConfirm={addMediaSource}
- loading={createMediaLoading}
- />
-
- >
- );
+export default function Page({ params }: { params: { id: string } }) {
+ const { id } = params;
+ return ;
}
diff --git a/src/components/createProduction/CreateProduction.tsx b/src/components/createProduction/CreateProduction.tsx
index e55faa00..6df5c2cf 100644
--- a/src/components/createProduction/CreateProduction.tsx
+++ b/src/components/createProduction/CreateProduction.tsx
@@ -2,12 +2,15 @@
import { useRouter } from 'next/navigation';
import { Button } from '../button/Button';
-import { LegacyRef, useCallback, useRef, useState, KeyboardEvent } from 'react';
+import { LegacyRef, useCallback, useRef, useState } from 'react';
import { IconServerCog, IconPlus } from '@tabler/icons-react';
-import Link from 'next/link';
import { useTranslate } from '../../i18n/useTranslate';
import { usePostProduction } from '../../hooks/productions';
import { refresh } from '../../utils/refresh';
+import { PresetDropdown } from '../startProduction/presetDropdown';
+import { Preset, PresetWithId } from '../../interfaces/preset';
+import { useGetMultiviewLayout } from '../../hooks/multiviewLayout';
+import { MultiviewSettings } from '../../interfaces/multiview';
export function CreateProduction() {
const router = useRouter();
@@ -15,25 +18,37 @@ export function CreateProduction() {
const inputRef: LegacyRef = useRef(null);
const [showing, setShowing] = useState(false);
+ const [selectedPreset, setSelectedPreset] = useState();
+ const getMultiviewLayout = useGetMultiviewLayout();
const t = useTranslate();
const handleCreateNew = useCallback(async () => {
const name = inputRef.current?.value;
- if (!name) {
+ if (!name || !selectedPreset) {
return;
}
- const insertedId = await postProduction(name);
+ let defaultMultiview: MultiviewSettings | undefined;
+ if (selectedPreset.default_multiview_reference) {
+ const defaultMv = await getMultiviewLayout(
+ selectedPreset.default_multiview_reference
+ );
+ defaultMultiview = {
+ layout: defaultMv.layout,
+ output: defaultMv.output,
+ name: defaultMv.name,
+ for_pipeline_idx: 0
+ };
+ }
+ const insertedId = await postProduction(
+ name,
+ selectedPreset,
+ defaultMultiview
+ );
refresh('/');
router.push(`/production/${insertedId}`);
}, [router, postProduction]);
- const handleKeyDown = (event: KeyboardEvent) => {
- if (event.key === 'Enter') {
- handleCreateNew();
- }
- };
-
const handleOpen = useCallback(
() => setShowing((showing: boolean) => !showing),
[]
@@ -84,10 +99,14 @@ export function CreateProduction() {
}
}}
required
- onKeyDown={handleKeyDown}
/>
+