@applicaster/quick-brick-player 12.0.5-rc.2 → 12.0.5-rc.4
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.5-rc.
|
|
3
|
+
"version": "12.0.5-rc.4",
|
|
4
4
|
"description": "Quick Brick Player",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@applicaster/quick-brick-mobile-transport-controls": "12.0.5-rc.0",
|
|
40
40
|
"@applicaster/quick-brick-tv-transport-controls": "12.0.4",
|
|
41
|
-
"@applicaster/zapp-react-native-tvos-app": "12.0.5-rc.
|
|
42
|
-
"@applicaster/zapp-react-native-ui-components": "12.0.5-rc.
|
|
43
|
-
"@applicaster/zapp-react-native-utils": "12.0.5-rc.
|
|
41
|
+
"@applicaster/zapp-react-native-tvos-app": "12.0.5-rc.4",
|
|
42
|
+
"@applicaster/zapp-react-native-ui-components": "12.0.5-rc.4",
|
|
43
|
+
"@applicaster/zapp-react-native-utils": "12.0.5-rc.4",
|
|
44
44
|
"keymirror": "^0.1.1",
|
|
45
45
|
"query-string": "7.1.3",
|
|
46
46
|
"react-native-web-linear-gradient": "1.1.2",
|
|
@@ -15,15 +15,9 @@ 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 {
|
|
18
|
+
import { useEffect, useState } from "react";
|
|
19
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";
|
|
20
|
+
import { formatSpeed } from "./utils";
|
|
27
21
|
import { addQueryParam, getLiveUrl } from "../Utils";
|
|
28
22
|
import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
|
|
29
23
|
import {
|
|
@@ -40,6 +34,12 @@ import {
|
|
|
40
34
|
speed_1_5,
|
|
41
35
|
speed_2,
|
|
42
36
|
} from "@applicaster/quick-brick-mobile-transport-controls";
|
|
37
|
+
import {
|
|
38
|
+
SleepTimerController,
|
|
39
|
+
SleepTimerState,
|
|
40
|
+
SLEEP_TIMER_OFF,
|
|
41
|
+
} from "./SleepTimerManager";
|
|
42
|
+
import { SpeedController, PlaybackSpeed, DEFAULT_SPEED } from "./SpeedManager";
|
|
43
43
|
|
|
44
44
|
const { log_debug } = createLogger({
|
|
45
45
|
subsystem: "UIComponent",
|
|
@@ -161,17 +161,29 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
161
161
|
configuration?.audio_live_player_update_interval
|
|
162
162
|
);
|
|
163
163
|
|
|
164
|
-
const [
|
|
165
|
-
|
|
166
|
-
value: 1.0,
|
|
167
|
-
});
|
|
164
|
+
const [sleepTimerManager, setSleepTimerManager] =
|
|
165
|
+
useState<SleepTimerController | null>(null);
|
|
168
166
|
|
|
169
|
-
const [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
167
|
+
const [speedManager, setSpeedManager] = useState<SpeedController | null>(
|
|
168
|
+
null
|
|
169
|
+
);
|
|
173
170
|
|
|
174
|
-
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (player) {
|
|
173
|
+
setSleepTimerManager(new SleepTimerController(player));
|
|
174
|
+
setSpeedManager(new SpeedController(player));
|
|
175
|
+
} else {
|
|
176
|
+
setSleepTimerManager(null);
|
|
177
|
+
setSpeedManager(null);
|
|
178
|
+
}
|
|
179
|
+
}, [player]);
|
|
180
|
+
|
|
181
|
+
const [playbackSpeed, setPlaybackSpeed] =
|
|
182
|
+
useState<PlaybackSpeed>(DEFAULT_SPEED);
|
|
183
|
+
|
|
184
|
+
const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
|
|
185
|
+
new SleepTimerState(SLEEP_TIMER_OFF, 0)
|
|
186
|
+
);
|
|
175
187
|
|
|
176
188
|
const [defaultDimensions, setDefaultDimensions] =
|
|
177
189
|
React.useState<Dimensions>(undefined);
|
|
@@ -196,90 +208,27 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
196
208
|
if (!defaultDimensions) setDefaultDimensions({ width, height });
|
|
197
209
|
};
|
|
198
210
|
|
|
199
|
-
const cancelSleepTimer = useCallback(async () => {
|
|
200
|
-
player?.cancelSleepTimer();
|
|
201
|
-
|
|
202
|
-
setSleepTimer({
|
|
203
|
-
label: "playback_sleep_off_label",
|
|
204
|
-
value: 0,
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
setSleepTimerDate(0);
|
|
208
|
-
|
|
209
|
-
await localStorage.removeItem("sleep", NAMESPACE);
|
|
210
|
-
}, [player, setSleepTimer, setSleepTimerDate]);
|
|
211
|
-
|
|
212
|
-
const startSleepTimer = useCallback(
|
|
213
|
-
(timerValue: number) => {
|
|
214
|
-
try {
|
|
215
|
-
const futureSleepDate = Date.now() + timerValue * 60000;
|
|
216
|
-
|
|
217
|
-
player?.cancelSleepTimer?.();
|
|
218
|
-
|
|
219
|
-
player?.startSleepTimer?.(futureSleepDate);
|
|
220
|
-
|
|
221
|
-
return futureSleepDate;
|
|
222
|
-
} catch (e) {
|
|
223
|
-
return 0;
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
[player]
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
const handleSleep = useCallback(
|
|
230
|
-
async (timerValue: number, currentFutureSleepDate = null) => {
|
|
231
|
-
if (currentFutureSleepDate) {
|
|
232
|
-
if (hasDatePassed(currentFutureSleepDate)) {
|
|
233
|
-
await cancelSleepTimer();
|
|
234
|
-
|
|
235
|
-
return 0;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return currentFutureSleepDate;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (timerValue === 0) {
|
|
242
|
-
await cancelSleepTimer();
|
|
243
|
-
|
|
244
|
-
return timerValue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return startSleepTimer(timerValue);
|
|
248
|
-
},
|
|
249
|
-
[startSleepTimer, cancelSleepTimer]
|
|
250
|
-
);
|
|
251
|
-
|
|
252
211
|
useEffect(() => {
|
|
253
|
-
const
|
|
254
|
-
|
|
212
|
+
const sleepSubscription =
|
|
213
|
+
sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
|
|
255
214
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
player?.setPlaybackRate(speed.value);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const loadSleep = async () => {
|
|
263
|
-
const sleep = await loadItemsFromStorage("sleep");
|
|
215
|
+
const speedSubscription =
|
|
216
|
+
speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
|
|
264
217
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
|
|
269
|
-
setSleepTimerDate(sleepTimerDate);
|
|
270
|
-
}
|
|
218
|
+
return () => {
|
|
219
|
+
sleepSubscription?.unsubscribe();
|
|
220
|
+
speedSubscription?.unsubscribe();
|
|
271
221
|
};
|
|
222
|
+
}, [sleepTimerManager, speedManager]);
|
|
272
223
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
player,
|
|
282
|
-
]);
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
if (!playerState.isReadyToPlay) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
void speedManager?.loadSpeed();
|
|
230
|
+
void sleepTimerManager?.loadSleep();
|
|
231
|
+
}, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
|
|
283
232
|
|
|
284
233
|
const getValue = (key: string, stylesObj: {}) =>
|
|
285
234
|
R.propOr(null, key, stylesObj);
|
|
@@ -322,26 +271,16 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
322
271
|
|
|
323
272
|
const onSpeedSelect = React.useCallback(
|
|
324
273
|
async (item) => {
|
|
325
|
-
setPlaybackSpeed(item);
|
|
326
|
-
player?.setPlaybackRate(item.value);
|
|
327
|
-
await localStorage.setItem("speed", item, NAMESPACE);
|
|
274
|
+
await speedManager?.setPlaybackSpeed(item);
|
|
328
275
|
},
|
|
329
|
-
[
|
|
276
|
+
[speedManager]
|
|
330
277
|
);
|
|
331
278
|
|
|
332
279
|
const onSleepSelect = React.useCallback(
|
|
333
280
|
async (item) => {
|
|
334
|
-
setSleepTimer(item);
|
|
335
|
-
const futureSleepDate = await handleSleep(item.value);
|
|
336
|
-
setSleepTimerDate(futureSleepDate);
|
|
337
|
-
|
|
338
|
-
await localStorage.setItem(
|
|
339
|
-
"sleep",
|
|
340
|
-
{ ...item, futureSleepDate },
|
|
341
|
-
NAMESPACE
|
|
342
|
-
);
|
|
281
|
+
await sleepTimerManager?.setSleepTimer(item);
|
|
343
282
|
},
|
|
344
|
-
[
|
|
283
|
+
[sleepTimerManager]
|
|
345
284
|
);
|
|
346
285
|
|
|
347
286
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
@@ -373,7 +312,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
373
312
|
value: 0,
|
|
374
313
|
},
|
|
375
314
|
].concat(sleeps),
|
|
376
|
-
current_selection:
|
|
315
|
+
current_selection: sleepTimerState.timer,
|
|
377
316
|
onPress: onSleepSelect,
|
|
378
317
|
title: configuration["playback_sleep_title"],
|
|
379
318
|
},
|
|
@@ -383,12 +322,20 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
383
322
|
});
|
|
384
323
|
}, [
|
|
385
324
|
navigator,
|
|
386
|
-
|
|
325
|
+
sleepTimerState.timer,
|
|
387
326
|
configuration["playback_sleep_title"],
|
|
388
327
|
configuration["playback_sleep_off_label"],
|
|
389
328
|
onSleepSelect,
|
|
390
329
|
]);
|
|
391
330
|
|
|
331
|
+
// This method does not actually cancel the sleep timer
|
|
332
|
+
// (and should not to avoid race condition),
|
|
333
|
+
// it just performs the cleanup when timer expires
|
|
334
|
+
// TODO: rename it in code above
|
|
335
|
+
const cancelSleepTimer = React.useCallback(() => {
|
|
336
|
+
sleepTimerManager?.onTimerExpired();
|
|
337
|
+
}, [sleepTimerManager]);
|
|
338
|
+
|
|
392
339
|
const onCloseVideoModal = React.useCallback(() => {
|
|
393
340
|
playerManager.close();
|
|
394
341
|
}, [playerManager]);
|
|
@@ -436,9 +383,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
436
383
|
onSleepButtonPress={onSleepButtonPress}
|
|
437
384
|
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
438
385
|
playbackSleepName={
|
|
439
|
-
|
|
386
|
+
sleepTimerState.timer.value === 0
|
|
387
|
+
? "sleep_timer"
|
|
388
|
+
: "sleep_timer_active"
|
|
440
389
|
}
|
|
441
|
-
sleepTimerDate={
|
|
390
|
+
sleepTimerDate={sleepTimerState.timerDate}
|
|
442
391
|
cancelSleepTimer={cancelSleepTimer}
|
|
443
392
|
onSkipLive={onSkipLive}
|
|
444
393
|
onCloseVideoModal={onCloseVideoModal}
|
|
@@ -488,8 +437,8 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
488
437
|
playNextData,
|
|
489
438
|
onSkipStart,
|
|
490
439
|
onSkipLive,
|
|
491
|
-
|
|
492
|
-
|
|
440
|
+
sleepTimerState.timer.value,
|
|
441
|
+
sleepTimerState.timerDate,
|
|
493
442
|
]);
|
|
494
443
|
|
|
495
444
|
if (configuration.magic_background) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
3
|
+
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
4
|
+
import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
|
|
5
|
+
import { hasDatePassed, NAMESPACE } from "./utils";
|
|
6
|
+
|
|
7
|
+
const { log_debug, log_error } = createLogger({
|
|
8
|
+
subsystem: "Player",
|
|
9
|
+
category: "SleepTimerManager",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export type SleepTimer = {
|
|
13
|
+
label: string;
|
|
14
|
+
// interval in minutes for UI at the moment when timer was first started
|
|
15
|
+
value: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const SLEEP_TIMER_OFF: SleepTimer = {
|
|
19
|
+
label: "playback_sleep_off_label",
|
|
20
|
+
value: 0,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export class SleepTimerState {
|
|
24
|
+
constructor(
|
|
25
|
+
public timer: SleepTimer,
|
|
26
|
+
public timerDate: number
|
|
27
|
+
) {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const SLEEP_PREFERENCE_KEY = "sleep";
|
|
31
|
+
|
|
32
|
+
export class SleepTimerController {
|
|
33
|
+
private sleepTimerStateSubject = new BehaviorSubject<SleepTimerState>(
|
|
34
|
+
new SleepTimerState(SLEEP_TIMER_OFF, 0)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
constructor(private player: Player) {}
|
|
38
|
+
|
|
39
|
+
get sleepTimerState$() {
|
|
40
|
+
return this.sleepTimerStateSubject.asObservable();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private async scheduleSleep(sleepDate): Promise<boolean> {
|
|
44
|
+
log_debug(`Setting sleep interval to ${sleepDate}`);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
this.player.cancelSleepTimer?.();
|
|
48
|
+
this.player.startSleepTimer?.(sleepDate);
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
} catch (e) {
|
|
52
|
+
log_error("Failed to start sleep timer", { error: e.message });
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async setSleepTimer(item: SleepTimer) {
|
|
59
|
+
// User has selected Off state
|
|
60
|
+
if (item.value === 0) {
|
|
61
|
+
log_debug("Cancelling sleep timer");
|
|
62
|
+
this.player.cancelSleepTimer?.();
|
|
63
|
+
|
|
64
|
+
return this.onTimerExpired();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
log_debug(`Starting sleep timer in ${item.value} minutes`);
|
|
68
|
+
|
|
69
|
+
const futureSleepDate = Date.now() + item.value * 60000;
|
|
70
|
+
|
|
71
|
+
if (await this.scheduleSleep(futureSleepDate)) {
|
|
72
|
+
await localStorage.setItem(
|
|
73
|
+
SLEEP_PREFERENCE_KEY,
|
|
74
|
+
{ ...item, futureSleepDate },
|
|
75
|
+
NAMESPACE
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
this.sleepTimerStateSubject.next(
|
|
79
|
+
new SleepTimerState(item, futureSleepDate)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async onTimerExpired() {
|
|
85
|
+
await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
|
|
86
|
+
|
|
87
|
+
this.sleepTimerStateSubject.next(new SleepTimerState(SLEEP_TIMER_OFF, 0));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async loadSleep() {
|
|
91
|
+
const sleep = await localStorage.getItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
|
|
92
|
+
|
|
93
|
+
if (!sleep) return;
|
|
94
|
+
|
|
95
|
+
const { futureSleepDate, ...rest } = sleep;
|
|
96
|
+
|
|
97
|
+
if (!futureSleepDate || hasDatePassed(futureSleepDate)) {
|
|
98
|
+
log_debug("Sleep date has already passed");
|
|
99
|
+
await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
|
|
100
|
+
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (await this.scheduleSleep(futureSleepDate)) {
|
|
105
|
+
log_debug(`Sleep state restored, will go to sleep at ${futureSleepDate}`);
|
|
106
|
+
|
|
107
|
+
this.sleepTimerStateSubject.next(
|
|
108
|
+
new SleepTimerState(rest as SleepTimer, futureSleepDate)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Will be called if user closed the player explicitly.
|
|
114
|
+
// TODO: check if it will be called on Android when we need.
|
|
115
|
+
// Probably we will have to handle it on native instead.
|
|
116
|
+
public static async onPlayerDestroy() {
|
|
117
|
+
await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
3
|
+
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
4
|
+
import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
|
|
5
|
+
import { NAMESPACE } from "./utils";
|
|
6
|
+
|
|
7
|
+
const { log_debug } = createLogger({
|
|
8
|
+
subsystem: "Player",
|
|
9
|
+
category: "PlaybackSpeedManager",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export type PlaybackSpeed = {
|
|
13
|
+
asset: string;
|
|
14
|
+
value: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const SPEED_PREFERENCE_KEY = "speed";
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_SPEED: PlaybackSpeed = {
|
|
20
|
+
asset: "speed_1",
|
|
21
|
+
value: 1.0,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// todo: should not be applied to live streams
|
|
25
|
+
export class SpeedController {
|
|
26
|
+
private playbackSpeedSubject = new BehaviorSubject<PlaybackSpeed>(
|
|
27
|
+
DEFAULT_SPEED
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
constructor(private player: Player) {}
|
|
31
|
+
|
|
32
|
+
get playbackSpeed$() {
|
|
33
|
+
return this.playbackSpeedSubject.asObservable();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async loadSpeed() {
|
|
37
|
+
const speed = await localStorage.getItem(SPEED_PREFERENCE_KEY, NAMESPACE);
|
|
38
|
+
|
|
39
|
+
// checking for value in case we will pull empty or broken object
|
|
40
|
+
if (speed && speed.value) {
|
|
41
|
+
this.playbackSpeedSubject.next(speed);
|
|
42
|
+
this.player.setPlaybackRate?.(speed.value);
|
|
43
|
+
log_debug(`Speed loaded: ${speed}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async setPlaybackSpeed(speed: PlaybackSpeed) {
|
|
48
|
+
await localStorage.setItem(SPEED_PREFERENCE_KEY, speed, NAMESPACE);
|
|
49
|
+
this.playbackSpeedSubject.next(speed);
|
|
50
|
+
this.player.setPlaybackRate?.(speed.value);
|
|
51
|
+
}
|
|
52
|
+
}
|