Skip to content

Commit f295457

Browse files
authored
Merge pull request #187 from hoangvvo/use-iframe-api
Use YouTube API for videoId and playList changes
2 parents 35dec9d + ba645eb commit f295457

File tree

2 files changed

+72
-36
lines changed

2 files changed

+72
-36
lines changed

src/PlayerScripts.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ true;
5353

5454
return `player.${func}({playlist: ${playlistJson}, index: ${index}); true;`;
5555
},
56+
57+
loadVideoById: (videoId, play) => {
58+
const func = play ? 'loadVideoById' : 'cueVideoById';
59+
60+
return `player.${func}({videoId: ${JSON.stringify(videoId)}}); true;`;
61+
},
5662
};
5763

5864
export const playMode = {

src/YoutubeIframe.js

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
1+
import {EventEmitter} from 'events';
12
import React, {
2-
useRef,
3-
useState,
4-
useEffect,
53
forwardRef,
64
useCallback,
5+
useEffect,
76
useImperativeHandle,
87
useMemo,
8+
useRef,
9+
useState,
910
} from 'react';
10-
import {View, StyleSheet, Platform} from 'react-native';
11-
import {WebView} from './WebView';
11+
import {Platform, StyleSheet, View} from 'react-native';
1212
import {
13+
CUSTOM_USER_AGENT,
14+
DEFAULT_BASE_URL,
1315
PLAYER_ERROR,
1416
PLAYER_STATES,
15-
DEFAULT_BASE_URL,
16-
CUSTOM_USER_AGENT,
1717
} from './constants';
18-
import {EventEmitter} from 'events';
1918
import {
20-
playMode,
21-
soundMode,
2219
MAIN_SCRIPT,
2320
PLAYER_FUNCTIONS,
21+
playMode,
22+
soundMode,
2423
} from './PlayerScripts';
24+
import {WebView} from './WebView';
25+
26+
const deepComparePlayList = (lastPlayList, playList) => {
27+
return (
28+
typeof lastPlayList === typeof playList &&
29+
(Array.isArray(lastPlayList)
30+
? lastPlayList.join('')
31+
: lastPlayList === Array.isArray(playList)
32+
? playList.join('')
33+
: playList)
34+
);
35+
};
2536

2637
const YoutubeIframe = (props, ref) => {
2738
const {
@@ -41,7 +52,7 @@ const YoutubeIframe = (props, ref) => {
4152
onError = _err => {},
4253
onReady = _event => {},
4354
playListStartIndex = 0,
44-
initialPlayerParams = {},
55+
initialPlayerParams,
4556
allowWebViewZoom = false,
4657
forceAndroidAutoplay = false,
4758
onChangeState = _event => {},
@@ -50,6 +61,10 @@ const YoutubeIframe = (props, ref) => {
5061
onPlaybackRateChange = _playbackRate => {},
5162
} = props;
5263

64+
const lastVideoIdRef = useRef(videoId);
65+
const lastPlayListRef = useRef(playList);
66+
const initialPlayerParamsRef = useRef(initialPlayerParams || {});
67+
5368
const webViewRef = useRef(null);
5469
const eventEmitter = useRef(new EventEmitter());
5570
const [playerReady, setPlayerReady] = useState(0);
@@ -126,6 +141,39 @@ const YoutubeIframe = (props, ref) => {
126141
].forEach(webViewRef.current.injectJavaScript);
127142
}, [play, playerReady, mute, volume, playbackRate]);
128143

144+
useEffect(() => {
145+
if (playerReady < 1 || lastVideoIdRef.current === videoId) {
146+
// no instance of player is ready
147+
// or videoId has not changed
148+
return;
149+
}
150+
151+
lastVideoIdRef.current = videoId;
152+
153+
webViewRef.current.injectJavaScript(
154+
PLAYER_FUNCTIONS.loadVideoById(videoId, play),
155+
);
156+
}, [videoId, play, playerReady]);
157+
158+
useEffect(() => {
159+
if (playerReady < 1) {
160+
// no instance of player is ready
161+
return;
162+
}
163+
164+
// Also, right now, we are helping users by doing "deep" comparisons of playList prop,
165+
// but in the next major we should leave the responsibility to user (either via useMemo or moving the array outside)
166+
if (!playList || deepComparePlayList(lastPlayListRef.current, playList)) {
167+
return;
168+
}
169+
170+
lastPlayListRef.current = playList;
171+
172+
webViewRef.current.injectJavaScript(
173+
PLAYER_FUNCTIONS.loadPlaylist(playList, playListStartIndex, play),
174+
);
175+
}, [playList, play, playListStartIndex, playerReady]);
176+
129177
const onWebMessage = useCallback(
130178
event => {
131179
try {
@@ -141,15 +189,6 @@ const YoutubeIframe = (props, ref) => {
141189
case 'playerReady':
142190
onReady();
143191
setPlayerReady(prev => prev + 1);
144-
if (Array.isArray(playList)) {
145-
webViewRef.current.injectJavaScript(
146-
PLAYER_FUNCTIONS.loadPlaylist(
147-
playList,
148-
playListStartIndex,
149-
play,
150-
),
151-
);
152-
}
153192
break;
154193
case 'playerQualityChange':
155194
onPlaybackQualityChange(message.data);
@@ -169,13 +208,10 @@ const YoutubeIframe = (props, ref) => {
169208
}
170209
},
171210
[
172-
play,
173211
onReady,
174212
onError,
175-
playList,
176213
onChangeState,
177214
onFullScreenChange,
178-
playListStartIndex,
179215
onPlaybackRateChange,
180216
onPlaybackQualityChange,
181217
],
@@ -200,9 +236,9 @@ const YoutubeIframe = (props, ref) => {
200236

201237
const source = useMemo(() => {
202238
const ytScript = MAIN_SCRIPT(
203-
videoId,
204-
playList,
205-
initialPlayerParams,
239+
lastVideoIdRef.current,
240+
lastPlayListRef.current,
241+
initialPlayerParamsRef.current,
206242
allowWebViewZoom,
207243
contentScale,
208244
);
@@ -219,15 +255,7 @@ const YoutubeIframe = (props, ref) => {
219255
const data = ytScript.urlEncodedJSON;
220256

221257
return {uri: base + '?data=' + data};
222-
}, [
223-
videoId,
224-
playList,
225-
useLocalHTML,
226-
contentScale,
227-
baseUrlOverride,
228-
allowWebViewZoom,
229-
initialPlayerParams,
230-
]);
258+
}, [useLocalHTML, contentScale, baseUrlOverride, allowWebViewZoom]);
231259

232260
return (
233261
<View style={{height, width}}>
@@ -238,7 +266,9 @@ const YoutubeIframe = (props, ref) => {
238266
style={[styles.webView, webViewStyle]}
239267
mediaPlaybackRequiresUserAction={false}
240268
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
241-
allowsFullscreenVideo={!initialPlayerParams?.preventFullScreen}
269+
allowsFullscreenVideo={
270+
!initialPlayerParamsRef.current.preventFullScreen
271+
}
242272
userAgent={
243273
forceAndroidAutoplay
244274
? Platform.select({android: CUSTOM_USER_AGENT, ios: ''})

0 commit comments

Comments
 (0)