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
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