single network request function, network error exception, using requests session

closes #3
This commit is contained in:
aj 2020-06-22 20:15:22 +01:00
parent dd802a0d8f
commit 3e679d37ac
12 changed files with 414 additions and 618 deletions

View File

@ -41,11 +41,9 @@ def check_phone():
if __name__ == '__main__':
try:
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOT_SECRET'],
os.environ['SPOT_REFRESH']))
network.user.refresh_access_token()
network = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
client_secret=os.environ['SPOT_SECRET'],
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
found = False

View File

@ -29,10 +29,9 @@ if __name__ == '__main__':
# try:
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOT_SECRET'],
os.environ['SPOT_REFRESH']))
network.user.refresh_access_token()
network = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
client_secret=os.environ['SPOT_SECRET'],
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
playlists = network.get_user_playlists()
for playlist in playlists:

View File

@ -122,10 +122,9 @@ def go():
logger.critical('none to execute, terminating')
return
net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOT_SECRET'],
os.environ['SPOT_REFRESH']))
net.user.refresh_access_token()
net = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
client_secret=os.environ['SPOT_SECRET'],
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
engine = PlaylistEngine(net)

View File

@ -14,10 +14,8 @@ stream_handler.setFormatter(stream_formatter)
logger.addHandler(stream_handler)
if __name__ == '__main__':
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOT_SECRET'],
os.environ['SPOT_REFRESH']))
network.user.refresh_access_token()
network = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
client_secret=os.environ['SPOT_SECRET'],
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
print(network.user.access_token)

View File

@ -39,7 +39,7 @@ def listen(verbose, client_id, client_secret, refresh_token):
net = Network(NetworkUser(client_id=client_id,
client_secret=client_secret,
refresh_token=refresh_token).refresh_access_token())
refresh_token=refresh_token)).refresh_access_token()
cmd = ListenCmd(net, stream_handler)
cmd.cmdloop()

View File

@ -28,10 +28,9 @@ logger.addHandler(stream_handler)
def go(playlist_name):
net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
os.environ['SPOT_SECRET'],
os.environ['SPOT_REFRESH']))
net.user.refresh_access_token()
net = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
client_secret=os.environ['SPOT_SECRET'],
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
engine = PlaylistEngine(net)
engine.reorder_playlist_by_added_date(playlist_name)

View File

@ -13,10 +13,7 @@ class AbstractProcessor(ABC):
self.playlist_uris = uris
def has_targets(self) -> bool:
if self.playlist_names or self.playlist_uris:
return True
else:
return False
return bool(self.playlist_names or self.playlist_uris)
@abstractmethod
def process(self, tracks: List[SimplifiedTrack]) -> List[SimplifiedTrack]:
@ -69,5 +66,5 @@ class BatchSingleTypeAwareProcessor(BatchSingleProcessor, ABC):
return_tracks += malformed_tracks
return return_tracks
else:
return tracks

View File

@ -1,5 +1,5 @@
from spotframework.model.track import CurrentlyPlaying
from spotframework.net.network import Network
from spotframework.net.network import Network, SpotifyNetworkException
from typing import Optional
import logging
@ -20,15 +20,20 @@ class Listener:
self.recent_tracks = []
self.prev_now_playing: Optional[CurrentlyPlaying] = None
self.now_playing = None
try:
self.now_playing: Optional[CurrentlyPlaying] = net.get_player()
except SpotifyNetworkException as e:
logger.error(f'error occured retrieving currently playing - {e}')
self.on_playback_change = []
def update_now_playing(self):
"""update currently playing values"""
logger.debug('updating now playing')
live_now_playing = self.net.get_player()
try:
live_now_playing = self.net.get_player()
if self.now_playing is None and live_now_playing is None:
return
@ -40,11 +45,15 @@ class Listener:
else:
self.now_playing = live_now_playing
except SpotifyNetworkException as e:
logger.error(f'error occured retrieving currently playing - {e}')
def update_recent_tracks(self):
"""retrieve recently played tracks and merge with previously stored"""
logger.debug('updating recent tracks')
try:
tracks = self.net.get_recently_played_tracks(response_limit=self.request_size)
if tracks is not None:
for track in tracks:
if track.played_at not in [i.played_at for i in self.recent_tracks]:
self.recent_tracks.append(track)
@ -52,5 +61,6 @@ class Listener:
self.recent_tracks.sort(key=lambda x: x.played_at)
if self.max_recent_tracks is not None:
self.recent_tracks = self.recent_tracks[-self.max_recent_tracks:]
else:
logger.error('no recent tracks returned')
except SpotifyNetworkException as e:
logger.error(f'error occured retrieving recent tracks - {e}')

View File

