@applicaster/quick-brick-player 12.0.0-alpha.7101461483 → 12.0.0-alpha.7254207052

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.7101461483",
3
+ "version": "12.0.0-alpha.7254207052",
4
4
  "description": "Quick Brick Player",
5
5
  "main": "./src/index.ts",
6
6
  "types": "index.d.ts",
@@ -36,12 +36,13 @@
36
36
  },
37
37
  "homepage": "https://github.com/applicaster/quickbrick#readme",
38
38
  "dependencies": {
39
- "@applicaster/quick-brick-mobile-transport-controls": "12.0.0-alpha.7101461483",
39
+ "@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.33",
40
40
  "@applicaster/quick-brick-tv-transport-controls": "11.0.0-rc.133",
41
- "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.7101461483",
42
- "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.7101461483",
43
- "@applicaster/zapp-react-native-utils": "12.0.0-alpha.7101461483",
41
+ "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.7254207052",
42
+ "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.7254207052",
43
+ "@applicaster/zapp-react-native-utils": "12.0.0-alpha.7254207052",
44
44
  "keymirror": "^0.1.1",
45
+ "query-string": "7.1.3",
45
46
  "react-native-web-linear-gradient": "1.1.2",
46
47
  "shaka-player": "4.3.5",
47
48
  "typeface-montserrat": "^0.0.54",
@@ -1,8 +1,9 @@
1
1
  import React, { useEffect, useState } from "react";
2
- import { Image, StyleSheet, View } from "react-native";
2
+ import { StyleSheet, View } from "react-native";
3
3
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
4
4
  import { MUTED_USAGE } from "../const";
5
5
  import { LiveImageManager } from "@applicaster/zapp-react-native-ui-components/Components/VideoLive/LiveImageManager";
6
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
6
7
 
7
8
  type Asset = string | undefined;
8
9
 
@@ -3,7 +3,8 @@ import { Text, TextStyle, View } from "react-native";
3
3
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
4
 
5
5
  type AudioDescriptionProps = {
6
- content: ZappEntry;
6
+ title: string | number;
7
+ summary: string | number;
7
8
  layoutState: {
8
9
  inline: boolean;
9
10
  docked: boolean;
@@ -78,8 +79,7 @@ const summaryStyles = (value: GetValue, prefix: string): TextStyle => ({
78
79
  });
79
80
 
80
81
  export const AudioDescription = (props: AudioDescriptionProps) => {
81
- const { content, layoutState, value } = props;
82
- const { title, summary } = content;
82
+ const { title, summary, layoutState, value } = props;
83
83
  const prefix = layoutState.docked ? "docked_player_" : "";
84
84
 
85
85
  return (
@@ -24,6 +24,8 @@ import {
24
24
  NAMESPACE,
25
25
  } from "./utils";
26
26
  import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
27
+ import { addQueryParam, getLiveUrl } from "../Utils";
28
+ import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
27
29
 
28
30
  const { log_debug } = createLogger({
29
31
  subsystem: "UIComponent",
@@ -40,6 +42,7 @@ type Dimensions = {
40
42
  type Configuration = {
41
43
  [key: string]: any;
42
44
  magic_background?: boolean;
45
+ audio_player_background_image_query?: string;
43
46
  tablet_landscape_sidebar_width?: string;
44
47
  tablet_landscape_player_container_background_color?: string;
45
48
  };
@@ -142,6 +145,17 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
142
145
 
143
146
  const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
144
147
 
148
+ // TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
149
+
150
+ const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
151
+
152
+ const { title, summary } = useTitleSummaryOverlay(
153
+ feedUrl,
154
+ entry,
155
+ playerState?.isLive,
156
+ configuration?.audio_live_player_update_interval
157
+ );
158
+
145
159
  const [playbackSpeed, setPlaybackSpeed] = useState({
146
160
  asset: configuration["speed_1"],
147
161
  value: 1.0,
@@ -181,7 +195,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
181
195
  });
182
196
 
183
197
  setSleepTimerDate(0);
184
- player?.pause();
185
198
 
186
199
  await localStorage.removeItem("sleep", NAMESPACE);
187
200
  };
@@ -400,6 +413,8 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
400
413
  configuration={configuration}
401
414
  dimensions={dimensions}
402
415
  playNextData={playNextData}
416
+ title={title}
417
+ summary={summary}
403
418
  />
404
419
  {children}
405
420
  {!docked && (
@@ -438,9 +453,20 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
438
453
  ]);
439
454
 
440
455
  if (configuration.magic_background) {
456
+ const baseUrl = entry?.extensions?.audio_player_background_image ?? "";
457
+
458
+ const queryParamName =
459
+ configuration.audio_player_background_image_query || "width";
460
+
461
+ const queryParamValue = 15;
462
+
463
+ const url = addQueryParam(baseUrl, queryParamName, queryParamValue);
464
+
441
465
  return (
442
466
  <MagicBG
443
- image={entry?.extensions?.audio_player_background_image}
467
+ image={url}
468
+ fallbackUrl={baseUrl}
469
+ colors={entry?.extensions?.backgroundColors}
444
470
  defaultColor="#000000"
445
471
  >
446
472
  {MainContent}
@@ -61,6 +61,8 @@ type LayoutProps = {
61
61
  onSkipLive: () => void;
62
62
  onSkipStart: () => void;
63
63
  dimensions: Dimensions;
64
+ title: string | number;
65
+ summary: string | number;
64
66
  };
65
67
 
66
68
  const Styles = {
@@ -83,8 +85,8 @@ const Styles = {
83
85
  }),
84
86
  backgroundImageContainer: (dimensions: Dimensions, isTablet: boolean) => {
85
87
  return {
86
- width: isTablet ? 463 : dimensions.width,
87
- height: isTablet ? 463 : dimensions.width,
88
+ width: isTablet ? 463 : dimensions.width || 330,
89
+ height: isTablet ? 463 : dimensions.width || 330,
88
90
  };
89
91
  },
90
92
  backgroundImageContainerImageStyle: (isTablet) => ({
@@ -135,6 +137,8 @@ export default function DefaultLayout(props: LayoutProps) {
135
137
  dimensions,
136
138
  cancelSleepTimer,
137
139
  sleepTimerDate,
140
+ title,
141
+ summary,
138
142
  } = props;
139
143
 
140
144
  const isTablet = useIsTablet();
@@ -154,8 +158,7 @@ export default function DefaultLayout(props: LayoutProps) {
154
158
  sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
155
159
  );
156
160
 
157
- let showFullControls =
158
- controller?.hasContentDuration?.() && !controller?.isAd();
161
+ let showFullControls = !controller?.isAd();
159
162
 
160
163
  if (isLiveContent) {
161
164
  const seekEnabled = Boolean(value("liveSeekingEnabled"));
@@ -186,8 +189,15 @@ export default function DefaultLayout(props: LayoutProps) {
186
189
  return [play];
187
190
  }
188
191
 
189
- const rewind = rewindButton({ isTablet, onPress: onRewindButtonPress });
190
- const forward = forwardButton({ isTablet, onPress: onForwardButtonPress });
192
+ const rewind = rewindButton({
193
+ isTablet,
194
+ onPress: controller?.hasContentDuration?.() ? onRewindButtonPress : null,
195
+ });
196
+
197
+ const forward = forwardButton({
198
+ isTablet,
199
+ onPress: controller?.hasContentDuration?.() ? onForwardButtonPress : null,
200
+ });
191
201
 
192
202
  const speed = speedButton({
193
203
  isTablet,
@@ -231,6 +241,7 @@ export default function DefaultLayout(props: LayoutProps) {
231
241
  isLiveContent,
232
242
  playbackSpeedName,
233
243
  playbackSleepName,
244
+ controller,
234
245
  ]);
235
246
 
236
247
  useEffect(() => {
@@ -298,7 +309,8 @@ export default function DefaultLayout(props: LayoutProps) {
298
309
  imageStyle={Styles.backgroundImageContainerImageStyle(isTablet)}
299
310
  />
300
311
  <AudioDescription
301
- content={entry}
312
+ title={title}
313
+ summary={summary}
302
314
  value={value}
303
315
  layoutState={layoutState}
304
316
  />
@@ -48,6 +48,8 @@ type LayoutProps = {
48
48
  maximiseVideoModal: () => void;
49
49
  dimensions: Dimensions;
50
50
  playNextData: PlayNextData;
51
+ title: string | number;
52
+ summary: string | number;
51
53
  };
52
54
 
53
55
  const styles = StyleSheet.create({
@@ -196,6 +198,8 @@ export default function DockedLayout(props: LayoutProps) {
196
198
  playerState,
197
199
  configuration,
198
200
  playNextData,
201
+ title,
202
+ summary,
199
203
  } = props;
200
204
 
201
205
  const isTablet = useIsTablet();
@@ -232,7 +236,11 @@ export default function DockedLayout(props: LayoutProps) {
232
236
  !configuration.magic_background && backgroundColorStyle(value),
233
237
  ]}
234
238
  >
235
- <ContentInfo content={entry} value={value} docked />
239
+ <ContentInfo
240
+ content={{ ...entry, title, summary }}
241
+ value={value}
242
+ docked
243
+ />
236
244
  </View>
237
245
  </View>
238
246
  </TouchableWithoutFeedback>
@@ -41,6 +41,8 @@ type LayoutProps = {
41
41
  maximiseVideoModal: () => void;
42
42
  dimensions: Dimensions;
43
43
  playNextData: PlayNextData;
44
+ title: string | number;
45
+ summary: string | number;
44
46
  };
45
47
 
46
48
  export default function Layout(
@@ -4,14 +4,17 @@ import * as React from "react";
4
4
  import { StyleSheet, View } from "react-native";
5
5
  import LinearGradient from "../LinearGradient";
6
6
  import { getDominantColor, getPalettes } from "../../../utils/utils";
7
+ import { useEffect } from "react";
7
8
 
8
9
  type MagicBGProps = {
9
10
  children: React.ReactNode;
10
11
  image: string;
12
+ fallbackUrl?: string;
11
13
  defaultColor: string;
14
+ colors: string[]; // Updated to type string[] as it holds hex values
12
15
  };
13
16
 
14
- const colors = {
17
+ const defaultColors = {
15
18
  overlay: "rgba(0, 0, 0, 0.35)",
16
19
  defaultGradientStart: "rgba(0, 0, 0, 0.25)",
17
20
  defaultGradientEnd: "rgba(0, 0, 0, 0)",
@@ -23,7 +26,7 @@ const styles = StyleSheet.create({
23
26
  },
24
27
  fullScreenOverlay: {
25
28
  ...StyleSheet.absoluteFillObject,
26
- backgroundColor: colors.overlay,
29
+ backgroundColor: defaultColors.overlay,
27
30
  },
28
31
  gradientOverlay: {
29
32
  position: "absolute",
@@ -34,47 +37,81 @@ const styles = StyleSheet.create({
34
37
  },
35
38
  });
36
39
 
40
+ const fetchPaletteAndDominantColor = async (imageUrl: string) => {
41
+ const palette = await getPalettes(imageUrl);
42
+
43
+ const colors = palette.map(
44
+ (color: { r: number; g: number; b: number }) =>
45
+ `rgb(${color.r}, ${color.g}, ${color.b})`
46
+ );
47
+
48
+ const dominant = await getDominantColor(imageUrl);
49
+ const dominantColor = `rgb(${dominant.r}, ${dominant.g}, ${dominant.b})`;
50
+
51
+ return { gradientColors: colors.slice(0, 2), dominantColor };
52
+ };
53
+
37
54
  export const MagicBG: React.FC<MagicBGProps> = ({
38
55
  children,
39
56
  image,
57
+ fallbackUrl = "",
58
+ colors = [],
40
59
  defaultColor,
41
60
  }) => {
42
61
  const [dominantColor, setDominantColor] =
43
62
  React.useState<string>(defaultColor);
44
63
 
45
64
  const [gradientColors, setGradientColors] = React.useState<string[]>([
46
- colors.defaultGradientStart,
47
- colors.defaultGradientEnd,
65
+ defaultColors.defaultGradientStart,
66
+ defaultColors.defaultGradientEnd,
48
67
  ]);
49
68
 
50
- React.useEffect(() => {
69
+ useEffect(() => {
51
70
  const fetchColors = async () => {
52
- if (image) {
71
+ // Check if colors are provided and not empty
72
+ 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
76
+ } else if (image) {
77
+ // If no colors, try fetching from image
53
78
  try {
54
- const palette = await getPalettes(image);
55
-
56
- const colors = palette.map(
57
- (color: { r: number; g: number; b: number }) =>
58
- `rgb(${color.r}, ${color.g}, ${color.b})`
59
- );
60
-
61
- setGradientColors(colors.slice(0, 2));
79
+ const { gradientColors, dominantColor } =
80
+ await fetchPaletteAndDominantColor(image);
62
81
 
63
- const dominant = await getDominantColor(image);
64
- setDominantColor(`rgb(${dominant.r}, ${dominant.g}, ${dominant.b})`);
82
+ setGradientColors(gradientColors);
83
+ setDominantColor(dominantColor);
65
84
  } catch (error) {
66
- setGradientColors([
67
- colors.defaultGradientStart,
68
- colors.defaultGradientEnd,
69
- ]);
85
+ // If image fetch fails, try fallback URL
86
+ try {
87
+ const { gradientColors, dominantColor } =
88
+ await fetchPaletteAndDominantColor(fallbackUrl);
70
89
 
71
- setDominantColor(defaultColor);
90
+ setGradientColors(gradientColors);
91
+ setDominantColor(dominantColor);
92
+ } catch {
93
+ // If everything fails, use default color
94
+ setGradientColors([
95
+ defaultColors.defaultGradientStart,
96
+ defaultColors.defaultGradientEnd,
97
+ ]);
98
+
99
+ setDominantColor(defaultColor);
100
+ }
72
101
  }
102
+ } else {
103
+ // If no image or colors, use default color
104
+ setGradientColors([
105
+ defaultColors.defaultGradientStart,
106
+ defaultColors.defaultGradientEnd,
107
+ ]);
108
+
109
+ setDominantColor(defaultColor);
73
110
  }
74
111
  };
75
112
 
76
113
  fetchColors();
77
- }, [image, defaultColor]);
114
+ }, []);
78
115
 
79
116
  return (
80
117
  <View style={[styles.container, { backgroundColor: dominantColor }]}>
@@ -1,3 +1,5 @@
1
+ import queryString from "query-string";
2
+
1
3
  export const toTypeString = (obj) => {
2
4
  switch (typeof obj) {
3
5
  case "object":
@@ -19,3 +21,24 @@ export const stringsOnlyObject = (obj) => {
19
21
 
20
22
  return strObj;
21
23
  };
24
+
25
+ export const getLiveUrl = (url: string) => {
26
+ // Use the replace method with a regular expression to replace all instances of subClass with livePage
27
+ return url.replace(new RegExp("subClass", "g"), "livePage");
28
+ };
29
+
30
+ export const addQueryParam = (
31
+ baseUrl: string,
32
+ paramName: string,
33
+ paramValue: string | number
34
+ ) => {
35
+ const [urlWithoutQuery, query] = baseUrl.split("?");
36
+
37
+ const parsedQuery = queryString.parse(query);
38
+
39
+ parsedQuery[paramName] = paramValue.toString();
40
+
41
+ const updatedQueryString = queryString.stringify(parsedQuery);
42
+
43
+ return `${urlWithoutQuery}?${updatedQueryString}`;
44
+ };
@@ -1,3 +1 @@
1
- export * from "./AssetsHelper";
2
-
3
1
  export * from "./Utils";
@@ -36,6 +36,8 @@ import { AudioPlayerWrapper } from "./AudioLayer/AudioPlayerWrapper";
36
36
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
37
37
  import { getAllAccessibilityProps } from "@applicaster/zapp-react-native-utils/configurationUtils";
38
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";
39
41
 
40
42
  const RNPlayerManager = {
41
43
  QuickBrickPlayerPlugin: isApplePlatform()
@@ -555,6 +557,26 @@ export default class VideoPlayer extends React.Component<
555
557
  this.props.listener?.onPlayerDetached?.(nativeEvent);
556
558
  };
557
559
 
560
+ onVideoFullscreenPlayerDidDismiss = () => {
561
+ this.props.listener?.onVideoFullscreenPlayerDidDismiss?.();
562
+ };
563
+
564
+ onVideoFullscreenPlayerDidPresent = () => {
565
+ this.props.listener?.onVideoFullscreenPlayerDidPresent?.();
566
+ };
567
+
568
+ onVideoFullscreenPlayerWillDismiss = () => {
569
+ this.props.listener?.onVideoFullscreenPlayerWillDismiss?.();
570
+ };
571
+
572
+ onVideoFullscreenPlayerWillPresent = () => {
573
+ this.props.listener?.onVideoFullscreenPlayerWillPresent?.();
574
+ };
575
+
576
+ onPlayerClose = () => {
577
+ this.props.listener?.onPlayerClose?.();
578
+ };
579
+
558
580
  onPiPEnter = () => {
559
581
  this.setState({ piped: true });
560
582
  this.props.openPiP();
@@ -759,7 +781,6 @@ export default class VideoPlayer extends React.Component<
759
781
  render() {
760
782
  const {
761
783
  castAction,
762
- controls,
763
784
  docked,
764
785
  entry,
765
786
  fullscreen,
@@ -817,17 +838,31 @@ export default class VideoPlayer extends React.Component<
817
838
  const liveCatchUpEnabled =
818
839
  this.props.pluginConfiguration?.liveCatchUpEnabled;
819
840
 
820
- const hideNativeControls = isTV()
821
- ? { controls: !this.getPlayNextData() }
822
- : {};
841
+ // TODO: Get screen data more reliable way useCurrentScreenData
842
+ const screenData = getTargetScreenDataFromEntry(this.props.playableItem);
823
843
 
824
- const platformSpecificProps = isApplePlatform()
844
+ const isInlineTV = screenData?.ui_components?.length > 0 && isTV();
845
+
846
+ let platformSpecificProps: any = isApplePlatform()
825
847
  ? {
826
- ...hideNativeControls,
827
848
  resizeMode: nativeResizeMode,
828
849
  }
829
850
  : {};
830
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 = controls && !docked;
860
+
861
+ platformSpecificProps = {
862
+ ...platformSpecificProps,
863
+ controls: shouldUseNativeControls,
864
+ };
865
+
831
866
  const pictureInPictureEnabled =
832
867
  this.props.pluginConfiguration?.pictureInPictureEnabled;
833
868
 
@@ -835,7 +870,6 @@ export default class VideoPlayer extends React.Component<
835
870
  entry, // Must be passed first in list
836
871
  ...platformSpecificProps,
837
872
  audioTrackId,
838
- controls,
839
873
  docked,
840
874
  enableAutoAudioTrackSelection,
841
875
  fullscreen, // TODO: Check fullscreen params not crash
@@ -890,22 +924,17 @@ export default class VideoPlayer extends React.Component<
890
924
  onVideoEnd: this.onVideoEnd, // complete
891
925
  onVideoError: this.onVideoError, // error
892
926
  onVideoProgress: this.onVideoProgress,
893
- onVideoFullscreenPlayerDidDismiss: this.unHandledEvent(
894
- "onVideoFullscreenPlayerDidPresent"
895
- ),
896
- onVideoFullscreenPlayerDidPresent: this.unHandledEvent(
897
- "onVideoFullscreenPlayerDidPresent"
898
- ),
899
- onVideoFullscreenPlayerWillDismiss: this.unHandledEvent(
900
- "onVideoFullscreenPlayerWillDismiss"
901
- ),
902
- onVideoFullscreenPlayerWillPresent: this.unHandledEvent(
903
- "onVideoFullscreenPlayerWillPresent"
904
- ),
927
+ onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
928
+ onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
929
+ onVideoFullscreenPlayerWillDismiss:
930
+ this.onVideoFullscreenPlayerWillDismiss,
931
+ onVideoFullscreenPlayerWillPresent:
932
+ this.onVideoFullscreenPlayerWillPresent,
905
933
  onVideoLoad: this.onVideoLoad,
906
934
  onVideoLoadStart: this.unHandledEvent("onVideoLoadStart"),
907
935
  onVideoBuffer: this.onVideoBuffer,
908
936
  onPlaybackStalled: this.onPlaybackStalled,
937
+ onPlayerClose: this.onPlayerClose,
909
938
  onPlayerAttached: this.onPlayerAttached,
910
939
  onPlayerDetached: this.onPlayerDetached,
911
940
  };
@@ -979,7 +1008,7 @@ export default class VideoPlayer extends React.Component<
979
1008
  !needToShowPlayNextOverlay &&
980
1009
  this.props.mode !== "PIP";
981
1010
 
982
- if (controls === true) {
1011
+ if (shouldUseNativeControls) {
983
1012
  return null;
984
1013
  }
985
1014