Files

94 lines
4.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: lyrics-lookup
description: 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:
```bash
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
```bash
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:
```bash
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
```jsonc
{ "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.