migrated to uri id base

This commit is contained in:
aj 2019-09-15 15:33:29 +01:00
parent 6076ecd610
commit 2bd26df92f
13 changed files with 250 additions and 97 deletions

View File

@ -2,6 +2,7 @@ from spotframework.net.user import NetworkUser
from spotframework.net.network import Network from spotframework.net.network import Network
import spotframework.net.const as const import spotframework.net.const as const
import spotframework.io.json as json import spotframework.io.json as json
import spotframework.util.monthstrings as month
import os import os
import datetime import datetime
@ -62,7 +63,8 @@ if __name__ == '__main__':
playlists = network.get_user_playlists() playlists = network.get_user_playlists()
if data['alarm']['use_month']: 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: else:
playlisturi = data['alarm']['uri'] playlisturi = data['alarm']['uri']

View File

@ -35,7 +35,7 @@ if __name__ == '__main__':
playlists = network.get_user_playlists() playlists = network.get_user_playlists()
for playlist in 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] path = sys.argv[1]

View File

@ -8,6 +8,7 @@ from spotframework.engine.processor.added import AddedSince
from typing import List, Optional from typing import List, Optional
from spotframework.model.track import SpotifyTrack from spotframework.model.track import SpotifyTrack
from spotframework.model.playlist import SpotifyPlaylist from spotframework.model.playlist import SpotifyPlaylist
from spotframework.model.uri import Uri
from spotframework.net.network import Network from spotframework.net.network import Network
from spotframework.engine.processor.abstract import AbstractProcessor from spotframework.engine.processor.abstract import AbstractProcessor
from datetime import datetime from datetime import datetime
@ -44,7 +45,7 @@ class PlaylistEngine:
playlist: SpotifyPlaylist) -> None: playlist: SpotifyPlaylist) -> None:
logger.info(f"pulling tracks for {playlist.name}") 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: if tracks and len(tracks) > 0:
playlist.tracks = tracks playlist.tracks = tracks
else: else:
@ -94,7 +95,7 @@ class PlaylistEngine:
tracks = processor.process(tracks) tracks = processor.process(tracks)
if include_recommendations: 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) response_limit=recommendation_limit)
if recommendations and len(recommendations) > 0: if recommendations and len(recommendations) > 0:
tracks += recommendations tracks += recommendations
@ -137,16 +138,16 @@ class PlaylistEngine:
def reorder_playlist_by_added_date(self, def reorder_playlist_by_added_date(self,
name: str = None, name: str = None,
playlistid: str = None, uri: Uri = None,
reverse: bool = False): 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') logger.error('no playlist name or id provided')
raise ValueError('no playlist name or id provided') raise ValueError('no playlist name or id provided')
if name: if name:
playlist = next((i for i in self.playlists if i.name == name), None) playlist = next((i for i in self.playlists if i.name == name), None)
else: 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: if playlist is None:
logger.error('playlist not found') logger.error('playlist not found')
@ -171,9 +172,9 @@ class PlaylistEngine:
def execute_playlist(self, def execute_playlist(self,
tracks: List[SpotifyTrack], 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: if resp:
return resp return resp
else: else:
@ -182,7 +183,7 @@ class PlaylistEngine:
def change_description(self, def change_description(self,
playlistparts: List[str], playlistparts: List[str],
playlist_id: str, uri: Uri,
overwrite: bool = None, overwrite: bool = None,
suffix: str = None) -> Optional[Response]: suffix: str = None) -> Optional[Response]:
@ -194,7 +195,7 @@ class PlaylistEngine:
if suffix: if suffix:
string += f' - {str(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: if resp:
return resp return resp
else: else:

View File

@ -1,7 +1,8 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import List from typing import List, Union
from spotframework.util.console import Color from spotframework.util.console import Color
from spotframework.model.uri import Uri
if TYPE_CHECKING: if TYPE_CHECKING:
from spotframework.model.artist import Artist from spotframework.model.artist import Artist
@ -35,8 +36,7 @@ class SpotifyAlbum(Album):
artists: List[Artist], artists: List[Artist],
href: str = None, href: str = None,
spotify_id: str = None, uri: Union[str, Uri] = None,
uri: str = None,
genres: List[str] = None, genres: List[str] = None,
tracks: List = None, tracks: List = None,
@ -50,8 +50,10 @@ class SpotifyAlbum(Album):
super().__init__(name, artists) super().__init__(name, artists)
self.href = href self.href = href
self.spotify_id = spotify_id if isinstance(uri, str):
self.uri = uri self.uri = Uri(uri)
else:
self.uri = uri
self.genres = genres self.genres = genres
self.tracks = tracks self.tracks = tracks

View File

@ -1,5 +1,6 @@
from typing import List from typing import List, Union
from spotframework.util.console import Color from spotframework.util.console import Color
from spotframework.model.uri import Uri
class Artist: class Artist:
@ -19,8 +20,7 @@ class SpotifyArtist(Artist):
name: str, name: str,
href: str = None, href: str = None,
spotify_id: str = None, uri: Union[str, Uri] = None,
uri: str = None,
genres: List[str] = None, genres: List[str] = None,
@ -29,8 +29,10 @@ class SpotifyArtist(Artist):
super().__init__(name) super().__init__(name)
self.href = href self.href = href
self.spotify_id = spotify_id if isinstance(uri, str):
self.uri = uri self.uri = Uri(uri)
else:
self.uri = uri
self.genres = genres self.genres = genres

View File

@ -1,8 +1,9 @@
from spotframework.model.user import User from spotframework.model.user import User
from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack
from spotframework.model.uri import Uri
from spotframework.util.console import Color from spotframework.util.console import Color
from tabulate import tabulate from tabulate import tabulate
from typing import List from typing import List, Union
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -119,14 +120,13 @@ class Playlist:
class SpotifyPlaylist(Playlist): class SpotifyPlaylist(Playlist):
def __init__(self, def __init__(self,
playlistid: str, uri: Union[str, Uri],
name: str = None, name: str = None,
owner: User = None, owner: User = None,
description: str = None, description: str = None,
href: str = None, href: str = None,
uri: str = None,
collaborative: bool = None, collaborative: bool = None,
public: bool = None, public: bool = None,
@ -134,11 +134,13 @@ class SpotifyPlaylist(Playlist):
super().__init__(name=name, description=description) super().__init__(name=name, description=description)
self.playlist_id = playlistid
self.owner = owner self.owner = owner
self.href = href self.href = href
self.uri = uri if isinstance(uri, str):
self.uri = Uri(uri)
else:
self.uri = uri
self.collaborative = collaborative self.collaborative = collaborative
self.public = public self.public = public
@ -147,7 +149,6 @@ class SpotifyPlaylist(Playlist):
def __str__(self): def __str__(self):
prefix = f'\n==={self.name}===\n\n' if self.name is not None else '' 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 '' prefix += f'uri: {self.uri}\n' if self.uri is not None else ''
table = prefix + self.get_tracks_string() + '\n' + f'total: {len(self)}' table = prefix + self.get_tracks_string() + '\n' + f'total: {len(self)}'

View File

@ -1,15 +1,20 @@
from datetime import datetime from datetime import datetime
from spotframework.model.track import Track from spotframework.model.track import Track
from spotframework.model.uri import Uri
from enum import Enum from enum import Enum
from typing import Union
class Context: class Context:
def __init__(self, def __init__(self,
uri: str, uri: Union[str, Uri],
object_type: str = None, object_type: str = None,
href: str = None, href: str = None,
external_spot: 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.object_type = object_type
self.href = href self.href = href
self.external_spot = external_spot self.external_spot = external_spot

View File

@ -1,12 +1,14 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import List from typing import List, Union
from datetime import datetime from datetime import datetime
from spotframework.model.uri import Uri
from spotframework.util.console import Color from spotframework.util.console import Color
if TYPE_CHECKING: if TYPE_CHECKING:
from spotframework.model.album import Album from spotframework.model.album import Album
from spotframework.model.artist import Artist from spotframework.model.artist import Artist
from spotframework.model.user import User from spotframework.model.user import User
from spotframework.model.service import Context
class Track: class Track:
@ -57,8 +59,7 @@ class SpotifyTrack(Track):
artists: List[Artist], artists: List[Artist],
href: str = None, href: str = None,
spotify_id: str = None, uri: Union[str, Uri] = None,
uri: str = None,
disc_number: int = None, disc_number: int = None,
duration_ms: int = None, duration_ms: int = None,
@ -73,8 +74,10 @@ class SpotifyTrack(Track):
excplicit=explicit) excplicit=explicit)
self.href = href self.href = href
self.spotify_id = spotify_id if isinstance(uri, str):
self.uri = uri self.uri = Uri(uri)
else:
self.uri = uri
self.is_playable = is_playable self.is_playable = is_playable
self.popularity = popularity self.popularity = popularity
@ -90,13 +93,12 @@ class PlaylistTrack(SpotifyTrack):
album: Album, album: Album,
artists: List[Artist], artists: List[Artist],
added_at: str, added_at: datetime,
added_by: User, added_by: User,
is_local: bool, is_local: bool,
href: str = None, href: str = None,
spotify_id: str = None, uri: Union[str, Uri] = None,
uri: str = None,
disc_number: int = None, disc_number: int = None,
duration_ms: int = None, duration_ms: int = None,
@ -107,7 +109,6 @@ class PlaylistTrack(SpotifyTrack):
): ):
super().__init__(name=name, album=album, artists=artists, super().__init__(name=name, album=album, artists=artists,
href=href, href=href,
spotify_id=spotify_id,
uri=uri, uri=uri,
disc_number=disc_number, disc_number=disc_number,
@ -116,10 +117,46 @@ class PlaylistTrack(SpotifyTrack):
is_playable=is_playable, is_playable=is_playable,
popularity=popularity) popularity=popularity)
self.added_at = datetime.fromisoformat(added_at.replace('T', ' ').replace('Z', '')) self.added_at = added_at
self.added_by = added_by self.added_by = added_by
self.is_local = is_local self.is_local = is_local
def __repr__(self): def __repr__(self):
return Color.BOLD + Color.YELLOW + 'PlaylistTrack' + Color.END + \ return Color.BOLD + Color.YELLOW + 'PlaylistTrack' + Color.END + \
f': {self.name}, ({self.album}), {self.artists}, {self.uri}, {self.added_at}' 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}'

View File

@ -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

View File

@ -1,4 +1,6 @@
from spotframework.util.console import Color from spotframework.util.console import Color
from spotframework.model.uri import Uri
from typing import Union
class User: class User:
@ -6,14 +8,17 @@ class User:
username: str, username: str,
href: str = None, href: str = None,
uri: str = None, uri: Union[str, Uri] = None,
display_name: str = None, display_name: str = None,
ext_spotify: str = None): ext_spotify: str = None):
self.username = username self.username = username
self.href = href self.href = href
self.uri = uri if isinstance(uri, str):
self.uri = Uri(uri)
else:
self.uri = uri
self.display_name = display_name self.display_name = display_name
self.ext_spotify = ext_spotify self.ext_spotify = ext_spotify

