Skip to content
Snippets Groups Projects
Select Git revision
  • fd236dcb8581ca647b90793e29578fbdd2709bc0
  • mui5-annotation-on-video-stable default
  • get_setter_canvasSizeInformations
  • fix-error-div-into-p
  • annotation-on-video-v2
  • detached
  • annotation-on-video-r17
  • mui5
  • mui5-react-18
  • jacob-test
  • annotation-on-video protected
  • master
  • test-antoinev1
  • 20-fetch-thumbnail-on-annotation
  • add-research-field
  • Save
  • add-plugin
  • 14-wip-no-seek-to
  • 14-bug-on-video-time-control
  • 9_wip_videotests
  • _upgrade_material_ui
  • latest-tetras-16
  • v3.3.0
  • v3.2.0
  • v3.1.1
  • v3.1.0
  • v3.0.0
  • v3.0.0-rc.7
  • v3.0.0-rc.6
  • v3.0.0-rc.5
  • v3.0.0-rc.4
  • v3.0.0-rc.3
  • v3.0.0-rc.2
  • v3.0.0-rc.1
  • v3.0.0-beta.10
  • v3.0.0-beta.9
  • v3.0.0-beta.8
  • v3.0.0-beta.7
  • v3.0.0-beta.6
  • v3.0.0-beta.5
  • v3.0.0-beta.3
41 results

VideoViewer.js

Blame
  • 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: {},
    };