Skip to content

Commit

Permalink
Merge pull request #4943 from arsaboo/spotify_timeout
Browse files Browse the repository at this point in the history
Gracefully handle Spotify API errors
  • Loading branch information
sampsyo authored Oct 20, 2023
2 parents 0ccd70d + 66bf002 commit efc2cf7
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 24 deletions.
58 changes: 34 additions & 24 deletions beetsplug/spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,37 +171,47 @@ def _handle_response(self, request_type, url, params=None):
:return: JSON data for the class:`Response <Response>` object.
:rtype: dict
"""
response = request_type(
url,
headers={'Authorization': f'Bearer {self.access_token}'},
params=params,
)
if response.status_code != 200:
if 'token expired' in response.text:
try:
response = request_type(
url,
headers={'Authorization': f'Bearer {self.access_token}'},
params=params,
timeout=10,
)
response.raise_for_status()
return response.json()
except requests.exceptions.ReadTimeout:
self._log.error('ReadTimeout.')
raise SpotifyAPIError('Request timed out.')
except requests.exceptions.RequestException as e:
if e.response.status_code == 401:
self._log.debug(
'{} access token has expired. Reauthenticating.',
self.data_source,
f'{self.data_source} access token has expired. '
f'Reauthenticating.'
)
self._authenticate()
return self._handle_response(request_type, url, params=params)
elif response.status_code == 429:
elif e.response.status_code == 404:
raise SpotifyAPIError(f'API Error: {e.response.status_code}\n'
f'URL: {url}\nparams: {params}')
elif e.response.status_code == 429:
seconds = response.headers.get('Retry-After',
DEFAULT_WAITING_TIME)
self._log.debug('Too many API requests. Retrying after {} \
seconds.', seconds)
self._log.debug(f'Too many API requests. Retrying after '
f'{seconds} seconds.')
time.sleep(int(seconds) + 1)
return self._handle_response(request_type, url, params=params)
elif response.status_code == 404:
raise SpotifyAPIError("API Error: {}\nURL: {}\nparams: {}".
format(response.status_code, url,
params))
else:
raise ui.UserError(
'{} API error:\n{}\nURL:\n{}\nparams:\n{}'.format(
self.data_source, response.text, url, params
)
elif e.response.status_code == 503:
self._log.error('Service Unavailable.')
raise SpotifyAPIError('Service Unavailable.')
elif e.response is not None:
raise SpotifyAPIError(
f'{self.data_source} API error:\n{e.response.text}\n'
f'URL:\n{url}\nparams:\n{params}'
)
return response.json()
else:
self._log.error(f'Request failed. Error: {e}')
raise SpotifyAPIError('Request failed.')

def album_for_id(self, album_id):
"""Fetch an album by its Spotify ID or URL and return an
Expand Down Expand Up @@ -656,8 +666,8 @@ def track_popularity(self, track_id=None):
track_data = self._handle_response(
requests.get, self.track_url + track_id
)
self._log.debug('track_data: {}', track_data['popularity'])
return track_data['popularity']
self._log.debug('track_data: {}', track_data.get('popularity'))
return track_data.get('popularity')

def track_audio_features(self, track_id=None):
"""Fetch track audio features by its Spotify ID."""
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ New features:

Bug fixes:

* Fix a crash when the Spotify API timeouts or does not return a `Retry-After` interval.
:bug:`4942`
* :doc:`/plugins/scrub`: Fixed the import behavior where scrubbed database tags
were restored to newly imported tracks with config settings ``scrub.auto: yes``
and ``import.write: no``.
Expand Down

0 comments on commit efc2cf7

Please sign in to comment.