Each track's lyrics are resolved and cached at cache/<rel>/lyrics/<file>.<lrc|txt>,
with the format recorded as `lyrics: 'lrc'|'txt'` on the meta.tracks entry.
Precedence: external "<base>.lrc" > external "<base>.txt" > embedded tag
(lyrics / lyrics-<lang> / unsyncedlyrics — ffprobe now reads all format tags).
Content that contains [mm:ss] lines is stored as lrc even from a .txt/embedded
source. Only track-matching sidecars affect the version signature (a stray
notes.txt is ignored). Lyrics dir is wiped+regenerated per rebuild; new
`lyricsIndexed` counter.
Served by GET /api/music/lyrics?path=<rel>&file=<track> (text/plain +
X-Lyrics-Format header, ETag=<v>, 304, 404 when none).
Verified end-to-end: external .lrc wins over embedded; embedded → plain txt;
unmatched .txt ignored. MUSIC_API.md documents the field + endpoint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each indexed video gets a compressed poster (a frame grab ~10% in, capped at
30s, scaled ≤600px q5 like covers), written to cache/<rel>/posters/<file>.jpg
and recorded as `poster` on the meta.videos entry. The posters dir is wiped and
regenerated on each rebuild so orphans (removed videos) don't linger. New
`postersSaved` status counter.
Served by a new sidecar route GET /api/music/poster?path=<rel>&file=<video>
(image/jpeg, ETag=<v>, 304, 404 when none) — path-safe via basename.
Verified end-to-end on a real .mp4: video-only album → manifest {tracks:0,
videos:1}, meta.poster set, 14 KB poster on disk. MUSIC_API.md documents the
poster field + endpoint + postersSaved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Videos (all phone-compatible .mp4, plus common containers) that live in an
artist or album folder are now indexed alongside audio:
- Move 'mp4' out of AUDIO_EXT into a new VIDEO_EXT (mp4/m4v/mkv/mov/webm/avi) —
it was wrongly treated as an audio track before.
- ffprobeVideo captures file/title/durationSec/width/height per video.
- meta.json gains an optional `videos: IndexVideo[]`; a folder with only videos
now still gets a meta.json. Manifest entries gain optional `videos: N`.
- Video files join the album version signature (changes bump `v` for resync).
- New `videosIndexed` status counter + resync-log line.
Location is inherent in the folder rel (always an artist/album dir), so no
extra location field is needed. MUSIC_API.md documents the videos field +
manifest count. No poster/thumbnail generation yet (folder cover is reused).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
User-level state for the music app, served by the platform from Postgres (not
the sidecar, which is stateless about users) under the same /api/music prefix
so the music-app account gate permits it:
- music_favorites (userId, kind, key) — kind ∈ track|album|artist, opaque path
key the server never interprets; unique per (user,kind,key), newest-first.
GET /favorites (grouped), POST /favorites (idempotent), DELETE /favorites.
- music_now_playing (one row/user) — current track + position snapshot for
resume-across-launch/device, with a light title/artist/album cache so the
resume card renders before the library index syncs.
GET/PUT/DELETE /now-playing (upsert).
Routes registered before the catch-all proxy. Tables created via direct DDL;
query layer smoke-tested against the live DB. MUSIC_API.md documents the
contract for the app. App-side wiring (heart toggles, Favorites view, player
persistence) follows next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each Albums/<Artist>/_discography.md (author-maintained source of truth, never
modified) is compiled into a per-artist discography.json in the cache = album
folder → normalized release type (Studio/Live/Compilation/Single/EP/…), so the
player can split an artist's album list into sections.
- indexer.ts: parse the md table, normalize the Type (EP?→EP, Compilation (VA)→
Compilation, …), write discography.json. The artist folder's `v` now includes
_discography.md so regenerating it re-syncs just that small JSON (isolated from
the albums' meta/cover). Manifest gains `disco: true` on such entries. Also
fixed the skip check to require all expected outputs to exist, so artist/
cover-only folders no longer rebuild every run. New `discographies` counter.
- sidecar: GET /discography?path=<artist rel> (ETag/304), documented in the
contract header.
- MUSIC_API.md: §2.4 + manifest disco flag + resync algorithm updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Standalone reference for the mobile team: auth, streaming (/stream +
X-Audio-Duration), the synced library index (manifest/meta/cover + per-album v
diffing, ETag/304), building/refreshing (reindex + SSE progress), the
recommended resync algorithm, and the data shapes (IndexMeta/Manifest/
IndexStatus/IndexReport).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>