@norskvideo/norsk-studio-built-ins 1.19.0 → 1.26.0-2025-02-19-e81e84b9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/info.js +5983 -699
- package/client/output.preview/styles.css +69 -69
- package/client/output.whep/styles.css +9 -0
- package/client/style.css +283 -856
- package/lib/info.js +14 -14
- package/lib/info.js.map +1 -1
- package/lib/input.rtmp/info.js +19 -5
- package/lib/input.rtmp/info.js.map +1 -1
- package/lib/input.rtmp/inline-view.js +3 -3
- package/lib/input.rtmp/runtime.d.ts +46 -7
- package/lib/input.rtmp/runtime.js +245 -59
- package/lib/input.rtmp/runtime.js.map +1 -1
- package/lib/input.rtmp/summary-view.d.ts +3 -5
- package/lib/input.rtmp/summary-view.js +51 -6
- package/lib/input.rtmp/summary-view.js.map +1 -1
- package/lib/input.rtmp/types.d.ts +193 -0
- package/lib/{processor.browserOverlay/openApi.js → input.rtmp/types.js} +1 -1
- package/lib/input.rtmp/types.js.map +1 -0
- package/lib/input.rtmp/types.yaml +147 -0
- package/lib/input.silence/info.js +9 -2
- package/lib/input.silence/info.js.map +1 -1
- package/lib/input.silence/runtime.d.ts +1 -0
- package/lib/input.silence/runtime.js.map +1 -1
- package/lib/input.srt-caller/info.js +8 -1
- package/lib/input.srt-caller/info.js.map +1 -1
- package/lib/input.srt-caller/runtime.d.ts +1 -0
- package/lib/input.srt-caller/runtime.js.map +1 -1
- package/lib/input.srt-listener/info.js +15 -2
- package/lib/input.srt-listener/info.js.map +1 -1
- package/lib/input.srt-listener/runtime.d.ts +41 -5
- package/lib/input.srt-listener/runtime.js +236 -57
- package/lib/input.srt-listener/runtime.js.map +1 -1
- package/lib/input.srt-listener/summary-view.d.ts +3 -5
- package/lib/input.srt-listener/summary-view.js +45 -6
- package/lib/input.srt-listener/summary-view.js.map +1 -1
- package/lib/input.srt-listener/types.d.ts +193 -0
- package/lib/input.srt-listener/types.js +3 -0
- package/lib/input.srt-listener/types.js.map +1 -0
- package/lib/input.srt-listener/types.yaml +145 -0
- package/lib/input.udp-ts/info.js +2 -1
- package/lib/input.udp-ts/info.js.map +1 -1
- package/lib/input.udp-ts/runtime.d.ts +1 -0
- package/lib/input.udp-ts/runtime.js.map +1 -1
- package/lib/input.videoTestCard/info.d.ts +1 -1
- package/lib/input.videoTestCard/info.js +17 -4
- package/lib/input.videoTestCard/info.js.map +1 -1
- package/lib/input.videoTestCard/runtime.d.ts +1 -0
- package/lib/output.autoCmaf/form-views.d.ts +2 -2
- package/lib/output.autoCmaf/form-views.js +13 -3
- package/lib/output.autoCmaf/form-views.js.map +1 -1
- package/lib/output.autoCmaf/info.d.ts +21 -2
- package/lib/output.autoCmaf/info.js +106 -32
- package/lib/output.autoCmaf/info.js.map +1 -1
- package/lib/output.autoCmaf/inline-view.d.ts +6 -0
- package/lib/output.autoCmaf/inline-view.js +8 -0
- package/lib/output.autoCmaf/inline-view.js.map +1 -0
- package/lib/output.autoCmaf/runtime.d.ts +29 -43
- package/lib/output.autoCmaf/runtime.js +314 -96
- package/lib/output.autoCmaf/runtime.js.map +1 -1
- package/lib/output.autoCmaf/summary-view.d.ts +4 -0
- package/lib/output.autoCmaf/summary-view.js +24 -0
- package/lib/output.autoCmaf/summary-view.js.map +1 -0
- package/lib/output.autoCmaf/summary.js +3 -0
- package/lib/output.autoCmaf/summary.js.map +1 -1
- package/lib/output.autoCmaf/types.d.ts +158 -0
- package/lib/output.autoCmaf/types.js +3 -0
- package/lib/output.autoCmaf/types.js.map +1 -0
- package/lib/output.autoCmaf/types.yaml +182 -0
- package/lib/output.autoHls/info.d.ts +3 -0
- package/lib/output.autoHls/info.js +22 -0
- package/lib/output.autoHls/info.js.map +1 -0
- package/lib/output.autoHls/runtime.d.ts +6 -0
- package/lib/output.autoHls/runtime.js +13 -0
- package/lib/output.autoHls/runtime.js.map +1 -0
- package/lib/output.preview/info.d.ts +5 -2
- package/lib/output.preview/info.js +23 -3
- package/lib/output.preview/info.js.map +1 -1
- package/lib/output.preview/inline-view.d.ts +2 -4
- package/lib/output.preview/inline-view.js +65 -7
- package/lib/output.preview/inline-view.js.map +1 -1
- package/lib/output.preview/runtime.d.ts +18 -12
- package/lib/output.preview/runtime.js +122 -33
- package/lib/output.preview/runtime.js.map +1 -1
- package/lib/output.preview/types.d.ts +56 -0
- package/lib/output.preview/types.js +3 -0
- package/lib/output.preview/types.js.map +1 -0
- package/lib/output.preview/types.yaml +37 -0
- package/lib/output.rtmp/info.js +14 -2
- package/lib/output.rtmp/info.js.map +1 -1
- package/lib/output.rtmp/inline-view.js +5 -2
- package/lib/output.rtmp/inline-view.js.map +1 -1
- package/lib/output.rtmp/runtime.d.ts +31 -5
- package/lib/output.rtmp/runtime.js +169 -34
- package/lib/output.rtmp/runtime.js.map +1 -1
- package/lib/output.rtmp/summary-view.d.ts +4 -0
- package/lib/output.rtmp/summary-view.js +24 -0
- package/lib/output.rtmp/summary-view.js.map +1 -0
- package/lib/output.rtmp/types.d.ts +101 -0
- package/lib/output.rtmp/types.js +3 -0
- package/lib/output.rtmp/types.js.map +1 -0
- package/lib/output.rtmp/types.yaml +30 -0
- package/lib/output.srt/info.d.ts +2 -2
- package/lib/output.srt/info.js +29 -1
- package/lib/output.srt/info.js.map +1 -1
- package/lib/output.srt/inline-view.d.ts +6 -0
- package/lib/output.srt/inline-view.js +8 -0
- package/lib/output.srt/inline-view.js.map +1 -0
- package/lib/output.srt/runtime.d.ts +35 -5
- package/lib/output.srt/runtime.js +174 -6
- package/lib/output.srt/runtime.js.map +1 -1
- package/lib/output.srt/summary-view.d.ts +4 -0
- package/lib/output.srt/summary-view.js +24 -0
- package/lib/output.srt/summary-view.js.map +1 -0
- package/lib/output.srt/types.d.ts +101 -0
- package/lib/output.srt/types.js +3 -0
- package/lib/output.srt/types.js.map +1 -0
- package/lib/output.srt/types.yaml +31 -0
- package/lib/output.statistics/info.js +3 -1
- package/lib/output.statistics/info.js.map +1 -1
- package/lib/output.statistics/inline-view.js +3 -2
- package/lib/output.statistics/inline-view.js.map +1 -1
- package/lib/output.statistics/runtime.d.ts +1 -0
- package/lib/output.statistics/runtime.js.map +1 -1
- package/lib/output.udpTs/info.js +4 -0
- package/lib/output.udpTs/info.js.map +1 -1
- package/lib/output.udpTs/runtime.d.ts +1 -0
- package/lib/output.udpTs/runtime.js.map +1 -1
- package/lib/output.whep/info.d.ts +2 -2
- package/lib/output.whep/info.js +61 -1
- package/lib/output.whep/info.js.map +1 -1
- package/lib/output.whep/inline-view.d.ts +4 -0
- package/lib/output.whep/inline-view.js +57 -0
- package/lib/output.whep/inline-view.js.map +1 -0
- package/lib/output.whep/runtime.d.ts +39 -5
- package/lib/output.whep/runtime.js +175 -13
- package/lib/output.whep/runtime.js.map +1 -1
- package/lib/output.whep/summary-view.d.ts +4 -0
- package/lib/output.whep/summary-view.js +24 -0
- package/lib/output.whep/summary-view.js.map +1 -0
- package/lib/output.whep/types.d.ts +101 -0
- package/lib/output.whep/types.js +3 -0
- package/lib/output.whep/types.js.map +1 -0
- package/lib/output.whep/types.yaml +30 -0
- package/lib/processor.audioEncoder/form-views.d.ts +8 -0
- package/lib/processor.audioEncoder/form-views.js +8 -0
- package/lib/processor.audioEncoder/form-views.js.map +1 -0
- package/lib/processor.audioEncoder/info.d.ts +3 -0
- package/lib/processor.audioEncoder/info.js +160 -0
- package/lib/processor.audioEncoder/info.js.map +1 -0
- package/lib/processor.audioEncoder/runtime.d.ts +21 -0
- package/lib/processor.audioEncoder/runtime.js +20 -0
- package/lib/processor.audioEncoder/runtime.js.map +1 -0
- package/lib/processor.browserOverlay/info.js +1 -0
- package/lib/processor.browserOverlay/info.js.map +1 -1
- package/lib/processor.browserOverlay/runtime.d.ts +1 -0
- package/lib/processor.browserOverlay/runtime.js +6 -26
- package/lib/processor.browserOverlay/runtime.js.map +1 -1
- package/lib/processor.browserOverlay/types.js +3 -0
- package/lib/processor.browserOverlay/types.js.map +1 -0
- package/lib/processor.cascadingSwitch/info.js +3 -2
- package/lib/processor.cascadingSwitch/info.js.map +1 -1
- package/lib/processor.cascadingSwitch/inline-view.js +3 -3
- package/lib/processor.cascadingSwitch/runtime.d.ts +1 -0
- package/lib/processor.cascadingSwitch/runtime.js +2 -2
- package/lib/processor.cascadingSwitch/runtime.js.map +1 -1
- package/lib/processor.fixedLadder/codec-editor.js +1 -1
- package/lib/processor.fixedLadder/codec-editor.js.map +1 -1
- package/lib/processor.fixedLadder/codec-view.d.ts +1 -1
- package/lib/processor.fixedLadder/codec-view.js +1 -1
- package/lib/processor.fixedLadder/codec-view.js.map +1 -1
- package/lib/processor.fixedLadder/info.d.ts +33 -7
- package/lib/processor.fixedLadder/info.js +19 -6
- package/lib/processor.fixedLadder/info.js.map +1 -1
- package/lib/processor.fixedLadder/runtime.d.ts +16 -29
- package/lib/processor.fixedLadder/runtime.js +16 -1
- package/lib/processor.fixedLadder/runtime.js.map +1 -1
- package/lib/processor.fixedLadder/types.d.ts +240 -0
- package/lib/processor.fixedLadder/types.js +3 -0
- package/lib/processor.fixedLadder/types.js.map +1 -0
- package/lib/processor.fixedLadder/types.yaml +541 -0
- package/lib/processor.onscreenGraphic/info.js +11 -5
- package/lib/processor.onscreenGraphic/info.js.map +1 -1
- package/lib/processor.onscreenGraphic/runtime.d.ts +27 -2
- package/lib/processor.onscreenGraphic/runtime.js +116 -52
- package/lib/processor.onscreenGraphic/runtime.js.map +1 -1
- package/lib/processor.onscreenGraphic/summary-view.d.ts +1 -1
- package/lib/processor.onscreenGraphic/summary-view.js +235 -11
- package/lib/processor.onscreenGraphic/summary-view.js.map +1 -1
- package/lib/processor.onscreenGraphic/types.d.ts +15 -1
- package/lib/processor.onscreenGraphic/types.yaml +63 -14
- package/lib/processor.streamKeyOverride/info.d.ts +3 -0
- package/lib/processor.streamKeyOverride/info.js +151 -0
- package/lib/processor.streamKeyOverride/info.js.map +1 -0
- package/lib/processor.streamKeyOverride/runtime.d.ts +55 -0
- package/lib/processor.streamKeyOverride/runtime.js +226 -0
- package/lib/processor.streamKeyOverride/runtime.js.map +1 -0
- package/lib/shared/api.d.ts +35 -0
- package/lib/shared/api.js +34 -0
- package/lib/shared/api.js.map +1 -0
- package/lib/test/auto-cmaf.js +219 -43
- package/lib/test/auto-cmaf.js.map +1 -1
- package/lib/test/onscreen-graphic.js +79 -42
- package/lib/test/onscreen-graphic.js.map +1 -1
- package/lib/test/preview.js +69 -64
- package/lib/test/preview.js.map +1 -1
- package/lib/test/rtmp-input.js +6 -3
- package/lib/test/rtmp-input.js.map +1 -1
- package/lib/test/rtmp-output.js +105 -1
- package/lib/test/rtmp-output.js.map +1 -1
- package/lib/test/srt-input-listener.js +331 -0
- package/lib/test/srt-input-listener.js.map +1 -1
- package/lib/test/srt-output.js +93 -4
- package/lib/test/srt-output.js.map +1 -1
- package/lib/test/whep-output.js +192 -5
- package/lib/test/whep-output.js.map +1 -1
- package/lib/util.stats.latency/info.js +1 -0
- package/lib/util.stats.latency/info.js.map +1 -1
- package/lib/util.stats.latency/runtime.d.ts +1 -0
- package/lib/util.stats.latency/runtime.js +2 -2
- package/lib/util.stats.latency/runtime.js.map +1 -1
- package/package.json +4 -4
- package/lib/processor.browserOverlay/openApi.js.map +0 -1
- package/lib/processor.whisper-transcribe/info.d.ts +0 -3
- package/lib/processor.whisper-transcribe/info.js +0 -44
- package/lib/processor.whisper-transcribe/info.js.map +0 -1
- package/lib/processor.whisper-transcribe/runtime.d.ts +0 -29
- package/lib/processor.whisper-transcribe/runtime.js +0 -83
- package/lib/processor.whisper-transcribe/runtime.js.map +0 -1
- package/lib/util.stats.ma35d/info.d.ts +0 -3
- package/lib/util.stats.ma35d/info.js +0 -79
- package/lib/util.stats.ma35d/info.js.map +0 -1
- package/lib/util.stats.ma35d/inline-view.d.ts +0 -6
- package/lib/util.stats.ma35d/inline-view.js +0 -76
- package/lib/util.stats.ma35d/inline-view.js.map +0 -1
- package/lib/util.stats.ma35d/runtime.d.ts +0 -21
- package/lib/util.stats.ma35d/runtime.js +0 -49
- package/lib/util.stats.ma35d/runtime.js.map +0 -1
- package/lib/util.timestamps/info.d.ts +0 -5
- package/lib/util.timestamps/info.js +0 -87
- package/lib/util.timestamps/info.js.map +0 -1
- package/lib/util.timestamps/inline-view.d.ts +0 -6
- package/lib/util.timestamps/inline-view.js +0 -94
- package/lib/util.timestamps/inline-view.js.map +0 -1
- package/lib/util.timestamps/runtime.d.ts +0 -29
- package/lib/util.timestamps/runtime.js +0 -40
- package/lib/util.timestamps/runtime.js.map +0 -1
- /package/lib/processor.browserOverlay/{openApi.d.ts → types.d.ts} +0 -0
@@ -1,52 +1,35 @@
|
|
1
|
-
import { AdMarker, AutoProcessorMediaNode, CmafDestinationSettings, CmafMultiVariantOutputNode, Norsk, StreamKey } from '@norskvideo/norsk-sdk';
|
2
|
-
import { CreatedMediaNode, OnCreated, ServerComponentDefinition, StudioNodeSubscriptionSource, StudioRuntime } from '@norskvideo/norsk-studio/lib/extension/runtime-types';
|
1
|
+
import { AdMarker, AutoProcessorMediaNode, CmafDestinationSettings, CmafMultiVariantOutputNode, HlsTsMultiVariantOutputNode, Norsk, StreamKey } from '@norskvideo/norsk-sdk';
|
2
|
+
import { CreatedMediaNode, InstanceRouteInfo, OnCreated, ServerComponentDefinition, ServerComponentSchemas, StudioNodeSubscriptionSource, StudioRuntime } from '@norskvideo/norsk-studio/lib/extension/runtime-types';
|
3
3
|
import { CustomSinkNode } from '@norskvideo/norsk-studio/lib/extension/base-nodes';
|
4
|
-
import { ReportBuilder } from '@norskvideo/norsk-studio/lib/runtime/execution';
|
5
|
-
import { AxinomConfig, EzDrmConfig } from '@norskvideo/norsk-studio/lib/shared/config';
|
6
4
|
import { CryptoDetails } from '../shared/drm/cpix';
|
7
5
|
import { ContextPromiseControl } from '@norskvideo/norsk-studio/lib/runtime/util';
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
export type AutoCmafConfig =
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
s3Destinations: AutoCmafS3Destination[];
|
20
|
-
drmProvider?: 'ezdrm' | 'axinom';
|
21
|
-
__global: {
|
22
|
-
ezdrmConfig?: EzDrmConfig;
|
23
|
-
axinomConfig?: AxinomConfig;
|
24
|
-
};
|
25
|
-
};
|
26
|
-
export type AutoCmafSegment = {
|
27
|
-
retentionPeriod: number;
|
28
|
-
defaultSegmentCount?: number;
|
29
|
-
targetSegmentDuration: number;
|
30
|
-
targetPartDuration: number;
|
31
|
-
holdBackSegments?: number;
|
32
|
-
holdBackParts?: number;
|
33
|
-
};
|
34
|
-
export type CmafOutputState = {
|
6
|
+
import { components } from './types';
|
7
|
+
export type AutoCmafAkamaiDestinaton = components['schemas']['AutoCmafAkamaiDestination'];
|
8
|
+
export type AutoCmafS3Destination = components['schemas']['AutoCmafS3Destination'];
|
9
|
+
export type AutoCmafDestination = AutoCmafAkamaiDestinaton | AutoCmafS3Destination;
|
10
|
+
export type InitialState = components['schemas']['InitialState'];
|
11
|
+
export type AutoCmafConfig = components['schemas']['AutoCmafConfig'];
|
12
|
+
export type AutoCmafConfigExtended = components['schemas']['AutoCmafConfigExtended'];
|
13
|
+
export type AutoCmafSegment = components['schemas']['AutoCmafSegment'];
|
14
|
+
export type CmafOutputState = components['schemas']['CmafOutputState'];
|
15
|
+
export type CmafOutputEvent = {
|
16
|
+
type: 'url-published' | 'output-enabled' | 'output-disabled';
|
35
17
|
url?: string;
|
36
18
|
drmToken?: string;
|
37
19
|
};
|
38
|
-
export type
|
39
|
-
type: '
|
40
|
-
url: string;
|
41
|
-
drmToken?: string;
|
20
|
+
export type CmafOutputCommand = {
|
21
|
+
type: 'enable-output' | 'disable-output';
|
42
22
|
};
|
43
|
-
export type CmafOutputCommand = object;
|
44
23
|
export default class AutoCmafDefinition implements ServerComponentDefinition<AutoCmafConfig, AutoCmaf, CmafOutputState, CmafOutputCommand, CmafOutputEvent> {
|
45
|
-
create(norsk: Norsk, cfg: AutoCmafConfig, cb: OnCreated<AutoCmaf>,
|
24
|
+
create(norsk: Norsk, cfg: AutoCmafConfig, cb: OnCreated<AutoCmaf>, runtime: StudioRuntime<CmafOutputState, CmafOutputCommand, CmafOutputEvent>): Promise<void>;
|
25
|
+
createImpl(norsk: Norsk, cfg: AutoCmafConfigExtended, cb: OnCreated<AutoCmaf>, runtime: StudioRuntime<CmafOutputState, CmafOutputCommand, CmafOutputEvent>): Promise<void>;
|
26
|
+
handleCommand(node: AutoCmaf, command: CmafOutputCommand): Promise<void>;
|
27
|
+
instanceRoutes(): Promise<InstanceRouteInfo<AutoCmafConfig, AutoCmaf, CmafOutputState, CmafOutputCommand, CmafOutputEvent>[]>;
|
28
|
+
schemas(): Promise<ServerComponentSchemas>;
|
46
29
|
}
|
47
30
|
export declare class AutoCmaf extends CustomSinkNode {
|
48
31
|
norsk: Norsk;
|
49
|
-
cfg:
|
32
|
+
cfg: AutoCmafConfigExtended;
|
50
33
|
currentSources: Map<CreatedMediaNode, StudioNodeSubscriptionSource>;
|
51
34
|
currentMedia: {
|
52
35
|
node: AutoProcessorMediaNode<string>;
|
@@ -55,11 +38,11 @@ export declare class AutoCmaf extends CustomSinkNode {
|
|
55
38
|
}[];
|
56
39
|
crypto?: CryptoDetails;
|
57
40
|
control: ContextPromiseControl;
|
58
|
-
mv?: CmafMultiVariantOutputNode;
|
41
|
+
mv?: CmafMultiVariantOutputNode | HlsTsMultiVariantOutputNode;
|
59
42
|
defaultProgramNumber: number;
|
60
43
|
defaultSourceName: string;
|
61
44
|
currentMultiVariants: {
|
62
|
-
node?: CmafMultiVariantOutputNode;
|
45
|
+
node?: CmafMultiVariantOutputNode | HlsTsMultiVariantOutputNode;
|
63
46
|
programNumber: number;
|
64
47
|
sourceName: string;
|
65
48
|
}[];
|
@@ -67,10 +50,13 @@ export declare class AutoCmaf extends CustomSinkNode {
|
|
67
50
|
sessionId?: string;
|
68
51
|
destinations: CmafDestinationSettings[];
|
69
52
|
advertDestinations: string[];
|
70
|
-
|
71
|
-
|
72
|
-
|
53
|
+
runtime: StudioRuntime<CmafOutputState, CmafOutputCommand, CmafOutputEvent>;
|
54
|
+
enabled: boolean;
|
55
|
+
static create(norsk: Norsk, cfg: AutoCmafConfigExtended, runtime: StudioRuntime<CmafOutputState, CmafOutputCommand, CmafOutputEvent>): Promise<AutoCmaf>;
|
56
|
+
constructor(cfg: AutoCmafConfigExtended, norsk: Norsk, runtime: StudioRuntime<CmafOutputState, CmafOutputCommand, CmafOutputEvent>);
|
73
57
|
initialise(): Promise<void>;
|
58
|
+
enableOutput(): Promise<void>;
|
59
|
+
disableOutput(): Promise<void>;
|
74
60
|
handleContext(): Promise<void>;
|
75
61
|
subscribe(subs: StudioNodeSubscriptionSource[]): void;
|
76
62
|
}
|
@@ -1,4 +1,7 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
6
|
exports.AutoCmaf = void 0;
|
4
7
|
const norsk_sdk_1 = require("@norskvideo/norsk-sdk");
|
@@ -8,16 +11,112 @@ const logging_1 = require("@norskvideo/norsk-studio/lib/server/logging");
|
|
8
11
|
const ezdrm_1 = require("../shared/drm/ezdrm");
|
9
12
|
const axinom_1 = require("../shared/drm/axinom");
|
10
13
|
const util_2 = require("@norskvideo/norsk-studio/lib/runtime/util");
|
14
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
15
|
+
const json_refs_1 = require("json-refs");
|
16
|
+
const path_1 = __importDefault(require("path"));
|
17
|
+
const yaml_1 = __importDefault(require("yaml"));
|
18
|
+
function coreInfo(path, op) {
|
19
|
+
return {
|
20
|
+
url: path,
|
21
|
+
summary: op.summary,
|
22
|
+
description: op.description,
|
23
|
+
requestBody: op.requestBody,
|
24
|
+
responses: op.responses,
|
25
|
+
};
|
26
|
+
}
|
27
|
+
function post(path, paths) {
|
28
|
+
return {
|
29
|
+
...coreInfo(path, paths[path]['post']),
|
30
|
+
method: 'POST',
|
31
|
+
};
|
32
|
+
}
|
11
33
|
class AutoCmafDefinition {
|
12
|
-
async create(norsk, cfg, cb,
|
13
|
-
|
34
|
+
async create(norsk, cfg, cb, runtime) {
|
35
|
+
return this.createImpl(norsk, { mode: 'cmaf', ...cfg }, cb, runtime);
|
36
|
+
}
|
37
|
+
async createImpl(norsk, cfg, cb, runtime) {
|
38
|
+
const node = await AutoCmaf.create(norsk, cfg, runtime);
|
14
39
|
cb(node);
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
40
|
+
}
|
41
|
+
async handleCommand(node, command) {
|
42
|
+
switch (command.type) {
|
43
|
+
case 'enable-output':
|
44
|
+
await node.enableOutput();
|
45
|
+
break;
|
46
|
+
case 'disable-output':
|
47
|
+
await node.disableOutput();
|
48
|
+
break;
|
49
|
+
default:
|
50
|
+
(0, util_1.assertUnreachable)(command.type);
|
19
51
|
}
|
20
52
|
}
|
53
|
+
async instanceRoutes() {
|
54
|
+
const types = await promises_1.default.readFile(path_1.default.join(__dirname, 'types.yaml'));
|
55
|
+
const root = yaml_1.default.parse(types.toString());
|
56
|
+
const resolved = await (0, json_refs_1.resolveRefs)(root, {}).then((r) => r.resolved);
|
57
|
+
const paths = resolved.paths;
|
58
|
+
function waitFor(condition, finish) {
|
59
|
+
if (condition()) {
|
60
|
+
return finish();
|
61
|
+
}
|
62
|
+
setTimeout(() => {
|
63
|
+
waitFor(condition, finish);
|
64
|
+
}, 10);
|
65
|
+
}
|
66
|
+
return [
|
67
|
+
{
|
68
|
+
...post('/enable', paths),
|
69
|
+
handler: ({ runtime }) => async (_req, res) => {
|
70
|
+
try {
|
71
|
+
const state = runtime.updates.latest();
|
72
|
+
if (state.enabled) {
|
73
|
+
return res.status(400).json({ error: 'Output is already enabled' });
|
74
|
+
}
|
75
|
+
runtime.updates.sendCommand({
|
76
|
+
type: 'enable-output'
|
77
|
+
});
|
78
|
+
waitFor(() => runtime.updates.latest().enabled, () => {
|
79
|
+
res.sendStatus(204);
|
80
|
+
});
|
81
|
+
}
|
82
|
+
catch (error) {
|
83
|
+
console.error('Error in enable handler:', error);
|
84
|
+
res.status(500).json({ error: 'Failed to enable output' });
|
85
|
+
}
|
86
|
+
}
|
87
|
+
},
|
88
|
+
{
|
89
|
+
...post('/disable', paths),
|
90
|
+
handler: ({ runtime }) => async (_req, res) => {
|
91
|
+
try {
|
92
|
+
const state = runtime.updates.latest();
|
93
|
+
if (!state.enabled) {
|
94
|
+
return res.status(400).json({ error: 'Output is already disabled' });
|
95
|
+
}
|
96
|
+
runtime.updates.sendCommand({
|
97
|
+
type: 'disable-output'
|
98
|
+
});
|
99
|
+
waitFor(() => !runtime.updates.latest().enabled, () => {
|
100
|
+
res.sendStatus(204);
|
101
|
+
});
|
102
|
+
}
|
103
|
+
catch (error) {
|
104
|
+
console.error('Error in disable handler:', error);
|
105
|
+
res.status(500).json({ error: 'Failed to disable output' });
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
];
|
110
|
+
}
|
111
|
+
async schemas() {
|
112
|
+
const types = await promises_1.default.readFile(path_1.default.join(__dirname, 'types.yaml'));
|
113
|
+
const root = yaml_1.default.parse(types.toString());
|
114
|
+
const resolved = await (0, json_refs_1.resolveRefs)(root, {}).then((r) => r.resolved);
|
115
|
+
return {
|
116
|
+
config: resolved.components.schemas['AutoCmafConfig'],
|
117
|
+
state: resolved.components.schemas['CmafOutputState']
|
118
|
+
};
|
119
|
+
}
|
21
120
|
}
|
22
121
|
exports.default = AutoCmafDefinition;
|
23
122
|
class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
@@ -35,17 +134,22 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
35
134
|
sessionId;
|
36
135
|
destinations;
|
37
136
|
advertDestinations = [];
|
38
|
-
|
39
|
-
|
40
|
-
|
137
|
+
runtime;
|
138
|
+
enabled = true;
|
139
|
+
static async create(norsk, cfg, runtime) {
|
140
|
+
const node = new AutoCmaf(cfg, norsk, runtime);
|
41
141
|
await node.initialised;
|
42
142
|
return node;
|
43
143
|
}
|
44
|
-
constructor(cfg, norsk,
|
144
|
+
constructor(cfg, norsk, runtime) {
|
45
145
|
super(cfg.id);
|
46
146
|
this.cfg = cfg;
|
47
147
|
this.norsk = norsk;
|
48
|
-
this.
|
148
|
+
this.runtime = runtime;
|
149
|
+
this.enabled = cfg.initialState == 'enabled';
|
150
|
+
if (!this.enabled) {
|
151
|
+
runtime.updates.raiseEvent({ type: 'output-disabled' });
|
152
|
+
}
|
49
153
|
if (this.cfg.sessionId) {
|
50
154
|
this.sessionId = (Math.random() + 1).toString(36).substring(7);
|
51
155
|
}
|
@@ -57,9 +161,9 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
57
161
|
holdBackSeconds: (cfg.segments.holdBackSegments ?? 3) * cfg.segments.targetSegmentDuration,
|
58
162
|
partHoldBackSeconds: (cfg.segments.holdBackParts ?? 3) * cfg.segments.targetPartDuration,
|
59
163
|
sessionId: this.sessionId,
|
60
|
-
id:
|
164
|
+
id: `local-${this.id}`
|
61
165
|
});
|
62
|
-
cfg.
|
166
|
+
cfg.destinations.filter((d) => d.type == 's3').forEach((d, i) => {
|
63
167
|
let sanitisedPrefix = d.prefix;
|
64
168
|
if (sanitisedPrefix == "") {
|
65
169
|
sanitisedPrefix = "/";
|
@@ -70,7 +174,7 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
70
174
|
if (!sanitisedPrefix.endsWith("/"))
|
71
175
|
sanitisedPrefix = sanitisedPrefix + "/";
|
72
176
|
}
|
73
|
-
const id = `s3-${i}`;
|
177
|
+
const id = `s3-${i}-${this.id}`;
|
74
178
|
this.destinations.push({
|
75
179
|
id,
|
76
180
|
type: 's3',
|
@@ -87,6 +191,22 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
87
191
|
this.advertDestinations.push(id);
|
88
192
|
}
|
89
193
|
});
|
194
|
+
cfg.destinations.filter((d) => d.type == 'akamai').forEach((d, i) => {
|
195
|
+
const id = `akamai-${i}-${this.id}`;
|
196
|
+
const url = new URL(d.ingest);
|
197
|
+
this.destinations.push({
|
198
|
+
id,
|
199
|
+
type: 'generic',
|
200
|
+
host: url.host,
|
201
|
+
port: url.port ? parseInt(url.port, 10) : 80,
|
202
|
+
pathPrefix: url.pathname.endsWith("/") ? url.pathname : `${url.pathname}/`,
|
203
|
+
retentionPeriodSeconds: cfg.segments.retentionPeriod,
|
204
|
+
});
|
205
|
+
if (d.includeAdInsertions) {
|
206
|
+
this.advertDestinations.push(id);
|
207
|
+
}
|
208
|
+
});
|
209
|
+
this.advertDestinations.push('local');
|
90
210
|
this.initialised = this.initialise();
|
91
211
|
}
|
92
212
|
async initialise() {
|
@@ -105,16 +225,55 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
105
225
|
].join("\n"),
|
106
226
|
mpdAdditions: "",
|
107
227
|
} : {};
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
228
|
+
if (this.cfg.mode == 'cmaf') {
|
229
|
+
const cmaf = await this.norsk.output.cmafMultiVariant({
|
230
|
+
id: `${this.cfg.id}-multivariant-cmaf`,
|
231
|
+
playlistName: this.cfg.name,
|
232
|
+
destinations: this.destinations,
|
233
|
+
...mvCryptoSettings,
|
234
|
+
});
|
235
|
+
this.setup({ sink: cmaf });
|
236
|
+
this.mv = cmaf;
|
237
|
+
}
|
238
|
+
else {
|
239
|
+
const ts = await this.norsk.output.hlsTsMultiVariant({
|
240
|
+
id: `${this.cfg.id}-multivariant-ts`,
|
241
|
+
playlistName: this.cfg.name,
|
242
|
+
destinations: this.destinations,
|
243
|
+
...mvCryptoSettings,
|
244
|
+
});
|
245
|
+
this.setup({ sink: ts });
|
246
|
+
this.mv = ts;
|
247
|
+
}
|
248
|
+
this.runtime.report.registerOutput(this.cfg.id, this.mv.url);
|
249
|
+
this.runtime.updates.raiseEvent({ type: 'url-published', url: this.mv.url, drmToken: this.crypto?.token });
|
250
|
+
}
|
251
|
+
async enableOutput() {
|
252
|
+
if (!this.enabled) {
|
253
|
+
(0, logging_1.debuglog)("Enabling output", { id: this.id });
|
254
|
+
this.enabled = true;
|
255
|
+
await this.control.schedule();
|
256
|
+
this.runtime.updates.raiseEvent({ type: 'output-enabled' });
|
257
|
+
(0, logging_1.debuglog)("Output enabled", { id: this.id });
|
258
|
+
}
|
259
|
+
}
|
260
|
+
async disableOutput() {
|
261
|
+
if (this.enabled) {
|
262
|
+
(0, logging_1.debuglog)("Disabling output", { id: this.id });
|
263
|
+
this.enabled = false;
|
264
|
+
for (const media of this.currentMedia) {
|
265
|
+
await media.node?.close();
|
266
|
+
}
|
267
|
+
this.runtime.updates.raiseEvent({ type: 'output-disabled' });
|
268
|
+
this.currentMedia = [];
|
269
|
+
(0, logging_1.debuglog)("output disabled", { id: this.id });
|
270
|
+
}
|
116
271
|
}
|
117
272
|
async handleContext() {
|
273
|
+
if (!this.enabled) {
|
274
|
+
(0, logging_1.debuglog)("Skipping context handling - output disabled", { id: this.id });
|
275
|
+
return;
|
276
|
+
}
|
118
277
|
const streams = [];
|
119
278
|
this.currentSources.forEach((subscription) => {
|
120
279
|
for (const stream of subscription.latestStreams()) {
|
@@ -136,23 +295,35 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
136
295
|
this.defaultProgramNumber = stream.key.programNumber;
|
137
296
|
this.defaultSourceName = stream.key.sourceName;
|
138
297
|
}
|
139
|
-
if (
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
id:
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
298
|
+
if (this.cfg.multiplePrograms) {
|
299
|
+
if (!this.currentMultiVariants.find((v) => v.programNumber == stream.key.programNumber && v.sourceName == stream.key.sourceName)) {
|
300
|
+
const newMv = {
|
301
|
+
programNumber: stream.key.programNumber,
|
302
|
+
sourceName: stream.key.sourceName,
|
303
|
+
node: undefined,
|
304
|
+
};
|
305
|
+
this.currentMultiVariants.push(newMv);
|
306
|
+
(0, logging_1.debuglog)("Creating program-specific multi-variant in AutoPlaylist", { id: this.id, streamKey: stream.key });
|
307
|
+
if (this.cfg.mode == 'cmaf') {
|
308
|
+
newMv.node = await this.norsk.output.cmafMultiVariant({
|
309
|
+
id: `${this.cfg.id}-multivariant-${stream.key.sourceName}-${stream.key.programNumber}-cmaf`,
|
310
|
+
playlistName: `${this.cfg.name}-${stream.key.sourceName}-${stream.key.programNumber}`,
|
311
|
+
destinations: this.destinations
|
312
|
+
});
|
313
|
+
this.runtime.report.registerOutput(this.cfg.id, newMv.node.url);
|
314
|
+
}
|
315
|
+
else {
|
316
|
+
newMv.node = await this.norsk.output.cmafMultiVariant({
|
317
|
+
id: `${this.cfg.id}-multivariant-${stream.key.sourceName}-${stream.key.programNumber}-ts`,
|
318
|
+
playlistName: `${this.cfg.name}-${stream.key.sourceName}-${stream.key.programNumber}`,
|
319
|
+
destinations: this.destinations
|
320
|
+
});
|
321
|
+
this.runtime.report.registerOutput(this.cfg.id, newMv.node.url);
|
322
|
+
}
|
323
|
+
}
|
154
324
|
}
|
155
325
|
const streamKeyString = `${stream.key.sourceName}-${stream.key.programNumber}-${stream.key.streamId}-${stream.key.renditionName}`;
|
326
|
+
(0, logging_1.debuglog)("Setting up media nodes for key", { id: this.id, key: stream.key, type: stream.metadata.message.case });
|
156
327
|
switch (stream.metadata.message.case) {
|
157
328
|
case undefined:
|
158
329
|
throw "Bad server message";
|
@@ -162,29 +333,48 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
162
333
|
m3uAdditions: this.crypto.video.mediaSignaling,
|
163
334
|
mpdAdditions: this.crypto.video.dashSignalling,
|
164
335
|
} : {};
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
336
|
+
let node = undefined;
|
337
|
+
if (this.cfg.mode == 'cmaf') {
|
338
|
+
const local = node = await this.norsk.output.cmafVideo({
|
339
|
+
segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
|
340
|
+
partDurationSeconds: this.cfg.segments.targetPartDuration,
|
341
|
+
destinations: this.destinations,
|
342
|
+
id: `${this.id}-${streamKeyString}-video-cmaf`,
|
343
|
+
...videoCryptoSettings,
|
344
|
+
});
|
345
|
+
local.onPlaylistAddition = (_, p) => p;
|
346
|
+
local.onPlaylistAddition = undefined;
|
347
|
+
subscribes.push(new Promise((resolve, _reject) => {
|
348
|
+
local.subscribe([{
|
349
|
+
source: stream.source,
|
350
|
+
sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
|
351
|
+
}], (_) => true, (_) => resolve({}));
|
352
|
+
}));
|
353
|
+
}
|
354
|
+
else {
|
355
|
+
const local = node = await this.norsk.output.hlsTsVideo({
|
356
|
+
segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
|
357
|
+
destinations: this.destinations,
|
358
|
+
id: `${this.id}-${streamKeyString}-video-ts`,
|
359
|
+
...videoCryptoSettings,
|
360
|
+
});
|
361
|
+
subscribes.push(new Promise((resolve, _reject) => {
|
362
|
+
local.subscribe([{
|
363
|
+
source: stream.source,
|
364
|
+
sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
|
365
|
+
}], (_) => true, (_) => resolve({}));
|
366
|
+
}));
|
367
|
+
}
|
178
368
|
const newMedia = {
|
179
369
|
key: stream.key,
|
180
|
-
node
|
370
|
+
node,
|
181
371
|
scheduleAd: (ad, destinationId) => {
|
182
372
|
const now = new Date();
|
183
|
-
|
373
|
+
node?.scheduleTag(ad, now, destinationId);
|
184
374
|
}
|
185
375
|
};
|
186
376
|
this.currentMedia.push(newMedia);
|
187
|
-
this.registerInput(
|
377
|
+
this.registerInput(node);
|
188
378
|
break;
|
189
379
|
}
|
190
380
|
case "audio": {
|
@@ -193,51 +383,74 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
193
383
|
m3uAdditions: this.crypto.audio.mediaSignaling,
|
194
384
|
mpdAdditions: this.crypto.audio.dashSignalling,
|
195
385
|
} : {};
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
386
|
+
let node = undefined;
|
387
|
+
if (this.cfg.mode == 'cmaf') {
|
388
|
+
const local = node = await this.norsk.output.cmafAudio({
|
389
|
+
segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
|
390
|
+
partDurationSeconds: this.cfg.segments.targetPartDuration,
|
391
|
+
destinations: this.destinations,
|
392
|
+
id: `${this.id}-${streamKeyString}-audio-cmaf`,
|
393
|
+
...audioCryptoSettings,
|
394
|
+
});
|
395
|
+
local.onPlaylistAddition = (_, p) => p;
|
396
|
+
local.onPlaylistAddition = undefined;
|
397
|
+
subscribes.push(new Promise((resolve, _reject) => {
|
398
|
+
local.subscribe([{
|
399
|
+
source: stream.source,
|
400
|
+
sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
|
401
|
+
}], (_) => true, (_) => resolve({}));
|
402
|
+
}));
|
403
|
+
}
|
404
|
+
else {
|
405
|
+
const local = node = await this.norsk.output.hlsTsAudio({
|
406
|
+
segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
|
407
|
+
destinations: this.destinations,
|
408
|
+
id: `${this.id}-${streamKeyString}-audio-ts`,
|
409
|
+
...audioCryptoSettings,
|
410
|
+
});
|
411
|
+
subscribes.push(new Promise((resolve, _reject) => {
|
412
|
+
local.subscribe([{
|
413
|
+
source: stream.source,
|
414
|
+
sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
|
415
|
+
}], (_) => true, (_) => resolve({}));
|
416
|
+
}));
|
417
|
+
}
|
209
418
|
const newMedia = {
|
210
419
|
key: stream.key,
|
211
|
-
node
|
420
|
+
node,
|
212
421
|
scheduleAd: (ad, destinationId) => {
|
213
422
|
const now = new Date();
|
214
|
-
|
423
|
+
node?.scheduleTag(ad, now, destinationId);
|
215
424
|
}
|
216
425
|
};
|
217
426
|
this.currentMedia.push(newMedia);
|
218
|
-
this.registerInput(
|
427
|
+
this.registerInput(node);
|
219
428
|
break;
|
220
429
|
}
|
221
430
|
case "subtitle": {
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
431
|
+
if (this.cfg.mode == 'cmaf') {
|
432
|
+
const subtitleCmaf = await this.norsk.output.cmafWebVtt({
|
433
|
+
segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
|
434
|
+
destinations: this.destinations,
|
435
|
+
id: `${this.id}-${streamKeyString}-webvtt`,
|
436
|
+
});
|
437
|
+
subscribes.push(new Promise((resolve, _reject) => {
|
438
|
+
subtitleCmaf.subscribe([{
|
439
|
+
source: stream.source,
|
440
|
+
sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
|
441
|
+
}], (_) => true, (_) => resolve({}));
|
442
|
+
}));
|
443
|
+
const newMedia = {
|
444
|
+
key: stream.key,
|
445
|
+
node: subtitleCmaf,
|
446
|
+
scheduleAd: (ad, destinationId) => {
|
447
|
+
const now = new Date();
|
448
|
+
subtitleCmaf.scheduleTag(ad, now, destinationId);
|
449
|
+
}
|
450
|
+
};
|
451
|
+
this.currentMedia.push(newMedia);
|
452
|
+
this.registerInput(subtitleCmaf);
|
453
|
+
}
|
241
454
|
break;
|
242
455
|
}
|
243
456
|
case "ancillary": {
|
@@ -257,16 +470,18 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
257
470
|
id: adId,
|
258
471
|
scte35: message,
|
259
472
|
startDate: schedule,
|
260
|
-
durationSeconds: Number(command.breakDuration
|
473
|
+
durationSeconds: Number((command.breakDuration?.duration ?? 0n) / BigInt(90000.0))
|
261
474
|
}, d);
|
262
475
|
}
|
263
476
|
}
|
264
477
|
}
|
265
478
|
});
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
479
|
+
subscribes.push(new Promise((resolve, _reject) => {
|
480
|
+
ancillary.subscribe([{
|
481
|
+
source: stream.source,
|
482
|
+
sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
|
483
|
+
}], (_) => true, (_) => resolve({}));
|
484
|
+
}));
|
270
485
|
this.registerInput(ancillary);
|
271
486
|
break;
|
272
487
|
}
|
@@ -285,11 +500,13 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
285
500
|
for (const deletion of deletions) {
|
286
501
|
const node = deletion.node;
|
287
502
|
this.currentMedia = this.currentMedia.filter((x) => !(0, norsk_sdk_1.streamKeysAreEqual)(x.key, deletion.key));
|
288
|
-
|
503
|
+
(0, logging_1.debuglog)("Closing old media node for non-existant key", { id: this.id, key: deletion.key });
|
504
|
+
await node?.close();
|
289
505
|
}
|
506
|
+
(0, logging_1.debuglog)("Waiting for media playlists to be created", { id: this.id, count: creations.length });
|
290
507
|
await Promise.all(creations);
|
291
508
|
const defaultSources = this.currentMedia.flatMap((m) => {
|
292
|
-
if (m.key.programNumber == this.defaultProgramNumber && m.key.sourceName == this.defaultSourceName
|
509
|
+
if (m.key.programNumber == this.defaultProgramNumber && m.key.sourceName == this.defaultSourceName)
|
293
510
|
return [{ source: m.node, sourceSelector: norsk_sdk_1.selectPlaylist }];
|
294
511
|
return [];
|
295
512
|
});
|
@@ -298,7 +515,8 @@ class AutoCmaf extends base_nodes_1.CustomSinkNode {
|
|
298
515
|
this.defaultProgramNumber = 0;
|
299
516
|
this.defaultSourceName = '';
|
300
517
|
}
|
301
|
-
this.mv?.subscribe(defaultSources
|
518
|
+
this.mv?.subscribe(defaultSources.
|
519
|
+
map((s) => ({ source: s.source, sourceSelector: s.sourceSelector })));
|
302
520
|
for (const mv of this.currentMultiVariants) {
|
303
521
|
const sources = this.currentMedia.flatMap((m) => {
|
304
522
|
if (m.key.programNumber == mv.programNumber && m.key.sourceName == mv.sourceName && m.node)
|