attempt to persist a single video.js instance

Continually recreating video.js instance on render is bad.
Instead, persist a single instance, and simply update the source and poster on the existing instance.
This commit is contained in:
DispatchCommit 2021-01-23 11:38:55 -08:00 committed by Sean Yesmunt
parent 912489cce0
commit 7c8383f2dc

View file

@ -325,50 +325,79 @@ export default React.memo<Props>(function VideoJs(props: Props) {
} }
} }
// Create the video element. Note that a new videojs instantiation will happen on *every* render, so do not add props to this component! // Create the video DOM element and wrapper
useEffect(() => { function createVideoPlayerDOM(container) {
if (containerRef.current) { if (!container) {
console.error(`VideoPlayer Error: missing ref to container!`);
return;
}
// This seems like a poor way to generate the DOM for video.js
const wrapper = document.createElement('div'); const wrapper = document.createElement('div');
wrapper.setAttribute('data-vjs-player', 'true'); wrapper.setAttribute('data-vjs-player', 'true');
const el = document.createElement(isAudio ? 'audio' : 'video'); const el = document.createElement(isAudio ? 'audio' : 'video');
el.className = 'video-js'; el.className = 'video-js';
wrapper.appendChild(el); wrapper.appendChild(el);
// $FlowFixMe container.appendChild(wrapper);
containerRef.current.appendChild(wrapper);
fetch(source, { method: 'HEAD' }).then(response => { return el;
if (response && response.redirected && response.url && response.url.endsWith('m3u8')) { }
videoJsOptions.sources[0].type = 'application/x-mpegURL';
// Initialize video.js
function initializeVideoPlayer(el) {
if (!el) {
console.error(`Failed to initialize video player: Missing element to attach to`);
return;
} }
player = videojs(el, videoJsOptions, () => { player = videojs(el, videoJsOptions, () => {
if (player) { // this seems like a weird thing to have to check for here
if (!player) {
console.error(`Failed to create videojs player!`);
return;
}
// Add various event listeners to player
player.one('play', onInitialPlay); player.one('play', onInitialPlay);
player.on('volumechange', onVolumeChange); player.on('volumechange', onVolumeChange);
player.on('error', onError); player.on('error', onError);
player.on('ended', onEnded); player.on('ended', onEnded);
// Replace volume bar with custom LBRY volume bar
LbryVolumeBarClass.replaceExisting(player); LbryVolumeBarClass.replaceExisting(player);
// initialize mobile UI
player.mobileUi(); // Inits mobile version. No-op if Desktop. player.mobileUi(); // Inits mobile version. No-op if Desktop.
// I think this is a callback function
onPlayerReady(player); onPlayerReady(player);
}
}); });
// Add quality selector to player
player.hlsQualitySelector({ player.hlsQualitySelector({
displayCurrentQuality: true, displayCurrentQuality: true,
}); });
// Add reference to player to global scope
window.player = player; window.player = player;
// fixes #3498 (https://github.com/lbryio/lbry-desktop/issues/3498) // fixes #3498 (https://github.com/lbryio/lbry-desktop/issues/3498)
// summary: on firefox the focus would stick to the fullscreen button which caused buggy behavior with spacebar // summary: on firefox the focus would stick to the fullscreen button which caused buggy behavior with spacebar
// $FlowFixMe
player.on('fullscreenchange', () => document.activeElement && document.activeElement.blur()); player.on('fullscreenchange', () => document.activeElement && document.activeElement.blur());
window.addEventListener('keydown', handleKeyDown); return player;
}); }
// This lifecycle hook is only called once (on mount)
useEffect(() => {
const vjsElement = createVideoPlayerDOM(containerRef.current);
const vjsPlayer = initializeVideoPlayer(vjsElement);
// Add event listener for keyboard shortcuts
window.addEventListener('keydown', handleKeyDown);
// Cleanup
return () => { return () => {
window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keydown', handleKeyDown);
@ -376,8 +405,46 @@ export default React.memo<Props>(function VideoJs(props: Props) {
player.dispose(); player.dispose();
window.player = undefined; window.player = undefined;
} }
};
} }
}, []);
// Update video player settings and reload it when props change
useEffect(() => {
// For some reason the video player is responsible for detecting content type this way
fetch(source, { method: 'HEAD' }).then(response => {
if (!player) {
console.log(`Our player was disposed, we should disregard the fetch result.`);
return;
}
let type = sourceType;
// override type if we receive an .m3u8 (transcoded mp4)
if (
response &&
response.redirected &&
response.url &&
response.url.endsWith('m3u8')
) {
type = 'application/x-mpegURL';
}
// Update player poster
// note: the poster prop seems to return null usually.
if ( poster ) player.poster(poster);
// Update player source
player.src({
src: source,
type: type,
});
console.log(`Updated Player: ${source} (${type}) Poster: ${poster}`);
});
return () => {
console.log('Guess we could clean up something here if needed');
};
}); });
return ( return (