more resilient http error handling, added retry limit on refreshing token
This commit is contained in:
parent
af0abe0285
commit
0c6aeb5181
@ -25,8 +25,9 @@ class Network:
|
||||
|
||||
def __init__(self, user: NetworkUser):
|
||||
self.user = user
|
||||
self.refresh_counter = 0
|
||||
|
||||
def make_get_request(self, method, url=None, params=None, headers=None, whole_url=None) -> Optional[dict]:
|
||||
def get_request(self, method, url=None, params=None, headers=None, whole_url=None) -> Optional[dict]:
|
||||
|
||||
if headers is None:
|
||||
headers = dict()
|
||||
@ -50,25 +51,39 @@ class Network:
|
||||
if req.status_code == 429:
|
||||
retry_after = req.headers.get('Retry-After', None)
|
||||
|
||||
if self.refresh_counter < 5:
|
||||
self.refresh_counter += 1
|
||||
if retry_after:
|
||||
logger.warning(f'{method} rate limit reached: retrying in {retry_after} seconds')
|
||||
time.sleep(int(retry_after) + 1)
|
||||
return self.make_get_request(method, url, params, headers)
|
||||
return self.get_request(method, url, params, headers)
|
||||
else:
|
||||
logger.error(f'{method} rate limit reached: cannot find Retry-After header')
|
||||
else:
|
||||
self.refresh_counter = 0
|
||||
logger.critical(f'refresh token limit (5) reached')
|
||||
|
||||
elif req.status_code == 401:
|
||||
logger.warning(f'{method} access token expired, refreshing')
|
||||
self.user.refresh_token()
|
||||
return self.make_get_request(method, url, params, headers)
|
||||
if self.refresh_counter < 5:
|
||||
self.refresh_counter += 1
|
||||
return self.get_request(method, url, params, headers)
|
||||
else:
|
||||
self.refresh_counter = 0
|
||||
logger.critical(f'refresh token limit (5) reached')
|
||||
|
||||
else:
|
||||
error_text = req.json()['error']['message']
|
||||
logger.error(f'{method} get {req.status_code} {error_text}')
|
||||
error = req.json().get('error', None)
|
||||
if error:
|
||||
message = error.get('message', 'n/a')
|
||||
logger.error(f'{method} {req.status_code} {message}')
|
||||
else:
|
||||
logger.error(f'{method} {req.status_code} no error object found')
|
||||
|
||||
return None
|
||||
|
||||
def make_post_request(self, method, url, params=None, json=None, headers=None) -> Optional[Response]:
|
||||
def post_request(self, method, url, params=None, json=None, headers=None) -> Optional[Response]:
|
||||
|
||||
if headers is None:
|
||||
headers = dict()
|
||||
@ -85,25 +100,39 @@ class Network:
|
||||
if req.status_code == 429:
|
||||
retry_after = req.headers.get('Retry-After', None)
|
||||
|
||||
if self.refresh_counter < 5:
|
||||
self.refresh_counter += 1
|
||||
if retry_after:
|
||||
logger.warning(f'{method} rate limit reached: retrying in {retry_after} seconds')
|
||||
time.sleep(int(retry_after) + 1)
|
||||
return self.make_post_request(method, url, params, json, headers)
|
||||
return self.post_request(method, url, params, json, headers)
|
||||
else:
|
||||
logger.error(f'{method} rate limit reached: cannot find Retry-After header')
|
||||
else:
|
||||
self.refresh_counter = 0
|
||||
logger.critical(f'refresh token limit (5) reached')
|
||||
|
||||
elif req.status_code == 401:
|
||||
logger.warning(f'{method} access token expired, refreshing')
|
||||
self.user.refresh_token()
|
||||
return self.make_post_request(method, url, params, json, headers)
|
||||
if self.refresh_counter < 5:
|
||||
self.refresh_counter += 1
|
||||
return self.post_request(method, url, params, json, headers)
|
||||
else:
|
||||
self.refresh_counter = 0
|
||||
logger.critical(f'refresh token limit (5) reached')
|
||||
|
||||
else:
|
||||
error_text = str(req.text)
|
||||
logger.error(f'{method} post {req.status_code} {error_text}')
|
||||
error = req.json().get('error', None)
|
||||
if error:
|
||||
message = error.get('message', 'n/a')
|
||||
logger.error(f'{method} {req.status_code} {message}')
|
||||
else:
|
||||
logger.error(f'{method} {req.status_code} no error object found')
|
||||
|
||||
return None
|
||||
|
||||
def make_put_request(self, method, url, params=None, json=None, headers=None) -> Optional[Response]:
|
||||
def put_request(self, method, url, params=None, json=None, headers=None) -> Optional[Response]:
|
||||
|
||||
if headers is None:
|
||||
headers = dict()
|
||||
@ -120,21 +149,35 @@ class Network:
|
||||
if req.status_code == 429:
|
||||
retry_after = req.headers.get('Retry-After', None)
|
||||
|
||||
if self.refresh_counter < 5:
|
||||
self.refresh_counter += 1
|
||||
if retry_after:
|
||||
logger.warning(f'{method} rate limit reached: retrying in {retry_after} seconds')
|
||||
time.sleep(int(retry_after) + 1)
|
||||
return self.make_put_request(method, url, params, json, headers)
|
||||
return self.put_request(method, url, params, json, headers)
|
||||
else:
|
||||
logger.error(f'{method} rate limit reached: cannot find Retry-After header')
|
||||
else:
|
||||
self.refresh_counter = 0
|
||||
logger.critical(f'refresh token limit (5) reached')
|
||||
|
||||
elif req.status_code == 401:
|
||||
logger.warning(f'{method} access token expired, refreshing')
|
||||
self.user.refresh_token()
|
||||
return self.make_put_request(method, url, params, json, headers)
|
||||
if self.refresh_counter < 5:
|
||||
self.refresh_counter += 1
|
||||
return self.put_request(method, url, params, json, headers)
|
||||
else:
|
||||
self.refresh_counter = 0
|
||||
logger.critical(f'refresh token limit (5) reached')
|
||||
|
||||
else:
|
||||
error_text = str(req.text)
|
||||
logger.error(f'{method} put {req.status_code} {error_text}')
|
||||
error = req.json().get('error', None)
|
||||
if error:
|
||||
message = error.get('message', 'n/a')
|
||||
logger.error(f'{method} {req.status_code} {message}')
|
||||
else:
|
||||
logger.error(f'{method} {req.status_code} no error object found')
|
||||
|
||||
return None
|
||||
|
||||
@ -168,7 +211,7 @@ class Network:
|
||||
if description:
|
||||
json['description'] = description
|
||||
|
||||
req = self.make_post_request('createPlaylist', f'users/{username}/playlists', json=json)
|
||||
req = self.post_request('createPlaylist', f'users/{username}/playlists', json=json)
|
||||
|
||||
if 200 <= req.status_code < 300:
|
||||
return self.parse_playlist(req.json())
|
||||
@ -250,7 +293,7 @@ class Network:
|
||||
|
||||
logger.info("retrieving")
|
||||
|
||||
resp = self.make_get_request('getAvailableDevices', 'me/player/devices')
|
||||
resp = self.get_request('getAvailableDevices', 'me/player/devices')
|
||||
if resp:
|
||||
return [self.parse_device(i) for i in resp['devices']]
|
||||
else:
|
||||
@ -273,7 +316,7 @@ class Network:
|
||||
if before:
|
||||
params['before'] = int(before.timestamp() * 1000)
|
||||
|
||||
resp = self.make_get_request('getRecentlyPlayedTracks', 'me/player/recently-played', params=params)
|
||||
resp = self.get_request('getRecentlyPlayedTracks', 'me/player/recently-played', params=params)
|
||||
|
||||
if resp:
|
||||
pager = PageCollection(self, page=resp)
|
||||
@ -293,7 +336,7 @@ class Network:
|
||||
|
||||
logger.info("retrieved")
|
||||
|
||||
resp = self.make_get_request('getPlayer', 'me/player')
|
||||
resp = self.get_request('getPlayer', 'me/player')
|
||||
if resp:
|
||||
return self.parse_currently_playing(resp)
|
||||
else:
|
||||
@ -325,7 +368,7 @@ class Network:
|
||||
'play': True
|
||||
}
|
||||
|
||||
resp = self.make_put_request('changePlaybackDevice', 'me/player', json=json)
|
||||
resp = self.put_request('changePlaybackDevice', 'me/player', json=json)
|
||||
if resp:
|
||||
return True
|
||||
else:
|
||||
@ -351,7 +394,7 @@ class Network:
|
||||
if uris:
|
||||
payload['uris'] = [str(i) for i in uris[:200]]
|
||||
|
||||
req = self.make_put_request('play', 'me/player/play', params=params, json=payload)
|
||||
req = self.put_request('play', 'me/player/play', params=params, json=payload)
|
||||
if req:
|
||||
return req
|
||||
else:
|
||||
@ -367,7 +410,7 @@ class Network:
|
||||
else:
|
||||
params = None
|
||||
|
||||
req = self.make_put_request('pause', 'me/player/pause', params=params)
|
||||
req = self.put_request('pause', 'me/player/pause', params=params)
|
||||
if req:
|
||||
return req
|
||||
else:
|
||||
@ -383,7 +426,7 @@ class Network:
|
||||
else:
|
||||
params = None
|
||||
|
||||
req = self.make_post_request('next', 'me/player/next', params=params)
|
||||
req = self.post_request('next', 'me/player/next', params=params)
|
||||
if req:
|
||||
return req
|
||||
else:
|
||||
@ -399,7 +442,7 @@ class Network:
|
||||
else:
|
||||
params = None
|
||||
|
||||
req = self.make_post_request('previous', 'me/player/previous', params=params)
|
||||
req = self.post_request('previous', 'me/player/previous', params=params)
|
||||
if req:
|
||||
return req
|
||||
else:
|
||||
@ -414,7 +457,7 @@ class Network:
|
||||
if deviceid is not None:
|
||||
params['device_id'] = deviceid
|
||||
|
||||
req = self.make_put_request('setShuffle', 'me/player/shuffle', params=params)
|
||||
req = self.put_request('setShuffle', 'me/player/shuffle', params=params)
|
||||
if req:
|
||||
return req
|
||||
else:
|
||||
@ -431,7 +474,7 @@ class Network:
|
||||
if deviceid is not None:
|
||||
params['device_id'] = deviceid
|
||||
|
||||
req = self.make_put_request('setVolume', 'me/player/volume', params=params)
|
||||
req = self.put_request('setVolume', 'me/player/volume', params=params)
|
||||
if req:
|
||||
return req
|
||||
else:
|
||||
@ -450,7 +493,7 @@ class Network:
|
||||
|
||||
json = {"uris": [str(i) for i in uris[:100]]}
|
||||
|
||||
req = self.make_put_request('replacePlaylistTracks', f'playlists/{uri.object_id}/tracks',
|
||||
req = self.put_request('replacePlaylistTracks', f'playlists/{uri.object_id}/tracks',
|
||||
json=json, headers=headers)
|
||||
|
||||
if req is not None:
|
||||
@ -491,7 +534,7 @@ class Network:
|
||||
logger.warning('update dictionairy length 0')
|
||||
return None
|
||||
else:
|
||||
req = self.make_put_request('changePlaylistDetails', f'playlists/{uri.object_id}',
|
||||
req = self.put_request('changePlaylistDetails', f'playlists/{uri.object_id}',
|
||||
json=json, headers=headers)
|
||||
if req:
|
||||
return req
|
||||
@ -507,7 +550,7 @@ class Network:
|
||||
|
||||
json = {"uris": [str(i) for i in uris[:100]]}
|
||||
|
||||
req = self.make_post_request('addPlaylistTracks', f'playlists/{uri.object_id}/tracks',
|
||||
req = self.post_request('addPlaylistTracks', f'playlists/{uri.object_id}/tracks',
|
||||
json=json, headers=headers)
|
||||
|
||||
if req is not None:
|
||||
@ -542,7 +585,7 @@ class Network:
|
||||
logger.warning('update dictionairy length 0')
|
||||
return None
|
||||
else:
|
||||
resp = self.make_get_request('getRecommendations', 'recommendations', params=params)
|
||||
resp = self.get_request('getRecommendations', 'recommendations', params=params)
|
||||
if resp:
|
||||
if 'tracks' in resp:
|
||||
return [self.parse_track(i) for i in resp['tracks']]
|
||||
@ -600,7 +643,7 @@ class Network:
|
||||
'range_length': range_length,
|
||||
'insert_before': insert_before}
|
||||
|
||||
resp = self.make_put_request('reorderPlaylistTracks', f'playlists/{uri.object_id}/tracks', json=json)
|
||||
resp = self.put_request('reorderPlaylistTracks', f'playlists/{uri.object_id}/tracks', json=json)
|
||||
|
||||
if resp:
|
||||
return resp
|
||||
@ -613,7 +656,7 @@ class Network:
|
||||
audio_features = []
|
||||
chunked_uris = list(self.chunk(uris, 100))
|
||||
for chunk in chunked_uris:
|
||||
resp = self.make_get_request('getAudioFeatures',
|
||||
resp = self.get_request('getAudioFeatures',
|
||||
url='audio-features',
|
||||
params={'ids': ','.join(i.object_id for i in chunk)})
|
||||
|
||||
@ -1006,10 +1049,10 @@ class PageCollection:
|
||||
|
||||
params = {'limit': self.page_limit}
|
||||
if url:
|
||||
resp = self.net.make_get_request(method=self.name, whole_url=url, params=params)
|
||||
resp = self.net.get_request(method=self.name, whole_url=url, params=params)
|
||||
else:
|
||||
if self.url:
|
||||
resp = self.net.make_get_request(method=self.name, url=self.url, params=params)
|
||||
resp = self.net.get_request(method=self.name, url=self.url, params=params)
|
||||
else:
|
||||
raise ValueError('no url to query')
|
||||
|
||||
|
@ -26,6 +26,8 @@ class NetworkUser(User):
|
||||
|
||||
self.on_refresh = []
|
||||
|
||||
self.refresh_counter = 0
|
||||
|
||||
def __repr__(self):
|
||||
return Color.RED + Color.BOLD + 'NetworkUser' + Color.END + \
|
||||
f': {self.username}, {self.display_name}, {self.uri}'
|
||||
@ -65,15 +67,16 @@ class NetworkUser(User):
|
||||
retry_after = req.headers.get('Retry-After', None)
|
||||
|
||||
if retry_after:
|
||||
logger.warning(f'refresh_token rate limit reached: retrying in {retry_after} seconds')
|
||||
logger.warning(f'rate limit reached: retrying in {retry_after} seconds')
|
||||
time.sleep(int(retry_after) + 1)
|
||||
return self.refresh_token()
|
||||
else:
|
||||
logger.error('refresh_token rate limit reached: cannot find Retry-After header')
|
||||
logger.error('rate limit reached: cannot find Retry-After header')
|
||||
|
||||
else:
|
||||
error_text = req.json()['error']['message']
|
||||
logger.error(f'refresh_token get {req.status_code} {error_text}')
|
||||
error_text = req.json().get('error', 'n/a')
|
||||
error_description = req.json().get('error_description', 'n/a')
|
||||
logger.error(f'get {req.status_code} {error_text} - {error_description}')
|
||||
|
||||
def refresh_info(self) -> None:
|
||||
info = self.get_info()
|
||||
@ -109,17 +112,26 @@ class NetworkUser(User):
|
||||
retry_after = req.headers.get('Retry-After', None)
|
||||
|
||||
if retry_after:
|
||||
logger.warning(f'get_info rate limit reached: retrying in {retry_after} seconds')
|
||||
logger.warning(f'rate limit reached: retrying in {retry_after} seconds')
|
||||
time.sleep(int(retry_after) + 1)
|
||||
return self.get_info()
|
||||
else:
|
||||
logger.error('get_info rate limit reached: cannot find Retry-After header')
|
||||
logger.error('rate limit reached: cannot find Retry-After header')
|
||||
|
||||
elif req.status_code == 401:
|
||||
logger.warning('access token expired, refreshing')
|
||||
self.refresh_token()
|
||||
if self.refresh_counter < 5:
|
||||
self.refresh_counter += 1
|
||||
return self.get_info()
|
||||
else:
|
||||
self.refresh_counter = 0
|
||||
logger.critical('refresh token limit (5) reached')
|
||||
|
||||
else:
|
||||
error_text = req.json()['error']['message']
|
||||
logger.error(f'get_info get {req.status_code} {error_text}')
|
||||
error = req.json().get('error', None)
|
||||
if error:
|
||||
message = error.get('message', 'n/a')
|
||||
logger.error(f'{req.status_code} {message}')
|
||||
else:
|
||||
logger.error(f'{req.status_code} no error object found')
|
||||
|
Loading…
Reference in New Issue
Block a user