@eluvio/elv-client-js 4.0.56 → 4.0.58

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,1799 @@
1
+ var _regeneratorRuntime = require("@babel/runtime/regenerator");
2
+ var _asyncToGenerator = require("@babel/runtime/helpers/asyncToGenerator");
3
+ /**
4
+ * Methods for Live Stream creation and management
5
+ *
6
+ * @module ElvClient/LiveStream
7
+ */
8
+
9
+ var _require = require("./LiveConf"),
10
+ LiveConf = _require.LiveConf;
11
+ var path = require("path");
12
+ var fs = require("fs");
13
+ var HttpClient = require("../HttpClient");
14
+ //
15
+ // const {
16
+ // ValidateLibrary,
17
+ // ValidateVersion,
18
+ // ValidateParameters
19
+ // } = require("../Validation");
20
+
21
+ var MakeTxLessToken = /*#__PURE__*/function () {
22
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
23
+ var client, libraryId, objectId, versionHash, tok;
24
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
25
+ while (1) switch (_context.prev = _context.next) {
26
+ case 0:
27
+ client = _ref.client, libraryId = _ref.libraryId, objectId = _ref.objectId, versionHash = _ref.versionHash;
28
+ _context.next = 3;
29
+ return client.authClient.AuthorizationToken({
30
+ libraryId: libraryId,
31
+ objectId: objectId,
32
+ versionHash: versionHash,
33
+ channelAuth: false,
34
+ noCache: true,
35
+ noAuth: true
36
+ });
37
+ case 3:
38
+ tok = _context.sent;
39
+ return _context.abrupt("return", tok);
40
+ case 5:
41
+ case "end":
42
+ return _context.stop();
43
+ }
44
+ }, _callee);
45
+ }));
46
+ return function MakeTxLessToken(_x) {
47
+ return _ref2.apply(this, arguments);
48
+ };
49
+ }();
50
+ function sleep(ms) {
51
+ return new Promise(function (resolve) {
52
+ return setTimeout(resolve, ms);
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Retrieve the status of the current live stream session
58
+ *
59
+ * @methodGroup Live Stream
60
+ * @namedParams
61
+ * @param {string} name -
62
+ * @param {boolean} stopLro -
63
+ * @param {boolean} showParams -
64
+ * States:
65
+ * unconfigured - no live_recording_config
66
+ * uninitialized - no live_recording config generated
67
+ * inactive - live_recording config initialized but no 'edge write token'
68
+ * stopped - edge-write-token but not started
69
+ * starting - LRO running but no source data yet
70
+ * running - stream is running and producing output
71
+ * stalled - LRO running but no source data (so not producing output)
72
+ *
73
+ * @return {Object} - The status response for the object, as well as logs, warnings and errors from the master initialization
74
+ */
75
+ exports.StreamStatus = /*#__PURE__*/function () {
76
+ var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref3) {
77
+ var name, _ref3$stopLro, stopLro, _ref3$showParams, showParams, conf, status, libraryId, mainMeta, fabURI, edgeWriteToken, edgeMeta, recordings, sequence, period, tlro, sinceLastFinalize, recording_period, insertions, i, insertionTimeSinceEpoch, state, lroStatus, playout_urls, objectId, playout_options, hls_clear_enabled, hls_aes128_enabled, hls_sample_aes_enabled, networkInfo, token, embed_net, embed_url;
78
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
79
+ while (1) switch (_context2.prev = _context2.next) {
80
+ case 0:
81
+ name = _ref3.name, _ref3$stopLro = _ref3.stopLro, stopLro = _ref3$stopLro === void 0 ? false : _ref3$stopLro, _ref3$showParams = _ref3.showParams, showParams = _ref3$showParams === void 0 ? false : _ref3$showParams;
82
+ _context2.next = 3;
83
+ return this.LoadConf({
84
+ name: name
85
+ });
86
+ case 3:
87
+ conf = _context2.sent;
88
+ status = {
89
+ name: name
90
+ };
91
+ _context2.prev = 5;
92
+ _context2.next = 8;
93
+ return this.ContentObjectLibraryId({
94
+ objectId: conf.objectId
95
+ });
96
+ case 8:
97
+ libraryId = _context2.sent;
98
+ status.library_id = libraryId;
99
+ status.object_id = conf.objectId;
100
+ _context2.next = 13;
101
+ return this.ContentObjectMetadata({
102
+ libraryId: libraryId,
103
+ objectId: conf.objectId,
104
+ select: ["live_recording_config", "live_recording"]
105
+ });
106
+ case 13:
107
+ mainMeta = _context2.sent;
108
+ if (!(mainMeta.live_recording_config == undefined || mainMeta.live_recording_config.url == undefined)) {
109
+ _context2.next = 17;
110
+ break;
111
+ }
112
+ status.state = "unconfigured";
113
+ return _context2.abrupt("return", status);
114
+ case 17:
115
+ if (!(mainMeta.live_recording == undefined || mainMeta.live_recording.fabric_config == undefined || mainMeta.live_recording.playout_config == undefined || mainMeta.live_recording.recording_config == undefined)) {
116
+ _context2.next = 20;
117
+ break;
118
+ }
119
+ status.state = "uninitialized";
120
+ return _context2.abrupt("return", status);
121
+ case 20:
122
+ fabURI = mainMeta.live_recording.fabric_config.ingress_node_api;
123
+ if (!(fabURI === undefined)) {
124
+ _context2.next = 25;
125
+ break;
126
+ }
127
+ console.log("bad fabric config - missing ingress node API");
128
+ status.state = "uninitialized";
129
+ return _context2.abrupt("return", status);
130
+ case 25:
131
+ // Support both hostname and URL ingress_node_api
132
+ if (!fabURI.startsWith("http")) {
133
+ // Assume https
134
+ fabURI = "https://" + fabURI;
135
+ }
136
+ status.fabric_api = fabURI;
137
+ status.url = mainMeta.live_recording.recording_config.recording_params.origin_url;
138
+ edgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
139
+ if (!(edgeWriteToken == undefined)) {
140
+ _context2.next = 32;
141
+ break;
142
+ }
143
+ status.state = "inactive";
144
+ return _context2.abrupt("return", status);
145
+ case 32:
146
+ this.RecordWriteToken({
147
+ writeToken: edgeWriteToken,
148
+ fabricNodeUrl: fabURI
149
+ });
150
+ status.edge_write_token = edgeWriteToken;
151
+ status.stream_id = edgeWriteToken; // By convention the stream ID is its write token
152
+ _context2.next = 37;
153
+ return this.ContentObjectMetadata({
154
+ libraryId: libraryId,
155
+ objectId: conf.objectId,
156
+ writeToken: edgeWriteToken,
157
+ select: ["live_recording"]
158
+ });
159
+ case 37:
160
+ edgeMeta = _context2.sent;
161
+ if (!(edgeMeta.live_recording === undefined || edgeMeta.live_recording.recordings === undefined || edgeMeta.live_recording.recordings.recording_sequence === undefined)) {
162
+ _context2.next = 41;
163
+ break;
164
+ }
165
+ status.state = "stopped";
166
+ return _context2.abrupt("return", status);
167
+ case 41:
168
+ recordings = edgeMeta.live_recording.recordings;
169
+ status.recording_period_sequence = recordings.recording_sequence;
170
+ sequence = recordings.recording_sequence;
171
+ period = recordings.live_offering[sequence - 1];
172
+ tlro = period.live_recording_handle;
173
+ status.tlro = tlro;
174
+ sinceLastFinalize = Math.floor(new Date().getTime() / 1000) - period.video_finalized_parts_info.last_finalization_time / 1000000;
175
+ recording_period = {
176
+ activation_time_epoch_sec: period.recording_start_time_epoch_sec,
177
+ start_time_epoch_sec: period.start_time_epoch_sec,
178
+ start_time_text: new Date(period.start_time_epoch_sec * 1000).toLocaleString(),
179
+ end_time_epoch_sec: period.end_time_epoch_sec,
180
+ end_time_text: period.end_time_epoch_sec === 0 ? null : new Date(period.end_time_epoch_sec * 1000).toLocaleString(),
181
+ video_parts: period.video_finalized_parts_info.n_parts,
182
+ video_last_part_finalized_epoch_sec: period.video_finalized_parts_info.last_finalization_time / 1000000,
183
+ video_since_last_finalize_sec: sinceLastFinalize
184
+ };
185
+ status.recording_period = recording_period;
186
+ _context2.next = 52;
187
+ return this.FabricUrl({
188
+ libraryId: libraryId,
189
+ objectId: conf.objectId,
190
+ writeToken: edgeWriteToken,
191
+ call: "live/status/" + tlro
192
+ });
193
+ case 52:
194
+ status.lro_status_url = _context2.sent;
195
+ status.insertions = [];
196
+ if (edgeMeta.live_recording.playout_config.interleaves != undefined && edgeMeta.live_recording.playout_config.interleaves[sequence] != undefined) {
197
+ insertions = edgeMeta.live_recording.playout_config.interleaves[sequence];
198
+ for (i = 0; i < insertions.length; i++) {
199
+ insertionTimeSinceEpoch = recording_period.start_time_epoch_sec + insertions[i].insertion_time;
200
+ status.insertions[i] = {
201
+ insertion_time_since_start: insertions[i].insertion_time,
202
+ insertion_time: new Date(insertionTimeSinceEpoch * 1000).toISOString(),
203
+ insertion_time_local: new Date(insertionTimeSinceEpoch * 1000).toLocaleString(),
204
+ target: insertions[i].playout
205
+ };
206
+ }
207
+ }
208
+ if (showParams) {
209
+ status.recording_paramse = edgeMeta.live_recording.recording_config.recording_params;
210
+ }
211
+ state = "stopped";
212
+ lroStatus = "";
213
+ _context2.prev = 58;
214
+ _context2.t0 = this.utils;
215
+ _context2.next = 62;
216
+ return HttpClient.Fetch(status.lro_status_url);
217
+ case 62:
218
+ _context2.t1 = _context2.sent;
219
+ _context2.next = 65;
220
+ return _context2.t0.ResponseToJson.call(_context2.t0, _context2.t1);
221
+ case 65:
222
+ lroStatus = _context2.sent;
223
+ state = lroStatus.state;
224
+ _context2.next = 75;
225
+ break;
226
+ case 69:
227
+ _context2.prev = 69;
228
+ _context2.t2 = _context2["catch"](58);
229
+ console.log("LRO Status (failed): ", _context2.t2.response.statusCode);
230
+ status.state = "stopped";
231
+ status.error = _context2.t2.response;
232
+ return _context2.abrupt("return", status);
233
+ case 75:
234
+ // Convert LRO 'state' to desired 'state'
235
+ if (state === "running" && period.video_finalized_parts_info.last_finalization_time === 0) {
236
+ state = "starting";
237
+ } else if (state === "running" && sinceLastFinalize > 32.9) {
238
+ state = "stalled";
239
+ } else if (state == "terminated") {
240
+ state = "stopped";
241
+ }
242
+ status.state = state;
243
+ if (!((state === "running" || state === "stalled" || state === "starting") && stopLro)) {
244
+ _context2.next = 96;
245
+ break;
246
+ }
247
+ _context2.next = 80;
248
+ return this.FabricUrl({
249
+ libraryId: libraryId,
250
+ objectId: conf.objectId,
251
+ writeToken: edgeWriteToken,
252
+ call: "live/stop/" + tlro
253
+ });
254
+ case 80:
255
+ lroStopUrl = _context2.sent;
256
+ _context2.prev = 81;
257
+ _context2.t3 = this.utils;
258
+ _context2.next = 85;
259
+ return HttpClient.Fetch(lroStopUrl);
260
+ case 85:
261
+ _context2.t4 = _context2.sent;
262
+ _context2.next = 88;
263
+ return _context2.t3.ResponseToJson.call(_context2.t3, _context2.t4);
264
+ case 88:
265
+ console.log("LRO Stop: ", lroStatus.body);
266
+ _context2.next = 94;
267
+ break;
268
+ case 91:
269
+ _context2.prev = 91;
270
+ _context2.t5 = _context2["catch"](81);
271
+ console.log("LRO Stop (failed): ", _context2.t5.response.statusCode);
272
+ case 94:
273
+ state = "stopped";
274
+ status.state = state;
275
+ case 96:
276
+ if (!(state === "running")) {
277
+ _context2.next = 128;
278
+ break;
279
+ }
280
+ playout_urls = {};
281
+ objectId = conf.objectId;
282
+ _context2.next = 101;
283
+ return this.PlayoutOptions({
284
+ objectId: objectId,
285
+ linkPath: "public/asset_metadata/sources/default"
286
+ });
287
+ case 101:
288
+ playout_options = _context2.sent;
289
+ hls_clear_enabled = playout_options && playout_options.hls && playout_options.hls.playoutMethods && playout_options.hls.playoutMethods.clear !== undefined;
290
+ if (!hls_clear_enabled) {
291
+ _context2.next = 107;
292
+ break;
293
+ }
294
+ _context2.next = 106;
295
+ return this.FabricUrl({
296
+ libraryId: libraryId,
297
+ objectId: objectId,
298
+ rep: "playout/default/hls-clear/playlist.m3u8"
299
+ });
300
+ case 106:
301
+ playout_urls.hls_clear = _context2.sent;
302
+ case 107:
303
+ hls_aes128_enabled = playout_options && playout_options.hls && playout_options.hls.playoutMethods && playout_options.hls.playoutMethods["aes-128"] !== undefined;
304
+ if (!hls_aes128_enabled) {
305
+ _context2.next = 112;
306
+ break;
307
+ }
308
+ _context2.next = 111;
309
+ return this.FabricUrl({
310
+ libraryId: libraryId,
311
+ objectId: objectId,
312
+ rep: "playout/default/hls-aes128/playlist.m3u8"
313
+ });
314
+ case 111:
315
+ playout_urls.hls_aes128 = _context2.sent;
316
+ case 112:
317
+ hls_sample_aes_enabled = playout_options && playout_options.hls && playout_options.hls.playoutMethods && playout_options.hls.playoutMethods["sample-aes"] !== undefined;
318
+ if (!hls_sample_aes_enabled) {
319
+ _context2.next = 117;
320
+ break;
321
+ }
322
+ _context2.next = 116;
323
+ return this.FabricUrl({
324
+ libraryId: libraryId,
325
+ objectId: objectId,
326
+ rep: "playout/default/hls-sample-aes/playlist.m3u8"
327
+ });
328
+ case 116:
329
+ playout_urls.hls_sample_aes = _context2.sent;
330
+ case 117:
331
+ _context2.next = 119;
332
+ return this.NetworkInfo();
333
+ case 119:
334
+ networkInfo = _context2.sent;
335
+ _context2.next = 122;
336
+ return this.authClient.AuthorizationToken({
337
+ libraryId: libraryId,
338
+ objectId: objectId,
339
+ channelAuth: false,
340
+ noCache: true,
341
+ noAuth: true
342
+ });
343
+ case 122:
344
+ token = _context2.sent;
345
+ embed_net = "main";
346
+ if (networkInfo.name.includes("demo")) {
347
+ embed_net = "demo";
348
+ }
349
+ embed_url = "https://embed.v3.contentfabric.io/?net=".concat(embed_net, "&p&ct=h&oid=").concat(conf.objectId, "&mt=lv&ath=").concat(token);
350
+ playout_urls.embed_url = embed_url;
351
+ status.playout_urls = playout_urls;
352
+ case 128:
353
+ _context2.next = 133;
354
+ break;
355
+ case 130:
356
+ _context2.prev = 130;
357
+ _context2.t6 = _context2["catch"](5);
358
+ console.error(_context2.t6);
359
+ case 133:
360
+ return _context2.abrupt("return", status);
361
+ case 134:
362
+ case "end":
363
+ return _context2.stop();
364
+ }
365
+ }, _callee2, this, [[5, 130], [58, 69], [81, 91]]);
366
+ }));
367
+ return function (_x2) {
368
+ return _ref4.apply(this, arguments);
369
+ };
370
+ }();
371
+
372
+ // async StatusPrep({name}) {
373
+ //
374
+ // let conf = await this.LoadConf({name});
375
+ //
376
+ // try {
377
+ //
378
+ // // Set static token - avoid individual auth for separate channels/streams
379
+ // let token = await MakeTxLessToken({client: this.client, libraryId: conf.libraryId});
380
+ // this.client.SetStaticToken({token});
381
+ //
382
+ // } catch(error) {
383
+ // console.log("StatusPrep failed: ", error);
384
+ // return null;
385
+ // }
386
+ //
387
+ // }
388
+
389
+ /**
390
+ * Create a new edge write token
391
+ *
392
+ * @methodGroup Live Stream
393
+ * @namedParams
394
+ * @param {string} name -
395
+ * @param {boolean} start -
396
+ *
397
+ * @return {Object} - The status response for the object
398
+ *
399
+ */
400
+ exports.StreamCreate = /*#__PURE__*/function () {
401
+ var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(_ref5) {
402
+ var name, _ref5$start, start, status, objectId, libraryId, liveRecording, fabURI, response, edgeToken, writeToken, objectHash;
403
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
404
+ while (1) switch (_context3.prev = _context3.next) {
405
+ case 0:
406
+ name = _ref5.name, _ref5$start = _ref5.start, start = _ref5$start === void 0 ? false : _ref5$start;
407
+ _context3.next = 3;
408
+ return this.StreamStatus({
409
+ name: name
410
+ });
411
+ case 3:
412
+ status = _context3.sent;
413
+ if (!(status.state !== "inactive" && status.state !== "terminated" && status.state !== "stopped")) {
414
+ _context3.next = 6;
415
+ break;
416
+ }
417
+ return _context3.abrupt("return", {
418
+ state: status.state,
419
+ error: "stream still active - must terminate first"
420
+ });
421
+ case 6:
422
+ objectId = status.object_id;
423
+ console.log("START: ", name, "start", start);
424
+ _context3.next = 10;
425
+ return this.ContentObjectLibraryId({
426
+ objectId: objectId
427
+ });
428
+ case 10:
429
+ libraryId = _context3.sent;
430
+ _context3.next = 13;
431
+ return this.ContentObjectMetadata({
432
+ libraryId: libraryId,
433
+ objectId: objectId,
434
+ metadataSubtree: "/live_recording"
435
+ });
436
+ case 13:
437
+ liveRecording = _context3.sent;
438
+ fabURI = liveRecording.fabric_config.ingress_node_api; // Support both hostname and URL ingress_node_api
439
+ if (!fabURI.startsWith("http")) {
440
+ // Assume https
441
+ fabURI = "https://" + fabURI;
442
+ }
443
+ this.SetNodes({
444
+ fabricURIs: [fabURI]
445
+ });
446
+ console.log("Node URI", fabURI, "ID", liveRecording.fabric_config.ingress_node_id);
447
+ _context3.next = 20;
448
+ return this.EditContentObject({
449
+ libraryId: libraryId,
450
+ objectId: objectId
451
+ });
452
+ case 20:
453
+ response = _context3.sent;
454
+ edgeToken = response.write_token;
455
+ console.log("Edge token:", edgeToken);
456
+
457
+ /*
458
+ * Set the metadata, including the edge token.
459
+ */
460
+ _context3.next = 25;
461
+ return this.EditContentObject({
462
+ libraryId: libraryId,
463
+ objectId: objectId
464
+ });
465
+ case 25:
466
+ response = _context3.sent;
467
+ writeToken = response.write_token;
468
+ this.Log("Merging metadata: ", libraryId, objectId, writeToken);
469
+ _context3.next = 30;
470
+ return this.MergeMetadata({
471
+ libraryId: libraryId,
472
+ objectId: objectId,
473
+ writeToken: writeToken,
474
+ metadata: {
475
+ live_recording: {
476
+ status: {
477
+ edge_write_token: edgeToken,
478
+ state: "active" // indicates there is an active session (set to 'closed' when done)
479
+ },
480
+
481
+ fabric_config: {
482
+ edge_write_token: edgeToken
483
+ }
484
+ }
485
+ }
486
+ });
487
+ case 30:
488
+ this.Log("Finalizing content draft: ", libraryId, objectId, writeToken);
489
+ _context3.next = 33;
490
+ return this.FinalizeContentObject({
491
+ libraryId: libraryId,
492
+ objectId: objectId,
493
+ writeToken: writeToken,
494
+ commitMessage: "Create stream edge write token " + edgeToken
495
+ });
496
+ case 33:
497
+ response = _context3.sent;
498
+ objectHash = response.hash;
499
+ this.Log("Finalized object: ", objectHash);
500
+ status = {
501
+ object_id: objectId,
502
+ hash: objectHash,
503
+ library_id: libraryId,
504
+ stream_id: edgeToken,
505
+ edge_write_token: edgeToken,
506
+ fabric_api: fabURI,
507
+ state: "stopped"
508
+ };
509
+ if (start) {
510
+ status = this.StreamStartOrStopOrReset({
511
+ name: name,
512
+ op: start
513
+ });
514
+ }
515
+ return _context3.abrupt("return", status);
516
+ case 39:
517
+ case "end":
518
+ return _context3.stop();
519
+ }
520
+ }, _callee3, this);
521
+ }));
522
+ return function (_x3) {
523
+ return _ref6.apply(this, arguments);
524
+ };
525
+ }();
526
+
527
+ /**
528
+ * Start, stop or reset a stream within the current session (current edge write token)
529
+ *
530
+ * @methodGroup Live Stream
531
+ * @namedParams
532
+ * @param {string} name -
533
+ * @param {string=} op - The operation to perform. Possible values:
534
+ * 'start'
535
+ * 'reset' - Stops current LRO recording and starts a new one. Does
536
+ * not create a new edge write token (just creates a new recording
537
+ * period in the existing edge write token)
538
+ * - 'stop'
539
+ *
540
+ * @return {Object} - The status response for the stream
541
+ *
542
+ */
543
+ exports.StreamStartOrStopOrReset = /*#__PURE__*/function () {
544
+ var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(_ref7) {
545
+ var name, op, status, _tries, tries;
546
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
547
+ while (1) switch (_context4.prev = _context4.next) {
548
+ case 0:
549
+ name = _ref7.name, op = _ref7.op;
550
+ _context4.prev = 1;
551
+ console.log("Stream ", op, ": ", name);
552
+ _context4.next = 5;
553
+ return this.StreamStatus({
554
+ name: name
555
+ });
556
+ case 5:
557
+ status = _context4.sent;
558
+ if (!(status.state != "stopped")) {
559
+ _context4.next = 10;
560
+ break;
561
+ }
562
+ if (!(op === "start")) {
563
+ _context4.next = 10;
564
+ break;
565
+ }
566
+ status.error = "Unable to start stream - state: " + status.state;
567
+ return _context4.abrupt("return", status);
568
+ case 10:
569
+ if (!(status.state == "running" || status.state == "starting" || status.state == "stalled")) {
570
+ _context4.next = 33;
571
+ break;
572
+ }
573
+ console.log("STOPPING");
574
+ _context4.prev = 12;
575
+ _context4.next = 15;
576
+ return this.CallBitcodeMethod({
577
+ libraryId: status.library_id,
578
+ objectId: status.object_id,
579
+ writeToken: status.edge_write_token,
580
+ method: "/live/stop/" + status.tlro,
581
+ constant: false
582
+ });
583
+ case 15:
584
+ _context4.next = 19;
585
+ break;
586
+ case 17:
587
+ _context4.prev = 17;
588
+ _context4.t0 = _context4["catch"](12);
589
+ case 19:
590
+ // Wait until LRO is terminated
591
+ _tries = 10;
592
+ case 20:
593
+ if (!(status.state != "stopped" && _tries-- > 0)) {
594
+ _context4.next = 29;
595
+ break;
596
+ }
597
+ console.log("Wait to terminate - ", status.state);
598
+ _context4.next = 24;
599
+ return sleep(1000);
600
+ case 24:
601
+ _context4.next = 26;
602
+ return this.StreamStatus({
603
+ name: name
604
+ });
605
+ case 26:
606
+ status = _context4.sent;
607
+ _context4.next = 20;
608
+ break;
609
+ case 29:
610
+ console.log("Status after terminate - ", status.state);
611
+ if (!(_tries <= 0)) {
612
+ _context4.next = 33;
613
+ break;
614
+ }
615
+ console.log("Failed to terminate");
616
+ return _context4.abrupt("return", status);
617
+ case 33:
618
+ if (!(op === "stop")) {
619
+ _context4.next = 35;
620
+ break;
621
+ }
622
+ return _context4.abrupt("return", status);
623
+ case 35:
624
+ console.log("STARTING", "edge_write_token", status.edge_write_token);
625
+ _context4.prev = 36;
626
+ _context4.next = 39;
627
+ return this.CallBitcodeMethod({
628
+ libraryId: status.library_id,
629
+ objectId: status.object_id,
630
+ writeToken: status.edge_write_token,
631
+ method: "/live/start",
632
+ constant: false
633
+ });
634
+ case 39:
635
+ _context4.next = 45;
636
+ break;
637
+ case 41:
638
+ _context4.prev = 41;
639
+ _context4.t1 = _context4["catch"](36);
640
+ console.log("LRO Start (failed): ", _context4.t1);
641
+ return _context4.abrupt("return", {
642
+ state: status.state,
643
+ error: "LRO start failed - must create a stream first"
644
+ });
645
+ case 45:
646
+ // Wait until LRO is 'starting'
647
+ tries = 10;
648
+ case 46:
649
+ if (!(status.state != "starting" && tries-- > 0)) {
650
+ _context4.next = 55;
651
+ break;
652
+ }
653
+ console.log("Wait to start - ", status.state);
654
+ _context4.next = 50;
655
+ return sleep(1000);
656
+ case 50:
657
+ _context4.next = 52;
658
+ return this.StreamStatus({
659
+ name: name
660
+ });
661
+ case 52:
662
+ status = _context4.sent;
663
+ _context4.next = 46;
664
+ break;
665
+ case 55:
666
+ console.log("Status after restart - ", status.state);
667
+ return _context4.abrupt("return", status);
668
+ case 59:
669
+ _context4.prev = 59;
670
+ _context4.t2 = _context4["catch"](1);
671
+ console.error(_context4.t2);
672
+ case 62:
673
+ case "end":
674
+ return _context4.stop();
675
+ }
676
+ }, _callee4, this, [[1, 59], [12, 17], [36, 41]]);
677
+ }));
678
+ return function (_x4) {
679
+ return _ref8.apply(this, arguments);
680
+ };
681
+ }();
682
+
683
+ /*
684
+ * Stop the live stream session and close the edge write token.
685
+ * Not implemented fully
686
+ */
687
+ // async StopSession({name}) {
688
+ //
689
+ // try {
690
+ //
691
+ // console.log("TERMINATE: ", name);
692
+ //
693
+ // let conf = await this.LoadConf({name});
694
+ //
695
+ // let objectId = conf.objectId;
696
+ // let libraryId = await this.client.ContentObjectLibraryId({objectId: objectId});
697
+ //
698
+ // let mainMeta = await this.client.ContentObjectMetadata({
699
+ // libraryId: libraryId,
700
+ // objectId: objectId
701
+ // });
702
+ //
703
+ // let fabURI = mainMeta.live_recording.fabric_config.ingress_node_api;
704
+ // // Support both hostname and URL ingress_node_api
705
+ // if(!fabURI.startsWith("http")) {
706
+ // // Assume https
707
+ // fabURI = "https://" + fabURI;
708
+ // }
709
+ //
710
+ // this.client.SetNodes({fabricURIs: [fabURI]});
711
+ //
712
+ // let edgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
713
+ //
714
+ // if(edgeWriteToken === undefined || edgeWriteToken === "") {
715
+ // return {
716
+ // state: "inactive",
717
+ // error: "no active streams - must create a stream first"
718
+ // };
719
+ // }
720
+ // let edgeMeta = await this.client.ContentObjectMetadata({
721
+ // libraryId: libraryId,
722
+ // objectId: objectId,
723
+ // writeToken: edgeWriteToken
724
+ // });
725
+ //
726
+ // // Stop the LRO if running
727
+ // let status = await this.Status({name});
728
+ // if(status.state != "terminated") {
729
+ // console.log("STOPPING");
730
+ // try {
731
+ // await this.client.CallBitcodeMethod({
732
+ // libraryId: status.library_id,
733
+ // objectId: status.object_id,
734
+ // writeToken: status.edge_write_token,
735
+ // method: "/live/stop/" + status.tlro,
736
+ // constant: false
737
+ // });
738
+ // } catch(error) {
739
+ // // The /call/lro/stop API returns empty response
740
+ // // console.log("LRO Stop (failed): ", error);
741
+ // }
742
+ //
743
+ // // Wait until LRO is terminated
744
+ // let tries = 10;
745
+ // while (status.state != "terminated" && tries-- > 0) {
746
+ // console.log("Wait to terminate - ", status.state);
747
+ // await sleep(1000);
748
+ // status = await this.Status({name});
749
+ // }
750
+ // console.log("Status after terminate - ", status.state);
751
+ //
752
+ // if(tries <= 0) {
753
+ // console.log("Failed to terminate");
754
+ // return status;
755
+ // }
756
+ // }
757
+ //
758
+ // // Set stop time
759
+ // edgeMeta.recording_stop_time = Math.floor(new Date().getTime() / 1000);
760
+ // console.log("recording_start_time: ", edgeMeta.recording_start_time);
761
+ // console.log("recording_stop_time: ", edgeMeta.recording_stop_time);
762
+ //
763
+ // edgeMeta.live_recording.status = {
764
+ // state: "terminated",
765
+ // recording_stop_time: edgeMeta.recording_stop_time
766
+ // };
767
+ //
768
+ // edgeMeta.live_recording.fabric_config.edge_write_token = "";
769
+ //
770
+ // await this.client.ReplaceMetadata({
771
+ // libraryId: libraryId,
772
+ // objectId: objectId,
773
+ // writeToken: edgeWriteToken,
774
+ // metadata: edgeMeta
775
+ // });
776
+ //
777
+ // let fin = await this.client.FinalizeContentObject({
778
+ // libraryId,
779
+ // objectId,
780
+ // writeToken: edgeWriteToken,
781
+ // commitMessage: "Finalize live stream - stop time " + edgeMeta.recording_stop_time,
782
+ // publish: false // Don't publish this version because it is not currently useful
783
+ // });
784
+ //
785
+ // return {
786
+ // fin,
787
+ // name: name,
788
+ // edge_write_token: edgeWriteToken,
789
+ // state: "terminated"
790
+ // };
791
+ //
792
+ // } catch(error) {
793
+ // console.error(error);
794
+ // }
795
+ // }
796
+
797
+ // async Initialize({name, drm=false, format}) {
798
+ //
799
+ // const contentTypes = await this.client.ContentTypes();
800
+ //
801
+ // let typeAbrMaster;
802
+ // let typeLiveStream;
803
+ // for (let i = 0; i < Object.keys(contentTypes).length; i ++) {
804
+ // const key = Object.keys(contentTypes)[i];
805
+ // if(contentTypes[key].name.includes("ABR Master") || contentTypes[key].name.includes("Title")) {
806
+ // typeAbrMaster = contentTypes[key].hash;
807
+ // }
808
+ // if(contentTypes[key].name.includes("Live Stream")) {
809
+ // typeLiveStream = contentTypes[key].hash;
810
+ // }
811
+ // }
812
+ //
813
+ // if(typeAbrMaster === undefined || typeLiveStream === undefined) {
814
+ // console.log("ERROR - unable to find content types", "ABR Master", typeAbrMaster, "Live Stream", typeLiveStream);
815
+ // return {};
816
+ // }
817
+ // let res = await this.SetOfferingAndDRM({name, typeAbrMaster, typeLiveStream, drm, format});
818
+ // return res;
819
+ // }
820
+
821
+ // async SetOfferingAndDRM({name, typeAbrMaster, typeLiveStream, drm=false, format}) {
822
+ //
823
+ // let status = await this.Status({name});
824
+ // if(status.state != "inactive" && status.state != "terminated") {
825
+ // return {
826
+ // state: status.state,
827
+ // error: "stream still active - must terminate first"
828
+ // };
829
+ // }
830
+ //
831
+ // let objectId = status.object_id;
832
+ //
833
+ // console.log("INIT: ", name, objectId);
834
+ //
835
+ // const {GenerateOffering} = require("./LiveObjectSetupStepOne");
836
+ //
837
+ // const aBitRate = 128000;
838
+ // const aChannels = 2;
839
+ // const aSampleRate = 48000;
840
+ // const aStreamIndex = 1;
841
+ // const aTimeBase = "1/48000";
842
+ // const aChannelLayout = "stereo";
843
+ //
844
+ // const vBitRate = 14000000;
845
+ // const vHeight = 720;
846
+ // const vStreamIndex = 0;
847
+ // const vWidth = 1280;
848
+ // const vDisplayAspectRatio = "16/9";
849
+ // const vFrameRate = "30000/1001";
850
+ // const vTimeBase = "1/30000"; // "1/16000";
851
+ //
852
+ // const abrProfile = require("./abr_profile_live_drm.json");
853
+ //
854
+ // let playoutFormats = abrProfile.playout_formats;
855
+ // if(format) {
856
+ // drm = true; // Override DRM parameter
857
+ // playoutFormats = {};
858
+ // let formats = format.split(",");
859
+ // for (let i = 0; i < formats.length; i++) {
860
+ // if(formats[i] === "hls-clear") {
861
+ // abrProfile.drm_optional = true;
862
+ // playoutFormats["hls-clear"] = {
863
+ // "drm": null,
864
+ // "protocol": {
865
+ // "type": "ProtoHls"
866
+ // }
867
+ // };
868
+ // continue;
869
+ // }
870
+ // playoutFormats[formats[i]] = abrProfile.playout_formats[formats[i]];
871
+ // }
872
+ // } else if(!drm) {
873
+ // abrProfile.drm_optional = true;
874
+ // playoutFormats = {
875
+ // "hls-clear": {
876
+ // "drm": null,
877
+ // "protocol": {
878
+ // "type": "ProtoHls"
879
+ // }
880
+ // }
881
+ // };
882
+ // }
883
+ //
884
+ // abrProfile.playout_formats = playoutFormats;
885
+ //
886
+ // let libraryId = await this.client.ContentObjectLibraryId({objectId});
887
+ //
888
+ // try {
889
+ //
890
+ // let mainMeta = await this.client.ContentObjectMetadata({
891
+ // libraryId: libraryId,
892
+ // objectId: objectId
893
+ // });
894
+ //
895
+ // let fabURI = mainMeta.live_recording.fabric_config.ingress_node_api;
896
+ // // Support both hostname and URL ingress_node_api
897
+ // if(!fabURI.startsWith("http")) {
898
+ // // Assume https
899
+ // fabURI = "https://" + fabURI;
900
+ // }
901
+ //
902
+ // this.client.SetNodes({fabricURIs: [fabURI]});
903
+ //
904
+ // let streamUrl = mainMeta.live_recording.recording_config.recording_params.origin_url;
905
+ //
906
+ // await GenerateOffering({
907
+ // client: this.client,
908
+ // libraryId,
909
+ // objectId,
910
+ // typeAbrMaster, typeLiveStream,
911
+ // streamUrl,
912
+ // abrProfile,
913
+ // aBitRate, aChannels, aSampleRate, aStreamIndex,
914
+ // aTimeBase,
915
+ // aChannelLayout,
916
+ // vBitRate, vHeight, vStreamIndex, vWidth,
917
+ // vDisplayAspectRatio, vFrameRate, vTimeBase
918
+ // });
919
+ //
920
+ // console.log("GenerateOffering - DONE");
921
+ //
922
+ // return {
923
+ // name,
924
+ // object_id: objectId,
925
+ // state: "initialized"
926
+ // };
927
+ // } catch(error) {
928
+ // console.error(error);
929
+ // }
930
+ // }
931
+
932
+ // Add a content insertion entry
933
+ // Parameters:
934
+ // - insertionTime - seconds (float)
935
+ // - sinceStart - true if time specified since stream start, false if since epoch
936
+ // - duration - seconds (float, deafault 20.0)
937
+ // - targetHash - playable
938
+ // - remove - flag to remove the insertion at that exact 'time' (instead of adding)
939
+ // async Insertion({name, insertionTime, sinceStart, duration, targetHash, remove}) {
940
+ //
941
+ // // Determine audio and video parameters of the insertion
942
+ // const insertionInfo = await this.getOfferingInfo({versionHash: targetHash});
943
+ // const audioAbrDuration = insertionInfo.audio.seg_duration_sec;
944
+ // const videoAbrDuration = insertionInfo.video.seg_duration_sec;
945
+ //
946
+ // if(audioAbrDuration === 0 || videoAbrDuration === 0) {
947
+ // throw new Error("Bad segment duration hash:", targetHash);
948
+ // }
949
+ //
950
+ // if(duration === undefined) {
951
+ // duration = insertionInfo.duration_sec; // Use full duration of the insertion
952
+ // } else {
953
+ // if(duration > insertionInfo.duration_sec) {
954
+ // throw new Error("Bad duration - larger than insertion object duration", insertionInfo.duration_sec);
955
+ // }
956
+ // }
957
+ //
958
+ // let conf = await this.LoadConf({name});
959
+ // let libraryId = await this.client.ContentObjectLibraryId({objectId: conf.objectId});
960
+ // let objectId = conf.objectId;
961
+ //
962
+ // let mainMeta = await this.client.ContentObjectMetadata({
963
+ // libraryId: libraryId,
964
+ // objectId: conf.objectId
965
+ // });
966
+ //
967
+ // let fabURI = mainMeta.live_recording.fabric_config.ingress_node_api;
968
+ //
969
+ // // Support both hostname and URL ingress_node_api
970
+ // if(!fabURI.startsWith("http")) {
971
+ // // Assume https
972
+ // fabURI = "https://" + fabURI;
973
+ // }
974
+ // this.client.SetNodes({fabricURIs: [fabURI]});
975
+ // let edgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
976
+ //
977
+ // let edgeMeta = await this.client.ContentObjectMetadata({
978
+ // libraryId: libraryId,
979
+ // objectId: conf.objectId,
980
+ // writeToken: edgeWriteToken
981
+ // });
982
+ //
983
+ // // Find stream start time (from the most recent recording section)
984
+ // let recordings = edgeMeta.live_recording.recordings;
985
+ // let sequence = 1;
986
+ // let streamStartTime = 0;
987
+ // if(recordings != undefined && recordings.recording_sequence != undefined) {
988
+ // // We have at least one recording - check if still active
989
+ // sequence = recordings.recording_sequence;
990
+ // let period = recordings.live_offering[sequence - 1];
991
+ //
992
+ // if(period.end_time_epoch_sec > 0) {
993
+ // // The last period is closed - apply insertions to the next period
994
+ // sequence ++;
995
+ // } else {
996
+ // // The period is active
997
+ // streamStartTime = period.start_time_epoch_sec;
998
+ // }
999
+ // }
1000
+ //
1001
+ // if(streamStartTime === 0) {
1002
+ // // There is no active period - must use absolute time
1003
+ // if(sinceStart === false) {
1004
+ // throw new Error("Stream not running - must use 'time since start'");
1005
+ // }
1006
+ // }
1007
+ //
1008
+ // // Find the current period playout configuration
1009
+ // if(edgeMeta.live_recording.playout_config.interleaves === undefined) {
1010
+ // edgeMeta.live_recording.playout_config.interleaves = {};
1011
+ // }
1012
+ // if(edgeMeta.live_recording.playout_config.interleaves[sequence] === undefined) {
1013
+ // edgeMeta.live_recording.playout_config.interleaves[sequence] = [];
1014
+ // }
1015
+ //
1016
+ // let playoutConfig = edgeMeta.live_recording.playout_config;
1017
+ // let insertions = playoutConfig.interleaves[sequence];
1018
+ //
1019
+ // let res = {};
1020
+ //
1021
+ // if(!sinceStart) {
1022
+ // insertionTime = insertionTime - streamStartTime;
1023
+ // }
1024
+ //
1025
+ // // Assume insertions are sorted by insertion time
1026
+ // let errs = [];
1027
+ // let currentTime = -1;
1028
+ // let insertionDone = false;
1029
+ // let newInsertion = {
1030
+ // insertion_time: insertionTime,
1031
+ // duration: duration,
1032
+ // audio_abr_duration: audioAbrDuration,
1033
+ // video_abr_duration: videoAbrDuration,
1034
+ // playout: "/qfab/" + targetHash + "/rep/playout" // TO FIX - should be a link
1035
+ // };
1036
+ //
1037
+ // for (let i = 0; i < insertions.length; i ++) {
1038
+ // if(insertions[i].insertion_time <= currentTime) {
1039
+ // // Bad insertion - must be later than current time
1040
+ // append(errs, "Bad insertion - time:", insertions[i].insertion_time);
1041
+ // }
1042
+ // if(remove) {
1043
+ // if(insertions[i].insertion_time === insertionTime) {
1044
+ // insertions.splice(i, 1);
1045
+ // break;
1046
+ // }
1047
+ // } else {
1048
+ // if(insertions[i].insertion_time > insertionTime) {
1049
+ // if(i > 0) {
1050
+ // insertions = [
1051
+ // ...insertions.splice(0, i),
1052
+ // newInsertion,
1053
+ // ...insertions.splice(i)
1054
+ // ];
1055
+ // } else {
1056
+ // insertions = [
1057
+ // newInsertion,
1058
+ // ...insertions.splice(i)
1059
+ // ];
1060
+ // }
1061
+ // insertionDone = true;
1062
+ // break;
1063
+ // }
1064
+ // }
1065
+ // }
1066
+ //
1067
+ // if(!remove && !insertionDone) {
1068
+ // // Add to the end of the insertions list
1069
+ // console.log("Add insertion at the end");
1070
+ // insertions = [
1071
+ // ...insertions,
1072
+ // newInsertion
1073
+ // ];
1074
+ // }
1075
+ //
1076
+ // playoutConfig.interleaves[sequence] = insertions;
1077
+ //
1078
+ // // Store the new insertions in the write token
1079
+ // await this.client.ReplaceMetadata({
1080
+ // libraryId: libraryId,
1081
+ // objectId: objectId,
1082
+ // writeToken: edgeWriteToken,
1083
+ // metadataSubtree: "/live_recording/playout_config",
1084
+ // metadata: edgeMeta.live_recording.playout_config
1085
+ // });
1086
+ //
1087
+ // res.errors = errs;
1088
+ // res.insertions = insertions;
1089
+ // return res;
1090
+ // }
1091
+
1092
+ exports.LoadConf = /*#__PURE__*/function () {
1093
+ var _ref10 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(_ref9) {
1094
+ var name, streamsBuf, streams, conf;
1095
+ return _regeneratorRuntime.wrap(function _callee5$(_context5) {
1096
+ while (1) switch (_context5.prev = _context5.next) {
1097
+ case 0:
1098
+ name = _ref9.name;
1099
+ if (!name.startsWith("iq__")) {
1100
+ _context5.next = 3;
1101
+ break;
1102
+ }
1103
+ return _context5.abrupt("return", {
1104
+ name: name,
1105
+ objectId: name
1106
+ });
1107
+ case 3:
1108
+ _context5.prev = 3;
1109
+ streamsBuf = fs.readFileSync(path.resolve(__dirname, "../liveconf.json"));
1110
+ _context5.next = 11;
1111
+ break;
1112
+ case 7:
1113
+ _context5.prev = 7;
1114
+ _context5.t0 = _context5["catch"](3);
1115
+ console.log("Stream name must be a QID or a label in liveconf.json");
1116
+ return _context5.abrupt("return", {});
1117
+ case 11:
1118
+ streams = JSON.parse(streamsBuf);
1119
+ conf = streams[name];
1120
+ if (!(conf === null)) {
1121
+ _context5.next = 16;
1122
+ break;
1123
+ }
1124
+ console.log("Bad name: ", name);
1125
+ return _context5.abrupt("return", {});
1126
+ case 16:
1127
+ return _context5.abrupt("return", conf);
1128
+ case 17:
1129
+ case "end":
1130
+ return _context5.stop();
1131
+ }
1132
+ }, _callee5, null, [[3, 7]]);
1133
+ }));
1134
+ return function (_x5) {
1135
+ return _ref10.apply(this, arguments);
1136
+ };
1137
+ }();
1138
+
1139
+ /*
1140
+ * Read a playable contnet object and get information about a particular offering
1141
+ */
1142
+ // async getOfferingInfo({versionHash, offering = "default"}) {
1143
+ //
1144
+ // // Content Type check is currently disabled due to permissions
1145
+ // /*
1146
+ // let ct = await this.client.ContentObject({versionHash});
1147
+ // if(ct.type != undefined && ct.type != "") {
1148
+ // let typeMeta = await this.client.ContentObjectMetadata({
1149
+ // versionHash: ct.type
1150
+ // });
1151
+ // if(typeMeta.bitcode_flags != "abrmaster") {
1152
+ // throw new Error("Not a playable VoD object " + versionHash);
1153
+ // }
1154
+ // }
1155
+ // */
1156
+ // let offeringMeta = await this.client.ContentObjectMetadata({
1157
+ // versionHash,
1158
+ // metadataSubtree: "/offerings/" + offering
1159
+ // });
1160
+ //
1161
+ // var info = {
1162
+ // duration_sec: 0 // Minimum of video and audio duration
1163
+ // };
1164
+ // ["video", "audio"].forEach(mt => {
1165
+ // const stream = offeringMeta.media_struct.streams[mt];
1166
+ // info[mt] = {
1167
+ // seg_duration_sec: stream.optimum_seg_dur.float,
1168
+ // duration_sec: stream.duration.float,
1169
+ // frame_rate_rat: stream.rate,
1170
+ // };
1171
+ // if(info.duration_sec === 0 || stream.duration.float < info.duration_sec) {
1172
+ // info.duration_sec = stream.duration.float;
1173
+ // }
1174
+ // });
1175
+ // return info;
1176
+ // }
1177
+
1178
+ // async StreamDownload({name, period}) {
1179
+ //
1180
+ // let conf = await this.LoadConf({name});
1181
+ //
1182
+ // let status = {name};
1183
+ //
1184
+ // try {
1185
+ //
1186
+ // let libraryId = await this.client.ContentObjectLibraryId({objectId: conf.objectId});
1187
+ // status.library_id = libraryId;
1188
+ // status.object_id = conf.objectId;
1189
+ //
1190
+ // let mainMeta = await this.client.ContentObjectMetadata({
1191
+ // libraryId: libraryId,
1192
+ // objectId: conf.objectId
1193
+ // });
1194
+ //
1195
+ // let fabURI = mainMeta.live_recording.fabric_config.ingress_node_api;
1196
+ // if(fabURI === undefined) {
1197
+ // console.log("bad fabric config - missing ingress node API");
1198
+ // }
1199
+ //
1200
+ // // Support both hostname and URL ingress_node_api
1201
+ // if(!fabURI.startsWith("http")) {
1202
+ // // Assume https
1203
+ // fabURI = "https://" + fabURI;
1204
+ // }
1205
+ // this.client.SetNodes({fabricURIs: [fabURI]});
1206
+ //
1207
+ // let edgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
1208
+ // let edgeMeta = await this.client.ContentObjectMetadata({
1209
+ // libraryId: libraryId,
1210
+ // objectId: conf.objectId,
1211
+ // writeToken: edgeWriteToken
1212
+ // });
1213
+ //
1214
+ // // If a stream has never been started return state 'inactive'
1215
+ // if(edgeMeta.live_recording === undefined ||
1216
+ // edgeMeta.live_recording.recordings === undefined ||
1217
+ // edgeMeta.live_recording.recordings.recording_sequence === undefined) {
1218
+ // status.state = "no recordings";
1219
+ // return status;
1220
+ // }
1221
+ //
1222
+ // let recordings = edgeMeta.live_recording.recordings;
1223
+ // status.recording_period_sequence = recordings.recording_sequence;
1224
+ //
1225
+ // let sequence = recordings.recording_sequence;
1226
+ // if(period === undefined || period < 0 || period > sequence - 1) {
1227
+ // period = sequence - 1;
1228
+ // }
1229
+ //
1230
+ // console.log("Downloading stream", name, " period", period, " latest", sequence - 1);
1231
+ //
1232
+ // let recording = recordings.live_offering[period];
1233
+ // if(recording === undefined) {
1234
+ // console.log("ERROR - recording period not found: ", period);
1235
+ // }
1236
+ //
1237
+ // let dpath = "DOWNLOAD/" + edgeWriteToken + "." + period;
1238
+ // !fs.existsSync(dpath) && fs.mkdirSync(dpath, {recursive: true});
1239
+ //
1240
+ // let mts = ["audio", "video"];
1241
+ // for (let mi = 0; mi < mts.length; mi ++) {
1242
+ // let mt = mts[mi];
1243
+ // console.log("Downloading ", mt);
1244
+ // let mtpath = dpath + "/" + mt;
1245
+ // let partsfile = dpath + "/parts_" + mt + ".txt";
1246
+ // !fs.existsSync(mtpath) && fs.mkdirSync(mtpath);
1247
+ // var sources = recording.sources[mt];
1248
+ // for (let i = 0; i < sources.length - 1; i++) {
1249
+ // console.log(sources[i].hash);
1250
+ // let partHash = sources[i].hash;
1251
+ // let buf = await this.client.DownloadPart({
1252
+ // libraryId,
1253
+ // objectId: conf.objectId,
1254
+ // partHash,
1255
+ // format: "buffer",
1256
+ // chunked: false,
1257
+ // callback: ({bytesFinished, bytesTotal}) => {
1258
+ // console.log(" progress: ", bytesFinished + "/" + bytesTotal);
1259
+ // }
1260
+ // });
1261
+ //
1262
+ // let partfile = mtpath + "/" + partHash + ".mp4";
1263
+ // fs.appendFile(partfile, buf, (err) => {
1264
+ // if(err)
1265
+ // console.log(err);
1266
+ // });
1267
+ // fs.appendFile(partsfile, "file '" + mt + "/" + partHash + ".mp4'\n", (err) => {
1268
+ // if(err)
1269
+ // console.log(err);
1270
+ // });
1271
+ // }
1272
+ //
1273
+ // // Concatenate parts into one mp4
1274
+ // let cmd = "ffmpeg -f concat -safe 0 -i " + partsfile + " -c copy " + dpath + "/" + mt + ".mp4";
1275
+ // console.log("Running", cmd);
1276
+ // execSync(cmd);
1277
+ // }
1278
+ //
1279
+ // // Create final mp4 file
1280
+ // let f = dpath + "/download.mp4";
1281
+ // let cmd = "ffmpeg -i " + dpath + "/video.mp4" + " -i " + dpath + "/audio.mp4" + " -map 0:v:0 -map 1:a:0 -c copy -shortest " + f;
1282
+ // console.log("Running", cmd);
1283
+ // execSync(cmd);
1284
+ //
1285
+ // status.file = f;
1286
+ // status.state = "completed";
1287
+ // } catch(e) {
1288
+ // console.log("Download failed", e);
1289
+ // throw e;
1290
+ // }
1291
+ //
1292
+ // return status;
1293
+ // }
1294
+
1295
+ /**
1296
+ * Configure the stream
1297
+ *
1298
+ * @methodGroup Live Stream
1299
+ * @namedParams
1300
+ * @param {string} name -
1301
+ * @param {string=} op - The operation to perform. Possible values:
1302
+ * 'start'
1303
+ * 'reset' - Stops current LRO recording and starts a new one. Does
1304
+ * not create a new edge write token (just creates a new recording
1305
+ * period in the existing edge write token)
1306
+ * - 'stop'
1307
+ *
1308
+ * @return {Object} - The status response for the stream
1309
+ *
1310
+ */
1311
+ exports.StreamConfig = /*#__PURE__*/function () {
1312
+ var _ref12 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(_ref11) {
1313
+ var name, customSettings, conf, status, libraryId, mainMeta, userConfig, hostName, streamUrl, nodes, node, endpoint, probe, controller, timeoutId, probeUrl, lc, liveRecordingConfigStr, liveRecordingConfig, e, writeToken;
1314
+ return _regeneratorRuntime.wrap(function _callee6$(_context6) {
1315
+ while (1) switch (_context6.prev = _context6.next) {
1316
+ case 0:
1317
+ name = _ref11.name, customSettings = _ref11.customSettings;
1318
+ _context6.next = 3;
1319
+ return this.LoadConf({
1320
+ name: name
1321
+ });
1322
+ case 3:
1323
+ conf = _context6.sent;
1324
+ status = {
1325
+ name: name
1326
+ };
1327
+ _context6.next = 7;
1328
+ return this.ContentObjectLibraryId({
1329
+ objectId: conf.objectId
1330
+ });
1331
+ case 7:
1332
+ libraryId = _context6.sent;
1333
+ status.library_id = libraryId;
1334
+ status.object_id = conf.objectId;
1335
+ _context6.next = 12;
1336
+ return this.ContentObjectMetadata({
1337
+ libraryId: libraryId,
1338
+ objectId: conf.objectId
1339
+ });
1340
+ case 12:
1341
+ mainMeta = _context6.sent;
1342
+ userConfig = mainMeta.live_recording_config;
1343
+ status.user_config = userConfig;
1344
+
1345
+ // Get node URI from user config
1346
+ hostName = userConfig.url.replace("udp://", "").replace("rtmp://", "").split(":")[0];
1347
+ streamUrl = new URL(userConfig.url);
1348
+ console.log("Retrieving nodes...");
1349
+ _context6.next = 20;
1350
+ return this.SpaceNodes({
1351
+ matchEndpoint: hostName
1352
+ });
1353
+ case 20:
1354
+ nodes = _context6.sent;
1355
+ if (!(nodes.length < 1)) {
1356
+ _context6.next = 24;
1357
+ break;
1358
+ }
1359
+ status.error = "No node matching stream URL " + streamUrl.href;
1360
+ return _context6.abrupt("return", status);
1361
+ case 24:
1362
+ node = nodes[0];
1363
+ status.node = node;
1364
+ endpoint = node.endpoints[0];
1365
+ this.SetNodes({
1366
+ fabricURIs: [endpoint]
1367
+ });
1368
+
1369
+ // Probe the stream
1370
+ probe = {};
1371
+ controller = new AbortController();
1372
+ timeoutId = setTimeout(function () {
1373
+ controller.abort();
1374
+ }, 60 * 1000); // milliseconds
1375
+ _context6.prev = 31;
1376
+ _context6.next = 34;
1377
+ return this.Rep({
1378
+ libraryId: libraryId,
1379
+ objectId: conf.objectId,
1380
+ rep: "probe"
1381
+ });
1382
+ case 34:
1383
+ probeUrl = _context6.sent;
1384
+ _context6.t0 = this.utils;
1385
+ _context6.next = 38;
1386
+ return HttpClient.Fetch(probeUrl, {
1387
+ body: JSON.stringify({
1388
+ "filename": streamUrl.href,
1389
+ "listen": true
1390
+ }),
1391
+ method: "POST",
1392
+ signal: controller.signal
1393
+ });
1394
+ case 38:
1395
+ _context6.t1 = _context6.sent;
1396
+ _context6.next = 41;
1397
+ return _context6.t0.ResponseToJson.call(_context6.t0, _context6.t1);
1398
+ case 41:
1399
+ probe = _context6.sent;
1400
+ if (probe) {
1401
+ clearTimeout(timeoutId);
1402
+ }
1403
+ if (!probe.errors) {
1404
+ _context6.next = 45;
1405
+ break;
1406
+ }
1407
+ throw probe.errors[0];
1408
+ case 45:
1409
+ _context6.next = 54;
1410
+ break;
1411
+ case 47:
1412
+ _context6.prev = 47;
1413
+ _context6.t2 = _context6["catch"](31);
1414
+ if (!(_context6.t2.code === "ETIMEDOUT")) {
1415
+ _context6.next = 53;
1416
+ break;
1417
+ }
1418
+ throw "Stream probe time out - make sure the stream source is available";
1419
+ case 53:
1420
+ throw _context6.t2;
1421
+ case 54:
1422
+ console.log("PROBE", probe);
1423
+ probe.format.filename = streamUrl.href;
1424
+
1425
+ // Create live recording config
1426
+ lc = new LiveConf(probe, node.id, endpoint, false, false, true);
1427
+ liveRecordingConfigStr = lc.generateLiveConf({
1428
+ audioBitrate: customSettings.audioBitrate,
1429
+ audioIndex: customSettings.audioIndex,
1430
+ partTtl: customSettings.partTtl,
1431
+ channelLayout: customSettings.channelLayout
1432
+ });
1433
+ liveRecordingConfig = JSON.parse(liveRecordingConfigStr);
1434
+ console.log("CONFIG", JSON.stringify(liveRecordingConfig.live_recording));
1435
+
1436
+ // Store live recording config into the stream object
1437
+ _context6.next = 62;
1438
+ return this.EditContentObject({
1439
+ libraryId: libraryId,
1440
+ objectId: conf.objectId
1441
+ });
1442
+ case 62:
1443
+ e = _context6.sent;
1444
+ writeToken = e.write_token;
1445
+ _context6.next = 66;
1446
+ return this.ReplaceMetadata({
1447
+ libraryId: libraryId,
1448
+ objectId: conf.objectId,
1449
+ writeToken: writeToken,
1450
+ metadataSubtree: "live_recording",
1451
+ metadata: liveRecordingConfig.live_recording
1452
+ });
1453
+ case 66:
1454
+ _context6.next = 68;
1455
+ return this.ReplaceMetadata({
1456
+ libraryId: libraryId,
1457
+ objectId: conf.objectId,
1458
+ writeToken: writeToken,
1459
+ metadataSubtree: "probe",
1460
+ metadata: probe
1461
+ });
1462
+ case 68:
1463
+ _context6.next = 70;
1464
+ return this.FinalizeContentObject({
1465
+ libraryId: libraryId,
1466
+ objectId: conf.objectId,
1467
+ writeToken: writeToken,
1468
+ commitMessage: "Apply live stream configuration"
1469
+ });
1470
+ case 70:
1471
+ status.fin = _context6.sent;
1472
+ return _context6.abrupt("return", status);
1473
+ case 72:
1474
+ case "end":
1475
+ return _context6.stop();
1476
+ }
1477
+ }, _callee6, this, [[31, 47]]);
1478
+ }));
1479
+ return function (_x6) {
1480
+ return _ref12.apply(this, arguments);
1481
+ };
1482
+ }();
1483
+
1484
+ // const ChannelStatus = async ({client, name}) => {
1485
+ //
1486
+ // let status = {name: name};
1487
+ //
1488
+ // const conf = channels[name];
1489
+ // if(conf === null) {
1490
+ // console.log("Bad name: ", name);
1491
+ // return;
1492
+ // }
1493
+ //
1494
+ // try {
1495
+ //
1496
+ // let meta = await client.ContentObjectMetadata({
1497
+ // libraryId: conf.libraryId,
1498
+ // objectId: conf.objectId
1499
+ // });
1500
+ //
1501
+ // status.channel_title = meta.public.asset_metadata.title;
1502
+ // let source = meta.channel.offerings.default.items[0].source["/"];
1503
+ // let hash = source.split("/")[2];
1504
+ // status.stream_hash = hash;
1505
+ // latestHash = await client.LatestVersionHash({
1506
+ // versionHash: hash
1507
+ // });
1508
+ // status.stream_latest_hash = latestHash;
1509
+ //
1510
+ // if(hash != latestHash) {
1511
+ // status.warnings = ["Stream version is not the latest"];
1512
+ // }
1513
+ //
1514
+ // let channelFormatsUrl = await client.FabricUrl({
1515
+ // libraryId: conf.libraryId,
1516
+ // objectId: conf.objectId,
1517
+ // rep: "channel/options.json"
1518
+ // });
1519
+ //
1520
+ // try {
1521
+ // let offerings = await got(channelFormatsUrl);
1522
+ // status.offerings = JSON.parse(offerings.body);
1523
+ // } catch(error) {
1524
+ // console.log(error);
1525
+ // status.offerings_error = "Failed to retrieve channel offerings";
1526
+ // }
1527
+ //
1528
+ // status.playout = await ChannelPlayout({client, libraryId: conf.libraryId, objectId: conf.objectId});
1529
+ //
1530
+ // } catch(error) {
1531
+ // console.error(error);
1532
+ // }
1533
+ //
1534
+ // return status;
1535
+ // };
1536
+
1537
+ /*
1538
+ * Performs client-side playout operations - open the channel, read offerings,
1539
+ * retrieve playlist and one video init segment.
1540
+ */
1541
+ // const ChannelPlayout = async({client, libraryId, objectId}) => {
1542
+ //
1543
+ // let playout = {};
1544
+ //
1545
+ // const offerings = await client.AvailableOfferings({
1546
+ // libraryId,
1547
+ // objectId,
1548
+ // handler: "channel",
1549
+ // linkPath: "/public/asset_metadata/offerings"
1550
+ // });
1551
+ //
1552
+ // // Choosing offering 'default'
1553
+ // let offering = offerings.default;
1554
+ //
1555
+ // const playoutOptions = await client.PlayoutOptions({
1556
+ // libraryId,
1557
+ // objectId,
1558
+ // offeringURI: offering.uri
1559
+ // });
1560
+ //
1561
+ // // Retrieve master playlist
1562
+ // let masterPlaylistUrl = playoutOptions["hls"]["playoutMethods"]["fairplay"]["playoutUrl"];
1563
+ // playout.master_playlist_url = masterPlaylistUrl;
1564
+ // try {
1565
+ // //let masterPlaylist = await got(masterPlaylistUrl);
1566
+ // playout.master_playlist = "success";
1567
+ // } catch(error) {
1568
+ // playout.master_playlist = "fail";
1569
+ // }
1570
+ //
1571
+ // let url = new URL(masterPlaylistUrl);
1572
+ // let p = url.pathname.split("/");
1573
+ //
1574
+ // // Retrieve media playlist
1575
+ // p[p.length - 1] = "video/720@14000000/live.m3u8";
1576
+ // let pathMediaPlaylist = p.join("/");
1577
+ // url.pathname = pathMediaPlaylist;
1578
+ // let mediaPlaylistUrl = url.toString();
1579
+ // playout.media_playlist_url = mediaPlaylistUrl;
1580
+ // let mediaPlaylist;
1581
+ // try {
1582
+ // mediaPlaylist = await got(mediaPlaylistUrl);
1583
+ // playout.media_playlist = "success";
1584
+ // } catch(error) {
1585
+ // playout.media_playlist = "fail";
1586
+ // }
1587
+ //
1588
+ // // Retrieve init segment
1589
+ // var regex = new RegExp("^#EXT-X-MAP:URI=\"init.m4s.(.*)\"$", "m");
1590
+ // var match = regex.exec(mediaPlaylist.body);
1591
+ // let initQueryParams;
1592
+ // if(match) {
1593
+ // initQueryParams = match[1];
1594
+ // }
1595
+ //
1596
+ // p[p.length - 1] = "video/720@14000000/init.m4s";
1597
+ // let pathInit = p.join("/");
1598
+ // url.pathname = pathInit;
1599
+ // url.search=initQueryParams;
1600
+ // let initUrl = url.toString();
1601
+ // playout.init_segment_url = initUrl;
1602
+ // /*
1603
+ // try {
1604
+ // let initSegment = await got(initUrl);
1605
+ // playout.init_segment = "success"
1606
+ // } catch(error) {
1607
+ // playout.init_segment = "fail";
1608
+ // }
1609
+ // */
1610
+ // return playout;
1611
+ // };
1612
+
1613
+ // const Summary = async ({client}) => {
1614
+ //
1615
+ // let summary = {};
1616
+ //
1617
+ // try {
1618
+ // for (const [key] of Object.entries(streams)) {
1619
+ // conf = streams[key];
1620
+ // summary[key] = await Status({client, name: key, stopLro: false});
1621
+ // }
1622
+ //
1623
+ // } catch(error) {
1624
+ // console.error(error);
1625
+ // }
1626
+ // return summary;
1627
+ // };
1628
+
1629
+ // const ChannelSummary = async ({client}) => {
1630
+ //
1631
+ // let summary = {};
1632
+ //
1633
+ // try {
1634
+ // for (const [key] of Object.entries(channels)) {
1635
+ // conf = channels[key];
1636
+ // summary[key] = await ChannelStatus({client, name: key});
1637
+ // }
1638
+ //
1639
+ // } catch(error) {
1640
+ // console.error(error);
1641
+ // }
1642
+ // return summary;
1643
+ // };
1644
+
1645
+ // const ConfigStreamRebroadcast = async () => {
1646
+ //
1647
+ // const t = 1619850660;
1648
+ //
1649
+ // try {
1650
+ // let client;
1651
+ // if(conf.clientConf.configUrl) {
1652
+ // client = await ElvClient.FromConfigurationUrl({
1653
+ // configUrl: conf.clientConf.configUrl
1654
+ // });
1655
+ // } else {
1656
+ // client = new ElvClient(conf.clientConf);
1657
+ // }
1658
+ // const wallet = client.GenerateWallet();
1659
+ // const signer = wallet.AddAccount({ privateKey: conf.signerPrivateKey });
1660
+ // client.SetSigner({ signer });
1661
+ // const fabURI = client.fabricURIs[0];
1662
+ // console.log("Fabric URI: " + fabURI);
1663
+ // const ethURI = client.ethereumURIs[0];
1664
+ // console.log("Ethereum URI: " + ethURI);
1665
+ //
1666
+ // client.ToggleLogging(false);
1667
+ //
1668
+ // let mainMeta = await client.ContentObjectMetadata({
1669
+ // libraryId: conf.libraryId,
1670
+ // objectId: conf.objectId
1671
+ // });
1672
+ // console.log("Main meta:", mainMeta);
1673
+ //
1674
+ // edgeWriteToken = mainMeta.edge_write_token;
1675
+ // console.log("Edge: ", edgeWriteToken);
1676
+ //
1677
+ // let edgeMeta = await client.ContentObjectMetadata({
1678
+ // libraryId: conf.libraryId,
1679
+ // objectId: conf.objectId,
1680
+ // writeToken: edgeWriteToken
1681
+ // });
1682
+ // console.log("Edge meta:", edgeMeta);
1683
+ //
1684
+ // //console.log("CONFIG: ", edgeMeta.live_recording_parameters.live_playout_config);
1685
+ // console.log("recording_start_time: ", edgeMeta.recording_start_time);
1686
+ // console.log("recording_stop_time: ", edgeMeta.recording_stop_time);
1687
+ //
1688
+ // // Set rebroadcast start
1689
+ // edgeMeta.live_recording_parameters.live_playout_config.rebroadcast_start_time_sec_epoch = t;
1690
+ //
1691
+ // if(PRINT_DEBUG) console.log("MergeMetadata", conf.libraryId, conf.objectId, writeToken);
1692
+ // await client.MergeMetadata({
1693
+ // libraryId: conf.libraryId,
1694
+ // objectId: conf.objectId,
1695
+ // writeToken: edgeWriteToken,
1696
+ // metadata: {
1697
+ // "live_recording_parameters": {
1698
+ // "live_playout_config" : edgeMeta.live_recording_parameters.live_playout_config
1699
+ // }
1700
+ // }
1701
+ // });
1702
+ //
1703
+ // } catch(error) {
1704
+ // console.error(error);
1705
+ // }
1706
+ // };
1707
+
1708
+ // async function EnsureAll() {
1709
+ // client = await StatusPrep({name: null});
1710
+ // let summary = await Summary({client});
1711
+ //
1712
+ // var res = {
1713
+ // running: 0,
1714
+ // stalled: 0,
1715
+ // terminated: 0
1716
+ // };
1717
+ //
1718
+ // try {
1719
+ // for (const [key, value] of Object.entries(summary)) {
1720
+ // if(value.state === "stalled") {
1721
+ // console.log("Stream stalled: ", key, " - restarting");
1722
+ // console.log("todo ...");
1723
+ // }
1724
+ // res[value.state] = res[value.state] + 1;
1725
+ // }
1726
+ // } catch(error) {
1727
+ // console.error(error);
1728
+ // }
1729
+ //
1730
+ // return res;
1731
+ // }
1732
+
1733
+ /*
1734
+ * Original Run() function - kept for reference
1735
+ */
1736
+ // async function Run() {
1737
+ //
1738
+ // var client;
1739
+ //
1740
+ // switch (command) {
1741
+ //
1742
+ // case "start":
1743
+ // StartStream({name});
1744
+ // break;
1745
+ //
1746
+ // case "status":
1747
+ // client = await StatusPrep({name});
1748
+ // let status = await Status({client, name, stopLro: false});
1749
+ // console.log(JSON.stringify(status, null, 4));
1750
+ // break;
1751
+ //
1752
+ // case "stop":
1753
+ // client = await UpdatePrep({name});
1754
+ // Status({client, name, stopLro: true});
1755
+ // break;
1756
+ //
1757
+ // case "summary":
1758
+ // client = await StatusPrep({name: null});
1759
+ // let summary = await Summary({client});
1760
+ // console.log(JSON.stringify(summary, null, 4));
1761
+ // break;
1762
+ //
1763
+ // case "init": // Set up DRM
1764
+ // SetOfferingAndDRM();
1765
+ // break;
1766
+ //
1767
+ // case "reset": // Stop and start LRO recording (same edge write token)
1768
+ // client = await StatusPrep({name});
1769
+ // let reset = await Reset({client, name, stopLro: false});
1770
+ // console.log(JSON.stringify(reset, null, 4));
1771
+ // break;
1772
+ //
1773
+ // case "channel":
1774
+ // client = await StatusPrep({name});
1775
+ // let channelStatus = await ChannelStatus({client, name});
1776
+ // console.log(JSON.stringify(channelStatus, null, 4));
1777
+ // break;
1778
+ //
1779
+ // case "channel_summary":
1780
+ // client = await StatusPrep({name});
1781
+ // let channelSummary = await ChannelSummary({client, name});
1782
+ // console.log(JSON.stringify(channelSummary, null, 4));
1783
+ // break;
1784
+ //
1785
+ // case "ensure_all": // Check all and restart stalled
1786
+ // let ensureSummary = await EnsureAll();
1787
+ // console.log(JSON.stringify(ensureSummary, null, 4));
1788
+ // break;
1789
+ //
1790
+ // case "future_use_config":
1791
+ // ConfigStreamRebroadcast();
1792
+ // break;
1793
+ //
1794
+ // default:
1795
+ // console.log("Bad command: ", command);
1796
+ // break;
1797
+ //
1798
+ // }
1799
+ // }