From d6941392e071f2bd50fbe832dde203b7f18da769 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> Date: Fri, 5 Apr 2024 10:35:57 +0200 Subject: [PATCH] fix: ensure poster works as expected and add it to the sample (#3643) * fix: ensure poster works as expected and add it to the sample * chore: drop audioOnly property as not implemented on any platform * fix(ios): do not save pause state before seeking * fix(ts): onPlaybackRateChangeData was not correctly typed --- .eslintrc | 3 +++ docs/pages/component/props.mdx | 11 --------- examples/basic/src/VideoPlayer.tsx | 39 +++++++++++++++++++++++++++--- ios/Video/RCTVideo.swift | 7 ++---- package.json | 1 + src/Video.tsx | 15 +++++++----- src/types/video.ts | 1 - 7 files changed, 50 insertions(+), 27 deletions(-) diff --git a/.eslintrc b/.eslintrc index 2c39af83..05692c26 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,6 +7,9 @@ "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], + "rules": { + "no-trailing-spaces": 1 + }, "parserOptions": { "requireConfigFile": false } diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index d5a346d8..42a56af0 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -29,17 +29,6 @@ Indicates whether the player allows switching to external playback mode such as - **true (default)** - allow switching to external playback mode - **false** - Don't allow switching to external playback mode -### `audioOnly` - - - -Indicates whether the player should only play the audio track and instead of displaying the video track, show the poster instead. - -- **false (default)** - Display the video as normal -- **true** - Show the poster and play the audio - -For this to work, the poster prop must be set. - ### `audioOutput` diff --git a/examples/basic/src/VideoPlayer.tsx b/examples/basic/src/VideoPlayer.tsx index 98e1e17f..1e12d4c4 100644 --- a/examples/basic/src/VideoPlayer.tsx +++ b/examples/basic/src/VideoPlayer.tsx @@ -36,6 +36,7 @@ import Video, { OnTextTrackDataChangedData, TextTrackType, ISO639_1, + OnSeekData, OnPlaybackStateChangedData, OnPlaybackRateChangeData, } from 'react-native-video'; @@ -68,6 +69,7 @@ interface StateType { srcListId: number; loop: boolean; showRNVControls: boolean; + poster?: string; } class VideoPlayer extends Component { @@ -95,6 +97,7 @@ class VideoPlayer extends Component { srcListId: 0, loop: false, showRNVControls: false, + poster: undefined, }; seekerWidth = 0; @@ -140,7 +143,7 @@ class VideoPlayer extends Component { type: TextTrackType.VTT, uri: 'https://bitdash-a.akamaihd.net/content/sintel/subtitles/subtitles_en.vtt', }, - ] + ], }, ]; @@ -190,6 +193,10 @@ class VideoPlayer extends Component { }, ]; + // poster which can be displayed + samplePoster = + 'https://upload.wikimedia.org/wikipedia/commons/1/18/React_Native_Logo.png'; + srcList = this.srcAllPlatformList.concat( Platform.OS === 'android' ? this.srcAndroidList : this.srcIosList, ); @@ -223,14 +230,26 @@ class VideoPlayer extends Component { this.onTextTracks(data); }; - onProgress = (data: OnProgressData) => { - if (!this.state.seeking) { + updateSeeker = () => { + // put this code in timeout as because it may be put just after a setState + setTimeout(()=> { const position = this.calculateSeekerPosition(); this.setSeekerPosition(position); - } + }, 1) + } + + onProgress = (data: OnProgressData) => { this.setState({currentTime: data.currentTime}); + if (!this.state.seeking) { + this.updateSeeker() + } }; + onSeek = (data: OnSeekData) => { + this.setState({currentTime: data.currentTime}); + this.updateSeeker() + } + onVideoLoadStart = () => { console.log('onVideoLoadStart'); this.setState({isLoading: true}); @@ -662,6 +681,16 @@ class VideoPlayer extends Component { }} text="decoration" /> + { + this.setState({ + poster: this.state.poster ? undefined : this.samplePoster, + }); + }} + selectedText="poster" + unselectedText="no poster" + /> {/* shall be replaced by slider */} @@ -810,11 +839,13 @@ class VideoPlayer extends Component { onAspectRatio={this.onAspectRatio} onReadyForDisplay={this.onReadyForDisplay} onBuffer={this.onVideoBuffer} + onSeek={this.onSeek} repeat={this.state.loop} selectedTextTrack={this.state.selectedTextTrack} selectedAudioTrack={this.state.selectedAudioTrack} playInBackground={false} preventsDisplaySleepDuringVideoPlayback={true} + poster={this.state.poster} onPlaybackRateChange={this.onPlaybackRateChange} onPlaybackStateChanged={this.onPlaybackStateChanged} /> diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index cb22e870..571cc8c0 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -681,21 +681,18 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _pendingSeekTime = seekTime.floatValue return } - let wasPaused = _paused RCTPlayerOperations.seek( player: player, playerItem: item, - paused: wasPaused, + paused: _paused, seekTime: seekTime.floatValue, seekTolerance: seekTolerance.floatValue ) { [weak self] (_: Bool) in guard let self else { return } self._playerObserver.addTimeObserverIfNotSet() - if !wasPaused { - self.setPaused(false) - } + self.setPaused(_paused) self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))), "seekTime": seekTime, "target": self.reactTag]) diff --git a/package.json b/package.json index 0cdfa5a9..52990f37 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "test": "echo no test available", "check-ios": "scripts/swift-format.sh && scripts/swift-lint.sh && scripts/clang-format.sh", "check-android": "scripts/kotlin-lint.sh", + "check-all": "yarn check-android; yarn check-ios; yarn lint", "codegen": "node ./node_modules/react-native/scripts/generate-codegen-artifacts.js --path ./ ./output" }, "files": [ diff --git a/src/Video.tsx b/src/Video.tsx index 6afa510b..f357ee13 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -121,6 +121,8 @@ const Video = forwardRef( setRestoreUserInterfaceForPIPStopCompletionHandler, ] = useState(); + const hasPoster = !!poster; + const posterStyle = useMemo>( () => ({ ...StyleSheet.absoluteFillObject, @@ -286,19 +288,20 @@ const Video = forwardRef( const onVideoLoadStart = useCallback( (e: NativeSyntheticEvent) => { + hasPoster && setShowPoster(true); onLoadStart?.(e.nativeEvent); }, - [onLoadStart], + [hasPoster, onLoadStart], ); const onVideoLoad = useCallback( (e: NativeSyntheticEvent) => { if (Platform.OS === 'windows') { - setShowPoster(false); + hasPoster && setShowPoster(false); } onLoad?.(e.nativeEvent); }, - [onLoad, setShowPoster], + [onLoad, hasPoster, setShowPoster], ); const onVideoError = useCallback( @@ -388,9 +391,9 @@ const Video = forwardRef( ); const _onReadyForDisplay = useCallback(() => { - setShowPoster(false); + hasPoster && setShowPoster(false); onReadyForDisplay?.(); - }, [setShowPoster, onReadyForDisplay]); + }, [setShowPoster, hasPoster, onReadyForDisplay]); const _onPictureInPictureStatusChanged = useCallback( (e: NativeSyntheticEvent) => { @@ -567,7 +570,7 @@ const Video = forwardRef( _onReceiveAdEvent as (e: NativeSyntheticEvent) => void } /> - {showPoster ? ( + {hasPoster && showPoster ? ( ) : null} diff --git a/src/types/video.ts b/src/types/video.ts index 77c0f4db..2e703d5d 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -186,7 +186,6 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps { drm?: Drm; style?: StyleProp; adTagUrl?: string; - audioOnly?: boolean; audioOutput?: AudioOutput; // Mobile automaticallyWaitsToMinimizeStalling?: boolean; // iOS bufferConfig?: BufferConfig; // Android