@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.
Files changed (247) hide show
  1. package/client/info.js +5983 -699
  2. package/client/output.preview/styles.css +69 -69
  3. package/client/output.whep/styles.css +9 -0
  4. package/client/style.css +283 -856
  5. package/lib/info.js +14 -14
  6. package/lib/info.js.map +1 -1
  7. package/lib/input.rtmp/info.js +19 -5
  8. package/lib/input.rtmp/info.js.map +1 -1
  9. package/lib/input.rtmp/inline-view.js +3 -3
  10. package/lib/input.rtmp/runtime.d.ts +46 -7
  11. package/lib/input.rtmp/runtime.js +245 -59
  12. package/lib/input.rtmp/runtime.js.map +1 -1
  13. package/lib/input.rtmp/summary-view.d.ts +3 -5
  14. package/lib/input.rtmp/summary-view.js +51 -6
  15. package/lib/input.rtmp/summary-view.js.map +1 -1
  16. package/lib/input.rtmp/types.d.ts +193 -0
  17. package/lib/{processor.browserOverlay/openApi.js → input.rtmp/types.js} +1 -1
  18. package/lib/input.rtmp/types.js.map +1 -0
  19. package/lib/input.rtmp/types.yaml +147 -0
  20. package/lib/input.silence/info.js +9 -2
  21. package/lib/input.silence/info.js.map +1 -1
  22. package/lib/input.silence/runtime.d.ts +1 -0
  23. package/lib/input.silence/runtime.js.map +1 -1
  24. package/lib/input.srt-caller/info.js +8 -1
  25. package/lib/input.srt-caller/info.js.map +1 -1
  26. package/lib/input.srt-caller/runtime.d.ts +1 -0
  27. package/lib/input.srt-caller/runtime.js.map +1 -1
  28. package/lib/input.srt-listener/info.js +15 -2
  29. package/lib/input.srt-listener/info.js.map +1 -1
  30. package/lib/input.srt-listener/runtime.d.ts +41 -5
  31. package/lib/input.srt-listener/runtime.js +236 -57
  32. package/lib/input.srt-listener/runtime.js.map +1 -1
  33. package/lib/input.srt-listener/summary-view.d.ts +3 -5
  34. package/lib/input.srt-listener/summary-view.js +45 -6
  35. package/lib/input.srt-listener/summary-view.js.map +1 -1
  36. package/lib/input.srt-listener/types.d.ts +193 -0
  37. package/lib/input.srt-listener/types.js +3 -0
  38. package/lib/input.srt-listener/types.js.map +1 -0
  39. package/lib/input.srt-listener/types.yaml +145 -0
  40. package/lib/input.udp-ts/info.js +2 -1
  41. package/lib/input.udp-ts/info.js.map +1 -1
  42. package/lib/input.udp-ts/runtime.d.ts +1 -0
  43. package/lib/input.udp-ts/runtime.js.map +1 -1
  44. package/lib/input.videoTestCard/info.d.ts +1 -1
  45. package/lib/input.videoTestCard/info.js +17 -4
  46. package/lib/input.videoTestCard/info.js.map +1 -1
  47. package/lib/input.videoTestCard/runtime.d.ts +1 -0
  48. package/lib/output.autoCmaf/form-views.d.ts +2 -2
  49. package/lib/output.autoCmaf/form-views.js +13 -3
  50. package/lib/output.autoCmaf/form-views.js.map +1 -1
  51. package/lib/output.autoCmaf/info.d.ts +21 -2
  52. package/lib/output.autoCmaf/info.js +106 -32
  53. package/lib/output.autoCmaf/info.js.map +1 -1
  54. package/lib/output.autoCmaf/inline-view.d.ts +6 -0
  55. package/lib/output.autoCmaf/inline-view.js +8 -0
  56. package/lib/output.autoCmaf/inline-view.js.map +1 -0
  57. package/lib/output.autoCmaf/runtime.d.ts +29 -43
  58. package/lib/output.autoCmaf/runtime.js +314 -96
  59. package/lib/output.autoCmaf/runtime.js.map +1 -1
  60. package/lib/output.autoCmaf/summary-view.d.ts +4 -0
  61. package/lib/output.autoCmaf/summary-view.js +24 -0
  62. package/lib/output.autoCmaf/summary-view.js.map +1 -0
  63. package/lib/output.autoCmaf/summary.js +3 -0
  64. package/lib/output.autoCmaf/summary.js.map +1 -1
  65. package/lib/output.autoCmaf/types.d.ts +158 -0
  66. package/lib/output.autoCmaf/types.js +3 -0
  67. package/lib/output.autoCmaf/types.js.map +1 -0
  68. package/lib/output.autoCmaf/types.yaml +182 -0
  69. package/lib/output.autoHls/info.d.ts +3 -0
  70. package/lib/output.autoHls/info.js +22 -0
  71. package/lib/output.autoHls/info.js.map +1 -0
  72. package/lib/output.autoHls/runtime.d.ts +6 -0
  73. package/lib/output.autoHls/runtime.js +13 -0
  74. package/lib/output.autoHls/runtime.js.map +1 -0
  75. package/lib/output.preview/info.d.ts +5 -2
  76. package/lib/output.preview/info.js +23 -3
  77. package/lib/output.preview/info.js.map +1 -1
  78. package/lib/output.preview/inline-view.d.ts +2 -4
  79. package/lib/output.preview/inline-view.js +65 -7
  80. package/lib/output.preview/inline-view.js.map +1 -1
  81. package/lib/output.preview/runtime.d.ts +18 -12
  82. package/lib/output.preview/runtime.js +122 -33
  83. package/lib/output.preview/runtime.js.map +1 -1
  84. package/lib/output.preview/types.d.ts +56 -0
  85. package/lib/output.preview/types.js +3 -0
  86. package/lib/output.preview/types.js.map +1 -0
  87. package/lib/output.preview/types.yaml +37 -0
  88. package/lib/output.rtmp/info.js +14 -2
  89. package/lib/output.rtmp/info.js.map +1 -1
  90. package/lib/output.rtmp/inline-view.js +5 -2
  91. package/lib/output.rtmp/inline-view.js.map +1 -1
  92. package/lib/output.rtmp/runtime.d.ts +31 -5
  93. package/lib/output.rtmp/runtime.js +169 -34
  94. package/lib/output.rtmp/runtime.js.map +1 -1
  95. package/lib/output.rtmp/summary-view.d.ts +4 -0
  96. package/lib/output.rtmp/summary-view.js +24 -0
  97. package/lib/output.rtmp/summary-view.js.map +1 -0
  98. package/lib/output.rtmp/types.d.ts +101 -0
  99. package/lib/output.rtmp/types.js +3 -0
  100. package/lib/output.rtmp/types.js.map +1 -0
  101. package/lib/output.rtmp/types.yaml +30 -0
  102. package/lib/output.srt/info.d.ts +2 -2
  103. package/lib/output.srt/info.js +29 -1
  104. package/lib/output.srt/info.js.map +1 -1
  105. package/lib/output.srt/inline-view.d.ts +6 -0
  106. package/lib/output.srt/inline-view.js +8 -0
  107. package/lib/output.srt/inline-view.js.map +1 -0
  108. package/lib/output.srt/runtime.d.ts +35 -5
  109. package/lib/output.srt/runtime.js +174 -6
  110. package/lib/output.srt/runtime.js.map +1 -1
  111. package/lib/output.srt/summary-view.d.ts +4 -0
  112. package/lib/output.srt/summary-view.js +24 -0
  113. package/lib/output.srt/summary-view.js.map +1 -0
  114. package/lib/output.srt/types.d.ts +101 -0
  115. package/lib/output.srt/types.js +3 -0
  116. package/lib/output.srt/types.js.map +1 -0
  117. package/lib/output.srt/types.yaml +31 -0
  118. package/lib/output.statistics/info.js +3 -1
  119. package/lib/output.statistics/info.js.map +1 -1
  120. package/lib/output.statistics/inline-view.js +3 -2
  121. package/lib/output.statistics/inline-view.js.map +1 -1
  122. package/lib/output.statistics/runtime.d.ts +1 -0
  123. package/lib/output.statistics/runtime.js.map +1 -1
  124. package/lib/output.udpTs/info.js +4 -0
  125. package/lib/output.udpTs/info.js.map +1 -1
  126. package/lib/output.udpTs/runtime.d.ts +1 -0
  127. package/lib/output.udpTs/runtime.js.map +1 -1
  128. package/lib/output.whep/info.d.ts +2 -2
  129. package/lib/output.whep/info.js +61 -1
  130. package/lib/output.whep/info.js.map +1 -1
  131. package/lib/output.whep/inline-view.d.ts +4 -0
  132. package/lib/output.whep/inline-view.js +57 -0
  133. package/lib/output.whep/inline-view.js.map +1 -0
  134. package/lib/output.whep/runtime.d.ts +39 -5
  135. package/lib/output.whep/runtime.js +175 -13
  136. package/lib/output.whep/runtime.js.map +1 -1
  137. package/lib/output.whep/summary-view.d.ts +4 -0
  138. package/lib/output.whep/summary-view.js +24 -0
  139. package/lib/output.whep/summary-view.js.map +1 -0
  140. package/lib/output.whep/types.d.ts +101 -0
  141. package/lib/output.whep/types.js +3 -0
  142. package/lib/output.whep/types.js.map +1 -0
  143. package/lib/output.whep/types.yaml +30 -0
  144. package/lib/processor.audioEncoder/form-views.d.ts +8 -0
  145. package/lib/processor.audioEncoder/form-views.js +8 -0
  146. package/lib/processor.audioEncoder/form-views.js.map +1 -0
  147. package/lib/processor.audioEncoder/info.d.ts +3 -0
  148. package/lib/processor.audioEncoder/info.js +160 -0
  149. package/lib/processor.audioEncoder/info.js.map +1 -0
  150. package/lib/processor.audioEncoder/runtime.d.ts +21 -0
  151. package/lib/processor.audioEncoder/runtime.js +20 -0
  152. package/lib/processor.audioEncoder/runtime.js.map +1 -0
  153. package/lib/processor.browserOverlay/info.js +1 -0
  154. package/lib/processor.browserOverlay/info.js.map +1 -1
  155. package/lib/processor.browserOverlay/runtime.d.ts +1 -0
  156. package/lib/processor.browserOverlay/runtime.js +6 -26
  157. package/lib/processor.browserOverlay/runtime.js.map +1 -1
  158. package/lib/processor.browserOverlay/types.js +3 -0
  159. package/lib/processor.browserOverlay/types.js.map +1 -0
  160. package/lib/processor.cascadingSwitch/info.js +3 -2
  161. package/lib/processor.cascadingSwitch/info.js.map +1 -1
  162. package/lib/processor.cascadingSwitch/inline-view.js +3 -3
  163. package/lib/processor.cascadingSwitch/runtime.d.ts +1 -0
  164. package/lib/processor.cascadingSwitch/runtime.js +2 -2
  165. package/lib/processor.cascadingSwitch/runtime.js.map +1 -1
  166. package/lib/processor.fixedLadder/codec-editor.js +1 -1
  167. package/lib/processor.fixedLadder/codec-editor.js.map +1 -1
  168. package/lib/processor.fixedLadder/codec-view.d.ts +1 -1
  169. package/lib/processor.fixedLadder/codec-view.js +1 -1
  170. package/lib/processor.fixedLadder/codec-view.js.map +1 -1
  171. package/lib/processor.fixedLadder/info.d.ts +33 -7
  172. package/lib/processor.fixedLadder/info.js +19 -6
  173. package/lib/processor.fixedLadder/info.js.map +1 -1
  174. package/lib/processor.fixedLadder/runtime.d.ts +16 -29
  175. package/lib/processor.fixedLadder/runtime.js +16 -1
  176. package/lib/processor.fixedLadder/runtime.js.map +1 -1
  177. package/lib/processor.fixedLadder/types.d.ts +240 -0
  178. package/lib/processor.fixedLadder/types.js +3 -0
  179. package/lib/processor.fixedLadder/types.js.map +1 -0
  180. package/lib/processor.fixedLadder/types.yaml +541 -0
  181. package/lib/processor.onscreenGraphic/info.js +11 -5
  182. package/lib/processor.onscreenGraphic/info.js.map +1 -1
  183. package/lib/processor.onscreenGraphic/runtime.d.ts +27 -2
  184. package/lib/processor.onscreenGraphic/runtime.js +116 -52
  185. package/lib/processor.onscreenGraphic/runtime.js.map +1 -1
  186. package/lib/processor.onscreenGraphic/summary-view.d.ts +1 -1
  187. package/lib/processor.onscreenGraphic/summary-view.js +235 -11
  188. package/lib/processor.onscreenGraphic/summary-view.js.map +1 -1
  189. package/lib/processor.onscreenGraphic/types.d.ts +15 -1
  190. package/lib/processor.onscreenGraphic/types.yaml +63 -14
  191. package/lib/processor.streamKeyOverride/info.d.ts +3 -0
  192. package/lib/processor.streamKeyOverride/info.js +151 -0
  193. package/lib/processor.streamKeyOverride/info.js.map +1 -0
  194. package/lib/processor.streamKeyOverride/runtime.d.ts +55 -0
  195. package/lib/processor.streamKeyOverride/runtime.js +226 -0
  196. package/lib/processor.streamKeyOverride/runtime.js.map +1 -0
  197. package/lib/shared/api.d.ts +35 -0
  198. package/lib/shared/api.js +34 -0
  199. package/lib/shared/api.js.map +1 -0
  200. package/lib/test/auto-cmaf.js +219 -43
  201. package/lib/test/auto-cmaf.js.map +1 -1
  202. package/lib/test/onscreen-graphic.js +79 -42
  203. package/lib/test/onscreen-graphic.js.map +1 -1
  204. package/lib/test/preview.js +69 -64
  205. package/lib/test/preview.js.map +1 -1
  206. package/lib/test/rtmp-input.js +6 -3
  207. package/lib/test/rtmp-input.js.map +1 -1
  208. package/lib/test/rtmp-output.js +105 -1
  209. package/lib/test/rtmp-output.js.map +1 -1
  210. package/lib/test/srt-input-listener.js +331 -0
  211. package/lib/test/srt-input-listener.js.map +1 -1
  212. package/lib/test/srt-output.js +93 -4
  213. package/lib/test/srt-output.js.map +1 -1
  214. package/lib/test/whep-output.js +192 -5
  215. package/lib/test/whep-output.js.map +1 -1
  216. package/lib/util.stats.latency/info.js +1 -0
  217. package/lib/util.stats.latency/info.js.map +1 -1
  218. package/lib/util.stats.latency/runtime.d.ts +1 -0
  219. package/lib/util.stats.latency/runtime.js +2 -2
  220. package/lib/util.stats.latency/runtime.js.map +1 -1
  221. package/package.json +4 -4
  222. package/lib/processor.browserOverlay/openApi.js.map +0 -1
  223. package/lib/processor.whisper-transcribe/info.d.ts +0 -3
  224. package/lib/processor.whisper-transcribe/info.js +0 -44
  225. package/lib/processor.whisper-transcribe/info.js.map +0 -1
  226. package/lib/processor.whisper-transcribe/runtime.d.ts +0 -29
  227. package/lib/processor.whisper-transcribe/runtime.js +0 -83
  228. package/lib/processor.whisper-transcribe/runtime.js.map +0 -1
  229. package/lib/util.stats.ma35d/info.d.ts +0 -3
  230. package/lib/util.stats.ma35d/info.js +0 -79
  231. package/lib/util.stats.ma35d/info.js.map +0 -1
  232. package/lib/util.stats.ma35d/inline-view.d.ts +0 -6
  233. package/lib/util.stats.ma35d/inline-view.js +0 -76
  234. package/lib/util.stats.ma35d/inline-view.js.map +0 -1
  235. package/lib/util.stats.ma35d/runtime.d.ts +0 -21
  236. package/lib/util.stats.ma35d/runtime.js +0 -49
  237. package/lib/util.stats.ma35d/runtime.js.map +0 -1
  238. package/lib/util.timestamps/info.d.ts +0 -5
  239. package/lib/util.timestamps/info.js +0 -87
  240. package/lib/util.timestamps/info.js.map +0 -1
  241. package/lib/util.timestamps/inline-view.d.ts +0 -6
  242. package/lib/util.timestamps/inline-view.js +0 -94
  243. package/lib/util.timestamps/inline-view.js.map +0 -1
  244. package/lib/util.timestamps/runtime.d.ts +0 -29
  245. package/lib/util.timestamps/runtime.js +0 -40
  246. package/lib/util.timestamps/runtime.js.map +0 -1
  247. /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
