@norskvideo/norsk-sdk 1.0.367 → 1.0.370

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.
@@ -0,0 +1,46 @@
1
+ {
2
+ "license": "MIT",
3
+ "name": "@norskvideo/norsk-sdk",
4
+ "version": "1.0.370",
5
+ "dependencies": {
6
+ "@bufbuild/protobuf": "^0.3.0",
7
+ "@grpc/grpc-js": "^1.2.2",
8
+ "@norskvideo/norsk-api": "1.0.370",
9
+ "lodash": "^4.17.21",
10
+ "typescript-nullable": "^0.6.0"
11
+ },
12
+ "files": [
13
+ "dist/norsk-sdk.d.ts",
14
+ "lib/**/*.js",
15
+ "lib/**/*d.ts",
16
+ "lib/src/tsdoc-metadata.json",
17
+ "package.json",
18
+ "src/*.ts",
19
+ "tsconfig.json"
20
+ ],
21
+ "scripts": {
22
+ "build": "npx tsc && npx eslint src",
23
+ "clean": "rm -rf lib",
24
+ "api-extractor": "rm -rf docs/gen/api-extractor && mkdir -p docs/gen/api-extractor/{temp,json,report} && npx api-extractor run --local --verbose",
25
+ "api-markdown": "npx api-documenter markdown --input-folder docs/gen/api-extractor/json --output-folder docs/gen/api-extractor/markdown"
26
+ },
27
+ "main": "./lib/src/sdk.js",
28
+ "types": "./lib/src/sdk.d.ts",
29
+ "exports": {
30
+ "types": "./lib/src/sdk.d.ts",
31
+ "import": "./lib/src/sdk.js",
32
+ "node": "./lib/src/sdk.js",
33
+ "require": "./lib/src/sdk.js",
34
+ "default": "./lib/src/sdk.js"
35
+ },
36
+ "prettier": {},
37
+ "devDependencies": {
38
+ "@microsoft/api-documenter": "^7.19.27",
39
+ "@microsoft/api-extractor": "^7.33.7",
40
+ "@typescript-eslint/eslint-plugin": "^6.2.0",
41
+ "@typescript-eslint/parser": "^6.2.0",
42
+ "eslint": "^8.46.0",
43
+ "@types/lodash": "^4.14.202",
44
+ "typescript": "^4.1.2"
45
+ }
46
+ }
@@ -1,5 +1,8 @@
1
+ /// <reference types="node" />
1
2
  import * as grpc from "@grpc/grpc-js";
2
3
  import { MediaClient as MediaClientPB } from "@norskvideo/norsk-api/lib/media_grpc_pb";
4
+ import EventEmitter from "events";
5
+ /** @public */
3
6
  export declare type MediaClient = {
4
7
  media: MediaClientPB;
5
8
  subscriptions: grpc.ClientDuplexStream<SubscriptionChannelMessage, SubscriptionChannelResponse>;
@@ -61,12 +64,23 @@ export interface StreamStatisticsMixin {
61
64
  stats: MultiStreamStatistics) => void;
62
65
  onGopStructure?: (structure: GopStructure) => void;
63
66
  }
67
+ export declare type EventMap = {
68
+ [key: string]: (arg: any) => void;
69
+ };
70
+ export declare type MediaNodeStateEvents = {
71
+ close: (id: string) => void;
72
+ };
64
73
  /** @public */
