UNPKG

@8man/react-native-media-console

Version:
505 lines (503 loc) 16.6 kB
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } import React, { useCallback, useState, useEffect, useRef } from 'react'; import { View } from 'react-native'; import Video, { ResizeMode } from 'react-native-video'; import { useControlTimeout, useJSAnimations, usePanResponders } from './hooks'; import { Error, Loader, TopControls, BottomControls, PlayPause, Overlay } from './components'; import { PlatformSupport } from './OSSupport'; import { _onBack } from './utils'; import { _styles } from './styles'; import Gestures from '@8man/react-native-media-console/src/components/Gestures'; const volumeWidth = 150; const iconOffset = 0; const AnimatedVideoPlayer = props => { var _videoRef$current; const { animations, toggleResizeModeOnFullscreen, doubleTapTime = 130, resizeMode = ResizeMode.CONTAIN, isFullscreen = false, showOnStart = false, showOnEnd = false, alwaysShowControls = false, paused = false, muted = false, volume = 1, title = { primary: '', secondary: '' }, rate = 1, showDuration = false, showTimeRemaining = false, showHours = false, onSeek, onError, onBack, onEnd, onEnterFullscreen = () => {}, onExitFullscreen = () => {}, onHideControls = () => {}, onShowControls = () => {}, onPause, onPlay, onLoad, onLoadStart, onProgress, controlTimeoutDelay = 15000, tapAnywhereToPause = false, videoStyle = {}, containerStyle = {}, seekColor = '', source, disableBack = false, disableVolume = false, disableFullscreen = false, disableTimer = false, disableSeekbar = false, disablePlayPause = false, disableSeekButtons = false, disableOverlay, navigator, rewindTime = 15, pan: { horizontal: horizontalPan, inverted: invertedPan } = {}, testID, disableGesture = false } = props; const mounted = useRef(false); const _videoRef = useRef(null); const controlTimeout = useRef(setTimeout(() => {})).current; const tapActionTimeout = useRef(null); const [_resizeMode, setResizeMode] = useState(resizeMode); const [_paused, setPaused] = useState(paused); const [_muted, setMuted] = useState(muted); const [_volume, setVolume] = useState(volume); const [_isFullscreen, setIsFullscreen] = useState(isFullscreen || resizeMode === 'cover' || false); const [_showTimeRemaining, setShowTimeRemaining] = useState(showTimeRemaining); const [volumeTrackWidth, setVolumeTrackWidth] = useState(0); const [volumeFillWidth, setVolumeFillWidth] = useState(0); const [seekerFillWidth, setSeekerFillWidth] = useState(0); const [showControls, setShowControls] = useState(showOnStart); const [volumePosition, setVolumePositionState] = useState(0); const [seekerPosition, setSeekerPositionState] = useState(0); const [volumeOffset, setVolumeOffset] = useState(0); const [seekerOffset, setSeekerOffset] = useState(0); const [seekerWidth, setSeekerWidth] = useState(0); const [seeking, setSeeking] = useState(false); const [loading, setLoading] = useState(true); const [currentTime, setCurrentTime] = useState(0); const [error, setError] = useState(false); const [duration, setDuration] = useState(0); const [buffering, setBuffering] = useState(false); const [cachedDuration, setCachedDuration] = useState(0); const [cachedPosition, setCachedPosition] = useState(0); const videoRef = props.videoRef || _videoRef; const toggleFullscreen = () => setIsFullscreen(prevState => !prevState); const toggleControls = () => setShowControls(prevState => alwaysShowControls || !prevState); const toggleTimer = () => setShowTimeRemaining(prevState => !prevState); const togglePlayPause = () => { setPaused(prevState => !prevState); }; const styles = { videoStyle, containerStyle: containerStyle }; const _onSeek = obj => { if (!seeking) { setControlTimeout(); } setCurrentTime(obj.currentTime); console.log(obj); if (typeof onSeek === 'function') { onSeek(obj); } }; const _onEnd = () => { if (currentTime < duration) { setCurrentTime(duration); setPaused(!props.repeat); if (showOnEnd) { setShowControls(!props.repeat); } } if (typeof onEnd === 'function') { onEnd(); } }; const _onError = () => { setError(true); setLoading(false); }; function _onLoadStart(e) { setLoading(true); if (typeof onLoadStart === 'function') { onLoadStart(e); } } function _onLoad(data) { setDuration(data.duration); setLoading(false); if (showControls) { setControlTimeout(); } if (typeof onLoad === 'function') { onLoad(data); } } function _onProgress(data) { setLoading(false); if (!seeking && !buffering) { setCurrentTime(data.currentTime); setCachedDuration(data.playableDuration); if (typeof onProgress === 'function') { onProgress(data); } } } const _onScreenTouch = () => { if (tapActionTimeout.current) { clearTimeout(tapActionTimeout.current); tapActionTimeout.current = null; toggleFullscreen(); if (showControls) { resetControlTimeout(); } } else { tapActionTimeout.current = setTimeout(() => { if (tapAnywhereToPause && showControls) { togglePlayPause(); resetControlTimeout(); } else { toggleControls(); } tapActionTimeout.current = null; }, doubleTapTime); } }; const _onPlaybackRateChange = playBack => { if (playBack.playbackRate === 0 && !buffering) { setTimeout(() => { !buffering && setPaused(true); }); } else { setPaused(false); } console.log(playBack); }; const events = { onError: onError || _onError, onBack: onBack || _onBack(navigator), onEnd: _onEnd, onScreenTouch: _onScreenTouch, onEnterFullscreen, onExitFullscreen, onShowControls, onHideControls, onLoadStart: _onLoadStart, onProgress: _onProgress, onSeek: _onSeek, onLoad: _onLoad, onPause, onPlay, onPlaybackRateChange: _onPlaybackRateChange }; const constrainToSeekerMinMax = useCallback((val = 0) => { if (val <= 0) { return 0; } else if (val >= seekerWidth) { return seekerWidth; } return val; }, [seekerWidth]); const constrainToVolumeMinMax = (val = 0) => { if (val <= 0) { return 0; } else if (val >= volumeWidth + 9) { return volumeWidth + 9; } return val; }; const setSeekerPosition = useCallback((position = 0) => { const positionValue = constrainToSeekerMinMax(position); setSeekerPositionState(positionValue); setSeekerOffset(positionValue); setSeekerFillWidth(positionValue); }, [constrainToSeekerMinMax]); const setVolumePosition = (position = 0) => { const positionValue = constrainToVolumeMinMax(position); setVolumePositionState(positionValue + iconOffset); if (positionValue < 0) { setVolumeFillWidth(0); } else { setVolumeFillWidth(positionValue); } }; const { clearControlTimeout, resetControlTimeout, setControlTimeout } = useControlTimeout({ controlTimeout, controlTimeoutDelay, mounted: mounted.current, showControls, setShowControls, alwaysShowControls }); const { volumePanResponder, seekPanResponder } = usePanResponders({ duration, seekerOffset, volumeOffset, loading, seekerWidth, seeking, seekerPosition, seek: videoRef === null || videoRef === void 0 || (_videoRef$current = videoRef.current) === null || _videoRef$current === void 0 ? void 0 : _videoRef$current.seek, clearControlTimeout, setVolumePosition, setSeekerPosition, setSeeking, setControlTimeout, onEnd: events.onEnd, horizontal: horizontalPan, inverted: invertedPan }); useEffect(() => { if (currentTime >= duration) { var _videoRef$current2; videoRef === null || videoRef === void 0 || (_videoRef$current2 = videoRef.current) === null || _videoRef$current2 === void 0 || _videoRef$current2.seek(0); } }, [currentTime, duration, videoRef]); useEffect(() => { if (toggleResizeModeOnFullscreen) { setResizeMode(_isFullscreen ? ResizeMode.CONTAIN : ResizeMode.COVER); } if (mounted.current) { if (_isFullscreen) { typeof events.onEnterFullscreen === 'function' && events.onEnterFullscreen(); } else { typeof events.onExitFullscreen === 'function' && events.onExitFullscreen(); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [_isFullscreen, toggleResizeModeOnFullscreen]); useEffect(() => { setIsFullscreen(isFullscreen); }, [isFullscreen]); useEffect(() => { setPaused(paused); }, [paused]); useEffect(() => { if (_paused) { typeof events.onPause === 'function' && events.onPause(); } else { typeof events.onPlay === 'function' && events.onPlay(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [_paused]); useEffect(() => { if (!seeking && currentTime && duration) { const percent = currentTime / duration; const position = seekerWidth * percent; const cachedPercent = cachedDuration / duration; const _cachedPosition = seekerWidth * cachedPercent; const newCachedPosition = constrainToSeekerMinMax(_cachedPosition); setCachedPosition(newCachedPosition); setSeekerPosition(position); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentTime, duration, seekerWidth, setSeekerPosition]); // set current time when seeking useEffect(() => { if (seeking && seekerPosition && seekerWidth && duration) { const percent = seekerPosition / seekerWidth; const newTime = duration * percent; setCurrentTime(newTime); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [seeking, seekerPosition]); useEffect(() => { if (showControls) { animations.showControlAnimation(); setControlTimeout(); typeof events.onShowControls === 'function' && events.onShowControls(); } else { animations.hideControlAnimation(); clearControlTimeout(); typeof events.onHideControls === 'function' && events.onHideControls(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [showControls, loading]); useEffect(() => { setMuted(muted); }, [muted]); useEffect(() => { const newVolume = volumePosition / volumeWidth; if (newVolume <= 0) { setMuted(true); } else { setMuted(false); } setVolume(newVolume); setVolumeOffset(volumePosition); const newVolumeTrackWidth = volumeWidth - volumeFillWidth; if (newVolumeTrackWidth > 150) { setVolumeTrackWidth(150); } else { setVolumeTrackWidth(newVolumeTrackWidth); } }, [volumeFillWidth, volumePosition]); useEffect(() => { const position = volumeWidth * _volume; setVolumePosition(position); setVolumeOffset(position); mounted.current = true; return () => { mounted.current = false; clearControlTimeout(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const rewind = time => { var _videoRef$current3; const newTime = typeof time === 'number' ? currentTime - time : currentTime - rewindTime; setCurrentTime(newTime); videoRef === null || videoRef === void 0 || (_videoRef$current3 = videoRef.current) === null || _videoRef$current3 === void 0 || _videoRef$current3.seek(newTime); }; const forward = time => { var _videoRef$current4; const newTime = typeof time === 'number' ? currentTime + time : currentTime + rewindTime; setCurrentTime(newTime); videoRef === null || videoRef === void 0 || (_videoRef$current4 = videoRef.current) === null || _videoRef$current4 === void 0 || _videoRef$current4.seek(newTime); }; // reset on url change useEffect(() => { setLoading(true); setSeekerFillWidth(0); setSeekerPosition(0); setCachedPosition(0); setCurrentTime(0); // eslint-disable-next-line react-hooks/exhaustive-deps }, [typeof source === 'object' && 'uri' in source ? source.uri : null]); return /*#__PURE__*/React.createElement(PlatformSupport, { showControls: showControls, containerStyles: styles.containerStyle, onScreenTouch: events.onScreenTouch, testID: testID }, /*#__PURE__*/React.createElement(View, { style: [_styles.player.container, styles.containerStyle] }, /*#__PURE__*/React.createElement(Video, _extends({}, props, events, { ref: videoRef || _videoRef, resizeMode: resizeMode, volume: _volume, paused: _paused, muted: _muted, rate: rate, style: [_styles.player.video, styles.videoStyle], source: source, onBuffer: e => { setBuffering(e.isBuffering); if (!e.isBuffering) { setPaused(false); } } })), /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Error, { error: error }), !disableOverlay && /*#__PURE__*/React.createElement(Overlay, { animations: animations }), /*#__PURE__*/React.createElement(TopControls, { title: title, panHandlers: volumePanResponder.panHandlers, animations: animations, disableBack: disableBack, disableVolume: disableVolume, volumeFillWidth: volumeFillWidth, volumeTrackWidth: volumeTrackWidth, volumePosition: volumePosition, onBack: events.onBack, resetControlTimeout: resetControlTimeout, showControls: showControls }), loading ? /*#__PURE__*/React.createElement(Loader, { color: seekColor }) : /*#__PURE__*/React.createElement(PlayPause, { animations: animations, disablePlayPause: disablePlayPause, disableSeekButtons: disableSeekButtons, paused: _paused // pauseLabel={pauseLabel} , togglePlayPause: togglePlayPause, resetControlTimeout: resetControlTimeout, showControls: showControls, onPressRewind: rewind, onPressForward: forward, buffering: buffering, primaryColor: seekColor }), /*#__PURE__*/React.createElement(Gestures, { forward: forward, rewind: rewind, togglePlayPause: togglePlayPause, doubleTapTime: doubleTapTime, seekerWidth: seekerWidth, rewindTime: rewindTime, toggleControls: toggleControls, tapActionTimeout: tapActionTimeout, tapAnywhereToPause: tapAnywhereToPause, showControls: showControls, disableGesture: disableGesture }), /*#__PURE__*/React.createElement(BottomControls, { animations: animations, panHandlers: seekPanResponder.panHandlers, disableTimer: disableTimer, disableSeekbar: disableSeekbar, showHours: showHours, showDuration: showDuration, paused: _paused, showTimeRemaining: _showTimeRemaining, currentTime: currentTime, duration: duration, seekColor: seekColor, toggleTimer: toggleTimer, resetControlTimeout: resetControlTimeout, seekerFillWidth: seekerFillWidth, seekerPosition: seekerPosition, setSeekerWidth: setSeekerWidth, cachedPosition: cachedPosition, isFullscreen: isFullscreen, disableFullscreen: disableFullscreen, toggleFullscreen: toggleFullscreen, showControls: showControls })))); }; const CustomAnimations = ({ useAnimations, controlAnimationTiming = 450, ...props }) => { const animations = useAnimations(controlAnimationTiming); return /*#__PURE__*/React.createElement(AnimatedVideoPlayer, _extends({ animations: animations }, props)); }; const JSAnimations = props => { const animations = useJSAnimations(props.controlAnimationTiming); return /*#__PURE__*/React.createElement(AnimatedVideoPlayer, _extends({ animations: animations }, props)); }; export const VideoPlayer = props => { if (props !== null && props !== void 0 && props.useAnimations) { return /*#__PURE__*/React.createElement(CustomAnimations, _extends({ useAnimations: props === null || props === void 0 ? void 0 : props.useAnimations }, props)); } return /*#__PURE__*/React.createElement(JSAnimations, props); }; //# sourceMappingURL=VideoPlayer.js.map