trigger_library_scan and is_library_scanning were no-ops assuming
Navidrome auto-detects new files. Navidrome supports startScan and
getScanStatus via the Subsonic API, so use them like Plex and Jellyfin.
- DB path update uses suffix matching when exact match fails, so server
paths (/mnt/musicBackup/...) are found and updated after local moves
- Escape LIKE wildcards in artist/album names to prevent false matches
- Move album-level sidecars (cover.jpg, folder.jpg, etc.) alongside audio
- Post-pass sweep moves all remaining sidecars from emptied directories
- Multi-disc albums place album art in album root, not disc subfolders
Use config_manager for transfer path (same source as playback) instead of
separate DB read that silently falls back to ./Transfer under contention.
Abort scan if transfer folder doesn't exist to prevent mass false findings.
- personalized_playlists._get_active_source() now returns 'deezer' when
configured instead of always falling back to 'itunes'
- Add deezer_track_id to _build_track_dict() for discovery pool tracks
- Include album_deezer_id and artist_deezer_id in get_discovery_recent_albums()
response — fixes "No deezer album ID available" error when clicking cards
- Skip Spotify library section entirely when Spotify is not authenticated
Per-job and per-status filtering — respects active toolbar filters so
users can clear e.g. only track number findings or only dismissed items.
Confirmation uses the app's styled modal instead of browser confirm().
Previously only 502/503/504 triggered instance rotation. A 500 from one
instance (e.g. triton.squid.wtf choking on a specific query) would stop
the search entirely instead of trying the next instance.
Users can now choose between iTunes/Apple Music and Deezer as their free
metadata source in Settings. Spotify always takes priority when authenticated;
the fallback handles all lookups when it's not.
Core changes:
- DeezerClient: full metadata interface (search, albums, artists, tracks)
matching iTunesClient's API surface with identical dataclass return types
- SpotifyClient: configurable _fallback property switches between iTunes/Deezer
based on live config reads (no restart needed)
- MetadataService, web_server, watchlist_scanner, api/search, repair_worker,
seasonal_discovery, personalized_playlists: all direct iTunesClient imports
replaced with fallback-aware helpers
Database:
- deezer_artist_id on watchlist_artists and similar_artists tables
- deezer_track_id/album_id/artist_id on discovery_pool and discovery_cache
- Full CRUD for Deezer IDs: add, read, update, backfill, metadata enrichment
- Watchlist duplicate detection by artist name prevents re-adding across sources
- SimilarArtist dataclass and all query/insert methods handle Deezer columns
Bug fixes found during review:
- Similar artist backfill was writing Deezer IDs into iTunes columns
- Discover hero was storing resolved Deezer IDs in wrong column
- Status cache not invalidating on settings save (source name lag)
- Watchlist add allowing duplicates when switching metadata sources
Add minimum 60% title similarity gate to match_recording() — prevents
artist bonus + MB score from pushing unrelated titles past the confidence
threshold (e.g. "Sweet Surrender" matching "Answers" by same artist).
New MBID Mismatch Detector repair job reads embedded MusicBrainz recording
IDs from audio files, verifies them against the MusicBrainz API, and flags
tracks where the MBID points to a different song. Fix action strips the bad
MBID tag so media servers like Navidrome fall back to correct file tags.
New download mode alongside Soulseek, YouTube, Tidal, and Qobuz. Uses
community-run REST API instances (no auth required) that serve Tidal CDN
FLAC streams. Features quality fallback chain (hires→lossless→high→low),
automatic instance rotation on failure, and full hybrid mode support.
Also fixes 6 missing streaming source checks for HiFi and Qobuz in the
frontend that were blocking playback with "format not supported" errors.
Scrapes Spotify's embed endpoint to extract track data from any public
playlist or album URL. Full discovery flow with Deezer parity: parse →
card → discovery modal → live progress → sync → download missing.
- New scraper: core/spotify_public_scraper.py (embed endpoint parsing)
- 12 API endpoints mirroring Deezer's discovery/sync/download flow
- WebSocket live discovery updates via spotify_public_discovery_states
- Green-branded tab, cards, and input styling (#1DB954)
- Album vs playlist detection with distinct card icons (💿/🎵)
- Download persistence: card click reopens download modal after close
- Card phase reset on cancel/completion (closeDownloadMissingModal)
- Backend download linking for spotify_public_ and deezer_ prefixes
- Completion handlers (V2 + no-missing-tracks) for both platforms
- Source URLs stored on mirrored playlists for future auto-refresh
Southern hemisphere users now see correct seasons (e.g. March = Autumn,
December = Summer). Holidays (Halloween, Christmas, Valentine's) stay
calendar-fixed regardless of hemisphere.
- Hemisphere dropdown in Discovery Pool Settings
- GET/POST /api/discovery/hemisphere endpoints
- Season detection offsets months by 6 for southern hemisphere
- Stored in metadata table, defaults to northern
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
- Add whisoul.png to modal header with subtitle text and gradient background
- Responsive: smaller logo on mobile, hide subtitle
- Share _resolve_file_path in repair_worker for cross-environment path compat
- Use path resolution in orphan and duplicate file deletion
- Guard directory cleanup against removing the transfer folder itself
- Restore correct button label text on fix error recovery
- Add findings dashboard with summary stats and per-job clickable filter chips
- Redesign findings cards with expandable detail panels and per-type renderers
- Redesign history tab with status dots, stat pills, and full timestamps
- Fix dead file cleaner false positives by using suffix-based path resolution
- Fix orphan file detector false positives by matching via path suffixes
- Add help text modal for each repair job card
- Enlarge maintenance modal (1100px wide, 90vh tall)