@eluvio/elv-client-js 3.2.39 → 3.2.40

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.
Files changed (28) hide show
  1. package/dist/ElvClient-min.js +6 -6
  2. package/dist/ElvClient-node-min.js +9 -9
  3. package/dist/ElvFrameClient-min.js +1 -1
  4. package/dist/ElvPermissionsClient-min.js +7 -7
  5. package/dist/ElvWalletClient-min.js +6 -6
  6. package/dist/ElvWalletClient-node-min.js +9 -9
  7. package/dist/src/AuthorizationClient.js +599 -507
  8. package/dist/src/Utils.js +2 -1
  9. package/dist/src/client/ABRPublishing.js +2 -2
  10. package/dist/src/client/ContentManagement.js +45 -5
  11. package/dist/src/walletClient/Utils.js +16 -5
  12. package/package.json +1 -1
  13. package/src/AuthorizationClient.js +43 -0
  14. package/src/Utils.js +2 -2
  15. package/src/client/ABRPublishing.js +2 -2
  16. package/src/client/ContentManagement.js +29 -2
  17. package/testScripts/CreateProductionMaster.js +14 -6
  18. package/testScripts/abr_profile_4k_clear_store_encrypted.json +111 -0
  19. package/testScripts/{abr_profile_4k_clear.json → abr_profile_4k_clear_store_unencrypted.json} +0 -0
  20. package/testScripts/abr_profile_clear_store_encrypted.json +1945 -0
  21. package/testScripts/{abr_profile_clear.json → abr_profile_clear_store_unencrypted.json} +0 -0
  22. package/utilities/ProductionMasterCreate.js +18 -6
  23. package/utilities/example_files/abr_profile_4k_clear_store_encrypted.json +111 -0
  24. package/utilities/example_files/{abr_profile_4k_clear.json → abr_profile_4k_clear_store_unencrypted.json} +0 -0
  25. package/utilities/example_files/abr_profile_clear_hls_only_store_encrypted.json +1892 -0
  26. package/utilities/example_files/{abr_profile_clear_hls_only.json → abr_profile_clear_hls_only_store_unencrypted.json} +0 -0
  27. package/utilities/example_files/abr_profile_clear_store_encrypted.json +1899 -0
  28. /package/utilities/example_files/{abr_profile_clear.json → abr_profile_clear_store_unencrypted.json} +0 -0
package/dist/src/Utils.js CHANGED
@@ -466,7 +466,8 @@ var Utils = {
466
466
  return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
467
467
  },
468
468
  B64: function B64(str) {
469
- return Buffer.from(str, "utf-8").toString("base64");
469
+ var encoding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "utf-8";
470
+ return Buffer.from(str, encoding).toString("base64");
470
471
  },
