@eluvio/elv-client-js 4.0.48 → 4.0.50

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eluvio/elv-client-js",
3
- "version": "4.0.48",
3
+ "version": "4.0.50",
4
4
  "description": "Javascript client for the Eluvio Content Fabric",
5
5
  "main": "src/index.js",
6
6
  "author": "Kevin Talmadge",
package/src/ElvClient.js CHANGED
@@ -571,78 +571,78 @@ class ElvClient {
571
571
  *
572
572
  * @methodGroup Nodes
573
573
  * @namedParams
574
- * @param {string} - matchEndpoint
575
- * @param {string} - matchNodeId
574
+ * @param {string=} matchEndpoint - Return node(s) matching the specified endpoint
575
+ * @param {string=} matchNodeId - Return node(s) matching the specified node ID
576
576
  *
577
- * @return {Array<Object>} - A list of nodes in the space
577
+ * @return {Array<Object>} - A list of nodes in the space matching the parameters
578
578
  */
579
- async SpaceNodes({matchEndpoint, matchNodeId}) {
579
+ async SpaceNodes({matchEndpoint, matchNodeId}={}) {
580
580
  let bign = await this.CallContractMethod({
581
581
  contractAddress: this.contentSpaceAddress,
582
582
  methodName: "numActiveNodes",
583
583
  });
584
584
  let n = bign.toNumber();
585
- let nodes = [];
586
585
 
587
- for (let i = 0; i < n; i ++) {
586
+ return (await Utils.LimitedMap(
587
+ 5,
588
+ [...new Array(n)],
589
+ async (_, index) => {
590
+ let bigi = Ethers.BigNumber.from(index);
591
+ let addr = await this.CallContractMethod({
592
+ contractAddress: this.contentSpaceAddress,
593
+ methodName: "activeNodeAddresses",
594
+ methodArgs: [bigi],
595
+ formatArguments: true
596
+ });
588
597
 
589
- let bigi = Ethers.BigNumber.from(i);
590
- let addr = await this.CallContractMethod({
591
- contractAddress: this.contentSpaceAddress,
592
- methodName: "activeNodeAddresses",
593
- methodArgs: [bigi],
594
- formatArguments: true
595
- });
598
+ let locatorsHex = await this.CallContractMethod({
599
+ contractAddress: this.contentSpaceAddress,
600
+ methodName: "activeNodeLocators",
601
+ methodArgs: [bigi]
602
+ });
596
603
 
597
- let locatorsHex = await this.CallContractMethod({
598
- contractAddress: this.contentSpaceAddress,
599
- methodName: "activeNodeLocators",
600
- methodArgs: [bigi]
601
- });
604
+ let nodeId = this.utils.AddressToNodeId(addr);
602
605
 
603
- let nodeId = this.utils.AddressToNodeId(addr);
606
+ if(matchNodeId &&nodeId !== matchNodeId) {
607
+ return;
608
+ }
604
609
 
605
- if (matchNodeId != undefined && nodeId != matchNodeId) {
606
- continue; // Not a match
607
- }
610
+ let node = {id: nodeId, endpoints: []};
608
611
 
609
- let node = {id: nodeId, endpoints: []};
612
+ // Parse locators CBOR
613
+ let buffer = locatorsHex.slice(2, locatorsHex.length); // Skip "0x"
614
+ let hex = buffer.toString("hex");
615
+ let locators = CBOR.decodeAllSync(hex);
610
616
 
611
- // Parse locators CBOR
612
- let buffer = locatorsHex.slice(2, locatorsHex.length); // Skip "0x"
613
- let hex = buffer.toString("hex");
614
- let locators = CBOR.decodeAllSync(hex);
617
+ let match = false;
615
618
 
616
- let match = false;
619
+ if(locators.length >= 5) {
620
+ let fabArray = locators[4].fab;
621
+ if(fabArray) {
622
+ for(let i = 0; i < fabArray.length; i ++) {
623
+ let host = fabArray[i].host;
617
624
 
618
- if(locators.length >= 5) {
619
- let fabArray = locators[4].fab;
620
- if(fabArray != undefined) {
621
- for(let i = 0; i < fabArray.length; i ++) {
622
- let host = fabArray[i].host;
625
+ if(matchEndpoint && !matchEndpoint.includes(host)) {
626
+ continue; // Not a match
627
+ }
623
628
 
624
- if(matchEndpoint != undefined && !host.includes(matchEndpoint)) {
625
- continue; // Not a match
626
- }
629
+ match = true;
630
+ let endpoint = fabArray[i].scheme + "://" + host;
627
631
 
628
- match = true;
629
- let endpoint = fabArray[i].scheme + "://" + host;
632
+ if(fabArray[i].port) {
633
+ endpoint = endpoint + ":" + fabArray[i].port;
634
+ }
630
635
 
631
- if(fabArray[i].port != "") {
632
- endpoint = endpoint + ":" + fabArray[i].port;
636
+ endpoint = endpoint + "/" + fabArray[i].path;
637
+ node.endpoints.push(endpoint);
633
638
  }
634
-
635
- endpoint = endpoint + "/" + fabArray[i].path;
636
- node.endpoints.push(endpoint);
637
639
  }
638
640
  }
639
- }
640
- if(match) {
641
- nodes.push(node);
642
- }
643
- }
644
641
 
645
- return nodes;
642
+ return match ? node : undefined;
643
+ }
644
+ ))
645
+ .filter(n => n);
646
646
  }
647
647
 
648
648
  /**
@@ -131,7 +131,7 @@ class LiveConf {
131
131
 
132
132
  getStreamDataForCodecType(codecType) {
133
133
  let stream = null;
134
- for (let index = 0; index < this.probeData.streams.length; index++) {
134
+ for(let index = 0; index < this.probeData.streams.length; index++) {
135
135
  if(this.probeData.streams[index].codec_type == codecType) {
136
136
  stream = this.probeData.streams[index];
137
137
  }
@@ -166,7 +166,7 @@ class LiveConf {
166
166
  let frameRate = videoStream.frame_rate;
167
167
 
168
168
  let seg ={};
169
- switch (frameRate) {
169
+ switch(frameRate) {
170
170
  case "24":
171
171
  seg.video = 30 * sourceTimescale;
172
172
  seg.audio = 30 * 48000;
@@ -225,7 +225,7 @@ class LiveConf {
225
225
  syncAudioToStreamIdValue() {
226
226
  let sync_id = -1;
227
227
  let videoStream = this.getStreamDataForCodecType("video");
228
- switch (this.probeKind()) {
228
+ switch(this.probeKind()) {
229
229
  case "udp":
230
230
  sync_id = videoStream.stream_id;
231
231
  break;
@@ -68,12 +68,12 @@ exports.StreamStatus = async function({name, stopLro=false, showParams=false}) {
68
68
  ]
69
69
  });
70
70
 
71
- if (mainMeta.live_recording_config == undefined || mainMeta.live_recording_config.url == undefined) {
71
+ if(mainMeta.live_recording_config == undefined || mainMeta.live_recording_config.url == undefined) {
72
72
  status.state = "unconfigured";
73
73
  return status;
74
74
  }
75
75
 
76
- if (mainMeta.live_recording == undefined || mainMeta.live_recording.fabric_config == undefined ||
76
+ if(mainMeta.live_recording == undefined || mainMeta.live_recording.fabric_config == undefined ||
77
77
  mainMeta.live_recording.playout_config == undefined || mainMeta.live_recording.recording_config == undefined) {
78
78
  status.state = "uninitialized";
79
79
  return status;
@@ -96,7 +96,7 @@ exports.StreamStatus = async function({name, stopLro=false, showParams=false}) {
96
96
  status.url = mainMeta.live_recording.recording_config.recording_params.origin_url;
97
97
 
98
98
  let edgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
99
- if (edgeWriteToken == undefined) {
99
+ if(edgeWriteToken == undefined) {
100
100
  status.state = "inactive";
101
101
  return status;
102
102
  }
@@ -157,7 +157,7 @@ exports.StreamStatus = async function({name, stopLro=false, showParams=false}) {
157
157
  if((edgeMeta.live_recording.playout_config.interleaves != undefined) &&
158
158
  (edgeMeta.live_recording.playout_config.interleaves[sequence] != undefined)) {
159
159
  let insertions = edgeMeta.live_recording.playout_config.interleaves[sequence];
160
- for (let i = 0; i < insertions.length; i ++) {
160
+ for(let i = 0; i < insertions.length; i ++) {
161
161
  let insertionTimeSinceEpoch = recording_period.start_time_epoch_sec + insertions[i].insertion_time;
162
162
  status.insertions[i] = {
163
163
  insertion_time_since_start: insertions[i].insertion_time,
@@ -190,7 +190,7 @@ exports.StreamStatus = async function({name, stopLro=false, showParams=false}) {
190
190
  state = "starting";
191
191
  } else if(state === "running" && sinceLastFinalize > 32.9) {
192
192
  state = "stalled";
193
- } else if (state == "terminated") {
193
+ } else if(state == "terminated") {
194
194
  state = "stopped";
195
195
  }
196
196
  status.state = state;
@@ -493,7 +493,7 @@ exports.StreamStartOrStopOrReset = async function({name, op}) {
493
493
 
494
494
  // Wait until LRO is 'starting'
495
495
  let tries = 10;
496
- while (status.state != "starting" && tries-- > 0) {
496
+ while(status.state != "starting" && tries-- > 0) {
497
497
  console.log("Wait to start - ", status.state);
498
498
  await sleep(1000);
499
499
  status = await this.StreamStatus({name});
@@ -505,7 +505,7 @@ exports.StreamStartOrStopOrReset = async function({name, op}) {
505
505
  } catch(error) {
506
506
  console.error(error);
507
507
  }
508
- }
508
+ };
509
509
 
510
510
  /*
511
511
  * Stop the live stream session and close the edge write token.
@@ -943,7 +943,7 @@ exports.LoadConf = async function({name}) {
943
943
  }
944
944
 
945
945
  return conf;
946
- }
946
+ };
947
947
 
948
948
  /*
949
949
  * Read a playable contnet object and get information about a particular offering
@@ -1122,108 +1122,105 @@ exports.StreamConfig = async function({name}) {
1122
1122
  let conf = await this.LoadConf({name});
1123
1123
  let status = {name};
1124
1124
 
1125
- try {
1126
- let libraryId = await this.ContentObjectLibraryId({objectId: conf.objectId});
1127
- status.library_id = libraryId;
1128
- status.object_id = conf.objectId;
1129
-
1130
- let mainMeta = await this.ContentObjectMetadata({
1131
- libraryId: libraryId,
1132
- objectId: conf.objectId
1133
- });
1134
-
1135
- let userConfig = mainMeta.live_recording_config;
1136
- status.user_config = userConfig;
1125
+ let libraryId = await this.ContentObjectLibraryId({objectId: conf.objectId});
1126
+ status.library_id = libraryId;
1127
+ status.object_id = conf.objectId;
1137
1128
 
1138
- // Get node URI from user config
1139
- const hostName = userConfig.url.replace("udp://", "").split(":")[0];
1140
- const streamUrl = new URL(userConfig.url);
1129
+ let mainMeta = await this.ContentObjectMetadata({
1130
+ libraryId: libraryId,
1131
+ objectId: conf.objectId
1132
+ });
1141
1133
 
1142
- console.log("Retrieving nodes...");
1143
- let nodes = await this.SpaceNodes({matchEndpoint: hostName});
1144
- if(nodes.length < 1) {
1145
- status.error = "No node matching stream URL " + streamUrl.href;
1146
- return status;
1147
- }
1148
- const node = nodes[0];
1149
- status.node = node;
1150
-
1151
- let endpoint = node.endpoints[0];
1152
- this.SetNodes({fabricURIs: [endpoint]});
1153
-
1154
- // Probe the stream
1155
- let probe = {};
1156
- const controller = new AbortController();
1157
- const timeoutId = setTimeout(() => {
1158
- controller.abort();
1159
- }, 60 * 1000); // milliseconds
1160
- try {
1134
+ let userConfig = mainMeta.live_recording_config;
1135
+ status.user_config = userConfig;
1161
1136
 
1162
- let probeUrl = await this.Rep({
1163
- libraryId,
1164
- objectId: conf.objectId,
1165
- rep: "probe"
1166
- });
1137
+ // Get node URI from user config
1138
+ const hostName = userConfig.url.replace("udp://", "").replace("rtmp://", "").split(":")[0];
1139
+ const streamUrl = new URL(userConfig.url);
1167
1140
 
1168
- probe = await this.utils.ResponseToJson(
1169
- await HttpClient.Fetch(probeUrl, {
1170
- body: JSON.stringify({
1171
- "filename": streamUrl.href,
1172
- "listen": true
1173
- }),
1174
- method: "POST",
1175
- signal: controller.signal
1176
- })
1177
- );
1141
+ console.log("Retrieving nodes...");
1142
+ let nodes = await this.SpaceNodes({matchEndpoint: hostName});
1143
+ if(nodes.length < 1) {
1144
+ status.error = "No node matching stream URL " + streamUrl.href;
1145
+ return status;
1146
+ }
1147
+ const node = nodes[0];
1148
+ status.node = node;
1149
+
1150
+ let endpoint = node.endpoints[0];
1151
+ this.SetNodes({fabricURIs: [endpoint]});
1152
+
1153
+ // Probe the stream
1154
+ let probe = {};
1155
+ const controller = new AbortController();
1156
+ const timeoutId = setTimeout(() => {
1157
+ controller.abort();
1158
+ }, 60 * 1000); // milliseconds
1159
+ try {
1178
1160
 
1179
- if(probe) { clearTimeout(timeoutId); }
1180
- } catch(error) {
1181
- if(error.code === "ETIMEDOUT") {
1182
- status.error = "Stream probe time out - make sure the stream source is available";
1183
- } else {
1184
- console.log("Stream probe failed", error);
1185
- }
1186
- }
1161
+ let probeUrl = await this.Rep({
1162
+ libraryId,
1163
+ objectId: conf.objectId,
1164
+ rep: "probe"
1165
+ });
1187
1166
 
1188
- probe.format.filename = streamUrl.href;
1189
- console.log("PROBE", probe);
1167
+ probe = await this.utils.ResponseToJson(
1168
+ await HttpClient.Fetch(probeUrl, {
1169
+ body: JSON.stringify({
1170
+ "filename": streamUrl.href,
1171
+ "listen": true
1172
+ }),
1173
+ method: "POST",
1174
+ signal: controller.signal
1175
+ })
1176
+ );
1190
1177
 
1191
- // Create live reocording config
1192
- let lc = new LiveConf(probe, node.id, endpoint, false, false, true);
1178
+ if(probe) { clearTimeout(timeoutId); }
1193
1179
 
1194
- const liveRecordingConfigStr = lc.generateLiveConf();
1195
- let liveRecordingConfig = JSON.parse(liveRecordingConfigStr);
1196
- console.log("CONFIG", JSON.stringify(liveRecordingConfig.live_recording));
1180
+ if(probe.errors) {
1181
+ throw probe.errors[0];
1182
+ }
1183
+ } catch(error) {
1184
+ if(error.code === "ETIMEDOUT") {
1185
+ throw "Stream probe time out - make sure the stream source is available";
1186
+ } else {
1187
+ throw error;
1188
+ }
1189
+ }
1197
1190
 
1198
- // Store live recording config into the stream object
1199
- let e = await this.EditContentObject({
1200
- libraryId,
1201
- objectId: conf.objectId
1202
- });
1203
- let writeToken = e.write_token;
1191
+ console.log("PROBE", probe);
1192
+ probe.format.filename = streamUrl.href;
1204
1193
 
1205
- await this.ReplaceMetadata({
1206
- libraryId,
1207
- objectId: conf.objectId,
1208
- writeToken,
1209
- metadataSubtree: "live_recording",
1210
- metadata: liveRecordingConfig.live_recording
1211
- });
1194
+ // Create live reocording config
1195
+ let lc = new LiveConf(probe, node.id, endpoint, false, false, true);
1212
1196
 
1213
- let fin = await this.FinalizeContentObject({
1214
- libraryId,
1215
- objectId: conf.objectId,
1216
- writeToken,
1217
- commitMessage: "Apply live stream configuration"
1218
- });
1197
+ const liveRecordingConfigStr = lc.generateLiveConf();
1198
+ let liveRecordingConfig = JSON.parse(liveRecordingConfigStr);
1199
+ console.log("CONFIG", JSON.stringify(liveRecordingConfig.live_recording));
1219
1200
 
1220
- status.fin = fin;
1201
+ // Store live recording config into the stream object
1202
+ let e = await this.EditContentObject({
1203
+ libraryId,
1204
+ objectId: conf.objectId
1205
+ });
1206
+ let writeToken = e.write_token;
1207
+
1208
+ await this.ReplaceMetadata({
1209
+ libraryId,
1210
+ objectId: conf.objectId,
1211
+ writeToken,
1212
+ metadataSubtree: "live_recording",
1213
+ metadata: liveRecordingConfig.live_recording
1214
+ });
1221
1215
 
1222
- return status;
1216
+ status.fin = await this.FinalizeContentObject({
1217
+ libraryId,
1218
+ objectId: conf.objectId,
1219
+ writeToken,
1220
+ commitMessage: "Apply live stream configuration"
1221
+ });
1223
1222
 
1224
- } catch(e) {
1225
- console.log("ERROR", e);
1226
- }
1223
+ return status;
1227
1224
  };
1228
1225
 
1229
1226
  // const ChannelStatus = async ({client, name}) => {