mirror of
https://github.com/p-stream/p-stream.git
synced 2026-08-08 16:08:13 +00:00
We have short attention spans
now instead of watching your entire movie at one speed, you can do it in VARYING SPEEDS!
This commit is contained in:
parent
9bfb2f8628
commit
02bace8a47
9 changed files with 356 additions and 12 deletions
42
src/components/player/atoms/SpeedChangedPopout.tsx
Normal file
42
src/components/player/atoms/SpeedChangedPopout.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { Flare } from "@/components/utils/Flare";
|
||||
import { Transition } from "@/components/utils/Transition";
|
||||
import { useOverlayStack } from "@/stores/interface/overlayStack";
|
||||
import { usePlayerStore } from "@/stores/player/store";
|
||||
|
||||
export function SpeedChangedPopout() {
|
||||
const isSpeedBoosted = usePlayerStore((s) => s.interface.isSpeedBoosted);
|
||||
const showSpeedIndicator = usePlayerStore(
|
||||
(s) => s.interface.showSpeedIndicator,
|
||||
);
|
||||
const currentOverlay = useOverlayStack((s) => s.currentOverlay);
|
||||
const playbackRate = usePlayerStore((s) => s.mediaPlaying.playbackRate);
|
||||
|
||||
return (
|
||||
<Transition
|
||||
animation="slide-down"
|
||||
show={showSpeedIndicator && currentOverlay === "speed"}
|
||||
className="absolute inset-x-0 top-4 flex justify-center pointer-events-none"
|
||||
>
|
||||
<Flare.Base className="hover:flare-enabled pointer-events-auto bg-video-context-background pl-4 pr-6 py-3 group w-72 h-full rounded-lg transition-colors text-video-context-type-main">
|
||||
<Flare.Light
|
||||
enabled
|
||||
flareSize={200}
|
||||
cssColorVar="--colors-video-context-light"
|
||||
backgroundClass="bg-video-context-background duration-100"
|
||||
className="rounded-lg"
|
||||
/>
|
||||
<Flare.Child className="grid grid-cols-[auto,1fr] gap-3 pointer-events-auto relative transition-transform">
|
||||
<Icon className="text-2xl" icon={Icons.TACHOMETER} />
|
||||
<div className="w-full flex items-center justify-between">
|
||||
<span className="text-sm">
|
||||
{isSpeedBoosted
|
||||
? "Boosted to 2x speed"
|
||||
: `Speed: ${playbackRate}x`}
|
||||
</span>
|
||||
</div>
|
||||
</Flare.Child>
|
||||
</Flare.Base>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
|
@ -18,3 +18,4 @@ export * from "./NextEpisodeButton";
|
|||
export * from "./Chromecast";
|
||||
export * from "./CastingNotification";
|
||||
export * from "./Captions";
|
||||
export * from "./SpeedChangedPopout";
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ export function SubtitleRenderer() {
|
|||
const styling = useSubtitleStore((s) => s.styling);
|
||||
const overrideCasing = useSubtitleStore((s) => s.overrideCasing);
|
||||
const delay = useSubtitleStore((s) => s.delay);
|
||||
const playbackRate = usePlayerStore((s) => s.mediaPlaying.playbackRate);
|
||||
|
||||
const parsedCaptions = useMemo(
|
||||
() => (srtData ? parseSubtitles(srtData, language) : []),
|
||||
|
|
@ -125,9 +126,9 @@ export function SubtitleRenderer() {
|
|||
const visibileCaptions = useMemo(
|
||||
() =>
|
||||
parsedCaptions.filter(({ start, end }) =>
|
||||
captionIsVisible(start, end, delay, videoTime),
|
||||
captionIsVisible(start, end, delay, videoTime, playbackRate),
|
||||
),
|
||||
[parsedCaptions, videoTime, delay],
|
||||
[parsedCaptions, videoTime, delay, playbackRate],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ export function KeyboardEvents() {
|
|||
const mediaPlaying = usePlayerStore((s) => s.mediaPlaying);
|
||||
const time = usePlayerStore((s) => s.progress.time);
|
||||
const { setVolume, toggleMute } = useVolume();
|
||||
const setSpeedBoosted = usePlayerStore((s) => s.setSpeedBoosted);
|
||||
const setShowSpeedIndicator = usePlayerStore((s) => s.setShowSpeedIndicator);
|
||||
|
||||
const { toggleLastUsed } = useCaptions();
|
||||
const setShowVolume = useEmpheralVolumeStore((s) => s.setShowVolume);
|
||||
|
|
@ -28,6 +30,15 @@ export function KeyboardEvents() {
|
|||
const [isRolling, setIsRolling] = useState(false);
|
||||
const volumeDebounce = useRef<ReturnType<typeof setTimeout> | undefined>();
|
||||
const subtitleDebounce = useRef<ReturnType<typeof setTimeout> | undefined>();
|
||||
const speedIndicatorTimeoutRef = useRef<
|
||||
ReturnType<typeof setTimeout> | undefined
|
||||
>();
|
||||
const boostTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>();
|
||||
const isPendingBoostRef = useRef<boolean>(false);
|
||||
|
||||
// Tracking previous playback rate when space is held down
|
||||
const previousRateRef = useRef<number>(1);
|
||||
const isSpaceHeldRef = useRef<boolean>(false);
|
||||
|
||||
const setCurrentOverlay = useOverlayStack((s) => s.setCurrentOverlay);
|
||||
|
||||
|
|
@ -48,6 +59,13 @@ export function KeyboardEvents() {
|
|||
delay,
|
||||
setShowDelayIndicator,
|
||||
setCurrentOverlay,
|
||||
previousRateRef,
|
||||
isSpaceHeldRef,
|
||||
setSpeedBoosted,
|
||||
setShowSpeedIndicator,
|
||||
speedIndicatorTimeoutRef,
|
||||
boostTimeoutRef,
|
||||
isPendingBoostRef,
|
||||
});
|
||||
useEffect(() => {
|
||||
dataRef.current = {
|
||||
|
|
@ -67,6 +85,13 @@ export function KeyboardEvents() {
|
|||
delay,
|
||||
setShowDelayIndicator,
|
||||
setCurrentOverlay,
|
||||
previousRateRef,
|
||||
isSpaceHeldRef,
|
||||
setSpeedBoosted,
|
||||
setShowSpeedIndicator,
|
||||
speedIndicatorTimeoutRef,
|
||||
boostTimeoutRef,
|
||||
isPendingBoostRef,
|
||||
};
|
||||
}, [
|
||||
setShowVolume,
|
||||
|
|
@ -85,10 +110,12 @@ export function KeyboardEvents() {
|
|||
delay,
|
||||
setShowDelayIndicator,
|
||||
setCurrentOverlay,
|
||||
setSpeedBoosted,
|
||||
setShowSpeedIndicator,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const keyEventHandler = (evt: KeyboardEvent) => {
|
||||
const keydownEventHandler = (evt: KeyboardEvent) => {
|
||||
if (evt.target && (evt.target as HTMLInputElement).nodeName === "INPUT")
|
||||
return;
|
||||
|
||||
|
|
@ -126,6 +153,57 @@ export function KeyboardEvents() {
|
|||
if (next) dataRef.current.display?.setPlaybackRate(next);
|
||||
}
|
||||
|
||||
// Handle spacebar hold for 2x speed
|
||||
if (
|
||||
k === " " &&
|
||||
!dataRef.current.isSpaceHeldRef.current &&
|
||||
!dataRef.current.mediaPlaying.isPaused &&
|
||||
!dataRef.current.isPendingBoostRef.current
|
||||
) {
|
||||
// Skip if a button is targeted
|
||||
if (
|
||||
evt.target &&
|
||||
(evt.target as HTMLInputElement).nodeName === "BUTTON"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent the default spacebar behavior (play/pause)
|
||||
evt.preventDefault();
|
||||
|
||||
// Save current rate
|
||||
dataRef.current.previousRateRef.current =
|
||||
dataRef.current.mediaPlaying.playbackRate;
|
||||
|
||||
// Set pending boost flag
|
||||
dataRef.current.isPendingBoostRef.current = true;
|
||||
|
||||
// Add delay before boosting speed
|
||||
if (dataRef.current.boostTimeoutRef.current) {
|
||||
clearTimeout(dataRef.current.boostTimeoutRef.current);
|
||||
}
|
||||
|
||||
dataRef.current.boostTimeoutRef.current = setTimeout(() => {
|
||||
// Only apply boost if the key is still held down
|
||||
if (dataRef.current.isPendingBoostRef.current) {
|
||||
dataRef.current.isSpaceHeldRef.current = true;
|
||||
dataRef.current.isPendingBoostRef.current = false;
|
||||
|
||||
// Show speed indicator
|
||||
dataRef.current.setSpeedBoosted(true);
|
||||
dataRef.current.setShowSpeedIndicator(true);
|
||||
dataRef.current.setCurrentOverlay("speed");
|
||||
|
||||
// Clear any existing timeout
|
||||
if (dataRef.current.speedIndicatorTimeoutRef.current) {
|
||||
clearTimeout(dataRef.current.speedIndicatorTimeoutRef.current);
|
||||
}
|
||||
|
||||
dataRef.current.display?.setPlaybackRate(2);
|
||||
}
|
||||
}, 300); // 300ms delay before boost takes effect
|
||||
}
|
||||
|
||||
// Video progress
|
||||
if (k === "ArrowRight")
|
||||
dataRef.current.display?.setTime(dataRef.current.time + 5);
|
||||
|
|
@ -142,7 +220,10 @@ export function KeyboardEvents() {
|
|||
|
||||
// Utils
|
||||
if (keyL === "f") dataRef.current.display?.toggleFullscreen();
|
||||
if (k === " " || keyL === "k") {
|
||||
if (
|
||||
(k === " " || keyL === "k") &&
|
||||
!dataRef.current.isSpaceHeldRef.current
|
||||
) {
|
||||
if (
|
||||
evt.target &&
|
||||
(evt.target as HTMLInputElement).nodeName === "BUTTON"
|
||||
|
|
@ -187,10 +268,53 @@ export function KeyboardEvents() {
|
|||
}, 3000);
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", keyEventHandler);
|
||||
|
||||
const keyupEventHandler = (evt: KeyboardEvent) => {
|
||||
const k = evt.key;
|
||||
|
||||
// Handle spacebar release
|
||||
if (k === " ") {
|
||||
// If we haven't applied the boost yet but were about to, cancel it
|
||||
if (dataRef.current.isPendingBoostRef.current) {
|
||||
dataRef.current.isPendingBoostRef.current = false;
|
||||
if (dataRef.current.boostTimeoutRef.current) {
|
||||
clearTimeout(dataRef.current.boostTimeoutRef.current);
|
||||
}
|
||||
|
||||
// The space key was released quickly, so trigger play/pause
|
||||
const action = dataRef.current.mediaPlaying.isPaused
|
||||
? "play"
|
||||
: "pause";
|
||||
dataRef.current.display?.[action]();
|
||||
} else if (dataRef.current.isSpaceHeldRef.current) {
|
||||
// We were in boost mode, restore previous rate
|
||||
dataRef.current.display?.setPlaybackRate(
|
||||
dataRef.current.previousRateRef.current,
|
||||
);
|
||||
dataRef.current.isSpaceHeldRef.current = false;
|
||||
|
||||
// Update UI state
|
||||
dataRef.current.setSpeedBoosted(false);
|
||||
|
||||
// Set a timeout to hide the speed indicator
|
||||
if (dataRef.current.speedIndicatorTimeoutRef.current) {
|
||||
clearTimeout(dataRef.current.speedIndicatorTimeoutRef.current);
|
||||
}
|
||||
|
||||
dataRef.current.speedIndicatorTimeoutRef.current = setTimeout(() => {
|
||||
dataRef.current.setShowSpeedIndicator(false);
|
||||
dataRef.current.setCurrentOverlay(null);
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", keydownEventHandler);
|
||||
window.addEventListener("keyup", keyupEventHandler);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", keyEventHandler);
|
||||
window.removeEventListener("keydown", keydownEventHandler);
|
||||
window.removeEventListener("keyup", keyupEventHandler);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import classNames from "classnames";
|
||||
import { PointerEvent, useCallback } from "react";
|
||||
import { PointerEvent, useCallback, useRef, useState } from "react";
|
||||
import { useEffectOnce, useTimeoutFn } from "react-use";
|
||||
|
||||
import { useShouldShowVideoElement } from "@/components/player/internals/VideoContainer";
|
||||
import { useOverlayStack } from "@/stores/interface/overlayStack";
|
||||
import { PlayerHoverState } from "@/stores/player/slices/interface";
|
||||
import { usePlayerStore } from "@/stores/player/store";
|
||||
|
||||
|
|
@ -10,10 +11,15 @@ export function VideoClickTarget(props: { showingControls: boolean }) {
|
|||
const show = useShouldShowVideoElement();
|
||||
const display = usePlayerStore((s) => s.display);
|
||||
const isPaused = usePlayerStore((s) => s.mediaPlaying.isPaused);
|
||||
const playbackRate = usePlayerStore((s) => s.mediaPlaying.playbackRate);
|
||||
const updateInterfaceHovering = usePlayerStore(
|
||||
(s) => s.updateInterfaceHovering,
|
||||
);
|
||||
const setSpeedBoosted = usePlayerStore((s) => s.setSpeedBoosted);
|
||||
const setShowSpeedIndicator = usePlayerStore((s) => s.setShowSpeedIndicator);
|
||||
const hovering = usePlayerStore((s) => s.interface.hovering);
|
||||
const setCurrentOverlay = useOverlayStack((s) => s.setCurrentOverlay);
|
||||
|
||||
const [_, cancel, reset] = useTimeoutFn(() => {
|
||||
updateInterfaceHovering(PlayerHoverState.NOT_HOVERING);
|
||||
}, 3000);
|
||||
|
|
@ -21,12 +27,31 @@ export function VideoClickTarget(props: { showingControls: boolean }) {
|
|||
cancel();
|
||||
});
|
||||
|
||||
const previousRateRef = useRef(playbackRate);
|
||||
const isHoldingRef = useRef(false);
|
||||
const speedIndicatorTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const boostTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const [isPendingBoost, setIsPendingBoost] = useState(false);
|
||||
|
||||
const toggleFullscreen = useCallback(() => {
|
||||
display?.toggleFullscreen();
|
||||
}, [display]);
|
||||
|
||||
const togglePause = useCallback(
|
||||
(e: PointerEvent<HTMLDivElement>) => {
|
||||
// Don't toggle pause if holding for speed change
|
||||
if (isHoldingRef.current) {
|
||||
isHoldingRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel any pending boost if we're clicking to pause
|
||||
if (isPendingBoost) {
|
||||
clearTimeout(boostTimeoutRef.current!);
|
||||
setIsPendingBoost(false);
|
||||
isHoldingRef.current = false;
|
||||
}
|
||||
|
||||
// pause on mouse click
|
||||
if (e.pointerType === "mouse") {
|
||||
if (e.button !== 0) return;
|
||||
|
|
@ -44,9 +69,136 @@ export function VideoClickTarget(props: { showingControls: boolean }) {
|
|||
cancel();
|
||||
}
|
||||
},
|
||||
[display, isPaused, hovering, updateInterfaceHovering, reset, cancel],
|
||||
[
|
||||
display,
|
||||
isPaused,
|
||||
hovering,
|
||||
updateInterfaceHovering,
|
||||
reset,
|
||||
cancel,
|
||||
isPendingBoost,
|
||||
],
|
||||
);
|
||||
|
||||
const handlePointerDown = useCallback(
|
||||
(e: PointerEvent<HTMLDivElement>) => {
|
||||
if (e.pointerType === "mouse" && e.button === 0 && !isPaused) {
|
||||
// Store current rate before changing
|
||||
previousRateRef.current = playbackRate;
|
||||
|
||||
// Set a timeout before actually boosting speed
|
||||
if (boostTimeoutRef.current) {
|
||||
clearTimeout(boostTimeoutRef.current);
|
||||
}
|
||||
|
||||
setIsPendingBoost(true);
|
||||
|
||||
boostTimeoutRef.current = setTimeout(() => {
|
||||
// Only apply boost if we're still holding down
|
||||
isHoldingRef.current = true;
|
||||
setIsPendingBoost(false);
|
||||
|
||||
// Show speed indicator
|
||||
setSpeedBoosted(true);
|
||||
setShowSpeedIndicator(true);
|
||||
setCurrentOverlay("speed");
|
||||
|
||||
if (speedIndicatorTimeoutRef.current) {
|
||||
clearTimeout(speedIndicatorTimeoutRef.current);
|
||||
}
|
||||
|
||||
// Set to 2x speed
|
||||
display?.setPlaybackRate(2);
|
||||
}, 300); // 300ms delay before boost takes effect
|
||||
}
|
||||
},
|
||||
[
|
||||
display,
|
||||
playbackRate,
|
||||
isPaused,
|
||||
setSpeedBoosted,
|
||||
setShowSpeedIndicator,
|
||||
setCurrentOverlay,
|
||||
],
|
||||
);
|
||||
|
||||
const handlePointerUp = useCallback(
|
||||
(e: PointerEvent<HTMLDivElement>) => {
|
||||
// If we have a pending boost that hasn't activated yet, clear it
|
||||
if (isPendingBoost) {
|
||||
clearTimeout(boostTimeoutRef.current!);
|
||||
setIsPendingBoost(false);
|
||||
togglePause(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHoldingRef.current && e.pointerType === "mouse" && e.button === 0) {
|
||||
// Restore previous rate
|
||||
display?.setPlaybackRate(previousRateRef.current);
|
||||
isHoldingRef.current = false;
|
||||
|
||||
// Update state for speed indicator
|
||||
setSpeedBoosted(false);
|
||||
|
||||
// Set a timeout to hide the speed indicator
|
||||
if (speedIndicatorTimeoutRef.current) {
|
||||
clearTimeout(speedIndicatorTimeoutRef.current);
|
||||
}
|
||||
|
||||
speedIndicatorTimeoutRef.current = setTimeout(() => {
|
||||
setShowSpeedIndicator(false);
|
||||
setCurrentOverlay(null);
|
||||
speedIndicatorTimeoutRef.current = null;
|
||||
}, 1500);
|
||||
} else {
|
||||
// Regular click handler
|
||||
togglePause(e);
|
||||
}
|
||||
},
|
||||
[
|
||||
display,
|
||||
togglePause,
|
||||
setSpeedBoosted,
|
||||
setShowSpeedIndicator,
|
||||
setCurrentOverlay,
|
||||
isPendingBoost,
|
||||
],
|
||||
);
|
||||
|
||||
// Handle case where mouse leaves the player while still pressed
|
||||
const handlePointerLeave = useCallback(() => {
|
||||
// Clear pending boost if mouse leaves
|
||||
if (isPendingBoost) {
|
||||
clearTimeout(boostTimeoutRef.current!);
|
||||
setIsPendingBoost(false);
|
||||
}
|
||||
|
||||
if (isHoldingRef.current) {
|
||||
display?.setPlaybackRate(previousRateRef.current);
|
||||
isHoldingRef.current = false;
|
||||
|
||||
// Update state for speed indicator
|
||||
setSpeedBoosted(false);
|
||||
|
||||
// Set a timeout to hide the speed indicator
|
||||
if (speedIndicatorTimeoutRef.current) {
|
||||
clearTimeout(speedIndicatorTimeoutRef.current);
|
||||
}
|
||||
|
||||
speedIndicatorTimeoutRef.current = setTimeout(() => {
|
||||
setShowSpeedIndicator(false);
|
||||
setCurrentOverlay(null);
|
||||
speedIndicatorTimeoutRef.current = null;
|
||||
}, 1500);
|
||||
}
|
||||
}, [
|
||||
display,
|
||||
setSpeedBoosted,
|
||||
setShowSpeedIndicator,
|
||||
setCurrentOverlay,
|
||||
isPendingBoost,
|
||||
]);
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
|
|
@ -56,7 +208,9 @@ export function VideoClickTarget(props: { showingControls: boolean }) {
|
|||
"cursor-none": !props.showingControls,
|
||||
})}
|
||||
onDoubleClick={toggleFullscreen}
|
||||
onPointerUp={togglePause}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerLeave={handlePointerLeave}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,14 @@ export function captionIsVisible(
|
|||
end: number,
|
||||
delay: number,
|
||||
currentTime: number,
|
||||
playbackRate: number = 1,
|
||||
) {
|
||||
// Calculate subtitle timing with delay adjustment
|
||||
// When playback is sped up, we need to adjust the subtitle windows accordingly
|
||||
// so they appear at the right time but also remain visible for the right duration
|
||||
const delayedStart = start / 1000 + delay;
|
||||
const delayedEnd = end / 1000 + delay;
|
||||
|
||||
return (
|
||||
Math.max(0, delayedStart) <= currentTime &&
|
||||
Math.max(0, delayedEnd) >= currentTime
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ export function PlayerPart(props: PlayerPartProps) {
|
|||
|
||||
<Player.VolumeChangedPopout />
|
||||
<Player.SubtitleDelayPopout />
|
||||
<Player.SpeedChangedPopout />
|
||||
<UnreleasedEpisodeOverlay />
|
||||
|
||||
<Player.NextEpisodeButton
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { create } from "zustand";
|
||||
|
||||
type OverlayType = "volume" | "subtitle" | null;
|
||||
type OverlayType = "volume" | "subtitle" | "speed" | null;
|
||||
|
||||
interface OverlayStackStore {
|
||||
export interface OverlayStackState {
|
||||
currentOverlay: OverlayType;
|
||||
setCurrentOverlay: (overlay: OverlayType) => void;
|
||||
}
|
||||
|
||||
export const useOverlayStack = create<OverlayStackStore>((set) => ({
|
||||
export const useOverlayStack = create<OverlayStackState>((set) => ({
|
||||
currentOverlay: null,
|
||||
setCurrentOverlay: (overlay) => set({ currentOverlay: overlay }),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ export interface InterfaceSlice {
|
|||
leftControlHovering: boolean; // is the cursor hovered over the left side of player controls
|
||||
isHoveringControls: boolean; // is the cursor hovered over any controls?
|
||||
timeFormat: VideoPlayerTimeFormat; // Time format of the video player
|
||||
isSpeedBoosted: boolean; // is playback speed temporarily boosted to 2x
|
||||
showSpeedIndicator: boolean; // should the speed indicator be shown
|
||||
};
|
||||
updateInterfaceHovering(newState: PlayerHoverState): void;
|
||||
setSeeking(seeking: boolean): void;
|
||||
|
|
@ -42,6 +44,8 @@ export interface InterfaceSlice {
|
|||
setLastVolume(state: number): void;
|
||||
hideNextEpisodeButton(): void;
|
||||
setShouldStartFromBeginning(val: boolean): void;
|
||||
setSpeedBoosted(state: boolean): void;
|
||||
setShowSpeedIndicator(state: boolean): void;
|
||||
}
|
||||
|
||||
export const createInterfaceSlice: MakeSlice<InterfaceSlice> = (set, get) => ({
|
||||
|
|
@ -61,6 +65,8 @@ export const createInterfaceSlice: MakeSlice<InterfaceSlice> = (set, get) => ({
|
|||
canAirplay: false,
|
||||
hideNextEpisodeBtn: false,
|
||||
shouldStartFromBeginning: false,
|
||||
isSpeedBoosted: false,
|
||||
showSpeedIndicator: false,
|
||||
},
|
||||
|
||||
setShouldStartFromBeginning(val) {
|
||||
|
|
@ -112,4 +118,14 @@ export const createInterfaceSlice: MakeSlice<InterfaceSlice> = (set, get) => ({
|
|||
s.interface.hideNextEpisodeBtn = true;
|
||||
});
|
||||
},
|
||||
setSpeedBoosted(state) {
|
||||
set((s) => {
|
||||
s.interface.isSpeedBoosted = state;
|
||||
});
|
||||
},
|
||||
setShowSpeedIndicator(state) {
|
||||
set((s) => {
|
||||
s.interface.showSpeedIndicator = state;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue