UNPKG

@applicaster/zapp-react-native-utils

Version:

Applicaster Zapp React Native utilities package

295 lines (228 loc) • 7.97 kB
import * as R from "ramda"; import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils"; import { createLogger, utilsLogger } from "../../logger"; import { Player } from "./player"; import { findNodeHandle } from "react-native"; import { PlayerRole } from "./conts"; import { toBooleanWithDefaultFalse } from "../../booleanUtils"; import { RNPlayerManager } from "./nativePlayerManager"; const logger = createLogger({ category: "PlayerNativeController", subsystem: "General", parent: utilsLogger, }); const { log_warning } = logger; type PlayerPlugnId = "QuickBrickPlayerPlugin" | "KalturaPlayerPlugin"; type Props = { player: PlayerRef; listener?: QuickBrickPlayer.SharedPlayerCallBacks | null; config?: Record<string, any>; playerId: string; autoplay?: boolean; entry: ZappEntry; playerPluginId?: PlayerPlugnId; }; const PlayerModuleFuncNames = { seekTo: "seekTo", forward: "forward", rewind: "rewind", play: "play", pause: "pause", startSleepTimer: "startSleepTimer", cancelSleepTimer: "cancelSleepTimer", getSleepTimerEnd: "getSleepTimerEnd", }; export class PlayerNative extends Player { private readonly playerComponent?: PlayerRef | null; constructor(props: Props) { super(props); this.entry = props.entry; this.playerComponent = props.player; this.playerPluginId = props.playerPluginId; this.startPosition = this.getContinueWatchingOffset({ entry: this.getEntry() || (props.listener as any)?.entry, ignoreContinueWatching: this.playerRole === PlayerRole.Cell, }); } getContentPosition = () => { return this.playerState.contentPosition; }; currentPlayerComponent = (): ReactComponent<PlayerProps> => this.playerComponent?.current; getState = () => this.playerState; // TODO: Try to add module interface invokeNativeFunction = (funcName: string, ...params: any[]): boolean => { const module = this.getPlayerModule(); const node = this.getComponentNode(); const func = module?.[funcName]; if (!(node && func)) { return false; } func(node, ...params); return true; }; getPlayerModule = () => { return RNPlayerManager?.[this.playerPluginId]; }; getComponentNode = () => { const component = (this.playerComponent?.current as any)?._root || (this.playerComponent?.current as any); return findNodeHandle(component); }; getInstance = () => { return this.playerComponent; }; // Methods to call actions on the player seekTo = (position: number) => { if (!this.playerState.seekableDuration) { log_warning("seekTo: Can not seek, video is not seekable"); return; } const newPosition = R.clamp(0, this.playerState.seekableDuration, position); if ( !this.invokeNativeFunction( PlayerModuleFuncNames.seekTo, newPosition, 1000 ) ) { if (isEmptyOrNil(this.playerState.seekPosition)) { this.playerState.seekPosition = this.playerState.contentPosition; } this.playerState.seekPosition = newPosition; this.currentPlayerComponent()?.["seeking"](this.playerState.seekPosition); } this.logState(PlayerModuleFuncNames.seekTo, { position }); }; forward = (deltaTime: number) => { if (isEmptyOrNil(this.playerState.contentPosition)) { return; } if (!this.invokeNativeFunction(PlayerModuleFuncNames.forward, deltaTime)) { // Kaltura does not have yet this implementation, use legacy code this.currentPlayerComponent()?.["forward"](deltaTime); } this.notifyPlayHeadPositionUpdate(); this.logState(PlayerModuleFuncNames.forward, { deltaTime }); }; rewind = (deltaTime: number) => { if (isEmptyOrNil(this.playerState.contentPosition)) { return; } if (!this.invokeNativeFunction(PlayerModuleFuncNames.rewind, deltaTime)) { // Kaltura does not have yet this implementation, use legacy code this.currentPlayerComponent()?.["rewind"](deltaTime); } this.notifyPlayHeadPositionUpdate(); this.logState(PlayerModuleFuncNames.rewind, { deltaTime }); }; play = () => { if (!this.invokeNativeFunction(PlayerModuleFuncNames.play)) { this.currentPlayerComponent()?.[PlayerModuleFuncNames.play]?.(); } }; pause = () => { if (!this.invokeNativeFunction(PlayerModuleFuncNames.pause)) { this.currentPlayerComponent()?.[PlayerModuleFuncNames.pause]?.(); } }; startSleepTimer = (sleepTimestamp: number) => { if ( !this.invokeNativeFunction( PlayerModuleFuncNames.startSleepTimer, sleepTimestamp ) ) { this.currentPlayerComponent()?.["startSleepTimer"](sleepTimestamp); } }; cancelSleepTimer = () => { if (!this.invokeNativeFunction(PlayerModuleFuncNames.cancelSleepTimer)) { this.currentPlayerComponent()?.[ PlayerModuleFuncNames.cancelSleepTimer ]?.(); } }; disableBufferAnimation = (): boolean => false; selectTrack = ( selected: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack ) => { this.currentPlayerComponent()?.["selectTrack"]?.(selected); }; setPlaybackRate = (rate: number) => { this.currentPlayerComponent()?.["setPlaybackRate"]?.(rate); }; getPluginConfiguration = () => { return (this.currentPlayerComponent() as any)?.props?.pluginConfiguration; }; getProps = () => (this.currentPlayerComponent() as any)?.props; appStateChange = (appState, previousAppState) => { this.currentPlayerComponent()?.["appStateChange"]?.( appState, previousAppState ); }; closeNativePlayer = () => { // TODO: Delete does not work this.currentPlayerComponent()?.["closeNativePlayer"]?.(); }; togglePlayPause = () => { this.currentPlayerComponent()?.["togglePlayPause"]?.(); }; onVideoLoad = (event) => { if (this.startPosition) { super.onVideoLoad({ ...event, ignoreContinueWatching: true }); this.seekTo(this.startPosition); this.startPosition = null; } else { super.onVideoLoad(event); } }; // This function preserves current chromecast content position, // So we can resume from the same position if chromecast session is closed by user // Because, current position is lost when view is recreated. onVideoProgress = (event) => { super.onVideoProgress(event); // TODO: Use native player view reference const nativePlayerModule = (this.playerComponent?.current as any)?._root; if (!nativePlayerModule) { if (this.playerRole === PlayerRole.Cell) { return; } this.startPosition = event.currentTime; } }; // This function is called when native player used directly without wrapped component, // Since event from native comes with wrapped in NativeEvent property getNativeViewListener = (): QuickBrickPlayer.SharedPlayerCallBacks => { const listeners = this.getListener(); const wrapNativeEvent = (event, callerFunc) => { if (!event) { callerFunc(); } callerFunc(event?.nativeEvent || event); }; return R.map((f) => { return (event) => wrapNativeEvent(event, f); }, listeners); }; isAudioItem = () => this.getEntry()?.content?.type?.includes?.("audio") || this.getEntry()?.type?.value === "audio"; isFullScreenSupported = (): boolean => { const config = this.getConfig(); const disableFullScreen = config?.["disable_fullscreen"]; if (disableFullScreen) { return false; } const isFullScreenAudioPlayer = config?.["full_screen_audio_player"]; return !(isFullScreenAudioPlayer && this.isAudioItem()); }; supportsNativeControls = (): boolean => toBooleanWithDefaultFalse(this.getConfig()?.["supports_native_controls"]); supportNativeCast = (): boolean => toBooleanWithDefaultFalse( this.playerPluginId === "quick-brick-player-brightcove" ); }