added playlist engine and recent playlist
This commit is contained in:
parent
9750ff8b80
commit
89f8c8961b
@ -3,6 +3,14 @@ import spotframework.net.network as network
|
|||||||
import spotframework.net.user as user
|
import spotframework.net.user as user
|
||||||
import spotframework.log.log as log
|
import spotframework.log.log as log
|
||||||
import spotframework.io.json as json
|
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
|
import requests
|
||||||
|
|
||||||
@ -16,47 +24,58 @@ if __name__ == '__main__':
|
|||||||
data = json.loadJson(os.path.join(const.config_path, 'playlists.json'))
|
data = json.loadJson(os.path.join(const.config_path, 'playlists.json'))
|
||||||
|
|
||||||
net = network.network(user.User())
|
net = network.network(user.User())
|
||||||
playlists = net.getUserPlaylists()
|
|
||||||
|
engine = PlaylistEngine(net)
|
||||||
|
engine.load_user_playlists()
|
||||||
|
|
||||||
for tomake in data['playlists']:
|
for tomake in data['playlists']:
|
||||||
|
|
||||||
log.log("generatePlaylist", tomake['name'])
|
log.log("makePlaylist", tomake['name'])
|
||||||
|
|
||||||
tracks = []
|
processors = [DeduplicateByID()]
|
||||||
|
|
||||||
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)})
|
|
||||||
|
|
||||||
if 'shuffle' in tomake:
|
if 'shuffle' in tomake:
|
||||||
if tomake['shuffle'] is True:
|
if tomake['shuffle'] is True:
|
||||||
import random
|
processors.append(Shuffle())
|
||||||
random.shuffle(tracks)
|
|
||||||
else:
|
else:
|
||||||
tracks.sort(key=lambda x: x['track']['album']['release_date'], reverse=True)
|
processors.append(SortReverseReleaseDate())
|
||||||
else:
|
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:
|
else:
|
||||||
log.log("config json not found")
|
log.log("config json not found")
|
||||||
if 'SLACKHOOK' in os.environ:
|
if 'SLACKHOOK' in os.environ:
|
||||||
requests.post(os.environ['SLACKHOOK'], json={"text": "spot playlists: config json not found"})
|
requests.post(os.environ['SLACKHOOK'], json={"text": "spot playlists: config json not found"})
|
||||||
|
|
||||||
log.dumpLog()
|
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()
|
log.dumpLog()
|
||||||
|
0
spotframework/engine/__init__.py
Normal file
0
spotframework/engine/__init__.py
Normal file
0
spotframework/engine/filter/__init__.py
Normal file
0
spotframework/engine/filter/__init__.py
Normal file
11
spotframework/engine/filter/abstractprocessor.py
Normal file
11
spotframework/engine/filter/abstractprocessor.py
Normal file
@ -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
|
17
spotframework/engine/filter/addedbefore.py
Normal file
17
spotframework/engine/filter/addedbefore.py
Normal file
@ -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)]
|
17
spotframework/engine/filter/addedsince.py
Normal file
17
spotframework/engine/filter/addedsince.py
Normal file
@ -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)]
|
13
spotframework/engine/filter/deduplicatebyid.py
Normal file
13
spotframework/engine/filter/deduplicatebyid.py
Normal file
@ -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
|
19
spotframework/engine/filter/deduplicatebyname.py
Normal file
19
spotframework/engine/filter/deduplicatebyname.py
Normal file
@ -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
|
9
spotframework/engine/filter/shuffle.py
Normal file
9
spotframework/engine/filter/shuffle.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from .abstractprocessor import AbstractProcessor
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
class Shuffle(AbstractProcessor):
|
||||||
|
|
||||||
|
def process(self, tracks):
|
||||||
|
random.shuffle(tracks)
|
||||||
|
return tracks
|
8
spotframework/engine/filter/sortreversereleasedate.py
Normal file
8
spotframework/engine/filter/sortreversereleasedate.py
Normal file
@ -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
|
72
spotframework/engine/playlistengine.py
Normal file
72
spotframework/engine/playlistengine.py
Normal file
@ -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))
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
class playlist:
|
class Playlist:
|
||||||
|
|
||||||
def __init__(self, playlistid, uri=None, name=None, userid=None):
|
def __init__(self, playlistid, uri=None, name=None, userid=None):
|
||||||
self.tracks = []
|
self.tracks = []
|
||||||
@ -8,3 +8,9 @@ class playlist:
|
|||||||
self.playlistid = playlistid
|
self.playlistid = playlistid
|
||||||
self.userid = userid
|
self.userid = userid
|
||||||
self.uri = uri
|
self.uri = uri
|
||||||
|
|
||||||
|
def has_tracks(self):
|
||||||
|
if len(self.tracks) > 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import requests
|
import requests
|
||||||
from . import const
|
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
|
import spotframework.log.log as log
|
||||||
|
|
||||||
limit = 50
|
limit = 50
|
||||||
|
0
spotframework/util/__init__.py
Normal file
0
spotframework/util/__init__.py
Normal file
14
spotframework/util/monthstrings.py
Normal file
14
spotframework/util/monthstrings.py
Normal file
@ -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')
|
Loading…
Reference in New Issue
Block a user