added player and service objects

This commit is contained in:
aj 2019-09-15 03:32:24 +01:00
parent 92217ad3a4
commit 6076ecd610
13 changed files with 279 additions and 28 deletions

View File

@ -43,7 +43,6 @@ if __name__ == '__main__':
network = Network(NetworkUser(os.environ['SPOTCLIENT'],
os.environ['SPOTSECRET'],
os.environ['SPOTACCESS'],
os.environ['SPOTREFRESH']))
found = False

View File

@ -31,7 +31,6 @@ if __name__ == '__main__':
network = Network(NetworkUser(os.environ['SPOTCLIENT'],
os.environ['SPOTSECRET'],
os.environ['SPOTACCESS'],
os.environ['SPOTREFRESH']))
playlists = network.get_user_playlists()

View File

@ -124,7 +124,6 @@ def go():
net = Network(NetworkUser(os.environ['SPOTCLIENT'],
os.environ['SPOTSECRET'],
os.environ['SPOTACCESS'],
os.environ['SPOTREFRESH']))
engine = PlaylistEngine(net)

View File

@ -17,7 +17,6 @@ if __name__ == '__main__':
network = Network(NetworkUser(os.environ['SPOTCLIENT'],
os.environ['SPOTSECRET'],
os.environ['SPOTACCESS'],
os.environ['SPOTREFRESH']))
print(network.user.access_token)

View File

@ -30,7 +30,6 @@ def go(playlist_name):
net = Network(NetworkUser(os.environ['SPOTCLIENT'],
os.environ['SPOTSECRET'],
os.environ['SPOTACCESS'],
os.environ['SPOTREFRESH']))
engine = PlaylistEngine(net)

View File

@ -26,13 +26,7 @@ class BatchSingleProcessor(AbstractProcessor, ABC):
return track
def process_batch(self, tracks: List[Track]) -> List[Track]:
processed = []
for track in tracks:
processed_track = self.process_single(track)
processed.append(processed_track)
return processed
return [self.process_single(track) for track in tracks]
def process(self, tracks: List[Track]) -> List[Track]:
return [i for i in self.process_batch(tracks) if i is not None]

View File

@ -26,6 +26,12 @@ class Playlist:
def __len__(self):
return len(self.tracks)
def __getitem__(self, item) -> Track:
return self.tracks[item]
def __iter__(self):
return iter(self.tracks)
@property
def tracks(self) -> List[Track]:
return self._tracks

View File

@ -0,0 +1,91 @@
from datetime import datetime
from spotframework.model.track import Track
from enum import Enum
class Context:
def __init__(self,
uri: str,
object_type: str = None,
href: str = None,
external_spot: str = None):
self.uri = uri
self.object_type = object_type
self.href = href
self.external_spot = external_spot
def __repr__(self):
return f'Context: {self.object_type} uri({self.uri})'
def __str__(self):
return f'{self.object_type} / {self.uri}'
class Device:
class DeviceType(Enum):
COMPUTER = 1
TABLET = 2
SMARTPHONE = 3
SPEAKER = 4
TV = 5
AVR = 6
STB = 7
AUDIODONGLE = 8
GAMECONSOLE = 9
CASTVIDEO = 10
CASTAUDIO = 11
AUTOMOBILE = 12
UNKNOWN = 13
def __init__(self,
device_id: str,
is_active: bool,
is_private_session: bool,
is_restricted: bool,
name: str,
object_type: DeviceType,
volume: int):
self.device_id = device_id
self.is_active = is_active
self.is_private_session = is_private_session
self.is_restricted = is_restricted
self.name = name
self.object_type = object_type
self.volume = volume
def __repr__(self):
return f'Device: {self.name} active({self.is_active}) type({self.object_type}) vol({self.volume})'
def __str__(self):
return self.name
class CurrentlyPlaying:
def __init__(self,
context: Context,
timestamp: datetime,
progress_ms: int,
is_playing: bool,
track: Track,
device: Device,
shuffle: bool,
repeat: bool,
currently_playing_type: str):
self.context = context
self.timestamp = timestamp
self.progress_ms = progress_ms
self.is_playing = is_playing
self.track = track
self.device = device
self.shuffle = shuffle
self.repeat = repeat
self.currently_playing_type = currently_playing_type
def __repr__(self):
return f'CurrentlyPlaying: is_playing({self.is_playing}) progress({self.progress_ms}) ' \
f'context({self.context}) track({self.track}) device({self.device}) shuffle({self.shuffle}) ' \
f'repeat({self.repeat}) time({self.timestamp})'
def __str__(self):
return f'playing: {self.is_playing} - {self.track} on {self.device}'

