@eluvio/elv-client-js 4.0.104 → 4.0.107

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 (41) hide show
  1. package/dist/ElvClient-min.js +15 -14
  2. package/dist/ElvClient-node-min.js +14 -13
  3. package/dist/ElvFrameClient-min.js +10 -10
  4. package/dist/ElvPermissionsClient-min.js +9 -9
  5. package/dist/ElvWalletClient-min.js +15 -14
  6. package/dist/ElvWalletClient-node-min.js +14 -13
  7. package/dist/src/AuthorizationClient.js +9 -12
  8. package/dist/src/ContentObjectAudit.js +3 -3
  9. package/dist/src/ContentObjectVerification.js +3 -3
  10. package/dist/src/Crypto.js +2 -2
  11. package/dist/src/ElvClient.js +47 -28
  12. package/dist/src/ElvWallet.js +7 -5
  13. package/dist/src/EthClient.js +8 -9
  14. package/dist/src/FrameClient.js +8 -9
  15. package/dist/src/HttpClient.js +1 -2
  16. package/dist/src/Id.js +1 -2
  17. package/dist/src/PermissionsClient.js +31 -19
  18. package/dist/src/RemoteSigner.js +10 -11
  19. package/dist/src/UserProfileClient.js +194 -150
  20. package/dist/src/Utils.js +2 -3
  21. package/dist/src/abr_profiles/abr_profile_live_drm.js +22 -0
  22. package/dist/src/client/ABRPublishing.js +2 -2
  23. package/dist/src/client/AccessGroups.js +2 -2
  24. package/dist/src/client/ContentAccess.js +9 -6
  25. package/dist/src/client/ContentManagement.js +3 -3
  26. package/dist/src/client/Contracts.js +433 -108
  27. package/dist/src/client/Files.js +2 -2
  28. package/dist/src/client/LiveConf.js +60 -78
  29. package/dist/src/client/LiveStream.js +270 -158
  30. package/dist/src/client/NFT.js +2 -2
  31. package/dist/src/walletClient/ClientMethods.js +2 -2
  32. package/dist/src/walletClient/Profile.js +2 -2
  33. package/dist/src/walletClient/Utils.js +2 -2
  34. package/dist/src/walletClient/index.js +19 -16
  35. package/package.json +1 -1
  36. package/src/UserProfileClient.js +19 -20
  37. package/src/abr_profiles/abr_profile_live_drm.js +22 -0
  38. package/src/client/Contracts.js +244 -42
  39. package/src/client/LiveConf.js +86 -111
  40. package/src/client/LiveStream.js +141 -44
  41. package/testScripts/TestAddTenantContractId.js +45 -4
