@applicaster/quick-brick-player 12.0.0-rc.26 → 12.0.0-rc.28

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-rc.26",
3
+ "version": "12.0.0-rc.28",
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.26",
39
+ "@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.28",
40
40
  "@applicaster/quick-brick-tv-transport-controls": "11.0.0-rc.133",
41
- "@applicaster/zapp-react-native-tvos-app": "12.0.0-rc.26",
42
- "@applicaster/zapp-react-native-ui-components": "12.0.0-rc.26",
43
- "@applicaster/zapp-react-native-utils": "12.0.0-rc.26",
41
+ "@applicaster/zapp-react-native-tvos-app": "12.0.0-rc.28",
42
+ "@applicaster/zapp-react-native-ui-components": "12.0.0-rc.28",
43
+ "@applicaster/zapp-react-native-utils": "12.0.0-rc.28",
44
44
  "keymirror": "^0.1.1",
45
45
  "query-string": "7.1.3",
46
46
  "react-native-web-linear-gradient": "1.1.2",
@@ -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 (
@@ -15,8 +15,17 @@ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
15
15
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
16
16
  import { PlayerDetails } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/PlayerDetails";
17
17
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
18
+ import { useEffect, useState } from "react";
18
19
  import { MagicBG } from "./MagicBG";
19
- import { addQueryParam } from "../Utils";
20
+ import {
21
+ formatSpeed,
22
+ hasDatePassed,
23
+ loadItemsFromStorage,
24
+ NAMESPACE,
25
+ } from "./utils";
26
+ import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
27
+ import { getLiveUrl } from "../Utils";
28
+ import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
20
29
 
21
30
  const { log_debug } = createLogger({
22
31
  subsystem: "UIComponent",
@@ -98,6 +107,17 @@ const Styles = {
98
107
  },
99
108
  };
100
109
 
110
+ const sleeps = [
111
+ // { label: "End of episode", value: "episode" },
112
+ { label: "5 minutes", value: 5 },
113
+ { label: "10 minutes", value: 10 },
114
+ { label: "15 minutes", value: 15 },
115
+ { label: "30 minutes", value: 30 },
116
+ { label: "45 minutes", value: 45 },
117
+ { label: "1 hour", value: 60 },
118
+ { label: "2 hours", value: 120 },
119
+ ];
120
+
101
121
  export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
102
122
  const player = usePlayer();
103
123
  const playerState = usePlayerState("audio-player-wrapper");
@@ -113,15 +133,48 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
113
133
  isTabletPortrait,
114
134
  } = props;
115
135
 
136
+ const speeds = [
137
+ { asset: configuration["speed_0_8"], value: 0.8 },
138
+ { asset: configuration["speed_1"], value: 1.0 },
139
+ { asset: configuration["speed_1_2"], value: 1.2 },
140
+ { asset: configuration["speed_1_5"], value: 1.5 },
141
+ { asset: configuration["speed_2"], value: 2.0 },
142
+ ];
143
+
116
144
  const isTablet = useIsTablet();
117
145
 
118
146
  const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
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
+
159
+ const [playbackSpeed, setPlaybackSpeed] = useState({
160
+ asset: configuration["speed_1"],
161
+ value: 1.0,
162
+ });
163
+
164
+ const [sleepTimer, setSleepTimer] = useState({
165
+ label: "playback_sleep_off_label",
166
+ value: 0,
167
+ });
168
+
169
+ const [sleepTimerDate, setSleepTimerDate] = useState(0);
119
170
 
120
171
  const [dimensions, setDimensions] = React.useState<Dimensions>({
121
172
  width: 0,
122
173
  height: 0,
123
174
  });
124
175
 
176
+ const navigator = useNavigation();
177
+
125
178
  const { maximiseVideoModal } = useNavigation();
126
179
 
127
180
  const onLayout = (event: any) => {
@@ -133,6 +186,84 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
133
186
  });
134
187
  };
135
188
 
189
+ const cancelSleepTimer = async () => {
190
+ player?.cancelSleepTimer();
191
+
192
+ setSleepTimer({
193
+ label: "playback_sleep_off_label",
194
+ value: 0,
195
+ });
196
+
197
+ setSleepTimerDate(0);
198
+ player?.pause();
199
+
200
+ await localStorage.removeItem("sleep", NAMESPACE);
201
+ };
202
+
203
+ const startSleepTimer = (timerValue: number) => {
204
+ try {
205
+ const currentDate = new Date();
206
+ const futureDate = new Date(currentDate.getTime() + timerValue * 60000);
207
+ const futureSleepDate = futureDate.getTime();
208
+
209
+ player?.cancelSleepTimer();
210
+
211
+ player?.startSleepTimer(futureSleepDate);
212
+
213
+ return futureSleepDate;
214
+ } catch (e) {
215
+ return 0;
216
+ }
217
+ };
218
+
219
+ const handleSleep = async (
220
+ timerValue: number,
221
+ currentFutureSleepDate = null
222
+ ) => {
223
+ if (currentFutureSleepDate) {
224
+ if (hasDatePassed(currentFutureSleepDate)) {
225
+ await cancelSleepTimer();
226
+
227
+ return 0;
228
+ }
229
+
230
+ return currentFutureSleepDate;
231
+ }
232
+
233
+ if (timerValue === 0) {
234
+ await cancelSleepTimer();
235
+
236
+ return timerValue;
237
+ }
238
+
239
+ return startSleepTimer(timerValue);
240
+ };
241
+
242
+ useEffect(() => {
243
+ const loadSpeed = async () => {
244
+ const speed = await loadItemsFromStorage("speed");
245
+
246
+ if (playerState.isReadyToPlay && speed) {
247
+ setPlaybackSpeed(speed);
248
+ player?.setPlaybackRate(speed.value);
249
+ }
250
+ };
251
+
252
+ const loadSleep = async () => {
253
+ const sleep = await loadItemsFromStorage("sleep");
254
+
255
+ if (playerState.isReadyToPlay && sleep) {
256
+ const { futureSleepDate, ...rest } = sleep;
257
+ setSleepTimer(rest);
258
+ const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
259
+ setSleepTimerDate(sleepTimerDate);
260
+ }
261
+ };
262
+
263
+ loadSpeed().then();
264
+ loadSleep().then();
265
+ }, [playerState.isReadyToPlay]);
266
+
136
267
  const getValue = (key: string, stylesObj: {}) =>
137
268
  R.propOr(null, key, stylesObj);
138
269
 
@@ -172,6 +303,60 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
172
303
  player?.forward(seekDuration);
173
304
  }, [seekDuration, player]);
174
305
 
306
+ const onSpeedSelect = React.useCallback(
307
+ async (item) => {
308
+ setPlaybackSpeed(item);
309
+ player.setPlaybackRate(item.value);
310
+ await localStorage.setItem("speed", item, NAMESPACE);
311
+ },
312
+ [player, setPlaybackSpeed]
313
+ );
314
+
315
+ const onSleepSelect = React.useCallback(
316
+ async (item) => {
317
+ setSleepTimer(item);
318
+ const futureSleepDate = await handleSleep(item.value);
319
+ setSleepTimerDate(futureSleepDate);
320
+
321
+ await localStorage.setItem(
322
+ "sleep",
323
+ { ...item, futureSleepDate },
324
+ NAMESPACE
325
+ );
326
+ },
327
+ [player, setSleepTimer]
328
+ );
329
+
330
+ const onSpeedButtonPress = React.useCallback(() => {
331
+ navigator.openModalBottomSheet({
332
+ modalBottomSheetContentProps: {
333
+ items: speeds,
334
+ current_selection: playbackSpeed,
335
+ onPress: onSpeedSelect,
336
+ title: configuration["playback_speed_title"],
337
+ },
338
+ options: {
339
+ supportedOrientations: ["portrait"],
340
+ },
341
+ });
342
+ }, [playbackSpeed, setPlaybackSpeed]);
343
+
344
+ const onSleepButtonPress = React.useCallback(() => {
345
+ navigator.openModalBottomSheet({
346
+ modalBottomSheetContentProps: {
347
+ items: [
348
+ { label: configuration["playback_sleep_off_label"], value: 0 },
349
+ ].concat(sleeps),
350
+ current_selection: sleepTimer,
351
+ onPress: onSleepSelect,
352
+ title: configuration["playback_sleep_title"],
353
+ },
354
+ options: {
355
+ supportedOrientations: ["portrait"],
356
+ },
357
+ });
358
+ }, [sleepTimer, setSleepTimer, configuration]);
359
+
175
360
  const onCloseVideoModal = React.useCallback(() => {
176
361
  playerManager.close();
177
362
  }, [playerManager]);
