Add option to retry video stream on failure

## Issue
Closes 4475 Option to retry video stream on failure
This commit is contained in:
infiinte-persistence 2020-07-15 19:20:27 +08:00 committed by Sean Yesmunt
parent 115c456318
commit 7bef092013
3 changed files with 59 additions and 19 deletions

View file

@ -9,7 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Allow zooming on Desktop _community pr!_ ([#4513](https://github.com/lbryio/lbry-desktop/pull/4513))
- Show "YT Creator" label in File Page as well _community pr!_ ([#4523](https://github.com/lbryio/lbry-desktop/pull/4523))
- Show "YT Creator" label in File Page as well _community pr!_ ([#4523](https://github.com/lbryio/lbry-desktop/pull/4523))
- Add option to retry video stream on failure _community pr!_ ([#4541](https://github.com/lbryio/lbry-desktop/pull/4541))
### Changed

View file

@ -1162,6 +1162,7 @@
"No information will be sent directly to LBRY, Inc. or third-parties about your usage. Note that as\n peer-to-peer software, your IP address and potentially other system information can be sent to other\n users, though this information is not stored permanently.": "No information will be sent directly to LBRY, Inc. or third-parties about your usage. Note that as\n peer-to-peer software, your IP address and potentially other system information can be sent to other\n users, though this information is not stored permanently.",
"%view_count% Views": "%view_count% Views",
"Tap to unmute": "Tap to unmute",
"Retry": "Retry",
"Playing in %seconds_left% seconds...": "Playing in %seconds_left% seconds...",
"Autoplay timer paused.": "Autoplay timer paused.",
"0 Bytes": "0 Bytes",

View file

@ -1,5 +1,5 @@
// @flow
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import Button from 'component/button';
import * as ICONS from 'constants/icons';
import classnames from 'classnames';
@ -7,6 +7,7 @@ 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 isUserTyping from 'util/detect-typing';
const isDev = process.env.NODE_ENV !== 'production';
export type Player = {
on: (string, (any) => void) => void,
@ -79,6 +80,7 @@ properties for this component should be kept to ONLY those that if changed shoul
*/
export default React.memo<Props>(function VideoJs(props: Props) {
const { startMuted, source, sourceType, poster, isAudio, onPlayerReady } = props;
const [reload, setReload] = useState('initial');
let player: ?Player;
const containerRef = useRef();
const videoJsOptions = {
@ -97,17 +99,33 @@ export default React.memo<Props>(function VideoJs(props: Props) {
videoJsOptions.muted = startMuted;
const tapToUnmuteRef = useRef();
const tapToRetryRef = useRef();
const TAP = {
UNMUTE: 'UNMUTE',
RETRY: 'RETRY',
};
function showTapButton(tapButton, newState) {
let theRef;
switch (tapButton) {
case TAP.UNMUTE:
theRef = tapToUnmuteRef;
break;
case TAP.RETRY:
theRef = tapToRetryRef;
break;
default:
if (isDev) throw new Error('showTapButton: unexpected ref');
return;
}
function showTapToUnmute(newState: boolean) {
// Use the DOM to control the state of the button to prevent re-renders.
// The button only needs to appear once per session.
if (tapToUnmuteRef.current) {
const curState = tapToUnmuteRef.current.style.visibility === 'visible';
if (theRef.current) {
const curState = theRef.current.style.visibility === 'visible';
if (newState !== curState) {
tapToUnmuteRef.current.style.visibility = newState ? 'visible' : 'hidden';
theRef.current.style.visibility = newState ? 'visible' : 'hidden';
}
} else if (process.env.NODE_ENV === 'development') {
throw new Error('[videojs.jsx] Empty video ref should not happen');
}
}
@ -118,32 +136,44 @@ export default React.memo<Props>(function VideoJs(props: Props) {
player.volume(1.0);
}
}
showTapToUnmute(false);
showTapButton(TAP.UNMUTE, false);
}
function retryVideoAfterFailure() {
if (player) {
setReload(Date.now());
showTapButton(TAP.RETRY, false);
}
}
function onInitialPlay() {
if (player && (player.muted() || player.volume() === 0)) {
// The css starts as "hidden". We make it visible here without
// re-rendering the whole thing.
showTapToUnmute(true);
showTapButton(TAP.UNMUTE, true);
}
showTapButton(TAP.RETRY, false);
}
function onVolumeChange() {
if (player && !player.muted()) {
showTapToUnmute(false);
showTapButton(TAP.UNMUTE, false);
}
}
function onError() {
showTapToUnmute(false);
showTapButton(TAP.UNMUTE, false);
showTapButton(TAP.RETRY, true);
if (player && player.loadingSpinner) {
player.loadingSpinner.hide();
}
}
function onEnded() {
showTapToUnmute(false);
showTapButton(TAP.UNMUTE, false);
showTapButton(TAP.RETRY, false);
}
function handleKeyDown(e: KeyboardEvent) {
@ -225,9 +255,9 @@ export default React.memo<Props>(function VideoJs(props: Props) {
});
return (
// $FlowFixMe
<div className={classnames('video-js-parent', { 'video-js-parent--ios': IS_IOS })} ref={containerRef}>
{
reload && (
// $FlowFixMe
<div className={classnames('video-js-parent', { 'video-js-parent--ios': IS_IOS })} ref={containerRef}>
<Button
label={__('Tap to unmute')}
button="link"
@ -236,7 +266,15 @@ export default React.memo<Props>(function VideoJs(props: Props) {
onClick={unmuteAndHideHint}
ref={tapToUnmuteRef}
/>
}
</div>
<Button
label={__('Retry')}
button="link"
icon={ICONS.REFRESH}
className="video-js--tap-to-unmute"
onClick={retryVideoAfterFailure}
ref={tapToRetryRef}
/>
</div>
)
);
});