Compare commits
14 commits
master
...
fix-videoj
Author | SHA1 | Date | |
---|---|---|---|
|
df84c4ccbd | ||
|
9b88463886 | ||
|
abbd524ead | ||
|
29dafce0a8 | ||
|
429ead3b3b | ||
|
fdfabe7ccf | ||
|
38740d7e43 | ||
|
391e2e25a2 | ||
|
1a4bc2a543 | ||
|
ed62423c02 | ||
|
91fe871be3 | ||
|
fb1e3e703b | ||
|
0e9ac90ae5 | ||
|
7bf2fa490f |
4 changed files with 105 additions and 39 deletions
|
@ -151,8 +151,8 @@ const onPlayerReady = (player, options) => {
|
||||||
* Never shows if the endscreen plugin is present
|
* Never shows if the endscreen plugin is present
|
||||||
*/
|
*/
|
||||||
const mobileUi = function(options) {
|
const mobileUi = function(options) {
|
||||||
// if (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) {
|
if (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) {
|
||||||
if (videojs.browser.IS_ANDROID) {
|
// if (videojs.browser.IS_ANDROID) {
|
||||||
this.ready(() => {
|
this.ready(() => {
|
||||||
onPlayerReady(this, videojs.mergeOptions(defaults, options));
|
onPlayerReady(this, videojs.mergeOptions(defaults, options));
|
||||||
});
|
});
|
||||||
|
|
|
@ -59,6 +59,7 @@ type Props = {
|
||||||
userId: ?number,
|
userId: ?number,
|
||||||
// allowPreRoll: ?boolean,
|
// allowPreRoll: ?boolean,
|
||||||
shareTelemetry: boolean,
|
shareTelemetry: boolean,
|
||||||
|
showAutoplayCountdown: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
// type VideoJSOptions = {
|
// type VideoJSOptions = {
|
||||||
|
@ -84,11 +85,12 @@ const VIDEO_JS_OPTIONS = {
|
||||||
controls: true,
|
controls: true,
|
||||||
html5: {
|
html5: {
|
||||||
vhs: {
|
vhs: {
|
||||||
overrideNative: !videojs.browser.IS_ANY_SAFARI,
|
overrideNative: true, // don't override on safari
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// keys to bind to for keyboard shortcuts
|
||||||
const SPACE_BAR_KEYCODE = 32;
|
const SPACE_BAR_KEYCODE = 32;
|
||||||
const SMALL_F_KEYCODE = 70;
|
const SMALL_F_KEYCODE = 70;
|
||||||
const SMALL_M_KEYCODE = 77;
|
const SMALL_M_KEYCODE = 77;
|
||||||
|
@ -118,6 +120,7 @@ const SEEK_BACKWARD_KEYCODE_5 = ARROW_LEFT_KEYCODE;
|
||||||
const SEEK_STEP_5 = 5;
|
const SEEK_STEP_5 = 5;
|
||||||
const SEEK_STEP = 10; // time to seek in seconds
|
const SEEK_STEP = 10; // time to seek in seconds
|
||||||
|
|
||||||
|
// register videojs plugins
|
||||||
if (!Object.keys(videojs.getPlugins()).includes('eventTracking')) {
|
if (!Object.keys(videojs.getPlugins()).includes('eventTracking')) {
|
||||||
videojs.registerPlugin('eventTracking', eventTracking);
|
videojs.registerPlugin('eventTracking', eventTracking);
|
||||||
}
|
}
|
||||||
|
@ -197,6 +200,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
userId,
|
userId,
|
||||||
// allowPreRoll,
|
// allowPreRoll,
|
||||||
shareTelemetry,
|
shareTelemetry,
|
||||||
|
showAutoplayCountdown,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [reload, setReload] = useState('initial');
|
const [reload, setReload] = useState('initial');
|
||||||
|
@ -217,11 +221,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
eventTracking: true,
|
eventTracking: true,
|
||||||
overlay: OVERLAY.OVERLAY_DATA,
|
overlay: OVERLAY.OVERLAY_DATA,
|
||||||
},
|
},
|
||||||
// fixes problem of errant CC button showing up on iOS
|
// bigPlayButton: false,
|
||||||
// the true fix here is to fix the m3u8 file, see: https://github.com/lbryio/lbry-desktop/pull/6315
|
|
||||||
controlBar: {
|
|
||||||
subsCapsButton: false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const tapToUnmuteRef = useRef();
|
const tapToUnmuteRef = useRef();
|
||||||
|
@ -246,6 +246,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
|
|
||||||
switch (tapButton) {
|
switch (tapButton) {
|
||||||
case TAP.NONE:
|
case TAP.NONE:
|
||||||
|
document.getElementsByClassName('video-js--tap-to-unmute')[0].style.visibility = 'hidden';
|
||||||
setButtonVisibility(tapToUnmuteRef, false);
|
setButtonVisibility(tapToUnmuteRef, false);
|
||||||
setButtonVisibility(tapToRetryRef, false);
|
setButtonVisibility(tapToRetryRef, false);
|
||||||
break;
|
break;
|
||||||
|
@ -263,14 +264,18 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unmute video when player hits "Tap to unmute button"
|
||||||
function unmuteAndHideHint() {
|
function unmuteAndHideHint() {
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
if (player) {
|
if (player) {
|
||||||
|
// unmute the video
|
||||||
player.muted(false);
|
player.muted(false);
|
||||||
|
// turn the volume all the way up if it's at 0
|
||||||
if (player.volume() === 0) {
|
if (player.volume() === 0) {
|
||||||
player.volume(1.0);
|
player.volume(1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// hide "Tap to unmute" button
|
||||||
showTapButton(TAP.NONE);
|
showTapButton(TAP.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,29 +301,29 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
// as the listener to update static texts.
|
// as the listener to update static texts.
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
if (player) {
|
if (player) {
|
||||||
const controlBar = player.getChild('controlBar');
|
// const controlBar = player.getChild('controlBar');
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case 'play':
|
case 'play':
|
||||||
controlBar.getChild('PlayToggle').controlText(__('Pause (space)'));
|
// controlBar.getChild('PlayToggle').controlText(__('Pause (space)'));
|
||||||
break;
|
break;
|
||||||
case 'pause':
|
case 'pause':
|
||||||
controlBar.getChild('PlayToggle').controlText(__('Play (space)'));
|
// controlBar.getChild('PlayToggle').controlText(__('Play (space)'));
|
||||||
break;
|
break;
|
||||||
case 'volumechange':
|
case 'volumechange':
|
||||||
controlBar
|
// controlBar
|
||||||
.getChild('VolumePanel')
|
// .getChild('VolumePanel')
|
||||||
.getChild('MuteToggle')
|
// .getChild('MuteToggle')
|
||||||
.controlText(player.muted() || player.volume() === 0 ? __('Unmute (m)') : __('Mute (m)'));
|
// .controlText(player.muted() || player.volume() === 0 ? __('Unmute (m)') : __('Mute (m)'));
|
||||||
break;
|
break;
|
||||||
case 'fullscreenchange':
|
case 'fullscreenchange':
|
||||||
controlBar
|
// controlBar
|
||||||
.getChild('FullscreenToggle')
|
// .getChild('FullscreenToggle')
|
||||||
.controlText(player.isFullscreen() ? __('Exit Fullscreen (f)') : __('Fullscreen (f)'));
|
// .controlText(player.isFullscreen() ? __('Exit Fullscreen (f)') : __('Fullscreen (f)'));
|
||||||
break;
|
break;
|
||||||
case 'loadstart':
|
case 'loadstart':
|
||||||
// --- Do everything ---
|
// --- Do everything ---
|
||||||
controlBar.getChild('PlaybackRateMenuButton').controlText(__('Playback Rate (<, >)'));
|
// controlBar.getChild('PlaybackRateMenuButton').controlText(__('Playback Rate (<, >)'));
|
||||||
controlBar.getChild('QualityButton').controlText(__('Quality'));
|
// controlBar.getChild('QualityButton').controlText(__('Quality'));
|
||||||
resolveCtrlText({ type: 'play' });
|
resolveCtrlText({ type: 'play' });
|
||||||
resolveCtrlText({ type: 'pause' });
|
resolveCtrlText({ type: 'pause' });
|
||||||
resolveCtrlText({ type: 'volumechange' });
|
resolveCtrlText({ type: 'volumechange' });
|
||||||
|
@ -329,7 +334,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
// (2) We'll have to get 'makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)'
|
// (2) We'll have to get 'makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)'
|
||||||
// as a prop here so we can say "Theater mode|Default mode" instead of
|
// as a prop here so we can say "Theater mode|Default mode" instead of
|
||||||
// "Toggle Theater mode".
|
// "Toggle Theater mode".
|
||||||
controlBar.getChild('Button').controlText(__('Toggle Theater mode (t)'));
|
// controlBar.getChild('Button').controlText(__('Toggle Theater mode (t)'));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (isDev) throw Error('Unexpected: ' + e.type);
|
if (isDev) throw Error('Unexpected: ' + e.type);
|
||||||
|
@ -340,6 +345,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
|
|
||||||
function onInitialPlay() {
|
function onInitialPlay() {
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
|
// show the unmute button if the video is muted
|
||||||
if (player && (player.muted() || player.volume() === 0)) {
|
if (player && (player.muted() || player.volume() === 0)) {
|
||||||
// The css starts as "hidden". We make it visible here without
|
// The css starts as "hidden". We make it visible here without
|
||||||
// re-rendering the whole thing.
|
// re-rendering the whole thing.
|
||||||
|
@ -357,6 +363,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onError() {
|
function onError() {
|
||||||
|
console.log('error!');
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
showTapButton(TAP.RETRY);
|
showTapButton(TAP.RETRY);
|
||||||
|
|
||||||
|
@ -493,6 +500,7 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
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 vjs-big-play-centered';
|
el.className = 'video-js vjs-big-play-centered';
|
||||||
|
|
||||||
wrapper.appendChild(el);
|
wrapper.appendChild(el);
|
||||||
|
|
||||||
container.appendChild(wrapper);
|
container.appendChild(wrapper);
|
||||||
|
@ -625,10 +633,11 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
window.removeEventListener('keydown', handleKeyDown);
|
window.removeEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
if (player) {
|
player.pause()
|
||||||
player.dispose();
|
player.dispose();
|
||||||
|
window.oldPlayer = window.player;
|
||||||
window.player = undefined;
|
window.player = undefined;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}, [isAudio]);
|
}, [isAudio]);
|
||||||
|
|
||||||
|
@ -666,6 +675,8 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
});
|
});
|
||||||
}, [source, reload]);
|
}, [source, reload]);
|
||||||
|
|
||||||
|
console.log('RUNNING HERE VIDEOJS!');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
<div className={classnames('video-js-parent', { 'video-js-parent--ios': IS_IOS })} ref={containerRef}>
|
<div className={classnames('video-js-parent', { 'video-js-parent--ios': IS_IOS })} ref={containerRef}>
|
||||||
|
|
|
@ -109,6 +109,13 @@ function VideoViewer(props: Props) {
|
||||||
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 */
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const IS_IOS =
|
||||||
|
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||||
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
|
||||||
|
!window.MSStream;
|
||||||
|
|
||||||
|
console.log("RUNNING HERE PARENT VIEW.")
|
||||||
|
|
||||||
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (uri && previousUri && uri !== previousUri) {
|
if (uri && previousUri && uri !== previousUri) {
|
||||||
|
@ -203,7 +210,17 @@ function VideoViewer(props: Props) {
|
||||||
playerReadyDependencyList.push(desktopPlayStartTime);
|
playerReadyDependencyList.push(desktopPlayStartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let alreadyRunning = false;
|
||||||
|
|
||||||
const onPlayerReady = useCallback((player: Player) => {
|
const onPlayerReady = useCallback((player: Player) => {
|
||||||
|
console.log('already running');
|
||||||
|
console.log(alreadyRunning)
|
||||||
|
console.log(new Date())
|
||||||
|
alreadyRunning = true;
|
||||||
|
|
||||||
|
|
||||||
|
console.log("PLAYER READY CALLBACK")
|
||||||
|
|
||||||
if (!embedded) {
|
if (!embedded) {
|
||||||
player.muted(muted);
|
player.muted(muted);
|
||||||
player.volume(volume);
|
player.volume(volume);
|
||||||
|
@ -212,23 +229,52 @@ function VideoViewer(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldPlay = !embedded || autoplayIfEmbedded;
|
const shouldPlay = !embedded || autoplayIfEmbedded;
|
||||||
|
|
||||||
|
// console.log('should play');
|
||||||
|
// console.log(shouldPlay);
|
||||||
|
|
||||||
|
// TODO: this is causing issues with videos starting randomly
|
||||||
// https://blog.videojs.com/autoplay-best-practices-with-video-js/#Programmatic-Autoplay-and-Success-Failure-Detection
|
// https://blog.videojs.com/autoplay-best-practices-with-video-js/#Programmatic-Autoplay-and-Success-Failure-Detection
|
||||||
if (shouldPlay) {
|
if (shouldPlay) {
|
||||||
const playPromise = player.play();
|
console.log('starting video!');
|
||||||
const timeoutPromise = new Promise((resolve, reject) =>
|
|
||||||
setTimeout(() => reject(PLAY_TIMEOUT_ERROR), PLAY_TIMEOUT_LIMIT)
|
|
||||||
);
|
|
||||||
|
|
||||||
Promise.race([playPromise, timeoutPromise]).catch((error) => {
|
(async function() {
|
||||||
if (typeof error === 'object' && error.name && error.name === 'NotAllowedError') {
|
try {
|
||||||
if (player.autoplay() && !player.muted()) {
|
console.log('is playing already');
|
||||||
// player.muted(true);
|
console.log(isPlaying);
|
||||||
// another version had player.play()
|
console.log('player');
|
||||||
|
console.log(player);
|
||||||
|
// console.log('is paused!');
|
||||||
|
// console.log(player.paused());
|
||||||
|
|
||||||
|
const isAlreadyPlaying = isPlaying;
|
||||||
|
setIsPlaying(true);
|
||||||
|
|
||||||
|
if (!isAlreadyPlaying) {
|
||||||
|
console.log('STARTING PLAYER')
|
||||||
|
const playerResponse = player.play();
|
||||||
|
const isPaused = player.paused();
|
||||||
|
// console.log(playerResponse)
|
||||||
|
// await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
// console.log(playerResponse);
|
||||||
|
if (IS_IOS && isPaused) {
|
||||||
|
document.getElementsByClassName('video-js--tap-to-unmute')[0].style.visibility = 'visible';
|
||||||
|
player.muted(true);
|
||||||
|
const iosResponse = player.play();
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
console.log(iosResponse);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.log('ALREADY HAVE PLAYER, DISPOSING!!')
|
||||||
|
player.dispose()
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
console.log('\n\n')
|
||||||
setIsPlaying(false);
|
|
||||||
});
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -261,6 +307,7 @@ function VideoViewer(props: Props) {
|
||||||
changeMute(player.muted());
|
changeMute(player.muted());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
player.on('ratechange', () => {
|
player.on('ratechange', () => {
|
||||||
const HAVE_NOTHING = 0; // https://docs.videojs.com/player#readyState
|
const HAVE_NOTHING = 0; // https://docs.videojs.com/player#readyState
|
||||||
if (player && player.readyState() !== HAVE_NOTHING) {
|
if (player && player.readyState() !== HAVE_NOTHING) {
|
||||||
|
@ -289,7 +336,7 @@ function VideoViewer(props: Props) {
|
||||||
{isEndededEmbed && <FileViewerEmbeddedEnded uri={uri} />}
|
{isEndededEmbed && <FileViewerEmbeddedEnded uri={uri} />}
|
||||||
{embedded && !isEndededEmbed && <FileViewerEmbeddedTitle uri={uri} />}
|
{embedded && !isEndededEmbed && <FileViewerEmbeddedTitle uri={uri} />}
|
||||||
{/* disable this loading behavior because it breaks when player.play() promise hangs */}
|
{/* disable this loading behavior because it breaks when player.play() promise hangs */}
|
||||||
{isLoading && <LoadingScreen status={__('Loading')} />}
|
{/* {isLoading && <LoadingScreen status={__('Loading')} />} */}
|
||||||
|
|
||||||
{!isFetchingAd && adUrl && (
|
{!isFetchingAd && adUrl && (
|
||||||
<>
|
<>
|
||||||
|
@ -336,6 +383,7 @@ function VideoViewer(props: Props) {
|
||||||
userId={userId}
|
userId={userId}
|
||||||
allowPreRoll={!embedded && !authenticated}
|
allowPreRoll={!embedded && !authenticated}
|
||||||
shareTelemetry={shareTelemetry}
|
shareTelemetry={shareTelemetry}
|
||||||
|
showAutoplayCountdown={autoplaySetting}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -150,3 +150,10 @@
|
||||||
.bottom-gradient {
|
.bottom-gradient {
|
||||||
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.4) 0%, transparent 72px);
|
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.4) 0%, transparent 72px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vjs-big-play-button {
|
||||||
|
// if the user is using a mouse
|
||||||
|
@media (pointer: none), (pointer:coarse) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue