@applicaster/quick-brick-player 12.0.0-alpha.6602834797 → 12.0.0-alpha.7101461483
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 +5 -5
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +183 -0
- package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +84 -5
- package/src/Player/AudioLayer/Layout/DockedLayout.tsx +3 -5
- package/src/Player/AudioLayer/Layout/defaultLayoutsConts.ts +30 -1
- package/src/Player/AudioLayer/Layout/index.tsx +6 -0
- package/src/Player/AudioLayer/utils.ts +33 -0
- package/src/Player/index.tsx +12 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.7101461483",
|
|
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-alpha.
|
|
39
|
+
"@applicaster/quick-brick-mobile-transport-controls": "12.0.0-alpha.7101461483",
|
|
40
40
|
"@applicaster/quick-brick-tv-transport-controls": "11.0.0-rc.133",
|
|
41
|
-
"@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.
|
|
42
|
-
"@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.
|
|
43
|
-
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.
|
|
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",
|
|
44
44
|
"keymirror": "^0.1.1",
|
|
45
45
|
"react-native-web-linear-gradient": "1.1.2",
|
|
46
46
|
"shaka-player": "4.3.5",
|
|
@@ -15,7 +15,15 @@ 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";
|
|
20
|
+
import {
|
|
21
|
+
formatSpeed,
|
|
22
|
+
hasDatePassed,
|
|
23
|
+
loadItemsFromStorage,
|
|
24
|
+
NAMESPACE,
|
|
25
|
+
} from "./utils";
|
|
26
|
+
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
19
27
|
|
|
20
28
|
const { log_debug } = createLogger({
|
|
21
29
|
subsystem: "UIComponent",
|
|
@@ -96,6 +104,17 @@ const Styles = {
|
|
|
96
104
|
},
|
|
97
105
|
};
|
|
98
106
|
|
|
107
|
+
const sleeps = [
|
|
108
|
+
// { label: "End of episode", value: "episode" },
|
|
109
|
+
{ label: "5 minutes", value: 5 },
|
|
110
|
+
{ label: "10 minutes", value: 10 },
|
|
111
|
+
{ label: "15 minutes", value: 15 },
|
|
112
|
+
{ label: "30 minutes", value: 30 },
|
|
113
|
+
{ label: "45 minutes", value: 45 },
|
|
114
|
+
{ label: "1 hour", value: 60 },
|
|
115
|
+
{ label: "2 hours", value: 120 },
|
|
116
|
+
];
|
|
117
|
+
|
|
99
118
|
export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
100
119
|
const player = usePlayer();
|
|
101
120
|
const playerState = usePlayerState("audio-player-wrapper");
|
|
@@ -111,15 +130,37 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
111
130
|
isTabletPortrait,
|
|
112
131
|
} = props;
|
|
113
132
|
|
|
133
|
+
const speeds = [
|
|
134
|
+
{ asset: configuration["speed_0_8"], value: 0.8 },
|
|
135
|
+
{ asset: configuration["speed_1"], value: 1.0 },
|
|
136
|
+
{ asset: configuration["speed_1_2"], value: 1.2 },
|
|
137
|
+
{ asset: configuration["speed_1_5"], value: 1.5 },
|
|
138
|
+
{ asset: configuration["speed_2"], value: 2.0 },
|
|
139
|
+
];
|
|
140
|
+
|
|
114
141
|
const isTablet = useIsTablet();
|
|
115
142
|
|
|
116
143
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
117
144
|
|
|
145
|
+
const [playbackSpeed, setPlaybackSpeed] = useState({
|
|
146
|
+
asset: configuration["speed_1"],
|
|
147
|
+
value: 1.0,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const [sleepTimer, setSleepTimer] = useState({
|
|
151
|
+
label: "playback_sleep_off_label",
|
|
152
|
+
value: 0,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const [sleepTimerDate, setSleepTimerDate] = useState(0);
|
|
156
|
+
|
|
118
157
|
const [dimensions, setDimensions] = React.useState<Dimensions>({
|
|
119
158
|
width: 0,
|
|
120
159
|
height: 0,
|
|
121
160
|
});
|
|
122
161
|
|
|
162
|
+
const navigator = useNavigation();
|
|
163
|
+
|
|
123
164
|
const { maximiseVideoModal } = useNavigation();
|
|
124
165
|
|
|
125
166
|
const onLayout = (event: any) => {
|
|
@@ -131,6 +172,84 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
131
172
|
});
|
|
132
173
|
};
|
|
133
174
|
|
|
175
|
+
const cancelSleepTimer = async () => {
|
|
176
|
+
player?.cancelSleepTimer();
|
|
177
|
+
|
|
178
|
+
setSleepTimer({
|
|
179
|
+
label: "playback_sleep_off_label",
|
|
180
|
+
value: 0,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
setSleepTimerDate(0);
|
|
184
|
+
player?.pause();
|
|
185
|
+
|
|
186
|
+
await localStorage.removeItem("sleep", NAMESPACE);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const startSleepTimer = (timerValue: number) => {
|
|
190
|
+
try {
|
|
191
|
+
const currentDate = new Date();
|
|
192
|
+
const futureDate = new Date(currentDate.getTime() + timerValue * 60000);
|
|
193
|
+
const futureSleepDate = futureDate.getTime();
|
|
194
|
+
|
|
195
|
+
player?.cancelSleepTimer();
|
|
196
|
+
|
|
197
|
+
player?.startSleepTimer(futureSleepDate);
|
|
198
|
+
|
|
199
|
+
return futureSleepDate;
|
|
200
|
+
} catch (e) {
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const handleSleep = async (
|
|
206
|
+
timerValue: number,
|
|
207
|
+
currentFutureSleepDate = null
|
|
208
|
+
) => {
|
|
209
|
+
if (currentFutureSleepDate) {
|
|
210
|
+
if (hasDatePassed(currentFutureSleepDate)) {
|
|
211
|
+
await cancelSleepTimer();
|
|
212
|
+
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return currentFutureSleepDate;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (timerValue === 0) {
|
|
220
|
+
await cancelSleepTimer();
|
|
221
|
+
|
|
222
|
+
return timerValue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return startSleepTimer(timerValue);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
const loadSpeed = async () => {
|
|
230
|
+
const speed = await loadItemsFromStorage("speed");
|
|
231
|
+
|
|
232
|
+
if (playerState.isReadyToPlay && speed) {
|
|
233
|
+
setPlaybackSpeed(speed);
|
|
234
|
+
player?.setPlaybackRate(speed.value);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const loadSleep = async () => {
|
|
239
|
+
const sleep = await loadItemsFromStorage("sleep");
|
|
240
|
+
|
|
241
|
+
if (playerState.isReadyToPlay && sleep) {
|
|
242
|
+
const { futureSleepDate, ...rest } = sleep;
|
|
243
|
+
setSleepTimer(rest);
|
|
244
|
+
const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
|
|
245
|
+
setSleepTimerDate(sleepTimerDate);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
loadSpeed().then();
|
|
250
|
+
loadSleep().then();
|
|
251
|
+
}, [playerState.isReadyToPlay]);
|
|
252
|
+
|
|
134
253
|
const getValue = (key: string, stylesObj: {}) =>
|
|
135
254
|
R.propOr(null, key, stylesObj);
|
|
136
255
|
|
|
@@ -170,6 +289,60 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
170
289
|
player?.forward(seekDuration);
|
|
171
290
|
}, [seekDuration, player]);
|
|
172
291
|
|
|
292
|
+
const onSpeedSelect = React.useCallback(
|
|
293
|
+
async (item) => {
|
|
294
|
+
setPlaybackSpeed(item);
|
|
295
|
+
player.setPlaybackRate(item.value);
|
|
296
|
+
await localStorage.setItem("speed", item, NAMESPACE);
|
|
297
|
+
},
|
|
298
|
+
[player, setPlaybackSpeed]
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const onSleepSelect = React.useCallback(
|
|
302
|
+
async (item) => {
|
|
303
|
+
setSleepTimer(item);
|
|
304
|
+
const futureSleepDate = await handleSleep(item.value);
|
|
305
|
+
setSleepTimerDate(futureSleepDate);
|
|
306
|
+
|
|
307
|
+
await localStorage.setItem(
|
|
308
|
+
"sleep",
|
|
309
|
+
{ ...item, futureSleepDate },
|
|
310
|
+
NAMESPACE
|
|
311
|
+
);
|
|
312
|
+
},
|
|
313
|
+
[player, setSleepTimer]
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const onSpeedButtonPress = React.useCallback(() => {
|
|
317
|
+
navigator.openModalBottomSheet({
|
|
318
|
+
modalBottomSheetContentProps: {
|
|
319
|
+
items: speeds,
|
|
320
|
+
current_selection: playbackSpeed,
|
|
321
|
+
onPress: onSpeedSelect,
|
|
322
|
+
title: configuration["playback_speed_title"],
|
|
323
|
+
},
|
|
324
|
+
options: {
|
|
325
|
+
supportedOrientations: ["portrait"],
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
}, [playbackSpeed, setPlaybackSpeed]);
|
|
329
|
+
|
|
330
|
+
const onSleepButtonPress = React.useCallback(() => {
|
|
331
|
+
navigator.openModalBottomSheet({
|
|
332
|
+
modalBottomSheetContentProps: {
|
|
333
|
+
items: [
|
|
334
|
+
{ label: configuration["playback_sleep_off_label"], value: 0 },
|
|
335
|
+
].concat(sleeps),
|
|
336
|
+
current_selection: sleepTimer,
|
|
337
|
+
onPress: onSleepSelect,
|
|
338
|
+
title: configuration["playback_sleep_title"],
|
|
339
|
+
},
|
|
340
|
+
options: {
|
|
341
|
+
supportedOrientations: ["portrait"],
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
}, [sleepTimer, setSleepTimer, configuration]);
|
|
345
|
+
|
|
173
346
|
const onCloseVideoModal = React.useCallback(() => {
|
|
174
347
|
playerManager.close();
|
|
175
348
|
}, [playerManager]);
|
|
@@ -213,6 +386,14 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
213
386
|
onForwardButtonPress={onForwardButtonPress}
|
|
214
387
|
onPlayerSeek={onPlayerSeek}
|
|
215
388
|
onSkipStart={onSkipStart}
|
|
389
|
+
onSpeedButtonPress={onSpeedButtonPress}
|
|
390
|
+
onSleepButtonPress={onSleepButtonPress}
|
|
391
|
+
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
392
|
+
playbackSleepName={
|
|
393
|
+
sleepTimer.value === 0 ? "sleep_timer" : "sleep_timer_active"
|
|
394
|
+
}
|
|
395
|
+
sleepTimerDate={sleepTimerDate}
|
|
396
|
+
cancelSleepTimer={cancelSleepTimer}
|
|
216
397
|
onSkipLive={onSkipLive}
|
|
217
398
|
onCloseVideoModal={onCloseVideoModal}
|
|
218
399
|
maximiseVideoModal={maximiseVideoModal}
|
|
@@ -252,6 +433,8 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
252
433
|
playNextData,
|
|
253
434
|
onSkipStart,
|
|
254
435
|
onSkipLive,
|
|
436
|
+
sleepTimer.value,
|
|
437
|
+
sleepTimerDate,
|
|
255
438
|
]);
|
|
256
439
|
|
|
257
440
|
if (configuration.magic_background) {
|
|
@@ -6,21 +6,25 @@ 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";
|
|
12
13
|
|
|
13
|
-
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks
|
|
14
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
14
15
|
import {
|
|
15
16
|
forwardButton,
|
|
16
17
|
playbackButton,
|
|
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,11 +46,17 @@ 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;
|
|
@@ -68,8 +78,8 @@ const Styles = {
|
|
|
68
78
|
},
|
|
69
79
|
controls: (isTablet: boolean, isLiveShowFullControls: boolean) => ({
|
|
70
80
|
height: isTablet ? 64 : 50,
|
|
71
|
-
marginHorizontal:
|
|
72
|
-
justifyContent: isLiveShowFullControls ? "space-between" : "
|
|
81
|
+
marginHorizontal: 10,
|
|
82
|
+
justifyContent: isLiveShowFullControls ? "space-between" : "space-evenly",
|
|
73
83
|
}),
|
|
74
84
|
backgroundImageContainer: (dimensions: Dimensions, isTablet: boolean) => {
|
|
75
85
|
return {
|
|
@@ -117,8 +127,14 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
117
127
|
onPlayerSeek,
|
|
118
128
|
onSkipStart,
|
|
119
129
|
onSkipLive,
|
|
130
|
+
onSpeedButtonPress,
|
|
131
|
+
onSleepButtonPress,
|
|
132
|
+
playbackSpeedName,
|
|
133
|
+
playbackSleepName,
|
|
120
134
|
configuration,
|
|
121
135
|
dimensions,
|
|
136
|
+
cancelSleepTimer,
|
|
137
|
+
sleepTimerDate,
|
|
122
138
|
} = props;
|
|
123
139
|
|
|
124
140
|
const isTablet = useIsTablet();
|
|
@@ -128,8 +144,15 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
128
144
|
|
|
129
145
|
const languageData = useLanguageData("audio-player");
|
|
130
146
|
|
|
147
|
+
const showCounter = useMemo(() => sleepTimerDate > 0, [sleepTimerDate]);
|
|
148
|
+
|
|
131
149
|
const controller = playerManager.getActivePlayer();
|
|
132
150
|
const liveButton = Boolean(value("live_badge_enabled"));
|
|
151
|
+
const sleepTitle = value("playback_sleep_countdown_label");
|
|
152
|
+
|
|
153
|
+
const [timeLeft, setTimeLeft] = useState(
|
|
154
|
+
sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
|
|
155
|
+
);
|
|
133
156
|
|
|
134
157
|
let showFullControls =
|
|
135
158
|
controller?.hasContentDuration?.() && !controller?.isAd();
|
|
@@ -166,6 +189,18 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
166
189
|
const rewind = rewindButton({ isTablet, onPress: onRewindButtonPress });
|
|
167
190
|
const forward = forwardButton({ isTablet, onPress: onForwardButtonPress });
|
|
168
191
|
|
|
192
|
+
const speed = speedButton({
|
|
193
|
+
isTablet,
|
|
194
|
+
onPress: onSpeedButtonPress,
|
|
195
|
+
name: playbackSpeedName,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const sleep = sleepButton({
|
|
199
|
+
isTablet,
|
|
200
|
+
onPress: onSleepButtonPress,
|
|
201
|
+
name: playbackSleepName,
|
|
202
|
+
});
|
|
203
|
+
|
|
169
204
|
if (isLiveContent) {
|
|
170
205
|
const skipToStart = skipToStartButton({
|
|
171
206
|
isTablet,
|
|
@@ -180,7 +215,8 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
180
215
|
return [skipToStart, rewind, play, forward, skipToLive];
|
|
181
216
|
}
|
|
182
217
|
|
|
183
|
-
|
|
218
|
+
// Temporary empty button until we add sleep button. It will ensure the buttons will not break in their position
|
|
219
|
+
return [speed, rewind, play, forward, sleep];
|
|
184
220
|
}, [
|
|
185
221
|
playerState,
|
|
186
222
|
liveButton,
|
|
@@ -188,12 +224,47 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
188
224
|
onPlaybackButtonPress,
|
|
189
225
|
onRewindButtonPress,
|
|
190
226
|
onForwardButtonPress,
|
|
227
|
+
onSpeedButtonPress,
|
|
191
228
|
showFullControls,
|
|
192
229
|
onSkipStart,
|
|
193
230
|
onSkipLive,
|
|
194
231
|
isLiveContent,
|
|
232
|
+
playbackSpeedName,
|
|
233
|
+
playbackSleepName,
|
|
195
234
|
]);
|
|
196
235
|
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
if (showCounter && sleepTimerDate !== 0) {
|
|
238
|
+
// Setting the initial timeLeft based on sleepTimerDate
|
|
239
|
+
setTimeLeft(sleepTimerDate - Date.now());
|
|
240
|
+
|
|
241
|
+
// Setting up an interval to update the remaining time every second
|
|
242
|
+
const intervalId = setInterval(() => {
|
|
243
|
+
setTimeLeft(() => {
|
|
244
|
+
const newTimeLeft = sleepTimerDate - Date.now();
|
|
245
|
+
|
|
246
|
+
// If the new remaining time is less than or equal to 0, clear the interval and trigger the cancelSleepTimer function
|
|
247
|
+
if (newTimeLeft <= 0) {
|
|
248
|
+
clearInterval(intervalId);
|
|
249
|
+
|
|
250
|
+
cancelSleepTimer();
|
|
251
|
+
|
|
252
|
+
return 0;
|
|
253
|
+
} else {
|
|
254
|
+
// Otherwise, update the state with the new remaining time
|
|
255
|
+
return newTimeLeft;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}, 1000);
|
|
259
|
+
|
|
260
|
+
// Clearing the interval when the component unmounts or dependencies change
|
|
261
|
+
return () => clearInterval(intervalId);
|
|
262
|
+
} else {
|
|
263
|
+
// Ensure the timeLeft state is set correctly if showCounter is false
|
|
264
|
+
setTimeLeft(0);
|
|
265
|
+
}
|
|
266
|
+
}, [sleepTimerDate, showCounter, cancelSleepTimer]);
|
|
267
|
+
|
|
197
268
|
const Separator = () => <View style={Styles.separator} />;
|
|
198
269
|
|
|
199
270
|
const liveSeekingEnabled = value("liveSeekingEnabled");
|
|
@@ -222,7 +293,6 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
222
293
|
>
|
|
223
294
|
<View style={isTablet ? Styles.tabletLayout : null}>
|
|
224
295
|
<PlayerImageBackground
|
|
225
|
-
configuration={configuration}
|
|
226
296
|
entry={entry}
|
|
227
297
|
style={Styles.backgroundImageContainer(dimensions, isTablet)}
|
|
228
298
|
imageStyle={Styles.backgroundImageContainerImageStyle(isTablet)}
|
|
@@ -245,6 +315,15 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
245
315
|
disableFullscreen={true}
|
|
246
316
|
sliderStyle={2}
|
|
247
317
|
/>
|
|
318
|
+
|
|
319
|
+
{showCounter && timeLeft >= 0 && (
|
|
320
|
+
<CountDown
|
|
321
|
+
timeLeft={timeLeft}
|
|
322
|
+
value={value}
|
|
323
|
+
title={sleepTitle}
|
|
324
|
+
/>
|
|
325
|
+
)}
|
|
326
|
+
|
|
248
327
|
<Separator />
|
|
249
328
|
</View>
|
|
250
329
|
)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
import {
|
|
3
3
|
StyleSheet,
|
|
4
4
|
TouchableWithoutFeedback,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
ViewStyle,
|
|
7
7
|
} from "react-native";
|
|
8
8
|
import * as R from "ramda";
|
|
9
|
-
import {
|
|
9
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
ContentInfo,
|
|
@@ -198,9 +198,7 @@ export default function DockedLayout(props: LayoutProps) {
|
|
|
198
198
|
playNextData,
|
|
199
199
|
} = props;
|
|
200
200
|
|
|
201
|
-
const
|
|
202
|
-
deviceInfo: { isTablet },
|
|
203
|
-
} = useDimensions("screen", { deviceInfo: true });
|
|
201
|
+
const isTablet = useIsTablet();
|
|
204
202
|
|
|
205
203
|
const visible = true;
|
|
206
204
|
const liveButton = Boolean(value("live_badge_enabled"));
|
|
@@ -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
|
|
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,11 +24,17 @@ 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;
|
|
@@ -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
|
+
};
|
package/src/Player/index.tsx
CHANGED
|
@@ -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 } =
|
|
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,
|
|
@@ -1005,10 +1011,12 @@ export default class VideoPlayer extends React.Component<
|
|
|
1005
1011
|
inline,
|
|
1006
1012
|
docked,
|
|
1007
1013
|
isModal,
|
|
1014
|
+
pip: this.state.piped,
|
|
1008
1015
|
layoutState: {
|
|
1009
1016
|
inline,
|
|
1010
1017
|
docked,
|
|
1011
1018
|
isModal,
|
|
1019
|
+
pip: this.state.piped,
|
|
1012
1020
|
},
|
|
1013
1021
|
configuration: this.getPluginConfiguration(),
|
|
1014
1022
|
fullscreen: fullscreen,
|
|
@@ -1085,6 +1093,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1085
1093
|
listener={this.props.listener}
|
|
1086
1094
|
style={getStyle(playerDimensions)}
|
|
1087
1095
|
pluginConfiguration={this.getPluginConfiguration()}
|
|
1096
|
+
controller={this.props.controller}
|
|
1088
1097
|
>
|
|
1089
1098
|
{this.childrenWithPlayNext()}
|
|
1090
1099
|
</this.props.PlayerComponent>
|
|
@@ -1121,6 +1130,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1121
1130
|
listener={this.props.listener}
|
|
1122
1131
|
style={this.takeParentStyle}
|
|
1123
1132
|
pluginConfiguration={this.getPluginConfiguration()}
|
|
1133
|
+
controller={this.props.controller}
|
|
1124
1134
|
>
|
|
1125
1135
|
{this.childrenWithPlayNext()}
|
|
1126
1136
|
</this.props.PlayerComponent>
|