react-native-theoplayer
Version:
A THEOplayer video component for react-native.
332 lines (330 loc) • 13.5 kB
JavaScript
;
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