@@ -215,12 +400,22 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
215
400
  onForwardButtonPress={onForwardButtonPress}
216
401
  onPlayerSeek={onPlayerSeek}
217
402
  onSkipStart={onSkipStart}
403
+ onSpeedButtonPress={onSpeedButtonPress}
404
+ onSleepButtonPress={onSleepButtonPress}
405
+ playbackSpeedName={formatSpeed(playbackSpeed.value)}
406
+ playbackSleepName={
407
+ sleepTimer.value === 0 ? "sleep_timer" : "sleep_timer_active"
408
+ }
409
+ sleepTimerDate={sleepTimerDate}
410
+ cancelSleepTimer={cancelSleepTimer}
218
411
  onSkipLive={onSkipLive}
219
412
  onCloseVideoModal={onCloseVideoModal}
220
413
  maximiseVideoModal={maximiseVideoModal}
221
414
  configuration={configuration}
222
415
  dimensions={dimensions}
223
416
  playNextData={playNextData}
417
+ title={title}
418
+ summary={summary}
224
419
  />
225
420
  {children}
226
421
  {!docked && (
@@ -254,20 +449,24 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
254
449
  playNextData,
255
450
  onSkipStart,
256
451
  onSkipLive,
452
+ sleepTimer.value,
453
+ sleepTimerDate,
257
454
  ]);
258
455
 
