added playlist printing, model props, added popularity sorting

This commit is contained in:
aj 2019-09-05 11:42:35 +01:00
parent 0bd9fac8f4
commit ad9c40ea27
9 changed files with 196 additions and 47 deletions

View File

@ -0,0 +1,38 @@
from spotframework.engine.filter.abstract import AbstractProcessor
from typing import List
from spotframework.model.track import Track, SpotifyTrack
class SortPopularity(AbstractProcessor):
def __init__(self,
names: List[str] = None,
keep_malformed_type: bool = True):
super().__init__(names)
self.keep_malformed_type = keep_malformed_type
def sort(self, tracks: List[SpotifyTrack]):
tracks.sort(key=lambda x: x.popularity, reverse=True)
def process(self, tracks: List[Track]):
return_tracks = []
malformed_tracks = []
for track in tracks:
if isinstance(track, SpotifyTrack):
return_tracks.append(track)
else:
malformed_tracks.append(track)
self.sort(return_tracks)
if self.keep_malformed_type:
return_tracks += malformed_tracks
return return_tracks
class SortReversePopularity(SortPopularity):
def sort(self, tracks: List[SpotifyTrack]):
tracks.sort(key=lambda x: x.popularity, reverse=False)

View File

@ -1,31 +1,26 @@
from abc import ABC
from .abstract import AbstractProcessor
from typing import List
from spotframework.model.track import Track
class SortReverseReleaseDate(AbstractProcessor):
class BasicReversibleSort(AbstractProcessor, ABC):
def __init__(self,
names: List[str] = None,
reverse: bool = False):
super().__init__(names)
self.reverse = reverse
class SortReleaseDate(BasicReversibleSort):
def process(self, tracks: List[Track]):
tracks.sort(key=lambda x: x.album.release_date, reverse=True)
tracks.sort(key=lambda x: x.album.release_date, reverse=self.reverse)
return tracks
class SortReleaseDate(AbstractProcessor):
class SortArtistName(BasicReversibleSort):
def process(self, tracks: List[Track]):
tracks.sort(key=lambda x: x.album.release_date, reverse=False)
return tracks
class SortArtistName(AbstractProcessor):
def process(self, tracks: List[Track]):
tracks.sort(key=lambda x: x.artists[0].name, reverse=False)
return tracks
class SortReverseArtistName(AbstractProcessor):
def process(self, tracks: List[Track]):
tracks.sort(key=lambda x: x.artists[0].name, reverse=True)
tracks.sort(key=lambda x: x.artists[0].name, reverse=self.reverse)
return tracks

View File

@ -7,7 +7,7 @@ from spotframework.engine.filter.added import AddedSince
from typing import List
from spotframework.model.track import SpotifyTrack
from spotframework.model.playlist import Playlist
from spotframework.model.playlist import SpotifyPlaylist
from spotframework.net.network import Network
from spotframework.engine.filter.abstract import AbstractProcessor
from datetime import datetime
@ -40,7 +40,7 @@ class PlaylistEngine:
logger.error('error getting playlists')
def get_playlist_tracks(self,
playlist: Playlist):
playlist: SpotifyPlaylist):
logger.info(f"pulling tracks for {playlist.name}")
tracks = self.net.get_playlist_tracks(playlist.playlist_id)

View File

@ -10,6 +10,14 @@ class Album:
self.name = name
self.artists = artists
@property
def artists_names(self):
return self._join_strings([i.name for i in self.artists])
@staticmethod
def _join_strings(string_list: List[str]):
return ' , '.join(string_list)
def __str__(self):
artists = ' , '.join([i.name for i in self.artists]) if self.artists else 'n/a'

View File

@ -1,8 +1,75 @@
from spotframework.model.user import User
from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack
from tabulate import tabulate
from typing import List
import logging
logger = logging.getLogger(__name__)
class Playlist:
def __init__(self,
name: str = None,
description: str = None):
self._tracks = []
self.name = name
self.description = description
def has_tracks(self):
if len(self.tracks) > 0:
return True
else:
return False
def __len__(self):
return len(self.tracks)
@property
def tracks(self):
return self._tracks
@tracks.setter
def tracks(self, value: List[Track]):
tracks = []
not_tracks = []
for track in value:
if isinstance(track, Track):
tracks.append(track)
else:
not_tracks.append(track)
if len(not_tracks) > 0:
logger.error('playlist tracks must be off type Track')
self._tracks = tracks
def __str__(self):
headers = ['name', 'album', 'artist', 'added at', 'popularity', 'uri']
rows = []
for track in self.tracks:
track_row = [track.name,
track.album.name,
track.artists_names,
track.added_at if isinstance(track, PlaylistTrack) else '',
track.popularity if isinstance(track, SpotifyTrack) else '',
track.uri if isinstance(track, SpotifyTrack) else '']
rows.append(track_row)
table = tabulate(rows, headers=headers, showindex='always', tablefmt="fancy_grid")
prefix = f'\n==={self.name}===\n\n' if self.name is not None else ''
table = prefix + table + '\n' + f'total: {len(self)}'
return table
class SpotifyPlaylist(Playlist):
def __init__(self,
playlistid: str,
@ -16,12 +83,11 @@ class Playlist:
collaborative: bool = None,
public: bool = None,
ext_spotify: str = None):
self.tracks = []
self.name = name
super().__init__(name=name, description=description)
self.playlist_id = playlistid
self.owner = owner
self.description = description
self.href = href
self.uri = uri
@ -29,9 +95,3 @@ class Playlist:
self.collaborative = collaborative
self.public = public
self.ext_spotify = ext_spotify
def has_tracks(self):
if len(self.tracks) > 0:
return True
else:
return False

