mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
Spotify: gracefully handle 403 from deprecated audio-features API
Add a dedicated AudioFeaturesUnavailableError and track audio-features availability with an audio_features_available flag. If the audio-features endpoint returns HTTP 403, raise the new error, log a warning once, and disable further audio-features requests for the session. The plugin now skips attempting audio-features lookups when disabled (avoiding repeated failed calls and potential rate-limit issues). Also update changelog to document the behavior.
This commit is contained in:
parent
9608ec0925
commit
0d11e19ecf
2 changed files with 40 additions and 6 deletions
|
|
@ -77,6 +77,11 @@ class APIError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AudioFeaturesUnavailableError(Exception):
|
||||||
|
"""Raised when the audio features API returns 403 (deprecated/unavailable)."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SpotifyPlugin(
|
class SpotifyPlugin(
|
||||||
SearchApiMetadataSourcePlugin[
|
SearchApiMetadataSourcePlugin[
|
||||||
Union[SearchResponseAlbums, SearchResponseTracks]
|
Union[SearchResponseAlbums, SearchResponseTracks]
|
||||||
|
|
@ -140,6 +145,7 @@ class SpotifyPlugin(
|
||||||
self.config["client_id"].redact = True
|
self.config["client_id"].redact = True
|
||||||
self.config["client_secret"].redact = True
|
self.config["client_secret"].redact = True
|
||||||
|
|
||||||
|
self.audio_features_available = True # Track if audio features API is available
|
||||||
self.setup()
|
self.setup()
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
|
@ -246,6 +252,16 @@ class SpotifyPlugin(
|
||||||
f"API Error: {e.response.status_code}\n"
|
f"API Error: {e.response.status_code}\n"
|
||||||
f"URL: {url}\nparams: {params}"
|
f"URL: {url}\nparams: {params}"
|
||||||
)
|
)
|
||||||
|
elif e.response.status_code == 403:
|
||||||
|
# Check if this is the audio features endpoint
|
||||||
|
if self.audio_features_url in url:
|
||||||
|
raise AudioFeaturesUnavailableError(
|
||||||
|
"Audio features API returned 403 (deprecated or unavailable)"
|
||||||
|
)
|
||||||
|
raise APIError(
|
||||||
|
f"API Error: {e.response.status_code}\n"
|
||||||
|
f"URL: {url}\nparams: {params}"
|
||||||
|
)
|
||||||
elif e.response.status_code == 429:
|
elif e.response.status_code == 429:
|
||||||
seconds = e.response.headers.get(
|
seconds = e.response.headers.get(
|
||||||
"Retry-After", DEFAULT_WAITING_TIME
|
"Retry-After", DEFAULT_WAITING_TIME
|
||||||
|
|
@ -691,6 +707,8 @@ class SpotifyPlugin(
|
||||||
item["isrc"] = isrc
|
item["isrc"] = isrc
|
||||||
item["ean"] = ean
|
item["ean"] = ean
|
||||||
item["upc"] = upc
|
item["upc"] = upc
|
||||||
|
|
||||||
|
if self.audio_features_available:
|
||||||
audio_features = self.track_audio_features(spotify_track_id)
|
audio_features = self.track_audio_features(spotify_track_id)
|
||||||
if audio_features is None:
|
if audio_features is None:
|
||||||
self._log.info("No audio features found for: {}", item)
|
self._log.info("No audio features found for: {}", item)
|
||||||
|
|
@ -698,6 +716,9 @@ class SpotifyPlugin(
|
||||||
for feature, value in audio_features.items():
|
for feature, value in audio_features.items():
|
||||||
if feature in self.spotify_audio_features:
|
if feature in self.spotify_audio_features:
|
||||||
item[self.spotify_audio_features[feature]] = value
|
item[self.spotify_audio_features[feature]] = value
|
||||||
|
else:
|
||||||
|
self._log.debug("Audio features API unavailable, skipping")
|
||||||
|
|
||||||
item["spotify_updated"] = time.time()
|
item["spotify_updated"] = time.time()
|
||||||
item.store()
|
item.store()
|
||||||
if write:
|
if write:
|
||||||
|
|
@ -726,6 +747,13 @@ class SpotifyPlugin(
|
||||||
return self._handle_response(
|
return self._handle_response(
|
||||||
"get", f"{self.audio_features_url}{track_id}"
|
"get", f"{self.audio_features_url}{track_id}"
|
||||||
)
|
)
|
||||||
|
except AudioFeaturesUnavailableError as e:
|
||||||
|
self._log.warning(
|
||||||
|
"Audio features API is unavailable (403 error). "
|
||||||
|
"Skipping audio features for remaining tracks."
|
||||||
|
)
|
||||||
|
self.audio_features_available = False
|
||||||
|
return None
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
self._log.debug("Spotify API error: {}", e)
|
self._log.debug("Spotify API error: {}", e)
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,12 @@ New features:
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
|
- :doc:`/plugins/spotify`: The plugin now gracefully handles audio-features API
|
||||||
|
deprecation (HTTP 403 errors). When a 403 error is encountered from the
|
||||||
|
audio-features endpoint, the plugin logs a warning once and skips audio
|
||||||
|
features for all remaining tracks in the session, avoiding unnecessary API
|
||||||
|
calls and rate limit exhaustion.
|
||||||
|
|
||||||
For packagers:
|
For packagers:
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue