added model magic functions

This commit is contained in:
aj 2019-09-13 16:31:40 +01:00
parent 3879562e82
commit 92217ad3a4
9 changed files with 121 additions and 5 deletions

View File

@ -11,7 +11,7 @@ logger = logging.getLogger('spotframework')
log_format = '%(asctime)s %(levelname)s %(name)s - %(funcName)s - %(message)s' log_format = '%(asctime)s %(levelname)s %(name)s - %(funcName)s - %(message)s'
file_handler = logging.FileHandler(".spot/resort_playlist.log") file_handler = logging.FileHandler(".spot/sort_playlist.log")
formatter = logging.Formatter(log_format) formatter = logging.Formatter(log_format)
file_handler.setFormatter(formatter) file_handler.setFormatter(formatter)

View File

@ -1,6 +1,7 @@
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
from spotframework.util.console import Color
if TYPE_CHECKING: if TYPE_CHECKING:
from spotframework.model.artist import Artist from spotframework.model.artist import Artist
@ -23,6 +24,10 @@ class Album:
return f'{self.name} / {artists}' return f'{self.name} / {artists}'
def __repr__(self):
return Color.DARKCYAN + Color.BOLD + 'Album' + Color.END + \
f': {self.name}, [{self.artists}]'
class SpotifyAlbum(Album): class SpotifyAlbum(Album):
def __init__(self, def __init__(self,
@ -56,3 +61,7 @@ class SpotifyAlbum(Album):
self.label = label self.label = label
self.popularity = popularity self.popularity = popularity
def __repr__(self):
return Color.DARKCYAN + Color.BOLD + 'SpotifyAlbum' + Color.END + \
f': {self.name}, {self.artists}, {self.uri}, {self.tracks}'

View File

@ -1,4 +1,5 @@
from typing import List from typing import List
from spotframework.util.console import Color
class Artist: class Artist:
@ -8,6 +9,10 @@ class Artist:
def __str__(self): def __str__(self):
return f'{self.name}' return f'{self.name}'
def __repr__(self):
return Color.PURPLE + Color.BOLD + 'Artist' + Color.END + \
f': {self.name}'
class SpotifyArtist(Artist): class SpotifyArtist(Artist):
def __init__(self, def __init__(self,
@ -30,3 +35,7 @@ class SpotifyArtist(Artist):
self.genres = genres self.genres = genres
self.popularity = popularity self.popularity = popularity
def __repr__(self):
return Color.PURPLE + Color.BOLD + 'SpotifyArtist' + Color.END + \
f': {self.name}, {self.uri}'

View File

@ -1,5 +1,6 @@
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.util.console import Color
from tabulate import tabulate from tabulate import tabulate
from typing import List from typing import List
import logging import logging
@ -50,9 +51,46 @@ class Playlist:
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 ''
table = prefix + self.get_tracks_string() + '\n' + f'total: {len(self)}' table = prefix + self.get_tracks_string() + '\n' + f'total: {len(self)}'
return table return table
def __repr__(self):
return Color.GREEN + Color.BOLD + 'Playlist' + Color.END + \
f': {self.name}, ({len(self)})'
def __add__(self, other):
if isinstance(other, Track):
self.tracks.append(other)
return self
elif isinstance(other, list):
if all((isinstance(i, Track) for i in other)):
self.tracks += other
return self
else:
logger.error('list not full of tracks')
raise TypeError('list not full of tracks')
else:
logger.error('list of tracks needed to add')
raise TypeError('list of tracks needed to add')
def __sub__(self, other):
if isinstance(other, Track):
self.tracks.remove(other)
return self
elif isinstance(other, list):
if all((isinstance(i, Track) for i in other)):
self.tracks -= other
return self
else:
logger.error('list not full of tracks')
raise TypeError('list not full of tracks')
else:
logger.error('list of tracks needed to subtract')
raise TypeError('list of tracks needed to subtract')
def get_tracks_string(self): def get_tracks_string(self):
rows = [] rows = []
@ -109,3 +147,7 @@ class SpotifyPlaylist(Playlist):
table = prefix + self.get_tracks_string() + '\n' + f'total: {len(self)}' table = prefix + self.get_tracks_string() + '\n' + f'total: {len(self)}'
return table return table
def __repr__(self):
return Color.GREEN + Color.BOLD + 'SpotifyPlaylist' + Color.END + \
f': {self.name} ({self.owner}), ({len(self)}), {self.uri}'

View File

@ -2,6 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import List from typing import List
from datetime import datetime from datetime import datetime
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
@ -44,6 +45,10 @@ class Track:
return f'{self.name} / {album} / {artists}' return f'{self.name} / {album} / {artists}'
def __repr__(self):
return Color.YELLOW + Color.BOLD + 'Track' + Color.END + \
f': {self.name}, ({self.album}), {self.artists}'
class SpotifyTrack(Track): class SpotifyTrack(Track):
def __init__(self, def __init__(self,
@ -74,6 +79,10 @@ class SpotifyTrack(Track):
self.popularity = popularity self.popularity = popularity
def __repr__(self):
return Color.BOLD + Color.YELLOW + 'SpotifyTrack' + Color.END + \
f': {self.name}, ({self.album}), {self.artists}, {self.uri}'
class PlaylistTrack(SpotifyTrack): class PlaylistTrack(SpotifyTrack):
def __init__(self, def __init__(self,
@ -110,3 +119,7 @@ class PlaylistTrack(SpotifyTrack):
self.added_at = datetime.fromisoformat(added_at.replace('T', ' ').replace('Z', '')) self.added_at = datetime.fromisoformat(added_at.replace('T', ' ').replace('Z', ''))
self.added_by = added_by self.added_by = added_by
self.is_local = is_local 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}'

View File

@ -1,3 +1,4 @@
from spotframework.util.console import Color
class User: class User:
@ -19,3 +20,7 @@ class User:
def __str__(self): def __str__(self):
return f'{self.username}' return f'{self.username}'
def __repr__(self):
return Color.RED + Color.BOLD + 'User' + Color.END + \
f': {self.username}, {self.display_name}, {self.uri}'

View File

@ -5,6 +5,7 @@ import time
from typing import List, Optional from typing import List, Optional
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.model.playlist import SpotifyPlaylist from spotframework.model.playlist import SpotifyPlaylist
from spotframework.model.track import Track, PlaylistTrack from spotframework.model.track import Track, PlaylistTrack
from requests.models import Response from requests.models import Response
@ -16,7 +17,7 @@ logger = logging.getLogger(__name__)
class Network: class Network:
def __init__(self, user): def __init__(self, user: NetworkUser):
self.user = user self.user = user
def _make_get_request(self, method, url, params=None, headers={}) -> Optional[dict]: def _make_get_request(self, method, url, params=None, headers={}) -> Optional[dict]:
@ -40,6 +41,11 @@ class Network:
else: else:
logger.error(f'{method} rate limit reached: cannot find Retry-After header') logger.error(f'{method} rate limit reached: cannot find Retry-After header')
elif req.status_code == 401:
logger.warning(f'{method} access token expired, refreshing')
self.user.refresh_token()
return self._make_get_request(method, url, params, headers)
else: else:
error_text = req.json()['error']['message'] error_text = req.json()['error']['message']
logger.error(f'{method} get {req.status_code} {error_text}') logger.error(f'{method} get {req.status_code} {error_text}')
@ -67,6 +73,11 @@ class Network:
else: else:
logger.error(f'{method} rate limit reached: cannot find Retry-After header') logger.error(f'{method} rate limit reached: cannot find Retry-After header')
elif req.status_code == 401:
logger.warning(f'{method} access token expired, refreshing')
self.user.refresh_token()
return self._make_post_request(method, url, params, json, headers)
else: else:
error_text = str(req.text) error_text = str(req.text)
logger.error(f'{method} post {req.status_code} {error_text}') logger.error(f'{method} post {req.status_code} {error_text}')
@ -94,6 +105,11 @@ class Network:
else: else:
logger.error(f'{method} rate limit reached: cannot find Retry-After header') logger.error(f'{method} rate limit reached: cannot find Retry-After header')
elif req.status_code == 401:
logger.warning(f'{method} access token expired, refreshing')
self.user.refresh_token()
return self._make_put_request(method, url, params, json, headers)
else: else:
error_text = str(req.text) error_text = str(req.text)
logger.error(f'{method} put {req.status_code} {error_text}') logger.error(f'{method} put {req.status_code} {error_text}')

View File

@ -1,5 +1,6 @@
import requests import requests
from spotframework.model.user import User from spotframework.model.user import User
from spotframework.util.console import Color
from base64 import b64encode from base64 import b64encode
import logging import logging
import time import time
@ -22,6 +23,10 @@ class NetworkUser(User):
self.refresh_token() self.refresh_token()
self.refresh_info() self.refresh_info()
def __repr__(self):
return Color.RED + Color.BOLD + 'NetworkUser' + Color.END + \
f': {self.username}, {self.display_name}, {self.uri}'
def refresh_token(self) -> None: def refresh_token(self) -> None:
if self.refreshtoken is None: if self.refreshtoken is None:
@ -53,7 +58,7 @@ class NetworkUser(User):
time.sleep(int(retry_after) + 1) time.sleep(int(retry_after) + 1)
return self.refresh_token() return self.refresh_token()
else: else:
logger.error(f'refresh_token rate limit reached: cannot find Retry-After header') logger.error('refresh_token rate limit reached: cannot find Retry-After header')
else: else:
error_text = req.json()['error']['message'] error_text = req.json()['error']['message']
@ -97,7 +102,12 @@ class NetworkUser(User):
time.sleep(int(retry_after) + 1) time.sleep(int(retry_after) + 1)
return self.get_info() return self.get_info()
else: else:
logger.error(f'get_info rate limit reached: cannot find Retry-After header') logger.error('get_info rate limit reached: cannot find Retry-After header')
elif req.status_code == 401:
logger.warning('access token expired, refreshing')
self.refresh_token()
return self.get_info()
else: else:
error_text = req.json()['error']['message'] error_text = req.json()['error']['message']

View File

@ -0,0 +1,12 @@
class Color:
PURPLE = '\033[95m'
CYAN = '\033[96m'
DARKCYAN = '\033[36m'
BLUE = '\033[94m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
END = '\033[0m'