View File

@ -8,6 +8,7 @@ from spotframework.net.parse import parse
from spotframework.net.user import NetworkUser
from spotframework.model.playlist import SpotifyPlaylist
from spotframework.model.track import Track, PlaylistTrack
from spotframework.model.service import CurrentlyPlaying, Device
from requests.models import Response
limit = 50
@ -28,8 +29,12 @@ class Network:
if 200 <= req.status_code < 300:
logger.debug(f'{method} get {req.status_code}')
if req.status_code != 204:
return req.json()
else:
return None
else:
if req.status_code == 429:
retry_after = req.headers.get('Retry-After', None)
@ -215,37 +220,55 @@ class Network:
return tracks
def get_available_devices(self) -> Optional[dict]:
def get_available_devices(self) -> Optional[List[Device]]:
logger.info("retrieving")
resp = self._make_get_request('getAvailableDevices', 'me/player/devices')
if resp:
return resp
return [parse.parse_device(i) for i in resp['devices']]
else:
logger.error('no devices returned')
return None
def get_player(self) -> Optional[dict]:
def get_player(self) -> Optional[CurrentlyPlaying]:
logger.info("retrieved")
resp = self._make_get_request('getPlayer', 'me/player')
if resp:
return resp
return parse.parse_currently_playing(resp)
else:
logger.error('no player returned')
logger.info('no player returned')
return None
def get_device_id(self, devicename) -> Optional[str]:
logger.info(f"{devicename}")
resp = self.get_available_devices()
if resp:
return next((i for i in resp['devices'] if i['name'] == devicename), None)['id']
devices = self.get_available_devices()
if devices:
device = next((i for i in devices if i.name == devicename), None)
if device:
return device.device_id
else:
logger.error(f'{devicename} not found')
else:
logger.error('no devices returned')
def change_playback_device(self, device_id):
logger.info(device_id)
json = {
'device_ids': [device_id],
'play': True
}
resp = self._make_put_request('changePlaybackDevice', 'me/player', json=json)
if resp:
return True
else:
return None
def play(self, uri=None, uris=None, deviceid=None) -> Optional[Response]:
@ -260,14 +283,12 @@ class Network:
if uri and uris:
raise Exception('wont take both context uri and uris')
payload = dict()
if uri:
payload = {'context_uri': uri}
payload['context_uri'] = uri
if uris:
payload = {'uris': uris[:200]}
if not uri and not uris:
raise Exception('need either context uri or uris')
payload['uris'] = uris[:200]
req = self._make_put_request('play', 'me/player/play', params=params, json=payload)
if req:
@ -305,6 +326,21 @@ class Network:
else:
logger.error('error skipping')
def previous(self, deviceid=None) -> Optional[Response]:
logger.info(f"{deviceid if deviceid is not None else ''}")
if deviceid is not None:
params = {'device_id': deviceid}
else:
params = None
req = self._make_post_request('previous', 'me/player/previous', params=params)
if req:
return req
else:
logger.error('error reversing')
def set_shuffle(self, state, deviceid=None) -> Optional[Response]:
logger.info(f"{state}{' ' + deviceid if deviceid is not None else ''}")

View File