259
456
  if (configuration.magic_background) {
260
457
  const baseUrl = entry?.extensions?.audio_player_background_image ?? "";
261
458
 
262
- const queryParamName =
263
- configuration.audio_player_background_image_query || "width";
264
-
265
- const queryParamValue = 15;
459
+ // TODO: To be added when background is ready
266
460
 
267
- const url = addQueryParam(baseUrl, queryParamName, queryParamValue);
461
+ // const queryParamName =
462
+ // configuration.audio_player_background_image_query || "width";
463
+ //
464
+ // const queryParamValue = 15;
465
+ //
466
+ // const url = addQueryParam(baseUrl, queryParamName, queryParamValue);
268
467
 
269
468
  return (
270
- <MagicBG image={url} fallbackUrl={baseUrl} defaultColor="#000000">
469
+ <MagicBG image={baseUrl} fallbackUrl={baseUrl} defaultColor="#000000">
271
470
  {MainContent}
272
471
  </MagicBG>
273
472
  );
@@ -6,6 +6,7 @@ import {
6
6
  ProgressBar,
7
7
  Controls,
8
8
  TransportInfo,
9
+ CountDown,
9
10
  } from "@applicaster/quick-brick-mobile-transport-controls";
10
11
 
11
12
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
@@ -17,10 +18,13 @@ import {
17
18
  rewindButton,
18
19
  skipToStartButton,
19
20
  skipToLiveButton,
21
+ speedButton,
22
+ sleepButton,
20
23
  } from "./defaultLayoutsConts";
21
24
  import { AudioDescription } from "../AudioDescription";
22
25
  import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
23
26
  import { useLanguageData } from "@applicaster/zapp-react-native-utils/reactHooks/player";
27
+ import { useEffect, useMemo, useState } from "react";
24
28
 
25
29
  type Dimensions = {
26
30
  width: number;
@@ -42,15 +46,23 @@ type LayoutProps = {
42
46
  isPaused: boolean;
43
47
  isBuffering: boolean;
44
48
  };
49
+ sleepTimerDate: number;
45
50
  value: GetValue;
51
+ playbackSpeedName: string;
52
+ playbackSleepName: string;
46
53
  configuration: { [key: string]: any };
47
54
  onPlaybackButtonPress: () => void;
48
55
  onRewindButtonPress: () => void;
49
56
  onForwardButtonPress: () => void;
57
+ onSpeedButtonPress: () => void;
58
+ onSleepButtonPress: () => void;
59
+ cancelSleepTimer: () => void;
50
60
  onPlayerSeek: (targetTime: number) => void;
51
61
  onSkipLive: () => void;
52
62
  onSkipStart: () => void;
53
63
  dimensions: Dimensions;
64
+ title: string | number;
65
+ summary: string | number;
54
66
  };
55
67
 
56
68
  const Styles = {
@@ -68,8 +80,8 @@ const Styles = {
68
80
  },
69
81
  controls: (isTablet: boolean, isLiveShowFullControls: boolean) => ({
70
82
  height: isTablet ? 64 : 50,
71
- marginHorizontal: isLiveShowFullControls ? 10 : 24,
72
- justifyContent: isLiveShowFullControls ? "space-between" : "center",
83
+ marginHorizontal: 10,
84
+ justifyContent: isLiveShowFullControls ? "space-between" : "space-evenly",
73
85
  }),
74
86
  backgroundImageContainer: (dimensions: Dimensions, isTablet: boolean) => {
75
87
  return {
@@ -117,8 +129,16 @@ export default function DefaultLayout(props: LayoutProps) {
117
129
  onPlayerSeek,
118
130
  onSkipStart,
119
131
  onSkipLive,
132
+ onSpeedButtonPress,
133
+ onSleepButtonPress,
134
+ playbackSpeedName,
135
+ playbackSleepName,
120
136
  configuration,
121
137
  dimensions,
138
+ cancelSleepTimer,
139
+ sleepTimerDate,
140
+ title,
141
+ summary,
122
142
  } = props;
123
143
 
124
144
  const isTablet = useIsTablet();
@@ -128,8 +148,15 @@ export default function DefaultLayout(props: LayoutProps) {
128
148
 
129
149
  const languageData = useLanguageData("audio-player");
130
150
 
151
+ const showCounter = useMemo(() => sleepTimerDate > 0, [sleepTimerDate]);
152
+
131
153
  const controller = playerManager.getActivePlayer();
132
154
  const liveButton = Boolean(value("live_badge_enabled"));
155
+ const sleepTitle = value("playback_sleep_countdown_label");
156
+
157
+ const [timeLeft, setTimeLeft] = useState(
158
+ sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
159
+ );
133
160
 
134
161
  let showFullControls = !controller?.isAd();
135
162
 
@@ -172,6 +199,18 @@ export default function DefaultLayout(props: LayoutProps) {
172
199
  onPress: controller?.hasContentDuration?.() ? onForwardButtonPress : null,
173
200
  });
174
201
 
202
+ const speed = speedButton({
203
+ isTablet,
204
+ onPress: onSpeedButtonPress,
205
+ name: playbackSpeedName,
206
+ });
207
+
208
+ const sleep = sleepButton({
209
+ isTablet,
210
+ onPress: onSleepButtonPress,
211
+ name: playbackSleepName,
212
+ });
213
+
175
214
  if (isLiveContent) {
176
215
  const skipToStart = skipToStartButton({
177
216
  isTablet,
@@ -186,7 +225,8 @@ export default function DefaultLayout(props: LayoutProps) {
186
225
  return [skipToStart, rewind, play, forward, skipToLive];
187
226
  }
188
227
 
189
- return [rewind, play, forward];
228
+ // Temporary empty button until we add sleep button. It will ensure the buttons will not break in their position
229
+ return [speed, rewind, play, forward, sleep];
190
230
  }, [
191
231
  playerState,
192
232
  liveButton,
@@ -194,13 +234,48 @@ export default function DefaultLayout(props: LayoutProps) {
194
234
  onPlaybackButtonPress,
195
235
  onRewindButtonPress,
196
236
  onForwardButtonPress,
237
+ onSpeedButtonPress,
197
238
  showFullControls,
198
239
  onSkipStart,
199
240
  onSkipLive,
200
241
  isLiveContent,
242
+ playbackSpeedName,
243
+ playbackSleepName,
201
244
  controller,
202
245
  ]);
203
246
 
247
+ useEffect(() => {
248
+ if (showCounter && sleepTimerDate !== 0) {
249
+ // Setting the initial timeLeft based on sleepTimerDate
250
+ setTimeLeft(sleepTimerDate - Date.now());
251
+
252
+ // Setting up an interval to update the remaining time every second
253
+ const intervalId = setInterval(() => {
254
+ setTimeLeft(() => {
255
+ const newTimeLeft = sleepTimerDate - Date.now();
256
+
257
+ // If the new remaining time is less than or equal to 0, clear the interval and trigger the cancelSleepTimer function
258
+ if (newTimeLeft <= 0) {
259
+ clearInterval(intervalId);
260
+
261
+ cancelSleepTimer();
262
+
263
+ return 0;
264
+ } else {
265
+ // Otherwise, update the state with the new remaining time
266
+ return newTimeLeft;
267
+ }
268
+ });
269
+ }, 1000);
270
+
271
+ // Clearing the interval when the component unmounts or dependencies change
272
+ return () => clearInterval(intervalId);
273
+ } else {
274
+ // Ensure the timeLeft state is set correctly if showCounter is false
275
+ setTimeLeft(0);
276
+ }
277
+ }, [sleepTimerDate, showCounter, cancelSleepTimer]);
278
+
204
279
  const Separator = () => <View style={Styles.separator} />;
205
280
 
206
281
  const liveSeekingEnabled = value("liveSeekingEnabled");
@@ -229,13 +304,13 @@ export default function DefaultLayout(props: LayoutProps) {
229
304
  >
230
305
  <View style={isTablet ? Styles.tabletLayout : null}>
231
306
  <PlayerImageBackground
232
- configuration={configuration}
233
307
  entry={entry}
234
308
  style={Styles.backgroundImageContainer(dimensions, isTablet)}
235
309
  imageStyle={Styles.backgroundImageContainerImageStyle(isTablet)}
236
310
  />
237
311
  <AudioDescription
238
- content={entry}
312
+ title={title}
313
+ summary={summary}
239
314
  value={value}
240
315
  layoutState={layoutState}
241
316
  />
@@ -252,6 +327,15 @@ export default function DefaultLayout(props: LayoutProps) {
252
327
  disableFullscreen={true}
253
328
  sliderStyle={2}
254
329
  />
330
+
331
+ {showCounter && timeLeft >= 0 && (
332
+ <CountDown
333
+ timeLeft={timeLeft}
334
+ value={value}
335
+ title={sleepTitle}
336
+ />
337
+ )}
338
+
255
339
  <Separator />
256
340
  </View>
257
341
  )}
@@ -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>
@@ -53,8 +53,9 @@ type ButtonsProps = {
53
53
  onPress: () => void;
54
54
  isTablet: boolean;
55
55
  style?: any;
56
+ name?: string;
56
57
  };
57
- type ButtonsByNameProps = ButtonsProps & { name: string };
58
+ type ButtonsByNameProps = ButtonsProps & { name?: string };
58
59
 
59
60
  const rewindButtonByName = ({
60
61
  name,
@@ -79,6 +80,28 @@ const skipButtonByName = ({
79
80
  style,
80
81
  });
81
82
 
83
+ const speedButtonByName = ({
84
+ name = "speed_1",
85
+ onPress,
86
+ style,
87
+ }: ButtonsByNameProps): ControlsButton => ({
88
+ size: "small",
89
+ icon: name,
90
+ onPress,
91
+ style,
92
+ });
93
+
94
+ const sleepButtonByName = ({
95
+ name = "sleep_timer",
96
+ onPress,
97
+ style,
98
+ }: ButtonsByNameProps): ControlsButton => ({
99
+ size: "small",
100
+ icon: name,
101
+ onPress,
102
+ style,
103
+ });
104
+
82
105
  export const rewindButton = (props: ButtonsProps): ControlsButton =>
83
106
  rewindButtonByName({ ...props, name: "rewind" });
84
107
 
@@ -90,3 +113,9 @@ export const skipToStartButton = (props: ButtonsProps): ControlsButton =>
90
113
 
91
114
  export const skipToLiveButton = (props: ButtonsProps): ControlsButton =>
92
115
  skipButtonByName({ ...props, name: "skip_live" });
116
+
117
+ export const speedButton = (props: ButtonsProps): ControlsButton =>
118
+ speedButtonByName({ ...props });
119
+
120
+ export const sleepButton = (props: ButtonsProps): ControlsButton =>
121
+ sleepButtonByName({ ...props });
@@ -24,17 +24,25 @@ type LayoutProps = {
24
24
  isBuffering: boolean;
25
25
  };
26
26
  value: GetValue;
27
+ sleepTimerDate: number;
28
+ playbackSpeedName: string;
29
+ playbackSleepName: string;
27
30
  configuration: { [key: string]: any };
28
31
  onPlaybackButtonPress: () => void;
29
32
  onRewindButtonPress: () => void;
30
33
  onForwardButtonPress: () => void;
34
+ onSpeedButtonPress: () => void;
35
+ onSleepButtonPress: () => void;
31
36
  onCloseVideoModal: () => void;
37
+ cancelSleepTimer: () => void;
32
38
  onPlayerSeek: (targetTime: number) => void;
33
39
  onSkipStart: () => void;
34
40
  onSkipLive: () => void;
35
41
  maximiseVideoModal: () => void;
36
42
  dimensions: Dimensions;
37
43
  playNextData: PlayNextData;
44
+ title: string | number;
45
+ summary: string | number;
38
46
  };
39
47
 
40
48
  export default function Layout(
@@ -0,0 +1,33 @@
1
+ import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
2
+ import { parseJsonIfNeeded } from "@applicaster/zapp-react-native-utils/functionUtils";
3
+
4
+ export const NAMESPACE = "audio-player";
5
+
6
+ export async function loadItemsFromStorage(key: string) {
7
+ const storedItems = await localStorage.getItem(key, NAMESPACE);
8
+
9
+ return parseJsonIfNeeded(storedItems) || null;
10
+ }
11
+
12
+ export const hasDatePassed = (timestamp: number) => {
13
+ // Get the current date and time
14
+ const currentDate = new Date();
15
+
16
+ // Compare the current date's timestamp with the given timestamp
17
+ return currentDate.getTime() > timestamp;
18
+ };
19
+
20
+ export const formatSpeed = (value: number) => {
21
+ let formattedValue = value.toString();
22
+
23
+ // If the value ends with ".0", remove it
24
+ if (formattedValue.endsWith(".0")) {
25
+ formattedValue = formattedValue.slice(0, -2);
26
+ }
27
+
28
+ // Replace "." with "_"
29
+ formattedValue = formattedValue.replace(".", "_");
30
+
31
+ // Return the formatted string with "speed_" prefix
32
+ return `speed_${formattedValue}`;
33
+ };
@@ -22,6 +22,11 @@ export const stringsOnlyObject = (obj) => {
22
22
  return strObj;
23
23
  };
24
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
+
25
30
  export const addQueryParam = (
26
31
  baseUrl: string,
27
32
  paramName: string,
@@ -87,7 +87,7 @@ export default class VideoPlayer extends React.Component<
87
87
  // TODO: Can be deleted, on controller logic
88
88
  paused: false,
89
89
  piped: false,
90
- rate: 1,
90
+ rate: 1.0,
91
91
  // TODO: Hack for analytics, must br removed
92
92
  resume: null,
93
93
 
@@ -368,6 +368,10 @@ export default class VideoPlayer extends React.Component<
368
368
  }
369
369
  };
370
370
 
371
+ setPlaybackRate = (rate = 1.0) => {
372
+ this.setState({ rate });
373
+ };
374
+
371
375
  getCurrentTime = () => {
372
376
  return this.state.currentTime;
373
377
  };
@@ -767,7 +771,8 @@ export default class VideoPlayer extends React.Component<
767
771
  style,
768
772
  } = this.props;
769
773
 
770
- const { audioTrackId, keyEvent, paused, seek, textTrackId } = this.state;
774
+ const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
775
+ this.state;
771
776
 
772
777
  const isAudioItem =
773
778
  entry?.content?.type?.includes?.("audio") ||
@@ -839,6 +844,7 @@ export default class VideoPlayer extends React.Component<
839
844
  nowPlayingEnabled,
840
845
  paused,
841
846
  seek,
847
+ rate,
842
848
  textTrackId,
843
849
  liveSeekingEnabled,
844
850
  liveCatchUpEnabled,