@eluvio/elv-client-js 4.0.5 → 4.0.7

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 (43) hide show
  1. package/dist/ElvClient-min.js +12 -11
  2. package/dist/ElvClient-node-min.js +12 -11
  3. package/dist/ElvFrameClient-min.js +12 -11
  4. package/dist/ElvPermissionsClient-min.js +10 -9
  5. package/dist/ElvWalletClient-min.js +12 -11
  6. package/dist/ElvWalletClient-node-min.js +11 -10
  7. package/dist/src/AuthorizationClient.js +1556 -2077
  8. package/dist/src/ContentObjectVerification.js +134 -185
  9. package/dist/src/Crypto.js +225 -322
  10. package/dist/src/ElvClient.js +823 -1117
  11. package/dist/src/ElvWallet.js +64 -106
  12. package/dist/src/EthClient.js +719 -974
  13. package/dist/src/FrameClient.js +222 -318
  14. package/dist/src/HttpClient.js +112 -154
  15. package/dist/src/Id.js +1 -6
  16. package/dist/src/LogMessage.js +4 -8
  17. package/dist/src/PermissionsClient.js +973 -1271
  18. package/dist/src/RemoteSigner.js +161 -232
  19. package/dist/src/UserProfileClient.js +781 -1038
  20. package/dist/src/Utils.js +160 -302
  21. package/dist/src/Validation.js +2 -17
  22. package/dist/src/client/ABRPublishing.js +772 -942
  23. package/dist/src/client/AccessGroups.js +849 -1095
  24. package/dist/src/client/ContentAccess.js +3263 -4132
  25. package/dist/src/client/ContentManagement.js +1811 -2283
  26. package/dist/src/client/Contracts.js +468 -614
  27. package/dist/src/client/Files.js +1505 -1845
  28. package/dist/src/client/NFT.js +94 -116
  29. package/dist/src/client/NTP.js +326 -425
  30. package/dist/src/index.js +2 -5
  31. package/dist/src/walletClient/ClientMethods.js +1348 -1735
  32. package/dist/src/walletClient/Configuration.js +4 -2
  33. package/dist/src/walletClient/Notifications.js +98 -127
  34. package/dist/src/walletClient/Profile.js +184 -246
  35. package/dist/src/walletClient/Utils.js +76 -122
  36. package/dist/src/walletClient/index.js +1168 -1493
  37. package/package.json +2 -2
  38. package/src/Crypto.js +1 -1
  39. package/src/ElvClient.js +1 -3
  40. package/src/Utils.js +1 -1
  41. package/testScripts/Test.js +0 -1
  42. package/utilities/NFTIngest.js +527 -0
  43. package/utilities/lib/concerns/LocalFile.js +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eluvio/elv-client-js",
3
- "version": "4.0.5",
3
+ "version": "4.0.7",
4
4
  "description": "Javascript client for the Eluvio Content Fabric",
5
5
  "main": "src/index.js",
6
6
  "author": "Kevin Talmadge",
@@ -60,7 +60,7 @@
60
60
  ],
