@applicaster/quick-brick-player 12.0.0-alpha.4739493702 → 12.0.0-alpha.4796013211
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/src/Player/index.tsx +1057 -67
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.4796013211",
|
|
4
4
|
"description": "Quick Brick Player",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -38,17 +38,17 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.118",
|
|
40
40
|
"@applicaster/quick-brick-tv-transport-controls": "12.0.0-rc.112",
|
|
41
|
-
"@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.
|
|
42
|
-
"@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.
|
|
43
|
-
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.
|
|
41
|
+
"@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.4796013211",
|
|
42
|
+
"@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.4796013211",
|
|
43
|
+
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.4796013211",
|
|
44
44
|
"keymirror": "^0.1.1",
|
|
45
45
|
"query-string": "7.1.3",
|
|
46
46
|
"react-native-web-linear-gradient": "1.1.2",
|
|
47
47
|
"shaka-player": "4.3.5",
|
|
48
48
|
"typeface-montserrat": "^0.0.54",
|
|
49
|
-
"video.js": "
|
|
50
|
-
"videojs-contrib-eme": "^
|
|
51
|
-
"videojs-contrib-quality-levels": "
|
|
49
|
+
"video.js": "7.14.3",
|
|
50
|
+
"videojs-contrib-eme": "^3.8.1",
|
|
51
|
+
"videojs-contrib-quality-levels": "3.0.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@applicaster/quick-brick-xray": "*",
|
package/src/Player/index.tsx
CHANGED
|
@@ -2,32 +2,74 @@
|
|
|
2
2
|
/* eslint-disable react-native/no-color-literals */
|
|
3
3
|
|
|
4
4
|
import React from "react";
|
|
5
|
-
import
|
|
5
|
+
import * as R from "ramda";
|
|
6
6
|
|
|
7
|
-
import "
|
|
8
|
-
import "
|
|
7
|
+
import { findNodeHandle, StyleSheet, View } from "react-native";
|
|
8
|
+
import TransportControlsMobile from "@applicaster/quick-brick-mobile-transport-controls";
|
|
9
|
+
import TransportControlsTV from "@applicaster/quick-brick-tv-transport-controls";
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import { AudioPlayer } from "@applicaster/zapp-react-native-ui-components/Components/AudioPlayer";
|
|
12
|
+
import {
|
|
13
|
+
isAndroidPlatform,
|
|
14
|
+
isApplePlatform,
|
|
15
|
+
isTV,
|
|
16
|
+
isTvOSPlatform,
|
|
17
|
+
isWeb,
|
|
18
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
m3u8: "application/x-mpegURL",
|
|
15
|
-
mpd: "application/dash+xml",
|
|
16
|
-
};
|
|
20
|
+
import { PlayerWrapper } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal";
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
import { PlayNextOverlay } from "./PlayNextOverlay";
|
|
23
|
+
import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
};
|
|
25
|
+
import { GENERAL_EVENT } from "@applicaster/zapp-react-native-utils/analyticsUtils/events";
|
|
26
|
+
import { Categories, createLogger, Subsystem } from "../logger";
|
|
27
|
+
import { parseLanguageTracks } from "@applicaster/zapp-react-native-utils/playerUtils/configurationUtils";
|
|
28
|
+
import { DEFAULT_COLORS, SEEK_TIME } from "../utils/const";
|
|
29
|
+
import { AudioPlayerWrapper } from "./AudioLayer/AudioPlayerWrapper";
|
|
30
|
+
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
31
|
+
import { getAllAccessibilityProps } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
32
|
+
import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
|
|
33
|
+
import { getTargetScreenDataFromEntry } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useTargetScreenData";
|
|
34
|
+
import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
|
|
35
|
+
import {
|
|
36
|
+
AnimationComponent,
|
|
37
|
+
ComponentAnimationType,
|
|
38
|
+
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
39
|
+
import { PlayerAnalyticsManager } from "@applicaster/zapp-react-native-utils/analyticsUtils/PlayerAnalyticsManager";
|
|
40
|
+
import { PlayerAnalyticsTrackerI } from "@applicaster/zapp-react-native-utils/analyticsUtils/playerAnalyticsTracker";
|
|
41
|
+
|
|
42
|
+
import { RNPlayerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/nativePlayerManager";
|
|
43
|
+
|
|
44
|
+
const styles = StyleSheet.create({
|
|
45
|
+
container: { flex: 1 },
|
|
46
|
+
playerContainerBlack: { backgroundColor: DEFAULT_COLORS.black },
|
|
47
|
+
playerContainerTransparent: { backgroundColor: DEFAULT_COLORS.transparent }, // android
|
|
48
|
+
audio: {
|
|
49
|
+
overflow: "hidden",
|
|
50
|
+
position: "absolute",
|
|
51
|
+
zIndex: 1,
|
|
52
|
+
},
|
|
53
|
+
playerSize: { width: "100%", height: "100%" },
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const logger = createLogger({
|
|
57
|
+
category: Categories.PLAYER,
|
|
58
|
+
subsystem: Subsystem,
|
|
59
|
+
});
|
|
23
60
|
|
|
24
61
|
export default class VideoPlayer extends React.Component<
|
|
25
62
|
QuickBrickPlayer.PlayerProps,
|
|
26
63
|
QuickBrickPlayer.NativePlayerState
|
|
27
64
|
> {
|
|
65
|
+
public static propTypes = {};
|
|
28
66
|
_root: any;
|
|
29
67
|
playerPluginId: string;
|
|
30
68
|
takeParentStyle: Record<any, any>;
|
|
69
|
+
playbackStarted = false;
|
|
70
|
+
needToResumeOnForeground: boolean = null;
|
|
71
|
+
private onVideoLoadCalled: boolean;
|
|
72
|
+
playNextData: PlayNextData;
|
|
31
73
|
|
|
32
74
|
state: QuickBrickPlayer.NativePlayerState = {
|
|
33
75
|
audioTrackId: null,
|
|
@@ -52,8 +94,20 @@ export default class VideoPlayer extends React.Component<
|
|
|
52
94
|
duration: 0,
|
|
53
95
|
};
|
|
54
96
|
|
|
97
|
+
private _tracker: PlayerAnalyticsTrackerI;
|
|
98
|
+
get analyticsTracker() {
|
|
99
|
+
if (!this._tracker) {
|
|
100
|
+
this._tracker = PlayerAnalyticsManager.getInstance().getTracker(
|
|
101
|
+
this.props.playerId
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return this._tracker;
|
|
106
|
+
}
|
|
107
|
+
|
|
55
108
|
constructor(props) {
|
|
56
109
|
super(props);
|
|
110
|
+
this.onVideoLoadCalled = false; // android
|
|
57
111
|
|
|
58
112
|
this.takeParentStyle = isWeb()
|
|
59
113
|
? { width: "100%", flex: "1 1 auto", objectFit: "contain" }
|
|
@@ -62,58 +116,59 @@ export default class VideoPlayer extends React.Component<
|
|
|
62
116
|
this.playerPluginId = this.props?.playerPluginIdentifier;
|
|
63
117
|
}
|
|
64
118
|
|
|
119
|
+
componentWillUnmount() {
|
|
120
|
+
this.playbackStarted = false;
|
|
121
|
+
|
|
122
|
+
this.analyticsTracker.handleAnalyticEvent(GENERAL_EVENT.session_end);
|
|
123
|
+
this.onPlayerClosed();
|
|
124
|
+
|
|
125
|
+
if (isAndroidPlatform()) {
|
|
126
|
+
this.disablePiP();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
65
130
|
componentDidMount() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} = this.props.entry;
|
|
70
|
-
|
|
71
|
-
const options = {
|
|
72
|
-
height: 1080,
|
|
73
|
-
width: 1920,
|
|
74
|
-
playsinline: undefined,
|
|
75
|
-
autoplay: true,
|
|
76
|
-
retryOnError: true,
|
|
77
|
-
defaultVolume: 1,
|
|
78
|
-
preload: "metadata",
|
|
79
|
-
html5: {
|
|
80
|
-
nativeTextTracks: false,
|
|
81
|
-
nativeVideoTracks: false,
|
|
82
|
-
nativeAudioTracks: false,
|
|
83
|
-
vhs: {
|
|
84
|
-
limitRenditionByPlayerDimensions: true,
|
|
85
|
-
useDevicePixelRatio: true,
|
|
86
|
-
overrideNative: true,
|
|
87
|
-
enableLowInitialPlaylist: true,
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
};
|
|
131
|
+
this.playbackStarted = false;
|
|
132
|
+
this.onPlayerPresented();
|
|
133
|
+
}
|
|
91
134
|
|
|
92
|
-
|
|
135
|
+
shouldComponentUpdate(nextProps) {
|
|
136
|
+
const srcPath = ["entry", "content", "src"];
|
|
93
137
|
|
|
94
|
-
|
|
138
|
+
// when using vods from applicaster2, `entry.content.src` might be
|
|
139
|
+
// null when trying to exit the player. This prevents a crash in that scenario
|
|
140
|
+
return !(R.path(srcPath, this.props) && !R.path(srcPath, nextProps));
|
|
141
|
+
}
|
|
95
142
|
|
|
96
|
-
|
|
97
|
-
|
|
143
|
+
componentDidUpdate(prevProps) {
|
|
144
|
+
if (
|
|
145
|
+
this.props.castAction?.state?.connected &&
|
|
146
|
+
!this.state.isCastConnected
|
|
147
|
+
) {
|
|
148
|
+
this.stopBackgroundPlayback();
|
|
149
|
+
this.setState({ isCastConnected: true });
|
|
150
|
+
}
|
|
98
151
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
},
|
|
105
|
-
};
|
|
152
|
+
if (
|
|
153
|
+
!this.props.castAction?.state?.connected &&
|
|
154
|
+
this.state.isCastConnected
|
|
155
|
+
) {
|
|
156
|
+
this.setState({ isCastConnected: false });
|
|
106
157
|
}
|
|
107
158
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
keySystems,
|
|
113
|
-
},
|
|
114
|
-
]);
|
|
159
|
+
if (prevProps.entry?.content?.src !== this.props.entry?.content?.src) {
|
|
160
|
+
this.stopBackgroundPlayback();
|
|
161
|
+
this.playbackStarted = false;
|
|
162
|
+
}
|
|
115
163
|
}
|
|
116
164
|
|
|
165
|
+
// For close button
|
|
166
|
+
public closeNativePlayer = () => {
|
|
167
|
+
this.stopBackgroundPlayback();
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
getPlayNextData = () => this.props.playNextData as PlayNextData;
|
|
171
|
+
|
|
117
172
|
/**
|
|
118
173
|
* Assigns root / ref of the player.
|
|
119
174
|
*/
|
|
@@ -121,18 +176,953 @@ export default class VideoPlayer extends React.Component<
|
|
|
121
176
|
this._root = component;
|
|
122
177
|
};
|
|
123
178
|
|
|
179
|
+
getPluginConfiguration = () => {
|
|
180
|
+
return this.props.pluginConfiguration;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* This method is for the events that have no implementation in react but are being invoked by native
|
|
185
|
+
* When we get the synthetic event we need to pull the data in the nativeEvent object
|
|
186
|
+
* nativeEvent contains the return value from native
|
|
187
|
+
* Value could be null if nothing return
|
|
188
|
+
*/
|
|
189
|
+
unHandledEvent = (eventName) => {
|
|
190
|
+
return function dispatchUnhandledEvent({ nativeEvent = null }) {
|
|
191
|
+
logger.warning({
|
|
192
|
+
message:
|
|
193
|
+
"You have invoked an event that is being deprecated or unhandled",
|
|
194
|
+
data: { eventName, nativeEvent },
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
isReactNativeMethodsSupported = () =>
|
|
200
|
+
this.props.PlayerComponent === "QuickBrickDefaultPlayerView" ||
|
|
201
|
+
this.props.PlayerComponent === "PlayerModule";
|
|
202
|
+
|
|
203
|
+
pause = () => {
|
|
204
|
+
// TODO: Call player instance controller methods directly
|
|
205
|
+
// this._root was added here because chromecast,
|
|
206
|
+
// is checking player state for the pause
|
|
207
|
+
if (
|
|
208
|
+
this.isReactNativeMethodsSupported() &&
|
|
209
|
+
RNPlayerManager?.[this.playerPluginId]?.pause &&
|
|
210
|
+
this._root
|
|
211
|
+
) {
|
|
212
|
+
if (!this.state.paused) {
|
|
213
|
+
const node = findNodeHandle(this._root);
|
|
214
|
+
|
|
215
|
+
if (!node) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
RNPlayerManager?.[this.playerPluginId]?.pause(node);
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
this.setState({ paused: true });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// User invoked player pause action
|
|
226
|
+
this.needToResumeOnForeground = null;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
play = () => {
|
|
230
|
+
// TODO: Call player instance controller methods directly
|
|
231
|
+
// this._root was added here because chromecast,
|
|
232
|
+
// is checking player state for the pause
|
|
233
|
+
if (
|
|
234
|
+
this.isReactNativeMethodsSupported() &&
|
|
235
|
+
RNPlayerManager?.[this.playerPluginId]?.play &&
|
|
236
|
+
this._root
|
|
237
|
+
) {
|
|
238
|
+
if (this.state.paused) {
|
|
239
|
+
const node = findNodeHandle(this._root);
|
|
240
|
+
|
|
241
|
+
if (!node) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
RNPlayerManager?.[this.playerPluginId]?.play(node);
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
this.setState({ paused: false });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// User invoked player pause action
|
|
252
|
+
this.needToResumeOnForeground = null;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
stopBackgroundPlayback = () => {
|
|
256
|
+
if (
|
|
257
|
+
this.isReactNativeMethodsSupported() &&
|
|
258
|
+
RNPlayerManager?.[this.playerPluginId]?.stopBackgroundPlayback
|
|
259
|
+
) {
|
|
260
|
+
RNPlayerManager?.[this.playerPluginId]?.stopBackgroundPlayback();
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
forward = (seekTime = SEEK_TIME) => {
|
|
265
|
+
const currentTime = this.state.currentTime;
|
|
266
|
+
|
|
267
|
+
const _duration = this.state.isLive
|
|
268
|
+
? this.state?.seekableDuration
|
|
269
|
+
: this.getDuration();
|
|
270
|
+
|
|
271
|
+
const _time = this.state.isLive
|
|
272
|
+
? R.clamp(0, _duration, currentTime - seekTime)
|
|
273
|
+
: R.clamp(0, _duration, currentTime + seekTime);
|
|
274
|
+
|
|
275
|
+
const seekTo = {
|
|
276
|
+
time: _time,
|
|
277
|
+
tolerance: 1,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
this.setState({ seek: seekTo });
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
rewind = (seekTime = SEEK_TIME) => {
|
|
284
|
+
const currentTime = this.state.currentTime;
|
|
285
|
+
|
|
286
|
+
const _duration = this.state.isLive
|
|
287
|
+
? this.state?.seekableDuration
|
|
288
|
+
: this.getDuration();
|
|
289
|
+
|
|
290
|
+
const _time = this.state.isLive
|
|
291
|
+
? R.clamp(0, _duration, currentTime + seekTime)
|
|
292
|
+
: R.clamp(0, _duration, currentTime - seekTime);
|
|
293
|
+
|
|
294
|
+
const seekTo = {
|
|
295
|
+
time: _time,
|
|
296
|
+
tolerance: 1,
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
this.setState({ seek: seekTo });
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
seeking = (time, tolerance = 1) => {
|
|
303
|
+
if (isNaN(time)) {
|
|
304
|
+
logger.error({
|
|
305
|
+
message: `Specified time is not a number time: ${time}`,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// This hack was added, if send same value to seek two times like 0,
|
|
312
|
+
// It will not property change callback to native.
|
|
313
|
+
// TODO: This func must be refactored to View React method.
|
|
314
|
+
if (time === this.state.seek.time) {
|
|
315
|
+
this.setState({ seek: {} }, () => {
|
|
316
|
+
this.setState({ seek: { time, tolerance } });
|
|
317
|
+
});
|
|
318
|
+
} else {
|
|
319
|
+
this.setState({ seek: { time, tolerance } });
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
selectTrack = (selected) => {
|
|
324
|
+
const { trackType, id, language } = selected || {};
|
|
325
|
+
|
|
326
|
+
const isOff = language === "Off";
|
|
327
|
+
|
|
328
|
+
if (!id) {
|
|
329
|
+
logger.error({
|
|
330
|
+
message:
|
|
331
|
+
"Failed to set Audio or Text track. Language track is missing required parameter ID",
|
|
332
|
+
data: { selected },
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
switch (trackType) {
|
|
339
|
+
case "text":
|
|
340
|
+
this.setState({
|
|
341
|
+
textTrackId: isOff ? null : id,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
break;
|
|
345
|
+
case "audio":
|
|
346
|
+
this.setState({
|
|
347
|
+
audioTrackId: id,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
setPlaybackRate = (rate = 1.0) => {
|
|
355
|
+
this.setState({ rate });
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
getCurrentTime = () => {
|
|
359
|
+
return this.state.currentTime;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
getDuration = () => {
|
|
363
|
+
return this.state.duration;
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
getState = () => {
|
|
367
|
+
return { ...this.state, fullscreen: Boolean(this.props?.fullscreen) };
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Handles key events transmitted by the native
|
|
372
|
+
* Adds the key event to the playerState so that it can be consumed by interaction manager
|
|
373
|
+
* Needs for Android
|
|
374
|
+
*/
|
|
375
|
+
onKeyDown = (event) => {
|
|
376
|
+
this.setState({
|
|
377
|
+
keyEvent: event,
|
|
378
|
+
});
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
onPlayerPresented = () => {
|
|
382
|
+
this.analyticsTracker?.handleAnalyticEvent(GENERAL_EVENT.player_presented);
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
onPlayerClosed = () => {
|
|
386
|
+
this.analyticsTracker?.handleAnalyticEvent(GENERAL_EVENT.player_closed);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
onVideoEnd = () => {
|
|
390
|
+
this.setState({ displayOverlay: !this.state.displayOverlay }, () => {
|
|
391
|
+
if (this.state?.displayOverlay) {
|
|
392
|
+
this.props?.listener?.onVideoEnd({});
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// TODO: Should be unified on both platform
|
|
398
|
+
onVideoError = ({ nativeEvent }) => {
|
|
399
|
+
const { message } = nativeEvent;
|
|
400
|
+
|
|
401
|
+
return this.props?.listener?.onError(new Error(message));
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
onPlayerPause = ({ nativeEvent = null }) => {
|
|
405
|
+
this.setState({ paused: true });
|
|
406
|
+
this.props?.listener?.onPlayerPause(nativeEvent);
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
onPlayerResume = ({ nativeEvent = null }) => {
|
|
410
|
+
const { isAd, resume } = this.state;
|
|
411
|
+
const isNotAnAd = !isAd;
|
|
412
|
+
this.setState({ paused: false });
|
|
413
|
+
this.props?.listener?.onPlayerResume?.({ nativeEvent });
|
|
414
|
+
|
|
415
|
+
if (isNotAnAd && !resume) {
|
|
416
|
+
this.setState({ resume: true });
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
onPlayerSeekComplete = ({ nativeEvent }) => {
|
|
421
|
+
this.props?.listener?.onPlayerSeekComplete(nativeEvent);
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
onPlayerSeekStart = ({ nativeEvent }) => {
|
|
425
|
+
this.props?.listener?.onPlayerSeekStart(nativeEvent);
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
onBufferStart = ({ nativeEvent }) => {
|
|
429
|
+
this.props?.listener?.onBufferStart(nativeEvent);
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
onBufferComplete = ({ nativeEvent }) => {
|
|
433
|
+
this.props?.listener?.onBufferComplete(nativeEvent);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
onAdBegin = ({ nativeEvent }) => {
|
|
437
|
+
this.setState({ isAd: true });
|
|
438
|
+
this.props.listener?.onAdBegin?.(nativeEvent);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
onAdBreakBegin = ({ nativeEvent }) => {
|
|
442
|
+
this.setState({ isAd: true });
|
|
443
|
+
this.props.listener?.onAdBreakBegin?.(nativeEvent);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
onAdBreakEnd = ({ nativeEvent }) => {
|
|
447
|
+
this.setState({ isAd: false });
|
|
448
|
+
this.props.listener?.onAdBreakEnd?.(nativeEvent);
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
onAdEnd = ({ nativeEvent }) => {
|
|
452
|
+
this.setState({ isAd: false });
|
|
453
|
+
this.props.listener?.onAdEnd?.(nativeEvent);
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
onAdError = ({ nativeEvent }) => {
|
|
457
|
+
this.setState({ isAd: false });
|
|
458
|
+
this.props.listener?.onAdError?.(nativeEvent);
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
onAdRequest = ({ nativeEvent }) => {
|
|
462
|
+
this.props.listener?.onAdRequest?.(nativeEvent);
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
onAdClicked = ({ nativeEvent }) => {
|
|
466
|
+
// Broadcast click event
|
|
467
|
+
this.props.playerEvent("ad-clicked", nativeEvent);
|
|
468
|
+
this.props.listener?.onAdClicked?.(nativeEvent);
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
onAdTapped = ({ nativeEvent }) => {
|
|
472
|
+
// Broadcast click event
|
|
473
|
+
this.props.playerEvent("ad-tapped", nativeEvent);
|
|
474
|
+
this.props.listener?.onAdTapped?.(nativeEvent);
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
onPlaybackStalled = () => {
|
|
478
|
+
return this.props?.updatePlayerState?.("stalled");
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
onPlayerAttached = ({ nativeEvent }) => {
|
|
482
|
+
this.props.listener?.onPlayerAttached?.(nativeEvent);
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
onPlayerDetached = ({ nativeEvent }) => {
|
|
486
|
+
this.props.listener?.onPlayerDetached?.(nativeEvent);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
onVideoFullscreenPlayerDidDismiss = () => {
|
|
490
|
+
this.props.listener?.onVideoFullscreenPlayerDidDismiss?.();
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
onVideoFullscreenPlayerDidPresent = () => {
|
|
494
|
+
this.props.listener?.onVideoFullscreenPlayerDidPresent?.();
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
onVideoFullscreenPlayerWillDismiss = () => {
|
|
498
|
+
this.props.listener?.onVideoFullscreenPlayerWillDismiss?.();
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
onVideoFullscreenPlayerWillPresent = () => {
|
|
502
|
+
this.props.listener?.onVideoFullscreenPlayerWillPresent?.();
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
onPlayerClose = () => {
|
|
506
|
+
this.props.listener?.onPlayerClose?.();
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
onPiPEnter = () => {
|
|
510
|
+
this.setState({ piped: true });
|
|
511
|
+
this.props.openPiP();
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
onPiPExit = () => {
|
|
515
|
+
this.setState({ piped: false });
|
|
516
|
+
this.props.closePiP();
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
onPiPCloseByUser = () => {
|
|
520
|
+
if (this.state.piped) {
|
|
521
|
+
const node = findNodeHandle(this._root);
|
|
522
|
+
|
|
523
|
+
if (!node) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
playerManager.close();
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
disablePiP = () => {
|
|
532
|
+
const node = findNodeHandle(this._root);
|
|
533
|
+
|
|
534
|
+
if (!node) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
RNPlayerManager?.[this.playerPluginId]?.disablePiP?.(node);
|
|
539
|
+
// TODO: Remove after implementing play next for PiP
|
|
540
|
+
RNPlayerManager?.[this.playerPluginId]?.closePiP?.(node);
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
onVideoBuffer = ({ nativeEvent }) => {
|
|
544
|
+
// video buffer event is invoked from native side when asking the player to pause
|
|
545
|
+
// this causes the buffer animation to trigger and incorrectly request to update state
|
|
546
|
+
if (this.state.paused) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// TODO: This events must be called from native directly
|
|
551
|
+
if (nativeEvent?.isBuffering) {
|
|
552
|
+
this.onBufferStart({ nativeEvent });
|
|
553
|
+
} else {
|
|
554
|
+
this.onBufferComplete({ nativeEvent });
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return this.props?.updatePlayerState?.("loadstart");
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
onVideoLoad = ({ nativeEvent }) => {
|
|
561
|
+
const duration = nativeEvent.duration;
|
|
562
|
+
const currentTime = nativeEvent.currentTime;
|
|
563
|
+
|
|
564
|
+
// check if content is loaded
|
|
565
|
+
if (duration < 0) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
this.setState(
|
|
570
|
+
{
|
|
571
|
+
currentTime,
|
|
572
|
+
duration,
|
|
573
|
+
},
|
|
574
|
+
() => {
|
|
575
|
+
this.props?.listener?.onVideoLoad(nativeEvent);
|
|
576
|
+
|
|
577
|
+
if (!this.playbackStarted) {
|
|
578
|
+
this.playbackStarted = true;
|
|
579
|
+
// TODO: Remove not needed anymore, implemented on player.tsx
|
|
580
|
+
this.props?.listener?.onLoad(nativeEvent);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (!this.state.isAd && !this.onVideoLoadCalled) {
|
|
584
|
+
// TODO: Hack, needs to remove
|
|
585
|
+
this.onVideoLoadCalled = true;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
);
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
onTracksChanged = ({ nativeEvent }) => {
|
|
592
|
+
const { textTracks, audioTracks, audioTrackId, textTrackId } =
|
|
593
|
+
parseLanguageTracks(nativeEvent);
|
|
594
|
+
|
|
595
|
+
this.setState(
|
|
596
|
+
{
|
|
597
|
+
audioTrackId,
|
|
598
|
+
textTrackId,
|
|
599
|
+
},
|
|
600
|
+
() => {
|
|
601
|
+
this.props?.listener?.onTracksChanged?.({
|
|
602
|
+
textTracks,
|
|
603
|
+
audioTracks,
|
|
604
|
+
audioTrackId,
|
|
605
|
+
textTrackId,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
);
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
onVideoProgress = ({ nativeEvent }) => {
|
|
612
|
+
const duration = nativeEvent.duration;
|
|
613
|
+
const currentTime = nativeEvent.currentTime;
|
|
614
|
+
const seekableDuration = nativeEvent.seekableDuration;
|
|
615
|
+
const isLive = nativeEvent.isLive;
|
|
616
|
+
|
|
617
|
+
// TODO: Delete states on view
|
|
618
|
+
this.setState({
|
|
619
|
+
currentTime,
|
|
620
|
+
duration,
|
|
621
|
+
resume: false,
|
|
622
|
+
seekableDuration,
|
|
623
|
+
isLive,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
this.props?.listener?.onVideoProgress({
|
|
627
|
+
currentTime,
|
|
628
|
+
duration,
|
|
629
|
+
item: this.props?.entry,
|
|
630
|
+
seekableDuration,
|
|
631
|
+
isLive,
|
|
632
|
+
});
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
onPlaybackRateChange = ({ nativeEvent }) => {
|
|
636
|
+
if (nativeEvent.playbackRate !== this.state.rate) {
|
|
637
|
+
this.setState({ rate: nativeEvent.playbackRate });
|
|
638
|
+
|
|
639
|
+
if (this.props?.listener?.onPlaybackRateChange) {
|
|
640
|
+
this.props?.listener?.onPlaybackRateChange(nativeEvent);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (nativeEvent?.playbackRate > 0) {
|
|
644
|
+
if (this.props?.listener?.onPlayerResume) {
|
|
645
|
+
this.props?.listener?.onPlayerResume(nativeEvent);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
appStateChange = (appState, previousAppState) => {
|
|
652
|
+
const { pluginConfiguration } = this.props;
|
|
653
|
+
const shouldPauseOnExit = pluginConfiguration?.nowPlayingEnabled !== true;
|
|
654
|
+
|
|
655
|
+
// TODO: This const must be imported from upstate change of TS
|
|
656
|
+
const ACTIVE = "active";
|
|
657
|
+
const BACKGROUND = "background";
|
|
658
|
+
const INACTIVE = "inactive";
|
|
659
|
+
|
|
660
|
+
if (
|
|
661
|
+
previousAppState === ACTIVE &&
|
|
662
|
+
appState &&
|
|
663
|
+
(appState === BACKGROUND || appState === INACTIVE)
|
|
664
|
+
) {
|
|
665
|
+
if ((isTV() || shouldPauseOnExit) && this.props.mode !== "PIP") {
|
|
666
|
+
const wasPlaying = !this.state.paused;
|
|
667
|
+
this.pause();
|
|
668
|
+
this.needToResumeOnForeground = wasPlaying;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (appState && appState === ACTIVE && previousAppState !== ACTIVE) {
|
|
673
|
+
if (this.needToResumeOnForeground) {
|
|
674
|
+
this.play();
|
|
675
|
+
this.needToResumeOnForeground = null;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
childrenWithPlayNext = () => {
|
|
681
|
+
if (!isTvOSPlatform() || !this.getPlayNextData()) {
|
|
682
|
+
return this.props.children;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// tvos only
|
|
686
|
+
const { docked, entry } = this.props;
|
|
687
|
+
|
|
688
|
+
const children = R.pathOr([], "children", this.props);
|
|
689
|
+
|
|
690
|
+
return [
|
|
691
|
+
...children,
|
|
692
|
+
<PlayNextOverlay
|
|
693
|
+
key="play-next-overlay"
|
|
694
|
+
item={entry}
|
|
695
|
+
playerIsDocked={docked}
|
|
696
|
+
pluginConfiguration={this.getPluginConfiguration()}
|
|
697
|
+
playerId={this.props.playerId}
|
|
698
|
+
playNextData={this.getPlayNextData()}
|
|
699
|
+
setNextVideoPreloadThresholdPercentage={
|
|
700
|
+
this.props.setNextVideoPreloadThresholdPercentage
|
|
701
|
+
}
|
|
702
|
+
/>,
|
|
703
|
+
];
|
|
704
|
+
};
|
|
705
|
+
|
|
124
706
|
render() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
707
|
+
const {
|
|
708
|
+
docked,
|
|
709
|
+
entry,
|
|
710
|
+
fullscreen,
|
|
711
|
+
hideTransportControls,
|
|
712
|
+
inline,
|
|
713
|
+
isModal,
|
|
714
|
+
isTabletPortrait,
|
|
715
|
+
muted,
|
|
716
|
+
style,
|
|
717
|
+
castAction,
|
|
718
|
+
controller,
|
|
719
|
+
startComponentsAnimation,
|
|
720
|
+
} = this.props;
|
|
721
|
+
|
|
722
|
+
const supportNativeCast = controller.supportNativeCast();
|
|
723
|
+
|
|
724
|
+
const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
|
|
725
|
+
this.state;
|
|
726
|
+
|
|
727
|
+
const isAudioItem =
|
|
728
|
+
entry?.content?.type?.includes?.("audio") ||
|
|
729
|
+
entry?.type?.value === "audio";
|
|
730
|
+
|
|
731
|
+
const getStyle = (dimensions) => {
|
|
732
|
+
const generalStyles = isAudioItem ? styles.audio : styles.video;
|
|
733
|
+
|
|
734
|
+
if (isAndroidPlatform() && isAudioItem) {
|
|
735
|
+
return generalStyles;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return { ...generalStyles, ...dimensions };
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
const getBackgroundImageStyle = (dimensions): any =>
|
|
742
|
+
Object.assign(
|
|
743
|
+
{},
|
|
744
|
+
isAudioItem ? styles.audio : { position: "absolute" },
|
|
745
|
+
dimensions
|
|
746
|
+
);
|
|
747
|
+
|
|
748
|
+
const nativeResizeMode = "ScaleAspectFit";
|
|
749
|
+
|
|
750
|
+
const nowPlayingEnabled =
|
|
751
|
+
this.props?.pluginConfiguration?.nowPlayingEnabled;
|
|
752
|
+
|
|
753
|
+
const enableAutoAudioTrackSelection =
|
|
754
|
+
this.props.pluginConfiguration?.enable_auto_audio_track_selection;
|
|
755
|
+
|
|
756
|
+
const minimumAllowedSeekableDurationInSeconds =
|
|
757
|
+
this.props?.pluginConfiguration?.minimumAllowedSeekableDurationInSeconds;
|
|
758
|
+
// In case no seeking duration or it less than 10 min we do not want to enable live seeking feature
|
|
759
|
+
|
|
760
|
+
const liveSeekingEnabled =
|
|
761
|
+
(this.props.pluginConfiguration?.liveSeekingEnabled &&
|
|
762
|
+
this.state?.seekableDuration >
|
|
763
|
+
minimumAllowedSeekableDurationInSeconds) ||
|
|
764
|
+
false;
|
|
765
|
+
|
|
766
|
+
// keep undefined if not present, since not all native players have the property
|
|
767
|
+
const liveCatchUpEnabled =
|
|
768
|
+
this.props.pluginConfiguration?.liveCatchUpEnabled;
|
|
769
|
+
|
|
770
|
+
// TODO: Get screen data more reliable way useCurrentScreenData
|
|
771
|
+
const screenData = getTargetScreenDataFromEntry(this.props.playableItem);
|
|
772
|
+
|
|
773
|
+
const isInlineTV = screenData?.ui_components?.length > 0 && isTV();
|
|
774
|
+
|
|
775
|
+
let platformSpecificProps: any = isApplePlatform()
|
|
776
|
+
? {
|
|
777
|
+
resizeMode: nativeResizeMode,
|
|
778
|
+
}
|
|
779
|
+
: {};
|
|
780
|
+
|
|
781
|
+
const supportsNativeControls =
|
|
782
|
+
!!this.props.controller?.supportsNativeControls?.();
|
|
783
|
+
|
|
784
|
+
const controls = isAppleTV()
|
|
785
|
+
? (supportsNativeControls && !isInlineTV) || !!this.getPlayNextData()
|
|
786
|
+
: supportsNativeControls && !this.getPlayNextData();
|
|
787
|
+
|
|
788
|
+
const shouldUseNativeControls =
|
|
789
|
+
controls &&
|
|
790
|
+
!startComponentsAnimation &&
|
|
791
|
+
!docked &&
|
|
792
|
+
!castAction?.state?.connected;
|
|
793
|
+
|
|
794
|
+
platformSpecificProps = {
|
|
795
|
+
...platformSpecificProps,
|
|
796
|
+
controls: shouldUseNativeControls,
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
const pictureInPictureEnabled =
|
|
800
|
+
this.props.pluginConfiguration?.pictureInPictureEnabled;
|
|
801
|
+
|
|
802
|
+
const nativeProps = {
|
|
803
|
+
entry, // Must be passed first in list
|
|
804
|
+
...platformSpecificProps,
|
|
805
|
+
audioTrackId,
|
|
806
|
+
docked,
|
|
807
|
+
enableAutoAudioTrackSelection,
|
|
808
|
+
fullscreen, // TODO: Check fullscreen params not crash
|
|
809
|
+
inline,
|
|
810
|
+
muted,
|
|
811
|
+
nowPlayingEnabled,
|
|
812
|
+
paused,
|
|
813
|
+
seek,
|
|
814
|
+
rate,
|
|
815
|
+
textTrackId,
|
|
816
|
+
liveSeekingEnabled,
|
|
817
|
+
liveCatchUpEnabled,
|
|
818
|
+
playerId: this.props.playerId,
|
|
819
|
+
pictureInPictureEnabled,
|
|
820
|
+
seekStep:
|
|
821
|
+
(this.props.pluginConfiguration?.seek_duration || SEEK_TIME) * 1000, // Only for Android
|
|
822
|
+
accessibilityProps: getAllAccessibilityProps(
|
|
823
|
+
this.getPluginConfiguration()
|
|
824
|
+
),
|
|
825
|
+
}; // rate temporary removed, because of bug with subtitles
|
|
826
|
+
|
|
827
|
+
if (
|
|
828
|
+
this.isReactNativeMethodsSupported() &&
|
|
829
|
+
RNPlayerManager?.[this.playerPluginId]?.play
|
|
830
|
+
) {
|
|
831
|
+
delete nativeProps["paused"];
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const adEvents = {
|
|
835
|
+
onAdBegin: this.onAdBegin, // ad_start
|
|
836
|
+
onAdBreakBegin: this.onAdBreakBegin, // ad_break_start
|
|
837
|
+
onAdBreakEnd: this.onAdBreakEnd, // ad_break_completed
|
|
838
|
+
onAdEnd: this.onAdEnd, // ad_complete
|
|
839
|
+
onAdError: this.onAdError, // ad_error
|
|
840
|
+
onAdRequest: this.onAdRequest, // ad_request
|
|
841
|
+
onAdClicked: this.onAdClicked, // ad_clicked
|
|
842
|
+
onAdTapped: this.onAdTapped, // ad_tapped
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
const playbackEvents = {
|
|
846
|
+
onPlaybackRateChange: this.onPlaybackRateChange,
|
|
847
|
+
onPlayerPause: this.onPlayerPause, // pause
|
|
848
|
+
onPlayerResume: this.onPlayerResume, // play
|
|
849
|
+
onPlayerSeekComplete: this.onPlayerSeekComplete, // seek_complete
|
|
850
|
+
onPlayerSeekStart: this.onPlayerSeekStart, // seek_start
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
const lifecycleEvents = {
|
|
854
|
+
onReadyForDisplay: this.unHandledEvent("onReadyForDisplay"),
|
|
855
|
+
onVideoEnd: this.onVideoEnd, // complete
|
|
856
|
+
onVideoError: this.onVideoError, // error
|
|
857
|
+
onVideoProgress: this.onVideoProgress,
|
|
858
|
+
onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
|
|
859
|
+
onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
|
|
860
|
+
onVideoFullscreenPlayerWillDismiss:
|
|
861
|
+
this.onVideoFullscreenPlayerWillDismiss,
|
|
862
|
+
onVideoFullscreenPlayerWillPresent:
|
|
863
|
+
this.onVideoFullscreenPlayerWillPresent,
|
|
864
|
+
onVideoLoad: this.onVideoLoad,
|
|
865
|
+
onVideoLoadStart: this.unHandledEvent("onVideoLoadStart"),
|
|
866
|
+
onVideoBuffer: this.onVideoBuffer,
|
|
867
|
+
onPlaybackStalled: this.onPlaybackStalled,
|
|
868
|
+
onPlayerClose: this.onPlayerClose,
|
|
869
|
+
onPlayerAttached: this.onPlayerAttached,
|
|
870
|
+
onPlayerDetached: this.onPlayerDetached,
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
const androidLifecyclePiPEvents = isAndroidPlatform() && {
|
|
874
|
+
onPiPEnter: this.onPiPEnter,
|
|
875
|
+
onPiPExit: this.onPiPExit,
|
|
876
|
+
onPiPCloseByUser: this.onPiPCloseByUser,
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
const playerMetadataChangedEvents = {
|
|
880
|
+
onTimedMetadata: this.unHandledEvent("onTimedMetadata"),
|
|
881
|
+
onTracksChanged: this.onTracksChanged,
|
|
882
|
+
eventSubtitlesChange: this.unHandledEvent("eventSubtitlesChange"),
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
const playerSystemEvents = {
|
|
886
|
+
onAudioFocusChanged: this.unHandledEvent("onAudioFocusChanged"),
|
|
887
|
+
onVideoExternalPlaybackChange: this.unHandledEvent(
|
|
888
|
+
"onVideoExternalPlaybackChange"
|
|
889
|
+
),
|
|
890
|
+
onVideoAudioBecomingNoisy: this.unHandledEvent(
|
|
891
|
+
"onVideoAudioBecomingNoisy"
|
|
892
|
+
),
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
const androidPlayerEvents = {
|
|
896
|
+
onKeyChanged: keyEvent,
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
const unsupportedEvents = {
|
|
900
|
+
onBufferComplete: this.onBufferComplete, // buffer_complete
|
|
901
|
+
onBufferStart: this.onBufferStart, // buffer_start
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
const nativeEvents = {
|
|
905
|
+
...adEvents,
|
|
906
|
+
...androidPlayerEvents,
|
|
907
|
+
...lifecycleEvents,
|
|
908
|
+
...playerMetadataChangedEvents,
|
|
909
|
+
...playerSystemEvents,
|
|
910
|
+
...unsupportedEvents,
|
|
911
|
+
...playbackEvents,
|
|
912
|
+
...androidLifecyclePiPEvents,
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
const needToShowPlayNextOverlay =
|
|
916
|
+
isTV() && this.getPlayNextData() && !docked;
|
|
917
|
+
|
|
918
|
+
const needToShowDefaultControls = !(
|
|
919
|
+
(controls && !castAction?.state?.connected) ||
|
|
920
|
+
this.getPlayNextData()
|
|
136
921
|
);
|
|
922
|
+
|
|
923
|
+
const playerContainerStyles = isApplePlatform()
|
|
924
|
+
? styles.playerContainerBlack
|
|
925
|
+
: isTV() && isAudioItem
|
|
926
|
+
? styles.playerContainerTransparent
|
|
927
|
+
: styles.playerContainerBlack;
|
|
928
|
+
|
|
929
|
+
const isInline = () => {
|
|
930
|
+
if (isTV()) {
|
|
931
|
+
return inline;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// HACK: on mobile overwrite inline value if player is fullScreen
|
|
935
|
+
return fullscreen ? false : inline;
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
const renderTransportControls = (playerDimensions) => {
|
|
939
|
+
const needsToRender =
|
|
940
|
+
!hideTransportControls &&
|
|
941
|
+
!needToShowPlayNextOverlay &&
|
|
942
|
+
this.props.mode !== "PIP";
|
|
943
|
+
|
|
944
|
+
if (shouldUseNativeControls) {
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
const TransportControls = isTV()
|
|
949
|
+
? TransportControlsTV
|
|
950
|
+
: TransportControlsMobile;
|
|
951
|
+
|
|
952
|
+
return needsToRender ? (
|
|
953
|
+
<TransportControls
|
|
954
|
+
playerContent={entry}
|
|
955
|
+
configuration={this.getPluginConfiguration()}
|
|
956
|
+
playerDimensions={playerDimensions}
|
|
957
|
+
inline={isInline()}
|
|
958
|
+
docked={fullscreen ? false : docked}
|
|
959
|
+
isModal={isModal}
|
|
960
|
+
extraInfoText={castAction?.state?.friendlyStatus}
|
|
961
|
+
controlsAlwaysVisible={castAction?.state?.connected}
|
|
962
|
+
playNextData={this.getPlayNextData()}
|
|
963
|
+
needToShowDefaultControls={needToShowDefaultControls}
|
|
964
|
+
/>
|
|
965
|
+
) : null;
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const isFullScreenAudioPlayer =
|
|
969
|
+
this.getPluginConfiguration()?.["full_screen_audio_player"] || false;
|
|
970
|
+
|
|
971
|
+
const sharedWrapperViewProps = {
|
|
972
|
+
style,
|
|
973
|
+
entry,
|
|
974
|
+
inline,
|
|
975
|
+
docked,
|
|
976
|
+
isModal,
|
|
977
|
+
pip: this.state.piped,
|
|
978
|
+
layoutState: {
|
|
979
|
+
inline,
|
|
980
|
+
docked,
|
|
981
|
+
isModal,
|
|
982
|
+
pip: this.state.piped,
|
|
983
|
+
},
|
|
984
|
+
configuration: this.getPluginConfiguration(),
|
|
985
|
+
fullscreen: fullscreen,
|
|
986
|
+
isTabletPortrait: isTabletPortrait,
|
|
987
|
+
playNextData: this.getPlayNextData(),
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
const containerStyle = { flex: 1 };
|
|
991
|
+
|
|
992
|
+
const backgroundImageKey =
|
|
993
|
+
this.getPluginConfiguration()?.player_preview_image_key;
|
|
994
|
+
|
|
995
|
+
const renderMobilePlayer = () => {
|
|
996
|
+
return isFullScreenAudioPlayer && isAudioItem ? (
|
|
997
|
+
<View style={containerStyle}>
|
|
998
|
+
{(!castAction?.state?.connected || supportNativeCast) && (
|
|
999
|
+
<this.props.PlayerComponent
|
|
1000
|
+
ref={this._assignRoot}
|
|
1001
|
+
{...nativeEvents}
|
|
1002
|
+
{...nativeProps}
|
|
1003
|
+
/>
|
|
1004
|
+
)}
|
|
1005
|
+
<AudioPlayerWrapper {...sharedWrapperViewProps} />
|
|
1006
|
+
</View>
|
|
1007
|
+
) : (
|
|
1008
|
+
<PlayerWrapper {...sharedWrapperViewProps}>
|
|
1009
|
+
{(playerDimensions) => {
|
|
1010
|
+
const shouldRenderBackground =
|
|
1011
|
+
!R.isNil(backgroundImageKey) &&
|
|
1012
|
+
backgroundImageKey.length > 0 &&
|
|
1013
|
+
!this.onVideoLoadCalled &&
|
|
1014
|
+
!this.state.isAd;
|
|
1015
|
+
|
|
1016
|
+
return (
|
|
1017
|
+
<View style={styles.container}>
|
|
1018
|
+
<View style={[playerContainerStyles, playerDimensions]}>
|
|
1019
|
+
{isAudioItem && (
|
|
1020
|
+
<AnimationComponent
|
|
1021
|
+
animationType={ComponentAnimationType.player}
|
|
1022
|
+
additionalData={{
|
|
1023
|
+
...playerDimensions,
|
|
1024
|
+
inlineAudioPlayer: true,
|
|
1025
|
+
}}
|
|
1026
|
+
style={getStyle(playerDimensions)}
|
|
1027
|
+
>
|
|
1028
|
+
<AudioPlayer
|
|
1029
|
+
style={
|
|
1030
|
+
isTV()
|
|
1031
|
+
? getStyle(playerDimensions)
|
|
1032
|
+
: styles.playerSize
|
|
1033
|
+
}
|
|
1034
|
+
audio_item={entry}
|
|
1035
|
+
plugin_configuration={this.getPluginConfiguration()}
|
|
1036
|
+
/>
|
|
1037
|
+
</AnimationComponent>
|
|
1038
|
+
)}
|
|
1039
|
+
|
|
1040
|
+
{shouldRenderBackground && (
|
|
1041
|
+
<PlayerImageBackground
|
|
1042
|
+
imageKey={backgroundImageKey}
|
|
1043
|
+
configuration={this.getPluginConfiguration()}
|
|
1044
|
+
entry={entry}
|
|
1045
|
+
style={getBackgroundImageStyle(playerDimensions)}
|
|
1046
|
+
/>
|
|
1047
|
+
)}
|
|
1048
|
+
|
|
1049
|
+
{(!castAction?.state?.connected || supportNativeCast) && (
|
|
1050
|
+
<AnimationComponent
|
|
1051
|
+
animationType={ComponentAnimationType.player}
|
|
1052
|
+
additionalData={playerDimensions}
|
|
1053
|
+
style={getStyle(playerDimensions)}
|
|
1054
|
+
>
|
|
1055
|
+
<this.props.PlayerComponent
|
|
1056
|
+
ref={this._assignRoot}
|
|
1057
|
+
{...nativeEvents}
|
|
1058
|
+
{...nativeProps}
|
|
1059
|
+
listener={this.props.listener}
|
|
1060
|
+
style={
|
|
1061
|
+
isTV()
|
|
1062
|
+
? getStyle(playerDimensions)
|
|
1063
|
+
: styles.playerSize
|
|
1064
|
+
}
|
|
1065
|
+
pluginConfiguration={this.getPluginConfiguration()}
|
|
1066
|
+
controller={this.props.controller}
|
|
1067
|
+
>
|
|
1068
|
+
{this.childrenWithPlayNext()}
|
|
1069
|
+
</this.props.PlayerComponent>
|
|
1070
|
+
</AnimationComponent>
|
|
1071
|
+
)}
|
|
1072
|
+
</View>
|
|
1073
|
+
|
|
1074
|
+
{renderTransportControls(playerDimensions)}
|
|
1075
|
+
{!this.state.piped && (
|
|
1076
|
+
<PlayNextOverlay
|
|
1077
|
+
item={entry}
|
|
1078
|
+
playerIsDocked={docked}
|
|
1079
|
+
pluginConfiguration={this.getPluginConfiguration()}
|
|
1080
|
+
playerId={this.props.playerId}
|
|
1081
|
+
playNextData={this.getPlayNextData()}
|
|
1082
|
+
setNextVideoPreloadThresholdPercentage={
|
|
1083
|
+
this.props.setNextVideoPreloadThresholdPercentage
|
|
1084
|
+
}
|
|
1085
|
+
/>
|
|
1086
|
+
)}
|
|
1087
|
+
</View>
|
|
1088
|
+
);
|
|
1089
|
+
}}
|
|
1090
|
+
</PlayerWrapper>
|
|
1091
|
+
);
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
const renderTVPlayer = () => {
|
|
1095
|
+
return (
|
|
1096
|
+
<View style={this.takeParentStyle} testID={"player-view"}>
|
|
1097
|
+
<this.props.PlayerComponent
|
|
1098
|
+
ref={this._assignRoot}
|
|
1099
|
+
{...nativeEvents}
|
|
1100
|
+
{...nativeProps}
|
|
1101
|
+
listener={this.props.listener}
|
|
1102
|
+
style={this.takeParentStyle}
|
|
1103
|
+
pluginConfiguration={this.getPluginConfiguration()}
|
|
1104
|
+
controller={this.props.controller}
|
|
1105
|
+
>
|
|
1106
|
+
{this.childrenWithPlayNext()}
|
|
1107
|
+
</this.props.PlayerComponent>
|
|
1108
|
+
|
|
1109
|
+
{renderTransportControls(this.takeParentStyle)}
|
|
1110
|
+
{!isTvOSPlatform() && (
|
|
1111
|
+
<PlayNextOverlay
|
|
1112
|
+
item={entry}
|
|
1113
|
+
playerIsDocked={docked}
|
|
1114
|
+
pluginConfiguration={this.getPluginConfiguration()}
|
|
1115
|
+
playerId={this.props.playerId}
|
|
1116
|
+
playNextData={this.getPlayNextData()}
|
|
1117
|
+
setNextVideoPreloadThresholdPercentage={
|
|
1118
|
+
this.props.setNextVideoPreloadThresholdPercentage
|
|
1119
|
+
}
|
|
1120
|
+
/>
|
|
1121
|
+
)}
|
|
1122
|
+
</View>
|
|
1123
|
+
);
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
return isTV() ? renderTVPlayer() : renderMobilePlayer();
|
|
137
1127
|
}
|
|
138
1128
|
}
|