@eluvio/elv-client-js 4.0.49 → 4.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ElvClient-min.js +11 -10
- package/dist/ElvClient-node-min.js +10 -10
- package/dist/ElvFrameClient-min.js +4 -4
- package/dist/ElvPermissionsClient-min.js +1 -1
- package/dist/ElvWalletClient-min.js +10 -9
- package/dist/ElvWalletClient-node-min.js +10 -10
- package/dist/src/ElvClient.js +441 -306
- package/dist/src/FrameClient.js +2 -2
- package/dist/src/HttpClient.js +10 -1
- package/dist/src/UserProfileClient.js +206 -88
- package/dist/src/Utils.js +10 -0
- package/dist/src/Validation.js +1 -1
- package/dist/src/client/ContentAccess.js +574 -405
- package/dist/src/client/LiveConf.js +330 -0
- package/dist/src/client/LiveStream.js +1785 -0
- package/package.json +1 -1
- package/src/UserProfileClient.js +47 -0
- package/src/client/ContentAccess.js +17 -17
- package/src/client/LiveStream.js +85 -88
|
@@ -0,0 +1,1785 @@
|
|
|
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=v&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, 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;
|
|
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 reocording config
|
|
1426
|
+
lc = new LiveConf(probe, node.id, endpoint, false, false, true);
|
|
1427
|
+
liveRecordingConfigStr = lc.generateLiveConf();
|
|
1428
|
+
liveRecordingConfig = JSON.parse(liveRecordingConfigStr);
|
|
1429
|
+
console.log("CONFIG", JSON.stringify(liveRecordingConfig.live_recording));
|
|
1430
|
+
|
|
1431
|
+
// Store live recording config into the stream object
|
|
1432
|
+
_context6.next = 62;
|
|
1433
|
+
return this.EditContentObject({
|
|
1434
|
+
libraryId: libraryId,
|
|
1435
|
+
objectId: conf.objectId
|
|
1436
|
+
});
|
|
1437
|
+
case 62:
|
|
1438
|
+
e = _context6.sent;
|
|
1439
|
+
writeToken = e.write_token;
|
|
1440
|
+
_context6.next = 66;
|
|
1441
|
+
return this.ReplaceMetadata({
|
|
1442
|
+
libraryId: libraryId,
|
|
1443
|
+
objectId: conf.objectId,
|
|
1444
|
+
writeToken: writeToken,
|
|
1445
|
+
metadataSubtree: "live_recording",
|
|
1446
|
+
metadata: liveRecordingConfig.live_recording
|
|
1447
|
+
});
|
|
1448
|
+
case 66:
|
|
1449
|
+
_context6.next = 68;
|
|
1450
|
+
return this.FinalizeContentObject({
|
|
1451
|
+
libraryId: libraryId,
|
|
1452
|
+
objectId: conf.objectId,
|
|
1453
|
+
writeToken: writeToken,
|
|
1454
|
+
commitMessage: "Apply live stream configuration"
|
|
1455
|
+
});
|
|
1456
|
+
case 68:
|
|
1457
|
+
status.fin = _context6.sent;
|
|
1458
|
+
return _context6.abrupt("return", status);
|
|
1459
|
+
case 70:
|
|
1460
|
+
case "end":
|
|
1461
|
+
return _context6.stop();
|
|
1462
|
+
}
|
|
1463
|
+
}, _callee6, this, [[31, 47]]);
|
|
1464
|
+
}));
|
|
1465
|
+
return function (_x6) {
|
|
1466
|
+
return _ref12.apply(this, arguments);
|
|
1467
|
+
};
|
|
1468
|
+
}();
|
|
1469
|
+
|
|
1470
|
+
// const ChannelStatus = async ({client, name}) => {
|
|
1471
|
+
//
|
|
1472
|
+
// let status = {name: name};
|
|
1473
|
+
//
|
|
1474
|
+
// const conf = channels[name];
|
|
1475
|
+
// if(conf === null) {
|
|
1476
|
+
// console.log("Bad name: ", name);
|
|
1477
|
+
// return;
|
|
1478
|
+
// }
|
|
1479
|
+
//
|
|
1480
|
+
// try {
|
|
1481
|
+
//
|
|
1482
|
+
// let meta = await client.ContentObjectMetadata({
|
|
1483
|
+
// libraryId: conf.libraryId,
|
|
1484
|
+
// objectId: conf.objectId
|
|
1485
|
+
// });
|
|
1486
|
+
//
|
|
1487
|
+
// status.channel_title = meta.public.asset_metadata.title;
|
|
1488
|
+
// let source = meta.channel.offerings.default.items[0].source["/"];
|
|
1489
|
+
// let hash = source.split("/")[2];
|
|
1490
|
+
// status.stream_hash = hash;
|
|
1491
|
+
// latestHash = await client.LatestVersionHash({
|
|
1492
|
+
// versionHash: hash
|
|
1493
|
+
// });
|
|
1494
|
+
// status.stream_latest_hash = latestHash;
|
|
1495
|
+
//
|
|
1496
|
+
// if(hash != latestHash) {
|
|
1497
|
+
// status.warnings = ["Stream version is not the latest"];
|
|
1498
|
+
// }
|
|
1499
|
+
//
|
|
1500
|
+
// let channelFormatsUrl = await client.FabricUrl({
|
|
1501
|
+
// libraryId: conf.libraryId,
|
|
1502
|
+
// objectId: conf.objectId,
|
|
1503
|
+
// rep: "channel/options.json"
|
|
1504
|
+
// });
|
|
1505
|
+
//
|
|
1506
|
+
// try {
|
|
1507
|
+
// let offerings = await got(channelFormatsUrl);
|
|
1508
|
+
// status.offerings = JSON.parse(offerings.body);
|
|
1509
|
+
// } catch(error) {
|
|
1510
|
+
// console.log(error);
|
|
1511
|
+
// status.offerings_error = "Failed to retrieve channel offerings";
|
|
1512
|
+
// }
|
|
1513
|
+
//
|
|
1514
|
+
// status.playout = await ChannelPlayout({client, libraryId: conf.libraryId, objectId: conf.objectId});
|
|
1515
|
+
//
|
|
1516
|
+
// } catch(error) {
|
|
1517
|
+
// console.error(error);
|
|
1518
|
+
// }
|
|
1519
|
+
//
|
|
1520
|
+
// return status;
|
|
1521
|
+
// };
|
|
1522
|
+
|
|
1523
|
+
/*
|
|
1524
|
+
* Performs client-side playout operations - open the channel, read offerings,
|
|
1525
|
+
* retrieve playlist and one video init segment.
|
|
1526
|
+
*/
|
|
1527
|
+
// const ChannelPlayout = async({client, libraryId, objectId}) => {
|
|
1528
|
+
//
|
|
1529
|
+
// let playout = {};
|
|
1530
|
+
//
|
|
1531
|
+
// const offerings = await client.AvailableOfferings({
|
|
1532
|
+
// libraryId,
|
|
1533
|
+
// objectId,
|
|
1534
|
+
// handler: "channel",
|
|
1535
|
+
// linkPath: "/public/asset_metadata/offerings"
|
|
1536
|
+
// });
|
|
1537
|
+
//
|
|
1538
|
+
// // Choosing offering 'default'
|
|
1539
|
+
// let offering = offerings.default;
|
|
1540
|
+
//
|
|
1541
|
+
// const playoutOptions = await client.PlayoutOptions({
|
|
1542
|
+
// libraryId,
|
|
1543
|
+
// objectId,
|
|
1544
|
+
// offeringURI: offering.uri
|
|
1545
|
+
// });
|
|
1546
|
+
//
|
|
1547
|
+
// // Retrieve master playlist
|
|
1548
|
+
// let masterPlaylistUrl = playoutOptions["hls"]["playoutMethods"]["fairplay"]["playoutUrl"];
|
|
1549
|
+
// playout.master_playlist_url = masterPlaylistUrl;
|
|
1550
|
+
// try {
|
|
1551
|
+
// //let masterPlaylist = await got(masterPlaylistUrl);
|
|
1552
|
+
// playout.master_playlist = "success";
|
|
1553
|
+
// } catch(error) {
|
|
1554
|
+
// playout.master_playlist = "fail";
|
|
1555
|
+
// }
|
|
1556
|
+
//
|
|
1557
|
+
// let url = new URL(masterPlaylistUrl);
|
|
1558
|
+
// let p = url.pathname.split("/");
|
|
1559
|
+
//
|
|
1560
|
+
// // Retrieve media playlist
|
|
1561
|
+
// p[p.length - 1] = "video/720@14000000/live.m3u8";
|
|
1562
|
+
// let pathMediaPlaylist = p.join("/");
|
|
1563
|
+
// url.pathname = pathMediaPlaylist;
|
|
1564
|
+
// let mediaPlaylistUrl = url.toString();
|
|
1565
|
+
// playout.media_playlist_url = mediaPlaylistUrl;
|
|
1566
|
+
// let mediaPlaylist;
|
|
1567
|
+
// try {
|
|
1568
|
+
// mediaPlaylist = await got(mediaPlaylistUrl);
|
|
1569
|
+
// playout.media_playlist = "success";
|
|
1570
|
+
// } catch(error) {
|
|
1571
|
+
// playout.media_playlist = "fail";
|
|
1572
|
+
// }
|
|
1573
|
+
//
|
|
1574
|
+
// // Retrieve init segment
|
|
1575
|
+
// var regex = new RegExp("^#EXT-X-MAP:URI=\"init.m4s.(.*)\"$", "m");
|
|
1576
|
+
// var match = regex.exec(mediaPlaylist.body);
|
|
1577
|
+
// let initQueryParams;
|
|
1578
|
+
// if(match) {
|
|
1579
|
+
// initQueryParams = match[1];
|
|
1580
|
+
// }
|
|
1581
|
+
//
|
|
1582
|
+
// p[p.length - 1] = "video/720@14000000/init.m4s";
|
|
1583
|
+
// let pathInit = p.join("/");
|
|
1584
|
+
// url.pathname = pathInit;
|
|
1585
|
+
// url.search=initQueryParams;
|
|
1586
|
+
// let initUrl = url.toString();
|
|
1587
|
+
// playout.init_segment_url = initUrl;
|
|
1588
|
+
// /*
|
|
1589
|
+
// try {
|
|
1590
|
+
// let initSegment = await got(initUrl);
|
|
1591
|
+
// playout.init_segment = "success"
|
|
1592
|
+
// } catch(error) {
|
|
1593
|
+
// playout.init_segment = "fail";
|
|
1594
|
+
// }
|
|
1595
|
+
// */
|
|
1596
|
+
// return playout;
|
|
1597
|
+
// };
|
|
1598
|
+
|
|
1599
|
+
// const Summary = async ({client}) => {
|
|
1600
|
+
//
|
|
1601
|
+
// let summary = {};
|
|
1602
|
+
//
|
|
1603
|
+
// try {
|
|
1604
|
+
// for (const [key] of Object.entries(streams)) {
|
|
1605
|
+
// conf = streams[key];
|
|
1606
|
+
// summary[key] = await Status({client, name: key, stopLro: false});
|
|
1607
|
+
// }
|
|
1608
|
+
//
|
|
1609
|
+
// } catch(error) {
|
|
1610
|
+
// console.error(error);
|
|
1611
|
+
// }
|
|
1612
|
+
// return summary;
|
|
1613
|
+
// };
|
|
1614
|
+
|
|
1615
|
+
// const ChannelSummary = async ({client}) => {
|
|
1616
|
+
//
|
|
1617
|
+
// let summary = {};
|
|
1618
|
+
//
|
|
1619
|
+
// try {
|
|
1620
|
+
// for (const [key] of Object.entries(channels)) {
|
|
1621
|
+
// conf = channels[key];
|
|
1622
|
+
// summary[key] = await ChannelStatus({client, name: key});
|
|
1623
|
+
// }
|
|
1624
|
+
//
|
|
1625
|
+
// } catch(error) {
|
|
1626
|
+
// console.error(error);
|
|
1627
|
+
// }
|
|
1628
|
+
// return summary;
|
|
1629
|
+
// };
|
|
1630
|
+
|
|
1631
|
+
// const ConfigStreamRebroadcast = async () => {
|
|
1632
|
+
//
|
|
1633
|
+
// const t = 1619850660;
|
|
1634
|
+
//
|
|
1635
|
+
// try {
|
|
1636
|
+
// let client;
|
|
1637
|
+
// if(conf.clientConf.configUrl) {
|
|
1638
|
+
// client = await ElvClient.FromConfigurationUrl({
|
|
1639
|
+
// configUrl: conf.clientConf.configUrl
|
|
1640
|
+
// });
|
|
1641
|
+
// } else {
|
|
1642
|
+
// client = new ElvClient(conf.clientConf);
|
|
1643
|
+
// }
|
|
1644
|
+
// const wallet = client.GenerateWallet();
|
|
1645
|
+
// const signer = wallet.AddAccount({ privateKey: conf.signerPrivateKey });
|
|
1646
|
+
// client.SetSigner({ signer });
|
|
1647
|
+
// const fabURI = client.fabricURIs[0];
|
|
1648
|
+
// console.log("Fabric URI: " + fabURI);
|
|
1649
|
+
// const ethURI = client.ethereumURIs[0];
|
|
1650
|
+
// console.log("Ethereum URI: " + ethURI);
|
|
1651
|
+
//
|
|
1652
|
+
// client.ToggleLogging(false);
|
|
1653
|
+
//
|
|
1654
|
+
// let mainMeta = await client.ContentObjectMetadata({
|
|
1655
|
+
// libraryId: conf.libraryId,
|
|
1656
|
+
// objectId: conf.objectId
|
|
1657
|
+
// });
|
|
1658
|
+
// console.log("Main meta:", mainMeta);
|
|
1659
|
+
//
|
|
1660
|
+
// edgeWriteToken = mainMeta.edge_write_token;
|
|
1661
|
+
// console.log("Edge: ", edgeWriteToken);
|
|
1662
|
+
//
|
|
1663
|
+
// let edgeMeta = await client.ContentObjectMetadata({
|
|
1664
|
+
// libraryId: conf.libraryId,
|
|
1665
|
+
// objectId: conf.objectId,
|
|
1666
|
+
// writeToken: edgeWriteToken
|
|
1667
|
+
// });
|
|
1668
|
+
// console.log("Edge meta:", edgeMeta);
|
|
1669
|
+
//
|
|
1670
|
+
// //console.log("CONFIG: ", edgeMeta.live_recording_parameters.live_playout_config);
|
|
1671
|
+
// console.log("recording_start_time: ", edgeMeta.recording_start_time);
|
|
1672
|
+
// console.log("recording_stop_time: ", edgeMeta.recording_stop_time);
|
|
1673
|
+
//
|
|
1674
|
+
// // Set rebroadcast start
|
|
1675
|
+
// edgeMeta.live_recording_parameters.live_playout_config.rebroadcast_start_time_sec_epoch = t;
|
|
1676
|
+
//
|
|
1677
|
+
// if(PRINT_DEBUG) console.log("MergeMetadata", conf.libraryId, conf.objectId, writeToken);
|
|
1678
|
+
// await client.MergeMetadata({
|
|
1679
|
+
// libraryId: conf.libraryId,
|
|
1680
|
+
// objectId: conf.objectId,
|
|
1681
|
+
// writeToken: edgeWriteToken,
|
|
1682
|
+
// metadata: {
|
|
1683
|
+
// "live_recording_parameters": {
|
|
1684
|
+
// "live_playout_config" : edgeMeta.live_recording_parameters.live_playout_config
|
|
1685
|
+
// }
|
|
1686
|
+
// }
|
|
1687
|
+
// });
|
|
1688
|
+
//
|
|
1689
|
+
// } catch(error) {
|
|
1690
|
+
// console.error(error);
|
|
1691
|
+
// }
|
|
1692
|
+
// };
|
|
1693
|
+
|
|
1694
|
+
// async function EnsureAll() {
|
|
1695
|
+
// client = await StatusPrep({name: null});
|
|
1696
|
+
// let summary = await Summary({client});
|
|
1697
|
+
//
|
|
1698
|
+
// var res = {
|
|
1699
|
+
// running: 0,
|
|
1700
|
+
// stalled: 0,
|
|
1701
|
+
// terminated: 0
|
|
1702
|
+
// };
|
|
1703
|
+
//
|
|
1704
|
+
// try {
|
|
1705
|
+
// for (const [key, value] of Object.entries(summary)) {
|
|
1706
|
+
// if(value.state === "stalled") {
|
|
1707
|
+
// console.log("Stream stalled: ", key, " - restarting");
|
|
1708
|
+
// console.log("todo ...");
|
|
1709
|
+
// }
|
|
1710
|
+
// res[value.state] = res[value.state] + 1;
|
|
1711
|
+
// }
|
|
1712
|
+
// } catch(error) {
|
|
1713
|
+
// console.error(error);
|
|
1714
|
+
// }
|
|
1715
|
+
//
|
|
1716
|
+
// return res;
|
|
1717
|
+
// }
|
|
1718
|
+
|
|
1719
|
+
/*
|
|
1720
|
+
* Original Run() function - kept for reference
|
|
1721
|
+
*/
|
|
1722
|
+
// async function Run() {
|
|
1723
|
+
//
|
|
1724
|
+
// var client;
|
|
1725
|
+
//
|
|
1726
|
+
// switch (command) {
|
|
1727
|
+
//
|
|
1728
|
+
// case "start":
|
|
1729
|
+
// StartStream({name});
|
|
1730
|
+
// break;
|
|
1731
|
+
//
|
|
1732
|
+
// case "status":
|
|
1733
|
+
// client = await StatusPrep({name});
|
|
1734
|
+
// let status = await Status({client, name, stopLro: false});
|
|
1735
|
+
// console.log(JSON.stringify(status, null, 4));
|
|
1736
|
+
// break;
|
|
1737
|
+
//
|
|
1738
|
+
// case "stop":
|
|
1739
|
+
// client = await UpdatePrep({name});
|
|
1740
|
+
// Status({client, name, stopLro: true});
|
|
1741
|
+
// break;
|
|
1742
|
+
//
|
|
1743
|
+
// case "summary":
|
|
1744
|
+
// client = await StatusPrep({name: null});
|
|
1745
|
+
// let summary = await Summary({client});
|
|
1746
|
+
// console.log(JSON.stringify(summary, null, 4));
|
|
1747
|
+
// break;
|
|
1748
|
+
//
|
|
1749
|
+
// case "init": // Set up DRM
|
|
1750
|
+
// SetOfferingAndDRM();
|
|
1751
|
+
// break;
|
|
1752
|
+
//
|
|
1753
|
+
// case "reset": // Stop and start LRO recording (same edge write token)
|
|
1754
|
+
// client = await StatusPrep({name});
|
|
1755
|
+
// let reset = await Reset({client, name, stopLro: false});
|
|
1756
|
+
// console.log(JSON.stringify(reset, null, 4));
|
|
1757
|
+
// break;
|
|
1758
|
+
//
|
|
1759
|
+
// case "channel":
|
|
1760
|
+
// client = await StatusPrep({name});
|
|
1761
|
+
// let channelStatus = await ChannelStatus({client, name});
|
|
1762
|
+
// console.log(JSON.stringify(channelStatus, null, 4));
|
|
1763
|
+
// break;
|
|
1764
|
+
//
|
|
1765
|
+
// case "channel_summary":
|
|
1766
|
+
// client = await StatusPrep({name});
|
|
1767
|
+
// let channelSummary = await ChannelSummary({client, name});
|
|
1768
|
+
// console.log(JSON.stringify(channelSummary, null, 4));
|
|
1769
|
+
// break;
|
|
1770
|
+
//
|
|
1771
|
+
// case "ensure_all": // Check all and restart stalled
|
|
1772
|
+
// let ensureSummary = await EnsureAll();
|
|
1773
|
+
// console.log(JSON.stringify(ensureSummary, null, 4));
|
|
1774
|
+
// break;
|
|
1775
|
+
//
|
|
1776
|
+
// case "future_use_config":
|
|
1777
|
+
// ConfigStreamRebroadcast();
|
|
1778
|
+
// break;
|
|
1779
|
+
//
|
|
1780
|
+
// default:
|
|
1781
|
+
// console.log("Bad command: ", command);
|
|
1782
|
+
// break;
|
|
1783
|
+
//
|
|
1784
|
+
// }
|
|
1785
|
+
// }
|