diff --git a/static/player/episodes/thumbnails.ts b/static/player/episodes/thumbnails.ts index 2a711f4..9408d79 100644 --- a/static/player/episodes/thumbnails.ts +++ b/static/player/episodes/thumbnails.ts @@ -43,5 +43,7 @@ export const setupThumbnails = (): void => { } }); }) - .catch((err) => console.error("Failed to fetch thumbnails:", err)); + .catch(() => { + window.showToast?.({ message: "Failed to load episode thumbnails." }); + }); }; diff --git a/static/player/main.ts b/static/player/main.ts index 6818f02..1aad499 100644 --- a/static/player/main.ts +++ b/static/player/main.ts @@ -91,7 +91,7 @@ const initPlayer = (): void => { teardownPlayer(); if (!initState(container)) { - console.error("Video player markup is missing required controls."); + window.showToast?.({ message: "Video player markup is missing required controls." }); return; } currentContainer = container; diff --git a/static/player/subtitles/vtt.test.ts b/static/player/subtitles/vtt.test.ts new file mode 100644 index 0000000..d673d21 --- /dev/null +++ b/static/player/subtitles/vtt.test.ts @@ -0,0 +1,30 @@ +import assert from "node:assert/strict"; +import { describe, test } from "node:test"; +import { parseVtt, parseVttTime } from "./vtt"; + +describe("vtt parsing", () => { + test("parses mm:ss timestamps", () => { + assert.equal(parseVttTime("01:02.500"), 62.5); + }); + + test("parses hh:mm:ss timestamps", () => { + assert.equal(parseVttTime("01:02:03.250"), 3723.25); + }); + + test("parses standard and compact cues", () => { + const cues = parseVtt(`WEBVTT + +1 +00:00.000 --> 00:01.500 +Hello + +00:02.000 --> 00:03.000 +World +`); + + assert.deepEqual(cues, [ + { start: 0, end: 1.5, text: "Hello" }, + { start: 2, end: 3, text: "World" }, + ]); + }); +}); diff --git a/static/player/subtitles/vtt.ts b/static/player/subtitles/vtt.ts index e673c99..730752a 100644 --- a/static/player/subtitles/vtt.ts +++ b/static/player/subtitles/vtt.ts @@ -2,9 +2,10 @@ export const parseVttTime = (raw: string): number => { const parts = raw.trim().split(":"); if (parts.length < 2) return 0; - const secPart = parts.pop()!; - const minPart = parts.pop()!; - const hourPart = parts.pop() ?? "0"; + const secPart = parts[parts.length - 1]; + const minPart = parts[parts.length - 2]; + const hourPart = parts.length > 2 ? parts[parts.length - 3] : "0"; + if (!secPart || !minPart) return 0; return Number(hourPart) * 3600 + Number(minPart) * 60 + Number(secPart.replace(",", ".")); };