import { PKSourcesConfigObject, PlayerStreamTypes, StreamType, Utils } from '@playkit-js/playkit-js'; import { getServerUIConf } from './setup-helpers'; import { KalturaPlayer } from '../../kaltura-player'; import { PartialKPOptionsObject } from '../../types'; import { SessionIdGenerator } from './session-id-generator'; const PLAY_MANIFEST = 'playmanifest/'; const PLAY_SESSION_ID = 'playSessionId='; const DRM_SESSION_ID = 'sessionId='; const REFERRER = 'referrer='; const UICONF_ID = 'uiConfId='; const CLIENT_TAG = 'clientTag=html5:v'; const UDRM_DOMAIN = 'kaltura.com'; const CUSTOM_DATA = 'custom_data='; const SIGNATURE = 'signature='; const SEEK_FROM = 'seekFrom='; const CLIP_TO = 'clipTo='; declare global { interface Window { originalRequestReferrer?: string; } } /** * @param {Player} player - player * @param {PartialKPOptionsObject} playerConfig - player config * @return {void} * @private */ function handleSessionId(player: KalturaPlayer, playerConfig: PartialKPOptionsObject): void { if (player.config.session && player.config.session.id) { // on change media updateSessionId(player, playerConfig); } else { // on first playback addSessionId(playerConfig); if (player?.playlist?.items?.length && playerConfig.sources?.id) { player.sessionIdCache?.set(playerConfig.sources.id, playerConfig.session!.id as string); } } } /** * @param {PartialKPOptionsObject} playerConfig - player config * @return {void} * @private */ function addSessionId(playerConfig: PartialKPOptionsObject): void { setSessionId(playerConfig, SessionIdGenerator.next()); } /** * @param {Player} player - player * @param {PartialKPOptionsObject} playerConfig - player config * @return {void} * @private */ function updateSessionId(player: KalturaPlayer, playerConfig: PartialKPOptionsObject): void { const entryId = playerConfig.sources?.id; if (!player?.playlist?.items?.length) { setSessionId(playerConfig, SessionIdGenerator.next()); } else if (entryId) { if (player.sessionIdCache?.get(entryId)) { setSessionId(playerConfig, player.sessionIdCache?.get(entryId)); } else { const sessionId = SessionIdGenerator.next(); player.sessionIdCache?.set(entryId, sessionId); setSessionId(playerConfig, sessionId); } } } /** * @param {PartialKPOptionsObject} playerConfig - player config * @param {string} sessionId - the session id * @return {void} * @private */ function setSessionId(playerConfig: PartialKPOptionsObject, sessionId: string): void { playerConfig.session = playerConfig.session || {}; playerConfig.session.id = sessionId; } /** * @param {string} url - url * @param {string} sessionId - session id * @param {string} paramName - optional param name of the session id * @return {string} - the url with the new sessionId * @private */ function updateSessionIdInUrl(player: KalturaPlayer | null, url: string, sessionId?: string, paramName: string = PLAY_SESSION_ID): string { if (sessionId) { const sessionIdInUrlRegex = new RegExp(paramName + '((?:[a-z0-9]|-)*:(?:[a-z0-9]|-)*)', 'i'); const sessionIdInUrl = sessionIdInUrlRegex.exec(url); // this url has session id (has already been played) if (sessionIdInUrl && sessionIdInUrl[1]) { // session id should be the same for the same entry if (!player?.playlist?.items?.length) { url = url.replace(sessionIdInUrl[1], sessionId); } } else { url += getQueryStringParamDelimiter(url) + paramName + sessionId; } } return url; } /** * @return {string} - The referrer * @private */ function getReferrer(): string { let referrer; try { referrer = window.parent.document.URL; //Ignore referrer from friendly iframe that contains kaltura.com if (referrer.toLowerCase().includes('kaltura.com')) { throw new Error('ignoring referrer:' + referrer); } } catch (e) { // unfriendly iframe // try to get it from originalReferrer supplied by backend // or fallback to document referrer referrer = window.originalRequestReferrer || document.referrer; } return referrer; } function getOriginalRequestReferrer(): string | undefined { return window.originalRequestReferrer; } /** * @param {string} url - url * @return {string} - the url with the referrer appended in the query params * @private */ function addReferrer(url: string): string { if (url.indexOf(REFERRER) === -1) { const referrer = btoa(getReferrer().substr(0, 1000)); url += getQueryStringParamDelimiter(url) + REFERRER + referrer; } return url; } /** * @param {string} url - url * @param {PartialKPOptionsObject} playerConfig - player config * @return {string} - the url with the uiconf id appended in the query params * @private */ function addUIConfId(url: string, playerConfig: PartialKPOptionsObject): string { const uiConfId = Utils.Object.getPropertyPath(playerConfig, 'provider.uiConfId'); if (url.indexOf(UICONF_ID) === -1 && typeof uiConfId === 'number') { url += getQueryStringParamDelimiter(url) + UICONF_ID + uiConfId; } return url; } /** * @param {string} url - url * @return {string} - returns the next param delimiter (? or &) according to the current url structure * @private */ function getQueryStringParamDelimiter(url: string): string { return url.indexOf('?') === -1 ? '?' : '&'; } /** * @param {string} url - url * @param {string} productVersion - product version * @return {string} - the url with the client tag appended in the query params * @private */ function addClientTag(url: string, productVersion?: string): string { if (url.indexOf(CLIENT_TAG) === -1) { url += getQueryStringParamDelimiter(url) + CLIENT_TAG + (productVersion || __VERSION__); } return url; } /** * @param {string} url - url * @param {PKSourcesConfigObject} sources - sources * @return {string} - the url with the seekFrom and clipTo appended in the query params * @private */ function addStartAndEndTime(url: string, sources: PKSourcesConfigObject): string { const seekFrom = Utils.Object.getPropertyPath(sources, 'seekFrom'); const clipTo = Utils.Object.getPropertyPath(sources, 'clipTo'); if (typeof seekFrom === 'number' && url.indexOf(SEEK_FROM) === -1) { url += getQueryStringParamDelimiter(url) + SEEK_FROM + Math.floor(seekFrom * 1000); } if (typeof clipTo === 'number' && url.indexOf(CLIP_TO) === -1) { url += getQueryStringParamDelimiter(url) + CLIP_TO + Math.floor(clipTo * 1000); } return url; } /** * Adding Kaltura specific params to player config and player sources. * @param {Player} player - player * @param {PartialKPOptionsObject} playerConfig - player config * @return {void} * @private */ function addKalturaParams(player: KalturaPlayer, playerConfig: PartialKPOptionsObject): void { handleSessionId(player, playerConfig); const sources: PKSourcesConfigObject = playerConfig.sources!; const sessionId = playerConfig.session && playerConfig.session.id; const productVersion = getServerUIConf()?.productVersion; Object.values(StreamType).forEach((key: PlayerStreamTypes) => { if (sources[key]) { sources[key].forEach((source) => { if ( typeof source.url === 'string' && source.url.toLowerCase().indexOf(PLAY_MANIFEST) > -1 && // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore !source.localSource ) { source.url = updateSessionIdInUrl(player, source.url, sessionId); source.url = addReferrer(source.url); source.url = addClientTag(source.url, productVersion); source.url = addStartAndEndTime(source.url, sources); } if (source['drmData'] && source['drmData'].length) { source['drmData'].forEach((drmData) => { if (typeof drmData.licenseUrl === 'string' && [UDRM_DOMAIN, CUSTOM_DATA, SIGNATURE].every((t) => drmData.licenseUrl.includes(t))) { drmData.licenseUrl = updateSessionIdInUrl(player, drmData.licenseUrl, sessionId, DRM_SESSION_ID); drmData.licenseUrl = addClientTag(drmData.licenseUrl, productVersion); drmData.licenseUrl = addReferrer(drmData.licenseUrl); drmData.licenseUrl = addUIConfId(drmData.licenseUrl, playerConfig); } }); } }); } }); } export { addKalturaParams, handleSessionId, updateSessionIdInUrl, getReferrer, addReferrer, addClientTag, addUIConfId, getOriginalRequestReferrer, addStartAndEndTime };