Skip to content

Commit

Permalink
Refactor player rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
cookpete committed Oct 26, 2017
1 parent 17b47db commit 91d1542
Show file tree
Hide file tree
Showing 16 changed files with 420 additions and 464 deletions.
168 changes: 168 additions & 0 deletions src/Player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import React, { Component } from 'react'

import { propTypes, defaultProps } from './props'

const SEEK_ON_PLAY_EXPIRY = 5000

export default class Player extends Component {
static displayName = 'Player'
static propTypes = propTypes
static defaultProps = defaultProps
mounted = false
isReady = false
isPlaying = false // Track playing state internally to prevent bugs
startOnPlay = true
seekOnPlay = null
componentDidMount () {
this.mounted = true
this.player.load(this.props.url)
}
componentWillUnmount () {
if (this.isReady) {
this.player.stop()
}
this.mounted = false
}
componentDidUpdate (prevProps) {
const { activePlayer, url } = this.props
if (prevProps.activePlayer !== activePlayer) {
this.isReady = false
this.seekOnPlay = null
this.startOnPlay = true
this.player.load(url, this.isReady)
}
}
componentWillReceiveProps (nextProps) {
// Invoke player methods based on incoming props
const { activePlayer, url, playing, volume, muted, playbackRate } = this.props
if (activePlayer !== nextProps.activePlayer) {
this.player.stop()
return // A new player is coming, so don't invoke any other methods
}
if (url !== nextProps.url) {
this.player.load(nextProps.url, this.isReady)
}
if (url && !nextProps.url) {
this.player.stop()
}
if (!playing && nextProps.playing && !this.isPlaying) {
this.player.play()
}
if (playing && !nextProps.playing && this.isPlaying) {
this.player.pause()
}
if (volume !== nextProps.volume && !nextProps.muted) {
this.player.setVolume(nextProps.volume)
}
if (muted !== nextProps.muted) {
this.player.setVolume(nextProps.muted ? 0 : nextProps.volume)
}
if (playbackRate !== nextProps.playbackRate && this.player.setPlaybackRate) {
this.player.setPlaybackRate(nextProps.playbackRate)
}
}
getCurrentTime () {
if (!this.isReady) return null
return this.player.getCurrentTime()
}
getSecondsLoaded () {
if (!this.isReady) return null
return this.player.getSecondsLoaded()
}
getDuration () {
if (!this.isReady) return null
return this.player.getDuration()
}
seekTo (amount) {
// When seeking before player is ready, store value and seek later
if (!this.isReady && amount !== 0) {
this.seekOnPlay = amount
setTimeout(() => {
this.seekOnPlay = null
}, SEEK_ON_PLAY_EXPIRY)
return
}
if (amount > 0 && amount < 1) {
// Convert fraction to seconds based on duration
const duration = this.player.getDuration()
if (!duration) {
console.warn('ReactPlayer: could not seek using fraction – duration not yet available')
return
}
this.player.seekTo(duration * amount)
return
}
this.player.seekTo(amount)
}
onReady = () => {
if (!this.mounted) return
const { onReady, playing } = this.props
this.isReady = true
this.loadingSDK = false
onReady()
if (playing) {
if (this.loadOnReady) {
this.player.load(this.loadOnReady)
this.loadOnReady = null
} else {
this.player.play()
}
}
this.onDurationCheck()
}
onPlay = () => {
this.isPlaying = true
const { volume, muted, onStart, onPlay, playbackRate } = this.props
if (this.startOnPlay) {
if (this.player.setPlaybackRate) {
this.player.setPlaybackRate(playbackRate)
}
this.player.setVolume(muted ? 0 : volume)
onStart()
this.startOnPlay = false
}
onPlay()
if (this.seekOnPlay) {
this.seekTo(this.seekOnPlay)
this.seekOnPlay = null
}
this.onDurationCheck()
}
onPause = () => {
this.isPlaying = false
this.props.onPause()
}
onEnded = () => {
const { activePlayer, loop, onEnded } = this.props
if (activePlayer.loopOnEnded && loop) {
this.seekTo(0)
}
onEnded()
}
onDurationCheck = () => {
clearTimeout(this.durationCheckTimeout)
const duration = this.getDuration()
if (duration) {
this.props.onDuration(duration)
} else {
this.durationCheckTimeout = setTimeout(this.onDurationCheck, 100)
}
}
ref = player => {
if (player) {
this.player = player
}
}
render () {
const Player = this.props.activePlayer
return (
<Player
{...this.props}
ref={this.ref}
onReady={this.onReady}
onPlay={this.onPlay}
onEnded={this.onEnded}
/>
)
}
}
92 changes: 25 additions & 67 deletions src/ReactPlayer.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,19 @@
import React, { Component } from 'react'

