refactor: group player state

This commit is contained in:
2026-06-16 10:37:55 +02:00
committed by Milas Holsting
parent 4d8486e6ea
commit b569b06591
8 changed files with 260 additions and 219 deletions

View File

@@ -14,13 +14,13 @@ const alternateModeFor = (mode: string): "sub" | "dub" | null => {
};
export const hydrateAlternateMode = async (signal?: AbortSignal): Promise<void> => {
const alternateMode = alternateModeFor(state.currentMode);
const alternateMode = alternateModeFor(state.playback.currentMode);
if (!alternateMode) return;
if (state.modeSources[alternateMode]?.token) return;
if (state.playback.modeSources[alternateMode]?.token) return;
try {
const res = await fetch(
`/api/watch/episode/${state.malID}/${encodeURIComponent(state.currentEpisode)}?mode=${encodeURIComponent(alternateMode)}`,
`/api/watch/episode/${state.episode.malID}/${encodeURIComponent(state.episode.current)}?mode=${encodeURIComponent(alternateMode)}`,
{ signal },
);
if (!res.ok) return;
@@ -32,8 +32,8 @@ export const hydrateAlternateMode = async (signal?: AbortSignal): Promise<void>
const alternateSource = sources[alternateMode];
if (!alternateSource?.token) return;
state.modeSources = {
...state.modeSources,
state.playback.modeSources = {
...state.playback.modeSources,
[alternateMode]: alternateSource,
};
@@ -50,24 +50,24 @@ export const hydrateAlternateMode = async (signal?: AbortSignal): Promise<void>
* Saves preference to localStorage, reloads video src.
*/
export const switchMode = (mode: string): void => {
if (!state.availableModes.includes(mode) || mode === state.currentMode) return;
state.currentMode = mode;
if (!state.playback.availableModes.includes(mode) || mode === state.playback.currentMode) return;
state.playback.currentMode = mode;
safeLocalStorage.setItem("player-audio-mode", mode);
const qualitySelect = state.container.querySelector(
const qualitySelect = state.elements.container.querySelector(
"[data-quality-select]",
) as HTMLSelectElement | null;
const url = streamUrlForMode(mode, qualitySelect?.value);
loadVideoSource(url, state.modeSources[mode]?.type);
loadVideoSource(url, state.playback.modeSources[mode]?.type);
// Fallback: if the media element doesn't actually switch sources (some browsers can get "stuck"),
// reload the page with the desired mode and resume time via sessionStorage.
if (url) {
const expectedToken = state.modeSources[mode]?.token;
const expectedToken = state.playback.modeSources[mode]?.token;
const expectedMode = mode;
const resumeSeconds = state.video.currentTime;
const resumeSeconds = state.elements.video.currentTime;
window.setTimeout(() => {
if (!expectedToken) return;
const currentSrc = state.video.currentSrc || state.video.src || "";
const currentSrc = state.elements.video.currentSrc || state.elements.video.src || "";
if (currentSrc.includes(`token=${encodeURIComponent(expectedToken)}`)) return;
try {
@@ -90,24 +90,24 @@ export const switchMode = (mode: string): void => {
* Disables unavailable modes.
*/
export const updateModeButtons = (): void => {
const dub = state.container.querySelector("[data-mode-dub]") as HTMLButtonElement | null;
const sub = state.container.querySelector("[data-mode-sub]") as HTMLButtonElement | null;
const m = state.currentMode;
const dub = state.elements.container.querySelector("[data-mode-dub]") as HTMLButtonElement | null;
const sub = state.elements.container.querySelector("[data-mode-sub]") as HTMLButtonElement | null;
const m = state.playback.currentMode;
dub?.classList.toggle("text-accent", m === "dub");
dub?.classList.toggle("text-foreground", m !== "dub");
dub?.classList.toggle("opacity-50", !state.availableModes.includes("dub"));
dub?.classList.toggle("cursor-not-allowed", !state.availableModes.includes("dub"));
dub?.classList.toggle("opacity-50", !state.playback.availableModes.includes("dub"));
dub?.classList.toggle("cursor-not-allowed", !state.playback.availableModes.includes("dub"));
if (dub) {
dub.disabled = !state.availableModes.includes("dub");
dub.disabled = !state.playback.availableModes.includes("dub");
}
sub?.classList.toggle("text-accent", m === "sub");
sub?.classList.toggle("text-foreground", m !== "sub");
sub?.classList.toggle("opacity-50", !state.availableModes.includes("sub"));
sub?.classList.toggle("cursor-not-allowed", !state.availableModes.includes("sub"));
sub?.classList.toggle("opacity-50", !state.playback.availableModes.includes("sub"));
sub?.classList.toggle("cursor-not-allowed", !state.playback.availableModes.includes("sub"));
if (sub) {
sub.disabled = !state.availableModes.includes("sub");
sub.disabled = !state.playback.availableModes.includes("sub");
}
};
@@ -115,17 +115,17 @@ export const updateModeButtons = (): void => {
* Binds click handlers for mode buttons and autoplay toggle.
*/
export const setupMode = (): void => {
const dub = state.container.querySelector("[data-mode-dub]") as HTMLButtonElement | null;
const sub = state.container.querySelector("[data-mode-sub]") as HTMLButtonElement | null;
const dub = state.elements.container.querySelector("[data-mode-dub]") as HTMLButtonElement | null;
const sub = state.elements.container.querySelector("[data-mode-sub]") as HTMLButtonElement | null;
dub?.addEventListener("click", () => {
if (state.availableModes.includes("dub")) {
if (state.playback.availableModes.includes("dub")) {
switchMode("dub");
showControls();
}
});
sub?.addEventListener("click", () => {
if (state.availableModes.includes("sub")) {
if (state.playback.availableModes.includes("sub")) {
switchMode("sub");
showControls();
}