Centralize the ordered metadata source list and source-priority helper so album completeness and the repair worker follow the same Deezer/iTunes-first fallback order. This also removes the last duplicate priority logic from the touched repair paths.
Album completeness and any other repair job now uses the centralized source/client helpers instead of a worker-local Spotify client or override plumbing
- This keeps source selection aligned with the configured primary provider and removes the last Spotify-only special case from the job path.
This change ultimately is a step towards further centralizing the Spotify client access and the associated `is_spotify_authenticated` check.
- Currently these look-ups are done all over the place in different feature implementations directly, but moving forward, any feature that uses `get_primary_client` or `get_client_for_source` to access the Spotify client, won't have to duplicate any rate-limiting or auth checks as long as these getters are used
Album completeness and downstream repair flow now follow the configured
primary provider first, with Discogs and Hydrabase support added alongside
existing Spotify, iTunes, and Deezer paths.
Keep spotify_track_id for compatibility while preserving source-aware track
IDs for provider-neutral handling.
Albums with zero local tracks were flagged as incomplete but the
auto-fill fix failed because there were no existing tracks to
determine the album folder or quality standard from. Now skipped
during scan — they'll be detected once tracks are actually added.
New setting: Min Completion % (default 0 = disabled). Skips albums
where the user has fewer than N% of tracks — filters out playlist
imports where a single track exists but the full album was never
intended to be downloaded. Catches real failed downloads (6/12)
while ignoring incidental singles (1/12).
Was Spotify-only — users without Spotify got zero results. Now queries
albums with any source ID (spotify_album_id, itunes_album_id, deezer_id)
and uses the matching API client for track count and missing track lookup.
Falls back gracefully across sources with client-type detection.
The SQL HAVING clause filtered on local track count instead of expected
track count, excluding albums with fewer than 3 local tracks from the
scan entirely. Now fetches all Spotify-matched albums and filters by
expected track count in the loop.
When the maintenance worker flags an incomplete album, users can now
click "Auto-Fill" to automatically locate missing tracks in the library,
move/copy them into the album folder, and apply full metadata enhancement
(MusicBrainz, Deezer, cover art, etc.). Singles are moved; tracks from
multi-track albums are copied. Quality gate prevents filling FLAC albums
with lossy files. Tracks not found in library are added to wishlist with
album context for auto-download.
Repair jobs were making Spotify API calls without checking the global
rate limit ban, causing them to churn through every item getting rejected
one by one during a ban. Now all Spotify calls in repair jobs check
context.is_spotify_rate_limited() first and skip/fallback gracefully.
- Add is_spotify_rate_limited() helper to JobContext (base.py)
- Guard calls in track_number_repair, album_completeness,
missing_cover_art, and metadata_gap_filler
- Jobs fall through to iTunes/MusicBrainz fallbacks when rate-limited
- All 9 repair jobs now emit report_progress() for real-time card updates
(phase, log lines, per-item activity) via WebSocket repair:progress events
- Enrich finding details with album/artist thumb URLs across all repair jobs
(dead_file, duplicate, metadata_gap, album_completeness, missing_cover_art,
acoustid_scanner, track_number_repair, fake_lossless, orphan_file)
- Track number repair: return match_score from fuzzy matching, add suffix-based
DB lookup for album/artist art (handles cross-environment path mismatches)
- Fix Plex/Jellyfin relative thumb URLs in findings endpoint via fix_artist_image_url
- Labeled media cards in finding detail panels (album title + artist name under images)
- Dashboard tooltip shows current job name + per-job progress instead of stale stats