- export type AutoCmafS3Destination = {
9
- host: string;
10
- prefix: string;
11
- includeAdInsertions: boolean;
12
- };
13
- export type AutoCmafConfig = {
14
- id: string;
15
- displayName: string;
16
- name: string;
17
- sessionId: boolean;
18
- segments: AutoCmafSegment;
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 CmafOutputEvent = {
39
- type: 'url-published';
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>, { updates, report }: StudioRuntime<CmafOutputState, CmafOutputCommand, CmafOutputEvent>): Promise<void>;
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: AutoCmafConfig;
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
- report: ReportBuilder;
71
- static create(norsk: Norsk, cfg: AutoCmafConfig, report: ReportBuilder): Promise<AutoCmaf>;
72
- constructor(cfg: AutoCmafConfig, norsk: Norsk, report: ReportBuilder);
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, { updates, report }) {
13
- const node = await AutoCmaf.create(norsk, cfg, report);
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
- const mv = node.mv;
16
- if (mv) {
17
- report.registerOutput(cfg.id, mv.url);
18
- updates.raiseEvent({ type: 'url-published', url: mv.url, drmToken: node.crypto?.token });
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
- report;
39
- static async create(norsk, cfg, report) {
40
- const node = new AutoCmaf(cfg, norsk, report);
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, report) {
144
+ constructor(cfg, norsk, runtime) {
45
145
  super(cfg.id);
46
146
  this.cfg = cfg;
47
147
  this.norsk = norsk;
48
- this.report = report;
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: 'local'
164
+ id: `local-${this.id}`
61
165
  });
62
- cfg.s3Destinations.forEach((d, i) => {
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
- const mv = await this.norsk.output.cmafMultiVariant({
109
- id: `${this.cfg.id}-multivariant`,
110
- playlistName: this.cfg.name,
111
- destinations: this.destinations,
112
- ...mvCryptoSettings,
113
- });
114
- this.setup({ sink: mv });
115
- this.mv = mv;
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 (!this.currentMultiVariants.find((v) => v.programNumber == stream.key.programNumber && v.sourceName == stream.key.sourceName)) {
140
- const newMv = {
141
- programNumber: stream.key.programNumber,
142
- sourceName: stream.key.sourceName,
143
- node: undefined
144
- };
145
- this.currentMultiVariants.push(newMv);
146
- const mv = await this.norsk.output.cmafMultiVariant({
147
- id: `${this.cfg.id}-multivariant-${stream.key.sourceName}-${stream.key.programNumber}`,
148
- playlistName: `${this.cfg.name}-${stream.key.sourceName}-${stream.key.programNumber}`,
149
- destinations: this.destinations
150
- });
151
- (0, logging_1.debuglog)("Creating program-specific multi-variant in AutoCMAF", { id: this.id, streamKey: stream.key });
152
- newMv.node = mv;
153
- this.report.registerOutput(this.cfg.id, mv.url);
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
- const video = await this.norsk.output.cmafVideo({
166
- segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
167
- partDurationSeconds: this.cfg.segments.targetPartDuration,
168
- destinations: this.destinations,
169
- id: `${this.id}-${streamKeyString}-video`,
170
- ...videoCryptoSettings,
171
- });
172
- subscribes.push(new Promise((resolve, _reject) => {
173
- video.subscribe([{
174
- source: stream.source,
175
- sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
176
- }], (_) => true, (_) => resolve({}));
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: video,
370
+ node,
181
371
  scheduleAd: (ad, destinationId) => {
182
372
  const now = new Date();
183
- video.scheduleTag(ad, now, destinationId);
373
+ node?.scheduleTag(ad, now, destinationId);
184
374
  }
185
375
  };
186
376
  this.currentMedia.push(newMedia);
187
- this.registerInput(video);
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
- const audio = await this.norsk.output.cmafAudio({
197
- segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
198
- partDurationSeconds: this.cfg.segments.targetPartDuration,
199
- destinations: this.destinations,
200
- id: `${this.id}-${streamKeyString}-audio`,
201
- ...audioCryptoSettings,
202
- });
203
- subscribes.push(new Promise((resolve, _reject) => {
204
- audio.subscribe([{
205
- source: stream.source,
206
- sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
207
- }], (_) => true, (_) => resolve({}));
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: audio,
420
+ node,
212
421
  scheduleAd: (ad, destinationId) => {
213
422
  const now = new Date();
214
- audio.scheduleTag(ad, now, destinationId);
423
+ node?.scheduleTag(ad, now, destinationId);
215
424
  }
216
425
  };
217
426
  this.currentMedia.push(newMedia);
218
- this.registerInput(audio);
427
+ this.registerInput(node);
219
428
  break;
220
429
  }
221
430
  case "subtitle": {
222
- const subtitle = await this.norsk.output.cmafWebVtt({
223
- segmentDurationSeconds: this.cfg.segments.targetSegmentDuration,
224
- destinations: this.destinations,
225
- id: `${this.id}-${streamKeyString}-webvtt`
226
- });
227
- subtitle.subscribe([{
228
- source: stream.source,
229
- sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
230
- }]);
231
- const newMedia = {
232
- key: stream.key,
233
- node: subtitle,
234
- scheduleAd: (ad, destinationId) => {
235
- const now = new Date();
236
- subtitle.scheduleTag(ad, now, destinationId);
237
- }
238
- };
239
- this.currentMedia.push(newMedia);
240
- this.registerInput(subtitle);
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.duration / BigInt(90000.0))
473
+ durationSeconds: Number((command.breakDuration?.duration ?? 0n) / BigInt(90000.0))
261
474
  }, d);
262
475
  }
263
476
  }
264
477
  }
265
478
  });
266
- ancillary.subscribe([{
267
- source: stream.source,
268
- sourceSelector: (0, norsk_sdk_1.selectExactKey)(stream.key)
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
- await node.close();
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 && m.node)
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)