Use cache before Spotify auth checks
Allow cached Spotify search results to return even when Spotify is rate-limited or temporarily unavailable, and remove redundant rate-limit gating after auth checks.
This commit is contained in:
parent
09a7465b22
commit
992be1a056
1 changed files with 77 additions and 89 deletions
|
|
@ -1105,49 +1105,46 @@ class SpotifyClient:
|
||||||
if Spotify is unavailable or returns an error.
|
if Spotify is unavailable or returns an error.
|
||||||
"""
|
"""
|
||||||
cache = get_metadata_cache()
|
cache = get_metadata_cache()
|
||||||
|
effective_limit = min(limit, 50) # Spotify API max is 50
|
||||||
|
|
||||||
|
# Check Spotify cache first so cached data remains usable even when
|
||||||
|
# Spotify is temporarily unavailable or rate limited.
|
||||||
|
cached_results = cache.get_search_results('spotify', 'track', query, effective_limit)
|
||||||
|
if cached_results is not None:
|
||||||
|
tracks = []
|
||||||
|
for raw in cached_results:
|
||||||
|
try:
|
||||||
|
tracks.append(Track.from_spotify_track(raw))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if tracks:
|
||||||
|
return tracks
|
||||||
|
|
||||||
use_spotify = self.is_spotify_authenticated()
|
use_spotify = self.is_spotify_authenticated()
|
||||||
|
|
||||||
if use_spotify:
|
if use_spotify:
|
||||||
# Check Spotify cache
|
try:
|
||||||
effective_limit = min(limit, 50) # Spotify API max is 50
|
results = self.sp.search(q=query, type='track', limit=effective_limit)
|
||||||
cached_results = cache.get_search_results('spotify', 'track', query, effective_limit)
|
|
||||||
if cached_results is not None:
|
|
||||||
tracks = []
|
tracks = []
|
||||||
for raw in cached_results:
|
raw_items = results['tracks']['items']
|
||||||
try:
|
|
||||||
tracks.append(Track.from_spotify_track(raw))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if tracks:
|
|
||||||
return tracks
|
|
||||||
|
|
||||||
# Skip Spotify if globally rate limited — fall through to fallback
|
for track_data in raw_items:
|
||||||
if self.is_rate_limited():
|
track = Track.from_spotify_track(track_data)
|
||||||
logger.debug(f"Spotify rate limited, skipping track search for: {query}")
|
tracks.append(track)
|
||||||
use_spotify = False
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
results = self.sp.search(q=query, type='track', limit=effective_limit)
|
|
||||||
tracks = []
|
|
||||||
raw_items = results['tracks']['items']
|
|
||||||
|
|
||||||
for track_data in raw_items:
|
# Cache individual tracks + search mapping
|
||||||
track = Track.from_spotify_track(track_data)
|
entries = [(td.get('id'), td) for td in raw_items if td.get('id')]
|
||||||
tracks.append(track)
|
if entries:
|
||||||
|
cache.store_entities_bulk('spotify', 'track', entries)
|
||||||
|
cache.store_search_results('spotify', 'track', query, effective_limit,
|
||||||
|
[td.get('id') for td in raw_items if td.get('id')])
|
||||||
|
|
||||||
# Cache individual tracks + search mapping
|
return tracks
|
||||||
entries = [(td.get('id'), td) for td in raw_items if td.get('id')]
|
|
||||||
if entries:
|
|
||||||
cache.store_entities_bulk('spotify', 'track', entries)
|
|
||||||
cache.store_search_results('spotify', 'track', query, effective_limit,
|
|
||||||
[td.get('id') for td in raw_items if td.get('id')])
|
|
||||||
|
|
||||||
return tracks
|
except Exception as e:
|
||||||
|
_detect_and_set_rate_limit(e, 'search_tracks')
|
||||||
except Exception as e:
|
logger.error(f"Error searching tracks via Spotify: {e}")
|
||||||
_detect_and_set_rate_limit(e, 'search_tracks')
|
# Fall through to fallback
|
||||||
logger.error(f"Error searching tracks via Spotify: {e}")
|
|
||||||
# Fall through to fallback
|
|
||||||
|
|
||||||
# Fallback (iTunes or Deezer — configured in settings)
|
# Fallback (iTunes or Deezer — configured in settings)
|
||||||
if allow_fallback:
|
if allow_fallback:
|
||||||
|
|
@ -1163,27 +1160,23 @@ class SpotifyClient:
|
||||||
if Spotify is unavailable or returns an error.
|
if Spotify is unavailable or returns an error.
|
||||||
"""
|
"""
|
||||||
cache = get_metadata_cache()
|
cache = get_metadata_cache()
|
||||||
|
# Check Spotify cache first so cached data remains usable even when
|
||||||
|
# Spotify is temporarily unavailable or rate limited.
|
||||||
|
cached_results = cache.get_search_results('spotify', 'artist', query, min(limit, 10))
|
||||||
|
if cached_results is not None:
|
||||||
|
artists = []
|
||||||
|
for raw in cached_results:
|
||||||
|
try:
|
||||||
|
artists.append(Artist.from_spotify_artist(raw))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if artists:
|
||||||
|
query_lower = query.lower().strip()
|
||||||
|
artists.sort(key=lambda a: (0 if a.name.lower().strip() == query_lower else 1))
|
||||||
|
return artists
|
||||||
|
|
||||||
use_spotify = self.is_spotify_authenticated()
|
use_spotify = self.is_spotify_authenticated()
|
||||||
|
|
||||||
if use_spotify:
|
|
||||||
# Check Spotify cache
|
|
||||||
cached_results = cache.get_search_results('spotify', 'artist', query, min(limit, 10))
|
|
||||||
if cached_results is not None:
|
|
||||||
artists = []
|
|
||||||
for raw in cached_results:
|
|
||||||
try:
|
|
||||||
artists.append(Artist.from_spotify_artist(raw))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if artists:
|
|
||||||
query_lower = query.lower().strip()
|
|
||||||
artists.sort(key=lambda a: (0 if a.name.lower().strip() == query_lower else 1))
|
|
||||||
return artists
|
|
||||||
|
|
||||||
if self.is_rate_limited():
|
|
||||||
logger.debug(f"Spotify rate limited, skipping artist search for: {query}")
|
|
||||||
use_spotify = False
|
|
||||||
|
|
||||||
if use_spotify:
|
if use_spotify:
|
||||||
try:
|
try:
|
||||||
search_query = f'artist:{query}' if len(query.strip()) <= 4 else query
|
search_query = f'artist:{query}' if len(query.strip()) <= 4 else query
|
||||||
|
|
@ -1230,49 +1223,44 @@ class SpotifyClient:
|
||||||
if Spotify is unavailable or returns an error.
|
if Spotify is unavailable or returns an error.
|
||||||
"""
|
"""
|
||||||
cache = get_metadata_cache()
|
cache = get_metadata_cache()
|
||||||
|
# Check Spotify cache first so cached data remains usable even when
|
||||||
|
# Spotify is temporarily unavailable or rate limited.
|
||||||
|
cached_results = cache.get_search_results('spotify', 'album', query, min(limit, 10))
|
||||||
|
if cached_results is not None:
|
||||||
|
albums = []
|
||||||
|
for raw in cached_results:
|
||||||
|
try:
|
||||||
|
albums.append(Album.from_spotify_album(raw))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if albums:
|
||||||
|
return albums
|
||||||
|
|
||||||
use_spotify = self.is_spotify_authenticated()
|
use_spotify = self.is_spotify_authenticated()
|
||||||
|
|
||||||
if use_spotify:
|
if use_spotify:
|
||||||
# Check Spotify cache
|
try:
|
||||||
cached_results = cache.get_search_results('spotify', 'album', query, min(limit, 10))
|
results = self.sp.search(q=query, type='album', limit=min(limit, 10))
|
||||||
if cached_results is not None:
|
|
||||||
albums = []
|
albums = []
|
||||||
for raw in cached_results:
|
raw_items = results['albums']['items']
|
||||||
try:
|
|
||||||
albums.append(Album.from_spotify_album(raw))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if albums:
|
|
||||||
return albums
|
|
||||||
|
|
||||||
if use_spotify:
|
for album_data in raw_items:
|
||||||
# Skip Spotify if globally rate limited — fall through to fallback
|
album = Album.from_spotify_album(album_data)
|
||||||
if self.is_rate_limited():
|
albums.append(album)
|
||||||
logger.debug(f"Spotify rate limited, skipping album search for: {query}")
|
|
||||||
use_spotify = False
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
results = self.sp.search(q=query, type='album', limit=min(limit, 10))
|
|
||||||
albums = []
|
|
||||||
raw_items = results['albums']['items']
|
|
||||||
|
|
||||||
for album_data in raw_items:
|
# Cache individual albums + search mapping (skip if full data already cached)
|
||||||
album = Album.from_spotify_album(album_data)
|
entries = [(ad.get('id'), ad) for ad in raw_items if ad.get('id')]
|
||||||
albums.append(album)
|
if entries:
|
||||||
|
cache.store_entities_bulk('spotify', 'album', entries, skip_if_exists=True)
|
||||||
|
cache.store_search_results('spotify', 'album', query, min(limit, 10),
|
||||||
|
[ad.get('id') for ad in raw_items if ad.get('id')])
|
||||||
|
|
||||||
# Cache individual albums + search mapping (skip if full data already cached)
|
return albums
|
||||||
entries = [(ad.get('id'), ad) for ad in raw_items if ad.get('id')]
|
|
||||||
if entries:
|
|
||||||
cache.store_entities_bulk('spotify', 'album', entries, skip_if_exists=True)
|
|
||||||
cache.store_search_results('spotify', 'album', query, min(limit, 10),
|
|
||||||
[ad.get('id') for ad in raw_items if ad.get('id')])
|
|
||||||
|
|
||||||
return albums
|
except Exception as e:
|
||||||
|
_detect_and_set_rate_limit(e, 'search_albums')
|
||||||
except Exception as e:
|
logger.error(f"Error searching albums via Spotify: {e}")
|
||||||
_detect_and_set_rate_limit(e, 'search_albums')
|
# Fall through to iTunes fallback
|
||||||
logger.error(f"Error searching albums via Spotify: {e}")
|
|
||||||
# Fall through to iTunes fallback
|
|
||||||
|
|
||||||
# Fallback (iTunes or Deezer)
|
# Fallback (iTunes or Deezer)
|
||||||
if allow_fallback:
|
if allow_fallback:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue