@eluvio/elv-client-js 4.2.34 → 4.2.36

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.2.34",
3
+ "version": "4.2.36",
4
4
  "description": "Javascript client for the Eluvio Content Fabric",
5
5
  "main": "src/index.js",
6
6
  "author": "Kevin Talmadge",
package/src/HttpClient.js CHANGED
@@ -65,10 +65,16 @@ class HttpClient {
65
65
  allowFailover=true,
66
66
  forceFailover=false,
67
67
  allowRetry=true,
68
- uriIndex
68
+ uriIndex,
69
+ nodeUrl
69
70
  }) {
70
71
  let baseURI = this.BaseURI(uriIndex);
71
72
 
73
+ if(nodeUrl) {
74
+ baseURI = new URI(nodeUrl);
75
+ allowFailover = false;
76
+ }
77
+
72
78
  // If URL contains a write token, it must go to the correct server and can not fail over
73
79
  const writeTokenMatch = path.replace(/^\//, "").match(/(qlibs\/ilib[a-zA-Z0-9]+|q|qid)\/(tqw__[a-zA-Z0-9]+)/);
74
80
  const writeToken = writeTokenMatch ? writeTokenMatch[2] : undefined;
@@ -171,7 +177,8 @@ class HttpClient {
171
177
  headers,
172
178
  attempts: attempts + 1,
173
179
  uriIndex,
174
- forceFailover
180
+ forceFailover,
181
+ nodeUrl
175
182
  });
176
183
  }
177
184
 
