added playlist reordering and sorting by added date
This commit is contained in:
parent
5140892288
commit
ac410dfeb9
58
sort_playlist.py
Normal file
58
sort_playlist.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from spotframework.net.network import Network
|
||||||
|
from spotframework.net.user import NetworkUser
|
||||||
|
from spotframework.engine.playlistengine import PlaylistEngine
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('spotframework')
|
||||||
|
|
||||||
|
log_format = '%(asctime)s %(levelname)s %(name)s - %(funcName)s - %(message)s'
|
||||||
|
|
||||||
|
file_handler = logging.FileHandler(".spot/resort_playlist.log")
|
||||||
|
formatter = logging.Formatter(log_format)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
stream_log_format = '%(levelname)s %(name)s:%(funcName)s - %(message)s'
|
||||||
|
stream_formatter = logging.Formatter(stream_log_format)
|
||||||
|
|
||||||
|
stream_handler = logging.StreamHandler()
|
||||||
|
stream_handler.setFormatter(stream_formatter)
|
||||||
|
|
||||||
|
logger.addHandler(stream_handler)
|
||||||
|
|
||||||
|
|
||||||
|
def go(playlist_name):
|
||||||
|
|
||||||
|
net = Network(NetworkUser(os.environ['SPOTCLIENT'],
|
||||||
|
os.environ['SPOTSECRET'],
|
||||||
|
os.environ['SPOTACCESS'],
|
||||||
|
os.environ['SPOTREFRESH']))
|
||||||
|
|
||||||
|
engine = PlaylistEngine(net)
|
||||||
|
engine.load_user_playlists()
|
||||||
|
playlist = next((j for j in engine.playlists if j.name == playlist_name), None)
|
||||||
|
|
||||||
|
if playlist is not None:
|
||||||
|
engine.get_playlist_tracks(playlist)
|
||||||
|
engine.reorder_playlist_by_added_date(playlist_name)
|
||||||
|
else:
|
||||||
|
logger.error('playlist not found')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
name = sys.argv[1]
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
for i in sys.argv[2:]:
|
||||||
|
name += ' ' + i
|
||||||
|
go(sys.argv[1])
|
||||||
|
else:
|
||||||
|
name = input('enter playlist name: ')
|
||||||
|
if name == '':
|
||||||
|
exit()
|
||||||
|
go(name)
|
@ -50,6 +50,13 @@ class PlaylistEngine:
|
|||||||
else:
|
else:
|
||||||
logger.error('error getting tracks')
|
logger.error('error getting tracks')
|
||||||
|
|
||||||
|
def load_playlist_tracks(self, name: str):
|
||||||
|
playlist = next((i for i in self.playlists if i.name == name), None)
|
||||||
|
if playlist is not None:
|
||||||
|
self.get_playlist_tracks(playlist)
|
||||||
|
else:
|
||||||
|
logger.error(f'playlist {name} not found')
|
||||||
|
|
||||||
def make_playlist(self,
|
def make_playlist(self,
|
||||||
playlist_parts: List[str],
|
playlist_parts: List[str],
|
||||||
processors: List[AbstractProcessor] = None,
|
processors: List[AbstractProcessor] = None,
|
||||||
@ -128,6 +135,39 @@ class PlaylistEngine:
|
|||||||
include_recommendations=include_recommendations,
|
include_recommendations=include_recommendations,
|
||||||
recommendation_limit=recommendation_limit)
|
recommendation_limit=recommendation_limit)
|
||||||
|
|
||||||
|
def reorder_playlist_by_added_date(self,
|
||||||
|
name: str = None,
|
||||||
|
playlistid: str = None,
|
||||||
|
reverse: bool = False):
|
||||||
|
if name is None and playlistid 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)
|
||||||
|
|
||||||
|
if playlist is None:
|
||||||
|
logger.error('playlist not found')
|
||||||
|
return None
|
||||||
|
|
||||||
|
tracks_to_sort = list(playlist.tracks)
|
||||||
|
for i in range(len(playlist)):
|
||||||
|
counter_track = tracks_to_sort[0]
|
||||||
|
for track in tracks_to_sort:
|
||||||
|
if reverse is False:
|
||||||
|
if counter_track.added_at > track.added_at:
|
||||||
|
counter_track = track
|
||||||
|
else:
|
||||||
|
if counter_track.added_at < track.added_at:
|
||||||
|
counter_track = track
|
||||||
|
|
||||||
|
self.net.reorder_playlist_tracks(playlist.playlist_id,
|
||||||
|
i + tracks_to_sort.index(counter_track),
|
||||||
|
1, i)
|
||||||
|
tracks_to_sort.remove(counter_track)
|
||||||
|
|
||||||
def execute_playlist(self,
|
def execute_playlist(self,
|
||||||
tracks: List[SpotifyTrack],
|
tracks: List[SpotifyTrack],
|
||||||
playlist_id: str) -> Optional[Response]:
|
playlist_id: str) -> Optional[Response]:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from abc import ABC
|
from abc import ABC
|
||||||
from .abstract import AbstractProcessor
|
from .abstract import AbstractProcessor, BatchSingleTypeAwareProcessor
|
||||||
from typing import List
|
from typing import List
|
||||||
from spotframework.model.track import Track
|
from spotframework.model.track import Track, PlaylistTrack
|
||||||
|
|
||||||
|
|
||||||
class BasicReversibleSort(AbstractProcessor, ABC):
|
class BasicReversibleSort(AbstractProcessor, ABC):
|
||||||
@ -24,3 +24,19 @@ class SortArtistName(BasicReversibleSort):
|
|||||||
def process(self, tracks: List[Track]) -> List[Track]:
|
def process(self, tracks: List[Track]) -> List[Track]:
|
||||||
tracks.sort(key=lambda x: x.artists[0].name, reverse=self.reverse)
|
tracks.sort(key=lambda x: x.artists[0].name, reverse=self.reverse)
|
||||||
return tracks
|
return tracks
|
||||||
|
|
||||||
|
|
||||||
|
class SortAddedDate(BatchSingleTypeAwareProcessor):
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
names: List[str] = None,
|
||||||
|
reverse: bool = False,
|
||||||
|
append_malformed: bool = True):
|
||||||
|
super().__init__(names=names,
|
||||||
|
instance_check=PlaylistTrack,
|
||||||
|
append_malformed=append_malformed)
|
||||||
|
self.reverse = reverse
|
||||||
|
|
||||||
|
def process_batch(self, tracks: List[PlaylistTrack]) -> List[PlaylistTrack]:
|
||||||
|
tracks.sort(key=lambda x: x.added_at, reverse=self.reverse)
|
||||||
|
return tracks
|
||||||
|
@ -16,10 +16,10 @@ class Album:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _join_strings(string_list: List[str]):
|
def _join_strings(string_list: List[str]):
|
||||||
return ' , '.join(string_list)
|
return ', '.join(string_list)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
artists = ' , '.join([i.name for i in self.artists]) if self.artists else 'n/a'
|
artists = ', '.join([i.name for i in self.artists]) if self.artists else 'n/a'
|
||||||
|
|
||||||
return f'{self.name} / {artists}'
|
return f'{self.name} / {artists}'
|
||||||
|
|
||||||
|
@ -46,9 +46,17 @@ class Playlist:
|
|||||||
self._tracks = tracks
|
self._tracks = tracks
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
headers = ['name', 'album', 'artist', 'added at', 'popularity', 'uri']
|
|
||||||
|
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)}'
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
def get_tracks_string(self):
|
||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
|
headers = ['name', 'album', 'artist', 'added at', 'popularity', 'uri']
|
||||||
for track in self.tracks:
|
for track in self.tracks:
|
||||||
track_row = [track.name,
|
track_row = [track.name,
|
||||||
track.album.name,
|
track.album.name,
|
||||||
@ -61,10 +69,6 @@ class Playlist:
|
|||||||
|
|
||||||
table = tabulate(rows, headers=headers, showindex='always', tablefmt="fancy_grid")
|
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
|
return table
|
||||||
|
|
||||||
|
|
||||||
@ -95,3 +99,13 @@ class SpotifyPlaylist(Playlist):
|
|||||||
self.collaborative = collaborative
|
self.collaborative = collaborative
|
||||||
self.public = public
|
self.public = public
|
||||||
self.ext_spotify = ext_spotify
|
self.ext_spotify = ext_spotify
|
||||||
|
|
||||||
|
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)}'
|
||||||
|
|
||||||
|
return table
|
||||||
|
@ -36,11 +36,11 @@ class Track:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _join_strings(string_list: List[str]):
|
def _join_strings(string_list: List[str]):
|
||||||
return ' , '.join(string_list)
|
return ', '.join(string_list)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
album = self.album.name if self.album else 'n/a'
|
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'
|
artists = ', '.join([i.name for i in self.artists]) if self.artists else 'n/a'
|
||||||
|
|
||||||
return f'{self.name} / {album} / {artists}'
|
return f'{self.name} / {album} / {artists}'
|
||||||
|
|
||||||
|
@ -456,3 +456,32 @@ class Network:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error('playlist has no id')
|
logger.error('playlist has no id')
|
||||||
|
|
||||||
|
def reorder_playlist_tracks(self,
|
||||||
|
playlistid: str,
|
||||||
|
range_start: int,
|
||||||
|
range_length: int,
|
||||||
|
insert_before: int) -> Optional[Response]:
|
||||||
|
|
||||||
|
logger.info(f'id: {playlistid}')
|
||||||
|
|
||||||
|
if range_start < 0:
|
||||||
|
logger.error('range_start must be positive')
|
||||||
|
raise ValueError('range_start must be positive')
|
||||||
|
if range_length < 0:
|
||||||
|
logger.error('range_length must be positive')
|
||||||
|
raise ValueError('range_length must be positive')
|
||||||
|
if insert_before < 0:
|
||||||
|
logger.error('insert_before must be positive')
|
||||||
|
raise ValueError('insert_before must be positive')
|
||||||
|
|
||||||
|
json = {'range_start': range_start,
|
||||||
|
'range_length': range_length,
|
||||||
|
'insert_before': insert_before}
|
||||||
|
|
||||||
|
resp = self._make_put_request('reorderPlaylistTracks', f'playlists/{playlistid}/tracks', json=json)
|
||||||
|
|
||||||
|
if resp:
|
||||||
|
return resp
|
||||||
|
else:
|
||||||
|
logger.error('error reordering playlist')
|
||||||
|
Loading…
Reference in New Issue
Block a user