Download src/lib/video/progress.ts from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 2.68 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/video/progress.ts
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/lib/video/progress.ts
-
curl -L -o progress.ts https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/video/progress.ts
2.68 kB
| // βββ Video Engine V1 β on-device progress (resume) βββββββββββββββββββββββββββ | |
| // Saves playback position on the student's device so they can resume later. | |
| // Bounded to the 50 most-recent videos (LRU) to stay light. | |
| const KEY_PREFIX = "docdoe_video_progress_"; | |
| const INDEX_KEY = "docdoe_video_progress_index"; | |
| const MAX_ENTRIES = 50; | |
| export type SavedVideoProgress = { | |
| video_id: string; | |
| current_segment_index: number; | |
| current_segment_elapsed: number; // seconds into the current segment | |
| completed_segment_ids: string[]; | |
| updated_at: string; | |
| }; | |
| function canUseStorage(): boolean { | |
| return typeof window !== "undefined" && !!window.localStorage; | |
| } | |
| function readIndex(): string[] { | |
| if (!canUseStorage()) return []; | |
| try { | |
| const raw = window.localStorage.getItem(INDEX_KEY); | |
| const parsed = raw ? (JSON.parse(raw) as unknown) : []; | |
| return Array.isArray(parsed) ? parsed.filter((v): v is string => typeof v === "string") : []; | |
| } catch { | |
| return []; | |
| } | |
| } | |
| function writeIndex(ids: string[]): void { | |
| if (!canUseStorage()) return; | |
| try { | |
| window.localStorage.setItem(INDEX_KEY, JSON.stringify(ids)); | |
| } catch { | |
| /* storage full / blocked β ignore */ | |
| } | |
| } | |
| function touch(videoId: string): void { | |
| let index = readIndex().filter((id) => id !== videoId); | |
| index.unshift(videoId); | |
| // Evict least-recently-used beyond the cap. | |
| if (index.length > MAX_ENTRIES) { | |
| const evicted = index.slice(MAX_ENTRIES); | |
| index = index.slice(0, MAX_ENTRIES); | |
| for (const id of evicted) { | |
| try { | |
| window.localStorage.removeItem(`${KEY_PREFIX}${id}`); | |
| } catch { | |
| /* ignore */ | |
| } | |
| } | |
| } | |
| writeIndex(index); | |
| } | |
| export function loadVideoProgress(videoId: string): SavedVideoProgress | null { | |
| if (!canUseStorage()) return null; | |
| try { | |
| const raw = window.localStorage.getItem(`${KEY_PREFIX}${videoId}`); | |
| if (!raw) return null; | |
| return JSON.parse(raw) as SavedVideoProgress; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| export function saveVideoProgress(progress: SavedVideoProgress): void { | |
| if (!canUseStorage()) return; | |
| try { | |
| window.localStorage.setItem( | |
| `${KEY_PREFIX}${progress.video_id}`, | |
| JSON.stringify({ ...progress, updated_at: new Date().toISOString() }), | |
| ); | |
| touch(progress.video_id); | |
| } catch { | |
| /* storage full / blocked β degrade silently */ | |
| } | |
| } | |
| export function clearVideoProgress(videoId: string): void { | |
| if (!canUseStorage()) return; | |
| try { | |
| window.localStorage.removeItem(`${KEY_PREFIX}${videoId}`); | |
| writeIndex(readIndex().filter((id) => id !== videoId)); | |
| } catch { | |
| /* ignore */ | |
| } | |
| } | |