From 6cd5c4b31a76d9a5ff09c00861402029dd39d0d9 Mon Sep 17 00:00:00 2001 From: esikkala <esko.ikkala@aalto.fi> Date: Wed, 22 Dec 2021 08:58:31 +0200 Subject: [PATCH] Add video player component --- src/client/actions/index.js | 5 + src/client/components/main_layout/Player.js | 100 ++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/client/components/main_layout/Player.js diff --git a/src/client/actions/index.js b/src/client/actions/index.js index fb16620b..79abf190 100644 --- a/src/client/actions/index.js +++ b/src/client/actions/index.js @@ -48,6 +48,7 @@ export const LOAD_LOCALES = 'LOAD_LOCALES' export const LOAD_LOCALES_FAILED = 'LOAD_LOCALES_FAILED' export const UPDATE_LOCALE = 'UPDATE_LOCALE' export const ANIMATE_MAP = 'ANIMATE_MAP' +export const UPDATE_VIDEO_PLAYER_TIME = 'UPDATE_VIDEO_PLAYER_TIME' export const CLIENT_FS_UPDATE_QUERY = 'CLIENT_FS_UPDATE_QUERY' export const CLIENT_FS_TOGGLE_DATASET = 'CLIENT_FS_TOGGLE_DATASET' export const CLIENT_FS_FETCH_RESULTS = 'CLIENT_FS_FETCH_RESULTS' @@ -323,6 +324,10 @@ export const animateMap = value => ({ type: ANIMATE_MAP, value }) +export const updateVideoPlayerTime = value => ({ + type: UPDATE_VIDEO_PLAYER_TIME, + value +}) export const updateMapBounds = ({ resultClass, bounds }) => ({ type: UPDATE_MAP_BOUNDS, resultClass, diff --git a/src/client/components/main_layout/Player.js b/src/client/components/main_layout/Player.js new file mode 100644 index 00000000..f2cc5c7d --- /dev/null +++ b/src/client/components/main_layout/Player.js @@ -0,0 +1,100 @@ +import React from 'react' +import { withStyles } from '@material-ui/core/styles' + +const styles = theme => ({ + // https://www.w3schools.com/howto/howto_css_responsive_iframes.asp + container: { + position: 'relative', + overflow: 'hidden', + width: '100%', + paddingTop: '56.25%' /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */ + }, + responsiveIframe: { + position: 'absolute', + top: 0, + left: 0, + bottom: 0, + right: 0, + width: '100%', + height: '100%' + } +}) + +class Player extends React.Component { + componentDidMount = () => { + // On mount, check to see if the API script is already loaded + if (!window.YT) { // If not, load the script asynchronously + const tag = document.createElement('script') + tag.src = 'https://www.youtube.com/iframe_api' + + // onYouTubeIframeAPIReady will load the video after the script is loaded + window.onYouTubeIframeAPIReady = this.loadVideo + + const firstScriptTag = document.getElementsByTagName('script')[0] + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag) + } else { // If script is already there, load the video directly + this.loadVideo() + } + } + + componentDidUpdate = prevProps => { + if (this.props.routeProps.location.hash !== prevProps.routeProps.location.hash) { + this.seekToBasedOnHash() + } + } + + componentWillUnmount () { + if (this.videoTimer) { + clearInterval(this.videoTimer) + } + } + + seekToBasedOnHash = () => { + const seconds = this.props.routeProps.location.hash.substring(1) + // https://developers.google.com/youtube/iframe_api_reference#seekTo + this.player.seekTo(seconds) + } + + loadVideo = () => { + const { youTubeID } = this.props.data + // the Player object is created uniquely based on the id in props + this.player = new window.YT.Player(`youtube-player-${youTubeID}`, { + videoId: youTubeID, + playerVars: { // https://developers.google.com/youtube/player_parameters#Parameters + start: 1 // the parameter value is a positive integer + }, + events: { + onReady: this.onPlayerReady, + onStateChange: this.onPlayerStateChange + } + }) + } + + onPlayerReady = event => { + if (this.props.routeProps.location.hash === '') { + this.props.updateVideoPlayerTime(parseInt(this.player.getCurrentTime())) + } else { + this.seekToBasedOnHash() + } + this.videoTimer = setInterval(() => { + if (this.player.getPlayerState() === 1 || this.player.getPlayerState() === 2) { + this.props.updateVideoPlayerTime(parseInt(this.player.getCurrentTime())) + } + }, 1000) + } + + onPlayerStateChange = event => { + this.props.updateVideoPlayerTime(parseInt(this.player.getCurrentTime())) + } + + render () { + const { classes } = this.props + return ( + <div className={classes.container}> + <div id={`youtube-player-${this.props.data.youTubeID}`} className={classes.responsiveIframe} /> + </div> + ) + } +} + +export default withStyles(styles)(Player) -- GitLab