61
61
  "dependencies": {
62
62
  "@babel/runtime": "^7.8.4",
63
- "@eluvio/crypto": ">=1.0.8",
63
+ "@eluvio/crypto": ">=1.0.12",
64
64
  "@eluvio/elv-abr-profile": "^1.0.0",
65
65
  "@sindresorhus/slugify": "^1.1.0",
66
66
  "babel-loader": "^8.0.6",
package/src/Crypto.js CHANGED
@@ -1,4 +1,4 @@
1
- if(typeof Buffer === "undefined") { Buffer = require("buffer/").Buffer; }
1
+ const Buffer = require("buffer/").Buffer;
2
2
 
3
3
  const bs58 = require("bs58");
4
4
  const Stream = require("stream");
package/src/ElvClient.js CHANGED
@@ -1,6 +1,4 @@
1
- if(typeof Buffer === "undefined") {
2
- Buffer = require("buffer/").Buffer;
3
- }
1
+ const Buffer = require("buffer/").Buffer;
4
2
 
5
3
  const URI = require("urijs");
6
4
  const Ethers = require("ethers");
package/src/Utils.js CHANGED
@@ -1,4 +1,4 @@
1
- if(typeof Buffer === "undefined") { Buffer = require("buffer/").Buffer; }
1
+ const Buffer = require("buffer/").Buffer;
2
2
 
3
3
  const bs58 = require("bs58");
4
4
  const BigNumber = require("bignumber.js").default;
@@ -6,7 +6,6 @@ const ethers = require("ethers");
6
6
 
7
7
  const Test = async () => {
8
8
  try {
9
-
10
9
  const client = await ElvClient.FromNetworkName({
11
10
  networkName: "demo"
12
11
  });
@@ -0,0 +1,527 @@
1
+ // Create new production master from specified file(s)
2
+ const R = require("ramda");
3
+
4
+ const { ModOpt, NewOpt } = require("./lib/options");
5
+ const Utility = require("./lib/Utility");
6
+
7
+ const ABR = require("@eluvio/elv-abr-profile");
8
+
9
+ const Client = require("./lib/concerns/Client");
10
+ const Finalize = require("./lib/concerns/Finalize");
11
+ const LocalFile = require("./lib/concerns/LocalFile");
12
+ const LRO = require("./lib/concerns/LRO");
13
+ const ArgLibraryId = require("./lib/concerns/ArgLibraryId");
14
+ const { seconds } = require("./lib/helpers");
15
+ var UrlJoin = require("url-join");
16
+ const path = require("path");
17
+ const fs = require("fs");
18
+ const mime = require("mime-types");
19
+
20
+ class NFTIngest extends Utility {
21
+ blueprint() {
22
+ return {
23
+ concerns: [Client, Finalize, LocalFile, ArgLibraryId, LRO],
24
+ options: [
25
+ ModOpt("libraryId", { demand: true, forX: "new media object" }),
26
+ NewOpt("title", {
27
+ demand: true,
28
+ descTemplate: "Title for new media object",
29
+ type: "string",
30
+ }),
31
+ NewOpt("description", {
32
+ demand: false,
33
+ descTemplate: "Description for the new media object",
34
+ type: "string",
35
+ }),
36
+ NewOpt("nft_name", {
37
+ demand: false,
38
+ descTemplate: "The name attribute added to the .json file if exists.",
39
+ type: "string",
40
+ }),
41
+ NewOpt("nft_display_name", {
42
+ demand: false,
43
+ descTemplate: "The display_name attribute added to the .json file if exists. Default is the nft_name.",
44
+ type: "string",
45
+ }),
46
+ NewOpt("nft_description", {
47
+ demand: false,
48
+ descTemplate: "The description attribute added to the .json file if exists.",
49
+ type: "string",
50
+ }),
51
+ NewOpt("drm", {
52
+ default: false,
53
+ descTemplate: "Use DRM for playback",
54
+ type: "boolean",
55
+ }),
56
+ NewOpt("encrypt", {
57
+ default: false,
58
+ descTemplate: "Encrypt Content",
59
+ type: "boolean",
60
+ }),
61
+ ModOpt("files", { forX: "for new media object" }),
62
+ ],
63
+ };
64
+ }
65
+
66
+ async body() {
67
+ const logger = this.logger;
68
+
69
+ let fileHandles = [];
70
+ const fileInfo = this.concerns.LocalFile.fileInfo(fileHandles);
71
+
72
+ // delay getting elvClient until this point so script exits faster
73
+ // if there is a validation error above
74
+ const client = await this.concerns.Client.get();
75
+ const configUrl = client.NetworkInfo().configUrl;
76
+ logger.log("Network Info: ", client.NetworkInfo());
77
+ logger.log("Args: ", this.args);
78
+ // get metadata from Library
79
+ const libInfo = await this.concerns.ArgLibraryId.libInfo();
80
+
81
+ const type = R.path(["metadata", "abr", "mez_content_type"], libInfo);
82
+ if(R.isNil(type))
83
+ throw Error("Library does not specify content type for simple ingests");
84
+
85
+ const libABRProfile = R.path(
86
+ ["metadata", "abr", "default_profile"],
87
+ libInfo
88
+ );
89
+ if(R.isNil(libABRProfile))
90
+ throw Error("Library does not specify ABR profile for simple ingests");
91
+
92
+ const libMezManageGroups = R.path(
93
+ ["metadata", "abr", "mez_manage_groups"],
94
+ libInfo
95
+ );
96
+
97
+ const libMezPermission = R.path(
98
+ ["metadata", "abr", "mez_permission_level"],
99
+ libInfo
100
+ );
101
+
102
+ const {drm, libraryId, title, description, encrypt, nft_name, nft_description} =
103
+ this.args;
104
+
105
+ var {nft_display_name} = this.args;
106
+
107
+ if(!nft_display_name) {
108
+ nft_display_name = nft_name;
109
+ }
110
+
111
+ logger.log("Uploading files...");
112
+
113
+ const createMasterResponse = await client.CreateProductionMaster({
114
+ libraryId,
115
+ type,
116
+ name: title,
117
+ description,
118
+ fileInfo,
119
+ encrypt,
120
+ copy: true,
121
+ callback: this.concerns.LocalFile.callback,
122
+ });
123
+
124
+ const { id, hash } = createMasterResponse;
125
+ // Log object id immediately, in case of error later in script
126
+ // Don't log hash yet, it will change if --streams was provided (or any other revision to object is needed)
127
+ logger.data("object_id", id);
128
+
129
+ // Close file handles (if any)
130
+ this.concerns.LocalFile.closeFileHandles(fileHandles);
131
+
132
+ logger.errorsAndWarnings(createMasterResponse);
133
+
134
+ logger.logList(
135
+ "",
136
+ "Production master default variant created:",
137
+ ` Object ID: ${id}`,
138
+ ` Version Hash: ${hash}`,
139
+ ""
140
+ );
141
+
142
+ logger.data("version_hash", hash);
143
+
144
+ if(
145
+ !R.isNil(createMasterResponse.errors) &&
146
+ !R.isEmpty(createMasterResponse.errors)
147
+ )
148
+ throw Error(
149
+ `Error(s) encountered while inspecting uploaded files: ${createMasterResponse.errors.join(
150
+ "\n"
151
+ )}`
152
+ );
153
+
154
+ await seconds(2);
155
+
156
+ await this.concerns.Finalize.waitForPublish({
157
+ latestHash: hash,
158
+ libraryId,
159
+ objectId: id,
160
+ });
161
+
162
+ // get production master metadata
163
+ const masterMetadata = await client.ContentObjectMetadata({
164
+ libraryId,
165
+ objectId: id,
166
+ versionHash: hash,
167
+ metadataSubtree: "/production_master",
168
+ });
169
+
170
+ const sources = R.prop("sources", masterMetadata);
171
+ const variant = R.path(["variants", "default"], masterMetadata);
172
+
173
+ // add info on source files and variant to data if --json selected
174
+ if(this.args.json) {
175
+ logger.data("media_files", sources);
176
+ logger.data("variant_default", variant);
177
+ }
178
+
179
+ // generate ABR profile
180
+ const genProfileRetVal = ABR.ABRProfileForVariant(
181
+ sources,
182
+ variant,
183
+ libABRProfile
184
+ );
185
+ if(!genProfileRetVal.ok)
186
+ throw Error(
187
+ `Error(s) encountered while generating ABR profile: ${genProfileRetVal.errors.join(
188
+ "\n"
189
+ )}`
190
+ );
191
+
192
+ // filter DRM/clear as needed
193
+ const filterProfileRetVal = drm
194
+ ? ABR.ProfileExcludeClear(genProfileRetVal.result)
195
+ : ABR.ProfileExcludeDRM(genProfileRetVal.result);
196
+ if(!filterProfileRetVal.ok)
197
+ throw Error(
198
+ `Error(s) encountered while setting playout formats: ${filterProfileRetVal.errors.join(
199
+ "\n"
200
+ )}`
201
+ );
202
+
203
+ // set up mezzanine offering
204
+ logger.log("Setting up media file conversion...");
205
+ const createMezResponse = await client.CreateABRMezzanine({
206
+ name: title,
207
+ libraryId,
208
+ objectId: id,
209
+ type,
210
+ masterVersionHash: hash,
211
+ variant: "default",
212
+ offeringKey: "default",
213
+ abrProfile: filterProfileRetVal.result,
214
+ });
215
+
216
+ logger.errorsAndWarnings(createMezResponse);
217
+ const createMezErrors = createMezResponse.errors;
218
+ if(!R.isNil(createMezErrors) && !R.isEmpty(createMezErrors))
219
+ throw Error(
220
+ `Error(s) encountered while setting up media file conversion: ${createMezErrors.join(
221
+ "\n"
222
+ )}`
223
+ );
224
+
225
+ await this.concerns.Finalize.waitForPublish({
226
+ latestHash: createMezResponse.hash,
227
+ libraryId,
228
+ objectId: id,
229
+ });
230
+
231
+ logger.log("Starting conversion to streaming format...");
232
+
233
+ const startJobsResponse = await client.StartABRMezzanineJobs({
234
+ libraryId,
235
+ objectId: id,
236
+ offeringKey: "default",
237
+ });
238
+
239
+ logger.errorsAndWarnings(startJobsResponse);
240
+ const startJobsErrors = createMezResponse.errors;
241
+ if(!R.isNil(startJobsErrors) && !R.isEmpty(startJobsErrors))
242
+ throw Error(
243
+ `Error(s) encountered while starting file conversion: ${startJobsErrors.join(
244
+ "\n"
245
+ )}`
246
+ );
247
+
248
+ const lroWriteToken = R.path(
249
+ ["lro_draft", "write_token"],
250
+ startJobsResponse
251
+ );
252
+ const lroNode = R.path(["lro_draft", "node"], startJobsResponse);
253
+
254
+ logger.data("library_id", libraryId);
255
+ logger.data("object_id", id);
256
+ logger.data("offering_key", "default");
257
+ logger.data("write_token", lroWriteToken);
258
+ logger.data("write_node", lroNode);
259
+
260
+ logger.logList(
261
+ "",
262
+ `Library ID: ${libraryId}`,
263
+ `Object ID: ${id}`,
264
+ "Offering: default",
265
+ `Write Token: ${lroWriteToken}`,
266
+ `Write Node: ${lroNode}`,
267
+ ""
268
+ );
269
+
270
+ // wait for latest version hash to become visible (if publish not finished, then checking progress can fail
271
+ // as metadata /lro_draft_default will not be found)
272
+
273
+ await this.concerns.Finalize.waitForPublish({
274
+ latestHash: startJobsResponse.hash,
275
+ libraryId,
276
+ objectId: id,
277
+ });
278
+
279
+ logger.log("Progress:");
280
+
281
+ const lro = this.concerns.LRO;
282
+ let done = false;
283
+ let lastStatus;
284
+ while(!done) {
285
+ const statusMap = await lro.status({ libraryId, objectId: id }); // TODO: check how offering key is used, if at all
286
+ const statusSummary = lro.statusSummary(statusMap);
287
+ lastStatus = statusSummary.run_state;
288
+ if(lastStatus !== LRO.STATE_RUNNING) done = true;
289
+ logger.log(`run_state: ${lastStatus}`);
290
+ const eta = statusSummary.estimated_time_left_h_m_s;
291
+ if(eta) logger.log(`estimated time left: ${eta}`);
292
+ await seconds(15);
293
+ }
294
+
295
+ const finalizeAbrResponse = await client.FinalizeABRMezzanine({
296
+ libraryId,
297
+ objectId: id,
298
+ offeringKey: "default",
299
+ });
300
+ let latestHash = finalizeAbrResponse.hash;
301
+
302
+ logger.errorsAndWarnings(finalizeAbrResponse);
303
+ const finalizeErrors = finalizeAbrResponse.errors;
304
+ if(!R.isNil(finalizeErrors) && !R.isEmpty(finalizeErrors)) {
305
+ throw Error(
306
+ `Error(s) encountered while finalizing object: ${finalizeErrors.join(
307
+ "\n"
308
+ )}`
309
+ );
310
+ }
311
+
312
+ if(libMezManageGroups && libMezManageGroups.length > 0) {
313
+ for(const groupAddress of libMezManageGroups) {
314
+ logger.log("Setting access permissions for managers");
315
+ await client.AddContentObjectGroupPermission({
316
+ objectId: id,
317
+ groupAddress,
318
+ permission: "manage",
319
+ });
320
+ }
321
+ }
322
+
323
+ if(libMezPermission) {
324
+ if(
325
+ !["owner", "editable", "viewable", "listable", "public"].includes(
326
+ libMezPermission
327
+ )
328
+ ) {
329
+ logger.warn(
330
+ `Bad value for mez_permission_level: '${libMezPermission}', skipping permission setting`
331
+ );
332
+ } else {
333
+ logger.log(`Setting object permission to '${libMezPermission}'`);
334
+ const prevHash = await client.LatestVersionHash({ objectId: id });
335
+
336
+ await client.SetPermission({
337
+ objectId: id,
338
+ permission: libMezPermission,
339
+ });
340
+
341
+ const newHash = await client.LatestVersionHash({ objectId: id });
342
+
343
+ if(prevHash === newHash) {
344
+ logger.log("Version hash unchanged: " + newHash);
345
+ } else {
346
+ logger.log("Previous version hash: " + prevHash);
347
+ logger.log("New version hash: " + newHash);
348
+ }
349
+ logger.data("version_hash", newHash);
350
+ latestHash = newHash;
351
+ }
352
+ }
353
+
354
+ logger.logList(
355
+ "",
356
+ "Playable media object created:",
357
+ ` Object ID: ${id}`,
358
+ ` Version Hash: ${latestHash}`,
359
+ ""
360
+ );
361
+
362
+ await this.concerns.Finalize.waitForPublish({
363
+ latestHash,
364
+ libraryId,
365
+ objectId: id,
366
+ });
367
+
368
+ let filepath = path.parse(fileInfo[0].fullPath);
369
+ let imageLinkPath = "public/asset_metadata/images/img/default";
370
+ const imageExtensions = ["gif", "jpg", "jpeg", "png", "svg", "webp"];
371
+
372
+ for(const ext of imageExtensions) {
373
+ //Upload Image and set links if exists
374
+ let imageFile = path.join(filepath.dir, filepath.name) + "." + ext;
375
+ if(fs.existsSync(imageFile)) {
376
+ logger.log("Found image file: ", imageFile);
377
+ const fileDescriptor = fs.openSync(imageFile, "r");
378
+ const size = fs.fstatSync(fileDescriptor).size;
379
+ const mimeType = mime.lookup(imageFile) || `image/${ext}`;
380
+
381
+ let imageInfo = [
382
+ {
383
+ path: path.basename(imageFile),
384
+ type: "file",
385
+ mime_type: mimeType,
386
+ size: size,
387
+ data: fileDescriptor,
388
+ },
389
+ ];
390
+
391
+ await client.EditAndFinalizeContentObject({
392
+ libraryId,
393
+ objectId: id,
394
+ commitMessage: "Add Image file.",
395
+ callback: async ({ writeToken }) => {
396
+ try {
397
+ await client.UploadFiles({
398
+ libraryId,
399
+ objectId: id,
400
+ writeToken,
401
+ fileInfo: imageInfo,
402
+ callback: (progress) => {
403
+ const fileProgress = progress[path.basename(imageFile)];
404
+ let percentage = Math.round(
405
+ (fileProgress.uploaded / fileProgress.total) * 100
406
+ );
407
+
408
+ logger.log(
409
+ `Uploading ${path.basename(imageFile)}: ${percentage}%`
410
+ );
411
+ },
412
+ encryption: encrypt ? "cgck" : "none",
413
+ });
414
+
415
+ await client.CreateLinks({
416
+ libraryId,
417
+ objectId: id,
418
+ writeToken,
419
+ links: [
420
+ {
421
+ target: `${path.basename(imageFile)}`,
422
+ path: imageLinkPath,
423
+ type: "file",
424
+ },
425
+ ],
426
+ });
427
+
428
+ logger.log("Image uploaded and link created.");
429
+ } catch(err) {
430
+ logger.warn("Could not upload image file", err);
431
+ }
432
+ },
433
+ });
434
+
435
+ latestHash = await client.LatestVersionHash({ objectId: id });
436
+ break;
437
+ }
438
+ }
439
+
440
+ logger.data("Version Hash: ", latestHash);
441
+ let imageUrl = await client.FabricUrl({
442
+ versionHash: latestHash,
443
+ noAuth: true,
444
+ });
445
+
446
+ imageUrl = UrlJoin(
447
+ lroNode,
448
+ "s",
449
+ GetNetForPublic({ configUrl }),
450
+ "q",
451
+ latestHash,
452
+ "meta",
453
+ imageLinkPath
454
+ );
455
+
456
+ let embedUrl = CreateEmbedUrl({
457
+ versionHash: latestHash,
458
+ network: GetNetForEmbed({ configUrl }),
459
+ });
460
+
461
+ logger.logList(`embed_url: ${embedUrl}`, `image: ${imageUrl}`);
462
+
463
+ let jsonFile = path.join(filepath.dir, filepath.name) + ".json";
464
+ logger.log("Looking for Json file: ", jsonFile);
465
+
466
+ let jsonFileContents = null;
467
+
468
+ if(fs.existsSync(jsonFile)) {
469
+ try {
470
+ jsonFileContents = JSON.parse(fs.readFileSync(jsonFile));
471
+ logger.log("JSON File found: ", jsonFileContents);
472
+ if(nft_name) {
473
+ jsonFileContents.name = nft_name;
474
+ }
475
+ if(nft_display_name) {
476
+ jsonFileContents.display_name = nft_display_name;
477
+ }
478
+ if(nft_description) {
479
+ jsonFileContents.description = nft_description;
480
+ }
481
+ jsonFileContents.image = imageUrl;
482
+ jsonFileContents.embed_url = embedUrl;
483
+
484
+ let jsonString = JSON.stringify(jsonFileContents, "", 2);
485
+ fs.writeFileSync(jsonFile, jsonString);
486
+ logger.log("Json file updated: ", jsonString);
487
+ //file written successfully
488
+ } catch(err) {
489
+ logger.warn("Could not read or write to json file", err);
490
+ }
491
+ }
492
+ }
493
+
494
+ header() {
495
+ return "Ingest media for Generative Video NFTs";
496
+ }
497
+ }
498
+
499
+ const CreateEmbedUrl = ({ versionHash, network }) => {
500
+ return `https://embed.v3.contentfabric.io/?p&net=${network}&vid=${versionHash}&m&ap&lp&nwm`;
501
+ };
502
+
503
+ const GetNetForEmbed = ({ configUrl }) => {
504
+ if(configUrl.includes("demo")) {
505
+ return "demo";
506
+ } else if(configUrl.includes("test")) {
507
+ return "test";
508
+ } else {
509
+ return "main";
510
+ }
511
+ };
512
+
513
+ const GetNetForPublic = ({ configUrl }) => {
514
+ if(configUrl.includes("demo")) {
515
+ return "demov3";
516
+ } else if(configUrl.includes("test")) {
517
+ return "test";
518
+ } else {
519
+ return "main";
520
+ }
521
+ };
522
+
523
+ if(require.main === module) {
524
+ Utility.cmdLineInvoke(NFTIngest);
525
+ } else {
526
+ module.exports = NFTIngest;
527
+ }
@@ -50,6 +50,7 @@ const New = context => {
50
50
  const mimeType = mime.lookup(fullPath) || "video/mp4";
51
51
 
52
52
  return {
53
+ fullPath,
53
54
  path: path.basename(fullPath),
54
55
  type: "file",
55
56
  mime_type: mimeType,
@@ -62,4 +63,4 @@ const New = context => {
62
63
  return {add, callback, closeFileHandles, fileInfo};
63
64
  };
64
65
 
65
- module.exports = {blueprint, New};
66
+ module.exports = {blueprint, New};