Files

4.1 KiB
Raw Permalink Blame History

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_json already 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() in lrclib_regen.py is 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 25 and 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.