vjs: Fix 'Video-setting persistence broken'

## Issue
5513: Video-setting persistence broken

## Notes
- Per videojs recommendation, the setting-restoration should be done after the video has been loaded, so the action was moved to `loadedmetadata`. This fixed the volume slider problem, and should have fixed the playbackRate too.
- For playbackRate, there is another special case where it gets reset to 1 (refer to comments in code).
This commit is contained in:
infiinte-persistence 2021-02-18 01:01:12 +08:00 committed by Jeremy Kauffman
parent 09c1cfeb8f
commit 83912627de
2 changed files with 32 additions and 4 deletions

View file

@ -31,6 +31,7 @@ export type Player = {
loadingSpinner: any, loadingSpinner: any,
getChild: (string) => any, getChild: (string) => any,
playbackRate: (?number) => number, playbackRate: (?number) => number,
readyState: () => number,
userActive: (?boolean) => boolean, userActive: (?boolean) => boolean,
overlay: (any) => void, overlay: (any) => void,
mobileUi: (any) => void, mobileUi: (any) => void,

View file

@ -76,6 +76,7 @@ function VideoViewer(props: Props) {
const [isPlaying, setIsPlaying] = useState(false); const [isPlaying, setIsPlaying] = useState(false);
const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false); const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false);
const [isEndededEmbed, setIsEndededEmbed] = useState(false); const [isEndededEmbed, setIsEndededEmbed] = useState(false);
const vjsCallbackDataRef: any = React.useRef();
/* isLoading was designed to show loading screen on first play press, rather than completely black screen, but /* isLoading was designed to show loading screen on first play press, rather than completely black screen, but
breaks because some browsers (e.g. Firefox) block autoplay but leave the player.play Promise pending */ breaks because some browsers (e.g. Firefox) block autoplay but leave the player.play Promise pending */
@ -93,6 +94,16 @@ function VideoViewer(props: Props) {
} }
}, [uri, previousUri]); }, [uri, previousUri]);
// Update vjsCallbackDataRef (ensures videojs callbacks are not using stale values):
useEffect(() => {
vjsCallbackDataRef.current = {
embedded: embedded,
muted: muted,
volume: volume,
videoPlaybackRate: videoPlaybackRate,
};
}, [embedded, muted, volume, videoPlaybackRate]);
function doTrackingBuffered(e: Event, data: any) { function doTrackingBuffered(e: Event, data: any) {
fetch(source, { method: 'HEAD' }).then((response) => { fetch(source, { method: 'HEAD' }).then((response) => {
data.playerPoweredBy = response.headers.get('x-powered-by'); data.playerPoweredBy = response.headers.get('x-powered-by');
@ -137,12 +148,17 @@ function VideoViewer(props: Props) {
} }
} }
function restoreSavedSettings(player) {
if (!vjsCallbackDataRef.current.embedded) {
player.muted(vjsCallbackDataRef.current.muted);
player.volume(vjsCallbackDataRef.current.volume);
player.playbackRate(vjsCallbackDataRef.current.videoPlaybackRate);
}
}
const onPlayerReady = useCallback( const onPlayerReady = useCallback(
(player: Player) => { (player: Player) => {
if (!embedded) { if (!embedded) {
player.muted(muted);
player.volume(volume);
player.playbackRate(videoPlaybackRate);
addTheaterModeButton(player, toggleVideoTheaterMode); addTheaterModeButton(player, toggleVideoTheaterMode);
} }
@ -170,6 +186,12 @@ function VideoViewer(props: Props) {
setIsLoading(shouldPlay); // if we are here outside of an embed, we're playing setIsLoading(shouldPlay); // if we are here outside of an embed, we're playing
// PR: #5535; Commit: "vjs: Fix 'Video-setting persistence broken'"
// Move the restoration to a later `loadedmetadata` phase to counter the
// delay from the header-fetch. This is a temp change until the next
// re-factoring.
player.on('loadedmetadata', () => restoreSavedSettings(player));
player.on('tracking:buffered', doTrackingBuffered); player.on('tracking:buffered', doTrackingBuffered);
player.on('tracking:firstplay', doTrackingFirstPlay); player.on('tracking:firstplay', doTrackingFirstPlay);
player.on('ended', onEnded); player.on('ended', onEnded);
@ -191,7 +213,12 @@ function VideoViewer(props: Props) {
} }
}); });
player.on('ratechange', () => { player.on('ratechange', () => {
if (player) { const HAVE_NOTHING = 0; // https://docs.videojs.com/player#readyState
if (player && player.readyState() !== HAVE_NOTHING) {
// The playbackRate occasionally resets to 1, typically when loading a fresh video or when 'src' changes.
// Videojs says it's a browser quirk (https://github.com/videojs/video.js/issues/2516).
// [x] Don't update 'videoPlaybackRate' in this scenario.
// [ ] Ideally, the controlBar should be hidden to prevent users from changing the rate while loading.
setVideoPlaybackRate(player.playbackRate()); setVideoPlaybackRate(player.playbackRate());
} }
}); });