65
- export declare class MediaNodeState {
74
+ export declare class MediaNodeState<Events extends EventMap & MediaNodeStateEvents = MediaNodeStateEvents> {
66
75
  id: MediaNodeId | undefined;
67
76
  closeAwait?: () => void;
68
77
  closed: boolean;
78
+ events: EventEmitter;
79
+ protected emit<E extends (keyof Events) & string>(name: E, arg: Parameters<Events[E]>[0]): void;
80
+ on<E extends (keyof Events) & string>(name: E, listener: Events[E]): void;
81
+ off<E extends (keyof Events) & string>(name: E, listener: Events[E]): void;
69
82
  constructor(client: MediaClient);
83
+ /** @public */
70
84
  close(): Promise<void>;
71
85
  }
72
86
  /** @public */
@@ -81,12 +95,18 @@ export declare type ReceiveFromAddressAuto = {
81
95
  };
82
96
  /** @public */
83
97
  export declare type PinToKey<Pins extends string> = Nullable<Partial<Record<Pins, StreamKey[]>>>;
98
+ /** @public */
84
99
  export interface SubscribeDestination {
85
100
  id?: string;
86
101
  sourceContextChange(responseCallback: (error?: SubscriptionError) => void): Promise<boolean>;
102
+ on?: (e: "close", a: () => void) => void;
103
+ off?: (e: "close", a: () => void) => void;
87
104
  }
105
+ export declare type SourceMediaNodeEvents = {
106
+ outboundContextChange: (streams: StreamMetadata[]) => void;
107
+ } & MediaNodeStateEvents;
88
108
  /** @public */
89
- export declare class SourceMediaNode extends MediaNodeState {
109
+ export declare class SourceMediaNode<Events extends SourceMediaNodeEvents = SourceMediaNodeEvents> extends MediaNodeState<Events> {
90
110
  outputStreams: StreamMetadata[];
91
111
  registerForContextChange(subscriber: SubscribeDestination): void;
92
112
  unregisterForContextChange(subscriber: SubscribeDestination): void;
@@ -106,7 +126,7 @@ export declare class SourceMediaNode extends MediaNodeState {
106
126
  * */
107
127
  export declare type SubscriptionValidationResponse = true | false | "accept" | "deny" | "accept_and_terminate" | "deny_and_queue" | "deny_and_drop";
108
128
  /** @public */
109
- export declare class SinkMediaNode<Pins extends string> extends MediaNodeState implements SubscribeDestination {
129
+ export declare class SinkMediaNode<Pins extends string, Events extends MediaNodeStateEvents = MediaNodeStateEvents> extends MediaNodeState<Events> implements SubscribeDestination {
110
130
  permissiveSubscriptionValidation(_context: Context): SubscriptionValidationResponse;
111
131
  restrictiveSubscriptionValidation(context: Context): SubscriptionValidationResponse;
112
132
  /** Subscribe to the given sources.
@@ -129,7 +149,7 @@ export declare class SinkMediaNode<Pins extends string> extends MediaNodeState i
129
149
  finalise(): void;
130
150
  }
131
151
  /** @public */
132
- export declare class AutoSinkMediaNode<Pins extends string> extends SinkMediaNode<Pins | "auto"> {
152
+ export declare class AutoSinkMediaNode<Pins extends string, Events extends MediaNodeStateEvents = MediaNodeStateEvents> extends SinkMediaNode<Pins | "auto", Events> {
133
153
  /** Subscribe to the given sources.
134
154
  *
135
155
  * This version of subscribe simply requires a list of stream keys to be
@@ -13,8 +13,12 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
13
13
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
16
19
  Object.defineProperty(exports, "__esModule", { value: true });
17
20
  exports.registerStreamHandlers = exports._ackContext = exports.AutoSinkMediaNode = exports.SinkMediaNode = exports.SourceMediaNode = exports.MediaNodeState = exports.applyMixins = void 0;
21
+ const events_1 = __importDefault(require("events"));
18
22
  const media_pb_1 = require("@norskvideo/norsk-api/lib/media_pb");
19
23
  const constants_1 = require("@grpc/grpc-js/build/src/constants");
20
24
  const typescript_nullable_1 = require("typescript-nullable");
@@ -43,9 +47,20 @@ exports.applyMixins = applyMixins;
43
47
  class MediaNodeState {
44
48
  constructor(client) {
45
49
  this.closed = false;
50
+ this.events = new events_1.default();
46
51
  this.client = client;
47
52
  this.id = undefined;
48
53
  }
54
+ emit(name, arg) {
55
+ this.events.emit(name, arg);
56
+ }
57
+ on(name, listener) {
58
+ this.events.on(name, listener);
59
+ }
60
+ off(name, listener) {
61
+ this.events.off(name, listener);
62
+ }
63
+ /** @public */
49
64
  async close() {
50
65
  if (this.closed)
51
66
  return;
@@ -54,6 +69,8 @@ class MediaNodeState {
54
69
  (0, utils_1.debuglog)("Sending close request for media node with id %s", this.id);
55
70
  this.closeAwait = r;
56
71
  this.client.media.closeMediaNode((0, utils_1.provideFull)(media_pb_1.MediaNodeId, { id: this.id }), () => { });
72
+ this.emit('close', this.id);
73
+ this.events.removeAllListeners();
57
74
  }
58
75
  else
59
76
  r();
@@ -82,9 +99,16 @@ class SourceMediaNode extends MediaNodeState {
82
99
  this.onOutboundContextChange = onOutboundContextChange;
83
100
  }
84
101
  registerForContextChange(subscriber) {
85
- this.subscribers.set(subscriber, null);
102
+ const closeCb = () => {
103
+ this.subscribers.delete(subscriber);
104
+ };
105
+ subscriber.on?.('close', closeCb);
106
+ this.subscribers.set(subscriber, closeCb);
86
107
  }
87
108
  unregisterForContextChange(subscriber) {
109
+ const closeCb = this.subscribers.get(subscriber);
110
+ if (closeCb)
111
+ subscriber.off?.('close', closeCb);
88
112
  this.subscribers.delete(subscriber);
89
113
  }
90
114
  /** @internal */
@@ -131,6 +155,8 @@ class SourceMediaNode extends MediaNodeState {
131
155
  if (this.onOutboundContextChange) {
132
156
  await this.onOutboundContextChange(this.outputStreams);
133
157
  }
158
+ // And some people might just want to know about it without blocking
159
+ this.emit('outboundContextChange', this.outputStreams);
134
160
  const pending = { pendingSubscribes: [], readyToAck: false };
135
161
  this.pendingContextAcks.set(context.blockingCallRef, pending);
136
162
  const notifications = [];
@@ -419,7 +445,7 @@ function registerStreamHandlers(grpcStream, unregisterNode, tag, reject, setting
419
445
  if (settings.onError) {
420
446
  return;
421
447
  }
422
- console.log("Error:", source, err.code, constants_1.Status[err.code], err.details, err.metadata?.getMap());
448
+ (0, utils_1.errorlog)("Error:", source, err.code, constants_1.Status[err.code], err.details, err.metadata?.getMap());
423
449
  };
424
450
  grpcStream.on("end", () => {
425
451
  (0, utils_1.debuglog)(`End of ${tag} node`);
@@ -357,6 +357,10 @@ export interface LocalFileInputSettings extends InputSettings<SourceMediaNode> {
357
357
  /** An optional callback that will be invoked when file end is reached */
358
358
  onEof?: () => void;
359
359
  }
360
+ /**
361
+ * @public
362
+ * see: {@link NorskInput.fileWebVtt}
363
+ */
360
364
  export interface WebVttFileInputSettings extends LocalFileInputSettings {
361
365
  /** Language tag to associate with the stream (optional) */
362
366
  language?: string;
@@ -369,6 +373,10 @@ export interface WebVttFileInputSettings extends LocalFileInputSettings {
369
373
  */
370
374
  export declare class FileWebVttInputNode extends SourceMediaNode {
371
375
  }
376
+ /**
377
+ * @public
378
+ * see: {@link NorskInput.streamWebVtt}
379
+ */
372
380
  export interface StreamWebVttInputSettings extends InputSettings<StreamWebVttInputNode> {
373
381
  /** Language tag to associate with the stream (optional) */
374
382
  language?: string;
@@ -523,10 +531,12 @@ export interface UdpTsInputSettings extends RemoteInputSettings<UdpTsInputNode>
523
531
  */
524
532
  export declare class UdpTsInputNode extends TsCommonInputNode<UdpTsInputMessage, UdpTsInputNode> {
525
533
  }
534
+ /** @public */
526
535
  export interface M3u8MediaInputSettings extends InputSettings<M3u8InputNode> {
527
536
  url: string;
528
537
  }
529
538
  /**
539
+ * @public
530
540
  * see: {@link NorskInput.m3u8Media}
531
541
  */
532
542
  export declare class M3u8InputNode extends TsCommonInputNode<M3u8MediaInputMessage, M3u8InputNode> {
@@ -616,7 +626,7 @@ export declare class AudioSignalGeneratorNode extends SourceMediaNode {
616
626
  /**
617
627
  * @public
618
628
  * Settings for an Video Testcard Generator
619
- * see: {@link NorskInput.videoTestcard}
629
+ * see: {@link NorskInput.videoTestCard}
620
630
  * */
621
631
  export interface VideoTestcardGeneratorSettings extends SourceNodeSettings<VideoTestcardGeneratorNode> {
622
632
  /** The source name to set in the stream key of the outgoing stream */
@@ -687,6 +697,11 @@ export interface FileMp4InputSettings extends SourceNodeSettings<FileMp4InputNod
687
697
  /** Whether to start paused or already playing (default: playing) */
688
698
  start?: 'playing' | 'paused';
689
699
  }
700
+ /**
701
+ * @public
702
+ * Settings for updating a file-based Mp4 Input
703
+ * see: {@link FileMp4InputNode.updateSettings}
704
+ */
690
705
  export interface FileMp4InputSettingsUpdate {
691
706
  /** Whether to loop back to the start of the file after reaching the end */
692
707
  loop?: boolean;
@@ -762,13 +777,13 @@ export interface NorskInput {
762
777
  udpTs(settings: UdpTsInputSettings): Promise<UdpTsInputNode>;
763
778
  /**
764
779
  * Read subtitles from a WebVTT file on disk
765
- * @param settings
780
+ * @param settings - Configuration for the file input
766
781
  */
767
782
  fileWebVtt(settings: WebVttFileInputSettings): Promise<FileWebVttInputNode>;
768
783
  /**
769
784
  * Stream subtitles in WebVTT format via API calls. Chunks of WebVTT cues can be sent
770
785
  * in an ongoing timeline (as if streaming a fragmented WebVTT file)
771
- * @param settings
786
+ * @param settings - Configuration for the stream input
772
787
  */
773
788
  streamWebVtt(settings: StreamWebVttInputSettings): Promise<StreamWebVttInputNode>;
774
789
  /**
@@ -977,6 +977,7 @@ class UdpTsInputNode extends TsCommonInputNode {
977
977
  }
978
978
  exports.UdpTsInputNode = UdpTsInputNode;
979
979
  /**
980
+ * @public
980
981
  * see: {@link NorskInput.m3u8Media}
981
982
  */
982
983
  class M3u8InputNode extends TsCommonInputNode {
@@ -99,7 +99,7 @@ declare type MediaStoreCut = {
99
99
  /**
100
100
  * @public
101
101
  * Settings to configure a media store recorder
102
- * see {@link NorskOutput.mediaStore}
102
+ * see {@link NorskMediaStore.recorder}
103
103
  */
104
104
  export interface MediaStoreRecorderSettings extends SinkNodeSettings<MediaStoreRecorderNode>, StreamStatisticsMixin {
105
105
  /**
@@ -121,7 +121,7 @@ export interface MediaStoreRecorderSettings extends SinkNodeSettings<MediaStoreR
121
121
  }
122
122
  /**
123
123
  * @public
124
- * see: {@link NorskOutput.mediaStore}
124
+ * see: {@link NorskMediaStore.recorder}
125
125
  */
126
126
  export declare class MediaStoreRecorderNode extends AutoSinkMediaNode<"audio" | "video"> {
127
127
  metadata(): Promise<MediaStoreSession[]>;
@@ -130,7 +130,7 @@ export declare class MediaStoreRecorderNode extends AutoSinkMediaNode<"audio" |
130
130
  /**
131
131
  * @public
132
132
  * Settings for Media Store playback
133
- * see: {@link NorskInput.mediaStore}
133
+ * see: {@link NorskMediaStore.player}
134
134
  */
135
135
  export interface MediaStorePlayerSettings extends InputSettings<MediaStorePlayerNode>, StreamStatisticsMixin {
136
136
  /**
@@ -142,13 +142,13 @@ export interface MediaStorePlayerSettings extends InputSettings<MediaStorePlayer
142
142
  }
143
143
  /**
144
144
  * @public
145
- * see: {@link NorskInput.mediaStore}
145
+ * see: {@link NorskMediaStore.player}
146
146
  */
147
147
  export declare class MediaStorePlayerNode extends SourceMediaNode {
148
148
  }
149
149
  /**
150
150
  * @public
151
- * see: {@link NorskOutput.mediaStore}
151
+ * see: {@link NorskMediaStore.makeCut}
152
152
  */
153
153
  export declare class MediaStoreActiveCut {
154
154
  /**
@@ -162,12 +162,12 @@ export declare class MediaStoreActiveCut {
162
162
  export declare type MediaStoreAssetFileWithHash = {
163
163
  file: string;
164
164
  hash: string;
165
- errorOnHashMismatch?: boolean;
165
+ importIfNeeded?: boolean;
166
166
  };
167
167
  /** @public */
168
168
  export declare type MediaStoreAssetFile = {
169
169
  file: string;
170
- errorOnHashMismatch?: boolean;
170
+ importIfNeeded?: boolean;
171
171
  };
172
172
  /** @public */
173
173
  export declare type MediaStoreAssetSource = MediaStoreAssetFileWithHash | MediaStoreAssetFile;
@@ -180,19 +180,22 @@ export declare type MediaStoreAssetSettings = {
180
180
  };
181
181
  /**
182
182
  * @public
183
- * see: {@link NorskOutput.mediaStore}
183
+ * see: {@link NorskMediaStore.asyncLoadAsset}
184
184
  */
185
185
  export declare class MediaStoreAsset {
186
186
  /** @public */
187
187
  durationMs?: bigint;
188
188
  /** @public */
189
189
  size?: bigint;
190
+ /** @public */
191
+ metadata?: MediaStoreSession[];
190
192
  cutListEntry(settings: MediaStoreAssetCut): MediaStoreCut;
193
+ close(): void;
191
194
  }
192
195
  /**
193
196
  * @public
194
197
  * Settings to configure a media store snapshot
195
- * see {@link NorskOutput.mediaStore}
198
+ * see {@link NorskMediaStore.snapshot}
196
199
  */
197
200
  export interface MediaStoreSnapshotSettings {
198
201
  /**
@@ -206,16 +209,17 @@ export interface MediaStoreSnapshotSettings {
206
209
  }
207
210
  /**
208
211
  * @public
209
- * see: {@link NorskOutput.mediaStore}
212
+ * see: {@link NorskMediaStore.snapshot}
210
213
  */
211
214
  export declare class MediaStoreSnapshot {
212
215
  update(): Promise<void>;
213
216
  metadata(): Promise<MediaStoreSession[]>;
214
217
  cutListEntry(settings: MediaStoreRecorderCut): MediaStoreCut;
218
+ close(): void;
215
219
  }
216
220
  /**
217
221
  * @public
218
- * Methods that allow you to ingest media into your application
222
+ * Methods to interact with the Media Store live-to-vod recording engine
219
223
  */
220
224
  export interface NorskMediaStore {
221
225
  /**
@@ -231,7 +235,7 @@ export interface NorskMediaStore {
231
235
  /**
232
236
  * Create a media store cut
233
237
  *
234
- * @param cut request - Configuration for the cut
238
+ * @param request - Configuration for the cut
235
239
  */
236
240
  makeCut(request: MediaStoreCutRequest): MediaStoreActiveCut;
237
241
  /**
@@ -89,7 +89,7 @@ function toSingleCuts(cuts) {
89
89
  }
90
90
  /**
91
91
  * @public
92
- * see: {@link NorskOutput.mediaStore}
92
+ * see: {@link NorskMediaStore.recorder}
93
93
  */
94
94
  class MediaStoreRecorderNode extends common_1.AutoSinkMediaNode {
95
95
  /** @internal */
@@ -154,6 +154,8 @@ class MediaStoreRecorderNode extends common_1.AutoSinkMediaNode {
154
154
  return node.initialised;
155
155
  }
156
156
  async metadata() {
157
+ if (this.closed)
158
+ throw new Error("Store Closed");
157
159
  const fn = util
158
160
  .promisify(this.client.media.mediaStoreMetadata)
159
161
  .bind(this.client.media);
@@ -161,6 +163,8 @@ class MediaStoreRecorderNode extends common_1.AutoSinkMediaNode {
161
163
  .then(fromMediaStoreMetadataResponse);
162
164
  }
163
165
  cutListEntry(settings) {
166
+ if (this.closed)
167
+ throw new Error("Store Closed");
164
168
  return {
165
169
  mediaStoreName: this.settings.name,
166
170
  cut: { type: "recorder", cut: settings }
@@ -170,7 +174,7 @@ class MediaStoreRecorderNode extends common_1.AutoSinkMediaNode {
170
174
  exports.MediaStoreRecorderNode = MediaStoreRecorderNode;
171
175
  /**
172
176
  * @public
173
- * see: {@link NorskInput.mediaStore}
177
+ * see: {@link NorskMediaStore.player}
174
178
  */
175
179
  class MediaStorePlayerNode extends common_1.SourceMediaNode {
176
180
  /** @internal */
@@ -223,7 +227,7 @@ class MediaStorePlayerNode extends common_1.SourceMediaNode {
223
227
  exports.MediaStorePlayerNode = MediaStorePlayerNode;
224
228
  /**
225
229
  * @public
226
- * see: {@link NorskOutput.mediaStore}
230
+ * see: {@link NorskMediaStore.makeCut}
227
231
  */
228
232
  class MediaStoreActiveCut {
229
233
  /** @internal */
@@ -242,7 +246,10 @@ class MediaStoreActiveCut {
242
246
  this.grpcStream = this.client.media.createMediaStoreCut();
243
247
  this._isComplete = false;
244
248
  this._complete = new Promise((resolve, reject) => {
245
- this.grpcStream.on("error", (err) => { reject(err); });
249
+ this.grpcStream.on("error", (err) => {
250
+ this.grpcStream.destroy();
251
+ reject(err);
252
+ });
246
253
  this.grpcStream.on("data", (data) => {
247
254
  const messageCase = data.message.case;
248
255
  switch (messageCase) {
@@ -254,11 +261,13 @@ class MediaStoreActiveCut {
254
261
  }
255
262
  case "cutCancelled": {
256
263
  this._isComplete = true;
264
+ this.grpcStream.end();
257
265
  reject(new Error("cancelled"));
258
266
  break;
259
267
  }
260
268
  case "cutComplete": {
261
269
  this._isComplete = true;
270
+ this.grpcStream.end();
262
271
  resolve(data.message.value.size);
263
272
  break;
264
273
  }
@@ -298,11 +307,13 @@ function isMediaStoreAssetFileWithHash(asset) {
298
307
  }
299
308
  /**
300
309
  * @public
301
- * see: {@link NorskOutput.mediaStore}
310
+ * see: {@link NorskMediaStore.asyncLoadAsset}
302
311
  */
303
312
  class MediaStoreAsset {
304
313
  /** @internal */
305
314
  constructor(settings, client) {
315
+ /** @internal */
316
+ this.closed = false;
306
317
  this.client = client;
307
318
  this.settings = settings;
308
319
  this.grpcStream = this.client.media.createMediaStoreAsset((0, common_1.provideFull)(media_pb_1.MediaStoreAssetSettings, {
@@ -313,19 +324,21 @@ class MediaStoreAsset {
313
324
  fileWithHash: (0, common_1.provideFull)(media_pb_1.MediaStoreAssetFileWithHash, {
314
325
  file: settings.source.file,
315
326
  hash: settings.source.hash,
316
- errorOnHashMismatch: settings.source.errorOnHashMismatch ?? true
327
+ importIfNeeded: settings.source.importIfNeeded ?? true
317
328
  })
318
329
  }) :
319
330
  (0, common_1.mkMessageCase)({
320
331
  file: (0, common_1.provideFull)(media_pb_1.MediaStoreAssetFile, {
321
332
  file: settings.source.file,
322
- errorOnHashMismatch: settings.source.errorOnHashMismatch ?? true
333
+ importIfNeeded: settings.source.importIfNeeded ?? true
323
334
  })
324
335
  })) : (0, common_1.mkMessageCase)({ noLoad: (0, common_1.provideFull)(protobuf_1.Empty, {}) })),
325
336
  path: settings.path
326
337
  }));
327
338
  this._ready = new Promise((resolve, reject) => {
328
339
  this.grpcStream.on("error", (err) => reject(err));
340
+ this.grpcStream.on("close", () => console.log("CLOSE"));
341
+ this.grpcStream.on("end", () => console.log("END"));
329
342
  this.grpcStream.on("data", (data) => {
330
343
  const messageCase = data.message.case;
331
344
  switch (messageCase) {
@@ -338,7 +351,16 @@ class MediaStoreAsset {
338
351
  case "importComplete": {
339
352
  this.durationMs = data.message.value.durationMs;
340
353
  this.size = data.message.value.size;
341
- resolve(this);
354
+ const metadataFn = util
355
+ .promisify(this.client.media.mediaStoreMetadata)
356
+ .bind(this.client.media);
357
+ metadataFn((0, common_1.provideFull)(media_pb_1.MediaStoreMetadataRequest, { mediaStoreName: this.settings.name }))
358
+ .then(fromMediaStoreMetadataResponse)
359
+ .then((metadata) => {
360
+ this.metadata = metadata;
361
+ resolve(this);
362
+ })
363
+ .catch((err) => reject(err));
342
364
  break;
343
365
  }
344
366
  default:
@@ -353,6 +375,8 @@ class MediaStoreAsset {
353
375
  return asset._ready;
354
376
  }
355
377
  cutListEntry(settings) {
378
+ if (this.closed)
379
+ throw new Error("Store Closed");
356
380
  if (this.durationMs == null) {
357
381
  throw new Error("Attempt to cut from an asset that isn't yet loaded");
358
382
  }
@@ -365,15 +389,24 @@ class MediaStoreAsset {
365
389
  cut: { type: "asset", cut: settings }
366
390
  };
367
391
  }
392
+ close() {
393
+ if (this.closed)
394
+ return;
395
+ this.grpcStream.cancel();
396
+ // this.grpcStream.destroy();
397
+ this.closed = true;
398
+ }
368
399
  }
369
400
  exports.MediaStoreAsset = MediaStoreAsset;
370
401
  /**
371
402
  * @public
372
- * see: {@link NorskOutput.mediaStore}
403
+ * see: {@link NorskMediaStore.snapshot}
373
404
  */
374
405
  class MediaStoreSnapshot {
375
406
  /** @internal */
376
407
  constructor(settings, client) {
408
+ /** @internal */
409
+ this.closed = false;
377
410
  this.client = client;
378
411
  this.settings = settings;
379
412
  this.grpcStream = this.client.media.createMediaStoreSnapshot();
@@ -413,6 +446,8 @@ class MediaStoreSnapshot {
413
446
  return snapshot._ready;
414
447
  }
415
448
  async update() {
449
+ if (this.closed)
450
+ throw new Error("Store Closed");
416
451
  if (this._updated) {
417
452
  return new Promise((_resolve, reject) => { reject(new Error("Update in progress")); });
418
453
  }
@@ -420,6 +455,8 @@ class MediaStoreSnapshot {
420
455
  return new Promise((resolve, reject) => { this._updated = { resolve, reject }; });
421
456
  }
422
457
  async metadata() {
458
+ if (this.closed)
459
+ throw new Error("Store Closed");
423
460
  const fn = util
424
461
  .promisify(this.client.media.mediaStoreMetadata)
425
462
  .bind(this.client.media);
@@ -427,11 +464,19 @@ class MediaStoreSnapshot {
427
464
  .then(fromMediaStoreMetadataResponse);
428
465
  }
429
466
  cutListEntry(settings) {
467
+ if (this.closed)
468
+ throw new Error("Store Closed");
430
469
  return {
431
470
  mediaStoreName: this.settings.name,
432
471
  cut: { type: "recorder", cut: settings }
433
472
  };
434
473
  }
474
+ close() {
475
+ if (this.closed)
476
+ return;
477
+ this.grpcStream.destroy();
478
+ this.closed = true;
479
+ }
435
480
  }
436
481
  exports.MediaStoreSnapshot = MediaStoreSnapshot;
437
482
  /** @internal */
@@ -43,6 +43,11 @@ export interface CmafOutputSettings extends SinkNodeSettings<CmafAudioOutputNode
43
43
  * The name to use for the playlist in a multivariant playlist
44
44
  */
45
45
  name?: string;
46
+ /**
47
+ * Whether to insert a program date-time directive on every segment. Not required to produce spec-compliant playlists,
48
+ * but may be useful for inspection or playlist manipulation
49
+ */
50
+ pdtEverySegment?: boolean;
46
51
  }
47
52
  /**
48
53
  * @public
@@ -75,6 +80,11 @@ export interface HlsTsVideoOutputSettings extends SinkNodeSettings<HlsTsVideoOut
75
80
  * The name to use for the playlist in a multivariant playlist
76
81
  */
77
82
  name?: string;
83
+ /**
84
+ * Whether to insert a program date-time directive on every segment. Not required to produce spec-compliant playlists,
85
+ * but may be useful for inspection or playlist manipulation
86
+ */
87
+ pdtEverySegment?: boolean;
78
88
  }
79
89
  /**
80
90
  * @public
@@ -107,6 +117,11 @@ export interface HlsTsAudioOutputSettings extends SinkNodeSettings<HlsTsAudioOut
107
117
  * The name to use for the playlist in a multivariant playlist
108
118
  */
109
119
  name?: string;
120
+ /**
121
+ * Whether to insert a program date-time directive on every segment. Not required to produce spec-compliant playlists,
122
+ * but may be useful for inspection or playlist manipulation
123
+ */
124
+ pdtEverySegment?: boolean;
110
125
  }
111
126
  /**
112
127
  * @public
@@ -127,6 +142,11 @@ export interface CmafWebVttOutputSettings extends SinkNodeSettings<CmafWebVttOut
127
142
  * The name to use for the playlist in a multivariant playlist
128
143
  */
129
144
  name?: string;
145
+ /**
146
+ * Whether to insert a program date-time directive on every segment. Not required to produce spec-compliant playlists,
147
+ * but may be useful for inspection or playlist manipulation
148
+ */
149
+ pdtEverySegment?: boolean;
130
150
  }
131
151
  /**
132
152
  * @public
@@ -170,6 +190,11 @@ export interface HlsTsCombinedPushOutputSettings extends SinkNodeSettings<HlsTsC
170
190
  * The name to use for the playlist in a multivariant playlist
171
191
  */
172
192
  name?: string;
193
+ /**
194
+ * Whether to insert a program date-time directive on every segment. Not required to produce spec-compliant playlists,
195
+ * but may be useful for inspection or playlist manipulation
196
+ */
197
+ pdtEverySegment?: boolean;
173
198
  }
174
199
  /**
175
200
  * @public