Skip future/unreleased albums in watchlist scanner

Albums announced but not yet released have no real audio available,
causing Soulseek to match random tracks with similar names. Both
discography methods (Spotify and generic client) now filter out
albums with release dates in the future. Skipped albums are not
marked as processed — they will be picked up on the first scan
after their release date passes.
This commit is contained in:
Broque Thomas 2026-04-07 13:31:15 -07:00
parent 08a7408d8b
commit 06e32d84c3

View file

@ -900,10 +900,15 @@ class WatchlistScanner:
filtered_albums.append(album) filtered_albums.append(album)
logger.info(f"Filtered {len(albums)} albums to {len(filtered_albums)} released after {cutoff_timestamp}") logger.info(f"Filtered {len(albums)} albums to {len(filtered_albums)} released after {cutoff_timestamp}")
return filtered_albums albums = filtered_albums
# Return all albums if no cutoff (lookback_period = 'all') # Skip future/unreleased albums — no real audio available yet
return albums now = datetime.now(timezone.utc)
released = [a for a in albums if not self._is_future_release(a, now)]
skipped = len(albums) - len(released)
if skipped:
logger.info(f"Skipped {skipped} future/unreleased albums (will be picked up after release)")
return released
except Exception as e: except Exception as e:
logger.error(f"Error getting discography for artist {spotify_artist_id}: {e}") logger.error(f"Error getting discography for artist {spotify_artist_id}: {e}")
@ -982,10 +987,15 @@ class WatchlistScanner:
filtered_albums.append(album) filtered_albums.append(album)
logger.info(f"Filtered {len(albums)} albums to {len(filtered_albums)} released after {cutoff_timestamp}") logger.info(f"Filtered {len(albums)} albums to {len(filtered_albums)} released after {cutoff_timestamp}")
return filtered_albums albums = filtered_albums
# Return all albums if no cutoff (lookback_period = 'all') # Skip future/unreleased albums — no real audio available yet
return albums now = datetime.now(timezone.utc)
released = [a for a in albums if not self._is_future_release(a, now)]
skipped = len(albums) - len(released)
if skipped:
logger.info(f"Skipped {skipped} future/unreleased albums (will be picked up after release)")
return released
except Exception as e: except Exception as e:
logger.error(f"Error getting discography for artist {artist_id}: {e}") logger.error(f"Error getting discography for artist {artist_id}: {e}")
@ -1285,6 +1295,28 @@ class WatchlistScanner:
logger.warning(f"Error comparing album date {album.release_date} with timestamp {timestamp}: {e}") logger.warning(f"Error comparing album date {album.release_date} with timestamp {timestamp}: {e}")
return True # Include if we can't determine return True # Include if we can't determine
def _is_future_release(self, album, now: datetime) -> bool:
"""Check if an album's release date is in the future. Returns False for unknown dates (safe default)."""
try:
if not album.release_date:
return False # Unknown date — assume released
release_date_str = album.release_date
if len(release_date_str) == 4:
album_date = datetime(int(release_date_str), 1, 1, tzinfo=timezone.utc)
elif len(release_date_str) == 7:
year, month = release_date_str.split('-')
album_date = datetime(int(year), int(month), 1, tzinfo=timezone.utc)
elif len(release_date_str) == 10:
album_date = datetime.strptime(release_date_str, "%Y-%m-%d").replace(tzinfo=timezone.utc)
elif 'T' in release_date_str:
date_part = release_date_str.split('T')[0]
album_date = datetime.strptime(date_part, "%Y-%m-%d").replace(tzinfo=timezone.utc)
else:
return False # Can't parse — assume released
return album_date > now
except Exception:
return False # Error — assume released
def _should_include_release(self, track_count: int, watchlist_artist: WatchlistArtist) -> bool: def _should_include_release(self, track_count: int, watchlist_artist: WatchlistArtist) -> bool:
""" """
Check if a release should be included based on user's preferences. Check if a release should be included based on user's preferences.