@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.
Files changed (2) hide show
  1. package/package.json +6 -6
  2. 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.4584840011",
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.96",
40
- "@applicaster/quick-brick-tv-transport-controls": "12.0.0-rc.100",
41
- "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.4584840011",
42
- "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.4584840011",
43
- "@applicaster/zapp-react-native-utils": "12.0.0-alpha.4584840011",
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",
@@ -2,78 +2,32 @@
2
2
  /* eslint-disable react-native/no-color-literals */
3
3
 
4
4
  import React from "react";
5
- import * as R from "ramda";
5
+ import videojs from "video.js";
6
6
 
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";
7
+ import "video.js/dist/video-js.css";
8
+ import "videojs-contrib-eme";
10
9
 
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";
10
+ import { View } from "react-native";
11
+ import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
19
12
 
20
- import { PlayerWrapper } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal";
13
+ const STREAM_TYPES = {
14
+ m3u8: "application/x-mpegURL",
15
+ mpd: "application/dash+xml",
16
+ };
21
17
 
22
- import { PlayNextOverlay } from "./PlayNextOverlay";
23
- import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
24
- import { AnalyticsTracker } from "@applicaster/zapp-react-native-utils/analyticsUtils/analyticsTracker";
18
+ const getStreamType = (url) => {
19
+ const extension = url.split(".").pop();
25
20
 
26
- import {
27
- AD_EVENT,
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
- this.playbackStarted = false;
126
- this.onPlayerPresented();
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
- shouldComponentUpdate(nextProps) {
130
- const srcPath = ["entry", "content", "src"];
92
+ const player = videojs(this._root, options);
131
93
 
132
- // when using vods from applicaster2, `entry.content.src` might be
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
- componentDidUpdate(prevProps) {
138
- if (
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
- if (
147
- !this.props.castAction?.state?.connected &&
148
- this.state.isCastConnected
149
- ) {
150
- this.setState({ isCastConnected: false });
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
- if (prevProps.entry?.content?.src !== this.props.entry?.content?.src) {
154
- this.stopBackgroundPlayback();
155
- this.playbackStarted = false;
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
- const {
779
- docked,
780
- entry,
781
- fullscreen,
782
- hideTransportControls,
783
- inline,
784
- isModal,
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
- {...nativeEvents}
1177
- {...nativeProps}
1178
- listener={this.props.listener}
1179
- style={this.takeParentStyle}
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
  }