@applicaster/quick-brick-player 12.0.0-alpha.3235354579 → 12.0.0-alpha.3262004353

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.3235354579",
3
+ "version": "12.0.0-alpha.3262004353",
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.58",
40
- "@applicaster/quick-brick-tv-transport-controls": "12.0.0-rc.62",
41
- "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.3235354579",
42
- "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.3235354579",
43
- "@applicaster/zapp-react-native-utils": "12.0.0-alpha.3235354579",
39
+ "@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.81",
40
+ "@applicaster/quick-brick-tv-transport-controls": "12.0.0-rc.70",
41
+ "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.3262004353",
42
+ "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.3262004353",
43
+ "@applicaster/zapp-react-native-utils": "12.0.0-alpha.3262004353",
44
44
  "keymirror": "^0.1.1",
45
45
  "query-string": "7.1.3",
46
46
  "react-native-web-linear-gradient": "1.1.2",
@@ -419,6 +419,7 @@ export default function DefaultLayout(props: LayoutProps) {
419
419
  startTimestamp={startTimestamp}
420
420
  endTimestamp={endTimestamp}
421
421
  isDateBasedLiveContent={isDateBasedLiveContent}
422
+ onSkipLive={onSkipLive}
422
423
  />
423
424
 
424
425
  {showCounter && timeLeft >= 0 && (
@@ -1,7 +1,6 @@
1
- // Disabling for now as lint can't recognise typescript definition here
2
1
  /* eslint-disable react/prop-types */
3
2
  import * as React from "react";
4
- import { StyleSheet, View } from "react-native";
3
+ import { Animated, StyleSheet, View } from "react-native";
5
4
  import LinearGradient from "../LinearGradient";
6
5
  import { getDominantColor, getPalettes } from "../../../utils/utils";
7
6
  import { useEffect } from "react";
@@ -11,7 +10,7 @@ type MagicBGProps = {
11
10
  image: string;
12
11
  fallbackUrl?: string;
13
12
  defaultColor: string;
14
- colors: string[]; // Updated to type string[] as it holds hex values
13
+ colors: string[];
15
14
  };
16
15
 
17
16
  const defaultColors = {
@@ -66,31 +65,46 @@ export const MagicBG: React.FC<MagicBGProps> = ({
66
65
  defaultColors.defaultGradientEnd,
67
66
  ]);
68
67
 
68
+ const [backgroundColorAnim] = React.useState(new Animated.Value(0)); // from 0 to 1
69
+ const [gradientOpacityAnim] = React.useState(new Animated.Value(0)); // from 0 to 1
70
+
69
71
  useEffect(() => {
72
+ const animateColors = () => {
73
+ Animated.timing(backgroundColorAnim, {
74
+ toValue: 1,
75
+ duration: 500,
76
+ useNativeDriver: false,
77
+ }).start();
78
+
79
+ Animated.timing(gradientOpacityAnim, {
80
+ toValue: 1,
81
+ duration: 500,
82
+ useNativeDriver: false,
83
+ }).start();
84
+ };
85
+
70
86
  const fetchColors = async () => {
71
- // Check if colors are provided and not empty
72
87
  if (colors && colors.length > 0) {
73
- // Use provided colors for gradient and dominant color
74
- setGradientColors(colors.slice(0, 2)); // First 2 colors for gradient
75
- setDominantColor(colors[0]); // First color as dominant
88
+ setGradientColors(colors.slice(0, 2));
89
+ setDominantColor(colors[0]);
90
+ animateColors();
76
91
  } else if (image) {
77
- // If no colors, try fetching from image
78
92
  try {
79
93
  const { gradientColors, dominantColor } =
80
94
  await fetchPaletteAndDominantColor(image);
81
95
 
82
96
  setGradientColors(gradientColors);
83
97
  setDominantColor(dominantColor);
98
+ animateColors();
84
99
  } catch (error) {
85
- // If image fetch fails, try fallback URL
86
100
  try {
87
101
  const { gradientColors, dominantColor } =
88
102
  await fetchPaletteAndDominantColor(fallbackUrl);
89
103
 
90
104
  setGradientColors(gradientColors);
91
105
  setDominantColor(dominantColor);
106
+ animateColors();
92
107
  } catch {
93
- // If everything fails, use default color
94
108
  setGradientColors([
95
109
  defaultColors.defaultGradientStart,
96
110
  defaultColors.defaultGradientEnd,
@@ -100,7 +114,6 @@ export const MagicBG: React.FC<MagicBGProps> = ({
100
114
  }
101
115
  }
102
116
  } else {
103
- // If no image or colors, use default color
104
117
  setGradientColors([
105
118
  defaultColors.defaultGradientStart,
106
119
  defaultColors.defaultGradientEnd,
@@ -113,11 +126,30 @@ export const MagicBG: React.FC<MagicBGProps> = ({
113
126
  fetchColors();
114
127
  }, []);
115
128
 
129
+ const interpolatedBackgroundColor = backgroundColorAnim.interpolate({
130
+ inputRange: [0, 1],
131
+ outputRange: ["black", dominantColor],
132
+ });
133
+
116
134
  return (
117
- <View style={[styles.container, { backgroundColor: dominantColor }]}>
135
+ <Animated.View
136
+ style={[
137
+ styles.container,
138
+ { backgroundColor: interpolatedBackgroundColor },
139
+ ]}
140
+ >
118
141
  <View style={styles.fullScreenOverlay} />
119
- <LinearGradient colors={gradientColors} style={styles.gradientOverlay} />
142
+
143
+ <Animated.View
144
+ style={[styles.gradientOverlay, { opacity: gradientOpacityAnim }]}
145
+ >
146
+ <LinearGradient
147
+ colors={gradientColors}
148
+ style={StyleSheet.absoluteFill}
149
+ />
150
+ </Animated.View>
151
+
120
152
  {children}
121
- </View>
153
+ </Animated.View>
122
154
  );
123
155
  };
@@ -22,6 +22,10 @@ export const addQueryParam = (
22
22
  return `${urlWithoutQuery}?${updatedQueryString}`;
23
23
  };
24
24
 
25
- export const isValidDate = (dateString: string): boolean => {
26
- return dateString.trim() !== "" && dayjs.utc(dateString).isValid();
25
+ export const isValidDate = (dateString: string | undefined): boolean => {
26
+ return (
27
+ typeof dateString === "string" &&
28
+ dateString.trim() !== "" &&
29
+ dayjs.utc(dateString).isValid()
30
+ );
27
31
  };
@@ -4,7 +4,7 @@
4
4
  import React from "react";
5
5
  import * as R from "ramda";
6
6
 
7
- import { findNodeHandle, NativeModules, StyleSheet, View } from "react-native";
7
+ import { findNodeHandle, StyleSheet, View } from "react-native";
8
8
  import TransportControlsMobile from "@applicaster/quick-brick-mobile-transport-controls";
9
9
  import TransportControlsTV from "@applicaster/quick-brick-tv-transport-controls";
10
10
 
@@ -21,8 +21,14 @@ import { PlayerWrapper } from "@applicaster/zapp-react-native-ui-components/Comp
21
21
 
22
22
  import { PlayNextOverlay } from "./PlayNextOverlay";
23
23
  import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
24
+ import { AnalyticsTracker } from "@applicaster/zapp-react-native-utils/analyticsUtils/analyticsTracker";
24
25
 
25
- import { GENERAL_EVENT } from "@applicaster/zapp-react-native-utils/analyticsUtils/events";
26
+ import {
27
+ AD_EVENT,
28
+ GENERAL_EVENT,
29
+ PLAYBACK_EVENT,
30
+ QOS_EVENT,
31
+ } from "@applicaster/zapp-react-native-utils/analyticsUtils/events";
26
32
  import { Categories, createLogger, Subsystem } from "../logger";
27
33
  import { parseLanguageTracks } from "@applicaster/zapp-react-native-utils/playerUtils/configurationUtils";
28
34
  import { DEFAULT_COLORS, SEEK_TIME } from "../utils/const";
@@ -36,17 +42,7 @@ import {
36
42
  AnimationComponent,
37
43
  ComponentAnimationType,
38
44
  } 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
- const RNPlayerManager = {
43
- QuickBrickPlayerPlugin: isApplePlatform()
44
- ? NativeModules.PlayerModule
45
- : NativeModules.QuickBrickDefaultPlayerModule,
46
- KalturaPlayerPlugin: isApplePlatform()
47
- ? NativeModules.KalturaPlayerModule
48
- : NativeModules.QBKalturaPlayerModule,
49
- };
45
+ import { RNPlayerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/nativePlayerManager";
50
46
 
51
47
  const styles = StyleSheet.create({
52
48
  container: { flex: 1 },
@@ -71,6 +67,7 @@ export default class VideoPlayer extends React.Component<
71
67
  > {
72
68
  public static propTypes = {};
73
69
  _root: any;
70
+ analyticsTracker: AnalyticsTracker;
74
71
  playerPluginId: string;
75
72
  takeParentStyle: Record<any, any>;
76
73
  playbackStarted = false;
@@ -101,17 +98,6 @@ export default class VideoPlayer extends React.Component<
101
98
  duration: 0,
102
99
  };
103
100
 
104
- private _tracker: PlayerAnalyticsTrackerI;
105
- get analyticsTracker() {
106
- if (!this._tracker) {
107
- this._tracker = PlayerAnalyticsManager.getInstance().getTracker(
108
- this.props.playerId
109
- );
110
- }
111
-
112
- return this._tracker;
113
- }
114
-
115
101
  constructor(props) {
116
102
  super(props);
117
103
  this.onVideoLoadCalled = false; // android
@@ -120,15 +106,14 @@ export default class VideoPlayer extends React.Component<
120
106
  ? { width: "100%", flex: "1 1 auto", objectFit: "contain" }
121
107
  : { flexBasis: "auto", flexGrow: 1, flexShrink: 1 };
122
108
 
109
+ const entry = this.props?.entry;
110
+ this.analyticsTracker = new AnalyticsTracker(entry);
123
111
  this.playerPluginId = this.props?.playerPluginIdentifier;
124
112
  }
125
113
 
126
114
  componentWillUnmount() {
127
- this.stopBackgroundPlayback();
128
-
129
115
  this.playbackStarted = false;
130
-
131
- this.analyticsTracker.handleAnalyticEvent(GENERAL_EVENT.session_end);
116
+ this.handleAnalyticEvent(GENERAL_EVENT.session_end);
132
117
  this.onPlayerClosed();
133
118
 
134
119
  if (isAndroidPlatform()) {
@@ -171,6 +156,11 @@ export default class VideoPlayer extends React.Component<
171
156
  }
172
157
  }
173
158
 
159
+ // For close button
160
+ public closeNativePlayer = () => {
161
+ this.stopBackgroundPlayback();
162
+ };
163
+
174
164
  getPlayNextData = () => this.props.playNextData as PlayNextData;
175
165
 
176
166
  /**
@@ -184,6 +174,27 @@ export default class VideoPlayer extends React.Component<
184
174
  return this.props.pluginConfiguration;
185
175
  };
186
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
+
187
198
  /**
188
199
  * This method is for the events that have no implementation in react but are being invoked by native
189
200
  * When we get the synthetic event we need to pull the data in the nativeEvent object
@@ -328,7 +339,7 @@ export default class VideoPlayer extends React.Component<
328
339
  * Sets either subtitle language, or audio language if we have a selected a new locale.
329
340
  * If no locale is set it defaults to system language for audio, and leaves subtitles unaltered.
330
341
  */
331
- setLanguage = (selected = null) => {
342
+ selectTrack = (selected = null) => {
332
343
  const { trackType, id, language } = selected || {};
333
344
 
334
345
  const isOff = language === "Off";
@@ -387,16 +398,28 @@ export default class VideoPlayer extends React.Component<
387
398
  };
388
399
 
389
400
  onPlayerPresented = () => {
390
- this.analyticsTracker?.handleAnalyticEvent(GENERAL_EVENT.player_presented);
401
+ this.handleAnalyticEvent(GENERAL_EVENT.player_presented);
391
402
  };
392
403
 
393
404
  onPlayerClosed = () => {
394
- this.analyticsTracker?.handleAnalyticEvent(GENERAL_EVENT.player_closed);
405
+ this.handleAnalyticEvent(GENERAL_EVENT.player_closed);
406
+ };
407
+
408
+ onPlayerSessionEnd = () => {
409
+ // TODO: Implement method on native android, ios exists
410
+ this.handleAnalyticEvent(GENERAL_EVENT.session_end);
411
+ };
412
+
413
+ onPlayerSessionStart = () => {
414
+ // TODO: Implement method on native android, ios exists
415
+
416
+ this.handleAnalyticEvent(GENERAL_EVENT.session_start);
395
417
  };
396
418
 
397
419
  onVideoEnd = () => {
398
420
  this.setState({ displayOverlay: !this.state.displayOverlay }, () => {
399
421
  if (this.state?.displayOverlay) {
422
+ this.handleAnalyticEvent(PLAYBACK_EVENT.complete);
400
423
  this.props?.listener?.onVideoEnd({});
401
424
  }
402
425
  });
@@ -406,12 +429,15 @@ export default class VideoPlayer extends React.Component<
406
429
  onVideoError = ({ nativeEvent }) => {
407
430
  const { message } = nativeEvent;
408
431
 
432
+ this.handleAnalyticEvent(PLAYBACK_EVENT.error, nativeEvent);
433
+
409
434
  return this.props?.listener?.onError(new Error(message));
410
435
  };
411
436
 
412
437
  onPlayerPause = ({ nativeEvent = null }) => {
413
438
  this.setState({ paused: true });
414
439
  this.props?.listener?.onPlayerPause(nativeEvent);
440
+ this.handleAnalyticEvent(PLAYBACK_EVENT.pause, nativeEvent);
415
441
  };
416
442
 
417
443
  onPlayerResume = ({ nativeEvent = null }) => {
@@ -422,61 +448,98 @@ export default class VideoPlayer extends React.Component<
422
448
 
423
449
  if (isNotAnAd && !resume) {
424
450
  this.setState({ resume: true });
451
+ this.handleAnalyticEvent(PLAYBACK_EVENT.play, nativeEvent);
425
452
  }
426
453
  };
427
454
 
428
455
  onPlayerSeekComplete = ({ nativeEvent }) => {
429
456
  this.props?.listener?.onPlayerSeekComplete(nativeEvent);
457
+ this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, nativeEvent);
430
458
  };
431
459
 
432
460
  onPlayerSeekStart = ({ nativeEvent }) => {
433
461
  this.props?.listener?.onPlayerSeekStart(nativeEvent);
462
+ this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, nativeEvent);
434
463
  };
435
464
 
436
465
  onBufferStart = ({ nativeEvent }) => {
437
466
  this.props?.listener?.onBufferStart(nativeEvent);
467
+ this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, nativeEvent);
438
468
  };
439
469
 
440
470
  onBufferComplete = ({ nativeEvent }) => {
441
471
  this.props?.listener?.onBufferComplete(nativeEvent);
472
+ this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_complete, nativeEvent);
473
+ };
474
+
475
+ onPlayerNext = ({ nativeEvent }) => {
476
+ // TODO: Implement method on native
477
+
478
+ this.handleAnalyticEvent(PLAYBACK_EVENT.next, nativeEvent);
479
+ };
480
+
481
+ onPlayerPrevious = ({ nativeEvent }) => {
482
+ // TODO: Implement method on native
483
+
484
+ this.handleAnalyticEvent(PLAYBACK_EVENT.previous, nativeEvent);
485
+ };
486
+
487
+ onPlayerStop = ({ nativeEvent }) => {
488
+ // TODO: Implement method on native
489
+
490
+ this.handleAnalyticEvent(PLAYBACK_EVENT.stop, nativeEvent);
491
+ };
492
+
493
+ onVideoBandwidthUpdate = ({ nativeEvent }) => {
494
+ // TODO: Implement method on native
495
+
496
+ this.handleAnalyticEvent(QOS_EVENT.bitrate_changed, nativeEvent);
442
497
  };
443
498
 
444
499
  onAdBegin = ({ nativeEvent }) => {
445
500
  this.setState({ isAd: true });
501
+ this.handleAnalyticEvent(AD_EVENT.ad_start, nativeEvent);
446
502
  this.props.listener?.onAdBegin?.(nativeEvent);
447
503
  };
448
504
 
449
505
  onAdBreakBegin = ({ nativeEvent }) => {
450
506
  this.setState({ isAd: true });
507
+ this.handleAnalyticEvent(AD_EVENT.ad_break_start, nativeEvent);
451
508
  this.props.listener?.onAdBreakBegin?.(nativeEvent);
452
509
  };
453
510
 
454
511
  onAdBreakEnd = ({ nativeEvent }) => {
455
512
  this.setState({ isAd: false });
513
+ this.handleAnalyticEvent(AD_EVENT.ad_break_completed, nativeEvent);
456
514
  this.props.listener?.onAdBreakEnd?.(nativeEvent);
457
515
  };
458
516
 
459
517
  onAdEnd = ({ nativeEvent }) => {
460
518
  this.setState({ isAd: false });
519
+ this.handleAnalyticEvent(AD_EVENT.ad_complete, nativeEvent);
461
520
  this.props.listener?.onAdEnd?.(nativeEvent);
462
521
  };
463
522
 
464
523
  onAdError = ({ nativeEvent }) => {
465
524
  this.setState({ isAd: false });
525
+ this.handleAnalyticEvent(AD_EVENT.ad_error, nativeEvent);
466
526
  this.props.listener?.onAdError?.(nativeEvent);
467
527
  };
468
528
 
469
529
  onAdRequest = ({ nativeEvent }) => {
530
+ this.handleAnalyticEvent(AD_EVENT.ad_request, nativeEvent);
470
531
  this.props.listener?.onAdRequest?.(nativeEvent);
471
532
  };
472
533
 
473
534
  onAdClicked = ({ nativeEvent }) => {
535
+ this.handleAnalyticEvent(AD_EVENT.ad_clicked, nativeEvent);
474
536
  // Broadcast click event
475
537
  this.props.playerEvent("ad-clicked", nativeEvent);
476
538
  this.props.listener?.onAdClicked?.(nativeEvent);
477
539
  };
478
540
 
479
541
  onAdTapped = ({ nativeEvent }) => {
542
+ this.handleAnalyticEvent(AD_EVENT.ad_tapped, nativeEvent);
480
543
  // Broadcast click event
481
544
  this.props.playerEvent("ad-tapped", nativeEvent);
482
545
  this.props.listener?.onAdTapped?.(nativeEvent);
@@ -589,6 +652,10 @@ export default class VideoPlayer extends React.Component<
589
652
  }
590
653
 
591
654
  if (!this.state.isAd && !this.onVideoLoadCalled) {
655
+ this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
656
+ player_type: this.props.playerPluginIdentifier,
657
+ });
658
+
592
659
  // TODO: Hack, needs to remove
593
660
  this.onVideoLoadCalled = true;
594
661
  }
@@ -856,6 +923,8 @@ export default class VideoPlayer extends React.Component<
856
923
  onPlayerResume: this.onPlayerResume, // play
857
924
  onPlayerSeekComplete: this.onPlayerSeekComplete, // seek_complete
858
925
  onPlayerSeekStart: this.onPlayerSeekStart, // seek_start
926
+ onPlayerSessionEnd: this.onPlayerSessionEnd, // session_end
927
+ onPlayerSessionStart: this.onPlayerSessionStart, // session_start
859
928
  };
860
929
 
861
930
  const lifecycleEvents = {
@@ -888,6 +957,7 @@ export default class VideoPlayer extends React.Component<
888
957
  onTimedMetadata: this.unHandledEvent("onTimedMetadata"),
889
958
  onTracksChanged: this.onTracksChanged,
890
959
  eventSubtitlesChange: this.unHandledEvent("eventSubtitlesChange"),
960
+ onVideoBandwidthUpdate: this.onVideoBandwidthUpdate,
891
961
  };
892
962
 
893
963
  const playerSystemEvents = {
@@ -907,6 +977,9 @@ export default class VideoPlayer extends React.Component<
907
977
  const unsupportedEvents = {
908
978
  onBufferComplete: this.onBufferComplete, // buffer_complete
909
979
  onBufferStart: this.onBufferStart, // buffer_start
980
+ onPlayerNext: this.onPlayerNext, // next
981
+ onPlayerPrevious: this.onPlayerPrevious, // previous
982
+ onPlayerStop: this.onPlayerStop, // stop
910
983
  };
911
984
 
912
985
  const nativeEvents = {