Fetch lyrics from LRCLIB (synced when available) and serve an admin media library of uploaded video and audio with cover art and embedded lyrics
This commit is contained in:
24
CLAUDE.md
24
CLAUDE.md
@@ -142,6 +142,30 @@ JSON shapes mirror the Tauri Rust bridge exactly — don't change one side alone
|
||||
(upsertMedia rows + `<id>.1.m4a/.mp4` files) — Playwright's Chromium plays
|
||||
AAC; launch with `--autoplay-policy=no-user-gesture-required`.
|
||||
|
||||
## Uploads + web lyrics
|
||||
- **Uploads** (`server/uploads.js`, `UPLOAD_DIR`, table `uploads`): an admin
|
||||
posts a file to `/api/admin/uploads` (multipart, ≤ 4 GB, admin cookie or API
|
||||
token). ffprobe runs ONCE and gives everything: title/artist/album tags,
|
||||
duration, whether there is a REAL video stream (a cover picture also appears
|
||||
as a video stream — check `disposition.attached_pic`), the cover itself
|
||||
(extracted to `<id>.art.jpg`; an uploaded image wins) and embedded lyrics
|
||||
tags (`LYRICS` / `lyrics-eng` / `UNSYNCEDLYRICS` / `©lyr`) which are parsed
|
||||
with `parseLrc` and saved as that song's shared lyrics (synced when in LRC
|
||||
form). Ids are `upl_<12 hex>` and are accepted anywhere a video id is:
|
||||
`/api/streams` (audio → `audioUrl` + art, video → one "Original" quality),
|
||||
`/api/download`, peaks/gif/clip (`audioSourcePath`/`videoSourcePath`), notes.
|
||||
`/api/search` puts the library first and still answers when yt-dlp fails.
|
||||
The app: 📁 Library view, `isUploadId`/`isMediaId` helpers, audio uploads
|
||||
force audio mode so the cover art shows where the video would be.
|
||||
- **Web lyrics**: `POST /api/notes/:id/lyrics/web` asks **LRCLIB** (free,
|
||||
key-less, often SYNCED) using a cleaned title/artist + duration
|
||||
(`cleanTitle`/`cleanArtist`/`pickLrclib`, ± 6 s). Profile users, tokens and
|
||||
admins may call it; the lyrics worker tries it BEFORE transcribing.
|
||||
`scripts/lyrics/web_lyrics.py --missing --agy` adds an agy web-search
|
||||
fallback (plain text → untimed lines, to be timed with Tap-sync).
|
||||
Gotcha fixed here: a `/g` regex reused for `.test()` skips every other call
|
||||
(lastIndex) — `cleanTitle` keeps a stateless copy.
|
||||
|
||||
## Watch party, sharing, playback extras (all in app.js unless noted)
|
||||
- **Watch party** (`server/party.js`, `Party`): `/ws/party` (Bun has ONE websocket
|
||||
handler — sockets are tagged `ws.data.hub = 'party'` and routed in server.js).
|
||||
|
||||
Reference in New Issue
Block a user