2020-03-26 11:44:38 +01:00
|
|
|
import React, { useEffect, useReducer, useRef } from 'react';
|
2020-03-24 22:57:41 +01:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
2020-03-26 16:07:14 +01:00
|
|
|
import Duration from '../Duration';
|
2020-03-24 22:57:41 +01:00
|
|
|
import LoadingIndicator from '../LoadingIndicator';
|
2020-03-25 21:40:51 +01:00
|
|
|
import PlayIcon from '../PlayIcon';
|
2020-03-24 22:57:41 +01:00
|
|
|
import Wrapper from './Wrapper';
|
2020-03-25 21:40:51 +01:00
|
|
|
import CanvasWrapper from './CanvasWrapper';
|
2020-03-26 16:07:14 +01:00
|
|
|
import Thumbnail from './Thumbnail';
|
2020-03-25 21:40:51 +01:00
|
|
|
|
|
|
|
import reducer, { initialState } from './reducer';
|
|
|
|
|
2020-03-26 16:07:14 +01:00
|
|
|
const VideoPreview = ({ hasIcon, previewUrl, src }) => {
|
2020-03-25 21:40:51 +01:00
|
|
|
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
2020-03-26 11:44:38 +01:00
|
|
|
const { duration, dataLoaded, isHover, metadataLoaded, snapshot, seeked } = reducerState.toJS();
|
|
|
|
|
2020-03-27 06:25:38 +01:00
|
|
|
// Adapted from https://github.com/brothatru/react-video-thumbnail/blob/master/src/components/VideoThumbnail.js
|
|
|
|
// And from https://github.com/soupette/poc-video-preview
|
2020-03-25 21:40:51 +01:00
|
|
|
const canvasRef = useRef();
|
|
|
|
const videoRef = useRef();
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const getSnapshot = () => {
|
|
|
|
try {
|
|
|
|
const video = videoRef.current;
|
|
|
|
const canvas = canvasRef.current;
|
|
|
|
|
|
|
|
canvas.height = video.videoHeight;
|
|
|
|
canvas.width = video.videoWidth;
|
|
|
|
canvas.getContext('2d').drawImage(video, 0, 0);
|
|
|
|
|
|
|
|
const thumbnail = canvas.toDataURL('image/png');
|
|
|
|
|
|
|
|
video.src = ''; // setting to empty string stops video from loading
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
type: 'SET_SNAPSHOT',
|
|
|
|
snapshot: thumbnail,
|
|
|
|
});
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (dataLoaded && metadataLoaded && videoRef.current) {
|
|
|
|
videoRef.current.currentTime = 0;
|
|
|
|
|
|
|
|
if (seeked && !snapshot) {
|
|
|
|
getSnapshot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [dataLoaded, metadataLoaded, seeked, snapshot]);
|
|
|
|
|
|
|
|
const toggleHover = () => {
|
2020-03-26 11:44:38 +01:00
|
|
|
dispatch({
|
|
|
|
type: 'SET_IS_HOVER',
|
|
|
|
});
|
2020-03-25 21:40:51 +01:00
|
|
|
};
|
2020-03-24 22:57:41 +01:00
|
|
|
|
|
|
|
return (
|
2020-03-25 21:40:51 +01:00
|
|
|
<Wrapper onMouseEnter={toggleHover} onMouseLeave={toggleHover}>
|
|
|
|
{!snapshot && <LoadingIndicator />}
|
|
|
|
<CanvasWrapper>
|
2020-03-26 16:07:14 +01:00
|
|
|
{previewUrl ? (
|
|
|
|
<Thumbnail src={previewUrl} />
|
|
|
|
) : (
|
|
|
|
<>
|
|
|
|
<video
|
|
|
|
muted
|
|
|
|
ref={videoRef}
|
|
|
|
src={src}
|
|
|
|
crossOrigin="anonymous"
|
|
|
|
onLoadedMetadata={() => {
|
|
|
|
dispatch({
|
|
|
|
type: 'METADATA_LOADED',
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
onLoadedData={({ target: { duration } }) => {
|
|
|
|
dispatch({
|
|
|
|
type: 'DATA_LOADED',
|
|
|
|
duration,
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
onSeeked={() => {
|
|
|
|
dispatch({
|
|
|
|
type: 'SEEKED',
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<canvas ref={canvasRef} />
|
|
|
|
</>
|
|
|
|
)}
|
2020-03-26 11:44:38 +01:00
|
|
|
<Duration duration={duration} />
|
2020-03-26 16:07:14 +01:00
|
|
|
{hasIcon && isHover && <PlayIcon small />}
|
2020-03-25 21:40:51 +01:00
|
|
|
</CanvasWrapper>
|
2020-03-24 22:57:41 +01:00
|
|
|
</Wrapper>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
VideoPreview.defaultProps = {
|
2020-03-26 16:07:14 +01:00
|
|
|
hasIcon: false,
|
|
|
|
previewUrl: null,
|
2020-03-25 21:40:51 +01:00
|
|
|
src: null,
|
2020-03-24 22:57:41 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
VideoPreview.propTypes = {
|
2020-03-26 16:07:14 +01:00
|
|
|
hasIcon: PropTypes.bool,
|
|
|
|
previewUrl: PropTypes.string,
|
2020-03-25 21:40:51 +01:00
|
|
|
src: PropTypes.string,
|
2020-03-24 22:57:41 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
export default VideoPreview;
|