UNPKG

react-native-youtube-iframe-2

Version:

A simple wrapper around the youtube iframe js API for react native

234 lines (222 loc) 6.49 kB
import React, { useRef, useState, useEffect, forwardRef, useCallback, useImperativeHandle, } from 'react'; import {View, StyleSheet, Platform} from 'react-native'; import {WebView} from './WebView'; import {PLAYER_STATES, PLAYER_ERROR, CUSTOM_USER_AGENT} from './constants'; import {EventEmitter} from 'events'; import { playMode, soundMode, MAIN_SCRIPT, PLAYER_FUNCTIONS, MAIN_SCRIPT_REMOTE, } from './PlayerScripts'; const YoutubeIframe = (props, ref) => { const { height, width, videoId, playList, remote, play = false, mute = false, volume = 100, webViewStyle, webViewProps, playbackRate = 1, contentScale = 1.0, onError = _err => {}, onReady = _event => {}, playListStartIndex = 0, initialPlayerParams = {}, allowWebViewZoom = false, forceAndroidAutoplay = false, onChangeState = _event => {}, onFullScreenChange = _status => {}, onPlaybackQualityChange = _quality => {}, onPlaybackRateChange = _playbackRate => {}, } = props; const webViewRef = useRef(null); const eventEmitter = useRef(new EventEmitter()); const [playerReady, setPlayerReady] = useState(false); useImperativeHandle( ref, () => ({ getDuration: () => { webViewRef.current.injectJavaScript(PLAYER_FUNCTIONS.durationScript); return new Promise(resolve => { eventEmitter.current.once('getDuration', resolve); }); }, getCurrentTime: () => { webViewRef.current.injectJavaScript(PLAYER_FUNCTIONS.currentTimeScript); return new Promise(resolve => { eventEmitter.current.once('getCurrentTime', resolve); }); }, isMuted: () => { webViewRef.current.injectJavaScript(PLAYER_FUNCTIONS.isMutedScript); return new Promise(resolve => { eventEmitter.current.once('isMuted', resolve); }); }, getVolume: () => { webViewRef.current.injectJavaScript(PLAYER_FUNCTIONS.getVolumeScript); return new Promise(resolve => { eventEmitter.current.once('getVolume', resolve); }); }, getPlaybackRate: () => { webViewRef.current.injectJavaScript( PLAYER_FUNCTIONS.getPlaybackRateScript, ); return new Promise(resolve => { eventEmitter.current.once('getPlaybackRate', resolve); }); }, getAvailablePlaybackRates: () => { webViewRef.current.injectJavaScript( PLAYER_FUNCTIONS.getAvailablePlaybackRatesScript, ); return new Promise(resolve => { eventEmitter.current.once('getAvailablePlaybackRates', resolve); }); }, seekTo: (seconds, allowSeekAhead) => { webViewRef.current.injectJavaScript( PLAYER_FUNCTIONS.seekToScript(seconds, allowSeekAhead), ); }, }), [], ); useEffect(() => { if (!playerReady) { return; } [ playMode[play], soundMode[mute], PLAYER_FUNCTIONS.setVolume(volume), PLAYER_FUNCTIONS.setPlaybackRate(playbackRate), ].forEach(webViewRef.current.injectJavaScript); }, [play, playerReady, mute, volume, playbackRate]); const onWebMessage = useCallback( event => { const message = JSON.parse(event.nativeEvent.data); try { switch (message.eventType) { case 'fullScreenChange': onFullScreenChange(message.data); break; case 'playerStateChange': onChangeState(PLAYER_STATES[message.data]); break; case 'playerReady': onReady(); setPlayerReady(true); if (Array.isArray(playList)) { webViewRef.current.injectJavaScript( PLAYER_FUNCTIONS.loadPlaylist( playList, playListStartIndex, play, ), ); } break; case 'playerQualityChange': onPlaybackQualityChange(message.data); break; case 'playerError': onError(PLAYER_ERROR[message.data]); break; case 'playbackRateChange': onPlaybackRateChange(message.data); break; default: eventEmitter.current.emit(message.eventType, message.data); break; } } catch (error) { console.warn(error); } }, [ play, onReady, onError, playList, onChangeState, onFullScreenChange, playListStartIndex, onPlaybackRateChange, onPlaybackQualityChange, ], ); return ( <View style={{height, width}}> <WebView originWhitelist={['*']} allowsInlineMediaPlayback style={[styles.webView, webViewStyle]} mediaPlaybackRequiresUserAction={false} allowsFullscreenVideo={!initialPlayerParams?.preventFullScreen} userAgent={ forceAndroidAutoplay ? Platform.select({android: CUSTOM_USER_AGENT, ios: ''}) : '' } onShouldStartLoadWithRequest={request => { return request.mainDocumentURL === 'about:blank'; }} bounces={false} // props above this are override-able // -- {...webViewProps} // -- //add props that should not be allowed to be overridden below ref={webViewRef} onMessage={onWebMessage} source={ !remote ? { // partially allow source to be overridden ...webViewProps?.source, method: 'GET', html: MAIN_SCRIPT( videoId, playList, initialPlayerParams, allowWebViewZoom, contentScale, ), } : { // partially allow source to be overridden ...webViewProps?.source, method: 'GET', uri: MAIN_SCRIPT_REMOTE( videoId, playList, initialPlayerParams, allowWebViewZoom, contentScale, remote, ), } } /> </View> ); }; const styles = StyleSheet.create({ webView: {backgroundColor: 'transparent'}, }); export default forwardRef(YoutubeIframe);