@eluvio/elv-client-js 4.2.31 → 4.2.33
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/package.json +1 -1
- package/src/FrameClient.js +1 -0
- package/src/client/LiveStream.js +165 -5
package/package.json
CHANGED
package/src/FrameClient.js
CHANGED
package/src/client/LiveStream.js
CHANGED
|
@@ -197,6 +197,10 @@ const CueInfo = async ({eventId, status}) => {
|
|
|
197
197
|
* @param {boolean=} options.linkToSite - If enabled, will create a link in the live stream site
|
|
198
198
|
* @param {boolean=} options.initializeDrm - If enabled, will initialize DRM for the object
|
|
199
199
|
* @param {string=} options.ingressNodeId - ID of the ingress node used for stream allocation (required for non-public nodes)
|
|
200
|
+
* @param {Object=} options.metadata - Additional metadata to merge into the live stream object metadata
|
|
201
|
+
* @param {Array<string>=} options.tags - Fabric tags to associate with the live stream
|
|
202
|
+
* @param {Array<string>=} options.groups - Fabric content group IDs to associate with the live stream. Distinct from access groups.
|
|
203
|
+
* @param {Object<string, string>=} options.queryFields - Fabric query fields to associate with the live stream
|
|
200
204
|
*
|
|
201
205
|
* @return {Promise<Object>} - Object containing objectId, libraryId, writeToken, and hash if finalized
|
|
202
206
|
*/
|
|
@@ -212,6 +216,20 @@ exports.StreamCreate = async function({
|
|
|
212
216
|
const existingObject = !!objectId;
|
|
213
217
|
let contentType;
|
|
214
218
|
let adminGroups = options.accessGroups ?? [];
|
|
219
|
+
const customMetadata = options.metadata === undefined ? {} : options.metadata;
|
|
220
|
+
const {tags, groups, queryFields} = options;
|
|
221
|
+
|
|
222
|
+
if(typeof customMetadata !== "object" || Array.isArray(customMetadata)) {
|
|
223
|
+
throw Error("Stream metadata must be an object");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if(customMetadata.public !== undefined && (customMetadata.public === null || typeof customMetadata.public !== "object" || Array.isArray(customMetadata.public))) {
|
|
227
|
+
throw Error("Stream public metadata must be an object");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if(customMetadata.public?.asset_metadata !== undefined && (customMetadata.public.asset_metadata === null || typeof customMetadata.public.asset_metadata !== "object" || Array.isArray(customMetadata.public.asset_metadata))) {
|
|
231
|
+
throw Error("Stream public asset metadata must be an object");
|
|
232
|
+
}
|
|
215
233
|
|
|
216
234
|
// Retrieve live stream content type
|
|
217
235
|
try {
|
|
@@ -265,7 +283,10 @@ exports.StreamCreate = async function({
|
|
|
265
283
|
editResponse = await this.CreateContentObject({
|
|
266
284
|
libraryId,
|
|
267
285
|
options: {
|
|
268
|
-
type: contentType
|
|
286
|
+
type: contentType,
|
|
287
|
+
tags,
|
|
288
|
+
groups,
|
|
289
|
+
queryFields
|
|
269
290
|
}
|
|
270
291
|
});
|
|
271
292
|
objectId = editResponse.objectId;
|
|
@@ -273,14 +294,27 @@ exports.StreamCreate = async function({
|
|
|
273
294
|
|
|
274
295
|
const {writeToken} = editResponse;
|
|
275
296
|
const {
|
|
276
|
-
accessGroup,
|
|
277
|
-
name=defaultName,
|
|
278
297
|
displayTitle,
|
|
279
298
|
description,
|
|
280
299
|
permission="editable",
|
|
281
300
|
ingressNodeId,
|
|
282
301
|
initializeDrm=true
|
|
283
302
|
} = options;
|
|
303
|
+
const name = options.name ?? customMetadata.public?.name ?? defaultName;
|
|
304
|
+
|
|
305
|
+
if(existingObject) {
|
|
306
|
+
if(tags !== undefined) {
|
|
307
|
+
await this.SetContentObjectTags({libraryId, objectId, writeToken, tags});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if(groups !== undefined) {
|
|
311
|
+
await this.SetContentObjectGroups({libraryId, objectId, writeToken, groups});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if(queryFields !== undefined) {
|
|
315
|
+
await this.SetContentObjectQueryFields({libraryId, objectId, writeToken, queryFields});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
284
318
|
|
|
285
319
|
if(!liveRecordingConfig) {
|
|
286
320
|
liveRecordingConfig = {};
|
|
@@ -311,10 +345,9 @@ exports.StreamCreate = async function({
|
|
|
311
345
|
})
|
|
312
346
|
);
|
|
313
347
|
|
|
314
|
-
const
|
|
348
|
+
const generatedMetadata = {
|
|
315
349
|
public: {
|
|
316
350
|
name,
|
|
317
|
-
description,
|
|
318
351
|
asset_metadata: {
|
|
319
352
|
display_title: displayTitle || name,
|
|
320
353
|
title: name || displayTitle || defaultName,
|
|
@@ -324,6 +357,22 @@ exports.StreamCreate = async function({
|
|
|
324
357
|
}
|
|
325
358
|
}
|
|
326
359
|
};
|
|
360
|
+
const metadata = R.mergeDeepRight(generatedMetadata, customMetadata);
|
|
361
|
+
|
|
362
|
+
// Explicit options take precedence over custom metadata
|
|
363
|
+
metadata.public.name = name;
|
|
364
|
+
if(options.name !== undefined) {
|
|
365
|
+
metadata.public.asset_metadata.title = name;
|
|
366
|
+
metadata.public.asset_metadata.slug = slugify(name);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if(displayTitle !== undefined) {
|
|
370
|
+
metadata.public.asset_metadata.display_title = displayTitle;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if(description !== undefined) {
|
|
374
|
+
metadata.public.description = description;
|
|
375
|
+
}
|
|
327
376
|
|
|
328
377
|
const currentLiveRecordingConfig = await this.ContentObjectMetadata({
|
|
329
378
|
libraryId,
|
|
@@ -1286,6 +1335,117 @@ exports.StreamStartRecording = async function({name, start=false}) {
|
|
|
1286
1335
|
return status;
|
|
1287
1336
|
};
|
|
1288
1337
|
|
|
1338
|
+
/**
|
|
1339
|
+
* Stop the current live recording and start a new one with a new edge write token.
|
|
1340
|
+
* The main object is finalized only once.
|
|
1341
|
+
*
|
|
1342
|
+
* @methodGroup Live Stream
|
|
1343
|
+
* @namedParams
|
|
1344
|
+
* @param {string} name - Object ID or name of the live stream object
|
|
1345
|
+
*
|
|
1346
|
+
* @return {Promise<Object>} - The status response for the new recording session
|
|
1347
|
+
*/
|
|
1348
|
+
exports.StreamRestartRecording = async function({name}) {
|
|
1349
|
+
let status = await this.StreamStatus({name});
|
|
1350
|
+
const oldEdgeWriteToken = status?.edgeWriteToken;
|
|
1351
|
+
|
|
1352
|
+
if(!oldEdgeWriteToken) {
|
|
1353
|
+
throw new Error("Unable to restart stream - no active recording session");
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
if(["running", "starting", "stalled"].includes(status.state)) {
|
|
1357
|
+
status = await this.StreamStartOrStopOrReset({name, op: "stop"});
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
const IsStopped = currentStatus =>
|
|
1361
|
+
currentStatus?.state === "stopped" &&
|
|
1362
|
+
!currentStatus.error &&
|
|
1363
|
+
currentStatus.edgeWriteToken === oldEdgeWriteToken;
|
|
1364
|
+
|
|
1365
|
+
if(!IsStopped(status)) {
|
|
1366
|
+
throw new Error(`Unable to restart stream - current LRO did not stop definitively (state: ${status?.state || "unknown"})`);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const {libraryId, objectId, fabricApi} = status;
|
|
1370
|
+
if(!fabricApi) {
|
|
1371
|
+
throw new Error("Unable to restart stream - ingress node API is unavailable");
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
this.SetNodes({fabricURIs: [fabricApi]});
|
|
1375
|
+
|
|
1376
|
+
let mainWriteToken;
|
|
1377
|
+
let newEdgeWriteToken;
|
|
1378
|
+
let mainObjectFinalizeStarted = false;
|
|
1379
|
+
|
|
1380
|
+
try {
|
|
1381
|
+
({writeToken: mainWriteToken} = await this.EditContentObject({
|
|
1382
|
+
libraryId,
|
|
1383
|
+
objectId
|
|
1384
|
+
}));
|
|
1385
|
+
|
|
1386
|
+
({writeToken: newEdgeWriteToken} = await this.EditContentObject({
|
|
1387
|
+
libraryId,
|
|
1388
|
+
objectId
|
|
1389
|
+
}));
|
|
1390
|
+
|
|
1391
|
+
await this.MergeMetadata({
|
|
1392
|
+
libraryId,
|
|
1393
|
+
objectId,
|
|
1394
|
+
writeToken: mainWriteToken,
|
|
1395
|
+
metadata: {
|
|
1396
|
+
live_recording: {
|
|
1397
|
+
status: {
|
|
1398
|
+
edge_write_token: newEdgeWriteToken,
|
|
1399
|
+
state: "active"
|
|
1400
|
+
},
|
|
1401
|
+
fabric_config: {
|
|
1402
|
+
edge_write_token: newEdgeWriteToken
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
// Verify the old LRO is definitively stopped before publishing and deleting the old token.
|
|
1409
|
+
status = await this.StreamStatus({name});
|
|
1410
|
+
if(!IsStopped(status)) {
|
|
1411
|
+
throw new Error(`Unable to restart stream - current LRO stop could not be confirmed (state: ${status?.state || "unknown"})`);
|
|
1412
|
+
}
|
|
1413
|
+
mainObjectFinalizeStarted = true;
|
|
1414
|
+
const finalizeResponse = await this.FinalizeContentObject({
|
|
1415
|
+
libraryId,
|
|
1416
|
+
objectId,
|
|
1417
|
+
writeToken: mainWriteToken,
|
|
1418
|
+
commitMessage: `Restart stream with edge write token ${newEdgeWriteToken}`
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
try {
|
|
1422
|
+
await this.DeleteWriteToken({writeToken: oldEdgeWriteToken});
|
|
1423
|
+
} catch(error) {
|
|
1424
|
+
this.Log(`Unable to delete old edge write token ${oldEdgeWriteToken}: ${error.message}`, true);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
const restartedStatus = await this.StreamStartOrStopOrReset({name, op: "start"});
|
|
1428
|
+
return {
|
|
1429
|
+
...restartedStatus,
|
|
1430
|
+
hash: finalizeResponse.hash
|
|
1431
|
+
};
|
|
1432
|
+
} catch(error) {
|
|
1433
|
+
// Before main-object finalization begins, both new drafts are safe to discard and the old
|
|
1434
|
+
// stopped recording remains referenced by the published object.
|
|
1435
|
+
if(!mainObjectFinalizeStarted) {
|
|
1436
|
+
for(const writeToken of [newEdgeWriteToken, mainWriteToken].filter(Boolean)) {
|
|
1437
|
+
try {
|
|
1438
|
+
await this.DeleteWriteToken({writeToken});
|
|
1439
|
+
} catch(cleanupError) {
|
|
1440
|
+
this.Log(`Unable to clean up restart write token ${writeToken}: ${cleanupError.message}`, true);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
throw error;
|
|
1446
|
+
}
|
|
1447
|
+
};
|
|
1448
|
+
|
|
1289
1449
|
/**
|
|
1290
1450
|
* Start, stop or reset a stream within the current session (current edge write token)
|
|
1291
1451
|
*
|