Adds nullable storage fields + availability column on tracks, remote
source/source_id identity on albums/artists, TrackRepository.materialize()
and get_or_create_remote() repos — groundwork for on-demand YTM library
(placeholders saved without audio, materialized in-place on first play).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolve, store and serve album cover art.
Sources (tag-first, mirroring enrichment): embedded artwork extracted
offline via mutagen (ID3 APIC / FLAC+OGG Picture / MP4 covr), then Cover
Art Archive by release-group MBID as a network fallback. Resolution runs
inside MetadataEnrichmentService after album resolution, only when the
album has no cover yet (idempotent, never overwrites), and is best-effort
so a cover failure never affects enrichment status.
- CoverArt value object + CoverArtExtractor/CoverArtProvider ports
- MutagenCoverExtractor + CoverArtArchiveClient adapters
- AcoustID parser now captures release_group_mbid
- Covers stored via FileStorage at covers/{album_id}.{ext} (local + S3)
- AlbumRepository.set_cover_path
- Serve real covers: GET /api/v1/albums|tracks/{id}/cover (StreamUser,
?token=), Subsonic getCoverArt (placeholder fallback)
- has_cover flag on AlbumOut/TrackOut
- coverart_enabled / coverart_base_url settings
- tests: cover resolution units + release_group parse + DB-backed
test_cover_api.py (139 green via make test-api)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Thin adapters over the existing services/repositories (no business logic):
- system: ping (auth check), getLicense
- browsing: getArtists/getArtist/getAlbum, getAlbumList(2) (newest/alpha/random),
getSong, getGenres, getMusicFolders/getIndexes/getMusicDirectory (one folder)
- search: search3 (delegates to the library repos)
- media: stream + download (reuse StreamingService, honor Range); getCoverArt
returns a placeholder until the cover pipeline lands
- playlists: get/create/update/delete over the playlist repo (owner-scoped)
- annotation: star/unstar → append-only like log, scrobble → play history,
setRating → clean no-op
- all endpoints also accept the .view suffix and GET+POST for client compat
Repo support: album list ordering (newest/random), track genre facets.
README documents the mandatory-HTTPS requirement and app-password workflow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>