View File

@ -26,6 +26,18 @@ class Track:
self.duration_ms = duration_ms
self.explicit = excplicit
@property
def artists_names(self):
return self._join_strings([i.name for i in self.artists])
@property
def album_artists_names(self):
return self.album.artists_names
@staticmethod
def _join_strings(string_list: List[str]):
return ' , '.join(string_list)
def __str__(self):
album = self.album.name if self.album else 'n/a'
artists = ' , '.join([i.name for i in self.artists]) if self.artists else 'n/a'

View File

@ -5,7 +5,7 @@ import time
from typing import List
from . import const
from spotframework.net.parse import parse
from spotframework.model.playlist import Playlist
from spotframework.model.playlist import SpotifyPlaylist
limit = 50
@ -33,7 +33,7 @@ class Network:
if retry_after:
logger.warning(f'{method} rate limit reached: retrying in {retry_after} seconds')
time.sleep(int(retry_after))
time.sleep(int(retry_after) + 1)
return self._make_get_request(method, url, params, headers)
else:
logger.error(f'{method} rate limit reached: cannot find Retry-After header')
@ -60,7 +60,7 @@ class Network:
if retry_after:
logger.warning(f'{method} rate limit reached: retrying in {retry_after} seconds')
time.sleep(int(retry_after))
time.sleep(int(retry_after) + 1)
return self._make_post_request(method, url, params, json, headers)
else:
logger.error(f'{method} rate limit reached: cannot find Retry-After header')
@ -87,7 +87,7 @@ class Network:
if retry_after:
logger.warning(f'{method} rate limit reached: retrying in {retry_after} seconds')
time.sleep(int(retry_after))
time.sleep(int(retry_after) + 1)
return self._make_put_request(method, url, params, json, headers)
else:
logger.error(f'{method} rate limit reached: cannot find Retry-After header')
@ -106,7 +106,7 @@ class Network:
if tracks is not None:
playlist = Playlist(playlistid)
playlist = SpotifyPlaylist(playlistid)
playlist.tracks += tracks
return playlist
@ -415,7 +415,7 @@ class Network:
return None
def write_playlist_object(self,
playlist: Playlist,
playlist: SpotifyPlaylist,
append_tracks: bool = False):
if playlist.playlist_id:

View File

@ -1,7 +1,7 @@
from spotframework.model.artist import Artist, SpotifyArtist
from spotframework.model.album import Album, SpotifyAlbum
from spotframework.model.track import Track, SpotifyTrack, PlaylistTrack
from spotframework.model.playlist import Playlist
from spotframework.model.playlist import SpotifyPlaylist
from spotframework.model.user import User
@ -175,12 +175,12 @@ def parse_playlist(playlist_dict):
public = playlist_dict.get('public', None)
uri = playlist_dict.get('uri', None)
return Playlist(playlistid=playlist_id,
name=name,
owner=owner,
description=description,
href=href,
uri=uri,
collaborative=collaborative,
public=public,
ext_spotify=ext_spotify)
return SpotifyPlaylist(playlistid=playlist_id,
name=name,
owner=owner,
description=description,
href=href,
uri=uri,
collaborative=collaborative,
public=public,
ext_spotify=ext_spotify)

View File

@ -2,6 +2,7 @@ import requests
from spotframework.model.user import User
from base64 import b64encode
import logging
import time
logger = logging.getLogger(__name__)
@ -21,6 +22,15 @@ class NetworkUser(User):
self.refresh_info()
def refresh_token(self):
if self.refreshtoken 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}
@ -33,7 +43,20 @@ class NetworkUser(User):
logger.debug('token refreshed')
self.accesstoken = req.json()['access_token']
else:
logger.error(f'http error {req.status_code}')
if req.status_code == 429:
retry_after = req.headers.get('Retry-After', None)
if retry_after:
logger.warning(f'refresh_token rate limit reached: retrying in {retry_after} seconds')
time.sleep(int(retry_after) + 1)
return self.refresh_token()
else:
logger.error(f'refresh_token rate limit reached: cannot find Retry-After header')
else:
error_text = req.json()['error']['message']
logger.error(f'refresh_token get {req.status_code} {error_text}')
def refresh_info(self):
info = self.get_info()
@ -64,4 +87,17 @@ class NetworkUser(User):
logger.debug(f'retrieved {req.status_code}')
return req.json()
else:
logger.error(f'http error {req.status_code}')
if req.status_code == 429:
retry_after = req.headers.get('Retry-After', None)
if retry_after:
logger.warning(f'get_info rate limit reached: retrying in {retry_after} seconds')
time.sleep(int(retry_after) + 1)
return self.get_info()
else:
logger.error(f'get_info rate limit reached: cannot find Retry-After header')
else:
error_text = req.json()['error']['message']
logger.error(f'get_info get {req.status_code} {error_text}')