2019-08-02 08:28:14 +02:00
|
|
|
// @flow
|
2019-08-13 07:35:13 +02:00
|
|
|
import React, { useRef, useEffect, useState } from 'react';
|
2019-08-02 08:28:14 +02:00
|
|
|
import { stopContextMenu } from 'util/context-menu';
|
|
|
|
import videojs from 'video.js';
|
|
|
|
import 'video.js/dist/video-js.css';
|
|
|
|
import isUserTyping from 'util/detect-typing';
|
|
|
|
|
|
|
|
const SPACE_BAR_KEYCODE = 32;
|
|
|
|
const VIDEO_JS_OPTIONS = {
|
|
|
|
autoplay: true,
|
|
|
|
controls: true,
|
|
|
|
preload: 'auto',
|
2019-08-13 07:35:13 +02:00
|
|
|
playbackRates: [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 2],
|
2019-08-02 08:28:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
type Props = {
|
2019-09-06 02:26:03 +02:00
|
|
|
volume: number,
|
|
|
|
position: number,
|
|
|
|
muted: boolean,
|
|
|
|
hasFileInfo: boolean,
|
|
|
|
changeVolume: number => void,
|
|
|
|
savePosition: (string, number) => void,
|
|
|
|
changeMute: boolean => void,
|
|
|
|
setPlayingUri: (string | null) => void,
|
2019-08-02 08:28:14 +02:00
|
|
|
source: string,
|
|
|
|
contentType: string,
|
2019-08-13 07:35:13 +02:00
|
|
|
hasFileInfo: boolean,
|
2019-09-06 02:26:03 +02:00
|
|
|
onEndedCB: any,
|
2019-08-02 08:28:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
function VideoViewer(props: Props) {
|
2019-09-06 02:26:03 +02:00
|
|
|
const { contentType, source, setPlayingUri, onEndedCB } = props;
|
2019-08-13 07:35:13 +02:00
|
|
|
const videoRef = useRef();
|
|
|
|
const [requireRedraw, setRequireRedraw] = useState(false);
|
2019-08-02 08:28:14 +02:00
|
|
|
|
2019-09-06 02:26:03 +02:00
|
|
|
useEffect(() => {
|
|
|
|
const currentVideo: HTMLVideoElement | null = document.querySelector('video');
|
|
|
|
|
|
|
|
function doEnded() {
|
2019-09-09 19:31:00 +02:00
|
|
|
// clear position
|
2019-09-06 02:26:03 +02:00
|
|
|
setPlayingUri(null);
|
|
|
|
onEndedCB();
|
|
|
|
}
|
2019-09-09 19:31:00 +02:00
|
|
|
function doPause(e: Event) {
|
|
|
|
// store position e.target.currentTime
|
|
|
|
}
|
|
|
|
function doVolume(e: Event) {
|
|
|
|
// store volume e.target.volume
|
|
|
|
}
|
2019-09-06 02:26:03 +02:00
|
|
|
|
|
|
|
if (currentVideo) {
|
|
|
|
currentVideo.addEventListener('ended', doEnded);
|
2019-09-09 19:31:00 +02:00
|
|
|
currentVideo.addEventListener('pause', doPause);
|
|
|
|
currentVideo.addEventListener('volumechange', doVolume);
|
2019-09-06 02:26:03 +02:00
|
|
|
}
|
|
|
|
// cleanup function:
|
|
|
|
return () => {
|
|
|
|
if (currentVideo) {
|
|
|
|
currentVideo.removeEventListener('ended', doEnded);
|
2019-09-09 19:31:00 +02:00
|
|
|
currentVideo.removeEventListener('pause', doPause);
|
|
|
|
currentVideo.removeEventListener('volumechange', doVolume);
|
2019-09-06 02:26:03 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
2019-08-02 08:28:14 +02:00
|
|
|
useEffect(() => {
|
2019-08-06 05:25:33 +02:00
|
|
|
const videoNode = videoRef.current;
|
|
|
|
const videoJsOptions = {
|
|
|
|
...VIDEO_JS_OPTIONS,
|
|
|
|
sources: [
|
|
|
|
{
|
|
|
|
src: source,
|
|
|
|
type: contentType,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
2019-08-02 08:28:14 +02:00
|
|
|
|
2019-08-13 07:35:13 +02:00
|
|
|
let player;
|
|
|
|
if (!requireRedraw) {
|
|
|
|
player = videojs(videoNode, videoJsOptions);
|
|
|
|
}
|
|
|
|
|
2019-08-06 05:25:33 +02:00
|
|
|
return () => {
|
2019-08-13 07:35:13 +02:00
|
|
|
if (!player) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Video.js has a player.dispose() function that is meant to cleanup a previous video
|
|
|
|
// We can't use this because it does some weird stuff to remove the video element from the page
|
|
|
|
// This makes it really hard to use because the ref we keep still thinks it's on the page
|
|
|
|
// requireRedraw just makes it so the video component is removed from the page _by react_
|
|
|
|
// Then it's set to false immediately after so we can re-mount a new player
|
|
|
|
setRequireRedraw(true);
|
2019-08-06 05:25:33 +02:00
|
|
|
};
|
2019-08-13 07:35:13 +02:00
|
|
|
}, [videoRef, source, contentType, setRequireRedraw, requireRedraw]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (requireRedraw) {
|
|
|
|
setRequireRedraw(false);
|
|
|
|
}
|
|
|
|
}, [requireRedraw]);
|
2019-08-02 08:28:14 +02:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
function handleKeyDown(e: KeyboardEvent) {
|
2019-08-13 07:35:13 +02:00
|
|
|
const videoNode = videoRef.current;
|
2019-08-02 08:28:14 +02:00
|
|
|
|
2019-08-13 07:35:13 +02:00
|
|
|
if (videoNode && !isUserTyping() && e.keyCode === SPACE_BAR_KEYCODE) {
|
2019-08-02 08:28:14 +02:00
|
|
|
e.preventDefault();
|
|
|
|
|
2019-08-13 07:35:13 +02:00
|
|
|
videoNode.paused ? videoNode.play() : videoNode.pause();
|
2019-08-02 08:28:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
|
|
return () => {
|
|
|
|
window.removeEventListener('keydown', handleKeyDown);
|
|
|
|
};
|
2019-08-13 07:35:13 +02:00
|
|
|
|
|
|
|
// include requireRedraw here so the event listener is re-added when we need to manually remove/add the video player
|
|
|
|
}, [videoRef, requireRedraw]);
|
2019-08-02 08:28:14 +02:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
|
2019-08-13 07:35:13 +02:00
|
|
|
{!requireRedraw && (
|
|
|
|
<div data-vjs-player>
|
|
|
|
<video ref={videoRef} className="video-js" />
|
|
|
|
</div>
|
|
|
|
)}
|
2019-08-02 08:28:14 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default VideoViewer;
|