diff --git a/bitmovin/account/organizations/groups.js b/bitmovin/account/organizations/groups.js new file mode 100644 index 0000000..f54de0c --- /dev/null +++ b/bitmovin/account/organizations/groups.js @@ -0,0 +1,38 @@ +import urljoin from 'url-join'; +import http from '../../http'; +import permissions from './permissions'; +import tenants from './tenants'; + +export const groups = (configuration, organizationId, http) => { + const { get, post, delete_ } = http; + const groupsBaseUrl = urljoin(configuration.apiBaseUrl, 'account', 'organizations', organizationId, 'groups'); + + let fn = (groupId) => { + return { + details: () => { + let url = urljoin(groupsBaseUrl, groupId); + return get(configuration, url); + }, + delete: () => { + let url = urljoin(groupsBaseUrl, groupId); + return delete_(configuration, url); + }, + permissions: permissions(configuration, organizationId, groupId), + tenants: tenants(configuration, organizationId, groupId) + }; + }; + + fn.add = (group) => { + const url = urljoin(groupsBaseUrl); + return post(configuration, url, group); + }; + + fn.list = () => { + const url = urljoin(groupsBaseUrl); + return get(configuration, url); + }; + + return fn; +}; + +export default (configuration, organizationId) => { return groups(configuration, organizationId, http); }; diff --git a/bitmovin/account/organizations/organizations.js b/bitmovin/account/organizations/organizations.js index 8c9448b..d42021b 100644 --- a/bitmovin/account/organizations/organizations.js +++ b/bitmovin/account/organizations/organizations.js @@ -1,8 +1,9 @@ import urljoin from 'url-join'; import http from '../../http'; +import groups from './groups'; export const organizations = (configuration, http) => { - const { get, post, delete_ } = http; + const { get, post, delete_, put } = http; const organizationsBaseUrl = urljoin(configuration.apiBaseUrl, 'account', 'organizations'); let fn = (organizationId) => { @@ -14,7 +15,12 @@ export const organizations = (configuration, http) => { delete: () => { let url = urljoin(organizationsBaseUrl, organizationId); return delete_(configuration, url); - } + }, + update: (organization) => { + let url = urljoin(organizationsBaseUrl, organizationId); + return put(configuration, url, organization); + }, + groups: groups(configuration, organizationId) }; }; diff --git a/bitmovin/account/organizations/permissions.js b/bitmovin/account/organizations/permissions.js new file mode 100644 index 0000000..5a68122 --- /dev/null +++ b/bitmovin/account/organizations/permissions.js @@ -0,0 +1,34 @@ +import urljoin from 'url-join'; +import http from '../../http'; + +export const permissions = (configuration, organizationId, groupId, http) => { + const { get, post, delete_ } = http; + const permissionsBaseUrl = urljoin(configuration.apiBaseUrl, 'account', 'organizations', organizationId, 'groups', groupId, 'permissions'); + + let fn = (groupId) => { + return { + details: () => { + let url = urljoin(permissionsBaseUrl, groupId); + return get(configuration, url); + }, + delete: () => { + let url = urljoin(permissionsBaseUrl, groupId); + return delete_(configuration, url); + } + }; + }; + + fn.add = (permission) => { + const url = urljoin(permissionsBaseUrl); + return post(configuration, url, permission); + }; + + fn.list = () => { + const url = urljoin(permissionsBaseUrl); + return get(configuration, url); + }; + + return fn; +}; + +export default (configuration, organizationId, groupId) => { return permissions(configuration, organizationId, groupId, http); }; diff --git a/bitmovin/account/organizations/tenants.js b/bitmovin/account/organizations/tenants.js new file mode 100644 index 0000000..bf1f06a --- /dev/null +++ b/bitmovin/account/organizations/tenants.js @@ -0,0 +1,34 @@ +import urljoin from 'url-join'; +import http from '../../http'; + +export const tenants = (configuration, organizationId, groupId, http) => { + const { get, post, delete_ } = http; + const tenantsBaseUrl = urljoin(configuration.apiBaseUrl, 'account', 'organizations', organizationId, 'groups', groupId, 'tenants'); + + let fn = (tenantId) => { + return { + details: () => { + let url = urljoin(tenantsBaseUrl, tenantId); + return get(configuration, url); + }, + delete: () => { + let url = urljoin(tenantsBaseUrl, tenantId); + return delete_(configuration, url); + } + }; + }; + + fn.add = (tenant) => { + const url = urljoin(tenantsBaseUrl); + return post(configuration, url, tenant); + }; + + fn.list = () => { + const url = urljoin(tenantsBaseUrl); + return get(configuration, url); + }; + + return fn; +}; + +export default (configuration, organizationId, groupId) => { return tenants(configuration, organizationId, groupId, http); }; diff --git a/bitmovin/analytics/query_builder.js b/bitmovin/analytics/query_builder.js index 93faf3b..bb47565 100644 --- a/bitmovin/analytics/query_builder.js +++ b/bitmovin/analytics/query_builder.js @@ -69,6 +69,13 @@ class Builder { } return this; } + licenseKey(licenseKey) { + this.query_ = { + ...this.query_, + licenseKey + } + return this; + } query() { return this.target_(this.query_) } diff --git a/bitmovin/bitmovin.js b/bitmovin/bitmovin.js index 3bd102d..dc70867 100644 --- a/bitmovin/bitmovin.js +++ b/bitmovin/bitmovin.js @@ -69,7 +69,7 @@ export default class Bitmovin { 'X-Api-Key' : configuration.apiKey, 'X-Tenant-Org-Id' : configuration.tenantOrgId, 'X-Api-Client' : configuration.xApiClient, - 'X-Api-Client-Version': '1.1.19' + 'X-Api-Client-Version': '1.2.2' }; this.configuration = configuration; diff --git a/bitmovin/encoding/hlsManifestMedia.js b/bitmovin/encoding/hlsManifestMedia.js index 7939442..681bbb4 100644 --- a/bitmovin/encoding/hlsManifestMedia.js +++ b/bitmovin/encoding/hlsManifestMedia.js @@ -47,7 +47,8 @@ export const hlsManifestMedia = (configuration, manifestId, http) => { video : typeFn('video'), audio : typeFn('audio'), subtitles : typeFn('subtitles'), - closedCaptions: typeFn('closed-captions') + closedCaptions: typeFn('closed-captions'), + vtt : typeFn('vtt') }; }; diff --git a/examples/encoding/08_encoding_hls_aes128.js b/examples/encoding/08_encoding_hls_aes128.js new file mode 100644 index 0000000..78ebb13 --- /dev/null +++ b/examples/encoding/08_encoding_hls_aes128.js @@ -0,0 +1,422 @@ +// 08_encoding_hls_aes128.js + +const Promise = require('bluebird'); +const Bitmovin = require('bitmovin-javascript').default; + + +const BITMOVIN_API_KEY = ''; +const bitmovin = new Bitmovin({apiKey: BITMOVIN_API_KEY, debug: true}); + +const ENCODING_NAME = 'TEST_ENCODING_' + new Date().toISOString(); + +const INPUT_S3_ACCESS_KEY = ''; +const INPUT_S3_SECRET_KEY = ''; +const INPUT_S3_BUCKET_NAME = ''; +const INPUT_S3_PATH = '/path/to/your/input/file.mp4'; + +const OUTPUT_S3_ACCESS_KEY = ''; +const OUTPUT_S3_SECRET_KEY = ''; +const OUTPUT_S3_BUCKET_NAME = ''; +const OUTPUT_S3_BASE_PATH = '/path/to/your/output/destination'; + +const DRM_AES_KEY = ''; +const DRM_AES_IV = ''; + + +const main = () => { + return new Promise((resolve, reject) => { + + const s3InputCreationPromise = createInput(); + const s3OutputCreationPromise = createOutput(); + const encodingResourceCreationPromise = createEncoding(); + const codecConfigurationH264At720pPromise = createH264CodecConfiguration(1280, 720, 2400000, 25.0); + const codecConfigurationH264At480pPromise = createH264CodecConfiguration(854, 480, 1200000, 25.0); + const codecConfigurationH264At360pPromise = createH264CodecConfiguration(640, 360, 800000, 25.0); + const codecConfigurationH264At240pPromise = createH264CodecConfiguration(426, 240, 400000, 25.0); + const codecConfigurationAACPromise = createAACCodecConfiguration(128000, 48000); + + Promise.all( + [ + s3InputCreationPromise, + s3OutputCreationPromise, + encodingResourceCreationPromise, + codecConfigurationH264At720pPromise, + codecConfigurationH264At480pPromise, + codecConfigurationH264At360pPromise, + codecConfigurationH264At240pPromise, + codecConfigurationAACPromise + ] + ).then(([ + input, + output, + encoding, + codecConfigurationH264At720p, + codecConfigurationH264At480p, + codecConfigurationH264At360p, + codecConfigurationH264At240p, + codecConfigurationAAC + ]) => { + console.log('Successfully created input, output and codec configurations.'); + const hlsManifestCreationPromise = createHlsManifest(output.id); + + hlsManifestCreationPromise.then(hlsManifest => { + console.log('Successfully created HLS Manifest Resources'); + + const streamDefinitions = [ + { + codecConfiguration: codecConfigurationAAC, + outputPathDiscriminator: 'audio/128' + }, + { + codecConfiguration: codecConfigurationH264At240p, + outputPathDiscriminator: 'video/240' + }, + { + codecConfiguration: codecConfigurationH264At360p, + outputPathDiscriminator: 'video/360' + }, { + codecConfiguration: codecConfigurationH264At480p, + outputPathDiscriminator: 'video/480' + }, { + codecConfiguration: codecConfigurationH264At720p, + outputPathDiscriminator: 'video/720' + } + ]; + + const promiseMap = Promise.map(streamDefinitions, (streamDefinition) => { + return createStreamWithMuxingsAndDRMsAndManifestResources( + streamDefinition.codecConfiguration, input, output, streamDefinition.outputPathDiscriminator, encoding, hlsManifest + ); + }, {concurrency: 1}); + + promiseMap.then((result) => { + console.log('Successfully created all resources. Starting Encoding Process...'); + startEncodingAndWaitForItToBeFinished(encoding).then(() => { + console.log('Successfully Finished Encoding Process.. Starting manifest creation...'); + + const hlsCreationPromise = startHlsManifestCreationAndWaitForItToBeFinished(hlsManifest); + hlsCreationPromise.then(() => { + console.log('Successfully finished HLS manifest creation!'); + resolve(result); + }); + }); + }).catch(logErrorPromise(reject, 'An error occurred: ')); + }); + }); + }); +}; + +const createInput = () => { + return new Promise((resolve, reject) => { + const s3Input = { + name: 'My awesome S3 Input', + description: 'Little description for my awesome S3 Input', + accessKey: INPUT_S3_ACCESS_KEY, + secretKey: INPUT_S3_SECRET_KEY, + bucketName: INPUT_S3_BUCKET_NAME + }; + bitmovin.encoding.inputs.s3.create(s3Input).then((createdInput) => { + console.log('Successfully created S3 Input.', createdInput); + resolve(createdInput); + }).catch(logErrorPromise(reject, 'Unable to create S3 Input.')); + }); +}; + +const createOutput = () => { + return new Promise((resolve, reject) => { + const s3Output = { + name: 'My awesome S3 Output', + description: 'Little description for my awesome S3 Output', + accessKey: OUTPUT_S3_ACCESS_KEY, + secretKey: OUTPUT_S3_SECRET_KEY, + bucketName: OUTPUT_S3_BUCKET_NAME + }; + bitmovin.encoding.outputs.s3.create(s3Output).then((createdOutput) => { + console.log('Successfully created S3 Output.', createdOutput); + resolve(createdOutput); + }).catch(logErrorPromise(reject, 'Unable to create S3 Output.')); + }); +}; + +const createEncoding = () => { + return new Promise((resolve, reject) => { + const encoding = { + name: ENCODING_NAME + }; + bitmovin.encoding.encodings.create(encoding).then((createdEncoding) => { + console.log('Successfully created Encoding resource.', createdEncoding); + resolve(createdEncoding); + }).catch(logErrorPromise(reject, 'Unable to create Encoding Resource.')); + }); +}; + +const createH264CodecConfiguration = (width, height, bitrate, fps) => { + return new Promise((resolve, reject) => { + const h264CodecConfiguration = { + name: 'H264 ' + height, + bitrate: bitrate, + rate: fps, + profile: 'HIGH', + width: width, + height: height + }; + bitmovin.encoding.codecConfigurations.h264.create(h264CodecConfiguration).then((createdCodecConfiguration) => { + console.log('Successfully created H264 Codec Configuration.', createdCodecConfiguration); + resolve(createdCodecConfiguration); + }).catch(logErrorPromise(reject, 'Unable to create H264 Codec Configuration')); + }); +}; + +const createAACCodecConfiguration = (bitrate, rate) => { + return new Promise((resolve, reject) => { + const aacCodecConfiguration = { + name: 'English', + bitrate: bitrate, + rate: rate + }; + bitmovin.encoding.codecConfigurations.aac.create(aacCodecConfiguration).then((createdCodecConfiguration) => { + console.log('Successfully created AAC Codec Configuration.', createdCodecConfiguration); + resolve(createdCodecConfiguration); + }).catch(logErrorPromise(reject, 'Unable to create AAC Codec Configuration')); + }); +}; + +const createHlsManifest = (outputId) => { + return new Promise((resolve, reject) => { + const hlsManifest = { + name: 'HLS Manifest for ' + ENCODING_NAME, + outputs: [{ + outputId: outputId, + outputPath: OUTPUT_S3_BASE_PATH, + acl: [{ + permission: 'PUBLIC_READ' + }] + }], + manifestName: 'hlsManifest.m3u8' + }; + bitmovin.encoding.manifests.hls.create(hlsManifest).then((createdManifest) => { + console.log('Successfully created HLS Manifest Resource.', createdManifest); + resolve(createdManifest); + }).catch(logErrorPromise(reject, 'Unable to create HLS manifest.')); + }); +}; + +const createStreamWithMuxingsAndDRMsAndManifestResources = (codecConfiguration, input, output, outputPath, encoding, hlsManifest) => { + return new Promise((resolve, reject) => { + const stream = { + name: 'Stream with ' + codecConfiguration.name, + codecConfigId: codecConfiguration.id, + inputStreams: [{ + inputId: input.id, + inputPath: INPUT_S3_PATH, + selectionMode: 'AUTO' + }] + }; + bitmovin.encoding.encodings(encoding.id).streams.add(stream).then((createdStream) => { + console.log('Successfully created Stream with Codec Configuration ' + codecConfiguration.name + '.', createdStream); + + const tsMuxingCreationPromise = createTSMuxingWithAESDRMAndManifestResources( + createdStream, outputPath, codecConfiguration, output, encoding, hlsManifest); + + tsMuxingCreationPromise.then(([createdTsMuxing, createdFairPlayDrm, createdHlsDrmRepresentation]) => { + resolve([ + createdStream, + [ + [createdTsMuxing, createdFairPlayDrm, createdHlsDrmRepresentation] + ] + ]); + }); + + }).catch(logErrorPromise(reject, 'Unable to create Stream with Codec Configuration ' + codecConfiguration.name + '.')); + }); +}; + +const createTSMuxingWithAESDRMAndManifestResources = (stream_, outputPath, codecConfiguration, output, encoding, hlsManifest) => { + return new Promise((resolve, reject) => { + const tsMuxing = { + name: 'TS Muxing for ' + stream_.name, + streams: [{ + streamId: stream_.id + }], + segmentLength: 4, + segmentNaming: 'seg_%number%.ts' + }; + + bitmovin.encoding.encodings(encoding.id).muxings.ts.add(tsMuxing).then((createdMuxing) => { + createAESDRMAndManifestResources(stream_, codecConfiguration, outputPath, createdMuxing, output, encoding, hlsManifest).then(([createdDrm, createdRepresentation]) => { + resolve([createdMuxing, createdDrm, createdRepresentation]); + }); + }).catch(logErrorPromise(reject, 'Unable to create TS Muxing ' + tsMuxing.name, + '.')); + }); +}; + +const createAESDRMAndManifestResources = (stream_, codecConfiguration, outputPath, tsMuxing, output, encoding, hlsManifest) => { + return new Promise((resolve, reject) => { + const drmOutputPath = OUTPUT_S3_BASE_PATH + '/' + outputPath + '/'; + const aesDRM = { + name: 'AES DRM for ' + tsMuxing.name, + outputs: [{ + outputId: output.id, + outputPath: drmOutputPath, + acl: [{ + permission: 'PUBLIC_READ' + }] + }], + method: 'AES_128', + key: DRM_AES_KEY, + iv: DRM_AES_IV + }; + + bitmovin.encoding.encodings(encoding.id).muxings.ts(tsMuxing.id).drms.aes.add(aesDRM).then((createdDrm) => { + console.log('Successfully created AES DRM ' + aesDRM.name + '.', createdDrm); + createHlsManifestRepresentation(encoding, stream_, tsMuxing, createdDrm, hlsManifest, drmOutputPath).then((createdRepresentation) => { + resolve([createdDrm, createdRepresentation]); + }); + }).catch(error => { + + logErrorPromise(reject, 'Unable to create AES DRM ' + aesDRM.name + '.') + }); + }); +}; + +const createHlsManifestRepresentation = (encoding, stream_ , tsMuxing, fairPlayDrm, hlsManifest, segmentPath) => { + return new Promise((resolve, reject) => { + let representationPromise = null; + if (segmentPath.includes('audio/')) { + representationPromise = createHlsAudioMedia(encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath); + } else { + representationPromise = createHlsVariantStream(encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath); + } + + representationPromise.then((response) => { + resolve(response); + }).catch((error) => { + reject(error); + }) + }); +}; + +const createHlsAudioMedia = (encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath) => { + return new Promise((resolve, reject) => { + const audioMedia = { + name: 'HLS Audio Media', + groupId: 'audio_group', + segmentPath: segmentPath, + encodingId: encoding.id, + streamId: stream_.id, + muxingId: tsMuxing.id, + drmId: fairPlayDrm.id, + language: 'en', + uri: 'audio_media.m3u8' + }; + + bitmovin.encoding.manifests.hls(hlsManifest.id).media.audio.add(audioMedia).then((createdAudioMedia) => { + console.log('Successfully created HLS Audio Media', createdAudioMedia); + resolve(createdAudioMedia); + }).catch(logErrorPromise(reject, 'Unable to create HLS Audio Media')); + }); +}; + +const createHlsVariantStream = (encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath) => { + return new Promise((resolve, reject) => { + const variantStream = { + audio: 'audio_group', + closedCaptions: 'NONE', + segmentPath: segmentPath, + encodingId: encoding.id, + streamId: stream_.id, + muxingId: tsMuxing.id, + drmId: fairPlayDrm.id, + uri: fairPlayDrm.id + '.m3u8' + }; + + bitmovin.encoding.manifests.hls(hlsManifest.id).streams.add(variantStream).then((createdVariantStream) => { + console.log('Successfully created Variant Stream for DRM with ID ' + fairPlayDrm.id + '.', createdVariantStream); + resolve(createdVariantStream); + }).catch(logErrorPromise(reject, 'Unable to create Variant Stream for DRM with ID ' + fairPlayDrm.id + '.')); + }); +}; + +const startEncodingAndWaitForItToBeFinished = (encoding) => { + const startPromise = bitmovin.encoding.encodings(encoding.id).start(); + + return new Promise((resolve, reject) => { + startPromise.then((startResponse) => { + waitUntilEncodingFinished(encoding).then((success) => { + console.log('Encoding finished', success); + resolve(true); + }).catch(logErrorPromise(reject, 'Encoding failed')); + }); + }); +}; + +const waitUntilEncodingFinished = (encoding) => { + return new Promise((resolve, reject) => { + const waitForEncodingToBeFinishedOrError = () => { + console.log('Getting Status for Encoding with ID ', encoding.id); + bitmovin.encoding.encodings(encoding.id).status().then((response) => { + console.log('Encoding Status is ' + response.status + '.'); + + if (response.status === 'FINISHED') { + return resolve(response.status); + } + + if (response.status === 'ERROR') { + return reject(response.status); + } + + setTimeout(waitForEncodingToBeFinishedOrError, 10000); + }); + }; + waitForEncodingToBeFinishedOrError(); + }); +}; + +const startHlsManifestCreationAndWaitForItToBeFinished = (manifest) => { + const startPromise = bitmovin.encoding.manifests.hls(manifest.id).start(); + + return new Promise((resolve, reject) => { + startPromise.then((startResponse) => { + waitUntilHlsManifestFinished(manifest).then((success) => { + console.log('hls manifest finished', success); + resolve(true); + }).catch(logErrorPromise(reject, 'HLS Manifest creation failed')); + }); + }); +}; + +const waitUntilHlsManifestFinished = (manifest) => { + return new Promise((resolve, reject) => { + const waitForManifestToBeFinished = () => { + console.log('Getting Status for HLS Manifest with ID ', manifest.id); + bitmovin.encoding.manifests.hls(manifest.id).status().then((response) => { + console.log('HLS Manifest Status is ' + response.status); + + if (response.status === 'FINISHED') { + return resolve(response.status); + } + + if (response.status === 'ERROR') { + return reject(response.status); + } + + setTimeout(waitForManifestToBeFinished, 10000); + }); + }; + waitForManifestToBeFinished(); + }); +}; + +const logErrorPromise = (reject, errorMessage) => { + return (error) => { + console.error(errorMessage, error); + reject(error); + }; +}; + +main().then(() => { + console.log('finished!'); +}).catch((error) => { + console.error('ERROR!', error); + process.exit(8); +}); diff --git a/examples/encoding/09_simple_encoding_mp4.js b/examples/encoding/09_simple_encoding_mp4.js new file mode 100644 index 0000000..e18b950 --- /dev/null +++ b/examples/encoding/09_simple_encoding_mp4.js @@ -0,0 +1,412 @@ +// 09_simple_encoding_mp4.js + +// --------------------------------------------------------------------------------------------------------------------- + +const Bitmovin = require('bitmovin-javascript').default; +const Promise = require('bluebird'); + +const BITMOVIN_API_KEY = ''; +const bitmovin = new Bitmovin({'apiKey': BITMOVIN_API_KEY, debug: false}); + +const ENCODING_NAME = 'simple_encoding_' + (new Date().getTime()/1000); + +const INPUT_FILE_HOST = ''; +const INPUT_FILE_PATH = '/path/to/your/input/file.mp4'; + +const S3_ACCESS_KEY = ''; +const S3_SECRET_KEY = ''; +const S3_BUCKET_NAME = ''; +const S3_CLOUD_REGION = ''; +const OUTPUT_PATH = '/path/to/your/output/destination/'; + + +const httpInput = { + name : 'Http input', + host : INPUT_FILE_HOST +}; + +const s3Output = { + name : 'S3 output', + accessKey : S3_ACCESS_KEY, + secretKey : S3_SECRET_KEY, + bucketName : S3_BUCKET_NAME, + cloudRegion: S3_CLOUD_REGION +}; + +const aacAudioCodecConfiguration = { + name : 'English', + bitrate: 128000, + rate : 48000 +}; +const h264VideoCodecConfiguration1080p = { + name : 'simple encoding - H264 1080p', + bitrate: 4800000, + rate : 24.0, + height : 1080, + profile: 'HIGH' +}; +const h264VideoCodecConfiguration720p = { + name : 'simple encoding - H264 720p', + bitrate: 2400000, + rate : 24.0, + height : 720, + profile: 'HIGH' +}; +const h264VideoCodecConfiguration480p = { + name : 'simple encoding - H264 480p', + bitrate: 1200000, + rate : 24.0, + height : 480, + profile: 'HIGH' +}; +const h264VideoCodecConfiguration360p = { + name : 'simple encoding - H264 360p', + bitrate: 800000, + rate : 24.0, + height : 360, + profile: 'HIGH' +}; +const h264VideoCodecConfiguration240p = { + name : 'simple encoding - H264 240p', + bitrate: 400000, + rate : 24.0, + height : 240, + profile: 'HIGH' +}; + +const encodingResource = { + name: ENCODING_NAME +}; + +const main = () => new Promise((resolve, reject) => { + let aacCodecConfiguration = Object.assign({}, aacAudioCodecConfiguration); + let h264CodecConfiguration1080p = Object.assign({}, h264VideoCodecConfiguration1080p); + let h264CodecConfiguration720p = Object.assign({}, h264VideoCodecConfiguration720p); + let h264CodecConfiguration480p = Object.assign({}, h264VideoCodecConfiguration480p); + let h264CodecConfiguration360p = Object.assign({}, h264VideoCodecConfiguration360p); + let h264CodecConfiguration240p = Object.assign({}, h264VideoCodecConfiguration240p); + let input = Object.assign({}, httpInput); + let output = Object.assign({}, s3Output); + let encoding = Object.assign({}, encodingResource); + + const createHttpInputPromise = createHttpInput(input); + createHttpInputPromise.then((createdInput) => { + console.log('Successfully created HTTP Input'); + input = createdInput; + }); + + const createS3OutputPromise = createS3Output(output); + createS3OutputPromise.then((createdS3Output) => { + console.log('Successfully created S3 Output'); + output = createdS3Output; + }); + + const createAACPromise = createAACCodecConfiguration(aacCodecConfiguration); + createAACPromise.then((createdCodecConfig) => { + console.log('Successfully created AAC Audio Codec Configuration'); + aacCodecConfiguration = createdCodecConfig; + }); + + const createH2641080pPromise = createH264CodecConfiguration(h264CodecConfiguration1080p); + createH2641080pPromise.then((createdCodecConfig) => { + console.log('Successfully created H264 Video Codec Configuration @1080p'); + h264CodecConfiguration1080p = createdCodecConfig; + }); + + const createH264720pPromise = createH264CodecConfiguration(h264CodecConfiguration720p); + createH264720pPromise.then((createdCodecConfig) => { + console.log('Successfully created H264 Video Codec Configuration @720p'); + h264CodecConfiguration720p = createdCodecConfig; + }); + + const createH264480pPromise = createH264CodecConfiguration(h264CodecConfiguration480p); + createH264480pPromise.then((createdCodecConfig) => { + console.log('Successfully created H264 Video Codec Configuration @480p'); + h264CodecConfiguration480p = createdCodecConfig; + }); + + const createH264360pPromise = createH264CodecConfiguration(h264CodecConfiguration360p); + createH264360pPromise.then((createdCodecConfig) => { + console.log('Successfully created H264 Video Codec Configuration @360p'); + h264CodecConfiguration360p = createdCodecConfig; + }); + + const createH264240pPromise = createH264CodecConfiguration(h264CodecConfiguration240p); + createH264240pPromise.then((createdCodecConfig) => { + console.log('Successfully created H264 Video Codec Configuration @240p'); + h264CodecConfiguration240p = createdCodecConfig; + }); + + const createEncodingPromise = createEncoding(encodingResource); + createEncodingPromise.then((createdEncoding) => { + console.log('Successfully created Encoding Resource with name ' + encodingResource.name); + encoding = createdEncoding; + }); + + const preparationPromises = [ + createHttpInputPromise, createS3OutputPromise, createH2641080pPromise, + createH264720pPromise, createH264480pPromise, createH264360pPromise, + createH264240pPromise, createAACPromise, createEncodingPromise + ]; + + const preparationPromise = Promise.all(preparationPromises); + + preparationPromise.then(() => { + console.log('----\nSuccessfully created input, output, codec configurations and encoding resource.\n----'); + + const videoCodecConfigurations = [ + h264CodecConfiguration1080p, h264CodecConfiguration720p, h264CodecConfiguration480p, + h264CodecConfiguration360p, h264CodecConfiguration240p + ]; + + addAudioStreamToEncoding(input, output, aacCodecConfiguration, encoding).then(audioStream => { + + const videoStreamMuxingsPromises = Promise.map(videoCodecConfigurations, (codecConfiguration) => { + console.log('Adding video stream with codecConfig ' + codecConfiguration.name + ' to encoding...'); + return addVideoStreamToEncoding(input, output, codecConfiguration, audioStream, encoding); + }, {concurrency: 1}).catch((error) => { + console.log('error creating video streams and muxings.'); + reject(error); + }); + + videoStreamMuxingsPromises.then(() => { + startEncodingAndWaitForItToBeFinished(encoding).then(finished => { + resolve() + }) + }) + }).catch(error => { + console.error('Could not create audio stream') + }) + }); +}); + +const addAudioStreamToEncoding = (input, output, audioCodecConfiguration, encoding) => { + const inputStream = { + inputId: input.id, + inputPath: INPUT_FILE_PATH, + selectionMode: 'AUTO' + }; + + let stream = { + inputStreams: [inputStream], + codecConfigId: audioCodecConfiguration.id + }; + + return new Promise((resolve, reject) => { + addStream(encoding, stream).then((addedStream) => { + console.log('Successfully created audio stream!'); + resolve(addedStream); + }).catch((error) => { + console.error('Unable to create stream.'); + reject(error); + }); + }); +}; + +const addVideoStreamToEncoding = (input, output, videoCodecConfig, audioStream, encoding) => { + const inputStream = { + inputId: input.id, + inputPath: INPUT_FILE_PATH, + selectionMode: 'AUTO' + }; + + let videoStream = { + inputStreams: [inputStream], + codecConfigId: videoCodecConfig.id + }; + + return new Promise((resolve, reject) => { + addVideoStreamAndCreateMuxingWithVideoAndAudioStream(encoding, videoStream, output, videoCodecConfig.bitrate, audioStream).then((addedMuxing) => { + console.log('Successfully created stream and muxing!'); + resolve(addedMuxing); + }).catch((error) => { + console.error('Unable to create stream and/or muxing.'); + reject(error); + }); + }); +}; + +const addStream = (encoding, stream) => { + const addStreamPromise = bitmovin.encoding.encodings(encoding.id).streams.add(stream); + + return new Promise((resolve, reject) => { + addStreamPromise.then((addedStream) => { + console.log('stream resource successfully added'); + resolve(addedStream); + }).catch((error) => { + console.error('unable to add stream to encoding', error); + reject(error); + }); + }); +}; + +const addVideoStreamAndCreateMuxingWithVideoAndAudioStream = (encoding, videoStream, output, bitrate, audioStream) => { + const addStreamPromise = bitmovin.encoding.encodings(encoding.id).streams.add(videoStream); + + return new Promise((resolve, reject) => { + addStreamPromise.then((addedStream) => { + console.log('stream resource successfully added'); + const prefix = bitrate + '/'; + + const streams = [ + { + streamId: addedStream.id + }, { + streamId: audioStream.id + } + ]; + + resolve(addMp4MuxingForStreams(encoding, streams, output, prefix)) + }).catch((error) => { + console.error('unable to add stream to encoding', error); + reject(error); + }); + }); +}; + + +const addMp4MuxingForStreams = (encoding, streams, output, output_prefix) => { + let mp4Muxing = { + name: 'MP4' + output_prefix, + streams, + outputs: [{ + outputId: output.id, + outputPath: OUTPUT_PATH + output_prefix, + acl: [{ + permission: 'PUBLIC_READ' + }] + }], + filename: 'file.mp4' + }; + + const addMuxingPromise = bitmovin.encoding.encodings(encoding.id).muxings.mp4.add(mp4Muxing); + + return new Promise((resolve, reject) => { + addMuxingPromise.then((addedFMP4Muxing) => { + console.log('added fmp4 muxing ' + mp4Muxing.name); + resolve(addedFMP4Muxing); + }).catch((error) => { + console.error('error adding fmp4 muxing ' + mp4Muxing.name, error); + reject(error); + }); + }); +}; + +const createHttpInput = (input) => { + const inputCreatePromise = bitmovin.encoding.inputs.http.create(input); + + return new Promise((resolve, reject) => { + inputCreatePromise.then((createdInput) => { + console.log('http input successfully created'); + resolve(createdInput); + }).catch((error) => { + console.error('error creating http input', error); + reject(error); + }); + }); +}; + +const createS3Output = (output) => { + const outputCreatePromise = bitmovin.encoding.outputs.s3.create(s3Output); + + return new Promise((resolve, reject) => { + outputCreatePromise.then((createdOutput) => { + console.log('S3 output successfully created'); + resolve(createdOutput); + }).catch((error) => { + console.error('error creating s3 output', error); + reject(error); + }); + }); +}; + +const createH264CodecConfiguration = (codecConfig) => { + const codecConfigPromise = bitmovin.encoding.codecConfigurations.h264.create(codecConfig); + + return new Promise((resolve, reject) => { + codecConfigPromise.then((createdConfig) => { + console.log('h264 Codec configuration ' + codecConfig.name + ' successfully created'); + resolve(createdConfig); + }).catch((error) => { + console.error('error creating h264 codec config ' + codecConfig.name); + reject(error); + }); + }); +}; + +const createAACCodecConfiguration = (codecConfig) => { + const codecConfigPromise = bitmovin.encoding.codecConfigurations.aac.create(codecConfig); + + return new Promise((resolve, reject) => { + codecConfigPromise.then((createdConfig) => { + console.log('aac Codec configuration ' + codecConfig.name + ' successfully created'); + resolve(createdConfig); + }).catch((error) => { + console.error('error creating aac codec config ' + codecConfig.name); + reject(error); + }); + }); +}; + +const createEncoding = (encoding) => { + const encodingPromise = bitmovin.encoding.encodings.create(encoding); + + return new Promise((resolve, reject) => { + encodingPromise.then((createdEncoding) => { + console.log('encoding ' + encoding.name + ' successfully created'); + resolve(createdEncoding); + }).catch((error) => { + console.error('error creating encoding ' + encoding.name); + reject(error); + }); + }); +}; + +const startEncodingAndWaitForItToBeFinished = (encoding) => { + const startPromise = bitmovin.encoding.encodings(encoding.id).start(); + + return new Promise((resolve, reject) => { + startPromise.then((startResponse) => { + waitUntilEncodingFinished(encoding).then((success) => { + console.log('dash encoding finished', success); + resolve(true); + }).catch((error) => { + console.log('dash encoding errored', error); + reject(error); + }) + }); + }); +}; + +const waitUntilEncodingFinished = (encoding) => { + return new Promise((resolve, reject) => { + const waitForEncodingToBeFinishedOrError = () => { + console.log('GET STATUS FOR ENCODING WITH ID ', encoding.id); + bitmovin.encoding.encodings(encoding.id).status().then((response) => { + console.log('Encoding status is ' + response.status); + + if (response.status === 'FINISHED') { + return resolve(response.status); + } + + if (response.status === 'ERROR') { + return reject(response.status); + } + + setTimeout(waitForEncodingToBeFinishedOrError, 10000); + }); + }; + waitForEncodingToBeFinishedOrError(); + }); +}; + +const exit = (code, message) => { + console.error('ERROR: ', message, 'Exiting with code ', code); + process.exit(code); +}; + +main().then((result) => { + console.log('finished!'); +}).catch((error) => { + exit(100, error); +}); diff --git a/examples/encoding/10_encoding_hls_aes128_subtitle.js b/examples/encoding/10_encoding_hls_aes128_subtitle.js new file mode 100644 index 0000000..0938d82 --- /dev/null +++ b/examples/encoding/10_encoding_hls_aes128_subtitle.js @@ -0,0 +1,443 @@ +// 08_encoding_hls_aes128.js + +const Promise = require('bluebird'); +const Bitmovin = require('bitmovin-javascript').default; + + +const BITMOVIN_API_KEY = ''; +const bitmovin = new Bitmovin({apiKey: BITMOVIN_API_KEY, debug: true}); + +const ENCODING_NAME = 'TEST_ENCODING_' + new Date().toISOString(); + +const INPUT_S3_ACCESS_KEY = ''; +const INPUT_S3_SECRET_KEY = ''; +const INPUT_S3_BUCKET_NAME = ''; +const INPUT_S3_PATH = '/path/to/your/input/file.mp4'; + +const OUTPUT_S3_ACCESS_KEY = ''; +const OUTPUT_S3_SECRET_KEY = ''; +const OUTPUT_S3_BUCKET_NAME = ''; +const OUTPUT_S3_BASE_PATH = '/path/to/your/output/destination'; + +const DRM_AES_KEY = ''; +const DRM_AES_IV = ''; + +const VTT_MEDIA_URL = ''; + +const main = () => { + return new Promise((resolve, reject) => { + + const s3InputCreationPromise = createInput(); + const s3OutputCreationPromise = createOutput(); + const encodingResourceCreationPromise = createEncoding(); + const codecConfigurationH264At720pPromise = createH264CodecConfiguration(1280, 720, 2400000, 25.0); + const codecConfigurationH264At480pPromise = createH264CodecConfiguration(854, 480, 1200000, 25.0); + const codecConfigurationH264At360pPromise = createH264CodecConfiguration(640, 360, 800000, 25.0); + const codecConfigurationH264At240pPromise = createH264CodecConfiguration(426, 240, 400000, 25.0); + const codecConfigurationAACPromise = createAACCodecConfiguration(128000, 48000); + + Promise.all( + [ + s3InputCreationPromise, + s3OutputCreationPromise, + encodingResourceCreationPromise, + codecConfigurationH264At720pPromise, + codecConfigurationH264At480pPromise, + codecConfigurationH264At360pPromise, + codecConfigurationH264At240pPromise, + codecConfigurationAACPromise + ] + ).then(([ + input, + output, + encoding, + codecConfigurationH264At720p, + codecConfigurationH264At480p, + codecConfigurationH264At360p, + codecConfigurationH264At240p, + codecConfigurationAAC + ]) => { + console.log('Successfully created input, output and codec configurations.'); + const hlsManifestCreationPromise = createHlsManifest(output.id); + + hlsManifestCreationPromise.then(hlsManifest => { + console.log('Successfully created HLS Manifest Resources'); + + const streamDefinitions = [ + { + codecConfiguration: codecConfigurationAAC, + outputPathDiscriminator: 'audio/128' + }, + { + codecConfiguration: codecConfigurationH264At240p, + outputPathDiscriminator: 'video/240' + }, + { + codecConfiguration: codecConfigurationH264At360p, + outputPathDiscriminator: 'video/360' + }, { + codecConfiguration: codecConfigurationH264At480p, + outputPathDiscriminator: 'video/480' + }, { + codecConfiguration: codecConfigurationH264At720p, + outputPathDiscriminator: 'video/720' + } + ]; + + const promiseMap = Promise.map(streamDefinitions, (streamDefinition) => { + return createStreamWithMuxingsAndDRMsAndManifestResources( + streamDefinition.codecConfiguration, input, output, streamDefinition.outputPathDiscriminator, encoding, hlsManifest + ); + }, {concurrency: 1}); + + promiseMap.then((result) => { + const vttMediaPromise = createHlsVttMedia(hlsManifest, VTT_MEDIA_URL); + vttMediaPromise.then(result => { + console.log('Successfully created all resources. Starting Encoding Process...'); + startEncodingAndWaitForItToBeFinished(encoding).then(() => { + console.log('Successfully Finished Encoding Process.. Starting manifest creation...'); + + const hlsCreationPromise = startHlsManifestCreationAndWaitForItToBeFinished(hlsManifest); + hlsCreationPromise.then(() => { + console.log('Successfully finished HLS manifest creation!'); + resolve(result); + }); + }); + }); + }).catch(logErrorPromise(reject, 'An error occurred: ')); + }); + }); + }); +}; + +const createInput = () => { + return new Promise((resolve, reject) => { + const s3Input = { + name: 'My awesome S3 Input', + description: 'Little description for my awesome S3 Input', + accessKey: INPUT_S3_ACCESS_KEY, + secretKey: INPUT_S3_SECRET_KEY, + bucketName: INPUT_S3_BUCKET_NAME + }; + bitmovin.encoding.inputs.s3.create(s3Input).then((createdInput) => { + console.log('Successfully created S3 Input.', createdInput); + resolve(createdInput); + }).catch(logErrorPromise(reject, 'Unable to create S3 Input.')); + }); +}; + +const createOutput = () => { + return new Promise((resolve, reject) => { + const s3Output = { + name: 'My awesome S3 Output', + description: 'Little description for my awesome S3 Output', + accessKey: OUTPUT_S3_ACCESS_KEY, + secretKey: OUTPUT_S3_SECRET_KEY, + bucketName: OUTPUT_S3_BUCKET_NAME + }; + bitmovin.encoding.outputs.s3.create(s3Output).then((createdOutput) => { + console.log('Successfully created S3 Output.', createdOutput); + resolve(createdOutput); + }).catch(logErrorPromise(reject, 'Unable to create S3 Output.')); + }); +}; + +const createEncoding = () => { + return new Promise((resolve, reject) => { + const encoding = { + name: ENCODING_NAME + }; + bitmovin.encoding.encodings.create(encoding).then((createdEncoding) => { + console.log('Successfully created Encoding resource.', createdEncoding); + resolve(createdEncoding); + }).catch(logErrorPromise(reject, 'Unable to create Encoding Resource.')); + }); +}; + +const createH264CodecConfiguration = (width, height, bitrate, fps) => { + return new Promise((resolve, reject) => { + const h264CodecConfiguration = { + name: 'H264 ' + height, + bitrate: bitrate, + rate: fps, + profile: 'HIGH', + width: width, + height: height + }; + bitmovin.encoding.codecConfigurations.h264.create(h264CodecConfiguration).then((createdCodecConfiguration) => { + console.log('Successfully created H264 Codec Configuration.', createdCodecConfiguration); + resolve(createdCodecConfiguration); + }).catch(logErrorPromise(reject, 'Unable to create H264 Codec Configuration')); + }); +}; + +const createAACCodecConfiguration = (bitrate, rate) => { + return new Promise((resolve, reject) => { + const aacCodecConfiguration = { + name: 'English', + bitrate: bitrate, + rate: rate + }; + bitmovin.encoding.codecConfigurations.aac.create(aacCodecConfiguration).then((createdCodecConfiguration) => { + console.log('Successfully created AAC Codec Configuration.', createdCodecConfiguration); + resolve(createdCodecConfiguration); + }).catch(logErrorPromise(reject, 'Unable to create AAC Codec Configuration')); + }); +}; + +const createHlsManifest = (outputId) => { + return new Promise((resolve, reject) => { + const hlsManifest = { + name: 'HLS Manifest for ' + ENCODING_NAME, + outputs: [{ + outputId: outputId, + outputPath: OUTPUT_S3_BASE_PATH, + acl: [{ + permission: 'PUBLIC_READ' + }] + }], + manifestName: 'hlsManifest.m3u8' + }; + bitmovin.encoding.manifests.hls.create(hlsManifest).then((createdManifest) => { + console.log('Successfully created HLS Manifest Resource.', createdManifest); + resolve(createdManifest); + }).catch(logErrorPromise(reject, 'Unable to create HLS manifest.')); + }); +}; + +const createStreamWithMuxingsAndDRMsAndManifestResources = (codecConfiguration, input, output, outputPath, encoding, hlsManifest) => { + return new Promise((resolve, reject) => { + const stream = { + name: 'Stream with ' + codecConfiguration.name, + codecConfigId: codecConfiguration.id, + inputStreams: [{ + inputId: input.id, + inputPath: INPUT_S3_PATH, + selectionMode: 'AUTO' + }] + }; + bitmovin.encoding.encodings(encoding.id).streams.add(stream).then((createdStream) => { + console.log('Successfully created Stream with Codec Configuration ' + codecConfiguration.name + '.', createdStream); + + const tsMuxingCreationPromise = createTSMuxingWithAESDRMAndManifestResources( + createdStream, outputPath, codecConfiguration, output, encoding, hlsManifest); + + tsMuxingCreationPromise.then(([createdTsMuxing, createdFairPlayDrm, createdHlsDrmRepresentation]) => { + resolve([ + createdStream, + [ + [createdTsMuxing, createdFairPlayDrm, createdHlsDrmRepresentation] + ] + ]); + }); + + }).catch(logErrorPromise(reject, 'Unable to create Stream with Codec Configuration ' + codecConfiguration.name + '.')); + }); +}; + +const createTSMuxingWithAESDRMAndManifestResources = (stream_, outputPath, codecConfiguration, output, encoding, hlsManifest) => { + return new Promise((resolve, reject) => { + const tsMuxing = { + name: 'TS Muxing for ' + stream_.name, + streams: [{ + streamId: stream_.id + }], + segmentLength: 4, + segmentNaming: 'seg_%number%.ts' + }; + + bitmovin.encoding.encodings(encoding.id).muxings.ts.add(tsMuxing).then((createdMuxing) => { + createAESDRMAndManifestResources(stream_, codecConfiguration, outputPath, createdMuxing, output, encoding, hlsManifest).then(([createdDrm, createdRepresentation]) => { + resolve([createdMuxing, createdDrm, createdRepresentation]); + }); + }).catch(logErrorPromise(reject, 'Unable to create TS Muxing ' + tsMuxing.name, + '.')); + }); +}; + +const createAESDRMAndManifestResources = (stream_, codecConfiguration, outputPath, tsMuxing, output, encoding, hlsManifest) => { + return new Promise((resolve, reject) => { + const drmOutputPath = OUTPUT_S3_BASE_PATH + '/' + outputPath + '/'; + const aesDRM = { + name: 'AES DRM for ' + tsMuxing.name, + outputs: [{ + outputId: output.id, + outputPath: drmOutputPath, + acl: [{ + permission: 'PUBLIC_READ' + }] + }], + method: 'AES_128', + key: DRM_AES_KEY, + iv: DRM_AES_IV + }; + + bitmovin.encoding.encodings(encoding.id).muxings.ts(tsMuxing.id).drms.aes.add(aesDRM).then((createdDrm) => { + console.log('Successfully created AES DRM ' + aesDRM.name + '.', createdDrm); + createHlsManifestRepresentation(encoding, stream_, tsMuxing, createdDrm, hlsManifest, drmOutputPath).then((createdRepresentation) => { + resolve([createdDrm, createdRepresentation]); + }); + }).catch(error => { + + logErrorPromise(reject, 'Unable to create AES DRM ' + aesDRM.name + '.') + }); + }); +}; + +const createHlsManifestRepresentation = (encoding, stream_ , tsMuxing, fairPlayDrm, hlsManifest, segmentPath) => { + return new Promise((resolve, reject) => { + let representationPromise = null; + if (segmentPath.includes('audio/')) { + representationPromise = createHlsAudioMedia(encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath); + } else { + representationPromise = createHlsVariantStream(encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath); + } + + representationPromise.then((response) => { + resolve(response); + }).catch((error) => { + reject(error); + }) + }); +}; + +const createHlsVttMedia = (hlsManifest, vttUrl) => { + return new Promise((resolve, reject) => { + const vttMedia = { + name: 'HLS Vtt Media', + groupId: 'subtitles_group', + language: 'en', + vttUrl, + uri: 'vtt_media.m3u8' + }; + + bitmovin.encoding.manifests.hls(hlsManifest.id).media.vtt.add(vttMedia).then((createdVttMedia) => { + console.log('Successfully created HLS VTT Media', createdVttMedia); + resolve(createdVttMedia); + }).catch(logErrorPromise(reject, 'Unable to create HLS VTT Media')); + }); +}; + +const createHlsAudioMedia = (encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath) => { + return new Promise((resolve, reject) => { + const audioMedia = { + name: 'HLS Audio Media', + groupId: 'audio_group', + segmentPath: segmentPath, + encodingId: encoding.id, + streamId: stream_.id, + muxingId: tsMuxing.id, + drmId: fairPlayDrm.id, + language: 'en', + uri: 'audio_media.m3u8' + }; + + bitmovin.encoding.manifests.hls(hlsManifest.id).media.audio.add(audioMedia).then((createdAudioMedia) => { + console.log('Successfully created HLS Audio Media', createdAudioMedia); + resolve(createdAudioMedia); + }).catch(logErrorPromise(reject, 'Unable to create HLS Audio Media')); + }); +}; +const createHlsVariantStream = (encoding, stream_, tsMuxing, fairPlayDrm, hlsManifest, segmentPath) => { + return new Promise((resolve, reject) => { + const variantStream = { + audio: 'audio_group', + subtitles: 'subtitles_group', + closedCaptions: 'NONE', + segmentPath: segmentPath, + encodingId: encoding.id, + streamId: stream_.id, + muxingId: tsMuxing.id, + drmId: fairPlayDrm.id, + uri: fairPlayDrm.id + '.m3u8' + }; + + bitmovin.encoding.manifests.hls(hlsManifest.id).streams.add(variantStream).then((createdVariantStream) => { + console.log('Successfully created Variant Stream for DRM with ID ' + fairPlayDrm.id + '.', createdVariantStream); + resolve(createdVariantStream); + }).catch(logErrorPromise(reject, 'Unable to create Variant Stream for DRM with ID ' + fairPlayDrm.id + '.')); + }); +}; + +const startEncodingAndWaitForItToBeFinished = (encoding) => { + const startPromise = bitmovin.encoding.encodings(encoding.id).start(); + + return new Promise((resolve, reject) => { + startPromise.then((startResponse) => { + waitUntilEncodingFinished(encoding).then((success) => { + console.log('Encoding finished', success); + resolve(true); + }).catch(logErrorPromise(reject, 'Encoding failed')); + }); + }); +}; + +const waitUntilEncodingFinished = (encoding) => { + return new Promise((resolve, reject) => { + const waitForEncodingToBeFinishedOrError = () => { + console.log('Getting Status for Encoding with ID ', encoding.id); + bitmovin.encoding.encodings(encoding.id).status().then((response) => { + console.log('Encoding Status is ' + response.status + '.'); + + if (response.status === 'FINISHED') { + return resolve(response.status); + } + + if (response.status === 'ERROR') { + return reject(response.status); + } + + setTimeout(waitForEncodingToBeFinishedOrError, 10000); + }); + }; + waitForEncodingToBeFinishedOrError(); + }); +}; + +const startHlsManifestCreationAndWaitForItToBeFinished = (manifest) => { + const startPromise = bitmovin.encoding.manifests.hls(manifest.id).start(); + + return new Promise((resolve, reject) => { + startPromise.then((startResponse) => { + waitUntilHlsManifestFinished(manifest).then((success) => { + console.log('hls manifest finished', success); + resolve(true); + }).catch(logErrorPromise(reject, 'HLS Manifest creation failed')); + }); + }); +}; + +const waitUntilHlsManifestFinished = (manifest) => { + return new Promise((resolve, reject) => { + const waitForManifestToBeFinished = () => { + console.log('Getting Status for HLS Manifest with ID ', manifest.id); + bitmovin.encoding.manifests.hls(manifest.id).status().then((response) => { + console.log('HLS Manifest Status is ' + response.status); + + if (response.status === 'FINISHED') { + return resolve(response.status); + } + + if (response.status === 'ERROR') { + return reject(response.status); + } + + setTimeout(waitForManifestToBeFinished, 10000); + }); + }; + waitForManifestToBeFinished(); + }); +}; + +const logErrorPromise = (reject, errorMessage) => { + return (error) => { + console.error(errorMessage, error); + reject(error); + }; +}; + +main().then(() => { + console.log('finished!'); +}).catch((error) => { + console.error('ERROR!', error); + process.exit(8); +}); diff --git a/examples/encoding/package.json b/examples/encoding/package.json index ed5fefb..2946f4f 100644 --- a/examples/encoding/package.json +++ b/examples/encoding/package.json @@ -2,7 +2,7 @@ "name": "bitmovin-javascript-examples", "version": "0.0.1", "dependencies": { - "bitmovin-javascript": "1.1.19", + "bitmovin-javascript": "1.2.2", "bluebird": "^3.4.7" } } diff --git a/package.json b/package.json index 87750af..117e200 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitmovin-javascript", - "version": "1.1.19", + "version": "1.2.2", "scripts": { "lint": "./node_modules/eslint/bin/eslint.js --color ./bitmovin", "test": "node scripts/testRunner.js tests/", diff --git a/tests/account/organizations/groups.test.js b/tests/account/organizations/groups.test.js new file mode 100644 index 0000000..7c355d0 --- /dev/null +++ b/tests/account/organizations/groups.test.js @@ -0,0 +1,50 @@ +import urljoin from 'url-join'; +import { groups } from '../../../bitmovin/account/organizations/groups'; + +import { + mockGet, + mockPost, + mockDelete, + mockHttp, + assertItReturnsUnderlyingPromise, + assertItCallsCorrectUrl, + testSetup, +} from '../../assertions'; + +import {getConfiguration} from '../../utils'; + +let testConfiguration = getConfiguration(); + +describe('account', () => { + beforeEach(testSetup); + describe('organizations', () => { + describe('groups', () => { + const testOrgId = '123'; + const client = groups(testConfiguration, testOrgId, mockHttp); + + describe('list', () => { + assertItCallsCorrectUrl('GET', urljoin('/v1/account/organizations', testOrgId, 'groups'), client.list); + assertItReturnsUnderlyingPromise(mockGet, client.list); + }); + + describe('add', () => { + assertItCallsCorrectUrl('POST', urljoin('/v1/account/organizations', testOrgId, 'groups'), client.add); + assertItReturnsUnderlyingPromise(mockPost, client.add); + }); + + describe('group', () => { + const testGroupId = '123'; + + describe('details', () => { + assertItCallsCorrectUrl('GET', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId), client(testGroupId).details); + assertItReturnsUnderlyingPromise(mockGet, client(testOrgId).details); + }); + describe('delete', () => { + assertItCallsCorrectUrl('DELETE', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId), client(testGroupId).delete); + assertItReturnsUnderlyingPromise(mockDelete, client(testOrgId).delete); + }); + }); + }); + }); +}); + diff --git a/tests/account/organizations/permissions.test.js b/tests/account/organizations/permissions.test.js new file mode 100644 index 0000000..e7bc9c7 --- /dev/null +++ b/tests/account/organizations/permissions.test.js @@ -0,0 +1,56 @@ +import urljoin from 'url-join'; +import { permissions } from '../../../bitmovin/account/organizations/permissions'; + +import { + mockGet, + mockPost, + mockDelete, + mockHttp, + assertItReturnsUnderlyingPromise, + assertItCallsCorrectUrl, + testSetup, +} from '../../assertions'; + +import {getConfiguration} from '../../utils'; + +let testConfiguration = getConfiguration(); + +describe('account', () => { + beforeEach(testSetup); + describe('organizations', () => { + describe('groups', () => { + describe('permissions', () => { + const testOrgId = '123'; + const testGroupId = '123'; + + const client = permissions(testConfiguration, testOrgId, testGroupId, mockHttp); + + describe('list', () => { + assertItCallsCorrectUrl('GET', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'permissions'), client.list); + assertItReturnsUnderlyingPromise(mockGet, client.list); + }); + + describe('add', () => { + assertItCallsCorrectUrl('POST', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'permissions'), client.add); + assertItReturnsUnderlyingPromise(mockPost, client.add); + }); + + describe('permission', () => { + const testPermissionId = '123'; + + describe('details', () => { + assertItCallsCorrectUrl('GET', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'permissions', testPermissionId), + client(testGroupId).details); + assertItReturnsUnderlyingPromise(mockGet, client(testOrgId).details); + }); + describe('delete', () => { + assertItCallsCorrectUrl('DELETE', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'permissions', testPermissionId), + client(testGroupId).delete); + assertItReturnsUnderlyingPromise(mockDelete, client(testOrgId).delete); + }); + }); + }); + }); + }); +}); + diff --git a/tests/account/organizations/tenants.test.js b/tests/account/organizations/tenants.test.js new file mode 100644 index 0000000..19d7101 --- /dev/null +++ b/tests/account/organizations/tenants.test.js @@ -0,0 +1,56 @@ +import urljoin from 'url-join'; +import { tenants } from '../../../bitmovin/account/organizations/tenants'; + +import { + mockGet, + mockPost, + mockDelete, + mockHttp, + assertItReturnsUnderlyingPromise, + assertItCallsCorrectUrl, + testSetup, +} from '../../assertions'; + +import {getConfiguration} from '../../utils'; + +let testConfiguration = getConfiguration(); + +describe('account', () => { + beforeEach(testSetup); + describe('organizations', () => { + describe('groups', () => { + describe('tenants', () => { + const testOrgId = '123'; + const testGroupId = '123'; + + const client = tenants(testConfiguration, testOrgId, testGroupId, mockHttp); + + describe('list', () => { + assertItCallsCorrectUrl('GET', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'tenants'), client.list); + assertItReturnsUnderlyingPromise(mockGet, client.list); + }); + + describe('add', () => { + assertItCallsCorrectUrl('POST', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'tenants'), client.add); + assertItReturnsUnderlyingPromise(mockPost, client.add); + }); + + describe('tenant', () => { + const testTenantId = '123'; + + describe('details', () => { + assertItCallsCorrectUrl('GET', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'tenants', testTenantId), + client(testGroupId).details); + assertItReturnsUnderlyingPromise(mockGet, client(testOrgId).details); + }); + describe('delete', () => { + assertItCallsCorrectUrl('DELETE', urljoin('/v1/account/organizations', testOrgId, 'groups', testGroupId, 'tenants', testTenantId), + client(testGroupId).delete); + assertItReturnsUnderlyingPromise(mockDelete, client(testOrgId).delete); + }); + }); + }); + }); + }); +}); + diff --git a/tests/analytics/queries.test.js b/tests/analytics/queries.test.js index 8f6175d..6cb70d3 100644 --- a/tests/analytics/queries.test.js +++ b/tests/analytics/queries.test.js @@ -60,6 +60,7 @@ describe('analytics', () => { const end = moment().toDate(); const testBuilderFunction = (func) => { const fn = func('STARTUPTIME') + .licenseKey('license-key') .between(start, end) .interval('DAY') .filter('STARTUPTIME', 'GT', 0) @@ -71,6 +72,7 @@ describe('analytics', () => { assertItReturnsPromise(mockPost, () => { return fn.query() }); assertPayload(mockPost, () => { return fn.query() }, { dimension: 'STARTUPTIME', + licenseKey: 'license-key', start: start, end: end, filters: [ diff --git a/tests/utils.js b/tests/utils.js index 132e363..b55e4bf 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -11,7 +11,7 @@ const getConfiguration = () => { 'X-Api-Key' : settings.apiKey, 'X-Tenant-Org-Id' : settings.tenantOrgId, 'X-Api-Client' : 'bitmovin-javascript', - 'X-Api-Client-Version': '0.0.1' + 'X-Api-Client-Version': '1.1.20' }, eMail: settings.eMail, password: settings.password