@eluvio/elv-client-js 4.0.78 → 4.0.79

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.
@@ -767,8 +767,7 @@ exports.StreamStartOrStopOrReset = async function({name, op}) {
767
767
  };
768
768
 
769
769
  /**
770
- * Stop the live stream session and close the edge write token.
771
- * Not implemented fully
770
+ * Close the edge write token and make the stream object inactive.
772
771
  *
773
772
  * @methodGroup Live Stream
774
773
  * @namedParams
@@ -778,8 +777,7 @@ exports.StreamStartOrStopOrReset = async function({name, op}) {
778
777
  */
779
778
  exports.StreamStopSession = async function({name}) {
780
779
  try {
781
- console.log("TERMINATE: ", name);
782
-
780
+ this.Log(`Terminating stream session for: ${name}`);
783
781
  let conf = await this.LoadConf({name});
784
782
 
785
783
  let {objectId} = conf;
@@ -799,86 +797,81 @@ exports.StreamStopSession = async function({name}) {
799
797
 
800
798
  this.SetNodes({fabricURIs: [fabURI]});
801
799
 
802
- let edgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
800
+ const metaEdgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
803
801
 
804
- if(edgeWriteToken === undefined || edgeWriteToken === "") {
802
+ if(!metaEdgeWriteToken) {
805
803
  return {
806
804
  state: "inactive",
807
- error: "no active streams - must create a stream first"
805
+ error: "The stream is not active"
808
806
  };
809
807
  }
810
- let edgeMeta = await this.ContentObjectMetadata({
811
- libraryId,
812
- objectId,
813
- writeToken: edgeWriteToken
814
- });
815
808
 
816
- // Stop the LRO if running
817
- let status = await this.StreamStatus({name});
818
- if(status.state != "terminated") {
819
- console.log("STOPPING");
820
- try {
821
- await this.CallBitcodeMethod({
822
- libraryId: status.library_id,
823
- objectId: status.object_id,
824
- writeToken: status.edge_write_token,
825
- method: "/live/stop/" + status.tlro,
826
- constant: false
827
- });
828
- } catch(error) {
829
- // The /call/lro/stop API returns empty response
830
- // console.log("LRO Stop (failed): ", error);
831
- }
809
+ try {
810
+ const streamMetadata = await this.ContentObjectMetadata({
811
+ libraryId,
812
+ objectId,
813
+ writeToken: metaEdgeWriteToken
814
+ });
832
815
 
833
- // Wait until LRO is terminated
834
- let tries = 10;
835
- while(status.state != "stopped" && tries-- > 0) {
836
- console.log("Wait to terminate - ", status.state);
837
- await Sleep(1000);
838
- status = await this.StreamStatus({name});
839
- }
840
- console.log("Status after stop - ", status.state);
816
+ const status = await this.StreamStatus({name});
841
817
 
842
- if(tries <= 0) {
843
- console.log("Failed to stop");
844
- return status;
818
+ if(status.state !== "stopped") {
819
+ return {
820
+ state: status.state,
821
+ error: "The stream must be stopped before terminating"
822
+ }
845
823
  }
824
+
825
+ await this.DeleteWriteToken({
826
+ libraryId,
827
+ writeToken: metaEdgeWriteToken
828
+ });
829
+ } catch(error) {
830
+ this.Log(`Unable to retrieve metadata for edge write token ${edgeWriteToken}`);
846
831
  }
847
832
 
848
- // Set stop time
849
- edgeMeta.recording_stop_time = Math.floor(new Date().getTime() / 1000);
850
- console.log("recording_start_time: ", edgeMeta.recording_start_time);
851
- console.log("recording_stop_time: ", edgeMeta.recording_stop_time);
833
+ const {writeToken} = await this.EditContentObject({
834
+ libraryId: libraryId,
835
+ objectId: objectId
836
+ });
852
837
 
853
- edgeMeta.live_recording.status = {
854
- state: "terminated",
855
- recording_stop_time: edgeMeta.recording_stop_time
856
- };
838
+ // Set stop time and inactive state
839
+ const newState = "inactive";
840
+ const stopTime = Math.floor(new Date().getTime() / 1000);
857
841
 
858
- edgeMeta.live_recording.fabric_config.edge_write_token = "";
842
+ const finalizeMetadata = {
843
+ live_recording: {
844
+ status: {
845
+ edge_write_token: "",
846
+ state: newState,
847
+ recording_stop_time: stopTime
848
+ },
849
+ fabric_config: {
850
+ edge_write_token: ""
851
+ }
852
+ },
853
+ recording_stop_time: stopTime
854
+ };
859
855
 
860
- await this.ReplaceMetadata({
856
+ await this.MergeMetadata({
861
857
  libraryId,
862
858
  objectId,
863
- writeToken: edgeWriteToken,
864
- metadata: edgeMeta
859
+ writeToken,
860
+ metadata: finalizeMetadata
865
861
  });
866
862
 
867
863
  let fin = await this.FinalizeContentObject({
868
864
  libraryId,
869
865
  objectId,
870
- writeToken: edgeWriteToken,
871
- commitMessage: "Finalize live stream - stop time " + edgeMeta.recording_stop_time,
872
- publish: false // Don't publish this version because it is not currently useful
866
+ writeToken,
867
+ commitMessage: `Deactivate live stream - stop time ${stopTime}`
873
868
  });
874
869
 
875
870
  return {
876
871
  fin,
877
872
  name,
878
- edge_write_token: edgeWriteToken,
879
- state: "terminated"
873
+ state: newState
880
874
  };
881
-
882
875
  } catch(error) {
883
876
  console.error(error);
884
877
  }
@@ -1419,97 +1412,122 @@ exports.StreamConfig = async function({name, customSettings={}}) {
1419
1412
  };
1420
1413
 
1421
1414
  /**
1422
- * Deactivate the stream
1415
+ * List the pre-allocated URLs for a site
1423
1416
  *
1424
1417
  * @methodGroup Live Stream
1425
1418
  * @namedParams
1426
- * @param {string} name - Object ID or name of the live stream object
1419
+ * @param {string=} - ID of the live stream site object
1427
1420
  *
1428
- * @return {Promise<Object>} - The status response for the stream
1421
+ * @return {Promise<Object>} - The list of stream URLs
1429
1422
  */
1430
- exports.StreamDeactivate = async function({name}) {
1423
+ exports.StreamListUrls = async function({siteId}={}) {
1431
1424
  try {
1432
- let conf = await this.LoadConf({name});
1433
-
1434
- let {objectId} = conf;
1435
- let libraryId = await this.ContentObjectLibraryId({objectId});
1436
-
1437
- let mainMeta = await this.ContentObjectMetadata({
1438
- libraryId,
1439
- objectId
1440
- });
1441
- let status = await this.StreamStatus({name});
1425
+ const STATUS_MAP = {
1426
+ UNCONFIGURED: "unconfigured",
1427
+ UNINITIALIZED: "uninitialized",
1428
+ INACTIVE: "inactive",
1429
+ STOPPED: "stopped",
1430
+ STARTING: "starting",
1431
+ RUNNING: "running",
1432
+ STALLED: "stalled",
1433
+ };
1442
1434
 
1443
- if(!mainMeta.live_recording) {
1444
- return {
1445
- state: status.state,
1446
- error: "Stream must be configured before deactivating"
1447
- };
1448
- }
1435
+ if(!siteId) {
1436
+ const tenantContractId = await this.userProfileClient.TenantContractId();
1449
1437
 
1450
- // Return error if the LRO is running
1451
- if(status.state !== "stopped") {
1452
- return {
1453
- state: status.state,
1454
- error: "Stream must be stopped before deactivating"
1455
- };
1456
- }
1438
+ if(!tenantContractId) {
1439
+ throw Error("No tenant contract ID configured");
1440
+ }
1457
1441
 
1458
- let fabURI = mainMeta.live_recording.fabric_config.ingress_node_api;
1459
- // Support both hostname and URL ingress_node_api
1460
- if(!fabURI.startsWith("http")) {
1461
- // Assume https
1462
- fabURI = "https://" + fabURI;
1442
+ siteId = await this.ContentObjectMetadata({
1443
+ libraryId: tenantContractId.replace("iten", "ilib"),
1444
+ objectId: tenantContractId.replace("iten", "iq__"),
1445
+ metadataSubtree: "public/sites/live_streams",
1446
+ });
1463
1447
  }
1464
1448
 
1465
- this.SetNodes({fabricURIs: [fabURI]});
1466
-
1467
- let edgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
1468
-
1469
- if(edgeWriteToken === undefined || edgeWriteToken === "") {
1470
- return {
1471
- state: "inactive",
1472
- error: "stream is already inactive"
1473
- };
1474
- }
1475
- let edgeMeta = await this.ContentObjectMetadata({
1476
- libraryId,
1477
- objectId,
1478
- writeToken: edgeWriteToken
1449
+ const streamMetadata = await this.ContentObjectMetadata({
1450
+ libraryId: await this.ContentObjectLibraryId({objectId: siteId}),
1451
+ objectId: siteId,
1452
+ metadataSubtree: "public/asset_metadata/live_streams",
1453
+ resolveLinks: true,
1454
+ resolveIgnoreErrors: true
1479
1455
  });
1480
1456
 
1481
- // Set stop time
1482
- edgeMeta.recording_stop_time = Math.floor(new Date().getTime() / 1000);
1483
- const newState = "inactive";
1457
+ const activeUrlMap = {};
1458
+ await this.utils.LimitedMap(
1459
+ 10,
1460
+ Object.keys(streamMetadata || {}),
1461
+ async slug => {
1462
+ const stream = streamMetadata[slug];
1463
+ let versionHash;
1464
+
1465
+ if(
1466
+ stream &&
1467
+ stream.sources &&
1468
+ stream.sources.default &&
1469
+ stream.sources.default["."] &&
1470
+ stream.sources.default["."].container ||
1471
+ ((stream["/"] || "").match(/^\/?qfab\/([\w]+)\/?.+/) || [])[1]
1472
+ ) {
1473
+ versionHash = (
1474
+ stream.sources.default["."].container ||
1475
+ ((stream["/"] || "").match(/^\/?qfab\/([\w]+)\/?.+/) || [])[1]
1476
+ );
1477
+ }
1484
1478
 
1485
- edgeMeta.live_recording.status = {
1486
- state: newState,
1487
- recording_stop_time: edgeMeta.recording_stop_time
1488
- };
1479
+ if(versionHash) {
1480
+ const objectId = this.utils.DecodeVersionHash(versionHash).objectId;
1481
+ const libraryId = await this.ContentObjectLibraryId({objectId});
1482
+
1483
+ const status = await this.StreamStatus({
1484
+ name: objectId
1485
+ });
1486
+
1487
+ const streamMeta = await this.ContentObjectMetadata({
1488
+ objectId,
1489
+ libraryId,
1490
+ select: [
1491
+ "live_recording_config/reference_url",
1492
+ // live_recording_config/url is the old path
1493
+ "live_recording_config/url"
1494
+ ]
1495
+ });
1496
+
1497
+ const url = streamMeta.live_recording_config.reference_url || streamMeta.live_recording_config.url;
1498
+ const isActive = [STATUS_MAP.STARTING, STATUS_MAP.RUNNING, STATUS_MAP.STALLED, STATUS_MAP.STOPPED].includes(status.state);
1499
+
1500
+ if(url && isActive) {
1501
+ activeUrlMap[url] = true;
1502
+ }
1503
+ }
1504
+ }
1505
+ );
1489
1506
 
1490
- edgeMeta.live_recording.fabric_config.edge_write_token = "";
1507
+ const streamUrlStatus = {};
1491
1508
 
1492
- await this.ReplaceMetadata({
1493
- libraryId,
1494
- objectId,
1495
- writeToken: edgeWriteToken,
1496
- metadata: edgeMeta
1509
+ const streamUrls = await this.ContentObjectMetadata({
1510
+ libraryId: await this.ContentObjectLibraryId({objectId: siteId}),
1511
+ objectId: siteId,
1512
+ metadataSubtree: "/live_stream_urls",
1513
+ resolveLinks: true,
1514
+ resolveIgnoreErrors: true
1497
1515
  });
1498
1516
 
1499
- let fin = await this.FinalizeContentObject({
1500
- libraryId,
1501
- objectId,
1502
- writeToken: edgeWriteToken,
1503
- commitMessage: "Deactivate stream"
1517
+ if(!streamUrls) {
1518
+ throw Error("No pre-allocated URLs configured");
1519
+ }
1520
+
1521
+ Object.keys(streamUrls || {}).forEach(protocol => {
1522
+ streamUrlStatus[protocol] = streamUrls[protocol].map(url => {
1523
+ return {
1524
+ url,
1525
+ active: activeUrlMap[url] || false
1526
+ };
1527
+ })
1504
1528
  });
1505
1529
 
1506
- return {
1507
- reference_url: status.reference_url,
1508
- fin,
1509
- name,
1510
- edge_write_token: edgeWriteToken,
1511
- state: newState
1512
- };
1530
+ return streamUrlStatus;
1513
1531
  } catch(error) {
1514
1532
  console.error(error);
1515
1533
  }
@@ -0,0 +1,87 @@
1
+ /* eslint-disable no-console */
2
+ const {ElvClient} = require("../src/ElvClient");
3
+
4
+ const yargs = require("yargs");
5
+ const argv = yargs
6
+ .option("configUrl", {
7
+ description: "Fabric configuration URL (e.g. https://main.net955210.contentfabric.io/config)"
8
+ })
9
+ .option("tenantContractId", {
10
+ description: "tenant Contract Id to set for the user"
11
+ })
12
+ .demandOption(
13
+ ["configUrl", "tenantContractId"],
14
+ "\nUsage: PRIVATE_KEY=<private-key> node CreateUserWithTenantId --configUrl <config-url> --tenantContractId <tenant_id>\n"
15
+ )
16
+ .argv;
17
+
18
+ const TestAddTenantContractId = async ({configUrl, tenantContractId}) => {
19
+ try {
20
+ if(!process.env.PRIVATE_KEY) {
21
+ console.log("ERROR: 'PRIVATE_KEY' environment variable must be set");
22
+ }
23
+
24
+ console.log(`\nAdd tenant to User, tenant contract : ${tenantContractId}\n`);
25
+
26
+ // create user wallet
27
+ const client = await ElvClient.FromConfigurationUrl({configUrl});
28
+ const wallet = client.GenerateWallet();
29
+ const signer = wallet.AddAccount({privateKey: process.env.PRIVATE_KEY});
30
+ client.SetSigner({signer});
31
+
32
+ // set tenant contract ID for user
33
+ await client.userProfileClient.SetTenantContractId({tenantContractId});
34
+ console.log(`tenant contract ${await client.userProfileClient.TenantContractId()} set for user ${client.signer.address}`);
35
+ // console.log(await client.userProfileClient.UserMetadata());
36
+
37
+ // tenant contract ID set during creation of library
38
+ libraryId = await client.CreateContentLibrary({name: "library_with_tenant_contract_id"});
39
+ libraryAddress = client.utils.HashToAddress(libraryId);
40
+ console.log(`library created: ${libraryId}`);
41
+ let actualTenantContractId = await client.TenantContractId({contractAddress:libraryAddress});
42
+ if(tenantContractId !== actualTenantContractId){
43
+ throw Error(`tenant mismatch, actual: ${actualTenantContractId}, expected: ${tenantContractId}`);
44
+ }
45
+ // console.log(await client.ContentObjectMetadata({libraryId, objectId: client.utils.AddressToObjectId(libraryAddress)}));
46
+
47
+ // tenant contract ID set during creation of content type
48
+ const typeMetadata = {
49
+ bitcode_flags: "abrmaster",
50
+ bitcode_format: "builtin",
51
+ public: {
52
+ "eluv.displayApp": "default",
53
+ "eluv.manageApp": "default",
54
+ }
55
+ };
56
+
57
+ const contentTypeId = await client.CreateContentType({
58
+ name: `${tenantContractId} - Title`,
59
+ metadata: {...typeMetadata}
60
+ });
61
+ contentTypeAddress = client.utils.HashToAddress(contentTypeId);
62
+ console.log(`content type - title created: ${contentTypeId}`);
63
+ actualTenantContractId = await client.TenantContractId({contractAddress:contentTypeAddress});
64
+ if(tenantContractId !== actualTenantContractId){
65
+ throw Error(`tenant mismatch, actual: ${actualTenantContractId}, expected: ${tenantContractId}`);
66
+ }
67
+ // console.log(await client.ContentObjectMetadata({
68
+ // libraryId: client.contentSpaceLibraryId,
69
+ // objectId: client.utils.AddressToObjectId(contentTypeAddress)}));
70
+
71
+ // tenant contract ID set during creation of access group
72
+ const groupAddress = await client.CreateAccessGroup({name: "Test Group"});
73
+ console.log(`group created: ${groupAddress}`);
74
+ actualTenantContractId = await client.TenantContractId({contractAddress:groupAddress});
75
+ if(tenantContractId !== actualTenantContractId){
76
+ throw Error(`tenant mismatch, actual: ${actualTenantContractId}, expected: ${tenantContractId}`);
77
+ }
78
+ // console.log(await client.ContentObjectMetadata({
79
+ // libraryId: client.contentSpaceLibraryId,
80
+ // objectId: client.utils.AddressToObjectId(groupAddress)}));
81
+
82
+ } catch(e){
83
+ console.error(e);
84
+ }
85
+ };
86
+
87
+ TestAddTenantContractId(argv);