single network request function, network error exception, using requests session
closes #3
This commit is contained in:
parent
dd802a0d8f
commit
3e679d37ac
8
alarm.py
8
alarm.py
@ -41,11 +41,9 @@ def check_phone():
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
network = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
|
||||||
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
client_secret=os.environ['SPOT_SECRET'],
|
||||||
os.environ['SPOT_SECRET'],
|
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
|
||||||
os.environ['SPOT_REFRESH']))
|
|
||||||
network.user.refresh_access_token()
|
|
||||||
|
|
||||||
found = False
|
found = False
|
||||||
|
|
||||||
|
@ -29,10 +29,9 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# try:
|
# try:
|
||||||
|
|
||||||
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
network = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
|
||||||
os.environ['SPOT_SECRET'],
|
client_secret=os.environ['SPOT_SECRET'],
|
||||||
os.environ['SPOT_REFRESH']))
|
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
|
||||||
network.user.refresh_access_token()
|
|
||||||
playlists = network.get_user_playlists()
|
playlists = network.get_user_playlists()
|
||||||
|
|
||||||
for playlist in playlists:
|
for playlist in playlists:
|
||||||
|
@ -122,10 +122,9 @@ def go():
|
|||||||
logger.critical('none to execute, terminating')
|
logger.critical('none to execute, terminating')
|
||||||
return
|
return
|
||||||
|
|
||||||
net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
net = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
|
||||||
os.environ['SPOT_SECRET'],
|
client_secret=os.environ['SPOT_SECRET'],
|
||||||
os.environ['SPOT_REFRESH']))
|
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
|
||||||
net.user.refresh_access_token()
|
|
||||||
|
|
||||||
engine = PlaylistEngine(net)
|
engine = PlaylistEngine(net)
|
||||||
|
|
||||||
|
@ -14,10 +14,8 @@ stream_handler.setFormatter(stream_formatter)
|
|||||||
logger.addHandler(stream_handler)
|
logger.addHandler(stream_handler)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
network = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
|
||||||
network = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
client_secret=os.environ['SPOT_SECRET'],
|
||||||
os.environ['SPOT_SECRET'],
|
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
|
||||||
os.environ['SPOT_REFRESH']))
|
|
||||||
network.user.refresh_access_token()
|
|
||||||
|
|
||||||
print(network.user.access_token)
|
print(network.user.access_token)
|
||||||
|
@ -39,7 +39,7 @@ def listen(verbose, client_id, client_secret, refresh_token):
|
|||||||
|
|
||||||
net = Network(NetworkUser(client_id=client_id,
|
net = Network(NetworkUser(client_id=client_id,
|
||||||
client_secret=client_secret,
|
client_secret=client_secret,
|
||||||
refresh_token=refresh_token).refresh_access_token())
|
refresh_token=refresh_token)).refresh_access_token()
|
||||||
cmd = ListenCmd(net, stream_handler)
|
cmd = ListenCmd(net, stream_handler)
|
||||||
cmd.cmdloop()
|
cmd.cmdloop()
|
||||||
|
|
||||||
|
@ -28,10 +28,9 @@ logger.addHandler(stream_handler)
|
|||||||
|
|
||||||
def go(playlist_name):
|
def go(playlist_name):
|
||||||
|
|
||||||
net = Network(NetworkUser(os.environ['SPOT_CLIENT'],
|
net = Network(NetworkUser(client_id=os.environ['SPOT_CLIENT'],
|
||||||
os.environ['SPOT_SECRET'],
|
client_secret=os.environ['SPOT_SECRET'],
|
||||||
os.environ['SPOT_REFRESH']))
|
refresh_token=os.environ['SPOT_REFRESH'])).refresh_access_token()
|
||||||
net.user.refresh_access_token()
|
|
||||||
|
|
||||||
engine = PlaylistEngine(net)
|
engine = PlaylistEngine(net)
|
||||||
engine.reorder_playlist_by_added_date(playlist_name)
|
engine.reorder_playlist_by_added_date(playlist_name)
|
||||||
|
@ -13,10 +13,7 @@ class AbstractProcessor(ABC):
|
|||||||
self.playlist_uris = uris
|
self.playlist_uris = uris
|
||||||
|
|
||||||
def has_targets(self) -> bool:
|
def has_targets(self) -> bool:
|
||||||
if self.playlist_names or self.playlist_uris:
|
return bool(self.playlist_names or self.playlist_uris)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def process(self, tracks: List[SimplifiedTrack]) -> List[SimplifiedTrack]:
|
def process(self, tracks: List[SimplifiedTrack]) -> List[SimplifiedTrack]:
|
||||||
@ -69,5 +66,5 @@ class BatchSingleTypeAwareProcessor(BatchSingleProcessor, ABC):
|
|||||||
return_tracks += malformed_tracks
|
return_tracks += malformed_tracks
|
||||||
|
|
||||||
return return_tracks
|
return return_tracks
|
||||||
else:
|
|
||||||
return tracks
|
return tracks
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from spotframework.model.track import CurrentlyPlaying
|
from spotframework.model.track import CurrentlyPlaying
|
||||||
from spotframework.net.network import Network
|
from spotframework.net.network import Network, SpotifyNetworkException
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import logging
|
import logging
|
||||||
@ -20,15 +20,20 @@ class Listener:
|
|||||||
|
|
||||||
self.recent_tracks = []
|
self.recent_tracks = []
|
||||||
self.prev_now_playing: Optional[CurrentlyPlaying] = None
|
self.prev_now_playing: Optional[CurrentlyPlaying] = None
|
||||||
|
self.now_playing = None
|
||||||
|
try:
|
||||||
self.now_playing: Optional[CurrentlyPlaying] = net.get_player()
|
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 = []
|
self.on_playback_change = []
|
||||||
|
|
||||||
def update_now_playing(self):
|
def update_now_playing(self):
|
||||||
"""update currently playing values"""
|
"""update currently playing values"""
|
||||||
logger.debug('updating now playing')
|
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:
|
if self.now_playing is None and live_now_playing is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -40,11 +45,15 @@ class Listener:
|
|||||||
else:
|
else:
|
||||||
self.now_playing = live_now_playing
|
self.now_playing = live_now_playing
|
||||||
|
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error occured retrieving currently playing - {e}')
|
||||||
|
|
||||||
def update_recent_tracks(self):
|
def update_recent_tracks(self):
|
||||||
"""retrieve recently played tracks and merge with previously stored"""
|
"""retrieve recently played tracks and merge with previously stored"""
|
||||||
logger.debug('updating recent tracks')
|
logger.debug('updating recent tracks')
|
||||||
|
|
||||||
|
try:
|
||||||
tracks = self.net.get_recently_played_tracks(response_limit=self.request_size)
|
tracks = self.net.get_recently_played_tracks(response_limit=self.request_size)
|
||||||
if tracks is not None:
|
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
if track.played_at not in [i.played_at for i in self.recent_tracks]:
|
if track.played_at not in [i.played_at for i in self.recent_tracks]:
|
||||||
self.recent_tracks.append(track)
|
self.recent_tracks.append(track)
|
||||||
@ -52,5 +61,6 @@ class Listener:
|
|||||||
self.recent_tracks.sort(key=lambda x: x.played_at)
|
self.recent_tracks.sort(key=lambda x: x.played_at)
|
||||||
if self.max_recent_tracks is not None:
|
if self.max_recent_tracks is not None:
|
||||||
self.recent_tracks = self.recent_tracks[-self.max_recent_tracks:]
|
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}')
|
||||||
|
@ -6,7 +6,3 @@ class Image:
|
|||||||
height: int
|
height: int
|
||||||
width: int
|
width: int
|
||||||
url: str
|
url: str
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def wrap(**kwargs):
|
|
||||||
return Image(**kwargs)
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,18 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import requests
|
|
||||||
from spotframework.model.user import PublicUser
|
from spotframework.model.user import PublicUser
|
||||||
from spotframework.util.console import Color
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from base64 import b64encode
|
from typing import List
|
||||||
import logging
|
from datetime import datetime
|
||||||
import time
|
|
||||||
from typing import Optional, List
|
|
||||||
from datetime import datetime, timezone
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NetworkUser:
|
class NetworkUser:
|
||||||
|
|
||||||
access_token: str
|
|
||||||
refresh_token: str
|
|
||||||
|
|
||||||
client_id: str
|
client_id: str
|
||||||
client_secret: str
|
client_secret: str
|
||||||
|
|
||||||
|
access_token: str = None
|
||||||
|
refresh_token: str = None
|
||||||
|
|
||||||
user: PublicUser = field(default=None, init=False)
|
user: PublicUser = field(default=None, init=False)
|
||||||
|
|
||||||
last_refreshed: datetime = 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)
|
on_refresh: List = field(default_factory=list, init=False)
|
||||||
|
|
||||||
refresh_counter: int = field(default=0, 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')
|
|
||||||
|
@ -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.track import SimplifiedTrack, Context, Device
|
||||||
from spotframework.model.album import AlbumFull
|
from spotframework.model.album import AlbumFull
|
||||||
from spotframework.model.playlist import FullPlaylist
|
from spotframework.model.playlist import FullPlaylist
|
||||||
from spotframework.model.uri import Uri
|
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -23,14 +22,20 @@ class Player:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def available_devices(self):
|
def available_devices(self):
|
||||||
|
try:
|
||||||
return self.net.get_available_devices()
|
return self.net.get_available_devices()
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error retrieving current devices - {e}')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
|
try:
|
||||||
new_status = self.net.get_player()
|
new_status = self.net.get_player()
|
||||||
if new_status:
|
if new_status:
|
||||||
self.last_status = new_status
|
self.last_status = new_status
|
||||||
return self.last_status
|
return self.last_status
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error retrieving current devices - {e}')
|
||||||
|
|
||||||
def play(self,
|
def play(self,
|
||||||
context: Union[Context, AlbumFull, FullPlaylist] = None,
|
context: Union[Context, AlbumFull, FullPlaylist] = None,
|
||||||
@ -43,6 +48,7 @@ class Player:
|
|||||||
if searched_device:
|
if searched_device:
|
||||||
device = searched_device
|
device = searched_device
|
||||||
|
|
||||||
|
try:
|
||||||
if context and (tracks or uris):
|
if context and (tracks or uris):
|
||||||
raise Exception('cant execute context and track list')
|
raise Exception('cant execute context and track list')
|
||||||
if context:
|
if context:
|
||||||
@ -64,15 +70,24 @@ class Player:
|
|||||||
self.net.play(uris=[i.uri for i in tracks] + uris)
|
self.net.play(uris=[i.uri for i in tracks] + uris)
|
||||||
else:
|
else:
|
||||||
self.net.play()
|
self.net.play()
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error playing - {e}')
|
||||||
|
|
||||||
def change_device(self, device: Device):
|
def change_device(self, device: Device):
|
||||||
|
try:
|
||||||
self.net.change_playback_device(device.id)
|
self.net.change_playback_device(device.id)
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error changing device to {device.name} - {e}')
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
|
try:
|
||||||
self.net.pause()
|
self.net.pause()
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error pausing - {e}')
|
||||||
|
|
||||||
def toggle_playback(self):
|
def toggle_playback(self):
|
||||||
status = self.status
|
status = self.status
|
||||||
|
try:
|
||||||
if status:
|
if status:
|
||||||
if status.is_playing:
|
if status.is_playing:
|
||||||
self.pause()
|
self.pause()
|
||||||
@ -81,17 +96,28 @@ class Player:
|
|||||||
else:
|
else:
|
||||||
logger.warning('no current playback, playing')
|
logger.warning('no current playback, playing')
|
||||||
self.play()
|
self.play()
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error toggling playback - {e}')
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
|
try:
|
||||||
self.net.next()
|
self.net.next()
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error skipping track - {e}')
|
||||||
|
|
||||||
def previous(self):
|
def previous(self):
|
||||||
|
try:
|
||||||
self.net.previous()
|
self.net.previous()
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error reversing track - {e}')
|
||||||
|
|
||||||
def shuffle(self, state=None):
|
def shuffle(self, state=None):
|
||||||
if state is not None:
|
if state is not None:
|
||||||
if isinstance(state, bool):
|
if isinstance(state, bool):
|
||||||
|
try:
|
||||||
self.net.set_shuffle(state)
|
self.net.set_shuffle(state)
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error setting shuffle - {e}')
|
||||||
else:
|
else:
|
||||||
raise TypeError(f'{state} is not bool')
|
raise TypeError(f'{state} is not bool')
|
||||||
else:
|
else:
|
||||||
@ -104,9 +130,12 @@ class Player:
|
|||||||
def volume(self, value: int, device: Device = None):
|
def volume(self, value: int, device: Device = None):
|
||||||
|
|
||||||
if 0 <= int(value) <= 100:
|
if 0 <= int(value) <= 100:
|
||||||
|
try:
|
||||||
if device:
|
if device:
|
||||||
self.net.set_volume(value, deviceid=device.id)
|
self.net.set_volume(value, deviceid=device.id)
|
||||||
else:
|
else:
|
||||||
self.net.set_volume(value)
|
self.net.set_volume(value)
|
||||||
|
except SpotifyNetworkException as e:
|
||||||
|
logger.error(f'error setting volume to {value} - {e}')
|
||||||
else:
|
else:
|
||||||
logger.error(f'{value} not between 0 and 100')
|
logger.error(f'{value} not between 0 and 100')
|
||||||
|
Loading…
Reference in New Issue
Block a user