From 7eee2be38c2197b19eaf04cf9d526e4a7f5ba0e9 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Mon, 23 Feb 2026 16:25:23 -0800 Subject: [PATCH] Add release_date to Track and UI 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. --- core/itunes_client.py | 4 +++- core/spotify_client.py | 4 +++- database/music_library.db-shm | Bin 32768 -> 0 bytes database/music_library.db-wal | Bin 61832 -> 0 bytes web_server.py | 3 ++- webui/static/script.js | 8 ++++---- 6 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 database/music_library.db-shm delete mode 100644 database/music_library.db-wal diff --git a/core/itunes_client.py b/core/itunes_client.py index cbd570fd..53e57e52 100644 --- a/core/itunes_client.py +++ b/core/itunes_client.py @@ -68,6 +68,7 @@ class Track: preview_url: Optional[str] = None external_urls: Optional[Dict[str, str]] = None image_url: Optional[str] = None + release_date: Optional[str] = None @classmethod def from_itunes_track(cls, track_data: Dict[str, Any], clean_artist_name: Optional[str] = None) -> 'Track': @@ -97,7 +98,8 @@ class Track: popularity=0, # iTunes doesn't provide popularity preview_url=track_data.get('previewUrl'), external_urls=external_urls if external_urls else None, - image_url=album_image_url + image_url=album_image_url, + release_date=track_data.get('releaseDate', '').split('T')[0] if track_data.get('releaseDate') else None ) @dataclass diff --git a/core/spotify_client.py b/core/spotify_client.py index 13f5128a..1141be95 100644 --- a/core/spotify_client.py +++ b/core/spotify_client.py @@ -85,6 +85,7 @@ class Track: preview_url: Optional[str] = None external_urls: Optional[Dict[str, str]] = None image_url: Optional[str] = None + release_date: Optional[str] = None @classmethod def from_spotify_track(cls, track_data: Dict[str, Any]) -> 'Track': @@ -105,7 +106,8 @@ class Track: popularity=track_data.get('popularity', 0), preview_url=track_data.get('preview_url'), external_urls=track_data.get('external_urls'), - image_url=album_image_url + image_url=album_image_url, + release_date=track_data.get('album', {}).get('release_date') ) @dataclass diff --git a/database/music_library.db-shm b/database/music_library.db-shm deleted file mode 100644 index 2dca81bb0f1fa2f375993d2b27c93420219e13ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 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