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