4.1 KiB
name, description
| name | description |
|---|---|
| lyrics-lookup | Find and inject lyrics for songs saved on worship.hesed.sbs that have none — LRCLIB first (free, often synced), then an agy web search (untimed), then local faster-whisper transcription. Use when the user asks to add/fill/inject lyrics, says a song has no lyrics, or asks to transcribe saved songs. For replacing lyrics that are already there but wrong, use lyrics-regenerate instead. |
Add lyrics to songs that don't have any
Shared lyrics live on the server (video_notes, kind lyrics) and every user of
worship.hesed.sbs sees the same ones. Every save keeps the previous version as a
revision (video_note_revs), so nothing is destroyed — but never overwrite
existing lyrics from this skill; that is lyrics-regenerate's job, and it takes
a backup first.
Sources, cheapest and best first
| Order | Source | Timed? | Cost | Where |
|---|---|---|---|---|
| 1 | LRCLIB | usually synced | free, no key | server-side: POST /api/notes/<id>/lyrics/web |
| 2 | agy web search (Genius/AZLyrics/hymnary…) | no — untimed | flat-rate agy | web_lyrics.py --agy |
| 3 | faster-whisper on this machine | synced (word timings) | free, ~0.65× real time | auto_lyrics.py |
Never reach for ElevenLabs Scribe or any credit-billed ASR — the user asked for free transcription only.
Credentials
Writing needs admin. Either env var works for every script:
export YTP_ADMIN_PASSWORD='…' # the /admin password, see the ytplayer-admin-password memory
export YTP_TOKEN='ytp_…' # or an API token minted at /admin
The password is also vault secret YTPLAYER_ADMIN_PASSWORD
(~/development/.secrets/ytplayer-admin.env) — use it through
vault__secret_exec when you must not print it.
Run it
cd ~/development/personal/ytplayer
# 1) LRCLIB for everything that has no lyrics, then agy for the leftovers
YTP_ADMIN_PASSWORD=… python3 scripts/lyrics/web_lyrics.py --missing --agy
# just look, change nothing
YTP_ADMIN_PASSWORD=… python3 scripts/lyrics/web_lyrics.py --missing --dry-run
# 2) transcribe what the web doesn't have (needs the venv below)
YTP_ADMIN_PASSWORD=… ~/.local/share/lyrics-asr/.venv/bin/python \
scripts/lyrics/auto_lyrics.py --missing
--missing lists songs from GET /api/admin/media and keeps only the ones with
lyricsLines == 0. Use --ids A,B,C to aim at specific videos. Ids are YouTube
ids (11 chars) or uploads (upl_<12 hex>).
Whisper venv, once:
uv venv ~/.local/share/lyrics-asr/.venv --python 3.12
~/.local/share/lyrics-asr/.venv/bin/pip install -r scripts/lyrics/requirements.txt
The same transcriber also runs unattended as the lyrics-worker container in
docker-compose.yml (auto_lyrics.py --watch 300 --state …), which is what gives
newly downloaded songs lyrics "at their own pace". It authenticates with
LYRICS_WORKER_TOKEN.
What lands in the database
{ "lines": [{ "t": 12.4, "text": "Holy You are", "kind": "line" }], // t null = untimed
"tags": ["from LRCLIB (synced)"], // provenance — always tag
"offset": 0 }
Tags in use: from LRCLIB (synced) / (plain text), from the web (untimed) — check and Tap-sync,
auto-transcribed, from the file (synced) (embedded in an admin upload).
The tag is how later runs tell machine lyrics from published ones — keep it accurate.
Things that bite
- LRCLIB 503/429 on bursts.
lrclib_regen.py:http_jsonalready retries with backoff; if you write new code against LRCLIB, copy it. Keep ~0.4 s between calls. - Wrong-artist matches. A common title ("Still") matches another genre's song.
artist_ok()inlrclib_regen.pyis the verification — port it rather than trusting a title+duration hit. - Karaoke / minus-one tracks have no vocals. Whisper returns noise; the script
calls them instrumental below
--min-words 25and skips them. That's correct. - Untimed lyrics are fine. The app shows them as a plain scrolling list and the user can Tap-sync them in the admin lyric editor.
- Never redistribute. These are third-party lyrics in a private library.