@playkit-js/kaltura-player-js
Version:
[](https://github.com/kaltura/kaltura-player-js/actions/workflows/run_canary.yaml) [ • 10 kB
text/typescript
import { EventType as CoreEventType, FakeEvent, loadPlayer, TrackType, Utils, getLogger } from '@playkit-js/playkit-js';
import { KalturaPlayer } from '../../kaltura-player';
import { PlayerSnapshot } from './player-snapshot';
import { CastEventType } from './cast-event-type';
import { RemoteAvailablePayload, RemoteConnectedPayload, RemoteDisconnectedPayload } from './remote-payload';
import { UIWrapper } from '../ui-wrapper';
import { SourcesConfig, PlaybackConfig } from '../../types';
/**
* @class RemoteControl
* @param {KalturaPlayer} player - The Kaltura player.
*/
class RemoteControl {
public static _logger: any = getLogger('RemoteControl');
/**
* Gets the player snapshot.
* @returns {PlayerSnapshot} - player snapshot.
* @memberof RemoteControl
* @instance
*/
public getPlayerSnapshot: () => void;
/**
* Gets the UI wrapper.
* @returns {UIWrapper} - The UI wrapper.
* @memberof RemoteControl
* @instance
*/
public getUIWrapper: () => void;
/**
* On remote device disconnected handler.
* @param {RemoteDisconnectedPayload} payload - disconnected payload.
* @returns {void}
* @memberof RemoteControl
* @instance
*/
public onRemoteDeviceDisconnected: () => void;
/**
* On remote device connected handler.
* @param {RemoteConnectedPayload} payload - connected payload.
* @returns {void}
* @memberof RemoteControl
* @instance
*/
public onRemoteDeviceConnected: () => void;
/**
* On remote device available handler.
* @param {RemoteAvailablePayload} payload - available payload.
* @returns {void}
* @memberof RemoteControl
* @instance
*/
public onRemoteDeviceAvailable: () => void;
/**
* On remote device connecting handler.
* @returns {void}
* @memberof RemoteControl
* @instance
* @fires CastEventType:CAST_SESSION_STARTING
*/
public onRemoteDeviceConnecting: () => void;
/**
* On remote device disconnecting handler.
* @returns {void}
* @memberof RemoteControl
* @instance
*/
public onRemoteDeviceDisconnecting: () => void;
/**
* On remote device connect failed handler.
* @returns {void}
* @memberof RemoteControl
* @instance
*/
public onRemoteDeviceConnectFailed: () => void;
/**
* Gets the player source selected.
* @returns {object} - player source.
* @memberof RemoteControl
* @instance
*/
public getPlayerSelectedSource: () => void;
constructor(player: KalturaPlayer) {
this.getPlayerSnapshot = getPlayerSnapshot.bind(player);
this.getUIWrapper = getUIWrapper.bind(player);
this.onRemoteDeviceAvailable = onRemoteDeviceAvailable.bind(player);
this.onRemoteDeviceConnected = onRemoteDeviceConnected.bind(player);
this.onRemoteDeviceDisconnected = onRemoteDeviceDisconnected.bind(player);
this.onRemoteDeviceConnecting = onRemoteDeviceConnecting.bind(player);
this.onRemoteDeviceDisconnecting = onRemoteDeviceDisconnecting.bind(player);
this.onRemoteDeviceConnectFailed = onRemoteDeviceConnectFailed.bind(player);
this.getPlayerSelectedSource = getPlayerSelectedSource.bind(player);
}
}
function onRemoteDeviceConnecting(): void {
RemoteControl._logger.debug('onRemoteDeviceConnecting');
this.dispatchEvent(new FakeEvent(CastEventType.CAST_SESSION_STARTING));
}
function onRemoteDeviceConnected(payload: RemoteConnectedPayload): void {
RemoteControl._logger.debug('onRemoteDeviceConnected', payload);
const { player, ui, session } = payload;
// After remote player is connected, clean all listeners and bounce the remote player events forward
this._eventManager.removeAll();
Object.values(CoreEventType).forEach((coreEvent) => this._eventManager.listen(player, coreEvent, (e) => this.dispatchEvent(e)));
// Use the current config with the remote player presets on the new ui wrapper
const config = this.config;
if (ui) {
Utils.Object.mergeDeep(config, { ui: { customPreset: ui.uis } });
}
this.configure({
playback: {
muted: this.muted,
volume: this.volume
}
});
// Reset the local player, create new ui wrapper and set the remote player
this._localPlayer.reset();
this._uiWrapper.destroy();
this._remotePlayer = player;
this._uiWrapper = new UIWrapper(this, config);
// Dispatch the cast started event
this.dispatchEvent(
new FakeEvent(CastEventType.CAST_SESSION_STARTED, {
session: session
})
);
}
function onRemoteDeviceDisconnecting(): void {
RemoteControl._logger.debug('onRemoteDeviceDisconnecting');
this.dispatchEvent(new FakeEvent(CastEventType.CAST_SESSION_ENDING));
}
function onRemoteDeviceDisconnected(payload: RemoteDisconnectedPayload): void {
RemoteControl._logger.debug('onRemoteDeviceDisconnected', payload);
const { player, snapshot } = payload;
if (this._remotePlayer && this._remotePlayer === player) {
// After remote player is disconnected, clean all listeners and bounce the remote player events forward
this._eventManager.removeAll();
reconstructPlayerComponents.call(this, snapshot);
if (snapshot) {
this.dispatchEvent(new FakeEvent(CastEventType.CAST_SESSION_ENDED));
const originConfig = this.config;
const shouldPause = !snapshot.config.playback.autoplay;
const mediaInfo = snapshot.mediaInfo;
const mediaConfig = snapshot.mediaConfig;
snapshot.config.playback.autoplay = true;
configurePlayback.call(this, snapshot.config);
this._eventManager.listenOnce(this, this.Event.Core.CHANGE_SOURCE_ENDED, () => {
configureTracks.call(this, snapshot.config.sources);
});
let mediaPromise;
if (mediaInfo) {
mediaPromise = this.loadMedia(mediaInfo);
} else if (mediaConfig) {
mediaPromise = Promise.resolve();
this.setMedia(mediaConfig);
}
mediaPromise &&
mediaPromise.then(() => {
this._eventManager.listenOnce(this, this.Event.Core.FIRST_PLAYING, () => {
this.textStyle = snapshot.textStyle;
configurePlayback.call(this, originConfig);
setInitialTracks.call(this, snapshot.config.playback);
if (shouldPause) {
this.pause();
}
});
});
}
}
}
function onRemoteDeviceAvailable(payload: RemoteAvailablePayload): void {
RemoteControl._logger.debug('onRemoteDeviceAvailable', payload);
const { player, available } = payload;
this.dispatchEvent(
new FakeEvent(CastEventType.CAST_AVAILABLE, {
type: player.type,
available: available
})
);
}
function onRemoteDeviceConnectFailed(): void {
RemoteControl._logger.debug('onRemoteDeviceConnectFailed');
this.dispatchEvent(new FakeEvent(CastEventType.CAST_SESSION_START_FAILED));
}
function getPlayerSnapshot(): PlayerSnapshot {
const snapshot = new PlayerSnapshot(this);
RemoteControl._logger.debug('getPlayerSnapshot', snapshot);
return snapshot;
}
function getPlayerSelectedSource(): PlayerSnapshot {
const sourceSelected = this._sourceSelected;
const sourceUrl = sourceSelected?.url ? sourceSelected.url : window.sessionStorage.getItem('sourceUrl');
window.sessionStorage.setItem('sourceUrl', sourceUrl);
RemoteControl._logger.debug('getPlayerSelectedSource', sourceUrl);
return sourceUrl;
}
function getUIWrapper(): UIWrapper {
RemoteControl._logger.debug('getUIWrapper');
return this._uiWrapper;
}
function reconstructPlayerComponents(snapshot: PlayerSnapshot): void {
// Destroy the remote ui wrapper
this._uiWrapper.destroy();
const playerConfig = this._localPlayer.config;
// If the local player config contains ima, needs to destroy the player and create it from scratch
if (playerConfig.plugins && playerConfig.plugins.ima) {
let imaConfig = {};
// If it's a VAST ad we are empty the ad tag so ads won't play and configure it later
if (playerConfig.cast.advertising && playerConfig.cast.advertising.vast) {
if (snapshot.config.sources.startTime > 0) {
const adTagUrl = playerConfig.plugins.ima.adTagUrl;
imaConfig = {
adTagUrl: ''
};
this._eventManager.listen(this, CoreEventType.FIRST_PLAYING, () => this.configure({ plugins: { ima: { adTagUrl: adTagUrl } } }));
}
} else {
imaConfig = {
// Needs to wait for engine to create the new ads container
delayInitUntilSourceSelected: true
};
}
Utils.Object.mergeDeep(playerConfig, { plugins: { ima: imaConfig } });
// Destroy the local player and load new one
this._localPlayer.destroy();
this._remotePlayer = null;
this._localPlayer = loadPlayer(playerConfig);
} else {
this._remotePlayer = null;
}
// Listen on the local player events and bounce its events forward
Object.values(CoreEventType).forEach((coreEvent) => this._eventManager.listen(this._localPlayer, coreEvent, (e) => this.dispatchEvent(e)));
// Create new ui wrapper with an updated state for the isCastAvailable prop
this._uiWrapper = new UIWrapper(this, this.config);
this._uiWrapper.setConfig({ isCastAvailable: this.isCastAvailable() }, 'engine');
}
function configurePlayback(config: any): void {
const { startTime } = config.sources;
const { autoplay } = config.playback;
this.configure({
sources: {
startTime
},
playback: {
autoplay
}
});
}
function configureTracks(sourcesConfig: SourcesConfig): void {
if (sourcesConfig.captions?.length) {
const { captions } = sourcesConfig;
this.configure({
sources: {
captions
}
});
}
}
function setInitialTracks(playbackConfig: PlaybackConfig): void {
if (playbackConfig.audioLanguage) {
const audioTrack = this.getTracks(TrackType.AUDIO).find((t) => t.language === playbackConfig.audioLanguage);
this.selectTrack(audioTrack);
}
if (playbackConfig.textLanguage) {
const textTrack = this.getTracks(TrackType.TEXT).find((t) => t.language === playbackConfig.textLanguage);
this.selectTrack(textTrack);
}
}
export { RemoteControl };