UNPKG

react-native-theoplayer

Version:

A THEOplayer video component for react-native.

174 lines (165 loc) 7.07 kB
"use strict"; /* eslint-disable @typescript-eslint/no-empty-function */ const DEFAULT_SKIP_FORWARD_INTERVAL = 5; const DEFAULT_SKIP_BACKWARD_INTERVAL = 5; export const defaultMediaControlConfiguration = { mediaSessionEnabled: true, skipForwardInterval: DEFAULT_SKIP_FORWARD_INTERVAL, skipBackwardInterval: DEFAULT_SKIP_BACKWARD_INTERVAL }; const NoOp = () => {}; // This prevents unnecessary errors when Media Session API is not available. const mediaSession = function () { const mediaSession = navigator.mediaSession || {}; mediaSession.setActionHandler = navigator.mediaSession?.setActionHandler || function () {}; mediaSession.setPositionState = navigator.mediaSession?.setPositionState || function () {}; window.MediaMetadata = window.MediaMetadata || function () {}; return mediaSession; }(); /** * The MediaSession interface of the Media Session API allows a web page to provide custom behaviors for standard media playback interactions, and * to report metadata that can be sent by the user agent to the device or operating system for presentation in standardized user interface elements. * * @link https://w3c.github.io/mediasession */ export class WebMediaSession { constructor(adapter, player, config = defaultMediaControlConfiguration) { this._player = player; this._webAdapter = adapter; this._config = config; this._player.addEventListener('sourcechange', this.onSourceChange); } updateMediaSession() { // update trickplay capabilities if (this.isTrickPlayEnabled()) { mediaSession.setActionHandler('seekbackward', event => { const skipTime = event.seekOffset || this._config.skipBackwardInterval || DEFAULT_SKIP_BACKWARD_INTERVAL; this._player.currentTime = Math.max(this._player.currentTime - skipTime, 0); this.updatePositionState(); }); mediaSession.setActionHandler('seekforward', event => { const skipTime = event.seekOffset || this._config.skipForwardInterval || DEFAULT_SKIP_FORWARD_INTERVAL; this._player.currentTime = Math.min(this._player.currentTime + skipTime, this._player.duration); this.updatePositionState(); }); mediaSession.setActionHandler('seekto', event => { const seekTime = event.seekTime; if (seekTime !== undefined) { this._player.currentTime = seekTime; } this.updatePositionState(); }); } else { mediaSession.setActionHandler('seekbackward', NoOp); mediaSession.setActionHandler('seekforward', NoOp); mediaSession.setActionHandler('seekto', NoOp); } // update play/pause capabilities if (this.isPlayPauseEnabled()) { mediaSession.setActionHandler('play', () => { this._player?.play(); }); mediaSession?.setActionHandler('pause', () => { this._player?.pause(); }); } else { mediaSession.setActionHandler('play', NoOp); mediaSession.setActionHandler('pause', NoOp); } // update playbackState mediaSession.playbackState = this._player.paused ? 'paused' : 'playing'; // update position this.updatePositionState(); } destroy() { this._player.removeEventListener(['play', 'playing'], this.onFirstPlaying); this._player.removeEventListener(['play', 'pause', 'loadedmetadata', 'durationchange', 'ratechange'], this.update); this._player.ads?.removeEventListener(['adbreakbegin', 'adbreakend'], this.update); mediaSession.setActionHandler('play', NoOp); mediaSession.setActionHandler('pause', NoOp); mediaSession.setActionHandler('seekbackward', NoOp); mediaSession.setActionHandler('seekforward', NoOp); mediaSession.setActionHandler('seekto', NoOp); } update = () => { this.updateMediaSession(); }; onFirstPlaying = () => { this._player.removeEventListener(['play', 'playing'], this.onFirstPlaying); this.updateMetadata(); this._player.addEventListener(['play', 'pause', 'loadedmetadata', 'durationchange', 'ratechange'], this.update); this._player.ads?.addEventListener(['adbreakbegin', 'adbreakend'], this.update); }; onSourceChange = () => { this._player.removeEventListener(['play', 'playing'], this.onFirstPlaying); this._player.removeEventListener(['play', 'pause', 'loadedmetadata', 'durationchange', 'ratechange'], this.update); this._player.ads?.removeEventListener(['adbreakbegin', 'adbreakend'], this.update); mediaSession.metadata = null; mediaSession.playbackState = 'none'; this._player.addEventListener(['play', 'playing'], this.onFirstPlaying); }; updateMetadata = () => { const source = this._player.source; const metadata = source?.metadata; const artwork = [metadata?.displayIconUri, source?.poster, ...(metadata?.images ? metadata.images : [])].filter(image => image !== undefined).map(image => { if (typeof image === 'string') { return { src: image }; } return image; }); mediaSession.metadata = new MediaMetadata({ title: metadata?.title, artist: metadata?.artist || metadata?.subtitle, album: metadata?.album, artwork }); }; updatePositionState = () => { const { duration, playbackRate, currentTime } = this._player; const isLive = !isFinite(duration); if (!isLive) { mediaSession.setPositionState({ // The duration is used to specify the duration in seconds. // It should always be positive and positive infinity can be used to indicate media without a defined end such as live playback. // NOTE: passing Infinite causes an exception to be thrown. duration: isNaN(duration) || duration < 0 ? 0 : duration, // The playbackRate is used to specify the playback rate. // It can be positive to represent forward playback or negative to represent backwards playback. It should not be zero. playbackRate, // The position is used to specify the last reported playback position in seconds. It should always be positive. position: currentTime }); } }; isLive() { return !isFinite(this._player.duration); } isInAd() { return this._player.ads?.playing === true; } isInBackground() { return document.visibilityState !== 'visible'; } // By default, only show trick-play buttons if: // - backgroundAudio is enabled, or the player is in foreground; // - and, the current asset is neither a live stream, nor an ad. isTrickPlayEnabled() { return (this.isBackgroundAudioEnabled() || !this.isInBackground()) && !this.isLive() && !this.isInAd(); } // By default, only show a play/pause button if: // - backgroundAudio is enabled, or the player is in foreground; // - and, the current asset is not an ad. isPlayPauseEnabled() { return (this.isBackgroundAudioEnabled() || !this.isInBackground()) && !this.isInAd(); } isBackgroundAudioEnabled() { return this._webAdapter.backgroundAudioConfiguration.enabled === true; } } //# sourceMappingURL=WebMediaSession.js.map