Adds an explicit field to the Album dataclass in core/metadata/types.py
and the client-level Album dataclasses in deezer_client.py,
itunes_client.py, and hydrabase_client.py (the legacy discography path
reads from client objects, not typed dicts).
Deezer extracts explicit_lyrics (int→bool), iTunes extracts
collectionExplicitness ('explicit' string), Hydrabase forwards the
explicit field from the server response. Spotify, Discogs, MusicBrainz,
Qobuz, and Tidal have no explicit signal and stay None.
The flag threads through both builder functions in discography.py and
renders as a small "E" badge next to explicit titles in the discography
download modal and artist-detail page cards.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
iTunes API can return collection metadata without song tracks for
region-restricted albums. The _lookup fallback only checked if results
was empty, so a collection-only response was accepted and cached as
{'items': []}. All future lookups returned the cached empty result.
Three fixes:
- get_album_tracks now checks for actual song items and tries fallback
storefronts when only collection metadata is returned
- Skip cached results with empty items array (prevents stale cache hits)
- Backend returns descriptive 404 error, frontend surfaces it in toast
Spotify album art: replace the 4-char size segment after '0000ab67616d'
with '82c1' to request the original uploaded master (up to 2000px+).
Applied via _upgrade_spotify_image_url() in Track, Artist, and Album
dataclass constructors and as a catch-all in _download_cover_art.
Scoped to the ab67616d album art prefix only — artist images use a
different prefix (ab676161) where the trick does not apply.
iTunes/Apple Music: replace '100x100bb' with '3000x3000bb' in all
artworkUrl100 replacements across Track, Artist, Album, and the
get_album images arrays. Also applied as a catch-all in _download_cover_art.
Deezer already uses cover_xl at its maximum — no changes needed there.
Deezer and iTunes defaulted to 50 albums max, silently truncating large
discographies. Deezer now paginates (100 per page) up to 200. iTunes
raised to 200 (single call). All callers in web_server.py updated to
use the new defaults instead of hardcoding limit=50.
Also adds diagnostic logging for allow_duplicates album comparison
to help debug inconsistent singles behavior.
Stripped 4,200+ emoji characters from print(), logger calls across
39 Python files. Logs are now clean text — easier to grep, more
professional, no encoding issues on terminals without Unicode support.
Seasonal config icons preserved for UI display.
- New core/api_call_tracker.py — centralized tracker with rolling 60s
timestamps (speedometer) and 24h minute-bucketed history (charts)
- Instrument all 9 service client rate_limited decorators to record
actual API calls with per-endpoint tracking for Spotify
- 1-second WebSocket push loop for real-time gauge updates
- Modern radial arc gauges with service brand colors, glowing active
arc, endpoint dot, 0/max scale labels, smooth CSS transitions
- Click any gauge to open detail modal with 24h call history chart
(Canvas 2D, HiDPI, gradient fill, grid lines, danger zone band)
- Spotify modal shows per-endpoint history lines with color legend
and live per-endpoint breakdown bars
- Rate limited state indicator — blinking red badge with countdown
timer appears on gauge card when Spotify ban is active
- REST endpoint GET /api/rate-monitor/history/<service> for chart data
- Responsive grid layout (5 cols desktop, 3 tablet, 2 phone)
New setting in Settings → Library → File Organization: "Collaborative
Album Artist" — choose between first listed artist (default) or all
artists combined for $albumartist in folder paths and album_artist tag.
Per-source resolution:
- Spotify: artists array has separate objects — picks first directly
- Deezer: API already returns first artist only — no change needed
- iTunes: combined string ("Larry June, Curren$y & The Alchemist") —
resolves via artistId API lookup to get primary name ("Larry June").
Safe for "Tyler, the Creator" and "Simon & Garfunkel" because their
IDs resolve to the same combined name (no change).
Applied to both folder path ($albumartist template) and album_artist
metadata tag for consistency. Track artist tag always keeps all artists.
iTunes lookup only fires when source is iTunes (numeric ID + not Deezer).
Add album_type and total_tracks fields to Track dataclass, populate from
Spotify/iTunes/Deezer API responses, and apply a small tiebreaker bonus
(+0.02 for albums, +0.01 for EPs) in all matching loops so album versions
win when confidence scores are otherwise equal.
Filled all missing Hydrabase fallthrough gates across 6 endpoints (artist album tracks, iTunes album, discover album, Spotify track, both similar artists), added track_number/disc_number to Track dataclass, fixed get_album_tracks to send soul_id instead of text query, mapped soul_id field from Hydrabase responses across all search methods, updated 7 frontend call sites to pass album name/artist params, and fixed M3U export defaulting to enabled when users never turned it on.
Add a release_date field to the Track dataclass for both iTunes and Spotify clients (iTunes: parsed from releaseDate, Spotify: from album.release_date). Propagate release_date into enhanced search results in web_server and into the client-side script so album objects include release_date when available. Also broaden playlistId matching in the missing-tracks process to include 'enhanced_search_track_'. Removed SQLite SHM/WAL files from the repo (cleanup of DB temporary files). These changes enable showing and using track release dates across the app.
Fixed issue where broken iTunes explicit album IDs were preferred over working clean versions during deduplication. Some iTunes explicit albums (e.g., "Mr. Morale & The Big Steppers" ID 1623854804) report track counts in metadata but return 0 tracks when queried.
Added validation in itunes_client.py get_artist_albums() to verify explicit albums actually have tracks before keeping them. If an explicit version has 0 tracks, it's skipped and the clean version is used instead.
This fixes:
- "No tracks found" error when clicking affected albums
- Incorrect track count mismatches (19/18) caused by broken API data
The validation only runs for explicit albums during deduplication, minimal performance impact.
Two fixes for iTunes integration:
1. iTunes failed tracks now properly added to wishlist
- Root cause: iTunes tracks had no 'album' field (unlike Spotify)
- Fix: Added album information to each track in get_album_tracks()
- Tracks now include: album id, name, images, release_date
2. Remove ' - Single' suffix from iTunes album names
- Root cause: iTunes API includes ' - Single' in collectionName
- Fix: Added _clean_album_name() helper method
- Strips ' - Single' and ' - EP' suffixes from all album names
- Applied to all 6 locations where collectionName is used
Both Spotify and iTunes sources now work identically for wishlist
auto-processing when tracks fail or are cancelled.
Implements lazy loading of artist images in search results, artist pages, and similar artist bubbles to improve performance and user experience. Updates the iTunes client to prefer explicit album versions and deduplicate albums accordingly. Adds a new API endpoint to fetch artist images, and updates frontend logic to asynchronously fetch and display images where missing.
Adds iTunes fallback to SpotifyClient for search and metadata when Spotify is not authenticated. Updates album type logic to distinguish EPs, singles, and albums more accurately. Refactors watchlist database methods to support both Spotify and iTunes artist IDs. Improves deduplication and normalization of album names from iTunes. Updates web server and frontend to use new album type logic and support both ID types. Adds artist bubble snapshot example data.
Updated get_album, get_album_tracks, and get_artist methods to return data structures compatible with Spotify's API format. This includes normalizing image arrays, artist and album fields, and adding synthetic URIs and external URLs for better interoperability.
Introduces core/itunes_client.py implementing an iTunes Search API client for music metadata, providing search and lookup for tracks, albums, and artists with rate limiting. Adds METADATA-FALLBACK-IMPLEMENTATION.md, a comprehensive guide comparing fallback strategies for music metadata, including anonymous Spotify access and iTunes, and outlines integration approaches for seamless user experience without requiring Spotify credentials.