added model magic functions
This commit is contained in:
parent
3879562e82
commit
92217ad3a4
@ -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)
|
||||||
|
|
||||||
|
@ -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}'
|
||||||
|
@ -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}'
|
||||||
|
@ -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}'
|
||||||
|
@ -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}'
|
||||||
|
@ -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}'
|
||||||
|
@ -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}')
|
||||||
|
@ -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']
|
||||||
|
12
spotframework/util/console.py
Normal file
12
spotframework/util/console.py
Normal 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'
|
Loading…
Reference in New Issue
Block a user