From 606df97eaeb281d804f544eef710a5c6b059783c Mon Sep 17 00:00:00 2001 From: mkelvers Date: Thu, 28 May 2026 11:29:23 +0200 Subject: [PATCH] chore: format player subtitles --- static/player/subtitles/index.ts | 48 ++++++++++++++++---------------- static/player/subtitles/vtt.ts | 22 +++++++-------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/static/player/subtitles/index.ts b/static/player/subtitles/index.ts index 885e112..79c9f66 100644 --- a/static/player/subtitles/index.ts +++ b/static/player/subtitles/index.ts @@ -1,6 +1,6 @@ -import type { SubtitleCue, SubtitleTrack } from '../types'; -import { state } from '../state'; -import { parseVtt } from './vtt'; +import type { SubtitleCue, SubtitleTrack } from "../types"; +import { state } from "../state"; +import { parseVtt } from "./vtt"; // proxy subtitle URL through backend (avoids CORS) const proxyUrl = (token: string) => `/watch/proxy/subtitle?token=${encodeURIComponent(token)}`; @@ -10,20 +10,20 @@ const subtitlesForMode = (): SubtitleTrack[] => { const src = state.modeSources[state.currentMode]; if (!src?.subtitles) return []; return src.subtitles - .map(t => ({ - lang: (t.lang || 'unknown').toLowerCase(), - label: t.lang || 'Unknown', + .map((t) => ({ + lang: (t.lang || "unknown").toLowerCase(), + label: t.lang || "Unknown", url: proxyUrl(t.token), })) - .filter(t => t.url !== ''); + .filter((t) => t.url !== ""); }; const hideSubtitleText = (): void => { - const el = state.container.querySelector('[data-subtitle-text]') as HTMLElement | null; + const el = state.container.querySelector("[data-subtitle-text]") as HTMLElement | null; if (!el) return; - el.textContent = ''; - el.classList.remove('block'); - el.classList.add('hidden'); + el.textContent = ""; + el.classList.remove("block"); + el.classList.add("hidden"); }; // fetches and parses VTT from proxy URL @@ -43,27 +43,27 @@ const loadSubtitle = async (url: string): Promise => { */ export const updateSubtitleOptions = (): void => { const select = state.container.querySelector( - '[data-subtitle-select]' + "[data-subtitle-select]", ) as HTMLSelectElement | null; if (!select) return; state.currentSubtitleTracks = subtitlesForMode(); - select.innerHTML = ''; + select.innerHTML = ""; - const none = document.createElement('option'); - none.value = 'none'; - none.textContent = 'Off'; + const none = document.createElement("option"); + none.value = "none"; + none.textContent = "Off"; select.appendChild(none); - select.value = 'none'; + select.value = "none"; state.currentSubtitleTracks.forEach((t, i) => { - const opt = document.createElement('option'); + const opt = document.createElement("option"); opt.value = String(i); opt.textContent = t.label; select.appendChild(opt); }); const wrapper = select.parentElement; - wrapper?.classList.toggle('hidden', state.currentSubtitleTracks.length === 0); + wrapper?.classList.toggle("hidden", state.currentSubtitleTracks.length === 0); state.activeSubtitles = []; hideSubtitleText(); }; @@ -73,7 +73,7 @@ export const updateSubtitleOptions = (): void => { * Finds active cue and shows/hides overlay. */ export const updateSubtitleRender = (time: number): void => { - const el = state.container.querySelector('[data-subtitle-text]') as HTMLElement | null; + const el = state.container.querySelector("[data-subtitle-text]") as HTMLElement | null; if (!el) return; if (!state.activeSubtitles.length) { hideSubtitleText(); @@ -104,7 +104,7 @@ export const updateSubtitleRender = (time: number): void => { } el.textContent = cue.text; - el.classList.remove('hidden'); + el.classList.remove("hidden"); }; /** @@ -113,10 +113,10 @@ export const updateSubtitleRender = (time: number): void => { */ export const setupSubtitles = (): void => { const select = state.container.querySelector( - '[data-subtitle-select]' + "[data-subtitle-select]", ) as HTMLSelectElement | null; - select?.addEventListener('change', async () => { - if (select.value === 'none') { + select?.addEventListener("change", async () => { + if (select.value === "none") { state.activeSubtitles = []; hideSubtitleText(); return; diff --git a/static/player/subtitles/vtt.ts b/static/player/subtitles/vtt.ts index 97efece..e673c99 100644 --- a/static/player/subtitles/vtt.ts +++ b/static/player/subtitles/vtt.ts @@ -1,28 +1,28 @@ // parses VTT timestamp (mm:ss.ms or hh:mm:ss.ms) to seconds export const parseVttTime = (raw: string): number => { - const parts = raw.trim().split(':'); + const parts = raw.trim().split(":"); if (parts.length < 2) return 0; const secPart = parts.pop()!; const minPart = parts.pop()!; - const hourPart = parts.pop() ?? '0'; - return Number(hourPart) * 3600 + Number(minPart) * 60 + Number(secPart.replace(',', '.')); + const hourPart = parts.pop() ?? "0"; + return Number(hourPart) * 3600 + Number(minPart) * 60 + Number(secPart.replace(",", ".")); }; // parses a single VTT cue: timestamp line + text lines export const parseVttCue = (line: string, lines: string[], i: number) => { - if (!line.includes('-->')) return null; - const [startRaw, endRaw] = line.split('-->'); + if (!line.includes("-->")) return null; + const [startRaw, endRaw] = line.split("-->"); const payload: string[] = []; let j = i + 1; // collect text until blank line - while (j < lines.length && lines[j].trim() !== '') { + while (j < lines.length && lines[j].trim() !== "") { payload.push(lines[j]); j++; } // strip tags, join lines const text = payload - .join('\n') - .replace(/<[^>]+>/g, '') + .join("\n") + .replace(/<[^>]+>/g, "") .trim(); if (!text) return null; return { start: parseVttTime(startRaw), end: parseVttTime(endRaw), text }; @@ -33,17 +33,17 @@ export const parseVttCue = (line: string, lines: string[], i: number) => { * Handles both compact (timestamp on separate line) and standard formats. */ export const parseVtt = (text: string) => { - const lines = text.replace(/\r/g, '').split('\n'); + const lines = text.replace(/\r/g, "").split("\n"); const cues = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (!line) continue; // compact: cue id on line i, timestamp on i+1 - if (i + 1 < lines.length && !line.includes('-->') && lines[i + 1].includes('-->')) { + if (i + 1 < lines.length && !line.includes("-->") && lines[i + 1].includes("-->")) { const cue = parseVttCue(lines[i + 1].trim(), lines, i + 1); if (cue) cues.push(cue); i++; - } else if (line.includes('-->')) { + } else if (line.includes("-->")) { // standard: timestamp on same line const cue = parseVttCue(line, lines, i); if (cue) cues.push(cue);