diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..ae93e56 --- /dev/null +++ b/.nycrc @@ -0,0 +1,14 @@ +{ +"exclude": [ + "test/" + ], + "reporter": [ + "text", + "html" + ], + "require": [ + "babel-register" + ], + "sourceMap": false, + "instrument": false +} diff --git a/flow-typed/interfaces/loader.js b/flow-typed/interfaces/loader.js new file mode 100644 index 0000000..e254b27 --- /dev/null +++ b/flow-typed/interfaces/loader.js @@ -0,0 +1,9 @@ +//@flow +import RequestBuilder from '../../src/k-provider/request-builder' + +declare interface ILoader { + static name: string; + requests: Array; + response: any; + isValid(): boolean; +} diff --git a/package.json b/package.json index a256e39..d8c910e 100644 --- a/package.json +++ b/package.json @@ -65,4 +65,4 @@ "url": "https://github.com/kaltura/playkit-js-providers/issues" }, "homepage": "https://github.com/kaltura/playkit-js-providers#readme" -} \ No newline at end of file +} diff --git a/src/entities/drm.js b/src/entities/drm.js new file mode 100644 index 0000000..b0d174d --- /dev/null +++ b/src/entities/drm.js @@ -0,0 +1,31 @@ +//@flow + +import {Scheme} from '../k-provider/enums'; + +/** + * Drm data + * @classdesc + */ +export default class Drm { + + /** + * @member - license URL + * @type {string} + */ + licenseUrl: string; + /** + * @member - drm scheme + * @type {Scheme} + */ + scheme: Scheme; + + /** + * @constructor + * @param {string} licenseUrl - the license URL + * @param {Scheme} scheme - the drm scheme + */ + constructor(licenseUrl: string, scheme: Scheme) { + this.licenseUrl = licenseUrl; + this.scheme = scheme; + } +} diff --git a/src/entities/media-entry.js b/src/entities/media-entry.js new file mode 100644 index 0000000..48e9004 --- /dev/null +++ b/src/entities/media-entry.js @@ -0,0 +1,46 @@ +//@flow +import MediaSource from './media-source' +import {MediaEntryType} from '../k-provider/enums' + +/** + * Media entry + * @classdesc + */ +export default class MediaEntry { + + /** + * @member - entry ID + * @type {string} + */ + id: string; + /** + * @member - entry sources + * @type {Array} + */ + sources: Array; + /** + * @member - entry duration + * @type {number} + */ + duration: number; + /** + * @member - entry type + * @type {MediaEntryType} + */ + type: MediaEntryType; + /** + * @member - entry metadata + * @type {Map} + */ + metaData: Map; + + /** + * @constructor + */ + constructor() { + this.metaData = new Map(); + this.type = MediaEntryType.Unknown; + } + + +} diff --git a/src/entities/media-format.js b/src/entities/media-format.js new file mode 100644 index 0000000..7b016a4 --- /dev/null +++ b/src/entities/media-format.js @@ -0,0 +1,50 @@ +//@flow +import {Enum} from 'enumify'; + +export class MediaFormat extends Enum { +} +MediaFormat.initEnum({ + dash: { + get mimeType() { + return "application/dash+xml"; + }, + get pathExt() { + return "mpd"; + } + }, + hls: { + get mimeType() { + return "application/x-mpegURL"; + }, + get pathExt() { + return "m3u8"; + } + }, + wvm: { + get mimeType() { + return "video/wvm"; + }, + get pathExt() { + return "wvm"; + } + }, + mp4: { + get mimeType() { + return "video/mp4"; + }, + get pathExt() { + return "mp4"; + } + }, + mp3: { + get mimeType() { + return "audio/mpeg"; + }, + get pathExt() { + return "mp3"; + } + } +}); + + + diff --git a/src/entities/media-source.js b/src/entities/media-source.js new file mode 100644 index 0000000..a6051a7 --- /dev/null +++ b/src/entities/media-source.js @@ -0,0 +1,35 @@ +//@flow +import Drm from '../entities/drm' + +/** + * Media source + * @classdesc + */ +export default class MediaSource { + /** + * @member - media source ID + * @type {string} + */ + id: string; + /** + * @member - media source URL + * @type {string} + */ + src: string; + /** + * @member - media source mimetype + * @type {string} + */ + mimetype: string; + /** + * @member - media source drm data + * @type {Array} + */ + drmData: Array; + + /** + * @constructor + */ + constructor(){} +} + diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..5becbc0 --- /dev/null +++ b/src/index.html @@ -0,0 +1,14 @@ + + + + + + Title + + + + + + + + diff --git a/src/k-provider/base-service-result.js b/src/k-provider/base-service-result.js new file mode 100644 index 0000000..b9ca1f4 --- /dev/null +++ b/src/k-provider/base-service-result.js @@ -0,0 +1,65 @@ +//@flow + +/** + * Base service result + * @classdesc + */ +export default class ServiceResult { + + /** + * @member - Is service returned an error + * @type {boolean} + */ + hasError: boolean = false; + /** + * @member - The service error + * @type {ServiceError} + */ + error: ServiceError; + /** + * @member - The service result data + * @type {Object} + */ + data: Object; + + /** + * @constructor + * @param {Object} response - Service response + */ + constructor(response: Object) { + if (response.objectType === "KalturaAPIException") { + this.hasError = true; + this.error = new ServiceError(response.code, response.message); + } + else { + this.data = response; + } + } +} + +/** + * Service error + * @classdesc + */ +class ServiceError { + /** + * @member - The error code + * @type {string} + */ + code: string; + /** + * @member - The error message + * @type {string} + */ + message: string; + + /** + * @constructor + * @param {string} code - The result code + * @param {string} message - The result message + */ + constructor(code: string, message: string) { + this.code = code; + this.message = message; + } +} diff --git a/src/k-provider/enums.js b/src/k-provider/enums.js new file mode 100644 index 0000000..9483f1f --- /dev/null +++ b/src/k-provider/enums.js @@ -0,0 +1,165 @@ +//@flow + +import {Enum} from 'enumify'; + +export class Scheme extends Enum { +} +Scheme.initEnum({ + PLAYREADY_CENC: "drm.PLAYREADY_CENC", + WIDEVINE_CENC: "drm.WIDEVINE_CENC", + FAIRPLAY: "fairplay.FAIRPLAY", + PLAYREADY: "playReady.PLAYREADY", + WIDEVINE: "widevine.WIDEVINE" +}); + + +export class KalturaRuleActionType extends Enum { +} +KalturaRuleActionType.initEnum({ + DRM_POLICY: "drm.DRM_POLICY", + BLOCK: 1, + PREVIEW: 2, + LIMIT_FLAVORS: 3, + ADD_TO_STORAGE: 4, + LIMIT_DELIVERY_PROFILES: 5, + SERVE_FROM_REMOTE_SERVER: 6, + REQUEST_HOST_REGEX: 7, + LIMIT_THUMBNAIL_CAPTURE: 8 +}); + +export class Status extends Enum { +} +Status.initEnum({ + ERROR: -1, + QUEUED: 0, + CONVERTING: 1, + READY: 2, + DELETED: 3, + NOT_APPLICABLE: 4, + TEMP: 5, + WAIT_FOR_CONVERT: 6, + IMPORTING: 7, + VALIDATING: 8, + EXPORTING: 9 +}); + +export class MetadataObjectType extends Enum { +} +MetadataObjectType.initEnum({ + AD_CUE_POINT: "adCuePointMetadata.AdCuePoint", + ANNOTATION: "annotationMetadata.Annotation", + CODE_CUE_POINT: "codeCuePointMetadata.CodeCuePoint", + THUMB_CUE_POINT: "thumbCuePointMetadata.thumbCuePoint", + ENTRY: 1, + CATEGORY: 2, + USER: 3, + PARTNER: 4, + DYNAMIC_OBJECT: 5 +}); + +export class MetadataStatus extends Enum { +} +MetadataStatus.initEnum({ + VALID: 1, + INVALID: 2, + DELETED: 3 +}); + + +export class EntryStatus extends Enum { +} +EntryStatus.initEnum({ + ERROR_IMPORTING: -2, + ERROR_CONVERTING: -1, + SCAN_FAILURE: "virusScan.ScanFailure", + IMPORT: 0, + INFECTED: "virusScan.Infected", + PRECONVERT: 1, + READY: 2, + DELETED: 3, + PENDING: 4, + MODERATE: 5, + BLOCKED: 6, + NO_CONTENT: 7 +}); + +export class EntryModerationStatus extends Enum { +} +EntryModerationStatus.initEnum({ + PENDING_MODERATION: 1, + APPROVED: 2, + REJECTED: 3, + FLAGGED_FOR_REVIEW: 4, + MODERATE: 5, + AUTO_APPROVED: 6 +}); + +export class EntryType extends Enum { +} +EntryType.initEnum({ + AUTOMATIC: {value: -1}, + EXTERNAL_MEDIA: {value: "externalMedia.externalMedia"}, + MEDIA_CLIP: {value: 1}, + MIX: {value: 2}, + PLAYLIST: {value: 5}, + DATA: {value: 6}, + LIVE_STREAM: {value: 7}, + LIVE_CHANNEL: {value: 8}, + DOCUMENT: {value: 10} +}); + +export class MediaType extends Enum { +} +MediaType.initEnum({ + VIDEO: {value: 1}, + IMAGE: {value: 2}, + AUDIO: {value: 5}, + LIVE_STREAM_FLASH: {value: 201}, + LIVE_STREAM_WINDOWS_MEDIA: {value: 202}, + LIVE_STREAM_REAL_MEDIA: {value: 203}, + LIVE_STREAM_QUICKTIME: {value: 204} +}); + + +export class MediaEntryType extends Enum { +} +MediaEntryType.initEnum(['Vod', 'Live', 'Image', 'Audio', 'Unknown']); + +export class UIConfType extends Enum { +} +UIConfType.initEnum({ + PLAYER: 1, + CONTRIBUTION_WIZARD: 2, + SIMPLE_EDITOR: 3, + ADVANCED_EDITOR: 4, + PLAYLIST: 5, + APP_STUDIO: 6, + KRECORD: 7, + PLAYER_V3: 8, + KMC_ACCOUNT: 9, + KMC_ANALYTICS: 10, + KMC_CONTENT: 11, + KMC_DASHBOARD: 12, + KMC_LOGIN: 13, + PLAYER_SL: 14, + CLIENTSIDE_ENCODER: 15, + KMC_GENERAL: 16, + KMC_ROLES_AND_PERMISSIONS: 17, + CLIPPER: 18, + KSR: 19, + KUPLOAD: 20, + WEBCASTING: 21 +}); + +export class UIConfCreationMode extends Enum { +} +UIConfCreationMode.initEnum({ + WIZARD: 2, + ADVANCED: 3 +}); + + + + + + diff --git a/src/k-provider/multi-request-builder.js b/src/k-provider/multi-request-builder.js new file mode 100644 index 0000000..058d591 --- /dev/null +++ b/src/k-provider/multi-request-builder.js @@ -0,0 +1,104 @@ +//@flow +import RequestBuilder from './request-builder' +import ServiceResult from './base-service-result' +import Logger from "../util/logger"; +/** + * @constant + */ +const logger = Logger.get("OvpProvider"); + +/** + * Multi Request builder + * @classdesc + */ +export default class MultiRequestBuilder extends RequestBuilder { + + /** + * @member - Array of requests + * @type {Array} + */ + requests: Array = []; + + /** + * @constructor + */ + constructor() { + super(); + } + + /** + * Adds request to requests array + * @function add + * @param {RequestBuilder} request The request + * @returns {MultiRequestBuilder} The multiRequest + */ + add(request: RequestBuilder): MultiRequestBuilder { + this.requests.push(request); + let requestParams = {}; + let serviceDef: Object = {service: request.service, action: request.action}; + Object.assign(requestParams, {[this.requests.length]: Object.assign(serviceDef, request.params)}); + Object.assign(requestParams, this.params); + this.params = requestParams; + return this; + } + + /** + * Executes a multi request + * @function execute + * @returns {Promise} The multirequest execution promisie + */ + execute(): Promise { + try { + this.params = JSON.stringify(this.params); + } + catch (err) { + logger.error(`${err.message}`); + } + return new Promise((resolve, reject) => { + this.doHttpRequest().then(data => { + resolve(new MultiRequestResult(data)); + }, + err => { + let errorText: string = `Error on multiRequest execution, error <${err}>.`; + reject(errorText); + + }); + }); + } + +} + +/** + * Multi Request result object + * @classdesc + */ +export class MultiRequestResult { + + /** + * @member - Is success + * @type {boolean} + */ + success: boolean; + /** + * @member - Multi request response data + * @type {Object} + */ + results: Array = []; + + /** + * @constructor + * @param {Object} response data + */ + constructor(response: Object) { + this.success = true; + response.forEach((result) => { + let serviceResult: ServiceResult = new ServiceResult(result); + this.results.push(serviceResult); + if (serviceResult.hasError) { + logger.error(`Service returned an error with error code: ${serviceResult.error.code} and message: ${serviceResult.error.message}.`); + this.success = false; + return; + } + }); + } +} diff --git a/src/k-provider/ott/config.js b/src/k-provider/ott/config.js new file mode 100644 index 0000000..d325a5d --- /dev/null +++ b/src/k-provider/ott/config.js @@ -0,0 +1,10 @@ +//@flow +const BE_URL: string = ""; +const SERVICE_CONFIG_PARAMAS: Object = { + clientTag: "playkit-js", + apiVersion: '3.3.0', + format: 1 +}; + +export {BE_URL, SERVICE_CONFIG_PARAMAS}; + diff --git a/src/k-provider/ott/ott-provider.js b/src/k-provider/ott/ott-provider.js index e69de29..75eaf61 100644 --- a/src/k-provider/ott/ott-provider.js +++ b/src/k-provider/ott/ott-provider.js @@ -0,0 +1,10 @@ +//@flow +export default class OttProvider { + constructor() { + } + + getConfig(): void { + } +} + + diff --git a/src/k-provider/ott/services/ott-service.js b/src/k-provider/ott/services/ott-service.js new file mode 100644 index 0000000..7691189 --- /dev/null +++ b/src/k-provider/ott/services/ott-service.js @@ -0,0 +1,16 @@ +//@flow +import MultiRequestBuilder from '../../multi-request-builder' +import * as config from '../config' + +export default class OttService { + static getMultirequest(ks: string): MultiRequestBuilder { + let ottParams = config.SERVICE_CONFIG_PARAMAS; + Object.assign(ottParams, {ks: ks}); + let multiReq = new MultiRequestBuilder(); + multiReq.method = "POST"; + multiReq.service = "multirequest"; + multiReq.baseUrl = config.BE_URL; + multiReq.params = ottParams; + return multiReq; + } +} diff --git a/src/k-provider/ovp/config.js b/src/k-provider/ovp/config.js new file mode 100644 index 0000000..63b3dea --- /dev/null +++ b/src/k-provider/ovp/config.js @@ -0,0 +1,26 @@ +//@flow + +const defaultConfig: Object = { + beUrl: "http://www.kaltura.com/api_v3", + baseUrl: "https://cdnapisec.kaltura.com", + serviceParams: { + clientTag: "playkit-js", + apiVersion: '3.3.0', + format: 1 + } +}; + +export default class Configuration { + + static set(clientConfig?: Object) { + if (clientConfig) { + Object.assign(defaultConfig, clientConfig); + } + } + + static get(): Object { + return defaultConfig; + } +} + + diff --git a/src/k-provider/ovp/loaders/data-loader-manager.js b/src/k-provider/ovp/loaders/data-loader-manager.js new file mode 100644 index 0000000..0880c37 --- /dev/null +++ b/src/k-provider/ovp/loaders/data-loader-manager.js @@ -0,0 +1,116 @@ +//@flow +import OvpService from '../services/ovp-service' +import MultiRequestBuilder from '../../multi-request-builder' +import {MultiRequestResult} from '../../multi-request-builder' + +/** + * Data loaders manager + * @classdesc + */ +export default class DataLoaderManager { + /** + * @member - Lodaers response map index + * @type {Map>} + * @private + * @static + */ + static _loadersResponseMap: Map> = new Map(); + /** + * @member - Loaders multi request + * @type {MultiRequestBuilder} + * @private + */ + _multiRequest: MultiRequestBuilder; + /** + * @member - Loaders multi response + * @type {MultiRequestResult} + * @private + */ + _multiResponse: MultiRequestResult; + /** + * @member - Loaders to execute + * @type {Map} + * @private + */ + _loaders: Map = new Map(); + + /** + * @constructor + * @param {string} partnerID Then partner ID + * @param {string} ks The ks + */ + constructor(partnerID: number, ks: string = "") { + this._multiRequest = OvpService.getMultirequest(ks, partnerID); + } + + /** + * Add loader too execution loaders map + * @function + * @param {Function} loader Loader to add + * @param {Object} params Loader params + * @returns {void} + */ + add(loader: typeof ILoader, params: Object): void { + let execution_loader = new loader(params); + if (execution_loader.isValid()) { + this._loaders.set(loader.name, execution_loader); + //Get the start index from the multiReqeust before adding current execution_loader requests + let startIndex = this._multiRequest.requests.length; + //Get the requests + let requests = execution_loader.requests; + //Add requests to muktiRequest queue + requests.forEach((request) => { + this._multiRequest.add(request); + }); + //Create range array of current execution_loader requests + let executionLoaderResponseMap = Array.from(new Array(requests.length), (val, index) => index + startIndex); + //Add to map + DataLoaderManager._loadersResponseMap.set(loader.name, executionLoaderResponseMap); + } + } + + /** + * Get data from all loaders using multi request + * @function + * @returns {Promise} Promise + */ + fetchData(): Promise { + return new Promise((resolve, reject) => { + this._multiRequest.execute() + .then(response => { + this._multiResponse = response; + if (!response.success) { + reject(response); + } + else { + let preparedData: Object = this.prepareData(response); + if (preparedData.success) { + resolve(this._loaders); + } + else { + reject({success: false, data: preparedData.error}); + } + } + }, + err => { + reject(err); + }); + }); + } + + prepareData(response: MultiRequestResult): Object { + this._loaders.forEach(function (loader, name) { + let loaderDataIndexes = DataLoaderManager._loadersResponseMap.get(name); + try { + if (loaderDataIndexes != null) { + loader.response = (response.results.slice(loaderDataIndexes[0], loaderDataIndexes[loaderDataIndexes.length - 1] + 1)); + } + } + catch (err) { + return {success: false, error: err}; + } + }); + return {success: true, data: this._loaders}; + } +} + diff --git a/src/k-provider/ovp/loaders/media-entry-loader.js b/src/k-provider/ovp/loaders/media-entry-loader.js new file mode 100644 index 0000000..c2e2fbf --- /dev/null +++ b/src/k-provider/ovp/loaders/media-entry-loader.js @@ -0,0 +1,77 @@ +//@flow + +import RequestBuilder from '../../request-builder' +import BaseEntryService from '../services/base-entry-service' +import MetaDataService from '../services/meta-data-service' +import Configuration from '../config' +import KalturaPlaybackContext from '../response-types/kaltura-playback-context' +import KalturaMetadataListResponse from '../response-types/kaltura-metadata-list-response' +import KalturaBaseEntryListResponse from '../response-types/kaltura-base-entry-list-response' + +const config = Configuration.get(); + +/** + * Media entry loader + * @classdesc + */ +export default class MediaEntryLoader implements ILoader { + static get name(): string { + return "media"; + } + + _entryId: string; + _requests: Array; + _response: any = {}; + + /** + * @constructor + * @param {Object} params loader params + */ + constructor(params: Object) { + this.requests = this.buildRequests(params); + this._entryId = params.entryId; + } + + set requests(requests: Array) { + this._requests = requests; + } + + get requests(): Array { + return this._requests; + } + + set response(response: any) { + let mediaEntryResponse: KalturaBaseEntryListResponse = new KalturaBaseEntryListResponse(response[0].data); + this._response.entry = mediaEntryResponse.entries[0]; + this._response.playBackContextResult = new KalturaPlaybackContext(response[1].data); + this._response.metadataListResult = new KalturaMetadataListResponse(response[2].data); + } + + get response(): any { + return this._response; + } + + /** + * Builds loader requests + * @function + * @param {Object} params Requests parameters + * @returns {RequestBuilder} The request builder + * @static + */ + buildRequests(params: Object): Array { + let requests: Array = []; + requests.push(BaseEntryService.list(config.beUrl, params.ks, params.entryId)); + requests.push(BaseEntryService.getPlaybackContext(config.beUrl, params.ks, params.entryId)); + requests.push(MetaDataService.list(config.beUrl, params.ks, params.entryId)); + return requests; + } + + /** + * Loader validation function + * @function + * @returns {boolean} Is valid + */ + isValid(): boolean { + return !!this._entryId; + } +} diff --git a/src/k-provider/ovp/loaders/session-loader.js b/src/k-provider/ovp/loaders/session-loader.js new file mode 100644 index 0000000..b9bcf0c --- /dev/null +++ b/src/k-provider/ovp/loaders/session-loader.js @@ -0,0 +1,71 @@ +//@flow +import SessionService from '../services/session-service' +import Configuration from '../config' +import RequestBuilder from '../../request-builder' + +const config = Configuration.get(); +/** + * Media entry loader + * @classdesc + */ +export default class SessionLoader implements ILoader { + static get name(): string { + return "session"; + } + + /** + * @member - partner ID + * @type {number} + * @private + */ + _partnerId: number; + _requests: Array; + _response: any = {}; + + /** + * @constructor + * @param {Object} params loader params + */ + constructor(params: Object) { + this.requests = this.buildRequests(params); + this._partnerId = params.partnerId; + } + + set requests(requests: Array) { + this._requests = requests; + } + + get requests(): Array { + return this._requests; + } + + set response(response: any) { + this._response.ks = response[0].data.ks; + } + + get response(): any { + return this._response.ks; + } + + /** + * Builds loader requests + * @function + * @param {Object} params Requests parameters + * @returns {RequestBuilder} The request builder + * @static + */ + buildRequests(params: Object): Array { + let requests: Array = []; + requests.push(SessionService.anonymousSession(config.beUrl, params.partnerId)); + return requests; + } + + /** + * Loader validation function + * @function + * @returns {boolean} Is valid + */ + isValid(): boolean { + return !!this._partnerId; + } +} diff --git a/src/k-provider/ovp/loaders/ui-config-loader.js b/src/k-provider/ovp/loaders/ui-config-loader.js new file mode 100644 index 0000000..d3468c2 --- /dev/null +++ b/src/k-provider/ovp/loaders/ui-config-loader.js @@ -0,0 +1,77 @@ +//@flow +import UiConfService from '../services/ui-conf-service' +import KalturaUiConfResponse from '../response-types/kalturaUIConfResponse' +import Configuration from '../config' +import RequestBuilder from '../../request-builder' + +const config = Configuration.get(); + +export default class UiConfigLoader implements ILoader { + static get name(): string { + return "uiConf"; + } + + /** + * @member - uiConf ID + * @type {number} + * @private + */ + _uiConfId: number; + _requests: Array; + _response: any = {}; + + /** + * @constructor + * @param {Object} params loader params + */ + constructor(params: Object) { + this.requests = this.buildRequests(params); + this._uiConfId = params.uiConfId; + } + + set requests(requests: Array) { + this._requests = requests; + } + + get requests(): Array { + return this._requests; + } + + set response(response: any) { + this._response.uiConf = new KalturaUiConfResponse(response[0].data); + } + + get response(): any { + if (this._response != null && this._response.uiConf != null && this._response.uiConf.config != null) + try { + return JSON.parse(this._response.uiConf.config).plugins; + } + catch (err) { + return null; + } + else + return null; + } + + /** + * Builds loader requests + * @function + * @param {Object} params Requests parameters + * @returns {RequestBuilder} The request builder + * @static + */ + buildRequests(params: Object): Array { + let requests: Array = []; + requests.push(UiConfService.get(config.beUrl, params.ks, params.uiConfId)); + return requests; + } + + /** + * Loader validation function + * @function + * @returns {boolean} Is valid + */ + isValid(): boolean { + return !!this._uiConfId; + } +} diff --git a/src/k-provider/ovp/ovp-provider.js b/src/k-provider/ovp/ovp-provider.js index e69de29..843cf56 100644 --- a/src/k-provider/ovp/ovp-provider.js +++ b/src/k-provider/ovp/ovp-provider.js @@ -0,0 +1,168 @@ +//@flow +import Logger from '../../util/logger' +import ProviderParser from './providerParser' +import DataLoaderManager from './loaders/data-loader-manager' +import MediaEntryLoader from './loaders/media-entry-loader' +import SessionLoader from './loaders/session-loader' +import UiConfigLoader from './loaders/ui-config-loader' +import Configuration from './config' +import MediaEntry from '../../entities/media-entry' +import MediaSource from '../../entities/media-source' + +/** + * @constant + */ +const logger = Logger.get("OvpProvider"); + +type playerConfig = { + id: string, + sources: Array, + duration: number, + type: string, + metadata: Object, + plugins: Object +}; + +/** + * Ovp provider + * @classdesc + */ +export class OvpProvider { + /** + * @member - ks + * @type {string} + */ + ks: string; + /** + * @member - partner ID + * @type {number} + */ + partnerID: number; + /** + * @member - is anonymous + * @type {boolean} + * @private + */ + _isAnonymous: boolean; + /** + * @member - uiConf ID + * @type {number} + * @private + */ + _uiConfId: number; + /** + * @member - Data loader + * @type {DataLoaderManager} + * @private + */ + _dataLoader: DataLoaderManager; + + /** + * @constructor + * @param {number} partnerID The partner ID + * @param {string} [ks=""] The provider ks (has empty string as default value) + * @param {Object} [config] The provider config(optional) + */ + constructor(partnerID: number, ks: string = "", config?: Object) { + this.partnerID = partnerID; + this.ks = ks; + this._isAnonymous = !this.ks; + Configuration.set(config); + } + + /** + * Returns player json configuration + * @function getConfig + * @param {string} entryId The entry ID + * @param {number} uiConfId The uiConf ID + * @returns {Promise} The provider config object as promise + */ + getConfig(entryId?: string, uiConfId?: number): Promise { + if (uiConfId != null) { + this._uiConfId = uiConfId; + } + this._dataLoader = new DataLoaderManager(this.partnerID, this.ks); + return new Promise((resolve, reject) => { + if (this.validateParams(entryId, uiConfId)) { + let ks: string = this.ks; + if (this._isAnonymous) { + ks = "{1:result:ks}"; + this._dataLoader.add(SessionLoader, {partnerId: this.partnerID}); + } + + this._dataLoader.add(MediaEntryLoader, {entryId: entryId, ks: ks}); + this._dataLoader.add(UiConfigLoader, {uiConfId: uiConfId, ks: ks}); + this._dataLoader.fetchData() + .then(response => { + resolve(this.parseDataFromResponse(response)); + }, + err => { + reject(err); + }); + } + else { + reject({success: false, data: "Missing mandatory parameter"}); + } + }); + } + + /** + * Parses BE data to json configuration object + * @function parseDataFromResponse + * @param {Map} data The data to parse + * @returns {Object} The parsed config object + */ + parseDataFromResponse(data: Map): Object { + logger.info("Data parsing started."); + let config: playerConfig = { + id: "", + sources: [], + duration: 0, + type: "Unknown", + metadata: {}, + plugins: {} + }; + if (data != null) { + if (data.has(SessionLoader.name)) { + let sessionLoader = data.get(SessionLoader.name); + if (sessionLoader != null && sessionLoader.response != null) { + this.ks = sessionLoader.response; + this._isAnonymous = !this.ks; + } + } + if (data.has(UiConfigLoader.name)) { + let uiConfLoader = data.get(UiConfigLoader.name); + let pluginsJson: Object = {}; + if (uiConfLoader != null) { + pluginsJson = uiConfLoader.response; + } + config.plugins = pluginsJson; + } + if (data.has(MediaEntryLoader.name)) { + let mediaLoader = data.get(MediaEntryLoader.name); + if (mediaLoader != null && mediaLoader.response != null) { + let mediaEntry: MediaEntry = ProviderParser.getMediaEntry(this.ks, this.partnerID, this._uiConfId, mediaLoader.response); + config.id = mediaEntry.id; + config.sources = mediaEntry.sources; + config.duration = mediaEntry.duration; + config.type = mediaEntry.type.name; + config.metadata = mediaEntry.metaData; + } + } + } + logger.info(config); + return (config); + } + + /** + * Parameters validation function + * @param {string} entryId The entry ID + * @param {number} uiConfId The uiConfID + * @returns {boolean} Is valid params + */ + validateParams(entryId?: string, uiConfId?: number): boolean { + return !!entryId || !!uiConfId; + } +} + +export default OvpProvider; diff --git a/src/k-provider/ovp/play-source-url-builder.js b/src/k-provider/ovp/play-source-url-builder.js new file mode 100644 index 0000000..0193d5b --- /dev/null +++ b/src/k-provider/ovp/play-source-url-builder.js @@ -0,0 +1,76 @@ +//@flow +import Configuration from './config' + +const config = Configuration.get(); + +/** + * Media source url builder + * @classdesc + */ +export default class PlaySourceUrlBuilder { + + /** + * Returns source url by given url params + * @function build + * @param {Object} urlParams The params + * @returns {string} The URL + * @static + */ + static build(urlParams: Object): string { + let baseUrl: string = config.baseUrl; + let partnerId: string = urlParams.partnerId; + let entryId: string = urlParams.entryId; + let ks: string = urlParams.ks; + let uiConfId: string = urlParams.uiConfId; + let format: string = urlParams.format; + let protocol: string = urlParams.protocol; + let extension: string = urlParams.extension; + let flavorIds: string = urlParams.flavorIds; + + if (baseUrl == "" && partnerId == "" && entryId == "" && extension == "" && format == "") { + return ""; + } + + let playUrl = baseUrl; + if (!PlaySourceUrlBuilder.endsWith(baseUrl, "/")) { + playUrl += "/"; + } + playUrl += "p/" + partnerId + "/sp/" + partnerId + "00" + "/playManifest/entryId/" + entryId + "/protocol/" + protocol + "/format/" + format; + + if (flavorIds != "") { + playUrl += "/falvorIds/" + flavorIds; + } + else if (uiConfId != "") { + playUrl += "/uiConfId/" + uiConfId; + } + + if (this.ks != "") { + playUrl += "/ks/" + ks; + } + + playUrl += "/a." + extension; + + if (uiConfId && flavorIds != "") { + playUrl += "?uiConfId=." + uiConfId; + } + + return playUrl; + } + + /** + * Checks if given string end with search string + * @param {string} string The given string + * @param {string} searchString The string to search + * @returns {boolean} Is given string end with search string + */ + static endsWith(string: string, searchString: string): boolean { + if (typeof string !== 'string') { + return false; + } + if (typeof searchString !== 'string') { + return false; + } + + return string.indexOf(searchString, string.length - searchString.length) != -1; + } +} diff --git a/src/k-provider/ovp/providerParser.js b/src/k-provider/ovp/providerParser.js new file mode 100644 index 0000000..5ad0cdb --- /dev/null +++ b/src/k-provider/ovp/providerParser.js @@ -0,0 +1,201 @@ +//@flow + +import KalturaMediaEntry from './response-types/kaltura-media-entry' +import KalturaPlaybackContext from './response-types/kaltura-playback-context' +import KalturaPlaybackSource from './response-types/kaltura-playback-source' +import KalturaFlavorAsset from './response-types/kaltura-flavor-asset' +import KalturaMetadataListResponse from './response-types/kaltura-metadata-list-response' +import PlaySourceUrlBuilder from "./play-source-url-builder" +import XmlParser from '../xml-parser' +import {MediaEntryType, EntryType, MediaType} from '../enums' +import Logger from '../../util/logger' +import Configuration from './config' +import {MediaFormat} from '../../entities/media-format' +import MediaEntry from '../../entities/media-entry' +import Drm from '../../entities/drm' +import MediaSource from '../../entities/media-source' + +const config = Configuration.get(); +/** + * @constant + */ +const logger = Logger.get("OvpProvider"); + +/** + * @constant + * @type {Map} + */ +const SUPPORTED_FORMATS: Map = new Map([ + ["mpegdash", MediaFormat.dash], + ["applehttp", MediaFormat.hls], + ["url", MediaFormat.mp4] +]); + +/** + * Ovp provider parser + * @classdesc + */ +export default class ProviderParser { + + /** + * Returns parsed media entry by given OVP response objects + * @function getMediaEntry + * @param {string} ks The ks + * @param {number} partnerID The partner ID + * @param {number} uiConfId The uiConf ID + * @param {any} mediaEntryResponse The media entry response + * @returns {MediaEntry} The media entry + * @static + */ + static getMediaEntry(ks: string, partnerID: number, uiConfId: number, mediaEntryResponse: any): MediaEntry { + let mediaEntry: MediaEntry = new MediaEntry(); + let entry = mediaEntryResponse.entry; + let playbackContext = mediaEntryResponse.playBackContextResult; + let metadataList = mediaEntryResponse.metadataListResult; + let kalturaSources: Array = playbackContext.sources; + let sources: Array = []; + + if (kalturaSources && kalturaSources.length > 0) { + kalturaSources.forEach((source) => { + sources.push(this.parseSource(source, ks, partnerID, uiConfId, entry, playbackContext)); + }); + } + else { + sources = []; + } + + mediaEntry.sources = sources; + + let metadata: Map = this.parseMetaData(metadataList); + mediaEntry.metaData = metadata; + mediaEntry.id = entry.id; + mediaEntry.duration = entry.duration; + + let type: MediaEntryType = MediaEntryType.Unknown; + + switch (entry.entryType) { + case MediaType.IMAGE.value: + type = MediaEntryType.Image; + break; + case MediaType.AUDIO.value: + type = MediaEntryType.Audio; + break; + default: + switch (entry.type) { + case EntryType.MEDIA_CLIP.value: + type = MediaEntryType.Vod; + break; + case EntryType.LIVE_STREAM.value: + case EntryType.LIVE_CHANNEL.value: + type = MediaEntryType.Live; + break; + default: + type = MediaEntryType.Unknown; + } + } + mediaEntry.type = type; + + return mediaEntry; + } + + /** + * + * @param {KalturaPlaybackSource} source The source + * @param {string} ks The ks + * @param {number} partnerID The partner ID + * @param {number} uiConfId The uiConf ID + * @param {KalturaMediaEntry} entry The entry + * @param {KalturaPlaybackContext} playbackContext The playback context + * @returns {MediaSource} The parsed media source + * @static + */ + static parseSource(source: KalturaPlaybackSource, ks: string, partnerID: number, uiConfId: number, entry: KalturaMediaEntry, playbackContext: KalturaPlaybackContext): MediaSource { + let playUrl: string = ""; + let mediaFormat = SUPPORTED_FORMATS.get(source.format); + let mediaSource: MediaSource = new MediaSource(); + // in case playbackSource doesn't have flavors we don't need to build the url and we'll use the provided one. + if (source.hasFlavorIds()) { + let splittedUrl: Array = config.baseUrl.split("/"); + let baseProtocol: string; + if (splittedUrl && splittedUrl.length > 0) { + baseProtocol = splittedUrl[0].substring(0, splittedUrl[0].length - 1); + } + else { + baseProtocol = "http"; + } + + let extension: string = ""; + if (!mediaFormat) { + let flavorIdsArr = source.flavorIds.split(","); + let flavors: Array = playbackContext.flavorAssets.filter(flavor => flavorIdsArr.indexOf(flavor.id) != -1); + if (flavors && flavors.length > 0) { + extension = flavors[0].fileExt; + } + } + else { + extension = mediaFormat.pathExt; + mediaSource.mimetype = mediaFormat.mimeType; + } + + playUrl = PlaySourceUrlBuilder.build({ + entryId: entry.id, + flavorIds: source.flavorIds, + format: source.format, + ks: ks, + partnerId: partnerID, + uiConfId: uiConfId, + extension: extension, + protocol: source.getProtocol(baseProtocol) + }); + + } + else { + playUrl = source.url; + } + + if (playUrl == "") { + logger.error(`failed to create play url from source, discarding source: (${entry.id}_${source.deliveryProfileId}), ${source.format}.`); + return mediaSource; + } + + + mediaSource.src = playUrl; + mediaSource.id = entry.id + "_" + source.deliveryProfileId + "," + source.format; + if (source.hasDrmData()) { + let drmParams: Array = []; + source.drm.forEach((drm) => { + drmParams.push(new Drm(drm.licenseURL, drm.scheme)); + }); + mediaSource.drmData = drmParams; + } + return mediaSource; + } + + /** + * Ovp metadata parser + * @function parseMetaData + * @param {KalturaMetadataListResponse} metadataList The metadata list + * @returns {Map} Parsed metadata + * @static + */ + static parseMetaData(metadataList: KalturaMetadataListResponse): Map { + let metadata: Object = {}; + if (metadataList && metadataList.metas && metadataList.metas.length > 0) { + metadataList.metas.forEach((meta) => { + let metaXml: Object; + let domParser: DOMParser = new DOMParser(); + meta.xml = meta.xml.replace(/\r?\n|\r/g, ""); + meta.xml = meta.xml.replace(/>\s*/g, '>'); + meta.xml = meta.xml.replace(/>\s*/g, '>'); + metaXml = domParser.parseFromString(meta.xml, 'text/xml'); + let metasObj: Object = XmlParser.xmlToJson(metaXml); + let metaKeys = Object.keys(metasObj.metadata); + metaKeys.forEach((key) => { + metadata[key] = metasObj.metadata[key]["#text"]; + }) + + }) + } + return metadata; + } +} diff --git a/src/k-provider/ovp/response-types/kaltura-access-control-message.js b/src/k-provider/ovp/response-types/kaltura-access-control-message.js new file mode 100644 index 0000000..e8453d9 --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-access-control-message.js @@ -0,0 +1,29 @@ +//@flow + +/** + * Ovp BE access control message + * @classdesc + */ +export default class KalturaAccessControlMessage { + /** + * @member - The access control message + * @type {string} + */ + message: string; + /** + * @member - The access control message code + * @@type {string} + */ + code: string; + + /** + * @constructor + * @param {Object} data The json response + */ + constructor(data: Object) { + this.message = data.message; + this.code = data.code + } +} + + diff --git a/src/k-provider/ovp/response-types/kaltura-base-entry-list-response.js b/src/k-provider/ovp/response-types/kaltura-base-entry-list-response.js new file mode 100644 index 0000000..e1eeb8b --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-base-entry-list-response.js @@ -0,0 +1,39 @@ +//@flow +import ServiceResult from '../../base-service-result' +import KalturaMediaEntry from './kaltura-media-entry' + +/** + * Ovp BE BaseEntryList service response + * @classdesc + */ +export default class KalturaBaseEntryListResponse extends ServiceResult { + /** + * @member - The total count + * @type {number} + */ + totalCount: number; + /** + * @member - The entries + * @type {Array} + */ + entries: Array; + + /** + * @constructor + * @param {Object} responseObj The json response + */ + constructor(responseObj: Object) { + super(responseObj); + if (!this.hasError) { + this.totalCount = responseObj.totalCount; + if (this.totalCount > 0) { + this.entries = []; + responseObj.objects.map(entry => this.entries.push(new KalturaMediaEntry(entry))); + } + } + } +} + + + + diff --git a/src/k-provider/ovp/response-types/kaltura-drm-playback-plugin-data.js b/src/k-provider/ovp/response-types/kaltura-drm-playback-plugin-data.js new file mode 100644 index 0000000..78619e3 --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-drm-playback-plugin-data.js @@ -0,0 +1,31 @@ +//@flow +import {Scheme} from '../../enums' + +/** + * Ovp BE DrmPlaybackPluginData + * @classdesc + */ +export default class KalturaDrmPlaybackPluginData { + /** + * @member - The drm scheme + * @type {Scheme} + */ + scheme: Scheme; + + /** + * @member - The license URL + * @type {string} + */ + licenseURL: string; + + /** + * @constructor + * @param {Object} drm The json response + */ + constructor(drm: any) { + this.scheme = drm.scheme; + this.licenseURL = drm.licenseURL; + } +} + + diff --git a/src/k-provider/ovp/response-types/kaltura-flavor-asset.js b/src/k-provider/ovp/response-types/kaltura-flavor-asset.js new file mode 100644 index 0000000..7e68eec --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-flavor-asset.js @@ -0,0 +1,103 @@ +//@flow +import {Status} from '../../enums' + +/** + * Ovp BE FlavorAsset + * @classdesc + */ +export default class KalturaFlavorAsset { + /** + * @member - The ID of the Flavor Asset + * @type {string} + */ + id: string; + /** + * @member -The Flavor Params used to create this Flavor Asset + * @type {string} + */ + flavorParamsId: string; + /** + * @member -The file extension + * @type {string} + */ + fileExt: string; + /** + * @member - The overall bitrate (in KBits) of the Flavor Asset + * @type {string} + */ + bitrate: number; + /** + * @member - The width of the Flavor Asset + * @type {number} + */ + width: number; + /** + * @member - The height of the Flavor Asset + * @type {number} + */ + height: number; + /** + * @member - The frame rate (in FPS) of the Flavor Asset + * @type {number} + */ + frameRate: number; + /** + * @member - True if this Flavor Asset is the original source + * @type {number} + */ + isOriginal: boolean; + /** + * @member - True if this Flavor Asset is playable in KDP + * @type {boolean} + */ + isWeb: boolean; + /** + * @member - The container format + * @type {boolean} + */ + containerFormat: string; + /** + *@member - The video codec + * @type {boolean} + */ + videoCodecId: string; + /** + * @member - The status of the Flavor Asset + * @type {string} + */ + status: Status; + /** + * @member - The language of the flavor asset + * @type {Status} + */ + language: string; + /** + * @member - The label of the flavor asset + * @type {string} + */ + label: string; + + /** + * @constructor + * @param {Object} data The json response + */ + constructor(data: any) { + this.id = data.id; + this.flavorParamsId = data.flavorParamsId; + this.fileExt = data.fileExt; + this.bitrate = data.bitrate; + this.width = data.width; + this.height = data.height; + this.id = data.id; + this.frameRate = data.frameRate; + this.isOriginal = data.isOriginal; + this.isWeb = data.isWeb; + this.containerFormat = data.containerFormat; + this.videoCodecId = data.videoCodecId; + this.status = data.status; + this.language = data.language; + this.label = data.label; + } +} + + diff --git a/src/k-provider/ovp/response-types/kaltura-media-entry.js b/src/k-provider/ovp/response-types/kaltura-media-entry.js new file mode 100644 index 0000000..765ac34 --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-media-entry.js @@ -0,0 +1,63 @@ +//@flow +import {EntryType, MediaType} from '../../enums' + +/** + * Ovp BE MediaEntry + * @classdesc + */ +export default class KalturaMediaEntry { + /** + * @member - The entry id + * @type {string} + */ + id: string; + /** + * @member - Entry name (Min 1 chars) + * @type {string} + */ + name: string; + /** + * @member - The URL used for playback. This is not the download URL. + * @type {string} + */ + dataUrl: string; + /** + * @member - Comma separated flavor params ids that exists for this media entry + * @type {string} + */ + flavorParamsIds: string; + /** + * @member - The entry duration + * @type {number} + */ + duration: number; + /** + * @member - The type of the entry, this is auto filled by the derived entry object + * @type {EntryType} + */ + type: EntryType; + /** + * @member - The type of the entry, this is auto filled by the derived entry object (Image, Audio etc.) + * @type {MediaType} + */ + entryType: MediaType; + + /** + * @constructor + * @param {Object} entry The json response + */ + constructor(entry: Object) { + this.id = entry.id; + this.name = entry.name; + this.dataUrl = entry.dataUrl; + this.type = entry.type; + this.entryType = entry.mediaType; + this.flavorParamsIds = entry.flavorParamsIds; + this.duration = entry.duration; + } +} + + + + + diff --git a/src/k-provider/ovp/response-types/kaltura-metadata-list-response.js b/src/k-provider/ovp/response-types/kaltura-metadata-list-response.js new file mode 100644 index 0000000..2046bfd --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-metadata-list-response.js @@ -0,0 +1,35 @@ +//@flow +import ServiceResult from '../../base-service-result' +import KalturaMetadata from './kaltura-metadata' + +/** + * Ovp BE Metadata list response + * @classdesc + */ +export default class KalturaMetadataListResponse extends ServiceResult { + totalCount: number; + /** + * @member -The mata data array + * @type {Array} + */ + metas: Array; + + /** + * @constructor + * @param {Object} responseObj The response + */ + constructor(responseObj: Object) { + super(responseObj); + if (!this.hasError) { + this.totalCount = responseObj.totalCount; + if (this.totalCount > 0) { + this.metas = []; + responseObj.objects.map(meta => this.metas.push(new KalturaMetadata(meta))); + } + + } + } +} + + + diff --git a/src/k-provider/ovp/response-types/kaltura-metadata.js b/src/k-provider/ovp/response-types/kaltura-metadata.js new file mode 100644 index 0000000..1f30af3 --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-metadata.js @@ -0,0 +1,47 @@ +//@flow +import {MetadataObjectType, MetadataStatus} from '../../enums' + +/** + * Ovp BE Metadata + * @classdesc + */ +export default class KalturaMetadata { + id: number; + metadataProfileId: number; + metadataProfileVersion: number; + metadataObjectType: MetadataObjectType; + objectId: string; + version: number; + created: Date; + updated: Date; + status: MetadataStatus; + /** + * @member - The Metadata xml - represented as XML string + * @type {string} + */ + xml: string; + + /** + * @constructor + * @param {Object} data The response + */ + constructor(data: Object) { + this.id = data.id; + this.metadataProfileId = data.metadataProfileId; + this.metadataProfileVersion = data.metadataProfileVersion; + this.metadataProfileId = data.metadataProfileId; + this.metadataObjectType = data.metadataObjectType; + this.objectId = data.objectId; + this.version = data.version; + this.created = new Date(0); + this.created.setUTCSeconds(data.createdAt); + this.updated = new Date(0); + this.updated.setUTCSeconds(data.updatedAt); + this.status = data.status; + this.xml = data.xml; + + } +} + + + diff --git a/src/k-provider/ovp/response-types/kaltura-playback-context.js b/src/k-provider/ovp/response-types/kaltura-playback-context.js new file mode 100644 index 0000000..a718c69 --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-playback-context.js @@ -0,0 +1,65 @@ +//@flow +import ServiceResult from '../../base-service-result' +import KalturaAccessControlMessage from './kaltura-access-control-message' +import KalturaPlaybackSource from './kaltura-playback-source' +import KalturaRuleAction from './kaltura-rule-action' +import KalturaFlavorAsset from './kaltura-flavor-asset' + +/** + * Ovp BE playback context response + * @classdesc + */ +export default class KalturaPlaybackContext extends ServiceResult { + /** + * @member - The playback sources + * @type {Array} + */ + sources: Array = []; + /** + * @member - Array of actions as received from the rules that invalidated + * @type {Array} + */ + actions: Array = []; + /** + * @member - Array of actions as received from the rules that invalidated + * @type {Array} + */ + messages: Array = []; + /** + * @member - The flavor assets + * @type {Array} + */ + flavorAssets: Array = []; + + /** + * @constructor + * @param {Object} response The response + */ + constructor(response: Object) { + super(response); + if (!this.hasError) { + + let messages = response.messages; + if (messages) { + messages.map(message => this.messages.push(new KalturaAccessControlMessage(message))); + } + + let actions = response.actions; + if (actions) { + actions.map(action => this.actions.push(new KalturaRuleAction(action))); + } + + let sources = response.sources; + if (sources) { + sources.map(source => this.sources.push(new KalturaPlaybackSource(source))); + } + + let flavorAssets = response.flavorAssets; + if (flavorAssets) { + flavorAssets.map(flavor => this.flavorAssets.push(new KalturaFlavorAsset(flavor))); + } + } + + } +} + diff --git a/src/k-provider/ovp/response-types/kaltura-playback-source.js b/src/k-provider/ovp/response-types/kaltura-playback-source.js new file mode 100644 index 0000000..344382a --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-playback-source.js @@ -0,0 +1,97 @@ +//@flow +import KalturaDrmPlaybackPluginData from './kaltura-drm-playback-plugin-data' + +/** + * Ovp BE playback source + * @classdesc + */ +export default class KalturaPlaybackSource { + /** + * @member - source format according to delivery profile streamer type (applehttp, mpegdash etc.) + * @type {string} + */ + format: string; + /** + * @member - delivery profile Id + * @type {string} + */ + deliveryProfileId: string; + /** + * @member - The source URL + * @type {string} + */ + url: string; + /** + * @member - comma separated string according to deliveryProfile media protocols ('http,https' etc.) + * @type {string} + */ + protocols: string; + /** + * @member - comma separated string of flavor ids + * @type {string} + */ + flavorIds: string; + /** + * @member - drm data object containing relevant license url ,scheme name and certificate + * @type {Array} + */ + drm: Array = []; + + /** + * @constructor + * @param {Object} source The response + */ + constructor(source: Object) { + this.format = source.format; + this.deliveryProfileId = source.deliveryProfileId; + this.url = source.url; + this.protocols = source.protocols; + this.flavorIds = source.flavorIds; + + if (source.drm) { + source.drm.map(drm => this.drm.push(new KalturaDrmPlaybackPluginData(drm))); + } + + + } + + /** + * Checks if source has DRM data + * @function hasDrmData + * @returns {boolean} Is source has DRM + */ + hasDrmData(): boolean { + return this.drm && this.drm.length > 0; + } + + /** + * Checks if source has flavor IDs + * @function hasFlavorIds + * @returns {boolean} Is source ha flavor IDs + */ + hasFlavorIds(): boolean { + return !!this.flavorIds && this.flavorIds.length > 0; + } + + /** + * Returns source desired protocol if supported + * @param {string} protocol - the desired protocol for the source (base play url protocol) + * @returns {string} - protocol if protocol is in the protocols list - if not empty string returned + */ + getProtocol(protocol: string): string { + let returnValue: string = ""; + if (this.protocols && this.protocols.length > 0) { + let protocolsArr: Array = this.protocols.split(","); + protocolsArr.forEach((p) => { + if (p == protocol) { + returnValue = p; + } + }); + } + else if (protocol == "http") { + return protocol; + } + return returnValue; + } +} + diff --git a/src/k-provider/ovp/response-types/kaltura-rule-action.js b/src/k-provider/ovp/response-types/kaltura-rule-action.js new file mode 100644 index 0000000..7b41b30 --- /dev/null +++ b/src/k-provider/ovp/response-types/kaltura-rule-action.js @@ -0,0 +1,24 @@ +//@flow +import {KalturaRuleActionType} from '../../enums' + +/** + * Ovp BE rule action + * @classdesc + */ +export default class KalturaRuleAction { + /** + * @member - The type of the action + * @type {KalturaRuleActionType} + */ + type: KalturaRuleActionType; + + /** + * @constructor + * @param {Object} data The response + */ + constructor(data: Object) { + this.type = data.type; + } +} + + diff --git a/src/k-provider/ovp/response-types/kalturaUIConfResponse.js b/src/k-provider/ovp/response-types/kalturaUIConfResponse.js new file mode 100644 index 0000000..c5d5746 --- /dev/null +++ b/src/k-provider/ovp/response-types/kalturaUIConfResponse.js @@ -0,0 +1,85 @@ +//@flow +import ServiceResult from '../../base-service-result' +import {UIConfType, UIConfCreationMode} from '../../enums'; + +/** + * Ovp BE Ui config response + * @classdesc + */ +export default class KalturaUiConfResponse extends ServiceResult { + /** + * @member -Name of the uiConf, this is not a primary key + * @type {string} + */ + name: string; + /** + * @member -Name of the uiConf, this is not a primary key + * @type {string} + */ + description: string; + objTypeAsString: string; + width: number; + height: number; + htmlParams: string; + swfUrl: string; + confFilePath: string; + confFile: string; + confFileFeatures: string; + name: string; + /** + * @member -plugins configuration represented as Json string + * @type {string} + */ + config: string; + confVars: string; + useCdn: boolean; + tags: string; + swfUrlVersion: string; + created: Date; + updated: Date; + html5Url: string; + version: string; + partnerTags: string; + objType: UIConfType; + creationMode: UIConfCreationMode; + + /** + * @constructor + * @param {Object} data The json response + */ + constructor(data: Object) { + super(data); + if (!this.hasError) { + this.name = data.name; + + this.description = data.description; + this.objTypeAsString = data.objTypeAsString; + this.width = data.width; + this.height = data.height; + this.htmlParams = data.htmlParams; + this.swfUrl = data.swfUrl; + this.confFilePath = data.confFilePath; + this.confFile = data.confFile; + this.confFileFeatures = data.confFileFeatures; + this.config = data.config; + this.confVars = data.confVars; + this.useCdn = data.useCdn; + this.tags = data.tags; + this.swfUrlVersion = data.swfUrlVersion; + this.created = new Date(0); + this.created.setUTCSeconds(data.createdAt); + this.updated = new Date(0); + this.updated.setUTCSeconds(data.updatedAt); + this.html5Url = data.description; + this.version = data.description; + this.partnerTags = data.description; + this.objType = data.description; + this.creationMode = data.description; + + } + } +} + + + + diff --git a/src/k-provider/ovp/services/base-entry-service.js b/src/k-provider/ovp/services/base-entry-service.js new file mode 100644 index 0000000..0d5a66f --- /dev/null +++ b/src/k-provider/ovp/services/base-entry-service.js @@ -0,0 +1,71 @@ +//@flow +import OvpService from './ovp-service' +import RequestBuilder from '../../request-builder' + +const SERVICE_NAME: string = "baseEntry"; + +/** + * Ovp BaseEntry service methods + * @classdesc + */ +export default class BaseEntryService extends OvpService { + + /** + * Creates an instance of RequestBuilder for baseentry.getPlaybackContext + * @function getPlaybackContext + * @param {string} baseUrl The service base URL + * @param {string} ks The ks + * @param {string} entryId The entry ID + * @returns {RequestBuilder} The request builder + * @static + */ + static getPlaybackContext(baseUrl: string, ks: string, entryId: string): RequestBuilder { + let request = new RequestBuilder(); + request.service = SERVICE_NAME; + request.action = "getPlaybackContext"; + request.method = "POST"; + request.baseUrl = baseUrl; + request.tag = "baseEntry-getPlaybackContext"; + let contextDataParams = {objectType: "KalturaContextDataParams", flavorTags: "all"}; + let params = {entryId: entryId, ks: ks, contextDataParams: contextDataParams}; + request.params = params; + return request; + } + + /** + * Creates an instance of RequestBuilder for baseentry.list + * @function list + * @param {string} baseUrl The base URL + * @param {string} ks The ks + * @param {string} entryId The entry ID + * @returns {RequestBuilder} The request builder + * @static + */ + static list(baseUrl: string, ks: string, entryId: string): RequestBuilder { + let request = new RequestBuilder(); + request.service = SERVICE_NAME; + request.action = "list"; + request.method = "POST"; + request.baseUrl = baseUrl; + request.tag = "list"; + request.params = BaseEntryService.getEntryListReqParams(entryId, ks); + return request; + } + + /** + * Gets baseentry.list service params + * @function getEntryListReqParams + * @param {string} entryId The entry ID + * @param {string} ks The ks + * @returns {{ks: string, filter: {redirectFromEntryId: string}, responseProfile: {fields: string, type: number}}} The service params object + * @static + */ + static getEntryListReqParams(entryId: string, ks: string): any { + let filterParams = {redirectFromEntryId: entryId}; + let responseProfileParams = { + fields: "id,name,dataUrl,duration,msDuration,flavorParamsIds,mediaType,type,tags", + type: 1 + }; + return {ks: ks, filter: filterParams, responseProfile: responseProfileParams}; + } +} diff --git a/src/k-provider/ovp/services/meta-data-service.js b/src/k-provider/ovp/services/meta-data-service.js new file mode 100644 index 0000000..c5bffa5 --- /dev/null +++ b/src/k-provider/ovp/services/meta-data-service.js @@ -0,0 +1,34 @@ +//@flow + +import OvpService from './ovp-service' +import RequestBuilder from '../../request-builder' + +const SERVICE_NAME: string = "metadata_metadata"; + +/** + * Ovp metadata_metadata service methods + * @classdesc + */ +export default class MetaDataService extends OvpService { + /** + * Creates an instance of RequestBuilder for metadata_metadata.list + * @function getPlaybackContext + * @param {string} baseUrl The service base URL + * @param {string} ks The ks + * @param {string} entryId The entry ID + * @returns {RequestBuilder} The request builder + * @static + */ + static list(baseUrl: string, ks: string, entryId: string) { + let request = new RequestBuilder(); + request.service = SERVICE_NAME; + request.action = "list"; + request.method = "POST"; + request.baseUrl = baseUrl; + request.tag = "metadata_metadata-list"; + let filter = {objectType: "KalturaMetadataFilter", objectIdEqual: entryId, metadataObjectTypeEqual: "1"}; + let params = {filter: filter, ks: ks}; + request.params = params; + return request; + } +} diff --git a/src/k-provider/ovp/services/ovp-service.js b/src/k-provider/ovp/services/ovp-service.js new file mode 100644 index 0000000..04d2d49 --- /dev/null +++ b/src/k-provider/ovp/services/ovp-service.js @@ -0,0 +1,34 @@ +//@flow +import MultiRequestBuilder from '../../multi-request-builder' +import Configuration from '../config' + +const config = Configuration.get(); +const SERVICE_NAME: string = "multirequest"; + +/** + * Base for all ovp services + * @classdesc + */ +export default class OvpService { + /** + * Gets a new instance of MultiRequestBuilder with ovp params + * @function getMultirequest + * @param {string} ks The ks + * @param {string} partnerId The partner ID + * @returns {MultiRequestBuilder} The multi request builder + * @static + */ + static getMultirequest(ks: string, partnerId?: number): MultiRequestBuilder { + let ovpParams = config.serviceParams; + Object.assign(ovpParams, {ks: ks}); + if (partnerId) { + Object.assign(ovpParams, {partnerId: partnerId}); + } + let multiReq = new MultiRequestBuilder(); + multiReq.method = "POST"; + multiReq.service = SERVICE_NAME; + multiReq.baseUrl = config.beUrl; + multiReq.params = ovpParams; + return multiReq; + } +} diff --git a/src/k-provider/ovp/services/session-service.js b/src/k-provider/ovp/services/session-service.js new file mode 100644 index 0000000..bf32bc6 --- /dev/null +++ b/src/k-provider/ovp/services/session-service.js @@ -0,0 +1,31 @@ +//@flow + +import OvpService from './ovp-service' +import RequestBuilder from '../../request-builder' + +const SERVICE_NAME: string = "session"; + +/** + * Ovp session service methods + * @classdesc + */ +export default class SessionService extends OvpService { + /** + * Creates an instance of RequestBuilder for session.startWidgetSession + * @function anonymousSession + * @param {string} baseUrl The service base URL + * @param {string} partnerId The partner ID + * @returns {RequestBuilder} The request builder + * @static + */ + static anonymousSession(baseUrl: string, partnerId: number) { + let request = new RequestBuilder(); + request.service = SERVICE_NAME; + request.action = "startWidgetSession"; + request.method = "POST"; + request.baseUrl = baseUrl; + request.tag = "session-startWidget"; + request.params = {widgetId: "_" + partnerId}; + return request; + } +} diff --git a/src/k-provider/ovp/services/ui-conf-service.js b/src/k-provider/ovp/services/ui-conf-service.js new file mode 100644 index 0000000..364f17a --- /dev/null +++ b/src/k-provider/ovp/services/ui-conf-service.js @@ -0,0 +1,36 @@ +//@flow + +import OvpService from './ovp-service' +import RequestBuilder from '../../request-builder' + +const SERVICE_NAME: string = "uiconf"; + +/** + * Ovp uiconf service methods + * @classdesc + */ +export default class UiConfService extends OvpService { + /** + * Creates an instance of RequestBuilder for uiconf.get + * @function get + * @param {string} baseUrl The service base URL + * @param {string} ks The ks + * @param {string} uiConfID The uiConf ID + * @returns {RequestBuilder} The request builder + * @static + */ + static get(baseUrl: string, ks: string, uiConfID: number) { + let request = new RequestBuilder(); + request.service = SERVICE_NAME; + request.action = "get"; + request.method = "POST"; + request.baseUrl = baseUrl; + request.tag = "uiconf-get"; + let responseProfileParams = { + fields: "config", + type: 1 + }; + request.params = {id: uiConfID, responseProfile: responseProfileParams, ks: ks}; + return request; + } +} diff --git a/src/k-provider/request-builder.js b/src/k-provider/request-builder.js new file mode 100644 index 0000000..11b5dda --- /dev/null +++ b/src/k-provider/request-builder.js @@ -0,0 +1,95 @@ +//@flow + +/** + * Request builder + * @classdesc + */ +export default class RequestBuilder { + + /** + * @member - Service name + * @type {string} + */ + service: string; + /** + * @member - Service action + * @type {string} + */ + action: string; + /** + * @member - Service params + * @type {any} + */ + params: any; + /** + * @member - Service headers + * @type {Map} + */ + headers: Map; + /** + * @member - Service base url + * @type {Map} + */ + baseUrl: string; + /** + * @member - Service method (POST,GET,DELETE etc..) + * @type {string} + */ + method: string; + /** + * @member - Service tag + * @type {string} + */ + tag: string; + + /** + * @constructor + * @param {Map} headers The request headers + */ + constructor(headers: Map = new Map()) { + this.headers = headers; + this.headers.set("Content-Type", "application/json"); + } + + /** + * Builds restful service URL + * @function getUrl + * @returns {string} The service URL + */ + getUrl(): string { + if (!this.baseUrl) { + throw new Error("baseUrl is mandatory for request builder"); + } + let url = this.baseUrl + '/service/' + this.service + (this.action ? +'/action/' + this.action : ''); + return url; + } + + /** + * Executes service + * @function doHttpRequest + * @returns {Promise.} Service response as promise + */ + doHttpRequest(): Promise { + let request = new XMLHttpRequest(); + return new Promise((resolve, reject) => { + request.onreadystatechange = function () { + if (request.readyState === 4) { + if (request.status === 200) { + let jsonResponse = JSON.parse(request.responseText); + if (jsonResponse && typeof(jsonResponse) === 'object' && jsonResponse.code && jsonResponse.message) + reject(jsonResponse); + else + resolve(jsonResponse); + } else { + reject(request.responseText); + } + } + }; + request.open(this.method, this.getUrl()); + this.headers.forEach((value, key) => { + request.setRequestHeader(key, value); + }); + request.send(this.params); + }); + } +} diff --git a/src/k-provider/xml-parser.js b/src/k-provider/xml-parser.js new file mode 100644 index 0000000..6667f83 --- /dev/null +++ b/src/k-provider/xml-parser.js @@ -0,0 +1,48 @@ +//@flow + +/** + * Xml parser + * @classdesc + */ +export default class XmlParser { + /** + * Parses xml string to json object + * @param {string} xml The xml to parse + * @returns {{}} The parsed xml as Json object + * @static + */ + static xmlToJson(xml: Object) { + let obj = {}; + if (xml.nodeType == 1) { + if (xml.attributes.length > 0) { + obj["@attributes"] = {}; + for (let j = 0; j < xml.attributes.length; j++) { + let attribute = xml.attributes.item(j); + obj["@attributes"][attribute.nodeName] = attribute.nodeValue; + } + } + } + else if (xml.nodeType == 3) { + obj = xml.nodeValue; + } + if (xml.hasChildNodes()) { + for (let i = 0; i < xml.childNodes.length; i++) { + let item = xml.childNodes.item(i); + let nodeName = item.nodeName; + if (typeof (obj[nodeName]) == "undefined") { + obj[nodeName] = this.xmlToJson(item); + } + else { + if (typeof (obj[nodeName].push) == "undefined") { + let old = obj[nodeName]; + obj[nodeName] = []; + obj[nodeName].push(old); + } + obj[nodeName].push(this.xmlToJson(item)); + } + } + } + return obj; + } +} + diff --git a/src/util/logger.js b/src/util/logger.js new file mode 100644 index 0000000..0e165f6 --- /dev/null +++ b/src/util/logger.js @@ -0,0 +1,30 @@ +//@flow +import * as JsLogger from 'js-logger'; + +class LoggerFactory { + constructor(options?: Object) { + JsLogger.useDefaults(options || {}); + } + + get(name?: string) { + if (!name) { + return JsLogger; + } + return JsLogger.get(name); + } +} + +let Logger = new LoggerFactory({defaultLevel: JsLogger.DEBUG}); +const LOG_LEVEL: {[level: string]: Object} = { + "DEBUG": JsLogger.DEBUG, + "INFO": JsLogger.INFO, + "TIME": JsLogger.TIME, + "WARN": JsLogger.WARN, + "ERROR": JsLogger.ERROR, + "OFF": JsLogger.OFF +}; + +export default Logger; +export {LOG_LEVEL}; + + diff --git a/test/src/k-provider/ovp/be-stubs.js b/test/src/k-provider/ovp/be-stubs.js new file mode 100644 index 0000000..fb5d745 --- /dev/null +++ b/test/src/k-provider/ovp/be-stubs.js @@ -0,0 +1,1516 @@ +//@flow +let AnonymousMocEntryWithoutUIConfNoDrmData = [ + { + "partnerId": 1082342, + "ks": "OGM0ZWM0Y2IwOWI5ZjM0MDcyZmQ3YmYxNzBiMGEwNGYxNWQ0ZTcyOXwxMDgyMzQyOzEwODIzNDI7MTQ5MDExNTg5MzswOzE0OTAwMjk0OTMuMTY3ODswO3ZpZXc6Kix3aWRnZXQ6MTs7", + "userId": 0, + "objectType": "KalturaStartWidgetSessionResponse" + }, + { + "objects": [ + { + "mediaType": 1, + "dataUrl": "http://cdnapi.kaltura.com/p/1082342/sp/108234200/playManifest/entryId/1_rsrdfext/format/url/protocol/http", + "flavorParamsIds": "0,487041,487051,487061,487071,487081,487091", + "duration": 55, + "msDuration": 55047, + "id": "1_rsrdfext", + "name": "FO21934-HDTX-SWE", + "tags": "", + "type": 1, + "objectType": "KalturaMediaEntry" + } + ], + "totalCount": 1, + "objectType": "KalturaBaseEntryListResponse" + }, + { + "sources": [ + { + "deliveryProfileId": 10081, + "format": "url", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/10081/protocol/https/format/url/name/a.mov", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 10101, + "format": "hdnetworkmanifest", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/10101/protocol/https/format/hdnetworkmanifest/manifest.f4m", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 10091, + "format": "applehttp", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/10091/protocol/https/format/applehttp/a.m3u8", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11241, + "format": "applehttp", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11241/protocol/https/format/applehttp/a.m3u8", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11611, + "format": "mpegdash", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11611/protocol/https/format/mpegdash/manifest.mpd", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11261, + "format": "mpegdash", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11261/protocol/https/format/mpegdash/manifest.mpd", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11251, + "format": "sl", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11251/protocol/https/format/sl/a.ism", + "drm": [], + "objectType": "KalturaPlaybackSource" + } + ], + "flavorAssets": [ + { + "flavorParamsId": 487041, + "width": 640, + "height": 360, + "bitrate": 471, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_ha0nqwz8", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 3164, + "tags": "mobile,web,mbr,iphone,iphonenew", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777079, + "description": "", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487051, + "width": 640, + "height": 360, + "bitrate": 670, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_gw7u4nf1", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 4505, + "tags": "mobile,web,mbr,iphone,iphonenew", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777079, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487061, + "width": 640, + "height": 360, + "bitrate": 964, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_rql6sqaa", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 6400, + "tags": "mobile,web,mbr,ipad,ipadnew,dash", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777064, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487071, + "width": 1280, + "height": 720, + "bitrate": 1547, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_sufd1yd9", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 10444, + "tags": "mobile,web,mbr,ipad,ipadnew,dash", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777080, + "description": "", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487081, + "width": 1280, + "height": 720, + "bitrate": 2628, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_9xvkk7a5", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 17408, + "tags": "web,mbr,dash", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777115, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487091, + "width": 1920, + "height": 1080, + "bitrate": 4128, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_4typ4pat", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 27443, + "tags": "web,mbr,dash", + "fileExt": "mp4", + "createdAt": 1484777023, + "updatedAt": 1484777325, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 0, + "width": 1920, + "height": 1080, + "bitrate": 18022, + "frameRate": 25, + "isOriginal": true, + "isWeb": false, + "containerFormat": "qt", + "videoCodecId": "avc1", + "status": 2, + "language": "English", + "id": "1_n75294r4", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 117760, + "tags": "source", + "fileExt": "mov", + "createdAt": 1484776914, + "updatedAt": 1484777022, + "description": "", + "objectType": "KalturaFlavorAsset" + } + ], + "actions": [], + "messages": [ + { + "message": "Out of scheduling\nWe're sorry, this content is currently unavailable.", + "code": "SCHEDULED_RESTRICTED", + "objectType": "KalturaAccessControlMessage" + } + ], + "objectType": "KalturaPlaybackContext" + }, + { + "objects": [ + { + "id": 331744001, + "partnerId": 1082342, + "metadataProfileId": 244381, + "metadataProfileVersion": 421, + "metadataObjectType": 1, + "objectId": "1_rsrdfext", + "version": 1, + "createdAt": 1484776887, + "updatedAt": 1484776887, + "status": 1, + "xml": "\nFO21934-HDTX-SWE.mov\nsv_SE\nFTA-nm\ntv-dc\nclip\nDisney Channel\nIBMS\nShow - TV\n1483225200\n1485903600\nNo Age Consent\n", + "objectType": "KalturaMetadata" + }, + { + "id": 331744121, + "partnerId": 1082342, + "metadataProfileId": 7330521, + "metadataProfileVersion": 61, + "metadataObjectType": 1, + "objectId": "1_rsrdfext", + "version": 11, + "createdAt": 1484776903, + "updatedAt": 1489508219, + "status": 1, + "xml": "\n FO21934-HDTX-SWE\n se-dc-lf\n VOD\n android\n Disney Channel SE\n\n", + "objectType": "KalturaMetadata" + } + ], + "totalCount": 2, + "objectType": "KalturaMetadataListResponse" + } +]; + +let AnonymousMocEntryWithoutUIConfWithDrmData = [ + { + "partnerId": 1068292, + "ks": "NTAwZjViZWZjY2NjNTRkNGEyMjU1MTg4OGE1NmUwNDljZWJkMzk1MXwxMDY4MjkyOzEwNjgyOTI7MTQ5MDE3NjE0NjswOzE0OTAwODk3NDYuMDIyNjswO3ZpZXc6Kix3aWRnZXQ6MTs7", + "userId": 0, + "objectType": "KalturaStartWidgetSessionResponse" + }, + { + "objects": [ + { + "mediaType": 1, + "dataUrl": "http://cdnapi.kaltura.com/p/1068292/sp/106829200/playManifest/entryId/1_rwbj3j0a/format/url/protocol/http", + "flavorParamsIds": "0,525091,525101,525111,525121", + "duration": 596, + "msDuration": 596000, + "id": "1_rwbj3j0a", + "name": "DRM TEST", + "type": 1, + "objectType": "KalturaMediaEntry" + } + ], + "totalCount": 1, + "objectType": "KalturaBaseEntryListResponse" + }, + { + "sources": [ + { + "deliveryProfileId": 11311, + "format": "applehttp", + "protocols": "http,https", + "flavorIds": "1_92dfyrz5,1_9dhh6m0i,1_zudouuah,1_r0wu6y77,1_oz343kla", + "url": "https://cdnapisec.kaltura.com/p/1068292/sp/1068292/playManifest/entryId/1_rwbj3j0a/flavorIds/1_92dfyrz5,1_9dhh6m0i,1_zudouuah,1_r0wu6y77,1_oz343kla/deliveryProfileId/11311/protocol/https/format/applehttp/a.m3u8", + "drm": [ + { + "certificate": "MIIE8DCCA9igAwIBAgIIWErELxCBDA4wDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBLZXkgU2VydmljZXMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTYxMTIzMDA0NDQyWhcNMTgxMTI0MDA0NDQyWjB0MQswCQYDVQQGEwJVUzEdMBsGA1UECgwUU3RhcndhdmUgQ29ycG9yYXRpb24xEzARBgNVBAsMCldLM1lFUlZKV0ExMTAvBgNVBAMMKEZhaXJQbGF5IFN0cmVhbWluZzogU3RhcndhdmUgQ29ycG9yYXRpb24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKDqSYs/fp9+BOvrgvnwy0p9WQt7ZUrPV4/RfWjcwU3ndYSR6LtNEWlRjHU18OF8HsU678nMqsxRAFwv12Hdew4WoZ7AEdPTX2tOZkvHqgQczo0TKdtuKF3+SYPWiE2u3epFuTAldCkc5stXOwtPnutHsUm+SxWc+DPhMBO1ryBlAgMBAAGjggH9MIIB+TAdBgNVHQ4EFgQUDCgFiGFqGqz0LuWC9mVG3Zktm5IwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRj5EdUy4VxWUYsg6zMRDFkZwMsvjCB4gYDVR0gBIHaMIHXMIHUBgkqhkiG92NkBQEwgcYwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5hcHBsZS5jb20va2V5c2VydmljZXMuY3JsMA4GA1UdDwEB/wQEAwIFIDBGBgsqhkiG92NkBg0BAwEB/wQ0AXdxb2EwczF5bWZ4cG5vN2gwcnh0bXp4amJzb2psa3BndTh5eWRsa3d1Y3NxZXltcWZvNTA1BgsqhkiG92NkBg0BBAEB/wQjAW5kam03a3hsamFiandvaXU3NGRmeG1zdXkybHlsMmhhdGIwDQYJKoZIhvcNAQEFBQADggEBAC2i5fDiQClCkp9mIyWBW+ZyMlUiM3Wr5eui0dUvKwSBTc8pyv4m4f60X7JoN5iZykriSnF3zO2aNY43AQsTzIpGvQ9kSJVBdTO7u/UevlbRcHDHrSoQ/8fKSVKvx9Be1pIRgznBRqS+iV6V0572rZOuR0ZQf0nY5vtEHBN6kTZyhPYFuTE32f77E/dtv6XnZ6qB1OKtv483mjVLqQrI0IOl3LyMKgAJ51whloEJEJC6g8frCmwmTLsmmOR17wq24/6YKjGNM2NuSa7/7RqwdEKuqDzzVyh3Zafmtn8rlIptACGPYWnmAP2gnCzBO/V6AIQDf0JcZUpOb+GfhQ7M7ik=", + "scheme": "fairplay.FAIRPLAY", + "licenseURL": "https://udrmv3.kaltura.com//fps/license?custom_data=eyJjYV9zeXN0ZW0iOiJPVlAiLCJ1c2VyX3Rva2VuIjoiTlRBd1pqVmlaV1pqWTJOak5UUmtOR0V5TWpVMU1UZzRPR0UxTm1Vd05EbGpaV0prTXprMU1Yd3hNRFk0TWpreU96RXdOamd5T1RJN01UUTVNREUzTmpFME5qc3dPekUwT1RBd09EazNORFl1TURJeU5qc3dPM1pwWlhjNktpeDNhV1JuWlhRNk1UczciLCJhY2NvdW50X2lkIjoxMDY4MjkyLCJjb250ZW50X2lkIjoiMV9yd2JqM2owYSIsImZpbGVzIjoiMV85MmRmeXJ6NSwxXzlkaGg2bTBpLDFfenVkb3V1YWgsMV9yMHd1Nnk3NywxX296MzQza2xhIn0%3D&signature=jl09zYv64OyEfVUt85rlA0WnK1w%3D", + "objectType": "KalturaFairPlayPlaybackPluginData" + } + ], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11331, + "format": "mpegdash", + "protocols": "http,https", + "flavorIds": "1_92dfyrz5,1_9dhh6m0i,1_zudouuah,1_r0wu6y77,1_oz343kla", + "url": "https://cdnapisec.kaltura.com/p/1068292/sp/1068292/playManifest/entryId/1_rwbj3j0a/flavorIds/1_92dfyrz5,1_9dhh6m0i,1_zudouuah,1_r0wu6y77,1_oz343kla/deliveryProfileId/11331/protocol/https/format/mpegdash/manifest.mpd", + "drm": [ + { + "scheme": "drm.PLAYREADY_CENC", + "licenseURL": "https://udrm.kaltura.com//cenc/playready/license?custom_data=eyJjYV9zeXN0ZW0iOiJPVlAiLCJ1c2VyX3Rva2VuIjoiTlRBd1pqVmlaV1pqWTJOak5UUmtOR0V5TWpVMU1UZzRPR0UxTm1Vd05EbGpaV0prTXprMU1Yd3hNRFk0TWpreU96RXdOamd5T1RJN01UUTVNREUzTmpFME5qc3dPekUwT1RBd09EazNORFl1TURJeU5qc3dPM1pwWlhjNktpeDNhV1JuWlhRNk1UczciLCJhY2NvdW50X2lkIjoxMDY4MjkyLCJjb250ZW50X2lkIjoiMV9yd2JqM2owYSIsImZpbGVzIjoiMV85MmRmeXJ6NSwxXzlkaGg2bTBpLDFfenVkb3V1YWgsMV9yMHd1Nnk3NywxX296MzQza2xhIn0%3D&signature=jl09zYv64OyEfVUt85rlA0WnK1w%3D", + "objectType": "KalturaDrmPlaybackPluginData" + }, + { + "scheme": "drm.WIDEVINE_CENC", + "licenseURL": "https://udrm.kaltura.com//cenc/widevine/license?custom_data=eyJjYV9zeXN0ZW0iOiJPVlAiLCJ1c2VyX3Rva2VuIjoiTlRBd1pqVmlaV1pqWTJOak5UUmtOR0V5TWpVMU1UZzRPR0UxTm1Vd05EbGpaV0prTXprMU1Yd3hNRFk0TWpreU96RXdOamd5T1RJN01UUTVNREUzTmpFME5qc3dPekUwT1RBd09EazNORFl1TURJeU5qc3dPM1pwWlhjNktpeDNhV1JuWlhRNk1UczciLCJhY2NvdW50X2lkIjoxMDY4MjkyLCJjb250ZW50X2lkIjoiMV9yd2JqM2owYSIsImZpbGVzIjoiMV85MmRmeXJ6NSwxXzlkaGg2bTBpLDFfenVkb3V1YWgsMV9yMHd1Nnk3NywxX296MzQza2xhIn0%3D&signature=jl09zYv64OyEfVUt85rlA0WnK1w%3D", + "objectType": "KalturaDrmPlaybackPluginData" + } + ], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11321, + "format": "sl", + "protocols": "http,https", + "flavorIds": "1_92dfyrz5,1_9dhh6m0i,1_zudouuah,1_r0wu6y77,1_oz343kla", + "url": "https://cdnapisec.kaltura.com/p/1068292/sp/1068292/playManifest/entryId/1_rwbj3j0a/flavorIds/1_92dfyrz5,1_9dhh6m0i,1_zudouuah,1_r0wu6y77,1_oz343kla/deliveryProfileId/11321/protocol/https/format/sl/a.ism", + "drm": [], + "objectType": "KalturaPlaybackSource" + } + ], + "flavorAssets": [ + { + "flavorParamsId": 525091, + "width": 480, + "height": 272, + "bitrate": 468, + "frameRate": 24, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_92dfyrz5", + "entryId": "1_rwbj3j0a", + "partnerId": 1068292, + "version": "1", + "size": 34099, + "tags": "mobile,web,mbr,iphone,iphonenew", + "fileExt": "mp4", + "createdAt": 1485272387, + "updatedAt": 1485272469, + "description": "", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 525101, + "width": 640, + "height": 360, + "bitrate": 667, + "frameRate": 24, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_9dhh6m0i", + "entryId": "1_rwbj3j0a", + "partnerId": 1068292, + "version": "1", + "size": 48537, + "tags": "mobile,web,mbr,iphone,iphonenew", + "fileExt": "mp4", + "createdAt": 1485272387, + "updatedAt": 1485272469, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 525111, + "width": 640, + "height": 360, + "bitrate": 964, + "frameRate": 24, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_zudouuah", + "entryId": "1_rwbj3j0a", + "partnerId": 1068292, + "version": "1", + "size": 68505, + "tags": "mobile,web,mbr,ipad,ipadnew,dash", + "fileExt": "mp4", + "createdAt": 1485272388, + "updatedAt": 1485272499, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 525121, + "width": 848, + "height": 480, + "bitrate": 1378, + "frameRate": 24, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_r0wu6y77", + "entryId": "1_rwbj3j0a", + "partnerId": 1068292, + "version": "1", + "size": 100249, + "tags": "mobile,web,mbr,ipad,ipadnew,dash", + "fileExt": "mp4", + "createdAt": 1485272388, + "updatedAt": 1485272564, + "description": "", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 0, + "width": 854, + "height": 480, + "bitrate": 2958, + "frameRate": 24, + "isOriginal": true, + "isWeb": false, + "containerFormat": "avi", + "videoCodecId": "fmp4", + "status": 2, + "id": "1_oz343kla", + "entryId": "1_rwbj3j0a", + "partnerId": 1068292, + "version": "11", + "size": 215040, + "tags": "source", + "fileExt": "avi", + "createdAt": 1485272298, + "updatedAt": 1485272387, + "description": "", + "objectType": "KalturaFlavorAsset" + } + ], + "actions": [ + { + "deliveryProfileIds": "11311,11321,11331", + "type": 5, + "objectType": "KalturaAccessControlLimitDeliveryProfilesAction" + }, + { + "policyId": 4081, + "type": "DRM_POLICY", + "objectType": "KalturaAccessControlDrmPolicyAction" + } + ], + "messages": [], + "objectType": "KalturaPlaybackContext" + }, + { + "objects": [], + "totalCount": 0, + "objectType": "KalturaMetadataListResponse" + } +]; + +let WrongEntryIDWithoutUIConf = [ + { + "partnerId": 1068292, + "ks": "NjVjMDdlZmUxY2VlMGI2YWMyYmZjOTczOTA5ZjhjNDIwMTJmODI3MXwxMDY4MjkyOzEwNjgyOTI7MTQ5MDE5MzIyNjswOzE0OTAxMDY4MjYuOTc2NDswO3ZpZXc6Kix3aWRnZXQ6MTs7", + "userId": 0, + "objectType": "KalturaStartWidgetSessionResponse" + }, + { + "code": "INVALID_ENTRY_ID", + "message": "Invalid entry id [\"1_rwbj3j0a55\"]", + "objectType": "KalturaAPIException", + "args": { + "ID": "1_rwbj3j0a55" + } + }, + { + "code": "ENTRY_ID_NOT_FOUND", + "message": "Entry id \"1_rwbj3j0a55\" not found", + "objectType": "KalturaAPIException", + "args": { + "ENTRY_ID": "1_rwbj3j0a55" + } + }, + { + "objects": [], + "totalCount": 0, + "objectType": "KalturaMetadataListResponse" + } +]; + +let EntryWithUIConfNoDrmData = [ + { + "partnerId": 1082342, + "ks": "YmQ0N2Q0M2QxZTAyMGIzYTY4YzI1ZDk5ZjI1NjhlZWVkOWZmOGViN3wxMDgyMzQyOzEwODIzNDI7MTQ5MDE5NjMwMDswOzE0OTAxMDk5MDAuOTA0NDswO3ZpZXc6Kix3aWRnZXQ6MTs7", + "userId": 0, + "objectType": "KalturaStartWidgetSessionResponse" + }, + { + "objects": [ + { + "mediaType": 1, + "dataUrl": "http://cdnapi.kaltura.com/p/1082342/sp/108234200/playManifest/entryId/1_rsrdfext/format/url/protocol/http", + "flavorParamsIds": "0,487041,487051,487061,487071,487081,487091", + "duration": 55, + "msDuration": 55047, + "id": "1_rsrdfext", + "name": "FO21934-HDTX-SWE", + "tags": "", + "type": 1, + "objectType": "KalturaMediaEntry" + } + ], + "totalCount": 1, + "objectType": "KalturaBaseEntryListResponse" + }, + { + "sources": [ + { + "deliveryProfileId": 10081, + "format": "url", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/10081/protocol/https/format/url/name/a.mov", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 10101, + "format": "hdnetworkmanifest", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/10101/protocol/https/format/hdnetworkmanifest/manifest.f4m", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 10091, + "format": "applehttp", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/10091/protocol/https/format/applehttp/a.m3u8", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11241, + "format": "applehttp", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11241/protocol/https/format/applehttp/a.m3u8", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11611, + "format": "mpegdash", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11611/protocol/https/format/mpegdash/manifest.mpd", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11261, + "format": "mpegdash", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11261/protocol/https/format/mpegdash/manifest.mpd", + "drm": [], + "objectType": "KalturaPlaybackSource" + }, + { + "deliveryProfileId": 11251, + "format": "sl", + "protocols": "http,https", + "flavorIds": "1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4", + "url": "https://cdnapisec.kaltura.com/p/1082342/sp/1082342/playManifest/entryId/1_rsrdfext/flavorIds/1_ha0nqwz8,1_gw7u4nf1,1_rql6sqaa,1_sufd1yd9,1_9xvkk7a5,1_4typ4pat,1_n75294r4/deliveryProfileId/11251/protocol/https/format/sl/a.ism", + "drm": [], + "objectType": "KalturaPlaybackSource" + } + ], + "flavorAssets": [ + { + "flavorParamsId": 487041, + "width": 640, + "height": 360, + "bitrate": 471, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_ha0nqwz8", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 3164, + "tags": "mobile,web,mbr,iphone,iphonenew", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777079, + "description": "", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487051, + "width": 640, + "height": 360, + "bitrate": 670, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_gw7u4nf1", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 4505, + "tags": "mobile,web,mbr,iphone,iphonenew", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777079, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487061, + "width": 640, + "height": 360, + "bitrate": 964, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_rql6sqaa", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 6400, + "tags": "mobile,web,mbr,ipad,ipadnew,dash", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777064, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487071, + "width": 1280, + "height": 720, + "bitrate": 1547, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_sufd1yd9", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 10444, + "tags": "mobile,web,mbr,ipad,ipadnew,dash", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777080, + "description": "", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487081, + "width": 1280, + "height": 720, + "bitrate": 2628, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_9xvkk7a5", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 17408, + "tags": "web,mbr,dash", + "fileExt": "mp4", + "createdAt": 1484777022, + "updatedAt": 1484777115, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 487091, + "width": 1920, + "height": 1080, + "bitrate": 4128, + "frameRate": 25, + "isOriginal": false, + "isWeb": true, + "containerFormat": "isom", + "videoCodecId": "avc1", + "status": 2, + "language": "Undefined", + "id": "1_4typ4pat", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 27443, + "tags": "web,mbr,dash", + "fileExt": "mp4", + "createdAt": 1484777023, + "updatedAt": 1484777325, + "description": "audio warnings: 2106,#Redundant bitrate.\n", + "objectType": "KalturaFlavorAsset" + }, + { + "flavorParamsId": 0, + "width": 1920, + "height": 1080, + "bitrate": 18022, + "frameRate": 25, + "isOriginal": true, + "isWeb": false, + "containerFormat": "qt", + "videoCodecId": "avc1", + "status": 2, + "language": "English", + "id": "1_n75294r4", + "entryId": "1_rsrdfext", + "partnerId": 1082342, + "version": "1", + "size": 117760, + "tags": "source", + "fileExt": "mov", + "createdAt": 1484776914, + "updatedAt": 1484777022, + "description": "", + "objectType": "KalturaFlavorAsset" + } + ], + "actions": [], + "messages": [ + { + "message": "Out of scheduling\nWe're sorry, this content is currently unavailable.", + "code": "SCHEDULED_RESTRICTED", + "objectType": "KalturaAccessControlMessage" + } + ], + "objectType": "KalturaPlaybackContext" + }, + { + "objects": [ + { + "id": 331744001, + "partnerId": 1082342, + "metadataProfileId": 244381, + "metadataProfileVersion": 421, + "metadataObjectType": 1, + "objectId": "1_rsrdfext", + "version": 1, + "createdAt": 1484776887, + "updatedAt": 1484776887, + "status": 1, + "xml": "\nFO21934-HDTX-SWE.mov\nsv_SE\nFTA-nm\ntv-dc\nclip\nDisney Channel\nIBMS\nShow - TV\n1483225200\n1485903600\nNo Age Consent\n", + "objectType": "KalturaMetadata" + }, + { + "id": 331744121, + "partnerId": 1082342, + "metadataProfileId": 7330521, + "metadataProfileVersion": 61, + "metadataObjectType": 1, + "objectId": "1_rsrdfext", + "version": 11, + "createdAt": 1484776903, + "updatedAt": 1489508219, + "status": 1, + "xml": "\n FO21934-HDTX-SWE\n se-dc-lf\n VOD\n android\n Disney Channel SE\n\n", + "objectType": "KalturaMetadata" + } + ], + "totalCount": 2, + "objectType": "KalturaMetadataListResponse" + }, + { + "id": 38621471, + "name": "Static 2.53", + "description": "", + "partnerId": 1082342, + "objType": 1, + "objTypeAsString": "Widget", + "width": "560", + "height": "395", + "swfUrl": "/flash/kdp3/v3.9.9/kdp3.swf", + "confFile": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n