maybe ready
This commit is contained in:
parent
9db9363b9f
commit
6c8b0b2d68
7 changed files with 103 additions and 96 deletions
|
@ -1,11 +1,15 @@
|
|||
import * as SETTINGS from 'constants/settings';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeSelectClaimForUri } from 'lbry-redux';
|
||||
import { withRouter } from 'react-router';
|
||||
import { makeSelectIsPlayerFloating, makeSelectNextUnplayedRecommended } from 'redux/selectors/content';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { doSetPlayingUri } from 'redux/actions/content';
|
||||
import AutoplayCountdown from './view';
|
||||
|
||||
/*
|
||||
AutoplayCountdown does not fetch it's own next content to play, it relies on <RecommendedContent> being rendered. This is dumb but I'm just the guy who noticed
|
||||
*/
|
||||
const select = (state, props) => {
|
||||
const nextRecommendedUri = makeSelectNextUnplayedRecommended(props.uri)(state);
|
||||
return {
|
||||
|
@ -20,4 +24,4 @@ const perform = dispatch => ({
|
|||
setPlayingUri: uri => dispatch(doSetPlayingUri(uri)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(AutoplayCountdown);
|
||||
export default withRouter(connect(select, perform)(AutoplayCountdown));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import Button from 'component/button';
|
||||
import UriIndicator from 'component/uriIndicator';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
|
@ -35,20 +35,22 @@ function AutoplayCountdown(props: Props) {
|
|||
navigateUrl = formatLbryUrlForWeb(nextRecommendedUri);
|
||||
}
|
||||
|
||||
function doNavigate() {
|
||||
// FIXME: make autoplay continue in floating player
|
||||
const doNavigate = useCallback(() => {
|
||||
if (!isFloating) {
|
||||
// if not floating
|
||||
setPlayingUri(null);
|
||||
if (navigateUrl) {
|
||||
push(navigateUrl);
|
||||
setPlayingUri(null);
|
||||
}
|
||||
} else {
|
||||
if (nextRecommendedUri) {
|
||||
setPlayingUri(nextRecommendedUri);
|
||||
}
|
||||
}
|
||||
}, [navigateUrl, nextRecommendedUri, isFloating, setPlayingUri]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let interval;
|
||||
if (!timerCanceled) {
|
||||
if (!timerCanceled && nextRecommendedUri) {
|
||||
interval = setInterval(() => {
|
||||
const newTime = timer - 1;
|
||||
if (newTime === 0) {
|
||||
|
@ -82,12 +84,12 @@ function AutoplayCountdown(props: Props) {
|
|||
<Button onClick={doNavigate} iconSize={30} title={__('Play')} className="button--icon button--play" />
|
||||
</div>
|
||||
<div className="file-viewer__overlay-secondary autoplay-countdown__counter">
|
||||
{__('Playing in %seconds_left% seconds', { seconds_left: timer })}
|
||||
</div>
|
||||
</div>
|
||||
{__('Playing in %seconds_left% seconds...', { seconds_left: timer })}{' '}
|
||||
<Button label={__('Cancel')} button="link" onClick={() => setTimerCanceled(true)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import * as SETTINGS from 'constants/settings';
|
|||
import { connect } from 'react-redux';
|
||||
import { makeSelectFileInfoForUri, makeSelectTitleForUri } from 'lbry-redux';
|
||||
import {
|
||||
makeSelectIsPlaying,
|
||||
makeSelectIsPlayerFloating,
|
||||
selectPlayingUri,
|
||||
makeSelectFileRenderModeForUri,
|
||||
|
@ -19,7 +18,6 @@ const select = (state, props) => {
|
|||
uri,
|
||||
title: makeSelectTitleForUri(uri)(state),
|
||||
fileInfo: makeSelectFileInfoForUri(uri)(state),
|
||||
isPlaying: makeSelectIsPlaying(uri)(state),
|
||||
isFloating: makeSelectIsPlayerFloating(props.location)(state),
|
||||
streamingUrl: makeSelectStreamingUrlForUriWebProxy(uri)(state),
|
||||
floatingPlayerEnabled: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state),
|
||||
|
|
|
@ -15,8 +15,6 @@ import { onFullscreenChange } from 'util/full-screen';
|
|||
import useIsMobile from 'effects/use-is-mobile';
|
||||
|
||||
type Props = {
|
||||
isLoading: boolean,
|
||||
isPlaying: boolean,
|
||||
isFloating: boolean,
|
||||
fileInfo: FileListItem,
|
||||
uri: string,
|
||||
|
@ -28,17 +26,7 @@ type Props = {
|
|||
};
|
||||
|
||||
export default function FileRenderFloating(props: Props) {
|
||||
const {
|
||||
isPlaying,
|
||||
fileInfo,
|
||||
uri,
|
||||
streamingUrl,
|
||||
title,
|
||||
isFloating,
|
||||
clearPlayingUri,
|
||||
floatingPlayerEnabled,
|
||||
renderMode,
|
||||
} = props;
|
||||
const { fileInfo, uri, streamingUrl, title, isFloating, clearPlayingUri, floatingPlayerEnabled, renderMode } = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const [fileViewerRect, setFileViewerRect] = usePersistedState('inline-file-viewer:rect');
|
||||
|
@ -75,7 +63,7 @@ export default function FileRenderFloating(props: Props) {
|
|||
};
|
||||
}, [setFileViewerRect, isFloating]);
|
||||
|
||||
if (!isPlayable || !isPlaying || !uri || (isFloating && (isMobile || !floatingPlayerEnabled))) {
|
||||
if (!isPlayable || !uri || (isFloating && (isMobile || !floatingPlayerEnabled))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// @flow
|
||||
import React, { useContext, useEffect, useRef } from 'react';
|
||||
import videojs from 'video.js/dist/alt/video.core.novtt.min.js';
|
||||
import 'video.js/dist/alt/video-js-cdn.min.css';
|
||||
import eventTracking from 'videojs-event-tracking';
|
||||
import { EmbedContext } from '../../../../page/embedWrapper/view';
|
||||
import isUserTyping from '../../../../util/detect-typing';
|
||||
import { EmbedContext } from 'page/embedWrapper/view';
|
||||
import isUserTyping from 'util/detect-typing';
|
||||
|
||||
type Props = {
|
||||
source: string,
|
||||
|
@ -11,17 +12,10 @@ type Props = {
|
|||
poster: boolean,
|
||||
autoplay: boolean,
|
||||
onPlayerReady: () => null,
|
||||
onVolumeChange: () => null,
|
||||
isAudio: boolean,
|
||||
};
|
||||
|
||||
const VIDEO_JS_OPTIONS: VideoJSOptions = {
|
||||
controls: true,
|
||||
autoplay: true,
|
||||
preload: 'auto',
|
||||
playbackRates: [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 1.75, 2],
|
||||
responsive: true,
|
||||
};
|
||||
|
||||
type VideoJSOptions = {
|
||||
controls: boolean,
|
||||
autoplay: boolean,
|
||||
|
@ -33,6 +27,14 @@ type VideoJSOptions = {
|
|||
poster?: string,
|
||||
};
|
||||
|
||||
const VIDEO_JS_OPTIONS: VideoJSOptions = {
|
||||
controls: true,
|
||||
autoplay: true,
|
||||
preload: 'auto',
|
||||
playbackRates: [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 1.75, 2],
|
||||
responsive: true,
|
||||
};
|
||||
|
||||
const F11_KEYCODE = 122;
|
||||
const SPACE_BAR_KEYCODE = 32;
|
||||
const SMALL_F_KEYCODE = 70;
|
||||
|
@ -57,7 +59,7 @@ properties for this component should be kept to ONLY those that if changed shoul
|
|||
*/
|
||||
export default React.memo(function VideoJs(props: Props) {
|
||||
const { autoplay, source, sourceType, poster, isAudio, onPlayerReady } = props;
|
||||
const videoRef = useRef();
|
||||
const containerRef = useRef();
|
||||
const embedded = useContext(EmbedContext);
|
||||
const videoJsOptions = {
|
||||
...VIDEO_JS_OPTIONS,
|
||||
|
@ -75,7 +77,7 @@ export default React.memo(function VideoJs(props: Props) {
|
|||
videoJsOptions.muted = autoplay && embedded;
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
const { current: videoNode } = videoRef;
|
||||
const videoNode = containerRef.current && containerRef.current.querySelector('video, audio');
|
||||
|
||||
if (!videoNode || isUserTyping()) {
|
||||
return;
|
||||
|
@ -113,27 +115,33 @@ export default React.memo(function VideoJs(props: Props) {
|
|||
}
|
||||
|
||||
let player;
|
||||
useEffect(() => {
|
||||
if (videoRef.current) {
|
||||
console.log('videojs effect to instatiate player');
|
||||
const { current: videoNode } = videoRef;
|
||||
|
||||
player = videojs(videoNode, videoJsOptions);
|
||||
// Create the video element. Note that a new videojs instantiation will happen on *every* render, so do not add props to this component!
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.setAttribute('data-vjs-player', true);
|
||||
const el = document.createElement(isAudio ? 'audio' : 'video');
|
||||
el.className = 'video-js';
|
||||
wrapper.appendChild(el);
|
||||
containerRef.current.appendChild(wrapper);
|
||||
|
||||
player = videojs(el, videoJsOptions);
|
||||
onPlayerReady(player);
|
||||
|
||||
// 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
|
||||
// $FlowFixMe
|
||||
player.on('fullscreenchange', () => document.activeElement && document.activeElement.blur());
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
console.log('videojs effect cleanup to dispose player');
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
player.dispose();
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div data-vjs-player>
|
||||
{isAudio ? <audio ref={videoRef} className="video-js" /> : <video ref={videoRef} className="video-js" />}
|
||||
</div>
|
||||
);
|
||||
return <div className="video-js-parent" ref={containerRef} />;
|
||||
});
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// @flow
|
||||
import React, { useRef, useEffect, useState, useContext, useCallback } from 'react';
|
||||
import React, { useEffect, useState, useContext, useCallback } from 'react';
|
||||
import { stopContextMenu } from 'util/context-menu';
|
||||
import VideoJs from './internal/videojs';
|
||||
|
||||
import isUserTyping from 'util/detect-typing';
|
||||
import analytics from 'analytics';
|
||||
import { EmbedContext } from 'page/embedWrapper/view';
|
||||
import classnames from 'classnames';
|
||||
|
@ -12,18 +11,18 @@ import AutoplayCountdown from 'component/autoplayCountdown';
|
|||
import usePrevious from 'effects/use-previous';
|
||||
import FileViewerEmbeddedEnded from 'lbrytv/component/fileViewerEmbeddedEnded';
|
||||
import FileViewerEmbeddedTitle from 'lbrytv/component/fileViewerEmbeddedTitle';
|
||||
import LoadingScreen from 'component/common/loading-screen';
|
||||
|
||||
type Props = {
|
||||
position: number,
|
||||
hasFileInfo: boolean,
|
||||
changeVolume: number => void,
|
||||
savePosition: (string, number) => void,
|
||||
changeMute: boolean => void,
|
||||
source: string,
|
||||
contentType: string,
|
||||
thumbnail: string,
|
||||
hasFileInfo: boolean,
|
||||
claim: Claim,
|
||||
muted: boolean,
|
||||
volume: number,
|
||||
uri: string,
|
||||
autoplaySetting: boolean,
|
||||
autoplayIfEmbedded: boolean,
|
||||
|
@ -46,6 +45,8 @@ function VideoViewer(props: Props) {
|
|||
position,
|
||||
claim,
|
||||
uri,
|
||||
muted,
|
||||
volume,
|
||||
autoplaySetting,
|
||||
autoplayIfEmbedded,
|
||||
doAnalyticsView,
|
||||
|
@ -58,18 +59,25 @@ function VideoViewer(props: Props) {
|
|||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false);
|
||||
const [isEndededEmbed, setIsEndededEmbed] = useState(false);
|
||||
const [player, setPlayer] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const previousUri = usePrevious(uri);
|
||||
const embedded = useContext(EmbedContext);
|
||||
|
||||
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
||||
useEffect(() => {
|
||||
if (uri && previousUri && uri !== previousUri) {
|
||||
setShowAutoplayCountdown(false);
|
||||
setIsEndededEmbed(false);
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [uri, previousUri]);
|
||||
|
||||
function doTrackingBuffered(e: Event, data: any) {
|
||||
analytics.videoBufferEvent(claimId, data.currentTime);
|
||||
}
|
||||
|
||||
function doTrackingFirstPlay(e: Event, data: any) {
|
||||
console.log('doTrackingFirstPlay: ' + data.secondsToLoad);
|
||||
|
||||
analytics.videoStartEvent(claimId, data.secondsToLoad);
|
||||
|
||||
doAnalyticsView(uri, data.secondsToLoad).then(() => {
|
||||
|
@ -85,14 +93,8 @@ function VideoViewer(props: Props) {
|
|||
}
|
||||
}
|
||||
|
||||
function onVolumeChange(e: Event) {
|
||||
const isMuted = player.muted();
|
||||
const volume = player.volume();
|
||||
changeVolume(volume);
|
||||
changeMute(isMuted);
|
||||
}
|
||||
|
||||
function onPlay() {
|
||||
setIsLoading(false);
|
||||
setIsPlaying(true);
|
||||
setShowAutoplayCountdown(false);
|
||||
setIsEndededEmbed(false);
|
||||
|
@ -103,38 +105,50 @@ function VideoViewer(props: Props) {
|
|||
}
|
||||
|
||||
const onPlayerReady = useCallback(player => {
|
||||
console.log('videoViewer.onPlayerReady attach effects');
|
||||
setIsLoading(!embedded); // if we are here outside of an embed, we're playing
|
||||
|
||||
player.on('tracking:buffered', doTrackingBuffered);
|
||||
|
||||
player.on('tracking:firstplay', doTrackingFirstPlay);
|
||||
|
||||
player.on('ended', onEnded);
|
||||
player.on('volumechange', onVolumeChange);
|
||||
player.on('play', onPlay);
|
||||
player.on('pause', onPause);
|
||||
|
||||
// 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
|
||||
// $FlowFixMe
|
||||
player.on('fullscreenchange', () => document.activeElement && document.activeElement.blur());
|
||||
player.on('volumechange', () => {
|
||||
if (player && player.volume() !== volume) {
|
||||
changeVolume(player.volume());
|
||||
}
|
||||
if (player && player.muted() !== muted) {
|
||||
changeMute(player.muted());
|
||||
}
|
||||
});
|
||||
|
||||
if (position) {
|
||||
player.currentTime(position);
|
||||
}
|
||||
}, []);
|
||||
|
||||
console.log('VideoViewer render');
|
||||
// FIXME: below breaks rendering?!
|
||||
/* if (!embedded) {
|
||||
if (muted) {
|
||||
player.muted(muted);
|
||||
}
|
||||
if (volume) {
|
||||
player.volume(volume);
|
||||
}
|
||||
} */
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('file-viewer', {
|
||||
'file-viewer--is-playing': isPlaying,
|
||||
'file-viewer--ended-embed': isEndededEmbed,
|
||||
})}
|
||||
onContextMenu={stopContextMenu}
|
||||
>
|
||||
{showAutoplayCountdown && <AutoplayCountdown uri={uri} />}
|
||||
{isEndededEmbed && <FileViewerEmbeddedEnded uri={uri} />}
|
||||
{embedded && !isEndededEmbed && <FileViewerEmbeddedTitle uri={uri} />}
|
||||
{/* change message at any time */}
|
||||
{isLoading && <LoadingScreen status={__('Loading')} />}
|
||||
<VideoJs
|
||||
source={source}
|
||||
isAudio={isAudio}
|
||||
|
|
|
@ -164,10 +164,6 @@
|
|||
}
|
||||
|
||||
.content__viewer--floating {
|
||||
.file-viewer__overlay {
|
||||
padding: var(--spacing-medium);
|
||||
}
|
||||
|
||||
.file-viewer__overlay-title,
|
||||
.file-viewer__overlay-secondary {
|
||||
overflow: hidden;
|
||||
|
@ -177,10 +173,6 @@
|
|||
}
|
||||
}
|
||||
@media (max-width: $breakpoint-small) {
|
||||
.file-viewer__overlay {
|
||||
padding: var(--spacing-medium);
|
||||
}
|
||||
|
||||
.file-viewer__overlay-title,
|
||||
.file-viewer__overlay-secondary {
|
||||
overflow: hidden;
|
||||
|
@ -291,16 +283,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
.video-js {
|
||||
.video-js-parent {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Removing the play button because we have autoplay turned on
|
||||
// These are classes added by video.js
|
||||
// By default no video js play button
|
||||
.vjs-big-play-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.vjs-modal-dialog .vjs-modal-dialog-content {
|
||||
position: relative;
|
||||
padding-top: 5rem;
|
||||
|
@ -321,11 +317,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.vjs-paused .vjs-big-play-button,
|
||||
.vjs-paused.vjs-has-started .vjs-big-play-button {
|
||||
@media (max-width: $breakpoint-small) {
|
||||
display: block;
|
||||
}
|
||||
.file-viewer--ended-embed .vjs-big-play-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-render {
|
||||
|
|
Loading…
Reference in a new issue