@ -3,6 +3,8 @@ from spotframework.model.album import SpotifyAlbum
from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack
from spotframework.model.playlist import SpotifyPlaylist
from spotframework.model.user import User
from spotframework.model.service import Context, CurrentlyPlaying, Device
import datetime
def parse_artist(artist_dict) -> SpotifyArtist:
@ -184,3 +186,33 @@ def parse_playlist(playlist_dict) -> SpotifyPlaylist:
collaborative=collaborative,
public=public,
ext_spotify=ext_spotify)
def parse_context(context_dict) -> Context:
return Context(object_type=context_dict['type'],
href=context_dict['href'],
external_spot=context_dict['external_urls']['spotify'],
uri=context_dict['uri'])
def parse_currently_playing(play_dict) -> CurrentlyPlaying:
return CurrentlyPlaying(context=parse_context(play_dict['context']) if play_dict['context'] is not None else None,
timestamp=datetime.datetime.fromtimestamp(play_dict['timestamp']/1000),
progress_ms=play_dict['progress_ms'],
is_playing=play_dict['is_playing'],
track=parse_track(play_dict['item']),
device=parse_device(play_dict['device']),
shuffle=play_dict['shuffle_state'],
repeat=play_dict['repeat_state'],
currently_playing_type=play_dict['currently_playing_type'])
def parse_device(device_dict) -> Device:
return Device(device_id=device_dict['id'],
is_active=device_dict['is_active'],
is_private_session=device_dict['is_private_session'],
is_restricted=device_dict['is_restricted'],
name=device_dict['name'],
object_type=Device.DeviceType[device_dict['type'].upper()],
volume=device_dict['volume_percent'])

View File

@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
class NetworkUser(User):
def __init__(self, client_id, client_secret, access_token, refresh_token):
def __init__(self, client_id, client_secret, refresh_token, access_token=None):
super().__init__('')
self.accesstoken = access_token

View File

View File

@ -0,0 +1,97 @@
from spotframework.net.network import Network
from spotframework.model.track import SpotifyTrack
from spotframework.model.service import Context, Device
from typing import List
import logging
logger = logging.getLogger(__name__)
class Player:
def __init__(self,
net: Network):
self.net = net
self.user = net.user
self.last_status = None
def __str__(self):
return f'{self.user.username} - {self.status}'
def __repr__(self):
return f'Player: {self.user} - {self.status}'
@property
def available_devices(self):
return self.net.get_available_devices()
@property
def status(self):
new_status = self.net.get_player()
if new_status:
self.last_status = new_status
return self.last_status
def play(self,
context: Context = None,
tracks: List[SpotifyTrack] = None,
device: Device = None):
if context and tracks:
raise Exception('cant execute context and track list')
if context:
if device:
self.net.play(uri=context.uri, deviceid=device.device_id)
else:
self.net.play(uri=context.uri)
elif tracks:
if device:
self.net.play(uris=[i.uri for i in tracks], deviceid=device.device_id)
else:
self.net.play(uris=[i.uri for i in tracks])
else:
self.net.play()
def change_device(self, device: Device):
self.net.change_playback_device(device.device_id)
def pause(self):
self.net.pause()
def toggle_playback(self):
status = self.status
if status:
if status.is_playing:
self.pause()
else:
self.play()
else:
logger.warning('no current playback, playing')
self.play()
def next(self):
self.net.next()
def previous(self):
self.net.previous()
def shuffle(self, state=None):
if state is not None:
if isinstance(state, bool):
self.net.set_shuffle(state)
else:
raise TypeError(f'{state} is not bool')
else:
status = self.status
if status.shuffle:
self.shuffle(state=False)
else:
self.shuffle(state=True)
def set_volume(self, value: int, device: Device = None):
if 0 <= int(value) <= 100:
if device:
self.net.set_volume(value, deviceid=device.device_id)
else:
self.net.set_volume(value)
else:
logger.error(f'{value} not between 0 and 100')