From 848664ae29dd70a74ba6e247a29dc6337dab3769 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 11:25:49 +0200 Subject: [PATCH 01/14] Udpadte dev port --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 6e3c1e838f..b19c4887d1 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -71,7 +71,7 @@ module.exports = (env, options) => { ...config, devServer: { hot: true, - port: 4444, + port: 4445, static: [ './__tests__/integration/mirador', './__tests__/fixtures', From 2c4706f2890a61cebe77b24ef60d2a2cca3ed984 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 11:26:02 +0200 Subject: [PATCH 02/14] Update html demo --- __tests__/integration/mirador/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/integration/mirador/index.html b/__tests__/integration/mirador/index.html index 61a091979c..a425dd172c 100644 --- a/__tests__/integration/mirador/index.html +++ b/__tests__/integration/mirador/index.html @@ -4,7 +4,7 @@ - Mirador + Mirador Video
From 316365576201536b62ab6144f6d46543ad04fab8 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 11:26:24 +0200 Subject: [PATCH 03/14] WIp fixing origin --- src/components/AnnotationsOverlayVideo.js | 6 +++--- src/components/VideoViewer.js | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/AnnotationsOverlayVideo.js b/src/components/AnnotationsOverlayVideo.js index 49309ab44e..5e2c1dab2c 100755 --- a/src/components/AnnotationsOverlayVideo.js +++ b/src/components/AnnotationsOverlayVideo.js @@ -626,9 +626,9 @@ export class AnnotationsOverlayVideo extends Component { const circularProgress = (); return ( <> - - - { showProgress && circularProgress } + + + { showProgress && circularProgress } ); } diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index ba833da037..e3bd6495b7 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -16,6 +16,7 @@ const StyledContainer = styled('div')(() => ({ const StyledFlexFill = styled('div')(() => ({ height: '100%', + position: 'relative', display: 'flex', justifyContent:'center' @@ -162,13 +163,13 @@ export class VideoViewer extends Component { { video && ( - <> +
{ vttContent.map(vttc => ()) } - - + +
)}
From 8aa8ca705e57564f72d2537fdac312e876b6cacf Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 13:14:39 +0200 Subject: [PATCH 04/14] Add debug border --- src/components/VideoViewer.js | 379 ++++++++++++++++++---------------- 1 file changed, 199 insertions(+), 180 deletions(-) diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index e3bd6495b7..cfefbdcc16 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -1,210 +1,229 @@ import flatten from 'lodash/flatten'; import flattenDeep from 'lodash/flattenDeep'; -import { createRef, Component } from 'react'; +import {createRef, Component} from 'react'; import PropTypes from 'prop-types'; -import { styled } from '@mui/material/styles'; +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%', - justifyContent:'center' + alignItems: 'center', + display: 'flex', + width: '100%', + justifyContent: 'center', + border: '3px solid blue', })); const StyledFlexFill = styled('div')(() => ({ - height: '100%', + height: '100%', - position: 'relative', - display: 'flex', - justifyContent:'center' + position: 'relative', + display: 'flex', + justifyContent: 'center', + border: '3px solid red' })); const StyledVideo = styled('video')(() => ({ - maxHeight: '100%', + maxHeight: '100%', + backgroundColor: '3px solid black', })); +const StyledDiv = styled('div')(() => ({ + border: '3px solid yellow', +})); + + /** */ 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(); - } + /** */ + constructor(props) { + super(props); + this.videoRef = createRef(); + + this.state = { + start: 0, + time: 0, + }; } - 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; - } - } + + /** */ + componentDidMount() { + const {setPaused, setHasTextTrack} = this.props; + setPaused(true); + + const video = this.videoRef.current; + if (video && video.textTracks.length > 0) setHasTextTrack(true); } - } - - /** */ - 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) { - // + + /** */ + 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 { - return {}; + 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; } - } - 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 ( - - - { video && ( -
- - - { vttContent.map(vttc => ()) } - - -
- )} - -
-
- ); - } - /* eslint-enable jsx-a11y/media-has-caption */ + 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 ( + + + {video && ( + <> + + + + + + {vttContent.map(vttc => ( + ))} + + + + + + + )} + + ); + } + + /* 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, + 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: {}, + annotations: [], + canvas: {}, + captions: [], + currentTime: 0, + muted: false, + paused: true, + setCurrentTime: () => { + }, + setHasTextTrack: () => { + }, + setPaused: () => { + }, + textTrackDisabled: true, + videoOptions: {}, }; From 93f22a621b5d6e07111a1d05f43a755591240ad3 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 13:20:00 +0200 Subject: [PATCH 05/14] Some progress --- src/components/VideoViewer.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index cfefbdcc16..4d9de5ea48 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -16,12 +16,13 @@ const StyledContainer = styled('div')(() => ({ })); const StyledFlexFill = styled('div')(() => ({ - height: '100%', - + maxHeight: 'calc(100vh - 120px)', + top: '-60px', position: 'relative', display: 'flex', justifyContent: 'center', - border: '3px solid red' + border: '3px solid red', + maxWidth: '100%', })); const StyledVideo = styled('video')(() => ({ @@ -31,6 +32,8 @@ const StyledVideo = styled('video')(() => ({ const StyledDiv = styled('div')(() => ({ border: '3px solid yellow', + maxHeight: '100%', + maxWidth: '100%', })); From 5aabfca8f9c7f0bf056e6b74864d686b2897776c Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 14:11:40 +0200 Subject: [PATCH 06/14] Reduce problem size, simplify code --- __tests__/integration/mirador/index.html | 9 ++--- src/components/VideoViewer.js | 44 +++++++++--------------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/__tests__/integration/mirador/index.html b/__tests__/integration/mirador/index.html index a425dd172c..5fb2167bb4 100644 --- a/__tests__/integration/mirador/index.html +++ b/__tests__/integration/mirador/index.html @@ -16,14 +16,11 @@ transitions: window.location.port === '4488' ? { create: () => 'none' } : {}, }, catalog: [ - { manifestId: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/e32a277e-91e2-4a6d-8ba6-cc4bad230410.json' }, - { manifestId: 'https://iiif.harvardartmuseums.org/manifests/object/299843' }, +/* { manifestId: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/e32a277e-91e2-4a6d-8ba6-cc4bad230410.json' }, + { manifestId: 'https://iiif.harvardartmuseums.org/manifests/object/299843' },*/ + { manifestId: "https://files.tetras-libre.fr/dev/vertical_video_with_annot.json"}, { manifestId: "https://files.tetras-libre.fr/dev/cats_video_with_annot.json"}, - { manifestId: "https://files.tetras-libre.fr/dev/test-vertical-video-800H.json"}, { manifestId: "https://files.tetras-libre.fr/dev/Heterogeneous-media-on-several-canvases.json"}, - { manifestId: "https://files.tetras-libre.fr/dev/vertical_video_with_annot.json"}, - - ] }); diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index 4d9de5ea48..3cc0b16607 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -15,25 +15,17 @@ const StyledContainer = styled('div')(() => ({ border: '3px solid blue', })); -const StyledFlexFill = styled('div')(() => ({ - maxHeight: 'calc(100vh - 120px)', - top: '-60px', - position: 'relative', - display: 'flex', - justifyContent: 'center', - border: '3px solid red', - maxWidth: '100%', -})); - const StyledVideo = styled('video')(() => ({ maxHeight: '100%', - backgroundColor: '3px solid black', + backgroundColor: '8px solid pink', })); -const StyledDiv = styled('div')(() => ({ - border: '3px solid yellow', - maxHeight: '100%', +const StyledVideoContent = styled('div')(() => ({ + border: '3px solid red', + maxHeight: 'calc(100vh - 120px)', maxWidth: '100%', + top: '-60px', + position: 'relative', })); @@ -172,22 +164,18 @@ export class VideoViewer extends Component { ? videoResources[len - 1].temporalfragment : []; return ( - {video && ( <> - - - - - - {vttContent.map(vttc => ( - ))} - - - - + + + + {vttContent.map(vttc => ( + ))} + + + )} From 3a30ffc673a79c1facbd5a7cbb1dca17550fecd1 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 14:39:38 +0200 Subject: [PATCH 07/14] Reduce problem size, simplify code --- src/components/VideoViewer.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index 3cc0b16607..def5138648 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -22,9 +22,8 @@ const StyledVideo = styled('video')(() => ({ const StyledVideoContent = styled('div')(() => ({ border: '3px solid red', - maxHeight: 'calc(100vh - 120px)', + maxHeight: 'calc(100% - 160px)', maxWidth: '100%', - top: '-60px', position: 'relative', })); @@ -163,18 +162,19 @@ export class VideoViewer extends Component { const videoTargetTemporalfragment = len > 0 ? videoResources[len - 1].temporalfragment : []; return ( - + {/*blue border*/} {video && ( <> - - + {/*red border*/} + {/*pink border*/} {vttContent.map(vttc => ( ))} + key={`${windowId} ${video.id}`} + style={{height: '100%', border: '3px solid orange'}}/> From e0732499fc4f53f4a67bd701246693d70512cb05 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 14:47:00 +0200 Subject: [PATCH 08/14] WIP --- src/components/VideoViewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index def5138648..d3b20c31f8 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -17,7 +17,7 @@ const StyledContainer = styled('div')(() => ({ const StyledVideo = styled('video')(() => ({ maxHeight: '100%', - backgroundColor: '8px solid pink', + border: '8px solid pink', })); const StyledVideoContent = styled('div')(() => ({ From f2e9205b234dc4d10ad49586015384c57ca7b751 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Wed, 9 Oct 2024 15:02:13 +0200 Subject: [PATCH 09/14] WIP Positionning --- src/components/AnnotationsOverlayVideo.js | 2 +- src/components/VideoViewer.js | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/AnnotationsOverlayVideo.js b/src/components/AnnotationsOverlayVideo.js index 5e2c1dab2c..ac829a8bff 100755 --- a/src/components/AnnotationsOverlayVideo.js +++ b/src/components/AnnotationsOverlayVideo.js @@ -626,7 +626,7 @@ export class AnnotationsOverlayVideo extends Component { const circularProgress = (); return ( <> - + { showProgress && circularProgress } diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index d3b20c31f8..d1c914ed1d 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -9,22 +9,24 @@ import WindowCanvasNavigationControlsVideo from '../containers/WindowCanvasNavig const StyledContainer = styled('div')(() => ({ alignItems: 'center', - display: 'flex', width: '100%', - justifyContent: 'center', - border: '3px solid blue', + border: '6px solid blue', + display: 'flex', })); const StyledVideo = styled('video')(() => ({ maxHeight: '100%', - border: '8px solid pink', + border: '6px solid pink', })); const StyledVideoContent = styled('div')(() => ({ - border: '3px solid red', - maxHeight: 'calc(100% - 160px)', + border: '6px solid red', maxWidth: '100%', + textAlign: 'center', + display: 'block', + maxHeight: '100%', position: 'relative', + })); @@ -174,7 +176,7 @@ export class VideoViewer extends Component { + style={{height: '100%', border: '6px solid orange'}}/> From 684ae8472af5ce249db6ecba48bbd53ededfed29 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Tue, 12 Nov 2024 15:07:41 +0100 Subject: [PATCH 10/14] WIP Positionning --- __tests__/integration/mirador/index.html | 2 + src/components/AnnotationsOverlayVideo.js | 20 +- src/components/VideoViewer.js | 420 ++++++++++++---------- 3 files changed, 249 insertions(+), 193 deletions(-) diff --git a/__tests__/integration/mirador/index.html b/__tests__/integration/mirador/index.html index 5fb2167bb4..d195ef2e18 100644 --- a/__tests__/integration/mirador/index.html +++ b/__tests__/integration/mirador/index.html @@ -21,6 +21,8 @@ { manifestId: "https://files.tetras-libre.fr/dev/vertical_video_with_annot.json"}, { manifestId: "https://files.tetras-libre.fr/dev/cats_video_with_annot.json"}, { manifestId: "https://files.tetras-libre.fr/dev/Heterogeneous-media-on-several-canvases.json"}, + { manifestId: "https://dzkimgs.l.u-tokyo.ac.jp/videos/cat2020/manifest.json"}, + { manifestId: "https://files.tetras-libre.fr/dev/vertical_video_with_annot.json"} ] }); diff --git a/src/components/AnnotationsOverlayVideo.js b/src/components/AnnotationsOverlayVideo.js index ac829a8bff..68504c1225 100755 --- a/src/components/AnnotationsOverlayVideo.js +++ b/src/components/AnnotationsOverlayVideo.js @@ -11,6 +11,7 @@ import CanvasOverlayVideo from '../lib/CanvasOverlayVideo'; import CanvasWorld from '../lib/CanvasWorld'; import CanvasAnnotationDisplay from '../lib/CanvasAnnotationDisplay'; import { VideosReferences } from '../plugins/VideosReferences'; +import {ORIENTATIONS} from './VideoViewer'; /** AnnotationsOverlayVideo - based on AnnotationsOverlay */ export class AnnotationsOverlayVideo extends Component { @@ -83,6 +84,8 @@ export class AnnotationsOverlayVideo extends Component { this.imagesLoading = []; this.imagesReady = []; + this.currentOrientation = props.currentOrientation; + const { videoTarget: temporalfragment } = this.props; if (temporalfragment && temporalfragment.length > 0) { this.temporalOffset = temporalfragment[0] || 0; @@ -626,9 +629,19 @@ export class AnnotationsOverlayVideo extends Component { const circularProgress = (); return ( <> - - - { showProgress && circularProgress } + + + { showProgress && circularProgress } ); } @@ -659,6 +672,7 @@ AnnotationsOverlayVideo.propTypes = { annotations: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types canvas: PropTypes.object, // eslint-disable-line react/forbid-prop-types canvasWorld: PropTypes.instanceOf(CanvasWorld).isRequired, + currentOrientation: PropTypes.string, currentTime: PropTypes.number, deselectAnnotation: PropTypes.func, drawAnnotations: PropTypes.bool, diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index d1c914ed1d..ab4f11e35c 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -1,222 +1,262 @@ import flatten from 'lodash/flatten'; import flattenDeep from 'lodash/flattenDeep'; -import {createRef, Component} from 'react'; +import React, { 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', - width: '100%', - border: '6px solid blue', - display: 'flex', -})); +export const ORIENTATIONS = { + LANDSCAPE: 'landscape', + PORTRAIT: 'portrait', +}; -const StyledVideo = styled('video')(() => ({ - maxHeight: '100%', - border: '6px solid pink', -})); +/** */ +export class VideoViewer extends Component { + /** */ + constructor(props) { + super(props); + this.videoRef = createRef(); -const StyledVideoContent = styled('div')(() => ({ - border: '6px solid red', - maxWidth: '100%', - textAlign: 'center', - display: 'block', - maxHeight: '100%', - position: 'relative', + 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); + } -/** */ -export class VideoViewer extends Component { - /** */ - constructor(props) { - super(props); - this.videoRef = createRef(); - - this.state = { - start: 0, - time: 0, - }; + /** */ + 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; + } + } + } + } - /** */ - componentDidMount() { - const {setPaused, setHasTextTrack} = this.props; - setPaused(true); + /** */ + componentWillUnmount() { + this.timerStop(); + } - const video = this.videoRef.current; - if (video && video.textTracks.length > 0) setHasTextTrack(true); - } + /** */ + 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); + } - /** */ - 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(); + /** */ + 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 { - 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; - } + return {}; } - } - } + } + const body = annot.getBody(); + return { body, temporalfragment }; + }), + ]).filter((resource) => resource.body && resource.body[0].__jsonld && resource.body[0].__jsonld.type === 'Video'), + ); - /** */ - componentWillUnmount() { - this.timerStop(); - } + const vttContent = annotations + .flatMap(annoPage => annoPage.json.items.map(anno => anno.body)) + .flat().filter((body) => body.format === 'text/vtt'); - /** */ - 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); - } + // 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 : []; - /** */ - timerStop() { - clearInterval(this.timer); - } + let currentOrientation; - /** */ - timerReset() { - this.setState({time: 0}); + if (video) { + console.log('video', video); + currentOrientation = video.getWidth() > video.getHeight() ? ORIENTATIONS.LANDSCAPE : ORIENTATIONS.PORTRAIT; } - /* 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 ( - {/*blue border*/} - {video && ( - <> - {/*red border*/} - {/*pink border*/} - - {vttContent.map(vttc => ( - ))} - - - - - - )} - - ); - } + return ( +
+ {video && ( + <> +
+ {/* red border */} +
+ + + +
+
+ + + )} +
+ ); + } - /* eslint-enable jsx-a11y/media-has-caption */ + /* 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, + 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: {}, + annotations: [], + canvas: {}, + captions: [], + currentTime: 0, + muted: false, + paused: true, + setCurrentTime: () => { + }, + setHasTextTrack: () => { + }, + setPaused: () => { + }, + textTrackDisabled: true, + videoOptions: {}, }; From 234471d626e17f477b9de0bfcffbec13041ebaf0 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Tue, 12 Nov 2024 19:32:02 +0100 Subject: [PATCH 11/14] Positionning seem to be ok in landscape and portrait video --- src/components/AnnotationsOverlayVideo.js | 4 ++-- src/components/VideoViewer.js | 27 +++++++++++------------ src/lib/CanvasAnnotationDisplay.js | 5 +++++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/components/AnnotationsOverlayVideo.js b/src/components/AnnotationsOverlayVideo.js index 68504c1225..72639f8ca4 100755 --- a/src/components/AnnotationsOverlayVideo.js +++ b/src/components/AnnotationsOverlayVideo.js @@ -633,11 +633,11 @@ export class AnnotationsOverlayVideo extends Component { ref={this.ref} style={{ left: 0, - position: (this.currentOrientation === ORIENTATIONS.PORTRAIT ? 'absolute' : 'block'), + position: 'relative', top: 0, border: '5px solid black', width: (this.currentOrientation === ORIENTATIONS.LANDSCAPE ? '100%' : 'auto'), - height: (this.currentOrientation === ORIENTATIONS.PORTRAIT ? '100%' : 'auto'), + // transform: "translate(50%)", }} /> diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index ab4f11e35c..7a71e397fd 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -152,6 +152,8 @@ export class VideoViewer extends Component { currentOrientation = video.getWidth() > video.getHeight() ? ORIENTATIONS.LANDSCAPE : ORIENTATIONS.PORTRAIT; } + console.log('currentOrientation', currentOrientation); + return (
{video && ( @@ -170,30 +172,26 @@ export class VideoViewer extends Component { border: '6px solid red', position: 'relative', width: '100%', - height: '100%', display: 'flex', - alignItems: 'flexEnd', + alignItems: 'center', + marginBottom: '170px', // TODO Space for navigation controls + flexDirection: "column", }} > - {/* red border */}
diff --git a/src/lib/CanvasAnnotationDisplay.js b/src/lib/CanvasAnnotationDisplay.js index 673e492f44..22e15d3f1a 100644 --- a/src/lib/CanvasAnnotationDisplay.js +++ b/src/lib/CanvasAnnotationDisplay.js @@ -15,6 +15,8 @@ export default class CanvasAnnotationDisplay { this.hovered = hovered; this.imageSource = imageSource; this.canvasSize = canvasSize; + + this.palette.selected.strokeStyle = 'red'; // TODO remove when finished } /** */ @@ -44,6 +46,9 @@ export default class CanvasAnnotationDisplay { currentPalette = this.palette.default; } + console.log('currentPalette', currentPalette); + + if (currentPalette.globalAlpha === 0) return; [...this.svgPaths].forEach((element) => { From 06c6c2a5f7e785ef1b7cd3602a189838b8069968 Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Tue, 12 Nov 2024 20:12:57 +0100 Subject: [PATCH 12/14] Improve positionning Need to improve landscape resiing when video view area are not large --- src/components/AnnotationsOverlayVideo.js | 6 ++-- src/components/VideoViewer.js | 38 ++++++++++++++--------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/components/AnnotationsOverlayVideo.js b/src/components/AnnotationsOverlayVideo.js index 72639f8ca4..eac9bc9291 100755 --- a/src/components/AnnotationsOverlayVideo.js +++ b/src/components/AnnotationsOverlayVideo.js @@ -621,11 +621,13 @@ export class AnnotationsOverlayVideo extends Component { } } + /** * Renders things */ render() { const { showProgress } = this.state; + const debugPositionning = false; const circularProgress = (); return ( <> @@ -635,8 +637,8 @@ export class AnnotationsOverlayVideo extends Component { left: 0, position: 'relative', top: 0, - border: '5px solid black', - width: (this.currentOrientation === ORIENTATIONS.LANDSCAPE ? '100%' : 'auto'), + border: debugPositionning ? '6px solid yellow' : 'none', + width: '100%', // transform: "translate(50%)", }} /> diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index 7a71e397fd..3ef5772de4 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -107,6 +107,8 @@ export class VideoViewer extends Component { this.setState({ time: 0 }); } + + /* eslint-disable jsx-a11y/media-has-caption */ /** */ render() { @@ -152,13 +154,13 @@ export class VideoViewer extends Component { currentOrientation = video.getWidth() > video.getHeight() ? ORIENTATIONS.LANDSCAPE : ORIENTATIONS.PORTRAIT; } - console.log('currentOrientation', currentOrientation); + const debugPositionning = true; return (
From 321a86b9a0732f999670e94c3f2fe00cd631d13b Mon Sep 17 00:00:00 2001 From: Anthony Geourjon Date: Tue, 12 Nov 2024 21:54:45 +0100 Subject: [PATCH 13/14] WIP --- src/components/AnnotationsOverlayVideo.js | 3 ++- src/components/VideoViewer.js | 13 ++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/AnnotationsOverlayVideo.js b/src/components/AnnotationsOverlayVideo.js index eac9bc9291..f2535820a8 100755 --- a/src/components/AnnotationsOverlayVideo.js +++ b/src/components/AnnotationsOverlayVideo.js @@ -638,7 +638,8 @@ export class AnnotationsOverlayVideo extends Component { position: 'relative', top: 0, border: debugPositionning ? '6px solid yellow' : 'none', - width: '100%', + width: (this.currentOrientation === ORIENTATIONS.LANDSCAPE ? '100%' : 'auto'), + // transform: "translate(50%)", }} /> diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index 3ef5772de4..16e5df0fb6 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -154,7 +154,7 @@ export class VideoViewer extends Component { currentOrientation = video.getWidth() > video.getHeight() ? ORIENTATIONS.LANDSCAPE : ORIENTATIONS.PORTRAIT; } - const debugPositionning = true; + const debugPositionning = false; return (