Skip to content

Commit

Permalink
feat(FEC-8364): support adding evaluated expressions also on player s…
Browse files Browse the repository at this point in the history
…etup and configure API (#139)

When calling player.setup or player.configure we need to:

Evaluate new expressions that are passed with tokens
Add new evaluation tokens to the list of evaluated tokens so they can be re-evaluated on each media change.
This feature will create a plugin token config store.
On each call to evaluate the list of new expressions will be analyzed, and if it contains evaluation tokens these tokens will be analyzed and also be saved in the config store so in next call to configure, loadMedia or setMedia the player will run the evaluation process on them.
  • Loading branch information
OrenMe authored and yairans committed Jul 3, 2018
1 parent 575b62c commit 84542c8
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 94 deletions.
134 changes: 134 additions & 0 deletions src/common/plugins/plugins-config-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//@flow
import {Utils} from 'playkit-js'

type dataStoreType = {[pluginName: string]: Object};
const defaultConfig: dataStoreType = {
"youbora": {
"playerVersion": "{{pVersion}}",
"playerName": "{{pName}}",
"entryId": "{{entryId}}",
"entryName": "{{entryName}}",
"entryType": "{{entryType}}",
"sessionId": "{{sessionId}}",
"uiConfId": "{{uiConfId}}"
},
"kanalytics": {
"playerVersion": "{{pVersion}}",
"entryId": "{{entryId}}",
"entryType": "{{entryType}}",
"sessionId": "{{sessionId}}",
"ks": "{{ks}}",
"uiConfId": "{{uiConfId}}",
"partnerId": "{{partnerId}}",
"referrer": "{{referrer}}"
},
"googleAnalytics": {
"entryId": "{{entryId}}",
"entryName": "{{entryName}}",
"uiConfId": "{{uiConfId}}",
"partnerId": "{{partnerId}}"
},
"ottAnalytics": {
"entryId": "{{entryId}}",
"ks": "{{ks}}",
"isAnonymous": "{{isAnonymous}}",
"partnerId": "{{partnerId}}",
"serviceUrl": "{{serviceUrl}}"
},
"ima": {
"playerVersion": "{{pVersion}}",
"playerName": "{{pName}}"
},
"kava": {
"playerVersion": "{{pVersion}}",
"playerName": "{{pName}}",
"partnerId": "{{partnerId}}",
"entryId": "{{entryId}}",
"entryType": "{{entryType}}",
"sessionId": "{{sessionId}}",
"ks": "{{ks}}",
"uiConfId": "{{uiConfId}}",
"referrer": "{{referrer}}"
},
"comscore": {
"playerVersion": "{{pVersion}}"
},
"vr": {
"rootElement": "{{domRootElementId}}"
}
};

let config = Utils.Object.copyDeep(defaultConfig);
const templateRegex = new RegExp(('{{.*}}'));

/**
* extract the object members which include an evaluation token of type {{.*}}
* @param {Object} obj - the config object
* @returns {dataStoreType} - the new object with new tokens
*/
const resolveNewConfig = (obj = {}): Object =>
Object.entries(obj)
.reduce(
(product, [key, value]): Object => {
if (Utils.Object.isObject(value)) {
product[key] = resolveNewConfig(value)
} else if (typeof value === "string" && templateRegex.test(value)) {
product[key] = value;
} else {
product[key] = undefined;
}
return product;
},
{}
);

/**
* remove undefined members from the token data store
* @param {Object} obj - the config object
* @returns {dataStoreType} - the new object with valid evaluate tokens
*/
const removeUndefineds = (obj = {}): Object =>
Object.entries(obj)
.reduce(
(product, [key, value]): Object => {
if (Utils.Object.isObject(value)) {
product[key] = removeUndefineds(value)
} else if (value) {
product[key] = value;
}
return product;
},
{}
);


const pluginConfig = {
/**
* return the token store object
* @returns {*|any} - token store object
*/
get: (): dataStoreType => {
return config;
},
/**
* recalculate the token store data, if new config with token is passed then add it to the data store, and if
* an existing token needs to be removed then remove it
* @param {?dataStoreType} pluginsConfig - the new config object
* @returns {void}
*/
set: (pluginsConfig: ?dataStoreType): void => {
if (pluginsConfig) {
const newConfig = resolveNewConfig(pluginsConfig);
config = removeUndefineds(Utils.Object.mergeDeep(config, newConfig));
}
},
/**
* reset the config store to its initial state
* @returns {void}
*/
reset: (): void => {
config = Utils.Object.copyDeep(defaultConfig);
}
};

export {pluginConfig, templateRegex};
121 changes: 84 additions & 37 deletions src/common/plugins/plugins-config.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,95 @@
//@flow
import pluginsConfig from './plugins-config.json'
import {pluginConfig, templateRegex} from './plugins-config-store.js'
import evaluate from '../utils/evaluate'
import {getReferrer} from '../utils/kaltura-params'
import {Utils} from 'playkit-js'

/**
* returns weather value is evaluated
* @param {*} value - the value to be checked
* @returns {boolean} - value is evaluated
*/
const isValueEvaluated = (value: any): boolean =>
(typeof value === "number" || typeof value === "string" || typeof value === "boolean") &&
!templateRegex.test(value.toString());

/**
* remove unevaluated expressions form object
* @param {Object} obj - the object examine
* @returns {Object} - the object without unevaluated strings
*/
const removeUnevaluatedExpression = (obj = {}): Object =>
Object.entries(obj)
.reduce(
(product, [key, value]): Object => {
if (Utils.Object.isObject(value)) {
product[key] = removeUnevaluatedExpression(value);
} else if (isValueEvaluated()) {
product[key] = value;
}
return product;
},
{}
);

/**
* returns the data model for evaluating evaluation tokens
* @param {KalturaPlayerOptionsObject} options - the kaltura player options object
* @returns {Object} - data model
*/
const getModel = (options: KalturaPlayerOptionsObject): Object => {
const dataModel: Object = {
pVersion: __VERSION__,
pName: __NAME__,
domRootElementId: options.targetId
};
if (options.provider && options.provider.env) {
dataModel['serviceUrl'] = options.provider.env.serviceUrl;
}
const entryDataModel = {
referrer: getReferrer()
};
if (options.provider) {
Utils.Object.mergeDeep(entryDataModel, {
ks: options.provider.ks,
uiConfId: options.provider.uiConfId,
partnerId: options.provider.partnerId
});
}
if (options.session) {
Utils.Object.mergeDeep(entryDataModel, {
sessionId: options.session.id,
ks: options.session.ks,
isAnonymous: options.session.isAnonymous,
uiConfId: options.session.uiConfId,
partnerId: options.session.partnerId
});
}
if (options.sources) {
Utils.Object.mergeDeep(entryDataModel, {
entryId: options.sources.id,
entryName: options.sources.metadata && options.sources.metadata.name,
entryType: options.sources.type
});
}
Object.keys(entryDataModel).forEach(key => {
if (entryDataModel[key] === undefined) {
delete entryDataModel[key];
}
});
Utils.Object.mergeDeep(dataModel, entryDataModel);
return dataModel;
};

/**
* @param {KalturaPlayerOptionsObject} options - player options
* @return {void}
*/
function evaluatePluginsConfig(options: KalturaPlayerOptionsObject): void {
if (options.plugins) {
const dataModel: Object = {
pVersion: __VERSION__,
pName: __NAME__,
domRootElementId: options.targetId
};
if (options.provider && options.provider.env) {
dataModel['serviceUrl'] = options.provider.env.serviceUrl;
}
if (options.session && options.sources) {
const entryDataModel = {
entryId: options.sources.id,
entryName: options.sources.metadata.name,
entryType: options.sources.type,
sessionId: options.session.id,
ks: options.session.ks,
isAnonymous: options.session.isAnonymous,
uiConfId: options.session.uiConfId,
partnerId: options.session.partnerId,
referrer: getReferrer()
};
Object.keys(entryDataModel).forEach(key => {
if (entryDataModel[key] === undefined) {
delete entryDataModel[key];
}
});
Utils.Object.mergeDeep(dataModel, entryDataModel);
}
const evaluatedConfig = evaluate(JSON.stringify(pluginsConfig), dataModel);
pluginConfig.set(options.plugins);
const dataModel = getModel(options);
const evaluatedConfig = evaluate(JSON.stringify(pluginConfig.get()), dataModel);
let evaluatedConfigObj;
try {
evaluatedConfigObj = JSON.parse(evaluatedConfig, function (key) {
Expand All @@ -49,14 +101,9 @@ function evaluatePluginsConfig(options: KalturaPlayerOptionsObject): void {
} catch (e) {
evaluatedConfigObj = {};
}
const templateRegex = new RegExp(('{{.*}}'));
Object.keys(evaluatedConfigObj).forEach((plugin) => {
Object.keys(evaluatedConfigObj[plugin]).forEach((key) => {
if (templateRegex.test(evaluatedConfigObj[plugin][key])) {
delete evaluatedConfigObj[plugin][key];
}
});
});
evaluatedConfigObj = removeUnevaluatedExpression(evaluatedConfigObj);
options.plugins = removeUnevaluatedExpression(options.plugins);

if (options.plugins) {
Object.keys(options.plugins).forEach((pluginName) => {
if (options.plugins && options.plugins[pluginName]) {
Expand Down
55 changes: 0 additions & 55 deletions src/common/plugins/plugins-config.json

This file was deleted.

5 changes: 3 additions & 2 deletions src/kaltura-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export default class KalturaPlayer {

configure(config: Object): void {
config = supportLegacyOptions(config);
// $FlowFixMe
evaluatePluginsConfig(config);
this._playerConfigure(config);
if (config.ui) {
this._uiWrapper.setConfig(config.ui);
Expand All @@ -56,11 +58,10 @@ export default class KalturaPlayer {
this._logger.debug('setMedia', mediaConfig);
const playerConfig = Utils.Object.copyDeep(mediaConfig);
Utils.Object.mergeDeep(playerConfig.sources, this._player.config.sources);
Utils.Object.mergeDeep(playerConfig.plugins, this._player.config.plugins);
Utils.Object.mergeDeep(playerConfig.session, this._player.config.session);
Object.keys(this._player.config.plugins).forEach(name => {playerConfig.plugins[name] = {}});
addKalturaPoster(playerConfig.sources, mediaConfig.sources, this._player.dimensions);
addKalturaParams(this._player, playerConfig);
evaluatePluginsConfig(playerConfig);
this._uiWrapper.setSeekbarConfig(mediaConfig);
this._player.configure(playerConfig);
}
Expand Down

0 comments on commit 84542c8

Please sign in to comment.