UNPKG

@eluvio/elv-utils-js

Version:

Utilities for the Eluvio Content Fabric

287 lines (250 loc) 8.78 kB
// Create a new mezzanine and start jobs const R = require('@eluvio/ramda-fork') const {seconds} = require('./lib/helpers') const {ModOpt, NewOpt} = require('./lib/options') const Utility = require('./lib/Utility') const AssetMetadata = require('./lib/concerns/AssetMetadata') const ArgMetadata = require('./lib/concerns/ArgMetadata') const ArgObjectId = require('./lib/concerns/ArgObjectId') const ArgType = require('./lib/concerns/ArgType') const Client = require('./lib/concerns/Client') const CloudAccess = require('./lib/concerns/CloudAccess') const ContentType = require('./lib/concerns/ContentType') const FabricObject = require('./lib/concerns/libs/FabricObject') const Finalize = require('./lib/concerns/Finalize') const JSON = require('./lib/concerns/JSON') const LRO = require('./lib/concerns/LRO') const chkLibraryPresent = (argv) => { if(!argv.existingMezId && !argv.libraryId) { throw Error('--libraryId must be supplied unless --existingMezId is present') } return true } const chkTypePresent = (argv) => { if(!argv.existingMezId && !argv.type) { throw Error('--type must be supplied unless --existingMezId is present') } return true } const chkTitlePresent = (argv) => { if(!argv.existingMezId && !argv.title) { throw Error('--title must be supplied unless --existingMezId is present') } return true } class MezCreate extends Utility { static blueprint() { return { concerns: [ AssetMetadata, ArgMetadata, ArgObjectId, ArgType, Client, CloudAccess, ContentType, FabricObject, Finalize, JSON, LRO ], options: [ ModOpt('libraryId', { alias: ['mezLib', 'mez-lib'], forX: 'mezzanine' }), ModOpt('objectId', { alias: ['existingMezId','existing-mez-id'], demand: false, descTemplate: 'Create the offering in existing mezzanine object with specified ID', }), ModOpt('type', { alias: ['mezType','mez-type'], forX: 'mezzanine' }), ModOpt('metadata', {ofX: 'mezzanine object'}), ModOpt('name', {ofX: 'mezzanine object (set to title + \' MEZ\' if not supplied and --existingMezId and --metadata not specified)'}), NewOpt('masterHash', { demand: true, descTemplate: 'Version hash of the master object', type: 'string' }), NewOpt('streamKeys', { descTemplate: 'If supplied, only the specified stream(s) will be processed', string: true, type: 'array' }), NewOpt('keepOtherStreams', { descTemplate: 'When offering already exists, keep pre-existing streams (other than the ones being transcoded)', implies: 'existingMezId', type: 'boolean' }), NewOpt('offeringKey', { default: 'default', descTemplate: 'Key to assign to new offering', type: 'string' }), NewOpt('variantKey', { default: 'default', descTemplate: 'Variant to use from production master', type: 'string' }), NewOpt('wait', { descTemplate: 'Wait for mezzanine to finish transcoding, then finalize before exiting script (not recommended except for very short titles)', type: 'boolean' }), NewOpt('addlOfferingSpecs', { descTemplate: 'Additional offerings to create via patching - JSON string (or file path if prefixed with \'@\')', implies: 'abrProfile', type: 'string' }), NewOpt('abrProfile', { descTemplate: 'Path to JSON file containing ABR profile with transcoding parameters and resolution ladders (if omitted, will be read from library metadata)', normalize: true, type: 'string' }) ], checksMap: {chkTypePresent, chkTitlePresent, chkLibraryPresent} } } async body() { const logger = this.logger const { addlOfferingSpecs, existingMezId, keepOtherStreams, masterHash, offeringKey, streamKeys } = this.args // do steps that don't require network access first // ---------------------------------------------------- const abrProfile = this.args.abrProfile ? this.concerns.JSON.parseFile({path: this.args.abrProfile}) : undefined let addlOffSpecs if(addlOfferingSpecs) { addlOffSpecs = this.concerns.JSON.parseStringOrFile({strOrPath: addlOfferingSpecs}) } const metadataFromArg = this.concerns.ArgMetadata.asObject() || {} let access = this.concerns.CloudAccess.credentialSet(false) // operations that may need to wait on network access // ---------------------------------------------------- if(existingMezId) await this.concerns.ArgObjectId.argsProc() const {libraryId} = this.args const client = await this.concerns.Client.get() let existingPublicMetadata = {} if(existingMezId) { logger.log(`Retrieving metadata from existing mezzanine object ${existingMezId}...`) existingPublicMetadata = (await this.concerns.FabricObject.metadata({ libraryId, objectId: existingMezId, subtree: 'public' })) || {} } if(!existingPublicMetadata.asset_metadata) existingPublicMetadata.asset_metadata = {} const mergedExistingAndArgMetadata = R.mergeDeepRight( {public: existingPublicMetadata}, metadataFromArg ) const newPublicMetadata = this.concerns.AssetMetadata.publicMetadata({ oldPublicMetadata: mergedExistingAndArgMetadata.public, backupNameSuffix: 'MEZ' }) const metadata = R.mergeDeepRight( metadataFromArg, {public: newPublicMetadata} ) const type = (existingMezId && !this.args.type) ? await this.concerns.ContentType.forItem({libraryId, objectId: existingMezId}) : await this.concerns.ArgType.typVersionHash() if(existingMezId) { logger.log('Updating existing mezzanine object...') } else { logger.log('Creating new mezzanine object...') } const createResponse = await client.CreateABRMezzanine({ name: metadata.public.name, libraryId, objectId: existingMezId, type, masterVersionHash: masterHash, variant: this.args.variantKey, offeringKey, metadata, abrProfile, addlOfferingSpecs: addlOffSpecs, keepOtherStreams, streamKeys }) logger.errorsAndWarnings(createResponse) const objectId = createResponse.id logger.log('Starting Mezzanine Job(s)') const startResponse = await client.StartABRMezzanineJobs({ libraryId, objectId, offeringKey, access }) logger.errorsAndWarnings(startResponse) const lroWriteToken = R.path(['lro_draft', 'write_token'], startResponse) const lroNode = R.path(['lro_draft', 'node'], startResponse) logger.data('library_id', libraryId) logger.data('object_id', objectId) logger.data('offering_key', offeringKey) logger.data('write_token', lroWriteToken) logger.data('write_node', lroNode) logger.logList( '', `Library ID: ${libraryId}`, `Object ID: ${objectId}`, `Offering: ${offeringKey}`, `Write Token: ${lroWriteToken}`, `Write Node: ${lroNode}`, '' ) if(!this.args.wait) return logger.log('Progress:') const lro = this.concerns.LRO let done = false let lastStatus while(!done) { const statusReport = await lro.status({libraryId, objectId}) const statusSummary = statusReport.summary lastStatus = statusSummary.run_state if(lastStatus !== LRO.STATE_RUNNING) done = true logger.log(`run_state: ${lastStatus}`) const eta = statusSummary.estimated_time_left_h_m_s if(eta) logger.log(`estimated time left: ${eta}`) await seconds(15) } const finalizeAbrResponse = await client.FinalizeABRMezzanine({ libraryId, objectId, offeringKey }) const latestHash = finalizeAbrResponse.hash logger.errorsAndWarnings(finalizeAbrResponse) logger.logList( '', 'ABR mezzanine object created:', ` Object ID: ${objectId}`, ` Version Hash: ${latestHash}`, '' ) logger.data('version_hash', latestHash) await this.concerns.Finalize.waitForPublish({ latestHash, libraryId, objectId }) } header() { return `Create Mezzanine offering '${this.args.offeringKey}' from Variant '${this.args.variantKey}' in Master version ${this.args.masterHash}` } } if(require.main === module) { Utility.cmdLineInvoke(MezCreate) } else { module.exports = MezCreate }