From 3ebd23112c6268e99a1483adf15be6bf00889e24 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 20 Aug 2021 13:33:47 +0200 Subject: [PATCH 1/2] Carga inicial --- src/components/external-video-player/index.js | 318 ++++++++++++++++++ .../external-video-player/styles.css | 22 ++ src/components/loader/data/index.scss | 3 +- src/components/loader/data/item.js | 8 +- src/components/loader/index.js | 25 +- src/components/loader/index.scss | 2 +- src/components/player.js | 72 +++- src/components/thumbnails/image.js | 14 +- src/components/thumbnails/index.scss | 5 + src/config.json | 6 +- src/locales/messages/es.json | 3 +- src/utils/builder.js | 61 ++-- src/utils/data/index.js | 16 +- src/utils/synchronizer.js | 30 +- 14 files changed, 526 insertions(+), 59 deletions(-) create mode 100644 src/components/external-video-player/index.js create mode 100644 src/components/external-video-player/styles.css diff --git a/src/components/external-video-player/index.js b/src/components/external-video-player/index.js new file mode 100644 index 00000000..960c14e8 --- /dev/null +++ b/src/components/external-video-player/index.js @@ -0,0 +1,318 @@ +import React, { Component } from 'react'; +import ReactPlayer from 'react-player'; +import cx from 'classnames'; +import { defineMessages } from 'react-intl'; +import logger from 'utils/logger'; +import { ID } from 'utils/constants'; +import { getCurrentDataIndex } from 'utils/data'; + +import './styles.css'; + + +const intlMessages = defineMessages({ + autoPlayWarning: { + id: 'player.externalVideo.autoPlayWarning', + description: 'Shown when user needs to interact with player to make it work', + }, + +}); + + +const SYNC_INTERVAL_SECOND = 5; +const AUTO_PLAY_BLOCK_DETECTION_TIMEOUT_SECONDS = 5; +const ORCHESTRATOR_INTERVAL_MILLISECOND = 500; + +class ExternalVideoPlayer extends Component { + + constructor(props) { + super(props); + + this.player = null; + + this.autoPlayTimeout = null; + + this.hasPlayedBefore = false; + this.playerIsReady = false; + + this.time = 0; + this.buffering= false; + this.lastTime = 0; + this.playerUpdateTime = -1; + this.primaryPlayerPlaying = false; + this.lastEventPlaybackRate = 1; + + this.state = { + muted: false, + playing: false, + autoPlayBlocked: false, + errorPlaying: false, + playbackRate: 1, + volume: 1, + }; + + this.opts = { + // default option for all players, can be overwritten + playerOptions: { + autoplay: false, + playsinline: true, + controls: false, + }, + file: { + attributes: { + controls: false, + autoPlay: false, + playsInline: true, + }, + }, + youtube: { + playerVars: { + autoplay: 0, + modestbranding: 1, + autohide: 1, + rel: 0, + ecver: 2, + controls: 0, + enablejsapi: 0, + showinfo: 0 + }, + }, + + preload: true, + }; + + + this.getCurrentTime = this.getCurrentTime.bind(this); + this.setPlaybackRate = this.setPlaybackRate.bind(this); + this.seekTo = this.seekTo.bind(this); + + this.handleFirstPlay = this.handleFirstPlay.bind(this); + this.handleOnReady = this.handleOnReady.bind(this); + this.handleOnPlay = this.handleOnPlay.bind(this); + this.handleOnPause = this.handleOnPause.bind(this); + this.handleVolumeChange = this.handleVolumeChange.bind(this); + this.handleOnBuffer = this.handleOnBuffer.bind(this); + this.handleOnBufferEnd = this.handleOnBufferEnd.bind(this); + + this.orchestrator = this.orchestrator.bind(this); + this.autoPlayBlockDetected = this.autoPlayBlockDetected.bind(this); + + } + + autoPlayBlockDetected() { + this.setState({ autoPlayBlocked: true }); + } + + handleFirstPlay() { + const { hasPlayedBefore } = this; + + if (!hasPlayedBefore) { + this.hasPlayedBefore = true; + + this.setState({ autoPlayBlocked: false }); + + if (this.autoPlayTimeout) { + clearTimeout(this.autoPlayTimeout); + } + + } + } + + getCurrentTime() { + if (this.player && this.player.getCurrentTime) { + return Math.round(this.player.getCurrentTime()); + } + } + + + setPlaybackRate() { + + const { primaryPlaybackRate } = this.props; + + // Rate depends on primary rate player + const rate = primaryPlaybackRate * this.lastEventPlaybackRate; + + const currentRate = this.state.playbackRate; + + logger.debug(`external_video: setPlaybackRate current=${currentRate} primary=${primaryPlaybackRate} lastEventPlaybackRate=${this.lastEventPlaybackRate} rate=${rate}`); + + if (currentRate === rate) { + return; + } + + this.setState({ playbackRate: rate }); + + } + + handleOnReady() { + const { hasPlayedBefore, playerIsReady } = this; + + if (hasPlayedBefore || playerIsReady) { + return; + } + + this.playerIsReady = true; + this.handleFirstPlay(); + + const { onPlayerReady } = this.props; + + if (onPlayerReady) onPlayerReady(ID.EXTERNAL_VIDEOS, this); + + + } + + handleOnPlay() { + const { playing } = this.state; + + if (!playing && this.primaryPlayerPlaying) { + this.setState({ playing: true }); + this.handleFirstPlay(); + } + } + + handleOnPause() { + const { playing } = this.state; + + if (playing) { + this.setState({ playing: false }); + this.handleFirstPlay(); + } + } + + handleOnBuffer() { + this.buffering = true; + } + + handleOnBufferEnd() { + this.buffering = false; + } + + handleVolumeChange = (value, isMuted) => { + this.setState({ volume: parseFloat(value)}); + this.setState({ muted: isMuted}); + } + + + seekTo(time) { + const { player } = this; + + if (!player) { + //return logger.error('No player on seek'); + return; + } + + // Seek if viewer has drifted too far away from presenter + if (Math.abs(this.getCurrentTime() - time) > SYNC_INTERVAL_SECOND * 0.75) { + player.seekTo(time, true); + } + } + + componentDidMount () { + this.timer = setInterval(() => this.orchestrator(), ORCHESTRATOR_INTERVAL_MILLISECOND); + } + + componentWillUnmount () { + clearInterval(this.timer); + } + + orchestrator () { + const { events, active, primaryPlaybackRate } = this.props; + const { playing, playbackRate } = this.state; + + let primaryPlayerPlaying = true; + + if (this.time === this.lastTime) { + primaryPlayerPlaying = false; + } + + this.lastTime = this.time; + this.primaryPlayerPlaying = primaryPlayerPlaying; + + if (active && !this.hasPlayedBefore && !this.autoPlayTimeout) { + this.autoPlayTimeout = setTimeout(this.autoPlayBlockDetected, AUTO_PLAY_BLOCK_DETECTION_TIMEOUT_SECONDS * 1000); + } + + const index = getCurrentDataIndex(events, this.time); + + logger.debug(`external_video: player time=${this.time} active=${active} Playing=${playing} primaryPlayerPlaying=${primaryPlayerPlaying} PlaybackRate=${playbackRate}`); + + + if (!primaryPlayerPlaying || !active) { + this.handleOnPause(); + this.playerUpdateTime = -1; + return + } + + if (index && events && events[index] && events[index].type) + { + const {type, time, rate, playing} = events[index]; + + logger.debug(`External Video Event: type=${type} time=${time} rate=${rate} playing=${playing}`); + + switch (type) { + case "stop": + this.handleOnPause(); + break; + case "play": + this.handleOnPlay(); + break; + case "playerUpdate": + if (this.playerUpdateTime !== time) { + this.lastEventPlaybackRate=rate; + this.seekTo(time); + playing ? this.handleOnPlay() : this.handleOnPause() + this.playerUpdateTime=time; + } + break; + default: + ; + } + } + + this.setPlaybackRate(); + } + + + render() { + + const { videoUrl, active, intl } = this.props; + const { playing, playbackRate, muted, autoPlayBlocked, volume } = this.state; + + return ( + +
{ this.playerParent = ref; }} + > + {autoPlayBlocked + ? ( +

+ {intl.formatMessage(intlMessages.autoPlayWarning)} +

+ ) + : '' + } + + { this.player = ref; }} + width="100%" + height="100%" + /> + +
+ ); + + } +} + +export default (ExternalVideoPlayer); diff --git a/src/components/external-video-player/styles.css b/src/components/external-video-player/styles.css new file mode 100644 index 00000000..ae59a053 --- /dev/null +++ b/src/components/external-video-player/styles.css @@ -0,0 +1,22 @@ +.autoPlayWarning { + position: absolute; + z-index: 100; + font-size: x-large; + color: white; + width: 100%; + background-color: rgba(6,23,42,0.5); + bottom: 20%; + vertical-align: middle; + text-align: center; + pointer-events: none; +} + +.externalVideos-wrapper { + display: flex; + position: absolute; + height: 100%; + position: absolute; + width: 95%; + left: 2.5%; +} + diff --git a/src/components/loader/data/index.scss b/src/components/loader/data/index.scss index a2b7e963..cbc59902 100644 --- a/src/components/loader/data/index.scss +++ b/src/components/loader/data/index.scss @@ -12,9 +12,10 @@ box-sizing: border-box; color: var(--loader-color); display: flex; + font-size: xx-large; font-weight: var(--font-weight-semi-bold); padding: $padding; - opacity: .25; + opacity: .5; } .loaded { diff --git a/src/components/loader/data/item.js b/src/components/loader/data/item.js index 45460325..036c26d5 100644 --- a/src/components/loader/data/item.js +++ b/src/components/loader/data/item.js @@ -1,11 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { files } from 'config'; import './index.scss'; -const TRANSITION = ((files.feedback.timeout / 1000) / 2).toFixed(2); - const propTypes = { icon: PropTypes.string, value: PropTypes.oneOfType([ @@ -25,10 +22,7 @@ const Item = ({ }) => { return ( -
+
); diff --git a/src/components/loader/index.js b/src/components/loader/index.js index 104ec293..dcd02757 100644 --- a/src/components/loader/index.js +++ b/src/components/loader/index.js @@ -3,11 +3,8 @@ import { defineMessages, useIntl, } from 'react-intl'; -import { - files, - medias, -} from 'config'; -import Data from './data'; +import config from 'config'; +//import Data from './data'; import Dots from './dots'; import Error from 'components/error'; import Player from 'components/player'; @@ -20,7 +17,7 @@ import { buildFileURL, getFileName, getFileType, - getLoadedData, +// getLoadedData, } from 'utils/data'; import { getLayout, @@ -48,7 +45,6 @@ const Loader = ({ match }) => { const started = useRef(false); const time = useRef(getTime()); - const [, setBuilt] = useState(0); const [error, setError] = useState(initError(recordId.current)); const [loaded, setLoaded] = useState(false); @@ -85,7 +81,7 @@ const Loader = ({ match }) => { }; const fetchMedia = () => { - const fetches = medias.map(type => { + const fetches = config.medias.map(type => { const url = buildFileURL(recordId.current, `video/webcams.${type}`); return fetch(url, { method: 'HEAD' }); }); @@ -96,7 +92,7 @@ const Loader = ({ match }) => { const { ok, url } = response; if (ok) { logger.debug(ID.LOADER, 'media', response); - media.push(medias.find(type => url.endsWith(type))); + media.push(config.medias.find(type => url.endsWith(type))); } }); @@ -112,10 +108,9 @@ const Loader = ({ match }) => { const update = () => { counter.current = counter.current + 1; - setBuilt(counter.current); // TODO: Better control - if (counter.current > Object.keys(files.data).length) { - if (!loaded) setTimeout(() => setLoaded(true), files.feedback.timeout); + if (counter.current > Object.keys(config.files.data).length) { + if (!loaded) setLoaded(true); } }; @@ -123,8 +118,8 @@ const Loader = ({ match }) => { started.current = true; if (recordId.current) { - for (const file in files.data) { - fetchFile(files.data[file]); + for (const file in config.files.data) { + fetchFile(config.files.data[file]); } fetchMedia(); @@ -155,7 +150,7 @@ const Loader = ({ match }) => {
- {files.feedback.enabled ? : null} + {/**/}
); diff --git a/src/components/loader/index.scss b/src/components/loader/index.scss index b4872d6c..35ecad7e 100644 --- a/src/components/loader/index.scss +++ b/src/components/loader/index.scss @@ -19,7 +19,7 @@ $dots-wrapper-width: 100px; } .loader-bottom { - align-items: flex-end; + align-items: flex-start; justify-content: center; } diff --git a/src/components/player.js b/src/components/player.js index 9bd5479f..98b62b94 100644 --- a/src/components/player.js +++ b/src/components/player.js @@ -6,6 +6,7 @@ import Chat from './chat'; import Notes from './notes'; import Presentation from './presentation'; import Screenshare from './screenshare'; +import ExternalVideoPlayer from './external-video-player'; import Thumbnails from './thumbnails'; import Video from './video'; import BottomBar from './bars/bottom'; @@ -105,13 +106,14 @@ export default class Player extends PureComponent { this.chat = mergeChatContent( getData(data, ID.CHAT), getData(data, ID.POLLS), - getData(data, ID.EXTERNAL_VIDEOS), + //getData(data, ID.EXTERNAL_VIDEOS), ); this.cursor = getData(data, ID.CURSOR); this.metadata = getData(data, ID.METADATA); this.notes = getData(data, ID.NOTES); this.panzooms = getData(data, ID.PANZOOMS); this.screenshare = getData(data, ID.SCREENSHARE); + this.external_videos = getData(data, ID.EXTERNAL_VIDEOS); this.shapes = getData(data, ID.SHAPES); this.canvases = this.shapes.canvases; @@ -131,15 +133,26 @@ export default class Player extends PureComponent { logger.debug(ID.PLAYER, 'ready', ID.SCREENSHARE); this.player.screenshare = player; break; + case ID.EXTERNAL_VIDEOS: + logger.debug(ID.PLAYER, 'ready', ID.EXTERNAL_VIDEOS); + this.player.external_videos = player; + break; default: logger.debug('unhandled', media); } - if (this.player.video && this.player.screenshare) { - this.synchronizer = new Synchronizer(this.player.video, this.player.screenshare); + if (this.external_videos.length === 0) { + if (this.player.video && this.player.screenshare) { + this.synchronizer = new Synchronizer(this.player.video, this.player.screenshare); + } + } else { + if (this.player.video && this.player.screenshare && this.player.external_videos) { + this.synchronizer = new Synchronizer(this.player.video, this.player.screenshare, this.player.external_videos); + } } + } - + handleSearch(value) { const { search } = this.state; @@ -452,11 +465,59 @@ export default class Player extends PureComponent { ); } + renderExternalVideo(active) { + //if (!this.layout.hasScreenshare()) return null; + + const { + intl, + data, + } = this.props; + + const { time } = this.state; + const { external_videos } = data; + + if (!external_videos) { + return; + } + + let currentDataIndex = getCurrentDataIndex(external_videos, time); + + if (currentDataIndex === -1) { + currentDataIndex = 0; + } + + const video = external_videos[currentDataIndex]; + + if (!video) { + return + } + + const url = video.url + const events = video.events; + + let primaryPlaybackRate = 1; + + if (this.player.video) { + primaryPlaybackRate = this.player.video.playbackRate(); + } + + return ( + + ); + } + renderContent() { if (this.layout.isSingle()) return null; const { time } = this.state; - const content = getActiveContent(this.screenshare, time); + const content = getActiveContent(this.screenshare, this.external_videos, time); return (
@@ -464,6 +525,7 @@ export default class Player extends PureComponent {
{this.renderPresentation(content === ID.PRESENTATION)} {this.renderScreenshare(content === ID.SCREENSHARE)} + {this.renderExternalVideo(content === ID.EXTERNAL_VIDEOS)}
{this.renderThumbnails()} diff --git a/src/components/thumbnails/image.js b/src/components/thumbnails/image.js index d3ce04fa..9c20835a 100644 --- a/src/components/thumbnails/image.js +++ b/src/components/thumbnails/image.js @@ -22,7 +22,9 @@ const Image = ({ src, recordId, }) => { - const screenshare = src === ID.SCREENSHARE; + + + const screenshare = src === ID.SCREENSHARE; if (screenshare) { return ( @@ -32,6 +34,16 @@ const Image = ({ ); } + const external_video = src === ID.EXTERNAL_VIDEOS; + + if (external_video) { + return ( +
+ +
+ ); + } + const logo = src.includes('logo'); return ( diff --git a/src/components/thumbnails/index.scss b/src/components/thumbnails/index.scss index c055b27b..7c39949c 100644 --- a/src/components/thumbnails/index.scss +++ b/src/components/thumbnails/index.scss @@ -42,5 +42,10 @@ $thumbnail-height: calc((#{$bottom-content-height} / 10) * 6); font-size: xxx-large; font-weight: var(--font-weight-semi-bold); } + + .external_video { + font-size: xxx-large; + font-weight: var(--font-weight-semi-bold); + } } } diff --git a/src/config.json b/src/config.json index dddce631..2701cfd3 100644 --- a/src/config.json +++ b/src/config.json @@ -21,13 +21,9 @@ "notes": "notes.html", "panzooms": "panzooms.xml", "polls": "polls.json", - "externalVideos": "external_videos.json", + "externalVideos": "external_videos.xml", "screenshare": "deskshare.xml", "shapes": "shapes.svg" - }, - "feedback": { - "enabled": true, - "timeout": 1000 } }, "locale": { diff --git a/src/locales/messages/es.json b/src/locales/messages/es.json index 095f4f9c..d35d9ce5 100644 --- a/src/locales/messages/es.json +++ b/src/locales/messages/es.json @@ -26,5 +26,6 @@ "player.search.modal.title": "Search", "player.search.modal.subtitle": "Find presentation slides content", "player.thumbnails.wrapper.aria": "Área de miniaturas", - "player.video.wrapper.aria": "Área de video" + "player.video.wrapper.aria": "Área de video", + "player.externalVideo.autoPlayWarning": "Necesitamos su permiso para reproducir audio." } diff --git a/src/utils/builder.js b/src/utils/builder.js index b4f073bf..43581f6b 100644 --- a/src/utils/builder.js +++ b/src/utils/builder.js @@ -94,18 +94,6 @@ const buildPolls = result => { return data; }; -const buildExternalVideos = result => { - if (!result) return []; - - const data = result.map(r => { - return { - timestamp: r.timestamp, - url: r.external_video_url, - }; - }); - - return data; -}; const buildMetadata = result => { let data = {}; @@ -216,6 +204,12 @@ const buildThumbnails = slides => { src: ID.SCREENSHARE, timestamp, }); + } else if (src.includes(ID.EXTERNAL_VIDEOS)) { + result.push({ + id, + src: ID.EXTERNAL_VIDEOS, + timestamp, + }); } else { result.push({ id, @@ -223,7 +217,7 @@ const buildThumbnails = slides => { timestamp, }); } - + return result; }, []); }; @@ -449,6 +443,36 @@ const buildScreenshare = result => { return data; }; + +const buildExternalVideos = result => { + let data = []; + const { recording } = result; + + if (hasProperty(recording, 'video')) { + data = recording.video.map(video => { + const attr = getAttr(video); + return { + timestamp: parseFloat(attr.start_timestamp), + clear: parseFloat(attr.stop_timestamp), + url: attr.url, + events: video.event.map(event => { + const attr = getAttr(event); + return { + timestamp: parseFloat(attr.timestamp), + type: attr.type, + time: attr.time, + rate: parseFloat(attr.rate), + playing: (attr.playing === 'true'), + } + }) + }; + }); + } + + return data; +}; + + const getOptions = filename => { let options = {}; @@ -482,10 +506,7 @@ const build = (filename, value) => { case config.data.polls: data = buildPolls(value); break; - case config.data.externalVideos: - data = buildExternalVideos(value); - break; - default: + default: logger.debug('unhandled', 'json', filename); reject(filename); } @@ -526,6 +547,9 @@ const build = (filename, value) => { case config.data.screenshare: data = buildScreenshare(result); break; + case config.data.externalVideos: + data = buildExternalVideos(result); + break; case config.data.shapes: data = buildShapes(result); break; @@ -554,10 +578,9 @@ const addAlternatesToThumbnails = (thumbnails, alternates) => { }); }; -const mergeChatContent = (chat, polls, externalVideos) => { +const mergeChatContent = (chat, polls) => { return [ ...chat, - ...externalVideos, ...polls, ].sort((a, b) => a.timestamp - b.timestamp); }; diff --git a/src/utils/data/index.js b/src/utils/data/index.js index 30e2705b..7504e688 100644 --- a/src/utils/data/index.js +++ b/src/utils/data/index.js @@ -40,13 +40,20 @@ const getAvatarStyle = name => { return `avatar-${NUMBERS[index]}`; }; -const getActiveContent = (screenshare, time) => { +const getActiveContent = (screenshare, externalVideos, time) => { const { SCREENSHARE, PRESENTATION, + EXTERNAL_VIDEOS } = ID; - const content = isEnabled(screenshare, time) ? SCREENSHARE : PRESENTATION; + let content=PRESENTATION; + + if (isEnabled(screenshare, time)) { + content=SCREENSHARE; + } else if (isEnabled(externalVideos, time)) { + content=EXTERNAL_VIDEOS; + } return content; }; @@ -173,6 +180,11 @@ const getData = (data, id) => { case ID.PANZOOMS: case ID.POLLS: case ID.EXTERNAL_VIDEOS: + if (!file || data[getFileName(file)] === null) { + return []; + } + + return data[getFileName(file)]; case ID.SCREENSHARE: case ID.TALKERS: if (!file || data[getFileName(file)] === null) { diff --git a/src/utils/synchronizer.js b/src/utils/synchronizer.js index a0a2ce01..274a68ab 100644 --- a/src/utils/synchronizer.js +++ b/src/utils/synchronizer.js @@ -36,20 +36,42 @@ const EVENTS = [ ]; export default class Synchronizer { - constructor(primary, secondary) { + constructor(primary, secondary, externalVideos = null) { this.primary = primary; this.secondary = secondary; + if (externalVideos) { + this.externalVideos = externalVideos; + } + this.status = { primary: 'waiting', secondary: 'waiting', } this.synching = false; - + this.init(); } + syncVolume() { + const volume = this.primary.volume(); + const muted = this.primary.muted(); + + if (this.externalVideos) { + this.externalVideos.handleVolumeChange(volume,muted); + } + } + + handleUpdateTime() { + const currentTime = this.primary.currentTime(); + + if (this.externalVideos && this.externalVideos.time !== currentTime) + { + this.externalVideos.time = currentTime; + } + } + init() { STATUSES.forEach(status => { this.primary.on(status, () => this.status.primary = status); @@ -69,6 +91,10 @@ export default class Synchronizer { this.secondary.playbackRate(playbackRate); }); + this.primary.on('volumechange', () => this.syncVolume()); + + this.primary.on('timeupdate', () => this.handleUpdateTime()); + this.primary.on('waiting', () => { if (!this.synching && this.status.secondary === 'canplay') { this.synching = true; From fadd4a62b85c26b50be55e534909f641a80b8517 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 22 Aug 2021 10:19:27 +0200 Subject: [PATCH 2/2] Improvement suggested by @hiroshisuga --- src/components/external-video-player/index.js | 4 +++- src/components/player.js | 9 ++++++++- src/utils/synchronizer.js | 13 +------------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/components/external-video-player/index.js b/src/components/external-video-player/index.js index 960c14e8..15ca40c7 100644 --- a/src/components/external-video-player/index.js +++ b/src/components/external-video-player/index.js @@ -215,9 +215,11 @@ class ExternalVideoPlayer extends Component { } orchestrator () { - const { events, active, primaryPlaybackRate } = this.props; + const { events, active, getCurrentPlayerTime } = this.props; const { playing, playbackRate } = this.state; + this.time = getCurrentPlayerTime(); + let primaryPlayerPlaying = true; if (this.time === this.lastTime) { diff --git a/src/components/player.js b/src/components/player.js index 98b62b94..052aa22d 100644 --- a/src/components/player.js +++ b/src/components/player.js @@ -88,6 +88,7 @@ export default class Player extends PureComponent { this.handlePlayerReady = this.handlePlayerReady.bind(this); this.handleSearch = this.handleSearch.bind(this); this.handleTimeUpdate = this.handleTimeUpdate.bind(this); + this.getCurrentPlayerTime = this.getCurrentPlayerTime.bind(this); } componentDidMount() { @@ -106,7 +107,6 @@ export default class Player extends PureComponent { this.chat = mergeChatContent( getData(data, ID.CHAT), getData(data, ID.POLLS), - //getData(data, ID.EXTERNAL_VIDEOS), ); this.cursor = getData(data, ID.CURSOR); this.metadata = getData(data, ID.METADATA); @@ -153,6 +153,7 @@ export default class Player extends PureComponent { } + handleSearch(value) { const { search } = this.state; @@ -169,6 +170,11 @@ export default class Player extends PureComponent { } } + getCurrentPlayerTime() { + const { time } = this.state; + return time; + } + initShortcuts() { const { seconds } = shortcuts.video; @@ -509,6 +515,7 @@ export default class Player extends PureComponent { onPlayerReady={this.handlePlayerReady} events={events} primaryPlaybackRate={primaryPlaybackRate} + getCurrentPlayerTime={this.getCurrentPlayerTime} /> ); } diff --git a/src/utils/synchronizer.js b/src/utils/synchronizer.js index 274a68ab..d32c29fc 100644 --- a/src/utils/synchronizer.js +++ b/src/utils/synchronizer.js @@ -63,16 +63,7 @@ export default class Synchronizer { } } - handleUpdateTime() { - const currentTime = this.primary.currentTime(); - - if (this.externalVideos && this.externalVideos.time !== currentTime) - { - this.externalVideos.time = currentTime; - } - } - - init() { + init() { STATUSES.forEach(status => { this.primary.on(status, () => this.status.primary = status); this.secondary.on(status, () => this.status.secondary = status); @@ -93,8 +84,6 @@ export default class Synchronizer { this.primary.on('volumechange', () => this.syncVolume()); - this.primary.on('timeupdate', () => this.handleUpdateTime()); - this.primary.on('waiting', () => { if (!this.synching && this.status.secondary === 'canplay') { this.synching = true;