@eluvio/elv-client-js 4.0.135 → 4.0.137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/ElvClient.js +67 -3
- package/src/FrameClient.js +2 -1
- package/src/client/ABRPublishing.js +215 -83
- package/src/client/ContentManagement.js +7 -6
- package/src/client/LiveConf.js +13 -9
- package/src/client/LiveStream.js +8 -3
- package/src/walletClient/index.js +10 -6
- package/utilities/FrontEndSimpleIngest.js +198 -0
- package/utilities/ProductionMasterCreate.js +9 -2
- package/utilities/SampleIngest.js +225 -0
- package/utilities/SampleIngestWithMaster.js +226 -0
- package/utilities/lib/concerns/ArgLibraryId.js +1 -1
- package/utilities/lib/concerns/ArgTenant.js +23 -0
- package/utilities/lib/concerns/ContentType.js +1 -1
- package/utilities/lib/concerns/Finalize.js +12 -7
- package/utilities/lib/concerns/LRO.js +3 -3
- package/utilities/lib/concerns/Tenant.js +47 -0
|
@@ -0,0 +1,226 @@
|
|
|
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 ArgTenant = require("./lib/concerns/ArgTenant");
|
|
15
|
+
const {seconds} = require("./lib/helpers");
|
|
16
|
+
const AbrProfile = require("./lib/abr_profiles/abr_profile_clear.json");
|
|
17
|
+
|
|
18
|
+
class SampleIngest extends Utility {
|
|
19
|
+
blueprint() {
|
|
20
|
+
return {
|
|
21
|
+
concerns: [Client, Finalize, LocalFile, ArgLibraryId, ArgTenant, LRO],
|
|
22
|
+
options: [
|
|
23
|
+
ModOpt("libraryId", {demand: true, forX: "new media object"}),
|
|
24
|
+
NewOpt("title", {
|
|
25
|
+
demand: true,
|
|
26
|
+
descTemplate: "Title for new media object",
|
|
27
|
+
type: "string"
|
|
28
|
+
}),
|
|
29
|
+
NewOpt("drm", {
|
|
30
|
+
default: false,
|
|
31
|
+
descTemplate: "Use DRM for playback",
|
|
32
|
+
type: "boolean"
|
|
33
|
+
}),
|
|
34
|
+
ModOpt("files", {forX: "for new media object"})
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async body() {
|
|
40
|
+
const logger = this.logger;
|
|
41
|
+
|
|
42
|
+
let fileHandles = [];
|
|
43
|
+
const fileInfo = this.concerns.LocalFile.fileInfo(fileHandles);
|
|
44
|
+
|
|
45
|
+
// delay getting elvClient until this point so script exits faster
|
|
46
|
+
// if there is a validation error above
|
|
47
|
+
const client = await this.concerns.Client.get();
|
|
48
|
+
|
|
49
|
+
// get type from Tenant
|
|
50
|
+
const tenantInfo = await this.concerns.ArgTenant.tenantInfo();
|
|
51
|
+
|
|
52
|
+
const type = tenantInfo.typeTitle;
|
|
53
|
+
|
|
54
|
+
if(R.isNil(type)) throw Error("Tenant does not specify content type for titles");
|
|
55
|
+
|
|
56
|
+
const {drm, libraryId, title} = this.args;
|
|
57
|
+
const encrypt = true;
|
|
58
|
+
|
|
59
|
+
logger.log("Uploading files...");
|
|
60
|
+
|
|
61
|
+
const createMasterResponse = await client.CreateProductionMaster({
|
|
62
|
+
libraryId,
|
|
63
|
+
type,
|
|
64
|
+
name: `${title} MASTER`,
|
|
65
|
+
description: `Media object created via sample ingest: ${title}`,
|
|
66
|
+
fileInfo,
|
|
67
|
+
encrypt,
|
|
68
|
+
copy: true,
|
|
69
|
+
callback: this.concerns.LocalFile.callback
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const {id, hash} = createMasterResponse;
|
|
73
|
+
|
|
74
|
+
// Log object id immediately, in case of error later in script
|
|
75
|
+
// Don't log hash yet, it will change if any other revision to object is needed
|
|
76
|
+
logger.data("object_id", id);
|
|
77
|
+
|
|
78
|
+
// Close file handles (if any)
|
|
79
|
+
this.concerns.LocalFile.closeFileHandles(fileHandles);
|
|
80
|
+
|
|
81
|
+
logger.errorsAndWarnings(createMasterResponse);
|
|
82
|
+
|
|
83
|
+
logger.logList(
|
|
84
|
+
"",
|
|
85
|
+
"Production master default variant created:",
|
|
86
|
+
` Object ID: ${id}`,
|
|
87
|
+
` Version Hash: ${hash}`,
|
|
88
|
+
""
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if(!R.isNil(createMasterResponse.errors) && !R.isEmpty(createMasterResponse.errors)) throw Error(`Error(s) encountered while inspecting uploaded files: ${createMasterResponse.errors.join("\n")}`);
|
|
92
|
+
|
|
93
|
+
// get production master metadata
|
|
94
|
+
const masterMetadata = (await client.ContentObjectMetadata({
|
|
95
|
+
libraryId,
|
|
96
|
+
objectId: id,
|
|
97
|
+
versionHash: hash,
|
|
98
|
+
metadataSubtree: "/production_master"
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
const sources = R.prop("sources", masterMetadata);
|
|
102
|
+
const variant = R.path(["variants", "default"], masterMetadata);
|
|
103
|
+
|
|
104
|
+
// add info on source files and variant to data if --json selected
|
|
105
|
+
if(this.args.json) {
|
|
106
|
+
logger.data("media_files", sources);
|
|
107
|
+
logger.data("variant_default", variant);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// generate ABR profile
|
|
111
|
+
const genProfileRetVal = ABR.ABRProfileForVariant(sources, variant, AbrProfile);
|
|
112
|
+
if(!genProfileRetVal.ok) throw Error(`Error(s) encountered while generating ABR profile: ${genProfileRetVal.errors.join("\n")}`);
|
|
113
|
+
|
|
114
|
+
// filter DRM/clear as needed
|
|
115
|
+
const filterProfileRetVal = drm ?
|
|
116
|
+
ABR.ProfileExcludeClear(genProfileRetVal.result) :
|
|
117
|
+
ABR.ProfileExcludeDRM(genProfileRetVal.result);
|
|
118
|
+
if(!filterProfileRetVal.ok) throw Error(`Error(s) encountered while setting playout formats: ${filterProfileRetVal.errors.join("\n")}`);
|
|
119
|
+
|
|
120
|
+
// set up mezzanine offering
|
|
121
|
+
logger.log("Setting up media file conversion...");
|
|
122
|
+
const {writeToken} = await client.CreateContentObject({
|
|
123
|
+
libraryId,
|
|
124
|
+
options: type ? { type } : {}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const createMezResponse = await client.CreateABRMezzanine({
|
|
128
|
+
name: `${title} MEZ`,
|
|
129
|
+
libraryId,
|
|
130
|
+
type,
|
|
131
|
+
writeToken,
|
|
132
|
+
masterVersionHash: hash,
|
|
133
|
+
variant: "default",
|
|
134
|
+
offeringKey: "default",
|
|
135
|
+
abrProfile: filterProfileRetVal.result
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
logger.errorsAndWarnings(createMezResponse);
|
|
139
|
+
const createMezErrors = createMezResponse.errors;
|
|
140
|
+
if(!R.isNil(createMezErrors) && !R.isEmpty(createMezErrors)) throw Error(`Error(s) encountered while setting up media file conversion: ${createMezErrors.join("\n")}`);
|
|
141
|
+
|
|
142
|
+
logger.log("Starting conversion to streaming format...");
|
|
143
|
+
|
|
144
|
+
const startJobsResponse = await client.StartABRMezzanineJobs({
|
|
145
|
+
libraryId,
|
|
146
|
+
objectId: createMezResponse.id,
|
|
147
|
+
offeringKey: "default",
|
|
148
|
+
writeToken
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
logger.errorsAndWarnings(startJobsResponse);
|
|
152
|
+
const startJobsErrors = createMezResponse.errors;
|
|
153
|
+
if(!R.isNil(startJobsErrors) && !R.isEmpty(startJobsErrors)) throw Error(`Error(s) encountered while starting file conversion: ${startJobsErrors.join("\n")}`);
|
|
154
|
+
|
|
155
|
+
const lroWriteToken = R.path(["lro_draft", "write_token"], startJobsResponse);
|
|
156
|
+
const lroNode = R.path(["lro_draft", "node"], startJobsResponse);
|
|
157
|
+
|
|
158
|
+
logger.data("library_id", libraryId);
|
|
159
|
+
logger.data("object_id", id);
|
|
160
|
+
logger.data("offering_key", "default");
|
|
161
|
+
logger.data("write_token", lroWriteToken);
|
|
162
|
+
logger.data("write_node", lroNode);
|
|
163
|
+
|
|
164
|
+
logger.logList(
|
|
165
|
+
"",
|
|
166
|
+
`Library ID: ${libraryId}`,
|
|
167
|
+
`Object ID: ${id}`,
|
|
168
|
+
"Offering: default",
|
|
169
|
+
`Write Token: ${lroWriteToken}`,
|
|
170
|
+
`Write Node: ${lroNode}`,
|
|
171
|
+
""
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
logger.log("Progress:");
|
|
175
|
+
|
|
176
|
+
const lro = this.concerns.LRO;
|
|
177
|
+
let done = false;
|
|
178
|
+
let lastStatus;
|
|
179
|
+
while(!done) {
|
|
180
|
+
const statusMap = await lro.status({libraryId, objectId: id, writeToken}); // TODO: check how offering key is used, if at all
|
|
181
|
+
const statusSummary = lro.statusSummary(statusMap);
|
|
182
|
+
lastStatus = statusSummary.run_state;
|
|
183
|
+
if(lastStatus !== LRO.STATE_RUNNING) done = true;
|
|
184
|
+
logger.log(`run_state: ${lastStatus}`);
|
|
185
|
+
const eta = statusSummary.estimated_time_left_h_m_s;
|
|
186
|
+
if(eta) logger.log(`estimated time left: ${eta}`);
|
|
187
|
+
await seconds(15);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const finalizeAbrResponse = await client.FinalizeABRMezzanine({
|
|
191
|
+
libraryId,
|
|
192
|
+
objectId: createMezResponse.id,
|
|
193
|
+
writeToken,
|
|
194
|
+
offeringKey: "default"
|
|
195
|
+
});
|
|
196
|
+
const latestHash = finalizeAbrResponse.hash;
|
|
197
|
+
|
|
198
|
+
logger.errorsAndWarnings(finalizeAbrResponse);
|
|
199
|
+
const finalizeErrors = finalizeAbrResponse.errors;
|
|
200
|
+
if(!R.isNil(finalizeErrors) && !R.isEmpty(finalizeErrors)) throw Error(`Error(s) encountered while finalizing object: ${finalizeErrors.join("\n")}`);
|
|
201
|
+
|
|
202
|
+
logger.logList(
|
|
203
|
+
"",
|
|
204
|
+
"Playable media object created:",
|
|
205
|
+
` Object ID: ${id}`,
|
|
206
|
+
` Version Hash: ${latestHash}`,
|
|
207
|
+
""
|
|
208
|
+
);
|
|
209
|
+
logger.data("version_hash", latestHash);
|
|
210
|
+
await this.concerns.Finalize.waitForPublish({
|
|
211
|
+
latestHash,
|
|
212
|
+
libraryId,
|
|
213
|
+
objectId: createMezResponse.id
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
header() {
|
|
218
|
+
return "Create playable media object via sample ingest";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if(require.main === module) {
|
|
223
|
+
Utility.cmdLineInvoke(SampleIngest);
|
|
224
|
+
} else {
|
|
225
|
+
module.exports = SampleIngest;
|
|
226
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// code related to tenant
|
|
2
|
+
|
|
3
|
+
const Tenant = require("./Tenant");
|
|
4
|
+
|
|
5
|
+
const blueprint = {
|
|
6
|
+
name: "ArgTenant",
|
|
7
|
+
concerns: [Tenant],
|
|
8
|
+
options: []
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const New = context => {
|
|
12
|
+
const tenantInfo = async () => await context.concerns.Tenant.info();
|
|
13
|
+
|
|
14
|
+
// instance interface
|
|
15
|
+
return {
|
|
16
|
+
tenantInfo
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
blueprint,
|
|
22
|
+
New
|
|
23
|
+
};
|
|
@@ -35,12 +35,17 @@ const New = context => {
|
|
|
35
35
|
let publishFinished = false;
|
|
36
36
|
let latestObjectData = {};
|
|
37
37
|
while(!publishFinished) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
try {
|
|
39
|
+
latestObjectData = await client.ContentObject({libraryId, objectId});
|
|
40
|
+
if(latestObjectData.hash === latestHash) {
|
|
41
|
+
logger.log("New object version now available");
|
|
42
|
+
publishFinished = true;
|
|
43
|
+
} else {
|
|
44
|
+
logger.log(" new version not available yet, waiting 15 seconds...");
|
|
45
|
+
await seconds(15);
|
|
46
|
+
}
|
|
47
|
+
} catch(error) {
|
|
48
|
+
console.error(`Waiting for master object publishing hash: ${latestHash}. Retrying.`, error);
|
|
44
49
|
await seconds(15);
|
|
45
50
|
}
|
|
46
51
|
}
|
|
@@ -52,4 +57,4 @@ const New = context => {
|
|
|
52
57
|
module.exports = {
|
|
53
58
|
blueprint,
|
|
54
59
|
New
|
|
55
|
-
};
|
|
60
|
+
};
|
|
@@ -73,9 +73,9 @@ const New = context => {
|
|
|
73
73
|
statusEntry.run_state = state;
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
const status = async ({libraryId, objectId}) => {
|
|
76
|
+
const status = async ({libraryId, objectId, writeToken}) => {
|
|
77
77
|
const client = await context.concerns.Client.get();
|
|
78
|
-
const statusMap = await client.LROStatus({libraryId, objectId}); // TODO: check how offering key is used, if at all
|
|
78
|
+
const statusMap = await client.LROStatus({libraryId, objectId, writeToken}); // TODO: check how offering key is used, if at all
|
|
79
79
|
|
|
80
80
|
if(kindOf(statusMap)==="undefined") throw Error("Received no job status information from server - object already finalized?");
|
|
81
81
|
return statusMapProcess(statusMap);
|
|
@@ -151,4 +151,4 @@ module.exports = {
|
|
|
151
151
|
STATE_STALLED,
|
|
152
152
|
STATE_BAD_PCT,
|
|
153
153
|
STATE_ERROR,
|
|
154
|
-
};
|
|
154
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// code related to tenant data
|
|
2
|
+
const Client = require("./Client");
|
|
3
|
+
const Logger = require("./Logger");
|
|
4
|
+
|
|
5
|
+
const blueprint = {
|
|
6
|
+
name: "Tenant",
|
|
7
|
+
concerns: [Client, Logger]
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const New = context => {
|
|
11
|
+
const logger = context.concerns.Logger;
|
|
12
|
+
|
|
13
|
+
const info = async () => {
|
|
14
|
+
const client = await context.concerns.Client.get();
|
|
15
|
+
const tenantId = await client.userProfileClient.TenantContractId();
|
|
16
|
+
logger.log(`Getting info for tenant ${tenantId}...`);
|
|
17
|
+
|
|
18
|
+
const metadata = await client.ContentObjectMetadata({
|
|
19
|
+
libraryId: tenantId.replace("iten", "ilib"),
|
|
20
|
+
objectId: tenantId.replace("iten", "iq__"),
|
|
21
|
+
metadataSubtree: "public",
|
|
22
|
+
select: [
|
|
23
|
+
"sites/live_streams",
|
|
24
|
+
"content_types/live_stream",
|
|
25
|
+
"content_types/title"
|
|
26
|
+
]
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const typeTitle = metadata.content_types && metadata.content_types.title;
|
|
30
|
+
const typeLiveStream = metadata.content_types && metadata.content_types.live_stream;
|
|
31
|
+
const sitesLiveStream = metadata.sites && metadata.sites.live_streams;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
typeTitle,
|
|
35
|
+
typeLiveStream,
|
|
36
|
+
sitesLiveStream,
|
|
37
|
+
tenantId
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return {info};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
blueprint,
|
|
46
|
+
New
|
|
47
|
+
};
|