From 89f8c8961b4a7dd284cd06db0ee3332dc857673f Mon Sep 17 00:00:00 2001 From: aj Date: Mon, 10 Jun 2019 21:46:43 +0100 Subject: [PATCH] added playlist engine and recent playlist --- generateplaylists.py | 73 ++++++++++++------- spotframework/engine/__init__.py | 0 spotframework/engine/filter/__init__.py | 0 .../engine/filter/abstractprocessor.py | 11 +++ spotframework/engine/filter/addedbefore.py | 17 +++++ spotframework/engine/filter/addedsince.py | 17 +++++ .../engine/filter/deduplicatebyid.py | 13 ++++ .../engine/filter/deduplicatebyname.py | 19 +++++ spotframework/engine/filter/shuffle.py | 9 +++ .../engine/filter/sortreversereleasedate.py | 8 ++ spotframework/engine/playlistengine.py | 72 ++++++++++++++++++ spotframework/model/playlist.py | 8 +- spotframework/net/network.py | 2 +- spotframework/util/__init__.py | 0 spotframework/util/monthstrings.py | 14 ++++ 15 files changed, 234 insertions(+), 29 deletions(-) create mode 100644 spotframework/engine/__init__.py create mode 100644 spotframework/engine/filter/__init__.py create mode 100644 spotframework/engine/filter/abstractprocessor.py create mode 100644 spotframework/engine/filter/addedbefore.py create mode 100644 spotframework/engine/filter/addedsince.py create mode 100644 spotframework/engine/filter/deduplicatebyid.py create mode 100644 spotframework/engine/filter/deduplicatebyname.py create mode 100644 spotframework/engine/filter/shuffle.py create mode 100644 spotframework/engine/filter/sortreversereleasedate.py create mode 100644 spotframework/engine/playlistengine.py create mode 100644 spotframework/util/__init__.py create mode 100644 spotframework/util/monthstrings.py diff --git a/generateplaylists.py b/generateplaylists.py index 5225651..0802791 100644 --- a/generateplaylists.py +++ b/generateplaylists.py @@ -3,6 +3,14 @@ import spotframework.net.network as network import spotframework.net.user as user import spotframework.log.log as log import spotframework.io.json as json +import spotframework.util.monthstrings as monthstrings +from spotframework.engine.playlistengine import PlaylistEngine +from spotframework.engine.filter.shuffle import Shuffle +from spotframework.engine.filter.sortreversereleasedate import SortReverseReleaseDate +from spotframework.engine.filter.deduplicatebyid import DeduplicateByID +from spotframework.engine.filter.deduplicatebyname import DeduplicateByName + +import datetime import requests @@ -16,47 +24,58 @@ if __name__ == '__main__': data = json.loadJson(os.path.join(const.config_path, 'playlists.json')) net = network.network(user.User()) - playlists = net.getUserPlaylists() + + engine = PlaylistEngine(net) + engine.load_user_playlists() for tomake in data['playlists']: - log.log("generatePlaylist", tomake['name']) + log.log("makePlaylist", tomake['name']) - tracks = [] - - for part in tomake['playlists']: - - play = next((i for i in playlists if i.name == part), None) - - if play is not None: - - if len(play.tracks) == 0: - log.log("pulling tracks for {}".format(play.name)) - play.tracks = net.getPlaylistTracks(play.playlistid) - - tracks += [i for i in play.tracks if i['track']['uri'] not in [j['track']['uri'] for j in tracks] and i['is_local'] is False] - - else: - log.log("requested playlist {} not found".format(part)) - if 'SLACKHOOK' in os.environ: - requests.post(os.environ['SLACKHOOK'], json={"text": "spot playlists: {} not found".format(part)}) + processors = [DeduplicateByID()] if 'shuffle' in tomake: if tomake['shuffle'] is True: - import random - random.shuffle(tracks) + processors.append(Shuffle()) else: - tracks.sort(key=lambda x: x['track']['album']['release_date'], reverse=True) + processors.append(SortReverseReleaseDate()) else: - tracks.sort(key=lambda x: x['track']['album']['release_date'], reverse=True) + processors.append(SortReverseReleaseDate()) + + tracks = engine.make_playlist(tomake['playlists'], processors) + + engine.execute_playlist(tracks, tomake['id']) + engine.change_description(tomake['playlists'], tomake['id']) + + if 'recents' in data: + recents_id = data['recents']['id'] + boundary_date = datetime.datetime.now() - datetime.timedelta(days=data['recents']['boundary']) + + recent_parts = [] + + for playlist in [i for i in data['playlists'] if 'include_in_recents' in i]: + if playlist['include_in_recents']: + recent_parts += [i for i in playlist['playlists']] + + if 'playlists' in data['recents']: + recent_parts += data['recents']['playlists'] + + processors = [DeduplicateByName(), SortReverseReleaseDate()] + + recent_tracks = engine.get_recent_playlist(boundary_date, recent_parts, processors) + engine.execute_playlist(recent_tracks, data['recents']['id']) + engine.change_description([monthstrings.get_this_month(), + monthstrings.get_last_month()] + , data['recents']['id']) - net.replacePlaylistTracks(tomake['id'], [i['track']['uri'] for i in tracks]) - net.changePlaylistDetails(tomake['id'], description=' / '.join(tomake['playlists'])) else: log.log("config json not found") if 'SLACKHOOK' in os.environ: requests.post(os.environ['SLACKHOOK'], json={"text": "spot playlists: config json not found"}) log.dumpLog() - except: + except Exception: + log.log("exception occured") + if 'SLACKHOOK' in os.environ: + requests.post(os.environ['SLACKHOOK'], json={"text": "spot playlists: exception occured"}) log.dumpLog() diff --git a/spotframework/engine/__init__.py b/spotframework/engine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spotframework/engine/filter/__init__.py b/spotframework/engine/filter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spotframework/engine/filter/abstractprocessor.py b/spotframework/engine/filter/abstractprocessor.py new file mode 100644 index 0000000..be35f61 --- /dev/null +++ b/spotframework/engine/filter/abstractprocessor.py @@ -0,0 +1,11 @@ +from abc import ABC, abstractmethod + + +class AbstractProcessor(ABC): + + def __init__(self, names=[]): + self.playlist_names = names + + @abstractmethod + def process(self, tracks): + pass diff --git a/spotframework/engine/filter/addedbefore.py b/spotframework/engine/filter/addedbefore.py new file mode 100644 index 0000000..1118a3b --- /dev/null +++ b/spotframework/engine/filter/addedbefore.py @@ -0,0 +1,17 @@ +from .abstractprocessor import AbstractProcessor +import datetime + + +class AddedBefore(AbstractProcessor): + + def __init__(self, boundary, names=[]): + self.playlist_names = names + self.boundary = boundary + + def check_date(self, track): + added_at = datetime.datetime.fromisoformat(track['added_at'].replace('T', ' ').replace('Z', '')) + + return added_at < self.boundary + + def process(self, tracks): + return [i for i in tracks if self.check_date(i)] diff --git a/spotframework/engine/filter/addedsince.py b/spotframework/engine/filter/addedsince.py new file mode 100644 index 0000000..b1ebb0d --- /dev/null +++ b/spotframework/engine/filter/addedsince.py @@ -0,0 +1,17 @@ +from .abstractprocessor import AbstractProcessor +import datetime + + +class AddedSince(AbstractProcessor): + + def __init__(self, boundary, names=[]): + self.playlist_names = names + self.boundary = boundary + + def check_date(self, track): + added_at = datetime.datetime.fromisoformat(track['added_at'].replace('T', ' ').replace('Z', '')) + + return added_at > self.boundary + + def process(self, tracks): + return [i for i in tracks if self.check_date(i)] diff --git a/spotframework/engine/filter/deduplicatebyid.py b/spotframework/engine/filter/deduplicatebyid.py new file mode 100644 index 0000000..d7deb7c --- /dev/null +++ b/spotframework/engine/filter/deduplicatebyid.py @@ -0,0 +1,13 @@ +from .abstractprocessor import AbstractProcessor + + +class DeduplicateByID(AbstractProcessor): + + def process(self, tracks): + return_tracks = [] + + for track in tracks: + if track['track']['uri'] not in [i['track']['uri'] for i in return_tracks]: + return_tracks.append(track) + + return return_tracks diff --git a/spotframework/engine/filter/deduplicatebyname.py b/spotframework/engine/filter/deduplicatebyname.py new file mode 100644 index 0000000..0a1332b --- /dev/null +++ b/spotframework/engine/filter/deduplicatebyname.py @@ -0,0 +1,19 @@ +from .abstractprocessor import AbstractProcessor + + +class DeduplicateByName(AbstractProcessor): + + def process(self, tracks): + return_tracks = [] + + for to_check in tracks: + + for cache_track in return_tracks: + if to_check['track']['name'].lower() == cache_track['track']['name'].lower(): + if to_check['track']['artists'][0]['name'].lower() \ + == cache_track['track']['artists'][0]['name'].lower(): + break + else: + return_tracks.append(to_check) + + return return_tracks diff --git a/spotframework/engine/filter/shuffle.py b/spotframework/engine/filter/shuffle.py new file mode 100644 index 0000000..8b267e5 --- /dev/null +++ b/spotframework/engine/filter/shuffle.py @@ -0,0 +1,9 @@ +from .abstractprocessor import AbstractProcessor +import random + + +class Shuffle(AbstractProcessor): + + def process(self, tracks): + random.shuffle(tracks) + return tracks diff --git a/spotframework/engine/filter/sortreversereleasedate.py b/spotframework/engine/filter/sortreversereleasedate.py new file mode 100644 index 0000000..a6f0da7 --- /dev/null +++ b/spotframework/engine/filter/sortreversereleasedate.py @@ -0,0 +1,8 @@ +from .abstractprocessor import AbstractProcessor + + +class SortReverseReleaseDate(AbstractProcessor): + + def process(self, tracks): + tracks.sort(key=lambda x: x['track']['album']['release_date'], reverse=True) + return tracks diff --git a/spotframework/engine/playlistengine.py b/spotframework/engine/playlistengine.py new file mode 100644 index 0000000..8810c0c --- /dev/null +++ b/spotframework/engine/playlistengine.py @@ -0,0 +1,72 @@ +import spotframework.log.log as log + +import requests +import os + +import spotframework.util.monthstrings as monthstrings +from spotframework.engine.filter.addedsince import AddedSince + + +class PlaylistEngine: + + def __init__(self, net): + self.playlists = [] + self.net = net + + def load_user_playlists(self): + self.playlists = self.net.getUserPlaylists() + + def append_user_playlists(self): + self.playlists += self.net.getUserPlaylists() + + def get_playlist_tracks(self, playlist): + log.log("pulling tracks for {}".format(playlist.name)) + playlist.tracks = self.net.getPlaylistTracks(playlist.playlistid) + + def make_playlist(self, playlist_parts, processors=[]): + + tracks = [] + + for part in playlist_parts: + + play = next((i for i in self.playlists if i.name == part), None) + + if play is not None: + + if play.has_tracks() is False: + self.get_playlist_tracks(play) + + playlist_tracks = list(play.tracks) + + for processor in [i for i in processors if play.name in [j for j in i.playlist_names]]: + playlist_tracks = processor.process(playlist_tracks) + + tracks += [i for i in playlist_tracks if i['is_local'] is False] + + else: + log.log("requested playlist {} not found".format(part)) + if play is not None: + if 'SLACKHOOK' in os.environ: + requests.post(os.environ['SLACKHOOK'], json={"text": "spot playlists: {} not found".format(part)}) + + for processor in [i for i in processors if len(i.playlist_names) <= 0]: + tracks = processor.process(tracks) + + # print(tracks) + return tracks + + def get_recent_playlist(self, boundary_date, recent_playlist_parts, processors=[]): + this_month = monthstrings.get_this_month() + last_month = monthstrings.get_last_month() + + datefilter = AddedSince(boundary_date, recent_playlist_parts) + + processors.append(datefilter) + + return self.make_playlist(recent_playlist_parts + [this_month, last_month], processors) + + def execute_playlist(self, tracks, playlist_id): + self.net.replacePlaylistTracks(playlist_id, [i['track']['uri'] for i in tracks]) + + def change_description(self, playlistparts, playlist_id): + self.net.changePlaylistDetails(playlist_id, description=' / '.join(playlistparts)) diff --git a/spotframework/model/playlist.py b/spotframework/model/playlist.py index 7c6655e..e01d0b8 100644 --- a/spotframework/model/playlist.py +++ b/spotframework/model/playlist.py @@ -1,6 +1,6 @@ -class playlist: +class Playlist: def __init__(self, playlistid, uri=None, name=None, userid=None): self.tracks = [] @@ -8,3 +8,9 @@ class playlist: self.playlistid = playlistid self.userid = userid self.uri = uri + + def has_tracks(self): + if len(self.tracks) > 0: + return True + else: + return False diff --git a/spotframework/net/network.py b/spotframework/net/network.py index b06a416..cb60342 100644 --- a/spotframework/net/network.py +++ b/spotframework/net/network.py @@ -1,6 +1,6 @@ import requests from . import const -from spotframework.model.playlist import playlist as playlistclass +from spotframework.model.playlist import Playlist as playlistclass import spotframework.log.log as log limit = 50 diff --git a/spotframework/util/__init__.py b/spotframework/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spotframework/util/monthstrings.py b/spotframework/util/monthstrings.py new file mode 100644 index 0000000..580ff9e --- /dev/null +++ b/spotframework/util/monthstrings.py @@ -0,0 +1,14 @@ +import datetime + + +def get_this_month(): + return datetime.date.today().strftime('%B %y').lower() + + +def get_last_month(): + month = datetime.date.today().replace(day=1) - datetime.timedelta(days=1) + return month.strftime('%B %y').lower() + + +def get_this_year(): + return datetime.date.today().strftime('%y')