@ -6,7 +6,3 @@ class Image:
height: int
width: int
url: str
@staticmethod
def wrap(**kwargs):
return Image(**kwargs)

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,18 @@
from __future__ import annotations
import requests
from spotframework.model.user import PublicUser
from spotframework.util.console import Color
from dataclasses import dataclass, field
from base64 import b64encode
import logging
import time
from typing import Optional, List
from datetime import datetime, timezone
logger = logging.getLogger(__name__)
from typing import List
from datetime import datetime
@dataclass
class NetworkUser:
access_token: str
refresh_token: str
client_id: str
client_secret: str
access_token: str = None
refresh_token: str = None
user: PublicUser = field(default=None, init=False)
last_refreshed: datetime = field(default=None, init=False)
@ -29,93 +21,3 @@ class NetworkUser:
on_refresh: List = field(default_factory=list, init=False)
refresh_counter: int = field(default=0, init=False)
def refresh_access_token(self) -> NetworkUser:
if self.refresh_token is None:
raise NameError('no refresh token to query')
if self.client_id is None:
raise NameError('no client id')
if self.client_secret is None:
raise NameError('no client secret')
idsecret = b64encode(bytes(self.client_id + ':' + self.client_secret, "utf-8")).decode("ascii")
headers = {'Authorization': 'Basic %s' % idsecret}
data = {"grant_type": "refresh_token", "refresh_token": self.refresh_token}
now = datetime.utcnow()
req = requests.post('https://accounts.spotify.com/api/token', data=data, headers=headers)
if 200 <= req.status_code < 300:
logger.debug('token refreshed')
resp = req.json()
self.access_token = resp['access_token']
if resp.get('refresh_token', None):
self.refresh_token = resp['refresh_token']
self.token_expiry = resp['expires_in']
self.last_refreshed = now
for func in self.on_refresh:
func(self)
else:
if req.status_code == 429:
retry_after = req.headers.get('Retry-After', None)
if retry_after:
logger.warning(f'rate limit reached: retrying in {retry_after} seconds')
time.sleep(int(retry_after) + 1)
return self.refresh_access_token()
else:
logger.error('rate limit reached: cannot find Retry-After header')
else:
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}')
return self
def refresh_info(self) -> None:
self.user = PublicUser(**self.get_info())
def get_info(self) -> Optional[dict]:
headers = {'Authorization': 'Bearer %s' % self.access_token}
req = requests.get('https://api.spotify.com/v1/me', headers=headers)
if 200 <= req.status_code < 300:
logger.debug(f'retrieved {req.status_code}')
return req.json()
else:
if req.status_code == 429:
retry_after = req.headers.get('Retry-After', None)
if retry_after:
logger.warning(f'rate limit reached: retrying in {retry_after} seconds')
time.sleep(int(retry_after) + 1)
return self.get_info()
else:
logger.error('rate limit reached: cannot find Retry-After header')
elif req.status_code == 401:
logger.warning('access token expired, refreshing')
self.refresh_access_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 = 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')

View File

@ -1,8 +1,7 @@
from spotframework.net.network import Network
from spotframework.net.network import Network, SpotifyNetworkException
from spotframework.model.track import SimplifiedTrack, Context, Device
from spotframework.model.album import AlbumFull
from spotframework.model.playlist import FullPlaylist
from spotframework.model.uri import Uri
from typing import List, Union
import logging
logger = logging.getLogger(__name__)
@ -23,14 +22,20 @@ class Player:
@property
def available_devices(self):
try:
return self.net.get_available_devices()
except SpotifyNetworkException as e:
logger.error(f'error retrieving current devices - {e}')
@property
def status(self):
try:
new_status = self.net.get_player()
if new_status:
self.last_status = new_status
return self.last_status
except SpotifyNetworkException as e:
logger.error(f'error retrieving current devices - {e}')
def play(self,
context: Union[Context, AlbumFull, FullPlaylist] = None,
@ -43,6 +48,7 @@ class Player:
if searched_device:
device = searched_device
try:
if context and (tracks or uris):
raise Exception('cant execute context and track list')
if context:
@ -64,15 +70,24 @@ class Player:
self.net.play(uris=[i.uri for i in tracks] + uris)
else:
self.net.play()
except SpotifyNetworkException as e:
logger.error(f'error playing - {e}')
def change_device(self, device: Device):
try:
self.net.change_playback_device(device.id)
except SpotifyNetworkException as e:
logger.error(f'error changing device to {device.name} - {e}')
def pause(self):
try:
self.net.pause()
except SpotifyNetworkException as e:
logger.error(f'error pausing - {e}')
def toggle_playback(self):
status = self.status
try:
if status:
if status.is_playing:
self.pause()
@ -81,17 +96,28 @@ class Player:
else:
logger.warning('no current playback, playing')
self.play()
except SpotifyNetworkException as e:
logger.error(f'error toggling playback - {e}')
def next(self):
try:
self.net.next()
except SpotifyNetworkException as e:
logger.error(f'error skipping track - {e}')
def previous(self):
try:
self.net.previous()
except SpotifyNetworkException as e:
logger.error(f'error reversing track - {e}')
def shuffle(self, state=None):
if state is not None:
if isinstance(state, bool):
try:
self.net.set_shuffle(state)
except SpotifyNetworkException as e:
logger.error(f'error setting shuffle - {e}')
else:
raise TypeError(f'{state} is not bool')
else:
@ -104,9 +130,12 @@ class Player:
def volume(self, value: int, device: Device = None):
if 0 <= int(value) <= 100:
try:
if device:
self.net.set_volume(value, deviceid=device.id)
else:
self.net.set_volume(value)
except SpotifyNetworkException as e:
logger.error(f'error setting volume to {value} - {e}')
else:
logger.error(f'{value} not between 0 and 100')