@applicaster/quick-brick-player 12.0.0-alpha.4620237415 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-player",
3
- "version": "12.0.0-alpha.4620237415",
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",
@@ -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.4620237415",
42
- "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.4620237415",
43
- "@applicaster/zapp-react-native-utils": "12.0.0-alpha.4620237415",
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",
47
47
  "shaka-player": "4.3.5",
48
48
  "typeface-montserrat": "^0.0.54",
49
- "video.js": "7.14.3",
50
- "videojs-contrib-eme": "^3.8.1",
51
- "videojs-contrib-quality-levels": "3.0.0"
49
+ "video.js": "8.21.0",
50
+ "videojs-contrib-eme": "^5.5.1",
51
+ "videojs-contrib-quality-levels": "^4.1.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "@applicaster/quick-brick-xray": "*",
@@ -1,4 +1,4 @@
1
- import React, { memo } from "react";
1
+ import React from "react";
2
2
  import { LiveImageManager } from "@applicaster/zapp-react-native-ui-components/Components/VideoLive/LiveImageManager";
3
3
 
4
4
  import { Context } from "./Context";
@@ -86,7 +86,7 @@ const initialEntryState = () => ({
86
86
  });
87
87
 
88
88
  export function ContextProvider(Component: ReactComponent<any>) {
89
- return memo(function DefaultPlayerProvider({
89
+ return function DefaultPlayerProvider({
90
90
  children,
91
91
  }: CellActionContextProviderProps) {
92
92
  const contextValue = React.useMemo(
@@ -104,5 +104,5 @@ export function ContextProvider(Component: ReactComponent<any>) {
104
104
  <Component>{children}</Component>
105
105
  </Context.Provider>
106
106
  );
107
- });
107
+ };
108
108
  }
@@ -2,74 +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";
18
+ const getStreamType = (url) => {
19
+ const extension = url.split(".").pop();
24
20
 
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
- });
21
+ return STREAM_TYPES[extension];
22
+ };
60
23
 
61
24
  export default class VideoPlayer extends React.Component<
62
25
  QuickBrickPlayer.PlayerProps,
63
26
  QuickBrickPlayer.NativePlayerState
64
27
  > {
65
- public static propTypes = {};
66
28
  _root: any;
67
29
  playerPluginId: string;
68
30
  takeParentStyle: Record<any, any>;
69
- playbackStarted = false;
70
- needToResumeOnForeground: boolean = null;
71
- private onVideoLoadCalled: boolean;
72
- playNextData: PlayNextData;
73
31
 
74
32
  state: QuickBrickPlayer.NativePlayerState = {
75
33
  audioTrackId: null,
@@ -94,20 +52,8 @@ export default class VideoPlayer extends React.Component<
94
52
  duration: 0,
95
53
  };
96
54
 
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
-
108
55
  constructor(props) {
109
56
  super(props);
110
- this.onVideoLoadCalled = false; // android
111
57
 
112
58
  this.takeParentStyle = isWeb()
113
59
  ? { width: "100%", flex: "1 1 auto", objectFit: "contain" }
@@ -116,59 +62,58 @@ export default class VideoPlayer extends React.Component<
116
62
  this.playerPluginId = this.props?.playerPluginIdentifier;
117
63
  }
118
64
 
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
-
130
65
  componentDidMount() {
131
- this.playbackStarted = false;
132
- this.onPlayerPresented();
133
- }
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
+ };
134
91
 
135
- shouldComponentUpdate(nextProps) {
136
- const srcPath = ["entry", "content", "src"];
92
+ const player = videojs(this._root, options);
137
93
 
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
- }
94
+ let keySystems;
142
95
 
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
- }
96
+ if (drm) {
97
+ player.eme();
151
98
 
152
- if (
153
- !this.props.castAction?.state?.connected &&
154
- this.state.isCastConnected
155
- ) {
156
- 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
+ };
157
106
  }
158
107
 
159
- if (prevProps.entry?.content?.src !== this.props.entry?.content?.src) {
160
- this.stopBackgroundPlayback();
161
- this.playbackStarted = false;
162
- }
108
+ player.src([
109
+ {
110
+ src: src,
111
+ type: getStreamType(type),
112
+ keySystems,
113
+ },
114
+ ]);
163
115
  }
164
116
 
165
- // For close button
166
- public closeNativePlayer = () => {
167
- this.stopBackgroundPlayback();
168
- };
169
-
170
- getPlayNextData = () => this.props.playNextData as PlayNextData;
171
-
172
117
  /**
173
118
  * Assigns root / ref of the player.
174
119
  */
@@ -176,953 +121,18 @@ export default class VideoPlayer extends React.Component<
176
121
  this._root = component;
177
122
  };
178
123
 
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
-
706
124
  render() {
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()
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
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
1098
132
  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();
133
+ ></video>
134
+ </div>
135
+ </View>
136
+ );
1127
137
  }
1128
138
  }