@@ -1,53 +1,48 @@
1
- const LadderTemplate = {
2
- "2160": {
3
- bit_rate: 14000000,
4
- codecs: "avc1.640028,mp4a.40.2",
5
- height: 2160,
6
- media_type: 1,
7
- representation: "videovideo_3840x2160_h264@14000000",
8
- stream_name: "video",
9
- width: 3840
10
- },
11
- "1080": {
12
- bit_rate: 9500000,
13
- codecs: "avc1.640028,mp4a.40.2",
14
- height: 1080,
15
- media_type: 1,
16
- representation: "videovideo_1920x1080_h264@9500000",
17
- stream_name: "video",
18
- stream_index: 0,
19
- width: 1920
20
- },
21
- "720": {
22
- bit_rate: 4500000,
23
- codecs: "avc1.640028,mp4a.40.2",
24
- height: 720,
25
- media_type: 1,
26
- representation: "videovideo_1280x720_h264@4500000",
27
- stream_name: "video",
28
- stream_index: 0,
29
- width: 1280
30
- },
31
- "540": {
32
- bit_rate: 2000000,
33
- codecs: "avc1.640028,mp4a.40.2",
34
- height: 540,
35
- media_type: 1,
36
- representation: "videovideo_960x540_h264@2000000",
37
- stream_name: "video",
38
- stream_index: 0,
39
- width: 960
40
- },
41
- "540_low": {
42
- bit_rate: 900000,
43
- codecs: "avc1.640028,mp4a.40.2",
44
- height: 540,
45
- media_type: 1,
46
- representation: "videovideo_960x540_h264@900000",
47
- stream_name: "video",
48
- stream_index: 0,
49
- width: 960
50
- }
1
+ const DefaultABRLadder = {
2
+ "video" : [
3
+ {
4
+ bit_rate: 14000000,
5
+ codecs: "avc1.640028,mp4a.40.2",
6
+ height: 2160,
7
+ width: 3840
8
+ },
9
+ {
10
+ bit_rate: 9500000,
11
+ codecs: "avc1.640028,mp4a.40.2",
12
+ height: 1080,
13
+ width: 1920
14
+ },
15
+ {
16
+ bit_rate: 4500000,
17
+ codecs: "avc1.640028,mp4a.40.2",
18
+ height: 720,
19
+ width: 1280
20
+ },
21
+ {
22
+ bit_rate: 2000000,
23
+ codecs: "avc1.640028,mp4a.40.2",
24
+ height: 540,
25
+ width: 960
26
+ },
27
+ {
28
+ bit_rate: 900000,
29
+ codecs: "avc1.640028,mp4a.40.2",
30
+ height: 540,
31
+ width: 960
32
+ }
33
+ ],
34
+ "audio" : [
35
+ {
36
+ bit_rate: 192000,
37
+ channels: 2,
38
+ codecs: "mp4a.40.2",
39
+ },
40
+ {
41
+ bit_rate: 384000,
42
+ channels: 6,
43
+ codecs: "mp4a.40.2",
44
+ }
45
+ ]
51
46
  };
52
47
 
53
48
  const LiveconfTemplate = {
@@ -115,16 +110,6 @@ const LiveconfTemplate = {
115
110
  }
116
111
  };
117
112
 
118
- const LadderSpecAudio = {
119
- bit_rate: 384000,
120
- channels: 2,
121
- codecs: "mp4a.40.2",
122
- media_type: 2,
123
- representation: "audioaudio_aac@384000",
124
- stream_name: "audio",
125
- stream_index: 0
126
- };
127
-
128
113
  class LiveConf {
129
114
  constructor(probeData, nodeId, nodeUrl, includeAVSegDurations, overwriteOriginUrl, syncAudioToVideo) {
130
115
  this.probeData = probeData;
@@ -390,7 +375,7 @@ class LiveConf {
390
375
  generateAudioStreamsConfig({customSettings}) {
391
376
 
392
377
  let audioStreams = {};
393
- if(customSettings && customSettings.audio) {
378
+ if(customSettings && customSettings.audio && Object.keys(customSettings.audio).length > 0) {
394
379
  for(let i = 0; i < Object.keys(customSettings.audio).length; i ++) {
395
380
  let audioIdx = Object.keys(customSettings.audio)[i];
396
381
  let audio = customSettings.audio[audioIdx];
@@ -405,7 +390,7 @@ class LiveConf {
405
390
  }
406
391
 
407
392
  // If no audio streams specified in custom config, set up all the suitable audio streams in the probe
408
- if(!customSettings.audio) {
393
+ if(!customSettings.audio || Object.keys(customSettings.audio).length == 0) {
409
394
  audioStreams = this.getAudioStreamsFromProbe();
410
395
  }
411
396
 
@@ -506,68 +491,57 @@ class LiveConf {
506
491
  conf.live_recording.recording_config.recording_params.xc_params.video_frame_duration_ts = segDurations.videoFrameDurationTs;
507
492
  }
508
493
 
509
- switch(videoStream.height) {
510
- case 2160:
511
- conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
512
- LadderTemplate[2160],
513
- LadderTemplate[1080],
514
- LadderTemplate[720],
515
- LadderTemplate[540],
516
- LadderTemplate["540_low"]
517
- );
518
- conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[2160].bit_rate;
519
- conf.live_recording.recording_config.recording_params.xc_params.enc_height = 2160;
520
- conf.live_recording.recording_config.recording_params.xc_params.enc_width = 3840;
494
+ const ladderProfile = customSettings.ladder_profile || DefaultABRLadder;
521
495
 
522
- break;
523
- case 1080:
524
- conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
525
- LadderTemplate[1080],
526
- LadderTemplate[720],
527
- LadderTemplate[540],
528
- LadderTemplate["540_low"]
529
- );
530
- conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[1080].bit_rate;
531
- conf.live_recording.recording_config.recording_params.xc_params.enc_height = 1080;
532
- conf.live_recording.recording_config.recording_params.xc_params.enc_width = 1920;
533
- break;
534
- case 720:
535
- conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
536
- LadderTemplate[720],
537
- LadderTemplate[540],
538
- LadderTemplate["540_low"]
539
- );
540
- conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[720].bit_rate;
541
- conf.live_recording.recording_config.recording_params.xc_params.enc_height = 720;
542
- conf.live_recording.recording_config.recording_params.xc_params.enc_width = 1280;
543
- break;
544
- case 540:
545
- conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
546
- LadderTemplate[540],
547
- LadderTemplate["540_low"]
548
- );
549
- conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[540].bit_rate;
550
- conf.live_recording.recording_config.recording_params.xc_params.enc_height = 540;
551
- conf.live_recording.recording_config.recording_params.xc_params.enc_width = 960;
552
- break;
553
- default:
554
- throw new Error("ERROR: Probed stream does not conform to one of the following built in resolution ladders [4096, 2160], [1920, 1080] [1280, 720], [960, 540]");
555
- }
496
+ conf.live_recording.recording_config.recording_params.xc_params.enc_height = videoStream.height;
497
+ conf.live_recording.recording_config.recording_params.xc_params.enc_width = videoStream.width;
556
498
 
499
+ // Determine video recording bitrate and ABR ladder
500
+ let topLadderRate = 0
501
+ for (let i = 0; i < ladderProfile.video.length; i ++) {
502
+ let elem = ladderProfile.video[i];
503
+ if (elem.height > videoStream.height)
504
+ continue;
505
+ if (elem.bit_rate > topLadderRate) {
506
+ topLadderRate = elem.bit_rate
507
+ }
508
+ elem.media_type = 1;
509
+ elem.stream_name = "video";
510
+ elem.stream_index = 0;
511
+ elem.representation = "videovideo_" + elem.width + "x" + elem.height + "_h264@" + elem.bit_rate;
512
+ conf.live_recording.recording_config.recording_params.ladder_specs.push(elem);
513
+ }
514
+ // Currently the recording bitrate is the top bitrate of the ladder (it will be configurable separately in the future)
515
+ conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = topLadderRate;
557
516
 
517
+ // Determine audio recording bitrate and ABR ladder
558
518
  let globalAudioBitrate = 0;
559
519
  let nAudio = 0;
560
520
 
561
521
  for(let i = 0; i < Object.keys(audioStreams).length; i ++ ) {
562
- let audioLadderSpec = {...LadderSpecAudio};
522
+ let audioLadderSpec = {};
563
523
  const audioIndex = Object.keys(audioStreams)[i];
564
524
  const audio = audioStreams[audioIndex];
565
- audioLadderSpec.bit_rate = audio.recordingBitrate;
566
- audioLadderSpec.representation = `audioaudio_aac@${audio.recordingBitrate}`;
525
+
526
+ // Find ladder rung for the specific channel layout (2 or 6 channels)
527
+ for (let j = 0; j < ladderProfile.audio.length; j ++) {
528
+ let elem = ladderProfile.audio[j];
529
+ if (elem.channels == audio.recordingChannels) {
530
+ audioLadderSpec = {...elem};
531
+ break;
532
+ }
533
+ }
534
+ if (Object.keys(audioLadderSpec).length === 0) {
535
+ // If no channels layout match, just use the first element in the ladder
536
+ audioLadderSpec = {...ladderProfile.audio[0]};
537
+ }
538
+
539
+ audioLadderSpec.representation = `audioaudio_aac@${audioLadderSpec.bit_rate}`;
567
540
  audioLadderSpec.channels = audio.recordingChannels;
568
541
  audioLadderSpec.stream_index = parseInt(audioIndex);
569
542
  audioLadderSpec.stream_name = `audio_${audioIndex}`;
570
543
  audioLadderSpec.stream_label = audio.playoutLabel ? audio.playoutLabel : null;
544
+ audioLadderSpec.media_type = 2;
571
545
 
572
546
  conf.live_recording.recording_config.recording_params.ladder_specs.push(audioLadderSpec);
573
547
  if(audio.recordingBitrate > globalAudioBitrate) {
@@ -583,4 +557,5 @@ class LiveConf {
583
557
  return conf;
584
558
  }
585
559
  }
560
+
586
561
  exports.LiveConf = LiveConf;
@@ -415,16 +415,23 @@ exports.StreamStatus = async function({name, stopLro=false, showParams=false}) {
415
415
 
416
416
  status.edge_write_token = edgeWriteToken;
417
417
  status.stream_id = edgeWriteToken; // By convention the stream ID is its write token
418
- let edgeMeta = await this.ContentObjectMetadata({
419
- libraryId: libraryId,
420
- objectId: objectId,
421
- writeToken: edgeWriteToken,
422
- select: [
423
- "live_recording"
424
- ]
425
- });
418
+ let edgeMeta;
419
+ try {
420
+ edgeMeta = await this.ContentObjectMetadata({
421
+ libraryId: libraryId,
422
+ objectId: objectId,
423
+ writeToken: edgeWriteToken,
424
+ select: [
425
+ "live_recording"
426
+ ]
427
+ });
428
+ } catch(error) {
429
+ console.error("Unable to read edge write token metadata. Has token been deleted?", error);
430
+ status.state = "inactive";
431
+ return status;
432
+ }
426
433
 
427
- status.edge_meta_size = JSON.stringify(edgeMeta).length;
434
+ status.edge_meta_size = JSON.stringify(edgeMeta || "").length;
428
435
 
429
436
  // If a stream has never been started return state 'inactive'
430
437
  if(edgeMeta.live_recording === undefined ||
@@ -1030,6 +1037,18 @@ exports.StreamSetOfferingAndDRM = async function({name, typeAbrMaster, typeLiveS
1030
1037
  };
1031
1038
  continue;
1032
1039
  }
1040
+ if(formats[i] === "dash-clear") {
1041
+ abrProfile.drm_optional = true;
1042
+ playoutFormats["dash-clear"] = {
1043
+ "drm": null,
1044
+ "protocol": {
1045
+ "min_buffer_length": 2,
1046
+ "type": "ProtoDash"
1047
+ }
1048
+ }
1049
+ continue;
1050
+ }
1051
+
1033
1052
  playoutFormats[formats[i]] = abrProfile.playout_formats[formats[i]];
1034
1053
  }
1035
1054
  } else if(!drm) {
@@ -1040,6 +1059,13 @@ exports.StreamSetOfferingAndDRM = async function({name, typeAbrMaster, typeLiveS
1040
1059
  "protocol": {
1041
1060
  "type": "ProtoHls"
1042
1061
  }
1062
+ },
1063
+ "dash-clear": {
1064
+ "drm": null,
1065
+ "protocol": {
1066
+ "min_buffer_length": 2,
1067
+ "type": "ProtoDash"
1068
+ }
1043
1069
  }
1044
1070
  };
1045
1071
  } else {
@@ -1766,26 +1792,36 @@ exports.StreamCopyToVod = async function({
1766
1792
  *
1767
1793
  * @methodGroup Live Stream
1768
1794
  * @namedParams
1795
+ * @param {string=} libraryId - Library ID of the live stream
1769
1796
  * @param {string} objectId - Object ID of the live stream
1797
+ * @param {string=} writeToken - Write token of the draft
1770
1798
  * @param {Array<string>} types - Specify which type of watermark to remove. Possible values:
1771
1799
  * - "image"
1772
1800
  * - "text"
1801
+ * - "forensic"
1773
1802
  * @param {boolean=} finalize - If enabled, target object will be finalized after removing watermark
1774
1803
  *
1775
1804
  * @return {Promise<Object>} - The finalize response
1776
1805
  */
1777
1806
  exports.StreamRemoveWatermark = async function({
1807
+ libraryId,
1778
1808
  objectId,
1809
+ writeToken,
1779
1810
  types,
1780
1811
  finalize=true
1781
1812
  }) {
1782
1813
  ValidateObject(objectId);
1783
1814
 
1784
- const libraryId = await this.ContentObjectLibraryId({objectId});
1785
- const {writeToken} = await this.EditContentObject({
1786
- objectId,
1787
- libraryId
1788
- });
1815
+ if(!libraryId) {
1816
+ libraryId = await this.ContentObjectLibraryId({objectId});
1817
+ }
1818
+
1819
+ if(!writeToken) {
1820
+ ({writeToken} = await this.EditContentObject({
1821
+ objectId,
1822
+ libraryId
1823
+ }));
1824
+ }
1789
1825
 
1790
1826
  this.Log(`Removing watermark types: ${types.join(", ")} ${libraryId} ${objectId}`);
1791
1827
 
@@ -1795,25 +1831,27 @@ exports.StreamRemoveWatermark = async function({
1795
1831
  metadataSubtree: "/live_recording/fabric_config/edge_write_token"
1796
1832
  });
1797
1833
 
1798
- const recordingParamsPath = "live_recording/recording_config/recording_params";
1834
+ const metadataPath = "live_recording/playout_config";
1799
1835
 
1800
- const recordingMetadata = await this.ContentObjectMetadata({
1836
+ const objectMetadata = await this.ContentObjectMetadata({
1801
1837
  libraryId,
1802
1838
  objectId,
1803
1839
  writeToken,
1804
- metadataSubtree: recordingParamsPath,
1840
+ metadataSubtree: metadataPath,
1805
1841
  resolveLinks: false
1806
1842
  });
1807
1843
 
1808
- if(!recordingMetadata) {
1809
- throw Error("Stream object must be configured");
1844
+ if(!objectMetadata) {
1845
+ throw Error("Stream object must be configured before removing a watermark");
1810
1846
  }
1811
1847
 
1812
1848
  types.forEach(type => {
1813
1849
  if(type === "text") {
1814
- delete recordingMetadata.simple_watermark;
1850
+ delete objectMetadata.simple_watermark;
1815
1851
  } else if(type === "image") {
1816
- delete recordingMetadata.image_watermark;
1852
+ delete objectMetadata.image_watermark;
1853
+ } else if(type === "forensic") {
1854
+ delete objectMetadata.forensic_watermark;
1817
1855
  }
1818
1856
  });
1819
1857
 
@@ -1821,8 +1859,8 @@ exports.StreamRemoveWatermark = async function({
1821
1859
  libraryId,
1822
1860
  objectId,
1823
1861
  writeToken,
1824
- metadataSubtree: recordingParamsPath,
1825
- metadata: recordingMetadata
1862
+ metadataSubtree: metadataPath,
1863
+ metadata: objectMetadata
1826
1864
  });
1827
1865
 
1828
1866
  if(edgeWriteToken) {
@@ -1830,8 +1868,8 @@ exports.StreamRemoveWatermark = async function({
1830
1868
  libraryId,
1831
1869
  objectId,
1832
1870
  writeToken: edgeWriteToken,
1833
- metadataSubtree: recordingParamsPath,
1834
- metadata: recordingMetadata
1871
+ metadataSubtree: metadataPath,
1872
+ metadata: objectMetadata
1835
1873
  });
1836
1874
  }
1837
1875
 
@@ -1852,26 +1890,72 @@ exports.StreamRemoveWatermark = async function({
1852
1890
  *
1853
1891
  * @methodGroup Live Stream
1854
1892
  * @namedParams
1893
+ * @param {string=} libraryId - Library ID of the live stream
1855
1894
  * @param {string} objectId - Object ID of the live stream
1895
+ * @param {string=} writeToken - Write token of the draft
1856
1896
  * @param {Object} simpleWatermark - Text watermark
1857
1897
  * @param {Object} imageWatermark - Image watermark
1898
+ * @param {Object} forensicWatermark - Forensic watermark
1858
1899
  * @param {boolean=} finalize - If enabled, target object will be finalized after adding watermark
1900
+ * Watermark examples:
1901
+ *
1902
+ * Simple Watermark:
1903
+ {
1904
+ "font_color": "",
1905
+ "font_relative_height": 0,
1906
+ "shadow": false,
1907
+ "template": "",
1908
+ "timecode": "",
1909
+ "timecode_rate": 0,
1910
+ "x": "",
1911
+ "y": ""
1912
+ }
1913
+ *
1914
+ * Image watermark:
1915
+ {
1916
+ "image": "",
1917
+ "align_h": "",
1918
+ "align_v": "",
1919
+ "target_video_height": 0,
1920
+ "wm_enabled": false
1921
+ }
1922
+ *
1923
+ * Forensic watermark:
1924
+ {
1925
+ "algo": 6,
1926
+ "forensic_duration": 0,
1927
+ "forensic_start": "",
1928
+ "image_a": <path_to_image>,
1929
+ "image_b": <path_to_image>,
1930
+ "is_stub": true,
1931
+ "payload_bit_nb": 23,
1932
+ "wm_enabled": true
1933
+ }
1934
+ *
1859
1935
  *
1860
1936
  * @return {Promise<Object>} - The finalize response
1861
1937
  */
1862
1938
  exports.StreamAddWatermark = async function({
1939
+ libraryId,
1863
1940
  objectId,
1941
+ writeToken,
1864
1942
  simpleWatermark,
1865
1943
  imageWatermark,
1944
+ forensicWatermark,
1866
1945
  finalize=true
1867
1946
  }) {
1868
1947
  ValidateObject(objectId);
1869
1948
 
1870
- const libraryId = await this.ContentObjectLibraryId({objectId});
1871
- const {writeToken} = await this.EditContentObject({
1872
- objectId,
1873
- libraryId
1874
- });
1949
+ if(!libraryId) {
1950
+ libraryId = await this.ContentObjectLibraryId({objectId});
1951
+ }
1952
+
1953
+ if(!writeToken) {
1954
+ ({writeToken} = await this.EditContentObject({
1955
+ objectId,
1956
+ libraryId
1957
+ }));
1958
+ }
1875
1959
 
1876
1960
  const edgeWriteToken = await this.ContentObjectMetadata({
1877
1961
  objectId,
@@ -1879,34 +1963,46 @@ exports.StreamAddWatermark = async function({
1879
1963
  metadataSubtree: "/live_recording/fabric_config/edge_write_token"
1880
1964
  });
1881
1965
 
1882
- this.Log(`Adding watermarking type: ${imageWatermark ? "image" : "text"} ${libraryId} ${objectId}`);
1966
+ const watermarkType = imageWatermark ? "image" : forensicWatermark ? "forensic" : "text";
1967
+ const metadataPath = "live_recording/playout_config";
1883
1968
 
1884
- const recordingParamsPath = "live_recording/recording_config/recording_params";
1969
+ this.Log(`Adding watermarking type: ${watermarkType} ${libraryId} ${objectId}`);
1885
1970
 
1886
- const recordingMetadata = await this.ContentObjectMetadata({
1971
+ const objectMetadata = await this.ContentObjectMetadata({
1887
1972
  libraryId,
1888
1973
  objectId,
1889
1974
  writeToken,
1890
- metadataSubtree: recordingParamsPath,
1975
+ metadataSubtree: metadataPath,
1891
1976
  resolveLinks: false
1892
1977
  });
1893
1978
 
1894
- if(!recordingMetadata) {
1895
- throw Error("Stream object must be configured");
1979
+ if(!objectMetadata) {
1980
+ throw Error("Stream object must be configured before adding a watermark");
1981
+ }
1982
+
1983
+ const watermarkArgCount = [simpleWatermark, imageWatermark, forensicWatermark].filter(i => !!i).length;
1984
+ console.log("watermark arg count", watermarkArgCount)
1985
+
1986
+ if(watermarkArgCount === 0) {
1987
+ throw Error("No watermark was provided");
1988
+ } else if(watermarkArgCount > 1) {
1989
+ throw Error("Only one watermark is allowed")
1896
1990
  }
1897
1991
 
1898
1992
  if(simpleWatermark) {
1899
- recordingMetadata.simple_watermark = simpleWatermark;
1993
+ objectMetadata.simple_watermark = simpleWatermark;
1900
1994
  } else if(imageWatermark) {
1901
- recordingMetadata.image_watermark = imageWatermark;
1995
+ objectMetadata.image_watermark = imageWatermark;
1996
+ } else if(forensicWatermark) {
1997
+ objectMetadata.forensic_watermark = forensicWatermark;
1902
1998
  }
1903
1999
 
1904
2000
  await this.ReplaceMetadata({
1905
2001
  libraryId,
1906
2002
  objectId,
1907
2003
  writeToken,
1908
- metadataSubtree: recordingParamsPath,
1909
- metadata: recordingMetadata
2004
+ metadataSubtree: metadataPath,
2005
+ metadata: objectMetadata
1910
2006
  });
1911
2007
 
1912
2008
  if(edgeWriteToken) {
@@ -1914,14 +2010,15 @@ exports.StreamAddWatermark = async function({
1914
2010
  libraryId,
1915
2011
  objectId,
1916
2012
  writeToken: edgeWriteToken,
1917
- metadataSubtree: recordingParamsPath,
1918
- metadata: recordingMetadata
2013
+ metadataSubtree: metadataPath,
2014
+ metadata: objectMetadata
1919
2015
  });
1920
2016
  }
1921
2017
 
1922
2018
  const response = {
1923
- "imageWatermark": recordingMetadata.image_watermark,
1924
- "textWatermark": recordingMetadata.simple_watermark
2019
+ "imageWatermark": objectMetadata.image_watermark,
2020
+ "textWatermark": objectMetadata.simple_watermark,
2021
+ "forensicWatermark": objectMetadata.forensic_watermark
1925
2022
  };
1926
2023
 
1927
2024
  if(finalize) {
@@ -7,15 +7,28 @@ const argv = yargs
7
7
  description: "Fabric configuration URL (e.g. https://main.net955210.contentfabric.io/config)"
8
8
  })
9
9
  .option("tenantContractId", {
10
- description: "tenant Contract Id to set for the user"
10
+ description: "tenant Contract Id to set for the objects"
11
+ })
12
+ .option("tenantIdToBeReplaced", {
13
+ description: "tenant Id to be changed for the objects"
11
14
  })
12
15
  .demandOption(
13
- ["configUrl", "tenantContractId"],
14
- "\nUsage: PRIVATE_KEY=<private-key> node CreateUserWithTenantId --configUrl <config-url> --tenantContractId <tenant_id>\n"
16
+ ["configUrl", "tenantContractId", "tenantIdToBeReplaced" ],
17
+ "\nUsage: PRIVATE_KEY=<private-key> node TestAddTenantContractId --configUrl <config-url> --tenantContractId <tenant_contract_id> --tenantIdToBeReplaced <tenant_id>\n"
15
18
  )
16
19
  .argv;
17
20
 
18
- const TestAddTenantContractId = async ({configUrl, tenantContractId}) => {
21
+ /**
22
+ * TestAddTenantContractId adds tenantContractId to wallet,library,group,content-type object
23
+ * and change tenantId to value provided.
24
+ *
25
+ * @param {string} configUrl - Config Url
26
+ * @param {string} tenantContractId - Id of tenantContract to be set
27
+ * @param {string} tenantIdToBeReplaced - Id of tenant admin group to be replaced
28
+ * @returns {Promise<void>}
29
+ * @constructor
30
+ */
31
+ const TestAddTenantContractId = async ({configUrl, tenantContractId, tenantIdToBeReplaced}) => {
19
32
  try {
20
33
  if(!process.env.PRIVATE_KEY) {
21
34
  console.log("ERROR: 'PRIVATE_KEY' environment variable must be set");
@@ -79,6 +92,34 @@ const TestAddTenantContractId = async ({configUrl, tenantContractId}) => {
79
92
  // libraryId: client.contentSpaceLibraryId,
80
93
  // objectId: client.utils.AddressToObjectId(groupAddress)}));
81
94
 
95
+
96
+ // Change tenantID (which in turn changes tenantContractId) to user, group, library and content-type
97
+ await client.userProfileClient.SetTenantId({id: tenantIdToBeReplaced});
98
+ const newTenantId = await client.userProfileClient.TenantId();
99
+ if(newTenantId !== tenantIdToBeReplaced) {
100
+ throw Error(`tenant mismatch, actual: ${tenantInfo.tenantId}, expected: ${tenantIdToBeReplaced}`);
101
+ }
102
+ console.log();
103
+ console.log(`tenantId for user changed to ${tenantIdToBeReplaced}`);
104
+
105
+ tenantInfo = await client.SetTenantId({objectId: libraryId, tenantId: tenantIdToBeReplaced});
106
+ if(tenantInfo.tenantId !== tenantIdToBeReplaced) {
107
+ throw Error(`tenant mismatch, actual: ${tenantInfo.tenantId}, expected: ${tenantIdToBeReplaced}`);
108
+ }
109
+ console.log(`tenantId for library changed to ${tenantIdToBeReplaced}`);
110
+
111
+ tenantInfo = await client.SetTenantId({contractAddress: contentTypeAddress, tenantId: tenantIdToBeReplaced});
112
+ if(tenantInfo.tenantId !== tenantIdToBeReplaced) {
113
+ throw Error(`tenant mismatch, actual: ${tenantInfo.tenantId}, expected: ${tenantIdToBeReplaced}`);
114
+ }
115
+ console.log(`tenantId for content-type object changed to ${tenantIdToBeReplaced}`)
116
+
117
+ tenantInfo = await client.SetTenantId({contractAddress: groupAddress, tenantId: tenantIdToBeReplaced});
118
+ if(tenantInfo.tenantId !== tenantIdToBeReplaced) {
119
+ throw Error(`tenant mismatch, actual: ${tenantInfo.tenantId}, expected: ${tenantIdToBeReplaced}`);
120
+ }
121
+ console.log(`tenantId for group-address changed to ${tenantIdToBeReplaced}`)
122
+
82
123
  } catch(e){
83
124
  console.error(e);
84
125
  }