View File

@ -3,12 +3,14 @@ import random
import logging import logging
import time import time
from typing import List, Optional from typing import List, Optional
from datetime import datetime
from . import const from . import const
from spotframework.net.parse import parse from spotframework.net.parse import parse
from spotframework.net.user import NetworkUser from spotframework.net.user import NetworkUser
from spotframework.model.playlist import SpotifyPlaylist 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.service import CurrentlyPlaying, Device
from spotframework.model.uri import Uri
from requests.models import Response from requests.models import Response
limit = 50 limit = 50
@ -121,20 +123,20 @@ class Network:
return None 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: if tracks is not None:
playlist = SpotifyPlaylist(playlistid) playlist = SpotifyPlaylist(uri.object_id)
playlist.tracks += tracks playlist.tracks += tracks
return playlist return playlist
else: else:
logger.error(f"{playlistid} - no tracks returned") logger.error(f"{uri} - no tracks returned")
return None return None
def create_playlist(self, def create_playlist(self,
@ -142,7 +144,7 @@ class Network:
name='New Playlist', name='New Playlist',
public=True, public=True,
collaborative=False, collaborative=False,
description=None) -> Optional[dict]: description=None) -> Optional[SpotifyPlaylist]:
json = {"name": name, "public": public, "collaborative": collaborative} 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) req = self._make_post_request('createPlaylist', f'users/{username}/playlists', json=json)
if 200 <= req.status_code < 300: if 200 <= req.status_code < 300:
return req.json() return parse.parse_playlist(req.json())
else: else:
logger.error('error creating playlist') logger.error('error creating playlist')
return None return None
@ -195,28 +197,28 @@ class Network:
logger.error('no playlists returned to filter') logger.error('no playlists returned to filter')
return None 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 = [] tracks = []
params = {'offset': offset, 'limit': limit} 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:
if resp.get('items', None): if resp.get('items', None):
tracks += [parse.parse_track(i) for i in resp.get('items', None)] tracks += [parse.parse_track(i) for i in resp.get('items', None)]
else: else:
logger.warning(f'{playlistid} no items returned') logger.warning(f'{uri} no items returned')
if resp.get('next', None): 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: if more_tracks:
tracks += more_tracks tracks += more_tracks
else: else:
logger.warning(f'{playlistid} error on response') logger.warning(f'{uri} error on response')
return tracks return tracks
@ -231,6 +233,32 @@ class Network:
logger.error('no devices returned') logger.error('no devices returned')
return None 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]: def get_player(self) -> Optional[CurrentlyPlaying]:
logger.info("retrieved") logger.info("retrieved")
@ -271,7 +299,7 @@ class Network:
else: else:
return None 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 ''}") logger.info(f"{uri}{' ' + deviceid if deviceid is not None else ''}")
@ -286,9 +314,9 @@ class Network:
payload = dict() payload = dict()
if uri: if uri:
payload['context_uri'] = uri payload['context_uri'] = str(uri)
if uris: 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) req = self._make_put_request('play', 'me/player/play', params=params, json=payload)
if req: if req:
@ -378,33 +406,34 @@ class Network:
logger.error(f"{volume} not accepted value") logger.error(f"{volume} not accepted value")
return None 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"} 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 req is not None:
if len(uris) > 100: if len(uris) > 100:
return self.add_playlist_tracks(playlistid, uris[100:]) return self.add_playlist_tracks(uri, uris[100:])
return req return req
else: else:
logger.error(f'error replacing playlist tracks, total: {len(uris)}') logger.error(f'error replacing playlist tracks, total: {len(uris)}')
def change_playlist_details(self, def change_playlist_details(self,
playlistid, uri: Uri,
name=None, name=None,
public=None, public=None,
collaborative=None, collaborative=None,
description=None) -> Optional[Response]: description=None) -> Optional[Response]:
logger.info(f"{playlistid}") logger.info(f"{uri}")
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@ -426,22 +455,24 @@ class Network:
logger.warning('update dictionairy length 0') logger.warning('update dictionairy length 0')
return None return None
else: 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: if req:
return req return req
else: else:
logger.error('error updating details') logger.error('error updating details')
return None 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"} 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: if req is not None:
resp = req.json() resp = req.json()
@ -450,12 +481,12 @@ class Network:
if len(uris) > 100: if len(uris) > 100:
snapshots += self.add_playlist_tracks(playlistid, uris[100:]) snapshots += self.add_playlist_tracks(uri, uris[100:])
return snapshots return snapshots
else: else:
logger.error(f'error retrieving tracks {playlistid}, total: {len(uris)}') logger.error(f'error retrieving tracks {uri}, total: {len(uris)}')
return [] return []
def get_recommendations(self, tracks=None, artists=None, response_limit=10) -> Optional[List[Track]]: def get_recommendations(self, tracks=None, artists=None, response_limit=10) -> Optional[List[Track]]:
@ -490,17 +521,17 @@ class Network:
playlist: SpotifyPlaylist, playlist: SpotifyPlaylist,
append_tracks: bool = False): append_tracks: bool = False):
if playlist.playlist_id: if playlist.uri:
if playlist.tracks == -1: if playlist.tracks == -1:
self.replace_playlist_tracks(playlist.playlist_id, []) self.replace_playlist_tracks(playlist.uri, [])
elif playlist.tracks: elif playlist.tracks:
if append_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: 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: 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.name,
playlist.public, playlist.public,
playlist.collaborative, playlist.collaborative,
@ -510,12 +541,12 @@ class Network:
logger.error('playlist has no id') logger.error('playlist has no id')
def reorder_playlist_tracks(self, def reorder_playlist_tracks(self,
playlistid: str, uri: Uri,
range_start: int, range_start: int,
range_length: int, range_length: int,
insert_before: int) -> Optional[Response]: insert_before: int) -> Optional[Response]:
logger.info(f'id: {playlistid}') logger.info(f'id: {uri}')
if range_start < 0: if range_start < 0:
logger.error('range_start must be positive') logger.error('range_start must be positive')
@ -531,7 +562,7 @@ class Network:
'range_length': range_length, 'range_length': range_length,
'insert_before': insert_before} '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: if resp:
return resp return resp

