Commit graph

6 commits

Author SHA1 Message Date
80472653b4 Add 4 new cover/tracklist sources: MB back cover, iTunes, Last.fm, Discogs tracklist
cover_handler.py:
- _download_image(): shared helper replaces duplicated download logic
- download_back_cover(): fetches back cover from MusicBrainz CAA (/back endpoint),
  saves as back.jpg; skips if already present
- _itunes_cover_url() / download_itunes_cover(): iTunes Search API (no auth),
  requests 600x600 artwork; fallback after Discogs
- _lastfm_cover_url() / download_lastfm_cover(): Last.fm album.getinfo
  (LASTFM_API_KEY env var); last cover fallback, skips placeholder images
- resolve_cover(): extended with iTunes → Last.fm fallback chain

metadata_resolver.py:
- _discogs_get_tracklist(): fetches full Discogs release via REST API,
  parses tracklist[] including heading-based disc detection
- _lastfm_tracklist(): fetches Last.fm album.getinfo tracks (LASTFM_API_KEY)
- resolve(): uses Discogs tracklist → Last.fm tracklist as fallback when
  MusicBrainz returns no tracks; LASTFM_API_KEY added to env var block

music_enricher.py:
- process_album(): calls download_back_cover() after execute_album() when MBID known

New cover priority:  local → MusicBrainz front → Discogs → iTunes → Last.fm
New tracklist priority: local → YouTube → MusicBrainz → Discogs → Last.fm → OCR
Test suite: 29 → 33 tests (all pass)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 08:55:17 +02:00
031e595ff7 Add Discogs as cover source fallback after MusicBrainz
- _discogs_cover_url(): searches Discogs database/search API by artist+album,
  returns primary image URL; uses DISCOGS_TOKEN if set, else anonymous
- download_discogs_cover(): downloads and saves as folder.jpg (PNG→JPEG via PIL)
- resolve_cover() priority: local → MusicBrainz → Discogs
- music_enricher: pass artist/album to resolve_cover() for Discogs lookup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 08:44:04 +02:00
d06c0bbcc9 Normalize all cover art to folder.jpg (Jellyfin standard)
- Add normalize_cover_to_folder_jpg(): renames/converts any local cover
  (Front.jpg, front.webp, cover.jpg, …) to folder.jpg in-place; WebP/PNG
  are converted to JPEG via PIL
- resolve_cover() now calls normalize_cover_to_folder_jpg() automatically
  after finding a local cover, so future enrichment runs always produce folder.jpg
- One-time batch: 38 existing library covers renamed/converted to folder.jpg

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 08:41:43 +02:00
7516de439f Save downloaded covers as folder.jpg (Jellyfin standard), PNG→JPEG on download
- download_cover() now writes folder.jpg instead of _cover_download{ext}
- PNG responses are converted to JPEG via PIL during download (avoids PNG
  in the album directory entirely)
- find_local_cover() priority: folder > front > cover > album (folder.jpg
  is now the canonical name for both downloaded and manually placed covers)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 08:26:33 +02:00
f86db982a5 Support WebP cover images: convert to JPEG via PIL, correct MIME type fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 05:50:46 +02:00
f7cf520dbe Initial implementation of Music Metadata Enricher
AI-powered per-album pipeline: scan → local hints → MusicBrainz/Discogs/Claude
resolve → cover art → interactive or auto review → tag write + rename + report.
All external dependencies optional; 17/17 unit tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 05:42:03 +02:00