@eluvio/elv-client-js 4.0.10 → 4.0.12
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/dist/.keep +0 -0
- package/dist/ElvClient-min.js +10 -10
- package/dist/ElvClient-node-min.js +3 -3
- package/dist/ElvFrameClient-min.js +10 -10
- package/dist/ElvWalletClient-min.js +9 -9
- package/dist/ElvWalletClient-node-min.js +3 -3
- package/dist/src/AuthorizationClient.js +7 -8
- package/dist/src/client/Files.js +2 -1
- package/package.json +4 -21
- package/src/AuthorizationClient.js +5 -5
- package/src/client/Files.js +1 -1
- package/dist/AccessClient-min.js +0 -54
- package/dist/AccessClient-node-min.js +0 -74
- package/dist/src/AccessClient.js +0 -798
- package/dist/src/LimitedMap.js +0 -120
- package/dist/src/client/Access.js +0 -1
- package/dist/src/client/Management.js +0 -1606
- package/dist/src/contracts/AccessIndexor.js +0 -831
- package/dist/src/contracts/Accessible.js +0 -31
- package/dist/src/contracts/AdmgrAdvertisement.js +0 -695
- package/dist/src/contracts/AdmgrCampaign.js +0 -648
- package/dist/src/contracts/AdmgrCampaignManager.js +0 -493
- package/dist/src/contracts/AdmgrCommercialOffering.js +0 -622
- package/dist/src/contracts/AdmgrCommercialOfferingManager.js +0 -413
- package/dist/src/contracts/AdmgrMarketPlace.js +0 -413
- package/dist/src/contracts/AvailsDelivery.js +0 -535
- package/dist/src/contracts/BaseAccessControlGroup.js +0 -1221
- package/dist/src/contracts/BaseAccessWallet.js +0 -1575
- package/dist/src/contracts/BaseAccessWalletFactory.js +0 -93
- package/dist/src/contracts/BaseContent.js +0 -1104
- package/dist/src/contracts/BaseContentFactory.js +0 -99
- package/dist/src/contracts/BaseContentFactoryExt.js +0 -219
- package/dist/src/contracts/BaseContentSpace.js +0 -1346
- package/dist/src/contracts/BaseContentType.js +0 -353
- package/dist/src/contracts/BaseFactory.js +0 -107
- package/dist/src/contracts/BaseGroupFactory.js +0 -93
- package/dist/src/contracts/BaseLibrary.js +0 -1035
- package/dist/src/contracts/BaseLibraryFactory.js +0 -96
- package/dist/src/contracts/Certifyer.js +0 -87
- package/dist/src/contracts/Container.js +0 -543
- package/dist/src/contracts/Content.js +0 -432
- package/dist/src/contracts/Editable.js +0 -295
- package/dist/src/contracts/FactorySpace.js +0 -57
- package/dist/src/contracts/KmsSpace.js +0 -52
- package/dist/src/contracts/LvProvider.js +0 -512
- package/dist/src/contracts/LvRecordableStream.js +0 -1026
- package/dist/src/contracts/LvRecording.js +0 -627
- package/dist/src/contracts/LvStreamRightsHolder.js +0 -551
- package/dist/src/contracts/MetaObject.js +0 -110
- package/dist/src/contracts/Node.js +0 -167
- package/dist/src/contracts/NodeSpace.js +0 -18
- package/dist/src/contracts/NodeSpaceImpl.js +0 -273
- package/dist/src/contracts/Ownable.js +0 -87
- package/dist/src/contracts/PaymentService.js +0 -616
- package/dist/src/contracts/Precompile.js +0 -15
- package/dist/src/contracts/SampleContentAdMarketplace.js +0 -564
- package/dist/src/contracts/SampleContentAdvertising.js +0 -444
- package/dist/src/contracts/SampleContentHelloWorld.js +0 -459
- package/dist/src/contracts/SampleContentLicensing.js +0 -618
- package/dist/src/contracts/SampleContentSigned.js +0 -447
- package/dist/src/contracts/SampleInstance.js +0 -438
- package/dist/src/contracts/SampleInstanceFactory.js +0 -451
- package/dist/src/contracts/Transactable.js +0 -82
- package/dist/src/contracts/UserSpace.js +0 -18
- package/dist/src/contracts/UserSpaceImpl.js +0 -43
- package/dist/src/contracts/Utils.js +0 -18
- package/dist/src/contracts/Verifier.js +0 -53
- package/dist/src/contracts/strings.js +0 -4
- package/dist/src/contracts/v2/BaseContentFactoryExt.js +0 -219
- package/dist/src/contracts/v2/FactorySpace.js +0 -57
- package/dist/src/contracts/v2/KmsSpace.js +0 -52
- package/dist/src/contracts/v2/NodeSpaceImpl.js +0 -273
- package/dist/src/contracts/v2/UserSpaceImpl.js +0 -43
- package/dist/src/marketplaceClient/ClientMethods.js +0 -1918
- package/dist/src/marketplaceClient/Configuration.js +0 -29
- package/dist/src/marketplaceClient/Utils.js +0 -304
- package/dist/src/marketplaceClient/index.js +0 -1553
- package/testScripts/CreateMezMonolithic.js +0 -779
|
@@ -1,779 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
|
|
3
|
-
const R = require("ramda");
|
|
4
|
-
const curry = require("crocks/helpers/curry");
|
|
5
|
-
const pipe = require("crocks/helpers/pipe");
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
ObjectModel,
|
|
9
|
-
NonNegativeInteger,
|
|
10
|
-
PositiveInteger
|
|
11
|
-
} = require("../utilities/lib/models/Models");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const MP4AtomReadContextModel = ObjectModel(
|
|
15
|
-
{
|
|
16
|
-
buffer: Buffer,
|
|
17
|
-
currentAtomLength: PositiveInteger,
|
|
18
|
-
currentAtomStart: NonNegativeInteger,
|
|
19
|
-
posWithinAtom: NonNegativeInteger
|
|
20
|
-
})
|
|
21
|
-
.assert(x => x.currentAtomLength >= 8, ()=>"currentAtomLength must be >= 8")
|
|
22
|
-
.assert(x => x.posWithinAtom < x.currentAtomLength, ()=>"posWithinAtom must be < currentAtomLength")
|
|
23
|
-
.assert(x => (x.currentAtomStart + x.currentAtomLength) <= x.buffer.length,()=> "currentAtomStart + currentAtomLength must be <= buffer.length");
|
|
24
|
-
|
|
25
|
-
class MP4AtomReadContext extends MP4AtomReadContextModel
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
get currentAtomBytesLeft() {
|
|
29
|
-
return this.currentAtomLength - this.posWithinAtom;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
get currentAtomEnd() {
|
|
33
|
-
return this.currentAtomStart + this.currentAtomLength;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get currentBufPos() {
|
|
37
|
-
return this.currentAtomStart + this.posWithinAtom;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// enter atom under read head, then move read head past header (length and type fields)
|
|
42
|
-
const enter = readContext => {
|
|
43
|
-
let {atomType, atomLength, headerLength} = readHeader(readContext);
|
|
44
|
-
// logger.log(`Entering atom '${atomType}', atom start = ${readContext.currentBufPos}, skipping ${headerLength} byte header`);
|
|
45
|
-
return new MP4AtomReadContext(
|
|
46
|
-
{
|
|
47
|
-
buffer: readContext.buffer,
|
|
48
|
-
currentAtomLength: atomLength,
|
|
49
|
-
currentAtomStart: readContext.currentBufPos,
|
|
50
|
-
currentAtomType: atomType,
|
|
51
|
-
posWithinAtom: headerLength
|
|
52
|
-
}
|
|
53
|
-
);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Read atoms without entering, looking for particular atom atomType(s)
|
|
57
|
-
// The read head must be at the start of any atom (reading the next 4 bytes must return the length field)
|
|
58
|
-
const find = curry(
|
|
59
|
-
(atomTypes, readContext) => {
|
|
60
|
-
let eof = false;
|
|
61
|
-
let found = false;
|
|
62
|
-
while(!found && !eof) {
|
|
63
|
-
let {atomType, atomLength} = readHeader(readContext);
|
|
64
|
-
if(atomTypes.includes(atomType)) {
|
|
65
|
-
found = true;
|
|
66
|
-
} else {
|
|
67
|
-
// skip to next atom
|
|
68
|
-
readContext = moveWithin(atomLength, readContext);
|
|
69
|
-
// check if we have run out of data
|
|
70
|
-
if(readContext.posWithinAtom === readContext.currentAtomLength) eof = true;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if(!found) throw Error(`Atom type(s) not found: ${atomTypes}`);
|
|
74
|
-
|
|
75
|
-
return readContext;
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const findAndEnter = curry(
|
|
80
|
-
(atomTypes, readContext) => {
|
|
81
|
-
return enter(find(atomTypes, readContext));
|
|
82
|
-
}
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const moveWithin = curry(
|
|
86
|
-
(offset, readContext) => {
|
|
87
|
-
// logger.log(`Move within atom: ${offset} bytes`);
|
|
88
|
-
|
|
89
|
-
const newPosWithinParent = readContext.posWithinAtom + offset;
|
|
90
|
-
if(newPosWithinParent >= readContext.currentAtomLength) throw Error("Cannot move past end of atom");
|
|
91
|
-
if(newPosWithinParent < 0) throw Error("Cannot move before beginning of atom");
|
|
92
|
-
return new MP4AtomReadContext({
|
|
93
|
-
buffer: readContext.buffer,
|
|
94
|
-
currentAtomLength: readContext.currentAtomLength,
|
|
95
|
-
currentAtomStart: readContext.currentAtomStart,
|
|
96
|
-
currentAtomType: readContext.currentAtomType,
|
|
97
|
-
posWithinAtom: newPosWithinParent
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const newContextFromBuffer = buffer => {
|
|
104
|
-
return new MP4AtomReadContext({
|
|
105
|
-
buffer,
|
|
106
|
-
currentAtomLength: buffer.length,
|
|
107
|
-
currentAtomStart: 0,
|
|
108
|
-
currentAtomType: "",
|
|
109
|
-
posWithinAtom: 0
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const readAtom = readContext => {
|
|
114
|
-
const {atomLength} = readHeader(readContext);
|
|
115
|
-
const atomStart = readContext.currentBufPos;
|
|
116
|
-
const atomEnd = atomStart + atomLength;
|
|
117
|
-
return readContext.buffer.subarray(atomStart, atomEnd);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const readHeader = readContext => {
|
|
121
|
-
const {buffer, currentAtomBytesLeft, currentBufPos} = readContext;
|
|
122
|
-
let headerLength = 8;
|
|
123
|
-
|
|
124
|
-
if(currentAtomBytesLeft < headerLength) throw Error("Cannot read atom, not enough bytes available to hold atomLength + type fields");
|
|
125
|
-
|
|
126
|
-
// read 4 bytes to get atomLength
|
|
127
|
-
let atomLength = buffer.readUInt32BE(currentBufPos);
|
|
128
|
-
if(atomLength === 0 || (atomLength > 1 && atomLength < 8)) throw Error(`Invalid length 0 found: ${atomLength}`);
|
|
129
|
-
|
|
130
|
-
// read 4 bytes to get atom type
|
|
131
|
-
const atomType = buffer.toString("ascii", currentBufPos + 4, currentBufPos + 8);
|
|
132
|
-
|
|
133
|
-
// if atomLength === 1, need to read 8 more bytes to get 64-bit atomLength
|
|
134
|
-
if(atomLength === 1) {
|
|
135
|
-
headerLength = 16;
|
|
136
|
-
if(currentAtomBytesLeft < headerLength) throw Error("Cannot read atom, not enough bytes available to hold 64-bit atomLength field");
|
|
137
|
-
atomLength = buffer.readUInt64BE(currentBufPos + 8);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if(atomLength > currentAtomBytesLeft) {
|
|
141
|
-
throw Error(`Invalid length found: ${atomLength} (only ${currentAtomBytesLeft} available)`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return {atomLength, atomType, headerLength};
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
module.exports = {
|
|
148
|
-
enter,
|
|
149
|
-
find,
|
|
150
|
-
findAndEnter,
|
|
151
|
-
moveWithin,
|
|
152
|
-
MP4AtomReadContext,
|
|
153
|
-
newContextFromBuffer,
|
|
154
|
-
readAtom,
|
|
155
|
-
readHeader
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// takes buffer containing MP4 init segment (or entire MP4 file) for a single x264/x265 video stream
|
|
161
|
-
// and returns codec descriptor string (e.g. "avc1.640028", "hev1.2.4.L150.90" etc.)
|
|
162
|
-
const codecDesc = buffer => {
|
|
163
|
-
let resultContext = decoderConfigAtom(buffer);
|
|
164
|
-
let {atomLength, atomType, headerLength} = readHeader(resultContext);
|
|
165
|
-
|
|
166
|
-
// make new buffer that omits header (length and type fields)
|
|
167
|
-
const decoderBodyBuf = resultContext.buffer.subarray(headerLength, atomLength);
|
|
168
|
-
switch(atomType) {
|
|
169
|
-
case "avcC":
|
|
170
|
-
return x264codecString(decoderBodyBuf);
|
|
171
|
-
case "hvcC":
|
|
172
|
-
return x265codecString(decoderBodyBuf);
|
|
173
|
-
default:
|
|
174
|
-
throw Error(`Unknown decoder configuration atom type: '${atomType}'`);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
// buffer containing init segment -> MP4AtomReadContext with buffer containing only avcC or hvcC atom
|
|
179
|
-
const decoderConfigAtom = pipe(
|
|
180
|
-
newContextFromBuffer,
|
|
181
|
-
findAndEnter(["moov"]),
|
|
182
|
-
findAndEnter(["trak"]),
|
|
183
|
-
findAndEnter(["mdia"]),
|
|
184
|
-
findAndEnter(["minf"]),
|
|
185
|
-
findAndEnter(["stbl"]),
|
|
186
|
-
findAndEnter(["stsd"]),
|
|
187
|
-
moveWithin(8),
|
|
188
|
-
findAndEnter(["avc1", "encv", "hev1"]),
|
|
189
|
-
moveWithin(78),
|
|
190
|
-
find(["avcC", "hvcC"]),
|
|
191
|
-
readAtom,
|
|
192
|
-
newContextFromBuffer
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// takes buffer containing decoder config atom (without the type/length fields at front) and returns x264 codec string
|
|
196
|
-
const x264codecString = atomBodyBuf => {
|
|
197
|
-
// See https://dvb.org/wp-content/uploads/2019/10/a168_DVB_MPEG-DASH_Nov_2017.pdf
|
|
198
|
-
|
|
199
|
-
if(atomBodyBuf.length < 4) throw Error("Not enough bytes in AVCDecoderConfiguration");
|
|
200
|
-
|
|
201
|
-
const avcProfileIndication = atomBodyBuf.readUInt8(1).toString(16).padStart(2, "0");
|
|
202
|
-
const profileCompatibility = atomBodyBuf.readUInt8(2).toString(16).padStart(2, "0");
|
|
203
|
-
const avcLevelIndication = atomBodyBuf.readUInt8(3).toString(16).padStart(2, "0");
|
|
204
|
-
return `avc1.${avcProfileIndication}${profileCompatibility}${avcLevelIndication}`;
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
// takes buffer containing decoder config atom (without the type/length fields at front) and returns x265 codec string
|
|
208
|
-
const x265codecString = atomBodyBuf => {
|
|
209
|
-
// See https://dvb.org/wp-content/uploads/2019/10/a168_DVB_MPEG-DASH_Nov_2017.pdf
|
|
210
|
-
|
|
211
|
-
if(atomBodyBuf.length < 13) throw Error("Not enough bytes in HVCDecoderConfiguration");
|
|
212
|
-
|
|
213
|
-
// parse bytes/bits into values
|
|
214
|
-
const bitFlags = atomBodyBuf.readUInt8(1);
|
|
215
|
-
const genProfileSpaceBits = (bitFlags & 0b11000000) >>> 6;
|
|
216
|
-
const genTierBit = (bitFlags & 0b00100000) >>> 5;
|
|
217
|
-
const genProfileIDC = bitFlags & 0b00011111;
|
|
218
|
-
const genProfileCompatibility = parseInt(R.reverse(atomBodyBuf.readUInt32BE(2).toString(2).padStart(32, "0")), 2).toString(16);
|
|
219
|
-
let genConstraintFlags = [
|
|
220
|
-
atomBodyBuf.readUInt8(6),
|
|
221
|
-
atomBodyBuf.readUInt8(7),
|
|
222
|
-
atomBodyBuf.readUInt8(8),
|
|
223
|
-
atomBodyBuf.readUInt8(9),
|
|
224
|
-
atomBodyBuf.readUInt8(10),
|
|
225
|
-
atomBodyBuf.readUInt8(11)
|
|
226
|
-
];
|
|
227
|
-
const genLevelIDC = atomBodyBuf.readUInt8(12);
|
|
228
|
-
|
|
229
|
-
// Format values into appropriate strings
|
|
230
|
-
|
|
231
|
-
// CODECSTRING = CODEC "." PROFILE "." LEVEL "." CONSTRAINTS
|
|
232
|
-
|
|
233
|
-
// CODEC = ("hev1" | "hvc1" )
|
|
234
|
-
let pieces = ["hev1"];
|
|
235
|
-
|
|
236
|
-
// PROFILE = PROFILE_SPACE PROFILE_IDC "." PROFILE_COMPATIBILITY
|
|
237
|
-
|
|
238
|
-
// PROFILE_SPACE = "" | "A" | "B" | "C" (for general_profile_space values 0,1,2,3)
|
|
239
|
-
let profileSpace = "";
|
|
240
|
-
switch(genProfileSpaceBits){
|
|
241
|
-
case 1:
|
|
242
|
-
profileSpace = "A";
|
|
243
|
-
break;
|
|
244
|
-
case 2:
|
|
245
|
-
profileSpace = "B";
|
|
246
|
-
break;
|
|
247
|
-
case 3:
|
|
248
|
-
profileSpace = "C";
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// PROFILE_IDC is a decimal number, PROFILE_COMPATIBILITY is a hex string
|
|
253
|
-
const profile = `${profileSpace}${genProfileIDC}.${genProfileCompatibility}`;
|
|
254
|
-
pieces.push(profile);
|
|
255
|
-
|
|
256
|
-
// LEVEL = TIER LEVEL_IDC
|
|
257
|
-
|
|
258
|
-
// TIER = "L" / "H" (general_tier_flag, 0=="L", 1=="H")
|
|
259
|
-
const tier = genTierBit ? "H" : "L";
|
|
260
|
-
const level = `${tier}${genLevelIDC}`;
|
|
261
|
-
pieces.push(level);
|
|
262
|
-
|
|
263
|
-
// CONSTRAINTS = 2(HEXDIG) [ "." CONSTRAINTS ] (hexadecimal representation of the general_constraint_indicator_flags. Each byte is separated by a '.', and trailing zero bytes may be omitted.)
|
|
264
|
-
|
|
265
|
-
while(genConstraintFlags.length > 0 && genConstraintFlags.slice(-1)[0] === 0) {
|
|
266
|
-
genConstraintFlags.pop();
|
|
267
|
-
}
|
|
268
|
-
const constraints = genConstraintFlags.map(x => x.toString(16)).join(".");
|
|
269
|
-
pieces.push(constraints);
|
|
270
|
-
|
|
271
|
-
return pieces.join(".");
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const setCodecDescs = async ({elvClient, libraryId, objectId, offeringKey, offeringMetadata, writeToken}) => {
|
|
277
|
-
|
|
278
|
-
// get first available playout format (for constructing init segment urls)
|
|
279
|
-
const formats = offeringMetadata.playout.playout_formats;
|
|
280
|
-
const firstFormatKey = Object.keys(formats)[0];
|
|
281
|
-
const player_profile = formats[firstFormatKey].drm && formats[firstFormatKey].drm.type === "DrmAes128" ?
|
|
282
|
-
"hls-js" :
|
|
283
|
-
"";
|
|
284
|
-
|
|
285
|
-
// find video streams, request init segments, set codec_desc
|
|
286
|
-
for(const [streamKey, stream] of Object.entries(offeringMetadata.media_struct.streams)) {
|
|
287
|
-
if(stream.codec_type === "video") {
|
|
288
|
-
console.log(`constructing codec strings for stream '${streamKey}'...`);
|
|
289
|
-
// get bitrate ladder
|
|
290
|
-
const reps = offeringMetadata.playout.streams[streamKey].representations;
|
|
291
|
-
for(const [repKey, rep] of R.sortBy(x => -x[1].bit_rate, Object.entries(reps))) {
|
|
292
|
-
const initSegUrl = `playout/${offeringKey}/${firstFormatKey}/${streamKey}/${repKey}/init.m4s`;
|
|
293
|
-
console.log("\n>> calling ElvClient.FabricUrl() with noAuth: false\n");
|
|
294
|
-
const url = new URL(
|
|
295
|
-
await elvClient.FabricUrl({
|
|
296
|
-
libraryId,
|
|
297
|
-
objectId,
|
|
298
|
-
rep: initSegUrl,
|
|
299
|
-
writeToken,
|
|
300
|
-
noAuth: false,
|
|
301
|
-
})
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
const path = url.pathname;
|
|
305
|
-
let queryParams = {
|
|
306
|
-
authorization: url.searchParams.get("authorization")
|
|
307
|
-
};
|
|
308
|
-
if(player_profile) queryParams.player_profile = player_profile;
|
|
309
|
-
|
|
310
|
-
console.log(`\n\nretrieving init segment @ ${url.href}\n`);
|
|
311
|
-
console.log(`queryParams: ${JSON.stringify(queryParams)}\n\n`);
|
|
312
|
-
|
|
313
|
-
// get init segment
|
|
314
|
-
const response = await elvClient.HttpClient.Request({
|
|
315
|
-
path,
|
|
316
|
-
queryParams,
|
|
317
|
-
method: "GET"
|
|
318
|
-
});
|
|
319
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
320
|
-
|
|
321
|
-
console.log(`constructing codec desc... ${repKey}`);
|
|
322
|
-
|
|
323
|
-
// make codec desc based on init seg
|
|
324
|
-
rep.codec_desc = codecDesc(buffer);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return {};
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const Utils = require("../src/Utils");
|
|
335
|
-
|
|
336
|
-
const preFinalizeFn = async ({elvClient, nodeUrl, writeToken}) => {
|
|
337
|
-
|
|
338
|
-
console.log(`\n\npreFinalizeFn() nodeUrl: ${JSON.stringify(nodeUrl,null,2)} writeToken: ${JSON.stringify(writeToken,null,2)}`);
|
|
339
|
-
|
|
340
|
-
const objectId = Utils.DecodeWriteToken(writeToken).objectId;
|
|
341
|
-
const libraryId = await elvClient.ContentObjectLibraryId({objectId});
|
|
342
|
-
|
|
343
|
-
// make sure client knows proper node to contact
|
|
344
|
-
elvClient.HttpClient.RecordWriteToken(writeToken, nodeUrl);
|
|
345
|
-
|
|
346
|
-
// get metadata from draft
|
|
347
|
-
const abrMezOfferingsMetadata = await elvClient.ContentObjectMetadata({
|
|
348
|
-
libraryId,
|
|
349
|
-
metadataSubtree: "/abr_mezzanine/offerings",
|
|
350
|
-
objectId,
|
|
351
|
-
writeToken
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
if(!abrMezOfferingsMetadata) throw Error("codecDescPrefinalizeFn: null metadata /abr_mezzanine/offerings from draft");
|
|
355
|
-
|
|
356
|
-
// get offering keys (there will be more than one if addlOfferingSpecs was used)
|
|
357
|
-
const offeringKeys = Object.keys(abrMezOfferingsMetadata);
|
|
358
|
-
if(offeringKeys.length === 0) throw Error("codecDescPrefinalizeFn: no offering keys found in draft's /abr_mezzanine/offerings");
|
|
359
|
-
|
|
360
|
-
// construct new metadata with codec descriptions included
|
|
361
|
-
const newAbrMezOffMetadata = {};
|
|
362
|
-
for(const offeringKey of offeringKeys) {
|
|
363
|
-
newAbrMezOffMetadata[offeringKey] = await setCodecDescs({
|
|
364
|
-
elvClient,
|
|
365
|
-
libraryId,
|
|
366
|
-
objectId,
|
|
367
|
-
offeringKey,
|
|
368
|
-
offeringMetadata: abrMezOfferingsMetadata[offeringKey],
|
|
369
|
-
writeToken
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
console.log("WRITING BACK TO METADATA...");
|
|
374
|
-
|
|
375
|
-
// write metadata back to draft
|
|
376
|
-
await elvClient.ReplaceMetadata({
|
|
377
|
-
libraryId,
|
|
378
|
-
metadata: newAbrMezOffMetadata,
|
|
379
|
-
metadataSubtree: "/abr_mezzanine/offerings",
|
|
380
|
-
objectId,
|
|
381
|
-
writeToken
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
/* eslint-disable no-console */
|
|
388
|
-
const ScriptBase = require("./parentClasses/ScriptBase");
|
|
389
|
-
ScriptBase.deprecationNotice("MezzanineCreate.js");
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const {ElvClient} = require("../src/ElvClient");
|
|
393
|
-
const readline = require("readline");
|
|
394
|
-
const fs = require("fs");
|
|
395
|
-
|
|
396
|
-
const yargs = require("yargs");
|
|
397
|
-
const argv = yargs
|
|
398
|
-
.option("library", {
|
|
399
|
-
description: "ID of the library in which to create the mezzanine"
|
|
400
|
-
})
|
|
401
|
-
.option("masterHash", {
|
|
402
|
-
description: "Version hash of the master object"
|
|
403
|
-
})
|
|
404
|
-
.option("type", {
|
|
405
|
-
description: "Name, object ID, or version hash of the type for the mezzanine"
|
|
406
|
-
})
|
|
407
|
-
.option("name", {
|
|
408
|
-
description: "Name for the mezzanine object (derived from ip-title-id and title if not provided)"
|
|
409
|
-
})
|
|
410
|
-
.option("title", {
|
|
411
|
-
description: "Title for the mezzanine"
|
|
412
|
-
})
|
|
413
|
-
.option("display-title", {
|
|
414
|
-
description: "Display title for the mezzanine (set to title if not specified)"
|
|
415
|
-
})
|
|
416
|
-
.option("slug", {
|
|
417
|
-
description: "Slug for the mezzanine (generated based on title if not specified)"
|
|
418
|
-
})
|
|
419
|
-
.option("ip-title-id", {
|
|
420
|
-
description: "IP title ID for the mezzanine (equivalent to slug if not specified)",
|
|
421
|
-
type: "string"
|
|
422
|
-
})
|
|
423
|
-
.option("title-type", {
|
|
424
|
-
description: "Title type for the mezzanine",
|
|
425
|
-
default: "title"
|
|
426
|
-
})
|
|
427
|
-
.option("asset-type", {
|
|
428
|
-
description: "Asset type for the mezzanine",
|
|
429
|
-
default: "primary"
|
|
430
|
-
})
|
|
431
|
-
.option("addlOfferingSpecs", {
|
|
432
|
-
description: "Additional offerings to create via patching - JSON string (or file path if prefixed with '@')"
|
|
433
|
-
})
|
|
434
|
-
.option("metadata", {
|
|
435
|
-
description: "Metadata JSON string (or file path if prefixed with '@') to include in the object metadata"
|
|
436
|
-
})
|
|
437
|
-
.option("variant", {
|
|
438
|
-
description: "Variant of the mezzanine",
|
|
439
|
-
default: "default"
|
|
440
|
-
})
|
|
441
|
-
.option("offering-key", {
|
|
442
|
-
description: "Offering key for the new mezzanine",
|
|
443
|
-
default: "default"
|
|
444
|
-
})
|
|
445
|
-
.option("existingMezzId", {
|
|
446
|
-
description: "If using an existing object to store mezzanine offering, the ID of that object",
|
|
447
|
-
type: "string"
|
|
448
|
-
})
|
|
449
|
-
.option("keep-other-streams", {
|
|
450
|
-
description: "If using an existing object to store mezzanine offering, and offering already exists,whether to preserve other streams",
|
|
451
|
-
default: false,
|
|
452
|
-
type: "boolean",
|
|
453
|
-
})
|
|
454
|
-
.option("stream-keys", {
|
|
455
|
-
description: "If supplied, only the specified stream(s) will be processed",
|
|
456
|
-
string: true,
|
|
457
|
-
type: "array",
|
|
458
|
-
})
|
|
459
|
-
.option("abr-profile", {
|
|
460
|
-
description: "Path to JSON file containing alternative ABR profile"
|
|
461
|
-
})
|
|
462
|
-
.option("elv-geo", {
|
|
463
|
-
type: "string",
|
|
464
|
-
description: "Geographic region for the fabric nodes. Available regions: na-west-north, na-west-south, na-east, eu-west, eu-east, as-east, au-east"
|
|
465
|
-
})
|
|
466
|
-
.option("config-url", {
|
|
467
|
-
type: "string",
|
|
468
|
-
description: "URL pointing to the Fabric configuration. i.e. https://main.net955210.contentfabric.io/config"
|
|
469
|
-
})
|
|
470
|
-
.option("credentials", {
|
|
471
|
-
type: "string",
|
|
472
|
-
description: "Path to JSON file containing credential sets for files stored in cloud"
|
|
473
|
-
})
|
|
474
|
-
.option("debug", {
|
|
475
|
-
type: "boolean",
|
|
476
|
-
description: "Enable client logging"
|
|
477
|
-
})
|
|
478
|
-
.option("wait", {
|
|
479
|
-
type: "boolean",
|
|
480
|
-
description: "Wait for mezzanine to finish transcoding, then finalize before exiting script"
|
|
481
|
-
})
|
|
482
|
-
.demandOption(
|
|
483
|
-
["library", "masterHash", "type"],
|
|
484
|
-
"\nUsage: PRIVATE_KEY=<private-key> node CreateABRMezzanine.js --library <mezzanine-library-id> --type <type> </type>--masterHash <production-master-hash> --title <title> (--variant <variant>) (--metadata '<metadata-json>') (--addlOfferingSpecs '<specs-json>') (--existingMezzId <object-id>) (--elv-geo eu-west)\n"
|
|
485
|
-
)
|
|
486
|
-
.strict().argv;
|
|
487
|
-
|
|
488
|
-
const ClientConfiguration = (!argv["config-url"]) ? (require("../TestConfiguration.json")) : {"config-url": argv["config-url"]};
|
|
489
|
-
|
|
490
|
-
const Slugify = str =>
|
|
491
|
-
(str || "").toLowerCase().replace(/ /g, "-").replace(/[^a-z0-9-]/g, "");
|
|
492
|
-
|
|
493
|
-
const Report = response => {
|
|
494
|
-
if(response.errors.length > 0) {
|
|
495
|
-
console.error("Errors:");
|
|
496
|
-
console.error(response.errors.join("\n"), "\n");
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if(response.warnings.length) {
|
|
500
|
-
console.warn("Warnings:");
|
|
501
|
-
console.warn(response.warnings.join("\n"), "\n");
|
|
502
|
-
}
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
const Create = async (
|
|
506
|
-
{
|
|
507
|
-
library,
|
|
508
|
-
masterHash,
|
|
509
|
-
type,
|
|
510
|
-
variant,
|
|
511
|
-
offeringKey,
|
|
512
|
-
name,
|
|
513
|
-
title,
|
|
514
|
-
displayTitle,
|
|
515
|
-
slug,
|
|
516
|
-
ipTitleId,
|
|
517
|
-
titleType,
|
|
518
|
-
assetType,
|
|
519
|
-
metadata,
|
|
520
|
-
addlOfferingSpecs,
|
|
521
|
-
existingMezzId,
|
|
522
|
-
abrProfile,
|
|
523
|
-
elvGeo,
|
|
524
|
-
credentials,
|
|
525
|
-
debug,
|
|
526
|
-
wait = false,
|
|
527
|
-
keepOtherStreams,
|
|
528
|
-
streamKeys
|
|
529
|
-
}
|
|
530
|
-
) => {
|
|
531
|
-
|
|
532
|
-
if(!existingMezzId && (keepOtherStreams)) {
|
|
533
|
-
throw Error("--existingMezzId required when using --keepOtherStreams");
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if(addlOfferingSpecs && !abrProfile) {
|
|
537
|
-
throw Error("--abrProfile required when using --addlOfferingSpecs");
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// force ipTitleId to be a string, if present
|
|
541
|
-
if(ipTitleId) {
|
|
542
|
-
ipTitleId = ipTitleId.toString();
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
try {
|
|
546
|
-
const privateKey = process.env.PRIVATE_KEY;
|
|
547
|
-
if(!privateKey) {
|
|
548
|
-
console.error("PRIVATE_KEY environment variable must be specified");
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
if(!existingMezzId && !title) {
|
|
553
|
-
throw Error("--title argument is required unless --existingMezzId is specified");
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const client = await ElvClient.FromConfigurationUrl({
|
|
557
|
-
configUrl: ClientConfiguration["config-url"],
|
|
558
|
-
region: elvGeo
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
let wallet = client.GenerateWallet();
|
|
562
|
-
let signer = wallet.AddAccount({
|
|
563
|
-
privateKey: process.env.PRIVATE_KEY
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
await client.SetSigner({signer});
|
|
567
|
-
|
|
568
|
-
if(debug) {
|
|
569
|
-
client.ToggleLogging(true);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// construct metadata
|
|
573
|
-
if(metadata) {
|
|
574
|
-
try {
|
|
575
|
-
if(metadata.startsWith("@")) {
|
|
576
|
-
metadata = fs.readFileSync(metadata.substring(1));
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
metadata = JSON.parse(metadata) || {};
|
|
580
|
-
if(!metadata.public) {
|
|
581
|
-
metadata.public = {};
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
name = name || metadata.public.name || metadata.name;
|
|
585
|
-
} catch(error) {
|
|
586
|
-
console.error("Error parsing metadata:");
|
|
587
|
-
console.error(error);
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
} else if(existingMezzId) {
|
|
591
|
-
const libraryId = await client.ContentObjectLibraryId({objectId: existingMezzId});
|
|
592
|
-
const assetMetadata = (await client.ContentObjectMetadata({
|
|
593
|
-
libraryId,
|
|
594
|
-
objectId: existingMezzId,
|
|
595
|
-
metadataSubtree: "public/asset_metadata"
|
|
596
|
-
})) || {};
|
|
597
|
-
|
|
598
|
-
const existingName = (await client.ContentObjectMetadata({
|
|
599
|
-
libraryId,
|
|
600
|
-
objectId: existingMezzId,
|
|
601
|
-
metadataSubtree: "public/name"
|
|
602
|
-
})) || {};
|
|
603
|
-
|
|
604
|
-
metadata = {
|
|
605
|
-
public: {
|
|
606
|
-
asset_metadata: assetMetadata || {},
|
|
607
|
-
name: name || existingName || ""
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
if(!title && !metadata.public.asset_metadata.title) {
|
|
612
|
-
throw Error("Existing mez does not have 'title' set and title argument was not provided");
|
|
613
|
-
}
|
|
614
|
-
} else {
|
|
615
|
-
metadata = {public: {asset_metadata: {}}};
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
if(abrProfile) {
|
|
619
|
-
abrProfile = JSON.parse(fs.readFileSync(abrProfile));
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
if(addlOfferingSpecs) {
|
|
623
|
-
try {
|
|
624
|
-
if(addlOfferingSpecs.startsWith("@")) {
|
|
625
|
-
addlOfferingSpecs = fs.readFileSync(addlOfferingSpecs.substring(1));
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
addlOfferingSpecs = JSON.parse(addlOfferingSpecs) || undefined;
|
|
629
|
-
} catch(error) {
|
|
630
|
-
console.error("Error parsing addlOfferingSpecs:");
|
|
631
|
-
console.error(error);
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
metadata.public.asset_metadata = {
|
|
637
|
-
title,
|
|
638
|
-
display_title: displayTitle || title,
|
|
639
|
-
slug: slug || Slugify(displayTitle || title),
|
|
640
|
-
ip_title_id: ipTitleId || slug || Slugify(displayTitle || title),
|
|
641
|
-
title_type: titleType,
|
|
642
|
-
asset_type: assetType,
|
|
643
|
-
...(metadata.public.asset_metadata || {})
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
name = name || metadata.public.name || metadata.public.asset_metadata.title + " MEZ";
|
|
647
|
-
|
|
648
|
-
let access;
|
|
649
|
-
if(credentials) {
|
|
650
|
-
access = JSON.parse(fs.readFileSync(credentials));
|
|
651
|
-
} else {
|
|
652
|
-
access = [
|
|
653
|
-
{
|
|
654
|
-
path_matchers: [".*"],
|
|
655
|
-
remote_access: {
|
|
656
|
-
protocol: "s3",
|
|
657
|
-
platform: "aws",
|
|
658
|
-
path: process.env.AWS_BUCKET + "/",
|
|
659
|
-
storage_endpoint: {
|
|
660
|
-
region: process.env.AWS_REGION
|
|
661
|
-
},
|
|
662
|
-
cloud_credentials: {
|
|
663
|
-
access_key_id: process.env.AWS_KEY,
|
|
664
|
-
secret_access_key: process.env.AWS_SECRET
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
];
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const originalType = type;
|
|
672
|
-
if(type.startsWith("iq__")) {
|
|
673
|
-
type = await client.ContentType({typeId: type});
|
|
674
|
-
} else if(type.startsWith("hq__")) {
|
|
675
|
-
type = await client.ContentType({versionHash: type});
|
|
676
|
-
} else {
|
|
677
|
-
type = await client.ContentType({name: type});
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if(!type) {
|
|
681
|
-
throw Error(`Unable to find content type "${originalType}"`);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
type = type.hash;
|
|
685
|
-
|
|
686
|
-
console.log("\nCreating ABR Mezzanine...");
|
|
687
|
-
const createResponse = await client.CreateABRMezzanine({
|
|
688
|
-
name,
|
|
689
|
-
libraryId: library,
|
|
690
|
-
objectId: existingMezzId,
|
|
691
|
-
type,
|
|
692
|
-
masterVersionHash: masterHash,
|
|
693
|
-
variant,
|
|
694
|
-
offeringKey: offeringKey,
|
|
695
|
-
metadata,
|
|
696
|
-
addlOfferingSpecs,
|
|
697
|
-
abrProfile,
|
|
698
|
-
keepOtherStreams,
|
|
699
|
-
streamKeys
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
Report(createResponse);
|
|
703
|
-
|
|
704
|
-
const objectId = createResponse.id;
|
|
705
|
-
|
|
706
|
-
console.log("Starting Mezzanine Job(s)");
|
|
707
|
-
|
|
708
|
-
const startResponse = await client.StartABRMezzanineJobs({
|
|
709
|
-
libraryId: library,
|
|
710
|
-
objectId,
|
|
711
|
-
offeringKey,
|
|
712
|
-
access
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
Report(startResponse);
|
|
716
|
-
|
|
717
|
-
console.log("\nLibrary ID", library);
|
|
718
|
-
console.log("Object ID", objectId);
|
|
719
|
-
console.log("Offering:", offeringKey);
|
|
720
|
-
console.log("Write Token:", startResponse.lro_draft.write_token);
|
|
721
|
-
console.log("Write Node:", startResponse.lro_draft.node, "\n");
|
|
722
|
-
|
|
723
|
-
if(!wait) {
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
console.log("Progress:");
|
|
728
|
-
|
|
729
|
-
// eslint-disable-next-line no-constant-condition
|
|
730
|
-
while(true) {
|
|
731
|
-
const status = await client.LROStatus({libraryId: library, objectId, offeringKey});
|
|
732
|
-
|
|
733
|
-
let done = true;
|
|
734
|
-
const progress = Object.keys(status).map(id => {
|
|
735
|
-
const info = status[id];
|
|
736
|
-
|
|
737
|
-
if(!info.end) {
|
|
738
|
-
done = false;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
if(done && info.run_state !== "finished") {
|
|
742
|
-
throw Error(`LRO ${id} failed with status ${info.run_state}`);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
return `${id}: ${parseFloat(info.progress.percentage || 0).toFixed(1)}%`;
|
|
746
|
-
});
|
|
747
|
-
|
|
748
|
-
readline.clearLine(process.stdout, 0);
|
|
749
|
-
readline.cursorTo(process.stdout, 0, null);
|
|
750
|
-
process.stdout.write(progress.join(" "));
|
|
751
|
-
|
|
752
|
-
if(done) {
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
const finalizeResponse = await client.FinalizeABRMezzanine({
|
|
760
|
-
libraryId: library,
|
|
761
|
-
objectId,
|
|
762
|
-
offeringKey: offeringKey,
|
|
763
|
-
preFinalizeThrow: true,
|
|
764
|
-
preFinalizeFn // Before object is finalized, try to set codec descriptors
|
|
765
|
-
});
|
|
766
|
-
|
|
767
|
-
Report(finalizeResponse);
|
|
768
|
-
|
|
769
|
-
console.log("\n\nABR mezzanine object created:");
|
|
770
|
-
console.log("\tObject ID:", objectId);
|
|
771
|
-
console.log("\tVersion Hash:", finalizeResponse.hash, "\n");
|
|
772
|
-
} catch(error) {
|
|
773
|
-
console.error("Error creating mezzanine:");
|
|
774
|
-
console.error(error.body ? JSON.stringify(error, null, 2) : error);
|
|
775
|
-
}
|
|
776
|
-
};
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
Create(argv);
|