471
472
  FromB64: function FromB64(str) {
472
473
  return Buffer.from(str, "base64").toString("utf-8");
@@ -42,7 +42,7 @@ var _require = require("../Validation"),
42
42
  * @param {string} contentTypeName - Name of the content type to use
43
43
  * @param {Object=} metadata - Additional metadata for the content object
44
44
  * @param {Array<Object>=} fileInfo - Files to upload (See UploadFiles/UploadFilesFromS3 method)
45
- * @param {boolean=} encrypt=false - (Local files only) - If specified, files will be encrypted
45
+ * @param {boolean=} encrypt=true - (Local or copied files only) - Unless `false` is passed in explicitly, any uploaded/copied files will be stored encrypted
46
46
  * @param {boolean=} copy=false - (S3) If specified, files will be copied from S3
47
47
  * @param {function=} callback - Progress callback for file upload (See UploadFiles/UploadFilesFromS3 method)
48
48
  * @param {Array<Object>=} access=[] - Array of cloud credentials, along with path matching regex strings - Required if any files in the masters are cloud references (currently only AWS S3 is supported)
@@ -87,7 +87,7 @@ exports.CreateProductionMaster = /*#__PURE__*/function () {
87
87
  while (1) {
88
88
  switch (_context.prev = _context.next) {
89
89
  case 0:
90
- libraryId = _ref.libraryId, type = _ref.type, name = _ref.name, description = _ref.description, _ref$metadata = _ref.metadata, metadata = _ref$metadata === void 0 ? {} : _ref$metadata, fileInfo = _ref.fileInfo, _ref$encrypt = _ref.encrypt, encrypt = _ref$encrypt === void 0 ? false : _ref$encrypt, _ref$access = _ref.access, access = _ref$access === void 0 ? [] : _ref$access, _ref$copy = _ref.copy, copy = _ref$copy === void 0 ? false : _ref$copy, callback = _ref.callback;
90
+ libraryId = _ref.libraryId, type = _ref.type, name = _ref.name, description = _ref.description, _ref$metadata = _ref.metadata, metadata = _ref$metadata === void 0 ? {} : _ref$metadata, fileInfo = _ref.fileInfo, _ref$encrypt = _ref.encrypt, encrypt = _ref$encrypt === void 0 ? true : _ref$encrypt, _ref$access = _ref.access, access = _ref$access === void 0 ? [] : _ref$access, _ref$copy = _ref.copy, copy = _ref$copy === void 0 ? false : _ref$copy, callback = _ref.callback;
91
91
  ValidateLibrary(libraryId);
92
92
  _context.next = 4;
93
93
  return this.CreateContentObject({
@@ -2646,7 +2646,8 @@ exports.UpdateContentObjectGraph = /*#__PURE__*/function () {
2646
2646
  target: string (path to link target),
2647
2647
  type: string ("file", "meta" | "metadata", "rep" - default "metadata")
2648
2648
  targetHash: string (optional, for cross-object links),
2649
- autoUpdate: boolean (if specified, link will be automatically updated to latest version by UpdateContentObjectGraph method)
2649
+ autoUpdate: boolean (if specified, link will be automatically updated to latest version by UpdateContentObjectGraph method),
2650
+ authContainer: string (optional, object id of container object if creating a signed link)
2650
2651
  }
2651
2652
  ]
2652
2653
 
@@ -2678,7 +2679,7 @@ exports.CreateLinks = /*#__PURE__*/function () {
2678
2679
  _context33.next = 5;
2679
2680
  return this.utils.LimitedMap(10, links, /*#__PURE__*/function () {
2680
2681
  var _ref57 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee31(info) {
2681
- var path, type, target, link;
2682
+ var path, type, target, authTarget, link, linkMetadata;
2682
2683
  return _regeneratorRuntime.wrap(function _callee31$(_context32) {
2683
2684
  while (1) {
2684
2685
  switch (_context32.prev = _context32.next) {
@@ -2690,7 +2691,7 @@ exports.CreateLinks = /*#__PURE__*/function () {
2690
2691
  type = "meta";
2691
2692
  }
2692
2693
 
2693
- target = info.target.replace(/^(\/|\.)+/, "");
2694
+ target = authTarget = info.target.replace(/^(\/|\.)+/, "");
2694
2695
 
2695
2696
  if (info.targetHash) {
2696
2697
  target = "/qfab/".concat(info.targetHash, "/").concat(type, "/").concat(target);
@@ -2708,9 +2709,48 @@ exports.CreateLinks = /*#__PURE__*/function () {
2708
2709
  tag: "latest"
2709
2710
  }
2710
2711
  };
2712
+ } // Sign link
2713
+
2714
+
2715
+ if (!info.authContainer) {
2716
+ _context32.next = 17;
2717
+ break;
2711
2718
  }
2712
2719
 
2713
- _context32.next = 9;
2720
+ _context32.next = 10;
2721
+ return _this6.ContentObjectMetadata({
2722
+ libraryId: libraryId,
2723
+ objectId: objectId,
2724
+ metadataSubtree: path
2725
+ });
2726
+
2727
+ case 10:
2728
+ linkMetadata = _context32.sent;
2729
+
2730
+ if (linkMetadata) {
2731
+ link["/"] = linkMetadata["/"];
2732
+ link["."] = linkMetadata["."];
2733
+ }
2734
+
2735
+ if (!link["."]) link["."] = {};
2736
+
2737
+ if (linkMetadata["."]["authorization"]) {
2738
+ _context32.next = 17;
2739
+ break;
2740
+ }
2741
+
2742
+ _context32.next = 16;
2743
+ return _this6.authClient.GenerateSignedLinkToken({
2744
+ containerId: info.authContainer,
2745
+ versionHash: info.targetHash,
2746
+ link: "./".concat(type, "/").concat(authTarget)
2747
+ });
2748
+
2749
+ case 16:
2750
+ link["."]["authorization"] = _context32.sent;
2751
+
2752
+ case 17:
2753
+ _context32.next = 19;
2714
2754
  return _this6.ReplaceMetadata({
2715
2755
  libraryId: libraryId,
2716
2756
  objectId: objectId,
@@ -2719,7 +2759,7 @@ exports.CreateLinks = /*#__PURE__*/function () {
2719
2759
  metadata: link
2720
2760
  });
2721
2761
 
2722
- case 9:
2762
+ case 19:
2723
2763
  case "end":
2724
2764
  return _context32.stop();
2725
2765
  }
@@ -65,13 +65,24 @@ var FormatNFTDetails = function FormatNFTDetails(entry) {
65
65
  var isListing = !!entry.id;
66
66
  var metadata = (isListing ? entry.nft : entry.meta) || {};
67
67
  var info = isListing ? entry.info : entry;
68
+ var paymentAccounts = entry.accepts || [];
68
69
  var details = {
69
- USDCAccepted: !!(entry.accepts || []).find(function (entry) {
70
- return entry.type === "sol";
70
+ USDCAccepted: paymentAccounts.length > 0,
71
+ USDCOnly: !!paymentAccounts.find(function (entry) {
72
+ return entry.preferred;
73
+ }),
74
+ EthUSDCAccepted: !!paymentAccounts.find(function (account) {
75
+ return account.type === "eth";
76
+ }),
77
+ EthUSDCOnly: !!paymentAccounts.find(function (account) {
78
+ return account.type === "eth" && account.preferred;
79
+ }),
80
+ SolUSDCAccepted: !!paymentAccounts.find(function (account) {
81
+ return account.type === "eth";
82
+ }),
83
+ SolUSDCOnly: !!paymentAccounts.find(function (account) {
84
+ return account.type === "eth" && account.preferred;
71
85
  }),
72
- USDCOnly: ((entry.accepts || []).find(function (entry) {
73
- return entry.type === "sol";
74
- }) || {}).preferred,
75
86
  TenantId: entry.tenant || entry.tenant_id,
76
87
  ContractAddr: info.contract_addr,
77
88
  ContractId: "ictr".concat(Utils.AddressToHash(info.contract_addr)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eluvio/elv-client-js",
3
- "version": "3.2.39",
3
+ "version": "3.2.40",
4
4
  "description": "Javascript client for the Eluvio Content Fabric",
5
5
  "main": "src/index.js",
6
6
  "author": "Kevin Talmadge",
@@ -3,6 +3,8 @@ const Ethers = require("ethers");
3
3
  const Utils = require("./Utils");
4
4
  const UrlJoin = require("url-join");
5
5
  const {LogMessage} = require("./LogMessage");
6
+ const {ValidateObject} = require("./Validation");
7
+ const Pako = require("pako");
6
8
 
7
9
  /*
8
10
  // -- Contract javascript files built using build/BuildContracts.js
@@ -240,6 +242,47 @@ class AuthorizationClient {
240
242
  return `${token}.${Utils.B64(multiSig)}`;
241
243
  }
242
244
 
245
+ async GenerateSignedLinkToken({containerId, versionHash, link}) {
246
+ ValidateObject(containerId);
247
+ const canEdit = await this.client.CallContractMethod({
248
+ contractAddress: Utils.HashToAddress(containerId),
249
+ methodName: "canEdit"
250
+ });
251
+
252
+ const { objectId } = Utils.DecodeVersionHash(versionHash);
253
+
254
+ if(!canEdit) {
255
+ throw Error(`Current user does not have permission to edit content object ${objectId}`);
256
+ }
257
+
258
+ const signerAddress = this.client.CurrentAccountAddress();
259
+
260
+ let token = {
261
+ adr: Utils.B64(signerAddress.replace("0x", ""), "hex"),
262
+ spc: await this.client.ContentSpaceId(),
263
+ lib: await this.client.ContentObjectLibraryId({objectId}),
264
+ qid: objectId,
265
+ sub: Utils.FormatAddress(signerAddress),
266
+ gra: "read",
267
+ iat: Date.now(),
268
+ exp: Date.now() + 3600000,
269
+ ctx: {
270
+ elv: {
271
+ lnk: link,
272
+ src: containerId
273
+ }
274
+ }
275
+ };
276
+
277
+ const compressedToken = Pako.deflateRaw(Buffer.from(JSON.stringify(token), "utf-8"));
278
+ const signature = await this.Sign(Ethers.utils.keccak256(compressedToken));
279
+
280
+ return `aslsjc${Utils.B58(Buffer.concat([
281
+ Buffer.from(signature.replace(/^0x/, ""), "hex"),
282
+ Buffer.from(compressedToken)
283
+ ]))}`;
284
+ }
285
+
243
286
  async MakeAccessRequest({
244
287
  libraryId,
245
288
  objectId,
package/src/Utils.js CHANGED
@@ -433,8 +433,8 @@ const Utils = {
433
433
  return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
434
434
  },
435
435
 
436
- B64: str => {
437
- return Buffer.from(str, "utf-8").toString("base64");
436
+ B64: (str, encoding="utf-8") => {
437
+ return Buffer.from(str, encoding).toString("base64");
438
438
  },
439
439
 
440
440
  FromB64: str => {
@@ -31,7 +31,7 @@ const {
31
31
  * @param {string} contentTypeName - Name of the content type to use
32
32
  * @param {Object=} metadata - Additional metadata for the content object
33
33
  * @param {Array<Object>=} fileInfo - Files to upload (See UploadFiles/UploadFilesFromS3 method)
34
- * @param {boolean=} encrypt=false - (Local files only) - If specified, files will be encrypted
34
+ * @param {boolean=} encrypt=true - (Local or copied files only) - Unless `false` is passed in explicitly, any uploaded/copied files will be stored encrypted
35
35
  * @param {boolean=} copy=false - (S3) If specified, files will be copied from S3
36
36
  * @param {function=} callback - Progress callback for file upload (See UploadFiles/UploadFilesFromS3 method)
37
37
  * @param {Array<Object>=} access=[] - Array of cloud credentials, along with path matching regex strings - Required if any files in the masters are cloud references (currently only AWS S3 is supported)
@@ -73,7 +73,7 @@ exports.CreateProductionMaster = async function({
73
73
  description,
74
74
  metadata={},
75
75
  fileInfo,
76
- encrypt=false,
76
+ encrypt=true,
77
77
  access=[],
78
78
  copy=false,
79
79
  callback
@@ -1319,7 +1319,8 @@ exports.UpdateContentObjectGraph = async function({libraryId, objectId, versionH
1319
1319
  target: string (path to link target),
1320
1320
  type: string ("file", "meta" | "metadata", "rep" - default "metadata")
1321
1321
  targetHash: string (optional, for cross-object links),
1322
- autoUpdate: boolean (if specified, link will be automatically updated to latest version by UpdateContentObjectGraph method)
1322
+ autoUpdate: boolean (if specified, link will be automatically updated to latest version by UpdateContentObjectGraph method),
1323
+ authContainer: string (optional, object id of container object if creating a signed link)
1323
1324
  }
1324
1325
  ]
1325
1326
 
@@ -1348,7 +1349,9 @@ exports.CreateLinks = async function({
1348
1349
  let type = (info.type || "file") === "file" ? "files" : info.type;
1349
1350
  if(type === "metadata") { type = "meta"; }
1350
1351
 
1351
- let target = info.target.replace(/^(\/|\.)+/, "");
1352
+ let target;
1353
+ let authTarget;
1354
+ target = authTarget = info.target.replace(/^(\/|\.)+/, "");
1352
1355
  if(info.targetHash) {
1353
1356
  target = `/qfab/${info.targetHash}/${type}/${target}`;
1354
1357
  } else {
@@ -1363,6 +1366,30 @@ exports.CreateLinks = async function({
1363
1366
  link["."] = { auto_update: { tag: "latest"} };
1364
1367
  }
1365
1368
 
1369
+ // Sign link
1370
+ if(info.authContainer) {
1371
+ const linkMetadata = await this.ContentObjectMetadata({
1372
+ libraryId,
1373
+ objectId,
1374
+ metadataSubtree: path
1375
+ });
1376
+
1377
+ if(linkMetadata) {
1378
+ link["/"] = linkMetadata["/"];
1379
+ link["."] = linkMetadata["."];
1380
+ }
1381
+
1382
+ if(!link["."]) link["."] = {};
1383
+
1384
+ if(!linkMetadata["."]["authorization"]) {
1385
+ link["."]["authorization"] = await this.authClient.GenerateSignedLinkToken({
1386
+ containerId: info.authContainer,
1387
+ versionHash: info.targetHash,
1388
+ link: `./${type}/${authTarget}`
1389
+ });
1390
+ }
1391
+ }
1392
+
1366
1393
  await this.ReplaceMetadata({
1367
1394
  libraryId,
1368
1395
  objectId,
@@ -41,7 +41,11 @@ const argv = yargs
41
41
  })
42
42
  .option("encrypt", {
43
43
  type: "boolean",
44
- description: "If specified, files will be encrypted (local files only)"
44
+ description: "DEPRECATED, HAS NO EFFECT: uploaded/copied files will always be stored encrypted unless --unencrypted specified."
45
+ })
46
+ .option("unencrypted", {
47
+ type: "boolean",
48
+ description: "Store uploaded/copied files unencrypted on server"
45
49
  })
46
50
  .option("s3-copy", {
47
51
  type: "boolean",
@@ -85,13 +89,19 @@ const Create = async ({
85
89
  slug,
86
90
  metadata,
87
91
  files,
88
- encrypt = false,
92
+ encrypt,
93
+ unencrypted,
89
94
  s3Reference,
90
95
  s3Copy,
91
96
  credentials,
92
97
  debug
93
98
  }) => {
94
99
 
100
+ if(encrypt && unencrypted) {
101
+ console.error("Cannot specify both --encrypted and --unencrypted");
102
+ return;
103
+ }
104
+
95
105
  // force ipTitleId to be a string, if present
96
106
  if(ipTitleId) {
97
107
  ipTitleId = ipTitleId.toString();
@@ -220,9 +230,7 @@ const Create = async ({
220
230
  type = await client.ContentType({name: type});
221
231
  }
222
232
 
223
- if(!type) {
224
- throw Error(`Unable to find content type "${originalType}"`);
225
- }
233
+ if(!type) throw Error(`Unable to find content type "${originalType}"`);
226
234
 
227
235
  type = type.hash;
228
236
 
@@ -234,7 +242,7 @@ const Create = async ({
234
242
  description: "Production Master for " + title,
235
243
  metadata,
236
244
  fileInfo,
237
- encrypt,
245
+ encrypt: !unencrypted,
238
246
  access,
239
247
  copy: s3Copy && !s3Reference,
240
248
  callback: progress => {
@@ -0,0 +1,111 @@
1
+ {
2
+ "drm_optional": true,
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-clear": {
88
+ "drm": null,
89
+ "protocol": {
90
+ "min_buffer_length": 2,
91
+ "type": "ProtoDash"
92
+ }
93
+ },
94
+ "hls-clear": {
95
+ "drm": null,
96
+ "protocol": {
97
+ "type": "ProtoHls"
98
+ }
99
+ }
100
+ },
101
+ "segment_specs": {
102
+ "audio": {
103
+ "segs_per_chunk": 15,
104
+ "target_dur": 2
105
+ },
106
+ "video": {
107
+ "segs_per_chunk": 15,
108
+ "target_dur": 2
109
+ }
110
+ }
111
+ }