@@ -1902,6 +1902,8 @@ exports.MakeFileServiceRequest = async function({
1902
1902
  * @param {boolean=} constant=true - If specified, a GET request authenticated with an AccessRequest will be made.
1903
1903
  * Otherwise, a POST with an UpdateRequest will be performed
1904
1904
  * @param {string=} format=json - The format of the response
1905
+ * @param {string=} nodeUrl - If specified, the call is made directly to this fabric node instead of
1906
+ * the client's currently configured node(s)
1905
1907
  *
1906
1908
  * @returns {Promise<format>} - The response from the call in the specified format
1907
1909
  */
@@ -1916,7 +1918,8 @@ exports.CallBitcodeMethod = async function({
1916
1918
  body={},
1917
1919
  headers={},
1918
1920
  constant=true,
1919
- format="json"
1921
+ format="json",
1922
+ nodeUrl
1920
1923
  }) {
1921
1924
  ValidateParameters({libraryId, objectId, versionHash});
1922
1925
  if(!method) { throw "Bitcode method not specified"; }
@@ -1960,7 +1963,8 @@ exports.CallBitcodeMethod = async function({
1960
1963
  method: verb,
1961
1964
  path,
1962
1965
  queryParams,
1963
- allowFailover: false
1966
+ allowFailover: false,
1967
+ nodeUrl
1964
1968
  }),
1965
1969
  this.HttpClient.debug,
1966
1970
  this.HttpClient.Log.bind(this.HttpClient)
@@ -813,10 +813,11 @@ exports.CreateNonOwnerCap = async function({objectId, libraryId, publicKey, writ
813
813
  * @param {object=} options -
814
814
  * @param {object=} options.meta - New metadata for the object - will be merged into existing metadata if specified
815
815
  * @param {string=} options.type - New type for the object - Object ID, version hash or name of type
816
+ * @param {string=} nodeUrl - Fabric node URL to create the draft on
816
817
  *
817
818
  * @returns {Promise<object>} - Response containing the object ID and write token of the draft, as well as URL of node handling the draft
818
819
  */
819
- exports.EditContentObject = async function({libraryId, objectId, options={}}) {
820
+ exports.EditContentObject = async function({libraryId, objectId, options={}, nodeUrl}) {
820
821
  ValidateParameters({libraryId, objectId});
821
822
 
822
823
  this.Log(`Opening content draft: ${libraryId} ${objectId}`);
@@ -842,11 +843,12 @@ exports.EditContentObject = async function({libraryId, objectId, options={}}) {
842
843
  headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
843
844
  method: "POST",
844
845
  path: path,
845
- body: options
846
+ body: options,
847
+ nodeUrl
846
848
  });
847
849
  // extract the url for the node that handled the request
848
850
  // TODO: remove/simplify after we start using /nodes API call to get node URLs for write tokens
849
- const nodeUrl = (new URL(rawEditResponse.url)).origin;
851
+ const responseNodeUrl = (new URL(rawEditResponse.url)).origin;
850
852
  const editResponse = await this.utils.ResponseToJson(
851
853
  rawEditResponse,
852
854
  this.HttpClient.debug,
@@ -854,11 +856,11 @@ exports.EditContentObject = async function({libraryId, objectId, options={}}) {
854
856
  );
855
857
 
856
858
  // Record the node used in creating this write token
857
- this.RecordWriteToken({writeToken: editResponse.write_token, fabricNodeUrl: nodeUrl});
859
+ this.RecordWriteToken({writeToken: editResponse.write_token, fabricNodeUrl: responseNodeUrl});
858
860
 
859
861
  editResponse.writeToken = editResponse.write_token;
860
862
  editResponse.objectId = editResponse.id;
861
- editResponse.nodeUrl = nodeUrl;
863
+ editResponse.nodeUrl = responseNodeUrl;
862
864
 
863
865
  return editResponse;
864
866
  };
@@ -538,8 +538,12 @@ class LiveConf {
538
538
  conf.live_recording.recording_config.recording_params.description = `Ingest stream ${fileName}`;
539
539
  conf.live_recording.recording_config.recording_params.name = `Ingest stream ${fileName}`;
540
540
  conf.live_recording.recording_config.recording_params.xc_params.sample_rate = sampleRate;
541
- conf.live_recording.recording_config.recording_params.xc_params.enc_height = videoStream.height;
542
- conf.live_recording.recording_config.recording_params.xc_params.enc_width = videoStream.width;
541
+
542
+ // Encoding dimensions default to the source but are overridden by profile values if present
543
+ conf.live_recording.recording_config.recording_params.xc_params.enc_height =
544
+ customXcParams?.enc_height ?? videoStream.height;
545
+ conf.live_recording.recording_config.recording_params.xc_params.enc_width =
546
+ customXcParams?.enc_width ?? videoStream.width;
543
547
 
544
548
  for(let i =0; i < Object.keys(audioStreams).length; i ++) {
545
549
  conf.live_recording.recording_config.recording_params.xc_params.audio_index[i] = parseInt(Object.keys(audioStreams)[i]);
@@ -606,9 +610,6 @@ class LiveConf {
606
610
  const ladder_specs = customSettings.liveRecordingConfigProfile?.playout_config?.ladder_specs;
607
611
  const ladderProfile = ladder_specs?.video?.length > 0 ? ladder_specs : DefaultABRLadder;
608
612
 
609
- conf.live_recording.recording_config.recording_params.xc_params.enc_height = videoStream.height;
610
- conf.live_recording.recording_config.recording_params.xc_params.enc_width = videoStream.width;
611
-
612
613
  // Reset ladder specs (updating existing stream will carry over old specs
613
614
  conf.live_recording.recording_config.recording_params.ladder_specs = [];
614
615
 
@@ -393,15 +393,42 @@ exports.StreamCreate = async function({
393
393
  metadata
394
394
  });
395
395
 
396
+ // If the profile is changing, only carry over name and url so stale
397
+ // settings from the old profile are cleared
398
+ const changingProfile = liveRecordingConfig?.name && liveRecordingConfig.name !== oldProfile;
399
+ const baseLiveRecordingConfig = changingProfile ?
400
+ R.pick(["url", "name"], currentLiveRecordingConfig) :
401
+ currentLiveRecordingConfig;
402
+
396
403
  // Full overwrite avoids the server-side merge mixing streams from the old and new profile
397
404
  await this.ReplaceMetadata({
398
405
  libraryId,
399
406
  objectId,
400
407
  writeToken,
401
408
  metadataSubtree: "live_recording_config",
402
- metadata: R.mergeDeepRight(currentLiveRecordingConfig, liveRecordingConfig)
409
+ metadata: R.mergeDeepRight(baseLiveRecordingConfig, liveRecordingConfig)
403
410
  });
404
411
 
412
+ if(changingProfile) {
413
+ // Clear live_recording and live_recording_overrides so stale settings from the
414
+ // previous profile don't persist, matching StreamApplyProfile
415
+ await this.ReplaceMetadata({
416
+ libraryId,
417
+ objectId,
418
+ writeToken,
419
+ metadataSubtree: "live_recording",
420
+ metadata: {}
421
+ });
422
+
423
+ await this.ReplaceMetadata({
424
+ libraryId,
425
+ objectId,
426
+ writeToken,
427
+ metadataSubtree: "live_recording_overrides",
428
+ metadata: {}
429
+ });
430
+ }
431
+
405
432
  try {
406
433
  await this.CreateLinks({
407
434
  libraryId,
@@ -1269,13 +1296,12 @@ exports.StreamStartRecording = async function({name, start=false}) {
1269
1296
  fabURI = "https://" + fabURI;
1270
1297
  }
1271
1298
 
1272
- this.SetNodes({fabricURIs: [fabURI]});
1273
-
1274
1299
  console.log("Node URI", fabURI, "ID", liveRecording.fabric_config.ingress_node_id);
1275
1300
 
1276
1301
  let response = await this.EditContentObject({
1277
1302
  libraryId: libraryId,
1278
- objectId: objectId
1303
+ objectId: objectId,
1304
+ nodeUrl: fabURI
1279
1305
  });
1280
1306
  const edgeToken = response.write_token;
1281
1307
  console.log("Edge token:", edgeToken);
@@ -1285,7 +1311,8 @@ exports.StreamStartRecording = async function({name, start=false}) {
1285
1311
  */
1286
1312
  response = await this.EditContentObject({
1287
1313
  libraryId: libraryId,
1288
- objectId: objectId
1314
+ objectId: objectId,
1315
+ nodeUrl: fabURI
1289
1316
  });
1290
1317
  let writeToken = response.write_token;
1291
1318
 
@@ -1371,8 +1398,6 @@ exports.StreamRestartRecording = async function({name}) {
1371
1398
  throw new Error("Unable to restart stream - ingress node API is unavailable");
1372
1399
  }
1373
1400
 
1374
- this.SetNodes({fabricURIs: [fabricApi]});
1375
-
1376
1401
  let mainWriteToken;
1377
1402
  let newEdgeWriteToken;
1378
1403
  let mainObjectFinalizeStarted = false;
@@ -1380,12 +1405,14 @@ exports.StreamRestartRecording = async function({name}) {
1380
1405
  try {
1381
1406
  ({writeToken: mainWriteToken} = await this.EditContentObject({
1382
1407
  libraryId,
1383
- objectId
1408
+ objectId,
1409
+ nodeUrl: fabricApi
1384
1410
  }));
1385
1411
 
1386
1412
  ({writeToken: newEdgeWriteToken} = await this.EditContentObject({
1387
1413
  libraryId,
1388
- objectId
1414
+ objectId,
1415
+ nodeUrl: fabricApi
1389
1416
  }));
1390
1417
 
1391
1418
  await this.MergeMetadata({
@@ -1595,8 +1622,6 @@ exports.StreamStopRecording = async function({name}) {
1595
1622
  fabURI = "https://" + fabURI;
1596
1623
  }
1597
1624
 
1598
- this.SetNodes({fabricURIs: [fabURI]});
1599
-
1600
1625
  const metaEdgeWriteToken = mainMeta.live_recording.fabric_config.edge_write_token;
1601
1626
 
1602
1627
  if(!metaEdgeWriteToken) {
@@ -1634,7 +1659,8 @@ exports.StreamStopRecording = async function({name}) {
1634
1659
 
1635
1660
  const {writeToken} = await this.EditContentObject({
1636
1661
  libraryId: libraryId,
1637
- objectId: objectId
1662
+ objectId: objectId,
1663
+ nodeUrl: fabURI
1638
1664
  });
1639
1665
 
1640
1666
  // Set stop time and inactive state
@@ -11,7 +11,7 @@ const Test = async () => {
11
11
 
12
12
  try {
13
13
  const client = await ElvClient.FromNetworkName({
14
- networkName: "main"
14
+ networkName: "demo"
15
15
  });
16
16
 
17
17
  const wallet = client.GenerateWallet();
@@ -21,19 +21,7 @@ const Test = async () => {
21
21
 
22
22
  client.SetSigner({signer});
23
23
 
24
- const configUrl = new URL(await client.ConfigUrl());
25
- configUrl.pathname = "/config";
26
- // if(geo) { configUrl.searchParams.set("elvgeo", geo); } // optional region scoping
27
-
28
- const fabricInfo = await (await fetch(configUrl.toString())).json();
29
- // fabricInfo.network.services.* e.g. live_egress, ethereum, authority, etc.
30
- const liveEgressUrls = fabricInfo?.network?.services?.live_egress;
31
- console.log('fabricINfo', fabricInfo.network?.services?.tenant_query)
32
-
33
- // tenant_query nodes are now wired into the client and used automatically by TenantContent:
34
- console.log('client tenantQueryURIs', client.Nodes().tenantQueryURIs)
35
- // const result = await client.TenantContent({limit: 5});
36
- // console.log(JSON.stringify(result, null, 2));
24
+ console.log(await client.ContentLibraries());
37
25
  } catch(error) {
38
26
  console.error(error);
39
27
  console.error(JSON.stringify(error, null, 2));