diff --git a/alarm.py b/alarm.py index b0d9b48..3180de1 100644 --- a/alarm.py +++ b/alarm.py @@ -2,6 +2,7 @@ from spotframework.net.user import NetworkUser from spotframework.net.network import Network import spotframework.net.const as const import spotframework.io.json as json +import spotframework.util.monthstrings as month import os import datetime @@ -62,7 +63,8 @@ if __name__ == '__main__': playlists = network.get_user_playlists() if data['alarm']['use_month']: - playlisturi = next((i.uri for i in playlists if i.name == date.strftime("%B %-y").lower()), data['alarm']['uri']) + playlisturi = next((i.uri for i in playlists if i.name == month.get_this_month()), + data['alarm']['uri']) else: playlisturi = data['alarm']['uri'] diff --git a/backup.py b/backup.py index 6fd69cd..a6b807c 100644 --- a/backup.py +++ b/backup.py @@ -35,7 +35,7 @@ if __name__ == '__main__': playlists = network.get_user_playlists() for playlist in playlists: - playlist.tracks = network.get_playlist_tracks(playlist.playlist_id) + playlist.tracks = network.get_playlist_tracks(playlist.uri) path = sys.argv[1] diff --git a/spotframework/engine/playlistengine.py b/spotframework/engine/playlistengine.py index e1e9abb..2824335 100644 --- a/spotframework/engine/playlistengine.py +++ b/spotframework/engine/playlistengine.py @@ -8,6 +8,7 @@ from spotframework.engine.processor.added import AddedSince from typing import List, Optional from spotframework.model.track import SpotifyTrack from spotframework.model.playlist import SpotifyPlaylist +from spotframework.model.uri import Uri from spotframework.net.network import Network from spotframework.engine.processor.abstract import AbstractProcessor from datetime import datetime @@ -44,7 +45,7 @@ class PlaylistEngine: playlist: SpotifyPlaylist) -> None: logger.info(f"pulling tracks for {playlist.name}") - tracks = self.net.get_playlist_tracks(playlist.playlist_id) + tracks = self.net.get_playlist_tracks(playlist.uri) if tracks and len(tracks) > 0: playlist.tracks = tracks else: @@ -94,7 +95,7 @@ class PlaylistEngine: tracks = processor.process(tracks) if include_recommendations: - recommendations = self.net.get_recommendations(tracks=[i.spotify_id for i in tracks], + recommendations = self.net.get_recommendations(tracks=[i.uri.object_id for i in tracks], response_limit=recommendation_limit) if recommendations and len(recommendations) > 0: tracks += recommendations @@ -137,16 +138,16 @@ class PlaylistEngine: def reorder_playlist_by_added_date(self, name: str = None, - playlistid: str = None, + uri: Uri = None, reverse: bool = False): - if name is None and playlistid is None: + if name is None and uri is None: logger.error('no playlist name or id provided') raise ValueError('no playlist name or id provided') if name: playlist = next((i for i in self.playlists if i.name == name), None) else: - playlist = next((i for i in self.playlists if i.spotify_id == playlistid), None) + playlist = next((i for i in self.playlists if i.uri == uri), None) if playlist is None: logger.error('playlist not found') @@ -171,9 +172,9 @@ class PlaylistEngine: def execute_playlist(self, tracks: List[SpotifyTrack], - playlist_id: str) -> Optional[Response]: + uri: Uri) -> Optional[Response]: - resp = self.net.replace_playlist_tracks(playlist_id, [i.uri for i in tracks]) + resp = self.net.replace_playlist_tracks(uri, [i.uri for i in tracks]) if resp: return resp else: @@ -182,7 +183,7 @@ class PlaylistEngine: def change_description(self, playlistparts: List[str], - playlist_id: str, + uri: Uri, overwrite: bool = None, suffix: str = None) -> Optional[Response]: @@ -194,7 +195,7 @@ class PlaylistEngine: if suffix: string += f' - {str(suffix)}' - resp = self.net.change_playlist_details(playlist_id, description=string) + resp = self.net.change_playlist_details(uri, description=string) if resp: return resp else: diff --git a/spotframework/model/album.py b/spotframework/model/album.py index 6d10e92..e27cf54 100644 --- a/spotframework/model/album.py +++ b/spotframework/model/album.py @@ -1,7 +1,8 @@ from __future__ import annotations from typing import TYPE_CHECKING -from typing import List +from typing import List, Union from spotframework.util.console import Color +from spotframework.model.uri import Uri if TYPE_CHECKING: from spotframework.model.artist import Artist @@ -35,8 +36,7 @@ class SpotifyAlbum(Album): artists: List[Artist], href: str = None, - spotify_id: str = None, - uri: str = None, + uri: Union[str, Uri] = None, genres: List[str] = None, tracks: List = None, @@ -50,8 +50,10 @@ class SpotifyAlbum(Album): super().__init__(name, artists) self.href = href - self.spotify_id = spotify_id - self.uri = uri + if isinstance(uri, str): + self.uri = Uri(uri) + else: + self.uri = uri self.genres = genres self.tracks = tracks diff --git a/spotframework/model/artist.py b/spotframework/model/artist.py index 6ea1bb7..a0cd859 100644 --- a/spotframework/model/artist.py +++ b/spotframework/model/artist.py @@ -1,5 +1,6 @@ -from typing import List +from typing import List, Union from spotframework.util.console import Color +from spotframework.model.uri import Uri class Artist: @@ -19,8 +20,7 @@ class SpotifyArtist(Artist): name: str, href: str = None, - spotify_id: str = None, - uri: str = None, + uri: Union[str, Uri] = None, genres: List[str] = None, @@ -29,8 +29,10 @@ class SpotifyArtist(Artist): super().__init__(name) self.href = href - self.spotify_id = spotify_id - self.uri = uri + if isinstance(uri, str): + self.uri = Uri(uri) + else: + self.uri = uri self.genres = genres diff --git a/spotframework/model/playlist.py b/spotframework/model/playlist.py index 49a5425..364ad0c 100644 --- a/spotframework/model/playlist.py +++ b/spotframework/model/playlist.py @@ -1,8 +1,9 @@ from spotframework.model.user import User from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack +from spotframework.model.uri import Uri from spotframework.util.console import Color from tabulate import tabulate -from typing import List +from typing import List, Union import logging logger = logging.getLogger(__name__) @@ -119,14 +120,13 @@ class Playlist: class SpotifyPlaylist(Playlist): def __init__(self, - playlistid: str, + uri: Union[str, Uri], name: str = None, owner: User = None, description: str = None, href: str = None, - uri: str = None, collaborative: bool = None, public: bool = None, @@ -134,11 +134,13 @@ class SpotifyPlaylist(Playlist): super().__init__(name=name, description=description) - self.playlist_id = playlistid self.owner = owner self.href = href - self.uri = uri + if isinstance(uri, str): + self.uri = Uri(uri) + else: + self.uri = uri self.collaborative = collaborative self.public = public @@ -147,7 +149,6 @@ class SpotifyPlaylist(Playlist): def __str__(self): prefix = f'\n==={self.name}===\n\n' if self.name is not None else '' - prefix += f'id: {self.playlist_id}\n' if self.playlist_id is not None else '' prefix += f'uri: {self.uri}\n' if self.uri is not None else '' table = prefix + self.get_tracks_string() + '\n' + f'total: {len(self)}' diff --git a/spotframework/model/service.py b/spotframework/model/service.py index 71abe2d..2173b5f 100644 --- a/spotframework/model/service.py +++ b/spotframework/model/service.py @@ -1,15 +1,20 @@ from datetime import datetime from spotframework.model.track import Track +from spotframework.model.uri import Uri from enum import Enum +from typing import Union class Context: def __init__(self, - uri: str, + uri: Union[str, Uri], object_type: str = None, href: str = None, external_spot: str = None): - self.uri = uri + if isinstance(uri, str): + self.uri = Uri(uri) + else: + self.uri = uri self.object_type = object_type self.href = href self.external_spot = external_spot diff --git a/spotframework/model/track.py b/spotframework/model/track.py index 903a4f8..49d5104 100644 --- a/spotframework/model/track.py +++ b/spotframework/model/track.py @@ -1,12 +1,14 @@ from __future__ import annotations from typing import TYPE_CHECKING -from typing import List +from typing import List, Union from datetime import datetime +from spotframework.model.uri import Uri from spotframework.util.console import Color if TYPE_CHECKING: from spotframework.model.album import Album from spotframework.model.artist import Artist from spotframework.model.user import User + from spotframework.model.service import Context class Track: @@ -57,8 +59,7 @@ class SpotifyTrack(Track): artists: List[Artist], href: str = None, - spotify_id: str = None, - uri: str = None, + uri: Union[str, Uri] = None, disc_number: int = None, duration_ms: int = None, @@ -73,8 +74,10 @@ class SpotifyTrack(Track): excplicit=explicit) self.href = href - self.spotify_id = spotify_id - self.uri = uri + if isinstance(uri, str): + self.uri = Uri(uri) + else: + self.uri = uri self.is_playable = is_playable self.popularity = popularity @@ -90,13 +93,12 @@ class PlaylistTrack(SpotifyTrack): album: Album, artists: List[Artist], - added_at: str, + added_at: datetime, added_by: User, is_local: bool, href: str = None, - spotify_id: str = None, - uri: str = None, + uri: Union[str, Uri] = None, disc_number: int = None, duration_ms: int = None, @@ -107,7 +109,6 @@ class PlaylistTrack(SpotifyTrack): ): super().__init__(name=name, album=album, artists=artists, href=href, - spotify_id=spotify_id, uri=uri, disc_number=disc_number, @@ -116,10 +117,46 @@ class PlaylistTrack(SpotifyTrack): is_playable=is_playable, popularity=popularity) - self.added_at = datetime.fromisoformat(added_at.replace('T', ' ').replace('Z', '')) + self.added_at = added_at self.added_by = added_by self.is_local = is_local def __repr__(self): return Color.BOLD + Color.YELLOW + 'PlaylistTrack' + Color.END + \ f': {self.name}, ({self.album}), {self.artists}, {self.uri}, {self.added_at}' + + +class PlayedTrack(SpotifyTrack): + def __init__(self, + name: str, + album: Album, + artists: List[Artist], + + href: str = None, + uri: Union[str, Uri] = None, + + disc_number: int = None, + duration_ms: int = None, + explicit: bool = None, + is_playable: bool = None, + + popularity: int = None, + + played_at: datetime = None, + context: Context = None + ): + super().__init__(name=name, album=album, artists=artists, + href=href, + uri=uri, + + disc_number=disc_number, + duration_ms=duration_ms, + explicit=explicit, + is_playable=is_playable, + popularity=popularity) + self.played_at = played_at + self.context = context + + def __repr__(self): + return Color.BOLD + Color.YELLOW + 'PlayedTrack' + Color.END + \ + f': {self.name}, ({self.album}), {self.artists}, {self.uri}, {self.played_at}' diff --git a/spotframework/model/uri.py b/spotframework/model/uri.py new file mode 100644 index 0000000..e26201d --- /dev/null +++ b/spotframework/model/uri.py @@ -0,0 +1,52 @@ +from enum import Enum + + +class Uri: + + class ObjectType(Enum): + track = 1 + album = 2 + artist = 3 + user = 4 + playlist = 5 + + def __init__(self, input_string: str): + self.object_type = None + self.object_id = None + self.username = None + + parts = input_string.split(':') + + if parts[0] != 'spotify': + raise ValueError('malformed uri') + + if len(parts) == 3: + self.object_type = self.ObjectType[parts[1]] + self.object_id = parts[2] + elif len(parts) == 5: + if parts[1] != 'user': + raise ValueError('malformed uri') + self.object_type = self.ObjectType[parts[3]] + self.object_id = parts[4] + else: + raise ValueError(f'malformed uri: {len(parts)} parts') + + def __str__(self): + if self.username: + return f'spotify:user:{self.username}:{self.object_type.name}:{self.object_id}' + else: + return f'spotify:{self.object_type.name}:{self.object_id}' + + def __repr__(self): + if self.username: + return f'URI: {self.username} / {self.object_type.name} / {self.object_id}' + else: + return f'URI: {self.object_type.name} / {self.object_id}' + + def __eq__(self, other): + if isinstance(other, Uri): + if other.object_type == self.object_type and other.object_id == self.object_id: + return True + + return False + diff --git a/spotframework/model/user.py b/spotframework/model/user.py index e630a1d..86ff0bf 100644 --- a/spotframework/model/user.py +++ b/spotframework/model/user.py @@ -1,4 +1,6 @@ from spotframework.util.console import Color +from spotframework.model.uri import Uri +from typing import Union class User: @@ -6,14 +8,17 @@ class User: username: str, href: str = None, - uri: str = None, + uri: Union[str, Uri] = None, display_name: str = None, ext_spotify: str = None): self.username = username self.href = href - self.uri = uri + if isinstance(uri, str): + self.uri = Uri(uri) + else: + self.uri = uri self.display_name = display_name self.ext_spotify = ext_spotify diff --git a/spotframework/net/network.py b/spotframework/net/network.py index c05532f..8d4294a 100644 --- a/spotframework/net/network.py +++ b/spotframework/net/network.py @@ -3,12 +3,14 @@ import random import logging import time from typing import List, Optional +from datetime import datetime from . import const 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.track import Track, PlaylistTrack, PlayedTrack from spotframework.model.service import CurrentlyPlaying, Device +from spotframework.model.uri import Uri from requests.models import Response limit = 50 @@ -121,20 +123,20 @@ class Network: return None - def get_playlist(self, playlistid: str) -> Optional[SpotifyPlaylist]: + def get_playlist(self, uri: Uri) -> Optional[SpotifyPlaylist]: - logger.info(f"{playlistid}") + logger.info(f"{uri}") - tracks = self.get_playlist_tracks(playlistid) + tracks = self.get_playlist_tracks(uri) if tracks is not None: - playlist = SpotifyPlaylist(playlistid) + playlist = SpotifyPlaylist(uri.object_id) playlist.tracks += tracks return playlist else: - logger.error(f"{playlistid} - no tracks returned") + logger.error(f"{uri} - no tracks returned") return None def create_playlist(self, @@ -142,7 +144,7 @@ class Network: name='New Playlist', public=True, collaborative=False, - description=None) -> Optional[dict]: + description=None) -> Optional[SpotifyPlaylist]: json = {"name": name, "public": public, "collaborative": collaborative} @@ -152,7 +154,7 @@ class Network: req = self._make_post_request('createPlaylist', f'users/{username}/playlists', json=json) if 200 <= req.status_code < 300: - return req.json() + return parse.parse_playlist(req.json()) else: logger.error('error creating playlist') return None @@ -195,28 +197,28 @@ class Network: logger.error('no playlists returned to filter') return None - def get_playlist_tracks(self, playlistid, offset=0) -> List[PlaylistTrack]: + def get_playlist_tracks(self, uri: Uri, offset=0) -> List[PlaylistTrack]: - logger.info(f"{playlistid}{' ' + str(offset) if offset is not 0 else ''}") + logger.info(f"{uri}{' ' + str(offset) if offset is not 0 else ''}") tracks = [] params = {'offset': offset, 'limit': limit} - resp = self._make_get_request('getPlaylistTracks', f'playlists/{playlistid}/tracks', params=params) + resp = self._make_get_request('getPlaylistTracks', f'playlists/{uri.object_id}/tracks', params=params) if resp: if resp.get('items', None): tracks += [parse.parse_track(i) for i in resp.get('items', None)] else: - logger.warning(f'{playlistid} no items returned') + logger.warning(f'{uri} no items returned') if resp.get('next', None): - more_tracks = self.get_playlist_tracks(playlistid, offset + limit) + more_tracks = self.get_playlist_tracks(uri, offset + limit) if more_tracks: tracks += more_tracks else: - logger.warning(f'{playlistid} error on response') + logger.warning(f'{uri} error on response') return tracks @@ -231,6 +233,32 @@ class Network: logger.error('no devices returned') return None + def get_recently_played_tracks(self, + response_limit: int = None, + after: datetime = None, + before: datetime = None) -> Optional[List[PlayedTrack]]: + + logger.info("retrieving") + + params = dict() + + if response_limit: + params['limit'] = response_limit + if after and before: + raise ValueError('cant have before and after') + if after: + params['after'] = int(after.timestamp() * 1000) + if before: + params['before'] = int(before.timestamp() * 1000) + + resp = self._make_get_request('getRecentlyPlayedTracks', 'me/player/recently-played', params=params) + + if resp: + return [parse.parse_track(i) for i in resp['items']] + else: + logger.error('no tracks returned') + return None + def get_player(self) -> Optional[CurrentlyPlaying]: logger.info("retrieved") @@ -271,7 +299,7 @@ class Network: else: return None - def play(self, uri=None, uris=None, deviceid=None) -> Optional[Response]: + def play(self, uri: Uri = None, uris: List[Uri] = None, deviceid=None) -> Optional[Response]: logger.info(f"{uri}{' ' + deviceid if deviceid is not None else ''}") @@ -286,9 +314,9 @@ class Network: payload = dict() if uri: - payload['context_uri'] = uri + payload['context_uri'] = str(uri) if uris: - payload['uris'] = uris[:200] + payload['uris'] = [str(i) for i in uris[:200]] req = self._make_put_request('play', 'me/player/play', params=params, json=payload) if req: @@ -378,33 +406,34 @@ class Network: logger.error(f"{volume} not accepted value") return None - def replace_playlist_tracks(self, playlistid, uris): + def replace_playlist_tracks(self, uri: Uri, uris: List[Uri]): - logger.info(f"{playlistid}") + logger.info(f"{uri}") headers = {"Content-Type": "application/json"} - json = {"uris": uris[:100]} + json = {"uris": [str(i) for i in uris[:100]]} - req = self._make_put_request('replacePlaylistTracks', f'playlists/{playlistid}/tracks', json=json, headers=headers) + req = self._make_put_request('replacePlaylistTracks', f'playlists/{uri.object_id}/tracks', + json=json, headers=headers) if req is not None: if len(uris) > 100: - return self.add_playlist_tracks(playlistid, uris[100:]) + return self.add_playlist_tracks(uri, uris[100:]) return req else: logger.error(f'error replacing playlist tracks, total: {len(uris)}') def change_playlist_details(self, - playlistid, + uri: Uri, name=None, public=None, collaborative=None, description=None) -> Optional[Response]: - logger.info(f"{playlistid}") + logger.info(f"{uri}") headers = {"Content-Type": "application/json"} @@ -426,22 +455,24 @@ class Network: logger.warning('update dictionairy length 0') return None else: - req = self._make_put_request('changePlaylistDetails', f'playlists/{playlistid}', json=json, headers=headers) + req = self._make_put_request('changePlaylistDetails', f'playlists/{uri.object_id}', + json=json, headers=headers) if req: return req else: logger.error('error updating details') return None - def add_playlist_tracks(self, playlistid: str, uris: List[str]) -> List[dict]: + def add_playlist_tracks(self, uri: Uri, uris: List[Uri]) -> List[dict]: - logger.info(f"{playlistid}") + logger.info(f"{uri}") headers = {"Content-Type": "application/json"} - json = {"uris": uris[:100]} + json = {"uris": [str(i) for i in uris[:100]]} - req = self._make_post_request('addPlaylistTracks', f'playlists/{playlistid}/tracks', json=json, headers=headers) + req = self._make_post_request('addPlaylistTracks', f'playlists/{uri.object_id}/tracks', + json=json, headers=headers) if req is not None: resp = req.json() @@ -450,12 +481,12 @@ class Network: if len(uris) > 100: - snapshots += self.add_playlist_tracks(playlistid, uris[100:]) + snapshots += self.add_playlist_tracks(uri, uris[100:]) return snapshots else: - logger.error(f'error retrieving tracks {playlistid}, total: {len(uris)}') + logger.error(f'error retrieving tracks {uri}, total: {len(uris)}') return [] def get_recommendations(self, tracks=None, artists=None, response_limit=10) -> Optional[List[Track]]: @@ -490,17 +521,17 @@ class Network: playlist: SpotifyPlaylist, append_tracks: bool = False): - if playlist.playlist_id: + if playlist.uri: if playlist.tracks == -1: - self.replace_playlist_tracks(playlist.playlist_id, []) + self.replace_playlist_tracks(playlist.uri, []) elif playlist.tracks: if append_tracks: - self.add_playlist_tracks(playlist.playlist_id, [i.uri for i in playlist.tracks]) + self.add_playlist_tracks(playlist.uri, [i.uri for i in playlist.tracks]) else: - self.replace_playlist_tracks(playlist.playlist_id, [i.uri for i in playlist.tracks]) + self.replace_playlist_tracks(playlist.uri, [i.uri for i in playlist.tracks]) if playlist.name or playlist.collaborative or playlist.public or playlist.description: - self.change_playlist_details(playlist.playlist_id, + self.change_playlist_details(playlist.uri, playlist.name, playlist.public, playlist.collaborative, @@ -510,12 +541,12 @@ class Network: logger.error('playlist has no id') def reorder_playlist_tracks(self, - playlistid: str, + uri: Uri, range_start: int, range_length: int, insert_before: int) -> Optional[Response]: - logger.info(f'id: {playlistid}') + logger.info(f'id: {uri}') if range_start < 0: logger.error('range_start must be positive') @@ -531,7 +562,7 @@ class Network: 'range_length': range_length, 'insert_before': insert_before} - resp = self._make_put_request('reorderPlaylistTracks', f'playlists/{playlistid}/tracks', json=json) + resp = self._make_put_request('reorderPlaylistTracks', f'playlists/{uri.object_id}/tracks', json=json) if resp: return resp diff --git a/spotframework/net/parse/parse.py b/spotframework/net/parse/parse.py index 02aec4f..5e28715 100644 --- a/spotframework/net/parse/parse.py +++ b/spotframework/net/parse/parse.py @@ -1,10 +1,11 @@ from spotframework.model.artist import SpotifyArtist from spotframework.model.album import SpotifyAlbum -from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack +from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack, PlayedTrack from spotframework.model.playlist import SpotifyPlaylist from spotframework.model.user import User from spotframework.model.service import Context, CurrentlyPlaying, Device import datetime +from typing import Union def parse_artist(artist_dict) -> SpotifyArtist: @@ -12,7 +13,6 @@ def parse_artist(artist_dict) -> SpotifyArtist: name = artist_dict.get('name', None) href = artist_dict.get('href', None) - spotify_id = artist_dict.get('id', None) uri = artist_dict.get('uri', None) genres = artist_dict.get('genres', None) @@ -23,7 +23,6 @@ def parse_artist(artist_dict) -> SpotifyArtist: return SpotifyArtist(name, href=href, - spotify_id=spotify_id, uri=uri, genres=genres, @@ -39,7 +38,6 @@ def parse_album(album_dict) -> SpotifyAlbum: artists = [parse_artist(i) for i in album_dict.get('artists', [])] href = album_dict.get('href', None) - spotify_id = album_dict.get('id', None) uri = album_dict.get('uri', None) genres = album_dict.get('genres', None) @@ -55,7 +53,6 @@ def parse_album(album_dict) -> SpotifyAlbum: artists=artists, href=href, - spotify_id=spotify_id, uri=uri, genres=genres, @@ -68,7 +65,7 @@ def parse_album(album_dict) -> SpotifyAlbum: popularity=popularity) -def parse_track(track_dict) -> Track: +def parse_track(track_dict) -> Union[Track, SpotifyTrack, PlayedTrack]: if 'track' in track_dict: track = track_dict.get('track', None) @@ -84,12 +81,9 @@ def parse_track(track_dict) -> Track: else: album = None - # print(album.name) - artists = [parse_artist(i) for i in track.get('artists', [])] href = track.get('href', None) - spotify_id = track.get('id', None) uri = track.get('uri', None) disc_number = track.get('disc_number', None) @@ -101,9 +95,16 @@ def parse_track(track_dict) -> Track: added_by = parse_user(track_dict.get('added_by')) if track_dict.get('added_by', None) else None added_at = track_dict.get('added_at', None) + if added_at: + added_at = datetime.datetime.strptime(added_at, '%Y-%m-%dT%H:%M:%S%z') is_local = track_dict.get('is_local', None) - # print(album.name) + played_at = track_dict.get('played_at', None) + if played_at: + played_at = datetime.datetime.strptime(played_at, '%Y-%m-%dT%H:%M:%S.%f%z') + context = track_dict.get('context', None) + if context: + context = parse_context(context) if added_at or added_by or is_local: return PlaylistTrack(name=name, @@ -115,7 +116,6 @@ def parse_track(track_dict) -> Track: is_local=is_local, href=href, - spotify_id=spotify_id, uri=uri, disc_number=disc_number, @@ -124,13 +124,28 @@ def parse_track(track_dict) -> Track: is_playable=is_playable, popularity=popularity) + elif played_at or context: + return PlayedTrack(name=name, + album=album, + artists=artists, + + href=href, + uri=uri, + + disc_number=disc_number, + duration_ms=duration_ms, + explicit=explicit, + is_playable=is_playable, + + popularity=popularity, + played_at=played_at, + context=context) else: return SpotifyTrack(name=name, album=album, artists=artists, href=href, - spotify_id=spotify_id, uri=uri, disc_number=disc_number, @@ -164,7 +179,6 @@ def parse_playlist(playlist_dict) -> SpotifyPlaylist: ext_spotify = playlist_dict['external_urls']['spotify'] href = playlist_dict.get('href', None) - playlist_id = playlist_dict.get('id', None) description = playlist_dict.get('description', None) name = playlist_dict.get('name', None) @@ -177,12 +191,11 @@ def parse_playlist(playlist_dict) -> SpotifyPlaylist: public = playlist_dict.get('public', None) uri = playlist_dict.get('uri', None) - return SpotifyPlaylist(playlistid=playlist_id, + return SpotifyPlaylist(uri=uri, name=name, owner=owner, description=description, href=href, - uri=uri, collaborative=collaborative, public=public, ext_spotify=ext_spotify) diff --git a/spotframework/player/player.py b/spotframework/player/player.py index ab590df..b0041d1 100644 --- a/spotframework/player/player.py +++ b/spotframework/player/player.py @@ -1,7 +1,9 @@ from spotframework.net.network import Network from spotframework.model.track import SpotifyTrack +from spotframework.model.album import SpotifyAlbum +from spotframework.model.playlist import SpotifyPlaylist from spotframework.model.service import Context, Device -from typing import List +from typing import List, Union import logging logger = logging.getLogger(__name__) @@ -32,7 +34,7 @@ class Player: return self.last_status def play(self, - context: Context = None, + context: Union[Context, SpotifyAlbum, SpotifyPlaylist] = None, tracks: List[SpotifyTrack] = None, device: Device = None): if context and tracks: