react-player
Version:
A React component for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable, Vimeo, Wistia and DailyMotion
185 lines (158 loc) • 5.08 kB
JSX
import React, { Component, Suspense, lazy } from 'react'
import merge from 'deepmerge'
import memoize from 'memoize-one'
import isEqual from 'react-fast-compare'
import { propTypes, defaultProps } from './props'
import { omit } from './utils'
import Player from './Player'
const Preview = lazy(() => import(/* webpackChunkName: 'reactPlayerPreview' */'./Preview'))
const IS_BROWSER = typeof window !== 'undefined' && window.document
const IS_GLOBAL = typeof global !== 'undefined' && global.window && global.window.document
const SUPPORTED_PROPS = Object.keys(propTypes)
// Return null when rendering on the server
// as Suspense is not supported yet
const UniversalSuspense = IS_BROWSER || IS_GLOBAL ? Suspense : () => null
const customPlayers = []
export const createReactPlayer = (players, fallback) => {
return class ReactPlayer extends Component {
static displayName = 'ReactPlayer'
static propTypes = propTypes
static defaultProps = defaultProps
static addCustomPlayer = player => { customPlayers.push(player) }
static removeCustomPlayers = () => { customPlayers.length = 0 }
static canPlay = url => {
for (const Player of [...customPlayers, ...players]) {
if (Player.canPlay(url)) {
return true
}
}
return false
}
static canEnablePIP = url => {
for (const Player of [...customPlayers, ...players]) {
if (Player.canEnablePIP && Player.canEnablePIP(url)) {
return true
}
}
return false
}
state = {
showPreview: !!this.props.light
}
// Use references, as refs is used by React
references = {
wrapper: wrapper => { this.wrapper = wrapper },
player: player => { this.player = player }
}
shouldComponentUpdate (nextProps, nextState) {
return !isEqual(this.props, nextProps) || !isEqual(this.state, nextState)
}
componentDidUpdate (prevProps) {
const { light } = this.props
if (!prevProps.light && light) {
this.setState({ showPreview: true })
}
if (prevProps.light && !light) {
this.setState({ showPreview: false })
}
}
handleClickPreview = (e) => {
this.setState({ showPreview: false })
this.props.onClickPreview(e)
}
showPreview = () => {
this.setState({ showPreview: true })
}
getDuration = () => {
if (!this.player) return null
return this.player.getDuration()
}
getCurrentTime = () => {
if (!this.player) return null
return this.player.getCurrentTime()
}
getSecondsLoaded = () => {
if (!this.player) return null
return this.player.getSecondsLoaded()
}
getInternalPlayer = (key = 'player') => {
if (!this.player) return null
return this.player.getInternalPlayer(key)
}
seekTo = (fraction, type, keepPlaying) => {
if (!this.player) return null
this.player.seekTo(fraction, type, keepPlaying)
}
handleReady = () => {
this.props.onReady(this)
}
getActivePlayer = memoize(url => {
for (const player of [...customPlayers, ...players]) {
if (player.canPlay(url)) {
return player
}
}
if (fallback) {
return fallback
}
return null
})
getConfig = memoize((url, key) => {
const { config } = this.props
return merge.all([
defaultProps.config,
defaultProps.config[key] || {},
config,
config[key] || {}
])
})
getAttributes = memoize(url => {
return omit(this.props, SUPPORTED_PROPS)
})
renderPreview (url) {
if (!url) return null
const { light, playIcon, previewTabIndex, oEmbedUrl } = this.props
return (
<Preview
url={url}
light={light}
playIcon={playIcon}
previewTabIndex={previewTabIndex}
oEmbedUrl={oEmbedUrl}
onClick={this.handleClickPreview}
/>
)
}
renderActivePlayer = url => {
if (!url) return null
const player = this.getActivePlayer(url)
if (!player) return null
const config = this.getConfig(url, player.key)
return (
<Player
{...this.props}
key={player.key}
ref={this.references.player}
config={config}
activePlayer={player.lazyPlayer || player}
onReady={this.handleReady}
/>
)
}
render () {
const { url, style, width, height, fallback, wrapper: Wrapper } = this.props
const { showPreview } = this.state
const attributes = this.getAttributes(url)
const wrapperRef = typeof Wrapper === 'string' ? this.references.wrapper : undefined
return (
<Wrapper ref={wrapperRef} style={{ ...style, width, height }} {...attributes}>
<UniversalSuspense fallback={fallback}>
{showPreview
? this.renderPreview(url)
: this.renderActivePlayer(url)}
</UniversalSuspense>
</Wrapper>
)
}
}
}