From 58bd82c03d714c731c6bb5c2cdcdde292bf07c36 Mon Sep 17 00:00:00 2001 From: "goose.art" Date: Wed, 1 Apr 2026 23:30:59 -0700 Subject: [PATCH] captions v3: offline pre-computation with incremental sentence splitting, debug overlay (Shift+D), cc loading animation, model persistence, processed range timeline --- spec/cc_load_1.png | Bin 0 -> 9829 bytes spec/cc_load_2.png | Bin 0 -> 9785 bytes src/lib/VideoPlayer.svelte | 178 ++++++++- src/lib/captions.svelte.ts | 482 ++++++++++++++--------- src/routes/+layout.svelte | 2 + src/routes/+page.svelte | 2 +- src/routes/profile/[handle]/+page.svelte | 2 +- static/cc_load_1.png | Bin 0 -> 9829 bytes static/cc_load_2.png | Bin 0 -> 9785 bytes 9 files changed, 477 insertions(+), 189 deletions(-) create mode 100644 spec/cc_load_1.png create mode 100644 spec/cc_load_2.png create mode 100644 static/cc_load_1.png create mode 100644 static/cc_load_2.png diff --git a/spec/cc_load_1.png b/spec/cc_load_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5acfc50f7d9277d5a90c43286dd437ed355ed3b3 GIT binary patch literal 9829 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 VZ(`XR!N9=4;OXk;vd$@?2>^DMRe1ma literal 0 HcmV?d00001 diff --git a/src/lib/VideoPlayer.svelte b/src/lib/VideoPlayer.svelte index 9f17574..256da61 100644 --- a/src/lib/VideoPlayer.svelte +++ b/src/lib/VideoPlayer.svelte @@ -3,15 +3,55 @@ import Hls from "hls.js"; import WavyBorder from "./WavyBorder.svelte"; import { playCroak } from "./croak"; - import { getModelStatus, getCaptionsEnabled, getCurrentCaption, attachVideo, detachVideo, toggleCaptionsDisplay, updateCaptionForTime, destroyCaptions } from "./captions.svelte"; + import { getModelStatus, getCaptionsEnabled, getCurrentCaption, toggleCaptionsDisplay, updateCaptionForTime, destroyCaptions, getCaptionCount, getDebugState, precomputeCaptions, getIsProcessing, getProcessProgress } from "./captions.svelte"; - // HLS video source URL (m3u8 playlist) - let { src }: { src: string } = $props(); + // HLS video source URL (m3u8 playlist) and AT URI for caption pre-computation + let { src, atUri = '' }: { src: string; atUri?: string } = $props(); let videoEl: HTMLVideoElement | undefined = $state(); let hls: Hls | null = null; let errorMsg = $state(""); + // CC debug overlay — toggle with Shift+D + let ccDebug = $state(false); + let debugInfo = $state({}); + let debugTimer: ReturnType | null = null; + + function toggleDebug() { + ccDebug = !ccDebug; + if (ccDebug && !debugTimer) { + debugTimer = setInterval(() => { debugInfo = getDebugState(currentTime); }, 250); + } else if (!ccDebug && debugTimer) { + clearInterval(debugTimer); + debugTimer = null; + } + } + + // CC loading animation + let ccLoadFrame = $state(1); + let ccLoadTimer: ReturnType | null = null; + let ccLoading = $state(false); + + function startCcLoadAnim() { + if (ccLoadTimer) return; + ccLoading = true; + ccLoadTimer = setInterval(() => { ccLoadFrame = ccLoadFrame === 1 ? 2 : 1; }, 500); + } + function stopCcLoadAnim() { + if (ccLoadTimer) { clearInterval(ccLoadTimer); ccLoadTimer = null; } + ccLoading = false; + } + + // Stop loading animation once first captions arrive + let prevCaptionCount = 0; + $effect(() => { + const count = getCaptionCount(); + if (getCaptionsEnabled() && ccLoading && count > prevCaptionCount) { + stopCcLoadAnim(); + } + prevCaptionCount = count; + }); + // Playback state let playing = $state(false); let currentTime = $state(0); @@ -52,8 +92,7 @@ hls.attachMedia(videoEl); hls.on(Hls.Events.MANIFEST_PARSED, () => { videoEl?.play().catch(() => {}); - // Attach video for background caption capture - if (videoEl) attachVideo(videoEl); + if (atUri) precomputeCaptions(atUri); }); hls.on(Hls.Events.ERROR, (_event, data) => { console.error("HLS error:", data); @@ -129,6 +168,11 @@ function toggleCaptions() { playCroak(); toggleCaptionsDisplay(); + if (getCaptionsEnabled()) { + startCcLoadAnim(); + } else { + stopCcLoadAnim(); + } } function togglePlay() { @@ -243,6 +287,9 @@ e.preventDefault(); togglePlay(); } + if (e.code === "KeyD" && e.shiftKey) { + toggleDebug(); + } } $effect(() => { @@ -274,7 +321,6 @@