import { propTypes, defaultProps, DEPRECATED_CONFIG_PROPS } from './props'
import { getConfig, omit } from './utils'
import YouTube from './players/YouTube'
import SoundCloud from './players/SoundCloud'
import Vimeo from './players/Vimeo'
import Facebook from './players/Facebook'
import { getConfig, omit, isObject } from './utils'
import players from './players'
import Player from './Player'
import FilePlayer from './players/FilePlayer'
import Streamable from './players/Streamable'
import Vidme from './players/Vidme'
import Wistia from './players/Wistia'
import DailyMotion from './players/DailyMotion'
import Twitch from './players/Twitch'
import renderPreloadPlayers from './preload'

const SUPPORTED_PROPS = Object.keys(propTypes)
const SUPPORTED_PLAYERS = [
YouTube,
SoundCloud,
Vimeo,
Facebook,
Streamable,
Vidme,
Wistia,
Twitch,
DailyMotion
]

export default class ReactPlayer extends Component {
static displayName = 'ReactPlayer'
static propTypes = propTypes
static defaultProps = defaultProps
static canPlay = url => {
const players = [...SUPPORTED_PLAYERS, FilePlayer]
for (let Player of players) {
if (Player.canPlay(url)) {
return true
Expand All @@ -47,17 +29,13 @@ export default class ReactPlayer extends Component {
clearTimeout(this.progressTimeout)
}
shouldComponentUpdate (nextProps) {
return (
this.props.url !== nextProps.url ||
this.props.playing !== nextProps.playing ||
this.props.loop !== nextProps.loop ||
this.props.volume !== nextProps.volume ||
this.props.muted !== nextProps.muted ||
this.props.playbackRate !== nextProps.playbackRate ||
this.props.height !== nextProps.height ||
this.props.width !== nextProps.width ||
this.props.hidden !== nextProps.hidden
)
for (let key of Object.keys(this.props)) {
const prop = this.props[key]
if (!isObject(prop) && prop !== nextProps[key]) {
return true
}
}
return false
}
seekTo = fraction => {
if (!this.player) return null
Expand Down Expand Up @@ -99,59 +77,39 @@ export default class ReactPlayer extends Component {
}
this.progressTimeout = setTimeout(this.progress, this.props.progressFrequency)
}
renderActivePlayer (url) {
if (!url) return null
for (let Player of SUPPORTED_PLAYERS) {
getActivePlayer (url) {
for (let Player of players) {
if (Player.canPlay(url)) {
return this.renderPlayer(Player)
return Player
}
}
// Fall back to FilePlayer if nothing else can play the URL
return this.renderPlayer(FilePlayer)
}
renderPlayer = Player => {
return (
<Player
{...this.props}
ref={this.activePlayerRef}
key={Player.displayName}
config={this.config}
/>
)
}
activePlayerRef = player => {
this.player = player
return FilePlayer
}
wrapperRef = wrapper => {
this.wrapper = wrapper
}
renderPreloadPlayers (url) {
// Render additional players if preload config is set
const preloadPlayers = []
if (!YouTube.canPlay(url) && this.config.youtube.preload) {
preloadPlayers.push(YouTube)
}
if (!Vimeo.canPlay(url) && this.config.vimeo.preload) {
preloadPlayers.push(Vimeo)
}
if (!DailyMotion.canPlay(url) && this.config.dailymotion.preload) {
preloadPlayers.push(DailyMotion)
}
return preloadPlayers.map(this.renderPreloadPlayer)
activePlayerRef = player => {
this.player = player
}
renderPreloadPlayer = Player => {
renderActivePlayer (url) {
if (!url) return null
const activePlayer = this.getActivePlayer(url)
return (
<Player
key={Player.displayName}
{...this.props}
key={activePlayer.displayName}
ref={this.activePlayerRef}
config={this.config}
activePlayer={activePlayer}
/>
)
}
render () {
const { url, style, width, height } = this.props
const otherProps = omit(this.props, SUPPORTED_PROPS, DEPRECATED_CONFIG_PROPS)
const activePlayer = this.renderActivePlayer(url)
const preloadPlayers = this.renderPreloadPlayers(url)
const preloadPlayers = renderPreloadPlayers(url, this.config)
return (
<div ref={this.wrapperRef} style={{ ...style, width, height }} {...otherProps}>
{[ activePlayer, ...preloadPlayers ]}
Expand Down

0 comments on commit 91d1542

Please sign in to comment.