View File

@ -1,10 +1,11 @@
from spotframework.model.artist import SpotifyArtist from spotframework.model.artist import SpotifyArtist
from spotframework.model.album import SpotifyAlbum 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.playlist import SpotifyPlaylist
from spotframework.model.user import User from spotframework.model.user import User
from spotframework.model.service import Context, CurrentlyPlaying, Device from spotframework.model.service import Context, CurrentlyPlaying, Device
import datetime import datetime
from typing import Union
def parse_artist(artist_dict) -> SpotifyArtist: def parse_artist(artist_dict) -> SpotifyArtist:
@ -12,7 +13,6 @@ def parse_artist(artist_dict) -> SpotifyArtist:
name = artist_dict.get('name', None) name = artist_dict.get('name', None)
href = artist_dict.get('href', None) href = artist_dict.get('href', None)
spotify_id = artist_dict.get('id', None)
uri = artist_dict.get('uri', None) uri = artist_dict.get('uri', None)
genres = artist_dict.get('genres', None) genres = artist_dict.get('genres', None)
@ -23,7 +23,6 @@ def parse_artist(artist_dict) -> SpotifyArtist:
return SpotifyArtist(name, return SpotifyArtist(name,
href=href, href=href,
spotify_id=spotify_id,
uri=uri, uri=uri,
genres=genres, genres=genres,
@ -39,7 +38,6 @@ def parse_album(album_dict) -> SpotifyAlbum:
artists = [parse_artist(i) for i in album_dict.get('artists', [])] artists = [parse_artist(i) for i in album_dict.get('artists', [])]
href = album_dict.get('href', None) href = album_dict.get('href', None)
spotify_id = album_dict.get('id', None)
uri = album_dict.get('uri', None) uri = album_dict.get('uri', None)
genres = album_dict.get('genres', None) genres = album_dict.get('genres', None)
@ -55,7 +53,6 @@ def parse_album(album_dict) -> SpotifyAlbum:
artists=artists, artists=artists,
href=href, href=href,
spotify_id=spotify_id,
uri=uri, uri=uri,
genres=genres, genres=genres,
@ -68,7 +65,7 @@ def parse_album(album_dict) -> SpotifyAlbum:
popularity=popularity) popularity=popularity)
def parse_track(track_dict) -> Track: def parse_track(track_dict) -> Union[Track, SpotifyTrack, PlayedTrack]:
if 'track' in track_dict: if 'track' in track_dict:
track = track_dict.get('track', None) track = track_dict.get('track', None)
@ -84,12 +81,9 @@ def parse_track(track_dict) -> Track:
else: else:
album = None album = None
# print(album.name)
artists = [parse_artist(i) for i in track.get('artists', [])] artists = [parse_artist(i) for i in track.get('artists', [])]
href = track.get('href', None) href = track.get('href', None)
spotify_id = track.get('id', None)
uri = track.get('uri', None) uri = track.get('uri', None)
disc_number = track.get('disc_number', 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_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) 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) 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: if added_at or added_by or is_local:
return PlaylistTrack(name=name, return PlaylistTrack(name=name,
@ -115,7 +116,6 @@ def parse_track(track_dict) -> Track:
is_local=is_local, is_local=is_local,
href=href, href=href,
spotify_id=spotify_id,
uri=uri, uri=uri,
disc_number=disc_number, disc_number=disc_number,
@ -124,13 +124,28 @@ def parse_track(track_dict) -> Track:
is_playable=is_playable, is_playable=is_playable,
popularity=popularity) 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: else:
return SpotifyTrack(name=name, return SpotifyTrack(name=name,
album=album, album=album,
artists=artists, artists=artists,
href=href, href=href,
spotify_id=spotify_id,
uri=uri, uri=uri,
disc_number=disc_number, disc_number=disc_number,
@ -164,7 +179,6 @@ def parse_playlist(playlist_dict) -> SpotifyPlaylist:
ext_spotify = playlist_dict['external_urls']['spotify'] ext_spotify = playlist_dict['external_urls']['spotify']
href = playlist_dict.get('href', None) href = playlist_dict.get('href', None)
playlist_id = playlist_dict.get('id', None)
description = playlist_dict.get('description', None) description = playlist_dict.get('description', None)
name = playlist_dict.get('name', None) name = playlist_dict.get('name', None)
@ -177,12 +191,11 @@ def parse_playlist(playlist_dict) -> SpotifyPlaylist:
public = playlist_dict.get('public', None) public = playlist_dict.get('public', None)
uri = playlist_dict.get('uri', None) uri = playlist_dict.get('uri', None)
return SpotifyPlaylist(playlistid=playlist_id, return SpotifyPlaylist(uri=uri,
name=name, name=name,
owner=owner, owner=owner,
description=description, description=description,
href=href, href=href,
uri=uri,
collaborative=collaborative, collaborative=collaborative,
public=public, public=public,
ext_spotify=ext_spotify) ext_spotify=ext_spotify)

View File

@ -1,7 +1,9 @@
from spotframework.net.network import Network from spotframework.net.network import Network
from spotframework.model.track import SpotifyTrack 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 spotframework.model.service import Context, Device
from typing import List from typing import List, Union
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -32,7 +34,7 @@ class Player:
return self.last_status return self.last_status
def play(self, def play(self,
context: Context = None, context: Union[Context, SpotifyAlbum, SpotifyPlaylist] = None,
tracks: List[SpotifyTrack] = None, tracks: List[SpotifyTrack] = None,
device: Device = None): device: Device = None):
if context and tracks: if context and tracks: