import { Env, TextStyle, Utils, setCapabilities, EngineType, DrmScheme, getLogger, LogLevel, setLogHandler, setLogLevel as _setLogLevel, ILogLevel, ILogHandler, PKSourcesConfigObject } from '@playkit-js/playkit-js'; import { ProviderOptionsObject } from '@playkit-js/playkit-js-providers/types'; import { ValidationErrorType } from './validation-error'; import LocalStorageManager from '../storage/local-storage-manager'; import { KalturaPlayer } from '../../kaltura-player'; import { addClientTag, addReferrer, updateSessionIdInUrl } from './kaltura-params'; import { DEFAULT_OBSERVED_THRESHOLDS, DEFAULT_PLAYER_THRESHOLD } from './viewability-manager'; import SessionStorageManager from '../storage/session-storage-manager'; import { BaseStorageManager } from '../storage/base-storage-manager'; import { BasePlugin } from '../plugins'; import { KalturaPlayerConfig, LegacyPartialKPOptionsObject, PartialKPOptionsObject, PluginsConfig, PlaybackConfig } from '../../types'; import { SessionIdGenerator } from './session-id-generator'; import { UiConfIdSingleton } from './ui-conf-id-singleton'; const setupMessages: Array = []; const CONTAINER_CLASS_NAME: string = 'kaltura-player-container'; const KALTURA_PLAYER_DEBUG_QS: string = 'debugKalturaPlayer'; const KALTURA_PLAYER_START_TIME_QS: string = 'kalturaStartTime'; const KALTURA_PLAYER_END_TIME_QS: string = 'kalturaEndTime'; const KALTURA_PLAYER_CLIP_START_TIME_QS: string = 'kalturaSeekFrom'; const KALTURA_PLAYER_CLIP_END_TIME_QS: string = 'kalturaClipTo'; const KAVA_DEFAULT_PARTNER = 2504201; // eslint-disable-next-line max-len const KAVA_DEFAULT_IMPRESSION = `https://analytics.kaltura.com/api_v3/index.php?service=analytics&action=trackEvent&apiVersion=3.3.0&format=1&eventType=1&partnerId=${KAVA_DEFAULT_PARTNER}&entryId=1_3bwzbc9o&&eventIndex=1&position=0`; declare let __CONFIG_DOCS_URL__: string; const logHandlers: Array<(messages: any[], context: object) => void> = []; const LOG_BUFFER_SIZE = 1000; const logBuffer: string[] = []; const logHandler = (messages: any[], ctx: { name: string }): void => { logHandlers.forEach((handler: ILogHandler): void => { handler(messages, { ...ctx, level: LogLevel.INFO }); }); }; /** * Validate the initial user config. * @private * @param {PartialKPOptionsObject} options - partial kaltura player options. * @returns {void} */ function validateConfig(options: PartialKPOptionsObject): void { if (!options) { throw new Error(ValidationErrorType.INITIAL_CONFIG_REQUIRED); } validateTargetId(options.targetId); } /** * Validate the user input for target id. * @private * @param {string} targetId - The DOM element id which the player will be append to. * @returns {void} */ function validateTargetId(targetId: string): void { if (!targetId) { throw new Error(ValidationErrorType.TARGET_ID_REQUIRED); } const targetIdElement = document.getElementById(targetId); if (!targetIdElement) { throw new Error(ValidationErrorType.DOM_ELEMENT_WITH_TARGET_ID_REQUIRED + targetId); } if (targetIdElement.getElementsByClassName(CONTAINER_CLASS_NAME).length > 0) { throw new Error(ValidationErrorType.TARGET_ID_ALREADY_USED + targetId); } } /** * @param {string} url - url * @param {string} productVersion - product version * @return {string} - the url with the product version appended in the query params * @private */ function addProductVersion(url: string, productVersion?: string): string { if (productVersion) { url += `&clientVer=${productVersion}`; } return url; } /** * Validate the initial user input for the provider options. * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function validateProviderConfig(options: KalturaPlayerConfig): void { const { provider: providerOptions }: { provider: ProviderOptionsObject } = options; const productVersion: string = getServerUIConf()?.productVersion; if (!providerOptions.partnerId || providerOptions.partnerId === KAVA_DEFAULT_PARTNER) { //create source object as a 'hack' to be able to use utility functions on url const source = { url: KAVA_DEFAULT_IMPRESSION, mimetype: '' }; source.url = addProductVersion(source.url, productVersion); source.url = addReferrer(source.url); source.url = addClientTag(source.url, productVersion); source.url = updateSessionIdInUrl(null, source.url, SessionIdGenerator.next()); navigator.sendBeacon && navigator.sendBeacon(source.url); } } /** * Creates the player container dom element. * @private * @param {string} targetId - The div id which the player will append to. * @returns {string} - The player container id. */ function createKalturaPlayerContainer(targetId: string): string { const el = document.createElement('div'); el.id = Utils.Generator.uniqueId(5); el.className = CONTAINER_CLASS_NAME; el.setAttribute('tabindex', '-1'); const parentNode = document.getElementById(targetId); if (parentNode && el) { parentNode.appendChild(el); } return el.id; } /** * Initializes the storage managers. * @private * @returns {void} */ function initializeStorageManagers(): void { LocalStorageManager.initialize(); SessionStorageManager.initialize(); } /** * Sets the storage config on the player config if certain conditions are met. * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function setStorageConfig(options: KalturaPlayerConfig): void { if (!options.disableUserCache) { BaseStorageManager.setStorageConfig(options); } } /** * Applies cache support if it's supported by the environment. * @private * @param {KalturaPlayer} player - The Kaltura player. * @returns {void} */ function applyStorageSupport(player: KalturaPlayer): void { BaseStorageManager.attachAll(player); } /** * Loads the registered remote players. * @private * @param {KalturaPlayerConfig} defaultOptions - The kaltura player options. * @param {KalturaPlayer} player - The Kaltura player. * @returns {void} */ function applyCastSupport(defaultOptions: KalturaPlayerConfig, player: KalturaPlayer): void { if (defaultOptions.cast) { player.remotePlayerManager.load(defaultOptions.cast, player); } } /** * Sets the player text style from storage. * @private * @param {KalturaPlayer} player - The Kaltura player. * @returns {void} */ function setStorageTextStyle(player: KalturaPlayer): void { if (!player.config.disableUserCache && LocalStorageManager.isStorageAvailable()) { const textStyleObj = LocalStorageManager.getPlayerTextStyle(); if (textStyleObj) { player.textStyle = Utils.Object.mergeDeep(new TextStyle(), textStyleObj); } } } /** * Call to setCapabilities on the first UI_CLICKED event * @private * @param {Player} player - The Kaltura player. * @returns {void} */ function attachToFirstClick(player: KalturaPlayer): void { if (Env.isIOS || Env.isIPadOS) { const onUIClicked = (): void => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore player.removeEventListener(player.Event.UI.UI_CLICKED, onUIClicked); setCapabilities(EngineType.HTML5, { autoplay: true }); }; const onSourceSelected = (): void => { player.removeEventListener( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore player.Event.SOURCE_SELECTED, onSourceSelected ); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore player.addEventListener(player.Event.UI.UI_CLICKED, onUIClicked); }; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore player.addEventListener(player.Event.SOURCE_SELECTED, onSourceSelected); } } /** * check the player debug mode according to config or URL query string params * @private * @returns {boolean} - if to set debug mode or not */ function isDebugMode(): boolean { let isDebugMode = false; if (window.DEBUG_KALTURA_PLAYER === true) { isDebugMode = true; } else { isDebugMode = getUrlParameter(KALTURA_PLAYER_DEBUG_QS) === ''; } return isDebugMode; } /** * get the parameter for start time * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function maybeApplyStartTimeQueryParam(options: KalturaPlayerConfig): void { const startTime = parseFloat(getUrlParameter(KALTURA_PLAYER_START_TIME_QS)); if (!isNaN(startTime)) { Utils.Object.createPropertyPath(options, 'sources.startTime', startTime); } } /** * get the parameter for end time * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function maybeApplyEndTimeQueryParam(options: KalturaPlayerConfig): void { const endTime = parseFloat(getUrlParameter(KALTURA_PLAYER_END_TIME_QS)); if (!isNaN(endTime)) { Utils.Object.createPropertyPath(options, 'sources.endTime', endTime); } } /** * get the parameters for seekFrom and clipTo * @private * @param {KPOptionsObject} options - kaltura player options * @returns {void} */ function maybeApplyClipQueryParams(options: KalturaPlayerConfig): void { const seekFrom = parseFloat(getUrlParameter(KALTURA_PLAYER_CLIP_START_TIME_QS)!); if (!isNaN(seekFrom)) { Utils.Object.createPropertyPath(options, 'sources.seekFrom', seekFrom); } const clipTo = parseFloat(getUrlParameter(KALTURA_PLAYER_CLIP_END_TIME_QS)!); if (!isNaN(clipTo)) { Utils.Object.createPropertyPath(options, 'sources.clipTo', clipTo); } } /** * set the logger * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function setLogOptions(options: KalturaPlayerConfig): void { if (!Utils.Object.getPropertyPath(options, 'ui.log')) { Utils.Object.createPropertyPath(options, 'ui.log', {}); } if (!Utils.Object.getPropertyPath(options, 'provider.log')) { Utils.Object.createPropertyPath(options, 'provider.log', {}); } if (!Utils.Object.getPropertyPath(options, 'log')) { Utils.Object.createPropertyPath(options, 'log', {}); } if (Utils.Object.getPropertyPath(options, 'log.useDebugInfo') === undefined) { options.log!.useDebugInfo = true; } setLogHandlers(options); let logLevelObj: ILogLevel = LogLevel.ERROR; if (options.log && isDebugMode()) { logLevelObj = LogLevel.DEBUG; options.log.level = LogLevel.DEBUG.name; } else if (options.log && options.log.level && LogLevel[options.log.level]) { logLevelObj = LogLevel[options.log.level]; } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore options.ui.log.level = options.provider.log.level = logLevelObj.name; _setLogLevel(logLevelObj); } function setLogHandlers(options: KalturaPlayerConfig): void { if (options.log && !options.log.handler && !options.log.useDebugInfo) return; function getFormattedMessage(messages: any[], ctx: any): string { const messagesStr = [...messages] .map((msg) => { try { // stringify objects, but protect against errors (e.g. circular references) return typeof msg === 'string' ? msg : JSON.stringify(msg); } catch (e) { return msg; } }) .join(' '); return `[${(ctx as { name: string }).name}] ${messagesStr}`; } logHandlers.push((messages, ctx) => { // when we use a handler, we have to explicitly print the message to console // eslint-disable-next-line no-console console.log(getFormattedMessage(messages, ctx)); }); if (options.log && (options.log as any).useDebugInfo) { logHandlers.push((messages, ctx) => { const message = getFormattedMessage(messages, ctx); if (logBuffer.length === LOG_BUFFER_SIZE) { logBuffer.shift(); } logBuffer.push(`${new Date().toLocaleString()} ${message} \n`); }); } // add custom log handlers if (options.log && typeof options.log.handler === 'function') { logHandlers.push(options.log.handler); } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore setLogHandler(logHandler); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore options.ui.log.handler = options.provider.log.handler = logHandler; } /** * gets the url query string parameter * @private * @param {string} name - name of query string param * @returns {?string} - value of the query string param or null if doesn't exist */ function getUrlParameter(name: string): string | null { const getUrlParamPolyfill = (name: string): string | null => { name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]'); const regex = new RegExp('[\\?&]' + name + '=([^&#]*)'); const results = regex.exec(location.search); const isExist = location.search.indexOf(name) > -1; return results === null ? (isExist ? '' : null) : decodeURIComponent(results[1].replace(/\+/g, ' ')); }; let value; if (window.URLSearchParams) { const urlParams = new URLSearchParams(window.location.search); value = urlParams.get(name); } else { value = getUrlParamPolyfill(name); } return value; } /** * get the server UIConf * @private * @returns {Object} - The server UIConf */ function getServerUIConf(): any { const uiConfId = UiConfIdSingleton.getInstance().getUiConfId(); if (uiConfId !== '' && window.KalturaPlayers && window.KalturaPlayers[uiConfId]?.config) { return window.KalturaPlayers[uiConfId].config; } return window.__kalturaplayerdata || {}; } /** * set the UIConfId if exists * @private * @param {PartialKPOptionsObject} options - partial user kaltura player options. * @returns {void} */ function setUIConfId(options: PartialKPOptionsObject): void { // Store uiConfId if it exists in options if (options.provider && options.provider.uiConfId) { UiConfIdSingleton.getInstance().setUiConfId(options.provider.uiConfId.toString()); } } /** * Gets the default options after merging the user options with the uiConf options and the default internal options. * @private * @param {PartialKPOptionsObject} options - partial user kaltura player options. * @returns {KalturaPlayerConfig} - default kaltura player options. */ function getDefaultOptions(options: PartialKPOptionsObject): KalturaPlayerConfig { const targetId = createKalturaPlayerContainer(options.targetId); // TODO - fix all KalturaPlayerConfig and Partial relationships // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore let defaultOptions: KalturaPlayerConfig = { targetId: options.targetId, provider: { partnerId: options.provider.partnerId }, ui: { targetId: targetId }, plugins: {}, advertising: { adBreaks: [] }, viewability: { observedThresholds: DEFAULT_OBSERVED_THRESHOLDS, playerThreshold: DEFAULT_PLAYER_THRESHOLD } }; Utils.Object.mergeDeep(defaultOptions, options); if (!options.provider.ignoreServerConfig) { const serverUIConf = Utils.Object.copyDeep(getServerUIConf()); delete serverUIConf.productVersion; defaultOptions = Utils.Object.mergeDeep({}, supportLegacyOptions(serverUIConf), defaultOptions); } checkNativeHlsSupport(defaultOptions); checkNativeTextTracksSupport(defaultOptions); configureSmartTVDefaultOptions(defaultOptions); configureEdgeDRMDefaultOptions(defaultOptions); configureIMADefaultOptions(defaultOptions); configureDAIDefaultOptions(defaultOptions); configureBumperDefaultOptions(defaultOptions); maybeSetFullScreenConfig(defaultOptions); maybeSetCapabilitiesForIos(defaultOptions); return defaultOptions; } /** * Sets config option for native HLS playback * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function checkNativeHlsSupport(options: KalturaPlayerConfig): void { if ((Env.isMacOS && Env.isSafari) || Env.isIOS) { const preferNativeHlsValue = Utils.Object.getPropertyPath(options, 'playback.preferNative.hls'); if (typeof preferNativeHlsValue !== 'boolean') { Utils.Object.mergeDeep(options, { playback: { preferNative: { hls: true } } }); } } } /** * Sets config option for native text track support * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function checkNativeTextTracksSupport(options: KalturaPlayerConfig): void { if ((Env.isMacOS && Env.isSafari) || Env.isIOS) { const useNativeTextTrack = Utils.Object.getPropertyPath(options, 'text.useNativeTextTrack'); if (typeof useNativeTextTrack !== 'boolean') { Utils.Object.mergeDeep(options, { text: { useNativeTextTrack: true } }); } } } /** * Sets config option for Ads with MSE * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function _configureAdsWithMSE(options: KalturaPlayerConfig): void { const playAdsWithMSE = Utils.Object.getPropertyPath(options, 'playback.playAdsWithMSE'); if (typeof playAdsWithMSE !== 'boolean') { options = Utils.Object.createPropertyPath(options, 'playback.playAdsWithMSE', true); } const disableMediaPreloadIma = Utils.Object.getPropertyPath(options, 'plugins.ima.disableMediaPreload'); const disableMediaPreloadBumper = Utils.Object.getPropertyPath(options, 'plugins.bumper.disableMediaPreload'); if (options.plugins && options.plugins.ima && typeof disableMediaPreloadIma !== 'boolean') { options = Utils.Object.createPropertyPath(options, 'plugins.ima.disableMediaPreload', true); } if (options.plugins && options.plugins.bumper && typeof disableMediaPreloadBumper !== 'boolean') { options = Utils.Object.createPropertyPath(options, 'plugins.bumper.disableMediaPreload', true); } } /** * Sets config option for LG TV SDK 2 live which has problem with long duration buffer * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function _configureLGSDK2HlsLiveConfig(options: KalturaPlayerConfig): void { const hlsLiveConfig = Utils.Object.getPropertyPath(options, 'playback.options.html5.hls.liveSyncDurationCount'); //webos SDK 2 and less detect as safari browser greater version is chrome if (typeof hlsLiveConfig !== 'boolean' && Env.isSafari) { options = Utils.Object.createPropertyPath(options, 'playback.options.html5.hls.liveSyncDurationCount', 2); } } /** * Sets config option for smart TV * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function configureSmartTVDefaultOptions(options: KalturaPlayerConfig): void { if (Env.isSmartTV) { //relevant for LG SDK 4 and HISENSE which doesn't support our check for autoplay with base64 source setCapabilities(EngineType.HTML5, { autoplay: true }); _configureAdsWithMSE(options); _configureLGSDK2HlsLiveConfig(options); if (options.plugins && options.plugins.ima) { const imaForceReload = Utils.Object.getPropertyPath(options, 'plugins.ima.forceReloadMediaAfterAds'); const delayUntilSourceSelected = Utils.Object.getPropertyPath(options, 'plugins.ima.delayInitUntilSourceSelected'); if (typeof imaForceReload !== 'boolean') { options = Utils.Object.createPropertyPath(options, 'plugins.ima.forceReloadMediaAfterAds', true); } if (typeof delayUntilSourceSelected !== 'boolean') { options = Utils.Object.createPropertyPath(options, 'plugins.ima.delayInitUntilSourceSelected', true); } } if (options.plugins && options.plugins.youbora) { const playheadMonitorInterval = Utils.Object.getPropertyPath(options, 'plugins.youbora.playheadMonitorInterval'); if (typeof playheadMonitorInterval !== 'number') { options = Utils.Object.createPropertyPath(options, 'plugins.youbora.playheadMonitorInterval', 2000); } } const lowLatencyMode = Utils.Object.getPropertyPath(options, 'streaming.lowLatencyMode'); if (typeof lowLatencyMode !== 'boolean') { options = Utils.Object.createPropertyPath(options, 'streaming.lowLatencyMode', false); } } } /** * prefer Playready in edge - from chromium version of edge Widevine is option as well * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function configureEdgeDRMDefaultOptions(options: KalturaPlayerConfig): void { if (Env.browser.name === 'Edge') { const keySystem = Utils.Object.getPropertyPath(options, 'drm.keySystem'); if (!keySystem) { if (Env.os.name === 'Windows') { options = Utils.Object.createPropertyPath(options, 'drm.keySystem', DrmScheme.PLAYREADY); } else { options = Utils.Object.createPropertyPath(options, 'drm.keySystem', DrmScheme.WIDEVINE); } } } } /** * Sets default config option for ima plugin * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function configureIMADefaultOptions(options: KalturaPlayerConfig): void { if (Env.isIOS && options.plugins && options.plugins.ima && !options.plugins.ima['disable']) { const playsinline = Utils.Object.getPropertyPath(options, 'playback.playsinline'); const disableMediaPreloadIma = Utils.Object.getPropertyPath(options, 'plugins.ima.disableMediaPreload'); if (playsinline === false && typeof disableMediaPreloadIma !== 'boolean') { Utils.Object.createPropertyPath(options, 'plugins.ima.disableMediaPreload', true); } } } /** * Sets default config option for dai plugin * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function configureDAIDefaultOptions(options: KalturaPlayerConfig): void { if (options.plugins && options.plugins.imadai && !options.plugins.imadai['disable']) { const autoStartLoadConfig = Utils.Object.getPropertyPath(options, 'playback.options.html5.hls.autoStartLoad'); if (typeof autoStartLoadConfig !== 'boolean') { Utils.Object.mergeDeep(options, { playback: { options: { html5: { hls: { autoStartLoad: false } } } } }); } } } /** * Sets default config option for bumper plugin when ima-dai enabled * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function configureBumperDefaultOptions(options: KalturaPlayerConfig): void { const bumperConfig = Utils.Object.getPropertyPath(options, 'plugins.bumper'); const daiConfig = Utils.Object.getPropertyPath(options, 'plugins.imadai'); if (bumperConfig) { const newBumperConfig: any = {}; if ( typeof bumperConfig.playOnMainVideoTag !== 'boolean' && (Env.isSmartTV || (Env.isIOS && options.playback && options.playback.playsinline === false)) ) { newBumperConfig['playOnMainVideoTag'] = true; } if (daiConfig && !daiConfig.disable) { if (!Array.isArray(bumperConfig.position)) { newBumperConfig['position'] = [0]; } if (typeof bumperConfig.disableMediaPreload !== 'boolean') { newBumperConfig['disableMediaPreload'] = true; } } Utils.Object.mergeDeep(options, { plugins: { bumper: newBumperConfig } }); } } /** * print kaltura version to log by configuration * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function printKalturaPlayerVersionToLog(options: PartialKPOptionsObject | LegacyPartialKPOptionsObject): void { const playerVersion = Utils.Object.getPropertyPath(options, 'log.playerVersion'); if (playerVersion !== false) { _setLogLevel(LogLevel.INFO); getLogger().log(`%c ${__NAME__} ${__VERSION__}`, 'color: #ff98f9; font-size: large'); getLogger().log(`%c For more details see ${__PACKAGE_URL__}`, 'color: #ff98f9;'); } } /** * Transform options structure from legacy structure to new structure. * @private * @param {Object} options - The options with the legacy structure. * @return {PartialKPOptionsObject} - Partial options with the expected structure. */ function supportLegacyOptions(options: LegacyPartialKPOptionsObject): PartialKPOptionsObject { const removePlayerEntry = (): void => { if (options.player) { setupMessages.push({ level: 'warn', msg: `Path config.player will be deprecated soon. Please update your config structure as describe here: ${__CONFIG_DOCS_URL__}` }); const playerOptions = Utils.Object.copyDeep(options.player); delete options.player; Utils.Object.mergeDeep(options, playerOptions); } }; const moveProp = (propPath: string, targetPath: string): void => { if (Utils.Object.hasPropertyPath(options, propPath)) { setupMessages.push({ level: 'warn', msg: `Path config.player.${propPath} will be deprecated soon. Please update your config structure as describe here: ${__CONFIG_DOCS_URL__}` }); if (!Utils.Object.hasPropertyPath(options, targetPath)) { const propValue = Utils.Object.getPropertyPath(options, propPath); const propObj = Utils.Object.createPropertyPath({}, targetPath, propValue); Utils.Object.mergeDeep(options, propObj); Utils.Object.deletePropertyPath(options, propPath); } else { Utils.Object.deletePropertyPath(options, propPath); } } }; const moves = [ ['duration', 'sources.duration'], ['type', 'sources.type'], ['dvr', 'sources.dvr'], ['id', 'sources.id'], ['name', 'metadata.name'], ['metadata.poster', 'sources.poster'], ['metadata', 'sources.metadata'], ['logLevel', 'log.level'], ['ui.components.fullscreen.inBrowserFullscreenForIOS', 'playback.inBrowserFullscreen'], ['playback.enableCEA708Captions', 'text.enableCEA708Captions'], ['playback.useNativeTextTrack', 'text.useNativeTextTrack'], ['playback.options.html5.dash.useShakaTextTrackDisplay', 'text.useShakaTextTrackDisplay'], ['playback.captionsTextTrack1Label', 'text.captionsTextTrack1Label'], ['playback.captionsTextTrack1LanguageCode', 'text.captionsTextTrack1LanguageCode'], ['playback.captionsTextTrack2Label', 'text.captionsTextTrack2Label'], ['playback.captionsTextTrack2LanguageCode', 'text.captionsTextTrack2LanguageCode'], ['plugins.visibility.threshold', 'viewability.playerThreshold'], ['plugins.visibility.floating', 'plugins.floating'], ['playback.startTime', 'sources.startTime'] ]; removePlayerEntry(); moves.forEach((move) => moveProp(move[0], move[1])); return options; } /** * Prints early setup messages. * @private * @returns {void} */ function printSetupMessages(): void { setupMessages.forEach((msgObj) => getLogger('KalturaPlayer:Setup')[msgObj.level](msgObj.msg)); } /** * Prints early setup messages. * @private * @param {Player} player - The player. * @param {string} engine - The player engine name. * @param {string} format - The player engine format. * @returns {PKPlaybackConfigObject} - The playback config. */ function addEngineToStreamPriority(player: KalturaPlayer, engine: string, format: string): PlaybackConfig { const playbackConfig = player.config.playback; let hasYoutube = false; playbackConfig.streamPriority.forEach((sp) => { if (sp.engine === engine) { hasYoutube = true; } }); if (!hasYoutube) { playbackConfig.streamPriority.push({ engine: engine, format: format }); } return playbackConfig; } /** * set stream priority according to playerConfig * @param {Player} player - player * @param {PKSourcesConfigObject} sources - sources * @return {void} */ function maybeSetStreamPriority(player: KalturaPlayer, sources: PKSourcesConfigObject): PlaybackConfig | null { if (sources && hasYoutubeSource(sources)) { return addEngineToStreamPriority(player, 'youtube', 'progressive'); } if (sources && hasImageSource(sources)) { return addEngineToStreamPriority(player, 'image', 'image'); } if (sources && hasDocumentSource(sources)) { return addEngineToStreamPriority(player, 'document', 'document'); } return null; } /** * returns true if sources contain youtube video source * @param {PKSourcesConfigObject} sources - thr sources object * @returns {boolean} - true if sources contain youtube source */ function hasYoutubeSource(sources: PKSourcesConfigObject): boolean { const source = sources && sources.progressive; return !!(source && source[0] && source[0].mimetype === 'video/youtube'); } /** * returns true if sources contain image source * @param {PKSourcesConfigObject} sources - thr sources object * @returns {boolean} - true if sources contain image source */ function hasImageSource(sources: PKSourcesConfigObject): boolean { // const IMAGE_MIME_TYPES = /(^image)(\/)[a-zA-Z0-9_]*/; const source = sources && sources.image; // return !!(source && source[0] && source[0].mimetype.match(IMAGE_MIME_TYPES)); return !!(source && source[0]); } /** * returns true if sources contain document source * @param {PKSourcesConfigObject} sources - thr sources object * @returns {boolean} - true if sources contain document source */ function hasDocumentSource(sources: PKSourcesConfigObject): boolean { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const source = sources && sources.document; return !!(source && source[0]); } /** * Maybe set inBrowserFullscreen config based on the plugins. * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function maybeSetFullScreenConfig(options: KalturaPlayerConfig): void { const vrPlugin = Utils.Object.getPropertyPath(options, 'plugins.vr'); if (vrPlugin && !vrPlugin.disable) { const fullscreenConfig = Utils.Object.getPropertyPath(options, 'playback.inBrowserFullscreen'); if (typeof fullscreenConfig !== 'boolean') { Utils.Object.mergeDeep(options, { playback: { inBrowserFullscreen: true } }); } } } /** * Set the autoplay capability to false for native Ios player. * @private * @param {KalturaPlayerConfig} options - kaltura player options * @returns {void} */ function maybeSetCapabilitiesForIos(options: KalturaPlayerConfig): void { if (Env.isIOS) { const playsinline = Utils.Object.getPropertyPath(options, 'playback.playsinline'); const isAirPlayConfigured = Utils.Object.hasPropertyPath(options, 'plugins.airplay'); const isPlaysinline = playsinline !== false; if (isAirPlayConfigured) { setCapabilities(EngineType.HTML5, { autoplay: false, mutedAutoPlay: isPlaysinline }); } else if (Env.device.model === 'iPhone' && !isPlaysinline) { setCapabilities(EngineType.HTML5, { autoplay: false, mutedAutoPlay: false }); } } } /** * Merge the provider plugins config (e.g. bumper) into the app config and returns it and the respective app config to restore in change media * @param {PluginsConfig} providerPluginsConfig - the provider plugins config * @param {KalturaPlayerConfig} appPluginsConfig - the entire app plugins config * @returns {Array} - the merged plugins config and the partial respective app plugins config */ function mergeProviderPluginsConfig(providerPluginsConfig: PluginsConfig, appPluginsConfig: PluginsConfig): Array { const mergePluginConfig: PluginsConfig = {}; const respectiveAppPluginsConfig: PluginsConfig = {}; Utils.Object.isObject(providerPluginsConfig) && Object.entries(providerPluginsConfig).forEach(([pluginName, pluginConfig]: [string, any]) => { mergePluginConfig[pluginName] = {} as BasePlugin; respectiveAppPluginsConfig[pluginName] = {} as BasePlugin; Object.entries(pluginConfig).forEach(([key, providerValue]) => { const appValue = Utils.Object.getPropertyPath(appPluginsConfig[pluginName], key); mergePluginConfig[pluginName][key] = appValue || providerValue; respectiveAppPluginsConfig[pluginName][key] = appValue; }); }); return [mergePluginConfig, respectiveAppPluginsConfig]; } function maybeLoadInitialServerResponse(player: KalturaPlayer): void { const { serviceUrl, initCallToServer } = player.provider.env; if (initCallToServer) { const url = serviceUrl + initCallToServer + '/format/1'; fetch(url).then((response) => { response.json().then((data) => { getLogger().log(`Initial server response: ${data}`); }); }); } return; } function getLogBuffer(): string { return logBuffer.join(''); } export { printSetupMessages, supportLegacyOptions, printKalturaPlayerVersionToLog, setStorageConfig, applyStorageSupport, applyCastSupport, setStorageTextStyle, attachToFirstClick, validateConfig, validateProviderConfig, setLogOptions, maybeApplyStartTimeQueryParam, maybeApplyEndTimeQueryParam, maybeApplyClipQueryParams, createKalturaPlayerContainer, checkNativeHlsSupport, getDefaultOptions, maybeSetStreamPriority, hasYoutubeSource, hasImageSource, hasDocumentSource, mergeProviderPluginsConfig, getServerUIConf, initializeStorageManagers, maybeLoadInitialServerResponse, KALTURA_PLAYER_START_TIME_QS, getLogBuffer, setUIConfId };