@eluvio/elv-client-js 4.0.21 → 4.0.22

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.21",
3
+ "version": "4.0.22",
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
@@ -12,6 +12,7 @@ class HttpClient {
12
12
  this.uriIndex = 0;
13
13
  this.debug = debug;
14
14
  this.draftURIs = {};
15
+ this.retries = Math.max(3, uris.length);
15
16
  }
16
17
 
17
18
  BaseURI() {
@@ -50,8 +51,9 @@ class HttpClient {
50
51
  bodyType="JSON",
51
52
  headers={},
52
53
  attempts=0,
53
- failover=true,
54
- forceFailover=false
54
+ allowFailover=true,
55
+ forceFailover=false,
56
+ allowRetry=true
55
57
  }) {
56
58
  let baseURI = this.BaseURI();
57
59
 
@@ -60,6 +62,8 @@ class HttpClient {
60
62
  const writeToken = writeTokenMatch ? writeTokenMatch[2] : undefined;
61
63
 
62
64
  if(writeToken) {
65
+ allowFailover = false;
66
+
63
67
  if(this.draftURIs[writeToken]) {
64
68
  // Use saved write token URI
65
69
  baseURI = this.draftURIs[writeToken];
@@ -107,10 +111,21 @@ class HttpClient {
107
111
 
108
112
  if(!response.ok) {
109
113
  // Fail over if not a write token request, the response was a server error, and we haven't tried all available nodes
110
- if(!writeToken && ((failover && parseInt(response.status) >= 500) || forceFailover) && attempts < this.uris.length) {
111
- // Server error - Try next node
112
- this.Log(`HttpClient failing over from ${this.BaseURI()}: ${attempts + 1} attempts`, true);
113
- this.uriIndex = (this.uriIndex + 1) % this.uris.length;
114
+ if(
115
+ (parseInt(response.status) >= 500 || forceFailover) &&
116
+ allowRetry &&
117
+ attempts < this.retries
118
+ ) {
119
+ // Server error
120
+ if(allowFailover) {
121
+ // Fail over to alternate node
122
+ this.uriIndex = (this.uriIndex + 1) % this.uris.length;
123
+ this.Log(`HttpClient failing over from ${baseURI.toString()}: ${attempts + 1} attempts`, true);
124
+ } else {
125
+ // Wait and retry
126
+ this.Log(`HttpClient retrying request from ${baseURI.toString()}: ${attempts + 1} attempts`, true);
127
+ await new Promise(resolve => setTimeout(resolve, 1000));
128
+ }
114
129
 
115
130
  return await this.Request({
116
131
  method,
@@ -1831,7 +1831,7 @@ exports.CallBitcodeMethod = async function({
1831
1831
  method: constant ? "GET" : "POST",
1832
1832
  path,
1833
1833
  queryParams,
1834
- failover: false
1834
+ allowFailover: false
1835
1835
  })
1836
1836
  );
1837
1837
  };
@@ -967,7 +967,7 @@ exports.FinalizeContentObject = async function({
967
967
  headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
968
968
  method: "POST",
969
969
  path: path,
970
- failover: false
970
+ allowFailover: false
971
971
  })
972
972
  );
973
973
 
@@ -1127,7 +1127,7 @@ exports.MergeMetadata = async function({libraryId, objectId, writeToken, metadat
1127
1127
  method: "POST",
1128
1128
  path: path,
1129
1129
  body: metadata,
1130
- failover: false
1130
+ allowFailover: false
1131
1131
  });
1132
1132
  };
1133
1133
 
@@ -1159,7 +1159,7 @@ exports.ReplaceMetadata = async function({libraryId, objectId, writeToken, metad
1159
1159
  method: "PUT",
1160
1160
  path: path,
1161
1161
  body: metadata,
1162
- failover: false
1162
+ allowFailover: false
1163
1163
  });
1164
1164
  };
1165
1165
 
@@ -1190,7 +1190,7 @@ exports.DeleteMetadata = async function({libraryId, objectId, writeToken, metada
1190
1190
  headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
1191
1191
  method: "DELETE",
1192
1192
  path: path,
1193
- failover: false
1193
+ allowFailover: false
1194
1194
  });
1195
1195
  };
1196
1196
 
@@ -267,6 +267,7 @@ exports.UploadFilesFromS3 = async function({
267
267
  exports.UploadFiles = async function({libraryId, objectId, writeToken, fileInfo, encryption="none", callback}) {
268
268
  ValidateParameters({libraryId, objectId});
269
269
  ValidateWriteToken(writeToken);
270
+ ValidatePresence("fileInfo", fileInfo);
270
271
 
271
272
  this.Log(`Uploading files: ${libraryId} ${objectId} ${writeToken}`);
272
273
 
@@ -279,8 +280,10 @@ exports.UploadFiles = async function({libraryId, objectId, writeToken, fileInfo,
279
280
  let progress = {};
280
281
  let fileDataMap = {};
281
282
 
282
- for(let i = 0; i < fileInfo.length; i++) {
283
- let entry = { ...fileInfo[i], data: undefined };
283
+ let originalFileInfo = fileInfo;
284
+ fileInfo = [];
285
+ for(let i = 0; i < originalFileInfo.length; i++) {
286
+ let entry = { ...originalFileInfo[i], data: undefined };
284
287
 
285
288
  entry.path = entry.path.replace(/^\/+/, "");
286
289
 
@@ -290,9 +293,8 @@ exports.UploadFiles = async function({libraryId, objectId, writeToken, fileInfo,
290
293
  };
291
294
  }
292
295
 
293
- fileDataMap[entry.path] = fileInfo[i].data;
296
+ fileDataMap[entry.path] = originalFileInfo[i].data;
294
297
 
295
- delete entry.data;
296
298
  entry.type = "file";
297
299
 
298
300
  progress[entry.path] = {
@@ -300,7 +302,7 @@ exports.UploadFiles = async function({libraryId, objectId, writeToken, fileInfo,
300
302
  total: entry.size
301
303
  };
302
304
 
303
- fileInfo[i] = entry;
305
+ fileInfo.push(entry);
304
306
  }
305
307
 
306
308
  this.Log(fileInfo);
@@ -321,7 +323,7 @@ exports.UploadFiles = async function({libraryId, objectId, writeToken, fileInfo,
321
323
  this.Log(jobs);
322
324
 
323
325
  // How far encryption can get ahead of upload
324
- const bufferSize = 100 * 1024 * 1024;
326
+ const bufferSize = 500 * 1024 * 1024;
325
327
 
326
328
  let jobSpecs = [];
327
329
  let prepared = 0;
@@ -387,16 +389,34 @@ exports.UploadFiles = async function({libraryId, objectId, writeToken, fileInfo,
387
389
  for(let f = 0; f < files.length; f++) {
388
390
  const fileInfo = files[f];
389
391
 
390
- await this.UploadFileData({
391
- libraryId,
392
- objectId,
393
- writeToken,
394
- uploadId: id,
395
- jobId,
396
- filePath: fileInfo.path,
397
- fileData: fileInfo.data,
398
- encryption
399
- });
392
+ let retries = 0;
393
+ let succeeded = false;
394
+ do {
395
+ try {
396
+ await this.UploadFileData({
397
+ libraryId,
398
+ objectId,
399
+ writeToken,
400
+ uploadId: id,
401
+ jobId,
402
+ filePath: fileInfo.path,
403
+ fileData: fileInfo.data,
404
+ encryption
405
+ });
406
+
407
+ succeeded = true;
408
+ } catch(error) {
409
+ this.Log(error, true);
410
+
411
+ retries += 1;
412
+
413
+ if(retries >= 10) {
414
+ throw error;
415
+ }
416
+
417
+ await new Promise(resolve => setTimeout(resolve, 10 * retries * 1000));
418
+ }
419
+ } while(!succeeded && retries < 10);
400
420
 
401
421
  delete jobSpecs[j].files[f].data;
402
422
  uploaded += fileInfo.len;
@@ -469,7 +489,7 @@ exports.CreateFileUploadJob = async function({libraryId, objectId, writeToken, o
469
489
  method: "POST",
470
490
  path: path,
471
491
  body,
472
- failover: false
492
+ allowFailover: false
473
493
  })
474
494
  );
475
495
  };
@@ -485,7 +505,7 @@ exports.UploadStatus = async function({libraryId, objectId, writeToken, uploadId
485
505
  headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
486
506
  method: "GET",
487
507
  path: path,
488
- failover: false
508
+ allowFailover: false
489
509
  })
490
510
  );
491
511
  };
@@ -496,82 +516,51 @@ exports.UploadJobStatus = async function({libraryId, objectId, writeToken, uploa
496
516
 
497
517
  const path = UrlJoin("q", writeToken, "file_jobs", uploadId, "uploads", jobId);
498
518
 
499
- // This request is sent during file data upload and might fail due to congestion
500
- let retries = 0;
501
- do {
502
- try {
503
- let jobStatus = this.utils.ResponseToJson(
504
- this.HttpClient.Request({
505
- headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
506
- method: "GET",
507
- path: path,
508
- failover: false
509
- })
510
- );
511
- return jobStatus;
512
- } catch(error) {
513
- this.Log(error, true);
514
-
515
- retries += 1;
516
- if(retries >= 5) {
517
- throw error;
518
- }
519
- await new Promise(resolve => setTimeout(resolve, 10 * retries * 1000));
520
- }
521
- } while(retries < 5);
522
-
519
+ return await this.utils.ResponseToJson(
520
+ this.HttpClient.Request({
521
+ headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
522
+ method: "GET",
523
+ path: path,
524
+ allowFailover: false
525
+ })
526
+ );
523
527
  };
524
528
 
525
529
  exports.UploadFileData = async function({libraryId, objectId, writeToken, encryption, uploadId, jobId, filePath, fileData}) {
526
530
  ValidateParameters({libraryId, objectId});
527
531
  ValidateWriteToken(writeToken);
528
532
 
529
- let retries = 0;
530
- do {
531
- try {
532
-
533
- const jobStatus = await this.UploadJobStatus({libraryId, objectId, writeToken, uploadId, jobId});
533
+ const jobStatus = await this.UploadJobStatus({libraryId, objectId, writeToken, uploadId, jobId});
534
534
 
535
- // Find the status of this file
536
- let fileStatus = jobStatus.files.find(item => item.path == filePath);
537
- if(encryption && encryption !== "none") {
538
- fileStatus = fileStatus.encrypted;
539
- }
540
-
541
- if(fileStatus.rem === 0) {
542
- // Job is actually done
543
- return;
544
- } else if(fileStatus.skip) {
545
- fileData = fileData.slice(fileStatus.skip);
546
- }
535
+ // Find the status of this file
536
+ let fileStatus = jobStatus.files.find(item => item.path == filePath);
537
+ if(encryption && encryption !== "none") {
538
+ fileStatus = fileStatus.encrypted;
539
+ }
547
540
 
548
- let path = UrlJoin("q", writeToken, "file_jobs", uploadId, jobId);
549
-
550
- return await this.utils.ResponseToJson(
551
- this.HttpClient.Request({
552
- method: "POST",
553
- path: path,
554
- body: fileData,
555
- bodyType: "BINARY",
556
- headers: {
557
- "Content-type": "application/octet-stream",
558
- ...(await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}))
559
- },
560
- failover: false
561
- })
562
- );
563
- } catch(error){
564
- this.Log(error, true);
541
+ if(fileStatus.rem === 0) {
542
+ // Job is actually done
543
+ return;
544
+ } else if(fileStatus.skip) {
545
+ fileData = fileData.slice(fileStatus.skip);
546
+ }
565
547
 
566
- retries += 1;
548
+ let path = UrlJoin("q", writeToken, "file_jobs", uploadId, jobId);
567
549
 
568
- if(retries >= 5) {
569
- throw error;
570
- }
571
-
572
- await new Promise(resolve => setTimeout(resolve, 10 * retries * 1000));
573
- }
574
- } while(retries < 5);
550
+ return await this.utils.ResponseToJson(
551
+ this.HttpClient.Request({
552
+ method: "POST",
553
+ path: path,
554
+ body: fileData,
555
+ bodyType: "BINARY",
556
+ headers: {
557
+ "Content-type": "application/octet-stream",
558
+ ...(await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}))
559
+ },
560
+ allowFailover: false,
561
+ allowRetry: false
562
+ })
563
+ );
575
564
  };
576
565
 
577
566
  exports.FinalizeUploadJob = async function({libraryId, objectId, writeToken}) {
@@ -587,7 +576,7 @@ exports.FinalizeUploadJob = async function({libraryId, objectId, writeToken}) {
587
576
  path: path,
588
577
  bodyType: "BINARY",
589
578
  headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
590
- failover: false
579
+ allowFailover: false
591
580
  });
592
581
  };
593
582
 
@@ -1051,7 +1040,7 @@ exports.CreatePart = async function({libraryId, objectId, writeToken, encryption
1051
1040
  path,
1052
1041
  bodyType: "BINARY",
1053
1042
  body: "",
1054
- failover: false
1043
+ allowFailover: false
1055
1044
  })
1056
1045
  );
1057
1046
 
@@ -1090,7 +1079,7 @@ exports.UploadPartChunk = async function({libraryId, objectId, writeToken, partW
1090
1079
  path: UrlJoin(path, partWriteToken),
1091
1080
  body: chunk,
1092
1081
  bodyType: "BINARY",
1093
- failover: false
1082
+ allowFailover: false
1094
1083
  })
1095
1084
  );
1096
1085
  };
@@ -1121,7 +1110,7 @@ exports.FinalizePart = async function({libraryId, objectId, writeToken, partWrit
1121
1110
  path: UrlJoin(path, partWriteToken),
1122
1111
  bodyType: "BINARY",
1123
1112
  body: "",
1124
- failover: false
1113
+ allowFailover: false
1125
1114
  })
1126
1115
  );
1127
1116
  };
@@ -1200,6 +1189,6 @@ exports.DeletePart = async function({libraryId, objectId, writeToken, partHash})
1200
1189
  headers: await this.authClient.AuthorizationHeader({libraryId, objectId, update: true}),
1201
1190
  method: "DELETE",
1202
1191
  path: path,
1203
- failover: false
1192
+ allowFailover: false
1204
1193
  });
1205
1194
  };
@@ -1,8 +1,7 @@
1
1
  const { ElvClient } = require("../src/ElvClient");
2
2
  const { ElvWalletClient } = require("../src/walletClient/index");
3
3
  const ClientConfiguration = require("../TestConfiguration.json");
4
-
5
- const ethers = require("ethers");
4
+ const fs = require("fs");
6
5
 
7
6
  const Test = async () => {
8
7
  try {
@@ -0,0 +1,84 @@
1
+ const { ElvClient } = require("../src/ElvClient");
2
+ const { ElvWalletClient } = require("../src/walletClient/index");
3
+ const ClientConfiguration = require("../TestConfiguration.json");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+
7
+ const createHash = require("node:crypto").createHash;
8
+ const MD5 = str => {
9
+ const f = createHash("md5");
10
+ f.update(str);
11
+ return f.digest("hex");
12
+ };
13
+
14
+ const Test = async () => {
15
+ try {
16
+ const client = await ElvClient.FromNetworkName({
17
+ networkName: "demo"
18
+ });
19
+
20
+ const wallet = client.GenerateWallet();
21
+ const signer = wallet.AddAccount({
22
+ privateKey: process.env.PRIVATE_KEY
23
+ });
24
+
25
+ client.SetSigner({signer});
26
+
27
+ const uploadDir = process.argv[2];
28
+
29
+ const dirname = `upload-test-${Date.now()}`;
30
+ const fileList = fs.readdirSync(uploadDir);
31
+ const fileInfo = fileList.map(filename => {
32
+ const data = fs.readFileSync(path.join(uploadDir, filename));
33
+
34
+ return {
35
+ path: `${dirname}/${filename}`,
36
+ size: data.length,
37
+ data
38
+ };
39
+ }).sort(() => Math.random() > 0.5 ? -1 : 1);
40
+
41
+
42
+ const libraryId = "ilib3Drbefo7VPfWvY1NVup4VZFzDJ68";
43
+ const objectId = "iq__2aqbcJhSKdkuRmAUqs6v99SLRRp";
44
+
45
+ const {hash} = await client.EditAndFinalizeContentObject({
46
+ libraryId,
47
+ objectId,
48
+ callback: async ({writeToken}) => {
49
+ await client.UploadFiles({
50
+ libraryId,
51
+ objectId,
52
+ writeToken,
53
+ //callback: console.log,
54
+ //encryption: "cgck",
55
+ fileInfo
56
+ });
57
+ }
58
+ });
59
+
60
+ console.log("\n\n");
61
+ for(let i = 0; i < fileInfo.length; i++) {
62
+ const {path, data} = fileInfo[i];
63
+
64
+ const downloadedData = await client.DownloadFile({
65
+ versionHash: hash,
66
+ format: "buffer",
67
+ filePath: path
68
+ });
69
+
70
+ console.log(path);
71
+ console.log(MD5(data.toString()));
72
+ console.log(MD5(downloadedData.toString()));
73
+ console.log(MD5(data.toString()) === MD5(downloadedData.toString()));
74
+ console.log();
75
+ }
76
+ } catch(error) {
77
+ console.error(error);
78
+ console.error(JSON.stringify(error, null, 2));
79
+ }
80
+
81
+ process.exit(0);
82
+ };
83
+
84
+ Test();
@@ -105,16 +105,6 @@
105
105
  "type": "ProtoHls"
106
106
  }
107
107
  },
108
- "hls-fairplay": {
109
- "drm": {
110
- "enc_scheme_name": "cbcs",
111
- "license_servers": [],
112
- "type": "DrmFairplay"
113
- },
114
- "protocol": {
115
- "type": "ProtoHls"
116
- }
117
- },
118
108
  "hls-sample-aes": {
119
109
  "drm": {
120
110
  "enc_scheme_name": "cbcs",
@@ -0,0 +1,128 @@
1
+ {
2
+ "drm_optional": false,
3
+ "store_clear": false,
4
+ "ladder_specs": {
5
+ "{\"media_type\":\"audio\",\"channels\":1}": {
6
+ "rung_specs": [
7
+ {
8
+ "bit_rate": 128000,
9
+ "media_type": "audio",
10
+ "pregenerate": true
11
+ }
12
+ ]
13
+ },
14
+ "{\"media_type\":\"audio\",\"channels\":2}": {
15
+ "rung_specs": [
16
+ {
17
+ "bit_rate": 192000,
18
+ "media_type": "audio",
19
+ "pregenerate": true
20
+ }
21
+ ]
22
+ },
23
+ "{\"media_type\":\"audio\",\"channels\":6}": {
24
+ "rung_specs": [
25
+ {
26
+ "bit_rate": 384000,
27
+ "media_type": "audio",
28
+ "pregenerate": true
29
+ }
30
+ ]
31
+ },
32
+ "{\"media_type\":\"video\",\"aspect_ratio_height\":9,\"aspect_ratio_width\":16}": {
33
+ "rung_specs": [
34
+ {
35
+ "bit_rate": 20000000,
36
+ "height": 2160,
37
+ "media_type": "video",
38
+ "pregenerate": true,
39
+ "width": 3840
40
+ },
41
+ {
42
+ "bit_rate": 9500000,
43
+ "height": 1080,
44
+ "media_type": "video",
45
+ "pregenerate": false,
46
+ "width": 1920
47
+ },
48
+ {
49
+ "bit_rate": 4500000,
50
+ "height":720 ,
51
+ "media_type": "video",
52
+ "pregenerate": false,
53
+ "width": 1280
54
+ },
55
+ {
56
+ "bit_rate": 2000000,
57
+ "height": 540,
58
+ "media_type": "video",
59
+ "pregenerate": false,
60
+ "width": 960
61
+ },
62
+ {
63
+ "bit_rate": 1100000,
64
+ "height": 432,
65
+ "media_type": "video",
66
+ "pregenerate": false,
67
+ "width": 768
68
+ },
69
+ {
70
+ "bit_rate": 810000,
71
+ "height": 360,
72
+ "media_type": "video",
73
+ "pregenerate": false,
74
+ "width": 640
75
+ },
76
+ {
77
+ "bit_rate": 520000,
78
+ "height": 360,
79
+ "media_type": "video",
80
+ "pregenerate": false,
81
+ "width": 640
82
+ }
83
+ ]
84
+ }
85
+ },
86
+ "playout_formats": {
87
+ "dash-widevine": {
88
+ "drm": {
89
+ "content_id": "",
90
+ "enc_scheme_name": "cenc",
91
+ "license_servers": [],
92
+ "type": "DrmWidevine"
93
+ },
94
+ "protocol": {
95
+ "min_buffer_length": 2,
96
+ "type": "ProtoDash"
97
+ }
98
+ },
99
+ "hls-sample-aes": {
100
+ "drm": {
101
+ "enc_scheme_name": "cbcs",
102
+ "type": "DrmSampleAes"
103
+ },
104
+ "protocol": {
105
+ "type": "ProtoHls"
106
+ }
107
+ },
108
+ "hls-aes128": {
109
+ "drm": {
110
+ "enc_scheme_name": "aes-128",
111
+ "type": "DrmAes128"
112
+ },
113
+ "protocol": {
114
+ "type": "ProtoHls"
115
+ }
116
+ }
117
+ },
118
+ "segment_specs": {
119
+ "audio": {
120
+ "segs_per_chunk": 15,
121
+ "target_dur": 2
122
+ },
123
+ "video": {
124
+ "segs_per_chunk": 15,
125
+ "target_dur": 2
126
+ }
127
+ }
128
+ }
@@ -1930,16 +1930,6 @@
1930
1930
  "type": "ProtoDash"
1931
1931
  }
1932
1932
  },
1933
- "hls-fairplay": {
1934
- "drm": {
1935
- "enc_scheme_name": "cbcs",
1936
- "license_servers": [],
1937
- "type": "DrmFairplay"
1938
- },
1939
- "protocol": {
1940
- "type": "ProtoHls"
1941
- }
1942
- },
1943
1933
  "hls-sample-aes": {
1944
1934
  "drm": {
1945
1935
  "enc_scheme_name": "cbcs",