@applicaster/quick-brick-player 13.0.0-alpha.3013227535 → 13.0.0-alpha.3193563717
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": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.3193563717",
|
|
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": "13.0.0-rc.
|
|
40
|
-
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.
|
|
41
|
-
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.
|
|
42
|
-
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.
|
|
43
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.
|
|
39
|
+
"@applicaster/quick-brick-mobile-transport-controls": "13.0.0-rc.28",
|
|
40
|
+
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.31",
|
|
41
|
+
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.3193563717",
|
|
42
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.3193563717",
|
|
43
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.3193563717",
|
|
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 {
|
|
@@ -33,6 +27,19 @@ import {
|
|
|
33
27
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
34
28
|
import { PADDING_HORIZONTAL } from "./Layout/defaultLayoutsConts";
|
|
35
29
|
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
30
|
+
import {
|
|
31
|
+
speed_0_8,
|
|
32
|
+
speed_1,
|
|
33
|
+
speed_1_2,
|
|
34
|
+
speed_1_5,
|
|
35
|
+
speed_2,
|
|
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";
|
|
36
43
|
|
|
37
44
|
const { log_debug } = createLogger({
|
|
38
45
|
subsystem: "UIComponent",
|
|
@@ -132,11 +139,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
132
139
|
} = props;
|
|
133
140
|
|
|
134
141
|
const speeds = [
|
|
135
|
-
{ asset: configuration["speed_0_8"], value: 0.8 },
|
|
136
|
-
{ asset: configuration["speed_1"], value: 1.0 },
|
|
137
|
-
{ asset: configuration["speed_1_2"], value: 1.2 },
|
|
138
|
-
{ asset: configuration["speed_1_5"], value: 1.5 },
|
|
139
|
-
{ asset: configuration["speed_2"], value: 2.0 },
|
|
142
|
+
{ asset: configuration["speed_0_8"] || speed_0_8, value: 0.8 },
|
|
143
|
+
{ asset: configuration["speed_1"] || speed_1, value: 1.0 },
|
|
144
|
+
{ asset: configuration["speed_1_2"] || speed_1_2, value: 1.2 },
|
|
145
|
+
{ asset: configuration["speed_1_5"] || speed_1_5, value: 1.5 },
|
|
146
|
+
{ asset: configuration["speed_2"] || speed_2, value: 2.0 },
|
|
140
147
|
];
|
|
141
148
|
|
|
142
149
|
const isTablet = useIsTablet();
|
|
@@ -154,17 +161,29 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
154
161
|
configuration?.audio_live_player_update_interval
|
|
155
162
|
);
|
|
156
163
|
|
|
157
|
-
const [
|
|
158
|
-
|
|
159
|
-
value: 1.0,
|
|
160
|
-
});
|
|
164
|
+
const [sleepTimerManager, setSleepTimerManager] =
|
|
165
|
+
useState<SleepTimerController | null>(null);
|
|
161
166
|
|
|
162
|
-
const [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
167
|
+
const [speedManager, setSpeedManager] = useState<SpeedController | null>(
|
|
168
|
+
null
|
|
169
|
+
);
|
|
166
170
|
|
|
167
|
-
|
|
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
|
+
);
|
|
168
187
|
|
|
169
188
|
const [defaultDimensions, setDefaultDimensions] =
|
|
170
189
|
React.useState<Dimensions>(undefined);
|
|
@@ -189,90 +208,27 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
189
208
|
if (!defaultDimensions) setDefaultDimensions({ width, height });
|
|
190
209
|
};
|
|
191
210
|
|
|
192
|
-
const cancelSleepTimer = useCallback(async () => {
|
|
193
|
-
player?.cancelSleepTimer();
|
|
194
|
-
|
|
195
|
-
setSleepTimer({
|
|
196
|
-
label: "playback_sleep_off_label",
|
|
197
|
-
value: 0,
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
setSleepTimerDate(0);
|
|
201
|
-
|
|
202
|
-
await localStorage.removeItem("sleep", NAMESPACE);
|
|
203
|
-
}, [player, setSleepTimer, setSleepTimerDate]);
|
|
204
|
-
|
|
205
|
-
const startSleepTimer = useCallback(
|
|
206
|
-
(timerValue: number) => {
|
|
207
|
-
try {
|
|
208
|
-
const futureSleepDate = Date.now() + timerValue * 60000;
|
|
209
|
-
|
|
210
|
-
player?.cancelSleepTimer?.();
|
|
211
|
-
|
|
212
|
-
player?.startSleepTimer?.(futureSleepDate);
|
|
213
|
-
|
|
214
|
-
return futureSleepDate;
|
|
215
|
-
} catch (e) {
|
|
216
|
-
return 0;
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
[player]
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
const handleSleep = useCallback(
|
|
223
|
-
async (timerValue: number, currentFutureSleepDate = null) => {
|
|
224
|
-
if (currentFutureSleepDate) {
|
|
225
|
-
if (hasDatePassed(currentFutureSleepDate)) {
|
|
226
|
-
await cancelSleepTimer();
|
|
227
|
-
|
|
228
|
-
return 0;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return currentFutureSleepDate;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (timerValue === 0) {
|
|
235
|
-
await cancelSleepTimer();
|
|
236
|
-
|
|
237
|
-
return timerValue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return startSleepTimer(timerValue);
|
|
241
|
-
},
|
|
242
|
-
[startSleepTimer, cancelSleepTimer]
|
|
243
|
-
);
|
|
244
|
-
|
|
245
211
|
useEffect(() => {
|
|
246
|
-
const
|
|
247
|
-
|
|
212
|
+
const sleepSubscription =
|
|
213
|
+
sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
|
|
248
214
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
player?.setPlaybackRate(speed.value);
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const loadSleep = async () => {
|
|
256
|
-
const sleep = await loadItemsFromStorage("sleep");
|
|
215
|
+
const speedSubscription =
|
|
216
|
+
speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
|
|
257
217
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
|
|
262
|
-
setSleepTimerDate(sleepTimerDate);
|
|
263
|
-
}
|
|
218
|
+
return () => {
|
|
219
|
+
sleepSubscription?.unsubscribe();
|
|
220
|
+
speedSubscription?.unsubscribe();
|
|
264
221
|
};
|
|
222
|
+
}, [sleepTimerManager, speedManager]);
|
|
265
223
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
player,
|
|
275
|
-
]);
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
if (!playerState.isReadyToPlay) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
void speedManager?.loadSpeed();
|
|
230
|
+
void sleepTimerManager?.loadSleep();
|
|
231
|
+
}, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
|
|
276
232
|
|
|
277
233
|
const getValue = (key: string, stylesObj: {}) =>
|
|
278
234
|
R.propOr(null, key, stylesObj);
|
|
@@ -315,26 +271,16 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
315
271
|
|
|
316
272
|
const onSpeedSelect = React.useCallback(
|
|
317
273
|
async (item) => {
|
|
318
|
-
setPlaybackSpeed(item);
|
|
319
|
-
player?.setPlaybackRate(item.value);
|
|
320
|
-
await localStorage.setItem("speed", item, NAMESPACE);
|
|
274
|
+
await speedManager?.setPlaybackSpeed(item);
|
|
321
275
|
},
|
|
322
|
-
[
|
|
276
|
+
[speedManager]
|
|
323
277
|
);
|
|
324
278
|
|
|
325
279
|
const onSleepSelect = React.useCallback(
|
|
326
280
|
async (item) => {
|
|
327
|
-
setSleepTimer(item);
|
|
328
|
-
const futureSleepDate = await handleSleep(item.value);
|
|
329
|
-
setSleepTimerDate(futureSleepDate);
|
|
330
|
-
|
|
331
|
-
await localStorage.setItem(
|
|
332
|
-
"sleep",
|
|
333
|
-
{ ...item, futureSleepDate },
|
|
334
|
-
NAMESPACE
|
|
335
|
-
);
|
|
281
|
+
await sleepTimerManager?.setSleepTimer(item);
|
|
336
282
|
},
|
|
337
|
-
[
|
|
283
|
+
[sleepTimerManager]
|
|
338
284
|
);
|
|
339
285
|
|
|
340
286
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
@@ -361,9 +307,12 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
361
307
|
navigator.openModalBottomSheet({
|
|
362
308
|
modalBottomSheetContentProps: {
|
|
363
309
|
items: [
|
|
364
|
-
{
|
|
310
|
+
{
|
|
311
|
+
label: configuration["playback_sleep_off_label"],
|
|
312
|
+
value: 0,
|
|
313
|
+
},
|
|
365
314
|
].concat(sleeps),
|
|
366
|
-
current_selection:
|
|
315
|
+
current_selection: sleepTimerState.timer,
|
|
367
316
|
onPress: onSleepSelect,
|
|
368
317
|
title: configuration["playback_sleep_title"],
|
|
369
318
|
},
|
|
@@ -373,12 +322,20 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
373
322
|
});
|
|
374
323
|
}, [
|
|
375
324
|
navigator,
|
|
376
|
-
|
|
325
|
+
sleepTimerState.timer,
|
|
377
326
|
configuration["playback_sleep_title"],
|
|
378
327
|
configuration["playback_sleep_off_label"],
|
|
379
328
|
onSleepSelect,
|
|
380
329
|
]);
|
|
381
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
|
+
|
|
382
339
|
const onCloseVideoModal = React.useCallback(() => {
|
|
383
340
|
playerManager.close();
|
|
384
341
|
}, [playerManager]);
|
|
@@ -426,9 +383,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
426
383
|
onSleepButtonPress={onSleepButtonPress}
|
|
427
384
|
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
428
385
|
playbackSleepName={
|
|
429
|
-
|
|
386
|
+
sleepTimerState.timer.value === 0
|
|
387
|
+
? "sleep_timer"
|
|
388
|
+
: "sleep_timer_active"
|
|
430
389
|
}
|
|
431
|
-
sleepTimerDate={
|
|
390
|
+
sleepTimerDate={sleepTimerState.timerDate}
|
|
432
391
|
cancelSleepTimer={cancelSleepTimer}
|
|
433
392
|
onSkipLive={onSkipLive}
|
|
434
393
|
onCloseVideoModal={onCloseVideoModal}
|
|
@@ -478,8 +437,8 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
478
437
|
playNextData,
|
|
479
438
|
onSkipStart,
|
|
480
439
|
onSkipLive,
|
|
481
|
-
|
|
482
|
-
|
|
440
|
+
sleepTimerState.timer.value,
|
|
441
|
+
sleepTimerState.timerDate,
|
|
483
442
|
]);
|
|
484
443
|
|
|
485
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
|
+
}
|