Add a lyrics-only service mode view with the sung line centred and auto-sized, and a free local faster-whisper script to transcribe saved songs

This commit is contained in:
Jonathan Sykes
2026-09-19 08:33:00 +08:00
parent b82f17cd46
commit 71fc51fb50
8 changed files with 457 additions and 2 deletions

View File

@@ -91,6 +91,23 @@ JSON shapes mirror the Tauri Rust bridge exactly — don't change one side alone
edit feed with view/restore, caption injection, JSON export, API examples.
Disabled unless `ADMIN_PASSWORD` is set (Dokploy Environment tab; compose passes
`${ADMIN_PASSWORD:-}`). Login is rate-limited per IP.
- **Service mode → 🎵 Lyrics** (`data.settings.serviceLyrics`, `serviceLyricsAutoscroll`):
lyrics-only view; the sung line is centred, highlighted and sized to the
largest font that fits ONE row. Sizing is measured with a hidden real
`.sl-line` in TWO passes — Bricolage Grotesque has an optical-size axis, so
glyphs get relatively wider at small sizes and a single 100px measurement
over-fits on phones. Re-fit (in place, never rebuilding rows — that would
move the scroll position) on resize, `document.fonts` load, and +900 ms.
Lyrics-only and the 📺 video panel are mutually exclusive.
- **Free batch transcription**: `scripts/lyrics/auto_lyrics.py` (faster-whisper
`large-v3-turbo` int8 on CPU, no API key/credits; venv at
`~/.local/share/lyrics-asr/.venv`). `--missing` uses `GET /api/admin/media`
(admin or token). Measured on the 16-core laptop: ~0.3–0.65× real time;
openai-whisper medium was ~2.5×. **`vad_filter` must stay False** — VAD calls
sung music non-speech and returns an empty transcript. Karaoke/minus-one
tracks have no vocals (reported "instrumental?"); their lyrics were read from
the on-screen text (frame sampling + OCR), not audio. ElevenLabs Scribe also
works but spends the TTS credit balance (free tier = 10k/month).
- Offline: the last-seen notes per video are cached in localStorage
(`ytpNotesCache`, 200 videos), so saved songs keep their lyrics offline.
- **Toast cap bug (fixed with this feature):** `toast()` used