UNPKG

react-native-theoplayer

Version:

A THEOplayer video component for react-native.

332 lines (330 loc) 13.5 kB
"use strict"; import React, { PureComponent } from 'react'; import { Dimensions, findNodeHandle, Platform, requireNativeComponent, StyleSheet, UIManager, View } from 'react-native'; import { isDateRangeCue, PresentationMode } from 'react-native-theoplayer'; import { CastEventType, PlayerEventType } from 'react-native-theoplayer'; import styles from './THEOplayerView.style'; import { decodeNanInf } from './utils/TypeUtils'; import { BaseEvent } from './adapter/event/BaseEvent'; import { DefaultAdEvent, DefaultAirplayStateChangeEvent, DefaultChromecastChangeEvent, DefaultChromecastErrorEvent, DefaultDimensionChangeEvent, DefaultDurationChangeEvent, DefaultErrorEvent, DefaultLoadedMetadataEvent, DefaultMediaTrackEvent, DefaultMediaTrackListEvent, DefaultPresentationModeChangeEvent, DefaultProgressEvent, DefaultRateChangeEvent, DefaultReadyStateChangeEvent, DefaultSegmentNotFoundEvent, DefaultTextTrackEvent, DefaultTextTrackListEvent, DefaultVolumeChangeEvent, DefaultTimeupdateEvent, DefaultResizeEvent, DefaultSeekingEvent, DefaultSeekedEvent, DefaultVideoResizeEvent } from './adapter/event/PlayerEvents'; import { toMediaTrackType, toMediaTrackTypeEventType, toTextTrackEventType, toTrackListEventType } from './adapter/event/native/NativeTrackEvent'; import { fromNativeTheoLiveEvent } from './adapter/event/native/NativeTheoLiveEvent'; import { fromNativeTheoAdsEvent } from './adapter/event/native/NativeTheoAdsEvent'; import { THEOplayerAdapter } from './adapter/THEOplayerAdapter'; import { getFullscreenSize } from './utils/Dimensions'; import { Poster } from './poster/Poster'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const INVALID_HANDLE = -1; export class THEOplayerView extends PureComponent { _dimensionsHandler = undefined; static initialState = { error: undefined, presentationMode: PresentationMode.inline, screenSize: getFullscreenSize(), posterActive: false, poster: undefined }; constructor(props) { super(props); this._root = /*#__PURE__*/React.createRef(); this.state = THEOplayerView.initialState; this._facade = new THEOplayerAdapter(this); } componentDidMount() { if (Platform.OS !== 'ios') { // On iOS we use the native deviceOrientation event. this._dimensionsHandler = Dimensions.addEventListener('change', this._onDimensionsChanged); } } componentWillUnmount() { // Allow proper cleanup on the native player before destruction this._facade.willUnmount(); // Notify the player will be destroyed. const { onPlayerDestroy } = this.props; if (onPlayerDestroy) { onPlayerDestroy(this._facade); } this._facade.dispatchEvent(new BaseEvent(PlayerEventType.DESTROY)); this._dimensionsHandler?.remove(); this._facade.clearEventListeners(); } get nativeHandle() { return findNodeHandle(this._root.current) || INVALID_HANDLE; } reset() { this.setState(prevState => ({ ...prevState, error: undefined })); } _onDimensionsChanged = () => { this.setState({ screenSize: getFullscreenSize() }); }; _onDeviceOrientationChanged = () => { if (Platform.OS === 'ios') { // On iOS, we use the native deviceOrientation event to update the screenSize // because of an issue on iPad with React-native's Dimensions. this._onDimensionsChanged(); } }; _onNativePlayerReady = event => { // Optionally apply an initial player state const { version, state } = event.nativeEvent; this._facade.initializeFromNativePlayer_(version, state).then(() => { this.props.onPlayerReady?.(this._facade); }); }; _onSourceChange = () => { this.reset(); this._facade.dispatchEvent(new BaseEvent(PlayerEventType.SOURCE_CHANGE)); this._updatePoster(); if (!this._facade.autoplay) { this._showPoster(); } }; _onLoadStart = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.LOAD_START)); }; _onLoadedData = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.LOADED_DATA)); }; _onLoadedMetadata = event => { const nativeEvent = event.nativeEvent; this._facade.dispatchEvent(new DefaultLoadedMetadataEvent(nativeEvent.textTracks, nativeEvent.audioTracks, nativeEvent.videoTracks, decodeNanInf(nativeEvent.duration), nativeEvent.selectedTextTrack, nativeEvent.selectedVideoTrack, nativeEvent.selectedAudioTrack)); }; _onVolumeChange = event => { this._facade.dispatchEvent(new DefaultVolumeChangeEvent(event.nativeEvent.volume, event.nativeEvent.muted)); }; _onError = event => { const { error } = event.nativeEvent; this.setState({ error }); this._facade.dispatchEvent(new DefaultErrorEvent(event.nativeEvent.error)); }; _onProgress = event => { this._facade.dispatchEvent(new DefaultProgressEvent(event.nativeEvent.seekable, event.nativeEvent.buffered)); }; _onCanPlay = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.CANPLAY)); }; _onPlay = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.PLAY)); }; _onPlaying = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.PLAYING)); this._hidePoster(); }; _onPause = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.PAUSE)); }; _onSeeking = event => { this._facade.dispatchEvent(new DefaultSeekingEvent(event.nativeEvent.currentTime)); }; _onSeeked = event => { this._facade.dispatchEvent(new DefaultSeekedEvent(event.nativeEvent.currentTime)); }; _onWaiting = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.WAITING)); }; _onEnded = () => { this._facade.dispatchEvent(new BaseEvent(PlayerEventType.ENDED)); }; _onReadStateChange = event => { this._facade.dispatchEvent(new DefaultReadyStateChangeEvent(event.nativeEvent.readyState)); }; _onTimeUpdate = event => { this._facade.dispatchEvent(new DefaultTimeupdateEvent(event.nativeEvent.currentTime, event.nativeEvent.currentProgramDateTime)); }; _onDurationChange = event => { this._facade.dispatchEvent(new DefaultDurationChangeEvent(decodeNanInf(event.nativeEvent.duration))); }; _onRateChange = event => { this._facade.dispatchEvent(new DefaultRateChangeEvent(event.nativeEvent.playbackRate)); }; _onSegmentNotFound = event => { const nativeEvent = event.nativeEvent; this._facade.dispatchEvent(new DefaultSegmentNotFoundEvent(nativeEvent.segmentStartTime, nativeEvent.error, nativeEvent.retryCount)); }; _onTextTrackListEvent = event => { const nativeEvent = event.nativeEvent; this._facade.dispatchEvent(new DefaultTextTrackListEvent(toTrackListEventType(nativeEvent.type), nativeEvent.track)); }; _onTextTrackEvent = event => { const nativeEvent = event.nativeEvent; const cue = nativeEvent.cue; if (cue) { this.normalizeCue(cue); } this._facade.dispatchEvent(new DefaultTextTrackEvent(toTextTrackEventType(nativeEvent.type), nativeEvent.trackUid, cue)); }; normalizeCue(cue) { cue.startTime = decodeNanInf(cue.startTime); cue.endTime = decodeNanInf(cue.endTime); if (isDateRangeCue(cue)) { cue.startDate = new Date(cue.startDate); if (cue.endDate) { cue.endDate = new Date(cue.endDate); } if (cue.duration) { cue.duration = decodeNanInf(cue.duration); } if (cue.plannedDuration) { cue.plannedDuration = decodeNanInf(cue.plannedDuration); } } } _onMediaTrackListEvent = event => { const nativeEvent = event.nativeEvent; this._facade.dispatchEvent(new DefaultMediaTrackListEvent(toTrackListEventType(nativeEvent.type), toMediaTrackType(nativeEvent.trackType), nativeEvent.track)); }; _onMediaTrackEvent = event => { const nativeEvent = event.nativeEvent; this._facade.dispatchEvent(new DefaultMediaTrackEvent(toMediaTrackTypeEventType(nativeEvent.type), toMediaTrackType(nativeEvent.trackType), nativeEvent.trackUid, nativeEvent.qualities)); }; _onAdEvent = event => { const nativeEvent = event.nativeEvent; this._facade.dispatchEvent(new DefaultAdEvent(nativeEvent.type, nativeEvent.ad)); }; _onTHEOliveEvent = event => { this._facade.dispatchEvent(fromNativeTheoLiveEvent(event)); }; _onTHEOadsEvent = event => { const theoAdsEvent = fromNativeTheoAdsEvent(this.nativeHandle, event); if (theoAdsEvent !== undefined) { this._facade.dispatchEvent(theoAdsEvent); } }; _onCastEvent = event => { switch (event.nativeEvent.type) { case CastEventType.CHROMECAST_STATE_CHANGE: this._facade.dispatchEvent(new DefaultChromecastChangeEvent(event.nativeEvent.state)); break; case CastEventType.AIRPLAY_STATE_CHANGE: this._facade.dispatchEvent(new DefaultAirplayStateChangeEvent(event.nativeEvent.state)); break; case CastEventType.CHROMECAST_ERROR: this._facade.dispatchEvent(new DefaultChromecastErrorEvent(event.nativeEvent.error)); break; } }; _onPresentationModeChange = event => { const presentationMode = event.nativeEvent.presentationMode; this.setState({ presentationMode }, () => { // Re-measure screen size after transitioning to fullscreen. if (presentationMode === PresentationMode.fullscreen) { this.setState({ screenSize: getFullscreenSize() }); } }); this._facade.dispatchEvent(new DefaultPresentationModeChangeEvent(event.nativeEvent.presentationMode, event.nativeEvent.previousPresentationMode, event.nativeEvent.context)); }; _onDimensionChange = event => { const width = event.nativeEvent.width; const height = event.nativeEvent.height; this._facade.dispatchEvent(new DefaultResizeEvent(width, height)); this._facade.dispatchEvent(new DefaultDimensionChangeEvent(width, height)); }; _onVideoResize = event => { this._facade.dispatchEvent(new DefaultVideoResizeEvent(event.nativeEvent.videoWidth, event.nativeEvent.videoHeight)); }; _updatePoster = () => { this.setState({ poster: this._facade.source?.poster }); }; _showPoster = () => { this.setState({ posterActive: true }); }; _hidePoster = () => { this.setState({ posterActive: false }); }; styleOverride() { const { presentationMode, screenSize: fullscreenSize } = this.state; return presentationMode === PresentationMode.fullscreen || Platform.OS === 'android' && presentationMode === PresentationMode.pip && this._facade?.pipConfiguration?.reparentPip == true ? fullscreenSize : {}; } render() { const { config, style, posterStyle, children } = this.props; const { posterActive, poster } = this.state; return /*#__PURE__*/_jsxs(View, { style: [styles.base, style, this.styleOverride()], children: [/*#__PURE__*/_jsx(THEOplayerRCTView, { ref: this._root, style: StyleSheet.absoluteFill, config: config || {}, onNativePlayerReady: this._onNativePlayerReady, onNativeSourceChange: this._onSourceChange, onNativeLoadStart: this._onLoadStart, onNativeLoadedData: this._onLoadedData, onNativeLoadedMetadata: this._onLoadedMetadata, onNativeVolumeChange: this._onVolumeChange, onNativeError: this._onError, onNativeProgress: this._onProgress, onNativeCanPlay: this._onCanPlay, onNativePlay: this._onPlay, onNativePlaying: this._onPlaying, onNativePause: this._onPause, onNativeSeeking: this._onSeeking, onNativeSeeked: this._onSeeked, onNativeWaiting: this._onWaiting, onNativeEnded: this._onEnded, onNativeReadyStateChange: this._onReadStateChange, onNativeTimeUpdate: this._onTimeUpdate, onNativeDurationChange: this._onDurationChange, onNativeRateChange: this._onRateChange, onNativeSegmentNotFound: this._onSegmentNotFound, onNativeTextTrackListEvent: this._onTextTrackListEvent, onNativeTextTrackEvent: this._onTextTrackEvent, onNativeMediaTrackListEvent: this._onMediaTrackListEvent, onNativeMediaTrackEvent: this._onMediaTrackEvent, onNativeAdEvent: this._onAdEvent, onNativeTHEOliveEvent: this._onTHEOliveEvent, onNativeTHEOadsEvent: this._onTHEOadsEvent, onNativeCastEvent: this._onCastEvent, onNativePresentationModeChange: this._onPresentationModeChange, onNativeDeviceOrientationChanged: this._onDeviceOrientationChanged, onNativeDimensionChange: this._onDimensionChange, onNativeVideoResize: this._onVideoResize }), posterActive && /*#__PURE__*/_jsx(Poster, { uri: poster, style: posterStyle }), children] }); } } const LINKING_ERROR = `The package 'react-native-theoplayer' doesn't seem to be linked. Make sure: \n\n` + Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo managed workflow\n'; const ComponentName = 'THEOplayerRCTView'; const THEOplayerRCTView = UIManager.getViewManagerConfig(ComponentName) != null ? requireNativeComponent(ComponentName) : () => { throw new Error(LINKING_ERROR); }; //# sourceMappingURL=THEOplayerView.js.map