Select Git revision
VideoViewer.js

Anthony authored
VideoViewer.js 5.82 KiB
import flatten from 'lodash/flatten';
import flattenDeep from 'lodash/flattenDeep';
import { createRef, Component } from 'react';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';
import AnnotationItem from '../lib/AnnotationItem';
import AnnotationsOverlayVideo from '../containers/AnnotationsOverlayVideo';
import WindowCanvasNavigationControlsVideo from '../containers/WindowCanvasNavigationControlsVideo';
const StyledContainer = styled('div')(() => ({
alignItems: 'center',
display: 'flex',
width: '100%',
}));
const StyledFlexFill = styled('div')(() => ({
height: '100%',
position: 'relative',
width: '100%',
}));
const StyledVideo = styled('video')(() => ({
maxHeight: '100%',
width: '100%',
}));
/** */
export class VideoViewer extends Component {
/** */
constructor(props) {
super(props);
this.videoRef = createRef();
this.state = {
start: 0,
time: 0,
};
}
/** */
componentDidMount() {
const { setPaused, setHasTextTrack } = this.props;
setPaused(true);
const video = this.videoRef.current;
if (video && video.textTracks.length > 0) setHasTextTrack(true);
}
/** */
componentDidUpdate(prevProps) {
const {
canvas, currentTime, muted, paused,
setCurrentTime, setPaused,
textTrackDisabled,
} = this.props;
if (paused !== prevProps.paused) {
if (currentTime === 0) {
this.timerReset();
}
if (paused) {
this.timerStop();
} else {
this.timerStart();
}
}
if (currentTime !== prevProps.currentTime) {
const duration = canvas.getDuration();
if (duration && duration < currentTime) {
if (!paused) {
setPaused(true);
setCurrentTime(0);
this.timerReset();
}
}
}
const video = this.videoRef.current;
if (video) {
if (video.muted !== muted) {
video.muted = muted;
}
if (video.textTracks && video.textTracks.length > 0) {
const newMode = textTrackDisabled ? 'disabled' : 'showing';
if (video.textTracks[0].mode !== newMode) {
video.textTracks[0].mode = newMode;
}
}
}
}
/** */
componentWillUnmount() {
this.timerStop();
}
/** */
timerStart() {
const { currentTime } = this.props;
this.setState({
start: Date.now() - currentTime * 1000,
time: currentTime * 1000,
});
this.timer = setInterval(() => {
const { setCurrentTime } = this.props;
this.setState(prevState => ({
time: Date.now() - prevState.start,
}));
const { time } = this.state;
setCurrentTime(time / 1000);
}, 100);
}
/** */
timerStop() {
clearInterval(this.timer);
}
/** */
timerReset() {
this.setState({ time: 0 });
}
/* eslint-disable jsx-a11y/media-has-caption */
/** */
render() {
const {
annotations, canvas, currentTime, videoOptions, windowId,
} = this.props;
const videoResources = flatten(
flattenDeep([
canvas.getContent().map(annot => {
const annotaion = new AnnotationItem(annot.__jsonld);
const temporalfragment = annotaion.temporalfragmentSelector;
if (temporalfragment && temporalfragment.length > 0) {
const start = temporalfragment[0] || 0;
const end = (temporalfragment.length > 1) ? temporalfragment[1] : Number.MAX_VALUE;
if (start <= currentTime && currentTime < end) {
//
} else {
return {};
}
}
const body = annot.getBody();
return { body, temporalfragment };
}),
]).filter((resource) => resource.body && resource.body[0].__jsonld && resource.body[0].__jsonld.type === 'Video'),
);
const vttContent = annotations
.flatMap(annoPage => annoPage.json.items.map(anno => anno.body))
.flat().filter((body) => body.format === 'text/vtt');
// Only one video can be displayed at a time in this implementation.
const len = videoResources.length;
const video = len > 0
? videoResources[len - 1].body[0] : null;
const videoTargetTemporalfragment = len > 0
? videoResources[len - 1].temporalfragment : [];
return (
<StyledContainer>
<StyledFlexFill>
{ video && (
<>
<StyledVideo key={video.id} ref={this.videoRef} {...videoOptions}>
<source src={video.id} type={video.getFormat()} />
{ vttContent.map(vttc => (<track key={vttc.id} src={vttc.id} srcLang={vttc.language} />)) }
</StyledVideo>
<AnnotationsOverlayVideo windowId={windowId} videoRef={this.videoRef} videoTarget={videoTargetTemporalfragment} key={`${windowId} ${video.id}`} />
</>
)}
<WindowCanvasNavigationControlsVideo windowId={windowId} />
</StyledFlexFill>
</StyledContainer>
);
}
/* eslint-enable jsx-a11y/media-has-caption */
}
VideoViewer.propTypes = {
annotations: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
// eslint-disable-next-line react/forbid-prop-types
canvas: PropTypes.object,
captions: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
currentTime: PropTypes.number,
muted: PropTypes.bool,
paused: PropTypes.bool,
setCurrentTime: PropTypes.func,
setHasTextTrack: PropTypes.func,
setPaused: PropTypes.func,
textTrackDisabled: PropTypes.bool,
videoOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types
windowId: PropTypes.string.isRequired,
};
VideoViewer.defaultProps = {
annotations: [],
canvas: {},
captions: [],
currentTime: 0,
muted: false,
paused: true,
setCurrentTime: () => {},
setHasTextTrack: () => {},
setPaused: () => {},
textTrackDisabled: true,
videoOptions: {},
};