diff --git a/google_test.py b/google_test.py deleted file mode 100644 index b481e3d..0000000 --- a/google_test.py +++ /dev/null @@ -1,3 +0,0 @@ -from spotframework.google.run_user_playlist import run_user_playlist as run_user_playlist - -run_user_playlist('andy', 'DNB') diff --git a/main.py b/main.py deleted file mode 100644 index 46a8f87..0000000 --- a/main.py +++ /dev/null @@ -1,12 +0,0 @@ - - -def run_user_playlist(event, context): - - import base64 - - name = base64.b64decode(event['data']).decode('utf-8') - username = event['attributes']['username'] - - from spotframework.google.run_user_playlist import run_user_playlist as run - - run(username, name) diff --git a/spotframework/__init__.py b/spotframework/__init__.py index c99472f..4125da8 100644 --- a/spotframework/__init__.py +++ b/spotframework/__init__.py @@ -2,24 +2,24 @@ import logging import os logger = logging.getLogger(__name__) -logger.setLevel('INFO') +logger.setLevel('DEBUG') -log_format = '%(levelname)s %(name)s:%(funcName)s - %(message)s' -formatter = logging.Formatter(log_format) - -if os.environ.get('CLOUD', None): +if os.environ.get('DEPLOY_DESTINATION', None) == 'PROD': from google.cloud import logging as glogging from google.cloud.logging.handlers import CloudLoggingHandler - client = glogging.Client() + log_format = '%(funcName)s - %(message)s' + formatter = logging.Formatter(log_format) - # handler = client.get_default_handler() - handler = CloudLoggingHandler(client) + client = glogging.Client() + handler = CloudLoggingHandler(client, name='spotframework') handler.setFormatter(formatter) logger.addHandler(handler) else: + log_format = '%(levelname)s %(name)s:%(funcName)s - %(message)s' + formatter = logging.Formatter(log_format) stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) diff --git a/spotframework/engine/playlistengine.py b/spotframework/engine/playlistengine.py index 86e48e6..b6062ad 100644 --- a/spotframework/engine/playlistengine.py +++ b/spotframework/engine/playlistengine.py @@ -15,14 +15,31 @@ class PlaylistEngine: self.net = net def load_user_playlists(self): - self.playlists = self.net.get_playlists() + logger.info('loading') + + playlists = self.net.get_playlists() + if playlists and len(playlists) > 0: + self.playlists = playlists + else: + logger.error('error getting playlists') def append_user_playlists(self): - self.playlists += self.net.get_playlists() + logger.info('loading') + + playlists = self.net.get_playlists() + if playlists and len(playlists) > 0: + self.playlists += playlists + else: + logger.error('error getting playlists') def get_playlist_tracks(self, playlist): logger.info(f"pulling tracks for {playlist.name}") - playlist.tracks = self.net.get_playlist_tracks(playlist.playlistid) + + tracks = self.net.get_playlist_tracks(playlist.playlistid) + if tracks and len(tracks) > 0: + playlist.tracks = tracks + else: + logger.error('error getting tracks') def make_playlist(self, playlist_parts, processors=[], include_recommendations=False, recommendation_limit=10): @@ -55,30 +72,62 @@ class PlaylistEngine: tracks = [i['track'] for i in tracks] if include_recommendations: - try: - tracks += self.net.get_recommendations(tracks=[i['id'] for i in tracks], - response_limit=recommendation_limit)['tracks'] - except Exception as e: - logger.exception('exception occured') + recommendations = self.net.get_recommendations(tracks=[i['id'] for i in tracks], + response_limit=recommendation_limit) + if recommendations and len(recommendations) > 0: + tracks += recommendations['tracks'] + else: + logger.error('error getting recommendations') - # print(tracks) return tracks - def get_recent_playlist(self, boundary_date, recent_playlist_parts, processors=[], include_recommendations=False, recommendation_limit=10): + def get_recent_playlist(self, + boundary_date, + recent_playlist_parts, + processors=[], + include_recommendations=False, + recommendation_limit=10, + add_this_month=False, + add_last_month=False): + this_month = monthstrings.get_this_month() last_month = monthstrings.get_last_month() - datefilter = AddedSince(boundary_date, recent_playlist_parts + [last_month]) + month_playlists = [] + + if add_this_month: + month_playlists.append(this_month) + + if add_last_month: + month_playlists.append(last_month) + + datefilter = AddedSince(boundary_date, recent_playlist_parts + month_playlists) processors.append(datefilter) - return self.make_playlist(recent_playlist_parts + [this_month, last_month], + return self.make_playlist(recent_playlist_parts + month_playlists, processors, include_recommendations=include_recommendations, recommendation_limit=recommendation_limit) def execute_playlist(self, tracks, playlist_id): - self.net.replace_playlist_tracks(playlist_id, [i['uri'] for i in tracks]) - def change_description(self, playlistparts, playlist_id): - self.net.change_playlist_details(playlist_id, description=' / '.join(playlistparts)) + resp = self.net.replace_playlist_tracks(playlist_id, [i['uri'] for i in tracks]) + if resp: + return resp + else: + logger.error('error executing') + return None + + def change_description(self, playlistparts, playlist_id, suffix=None): + + if suffix: + string = ' / '.join(playlistparts) + f' - {str(suffix)}' + else: + string = ' / '.join(playlistparts) + + resp = self.net.change_playlist_details(playlist_id, description=string) + if resp: + return resp + else: + logger.error('error changing description') diff --git a/spotframework/google/__init__.py b/spotframework/google/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/spotframework/google/run_user_playlist.py b/spotframework/google/run_user_playlist.py deleted file mode 100644 index 489253b..0000000 --- a/spotframework/google/run_user_playlist.py +++ /dev/null @@ -1,111 +0,0 @@ -from google.cloud import firestore - -import datetime -import logging - -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.net.network import Network -from spotframework.net.user import User - -db = firestore.Client() - -captured_playlists = [] - - -def run_user_playlist(username, playlist_name): - - logger = logging.getLogger(__name__) - - users = [i for i in db.collection(u'spotify_users').where(u'username', u'==', username).stream()] - - logger.info(f'{username} / {playlist_name}') - - if len(users) == 1: - - user_dict = users[0].to_dict() - - playlist_collection = db.collection(u'spotify_users', u'{}'.format(users[0].id), 'playlists') - - playlists = [i for i in playlist_collection.where(u'name', u'==', playlist_name).stream()] - - if len(playlists) == 1: - - playlist_dict = playlists[0].to_dict() - - if playlist_dict['playlist_id'] is None: - logger.critical(f'no playlist id to populate ({username}/{playlist_name})') - return - - if len(playlist_dict['parts']) == 0 and len(playlist_dict['playlist_references']) == 0: - logger.critical(f'no playlists to use for creation ({username}/{playlist_name})') - return - - spotify_keys = db.document('key/spotify').get().to_dict() - - net = Network(User(spotify_keys['clientid'], - spotify_keys['clientsecret'], - user_dict['access_token'], - user_dict['refresh_token'])) - - engine = PlaylistEngine(net) - engine.load_user_playlists() - - processors = [DeduplicateByID()] - - if playlist_dict['shuffle'] is True: - processors.append(Shuffle()) - else: - processors.append(SortReverseReleaseDate()) - - global captured_playlists - captured_playlists = [] - - submit_parts = playlist_dict['parts'] + generate_parts(users[0].id, playlist_dict['name']) - - submit_parts = [i for i in {j for j in submit_parts}] - - if playlist_dict['type'] == 'recents': - boundary_date = datetime.datetime.now() - datetime.timedelta(days=int(playlist_dict['day_boundary'])) - tracks = engine.get_recent_playlist(boundary_date, - submit_parts, - processors, - include_recommendations=playlist_dict['include_recommendations'], - recommendation_limit=int(playlist_dict['recommendation_sample'])) - else: - tracks = engine.make_playlist(submit_parts, - processors, - include_recommendations=playlist_dict['include_recommendations'], - recommendation_limit=int(playlist_dict['recommendation_sample'])) - - engine.execute_playlist(tracks, playlist_dict['playlist_id']) - engine.change_description(sorted(submit_parts), playlist_dict['playlist_id']) - - else: - logger.critical(f'multiple/no playlists found ({username}/{playlist_name})') - return - - else: - logger.critical(f'multiple/no user(s) found ({username}/{playlist_name})') - return - - -def generate_parts(user_id, name): - - playlist_doc = [i.to_dict() for i in - db.document(u'spotify_users/{}'.format(user_id)) - .collection(u'playlists') - .where(u'name', '==', name).stream()][0] - - return_parts = playlist_doc['parts'] - - captured_playlists.append(name) - - for i in playlist_doc['playlist_references']: - if i not in captured_playlists: - return_parts += generate_parts(user_id, i) - - return return_parts diff --git a/spotframework/net/network.py b/spotframework/net/network.py index 98f9377..4ccde0e 100644 --- a/spotframework/net/network.py +++ b/spotframework/net/network.py @@ -65,10 +65,15 @@ class Network: tracks = self.get_playlist_tracks(playlistid) - playlist = Playlist(playlistid) - playlist.tracks += tracks + if tracks is not None: - return playlist + playlist = Playlist(playlistid) + playlist.tracks += tracks + + return playlist + else: + logger.error(f"{playlistid} - no tracks returned") + return None def create_playlist(self, username, name='New Playlist', public=True, collaborative=False, description=None): @@ -81,6 +86,9 @@ class Network: if 200 <= req.status_code < 300: return req.json() + else: + logger.error('error creating playlist') + return None def get_playlists(self, offset=0): @@ -105,18 +113,27 @@ class Network: # playlists = playlists + resp['items'] if resp['next']: - playlists += self.get_playlists(offset + limit) + more_playlists = self.get_playlists(offset + limit) + if more_playlists: + playlists += more_playlists return playlists else: + logger.error(f'error getting playlists offset={offset}') return None def get_user_playlists(self): logger.info('retrieved') - return list(filter(lambda x: x.userid == self.user.username, self.get_playlists())) + playlists = self.get_playlists() + + if playlists: + return list(filter(lambda x: x.userid == self.user.username, playlists)) + else: + logger.error('no playlists returned to filter') + return None def get_playlist_tracks(self, playlistid, offset=0): @@ -128,33 +145,54 @@ class Network: resp = self._make_get_request('getPlaylistTracks', f'playlists/{playlistid}/tracks', params=params) - if resp and resp.get('items'): - tracks += resp['items'] + if resp: + if resp.get('items', None): + tracks += resp['items'] + else: + logger.warning(f'{playlistid} no items returned') else: - logger.warning(f'{playlistid} no response or items') + logger.warning(f'{playlistid} error on response') if resp.get('next', None): - tracks += self.get_playlist_tracks(playlistid, offset + limit) + + more_tracks = self.get_playlist_tracks(playlistid, offset + limit) + if more_tracks: + tracks += more_tracks return tracks def get_available_devices(self): - logger.info("retrieved") + logger.info("retrieving") - return self._make_get_request('getAvailableDevices', 'me/player/devices') + resp = self._make_get_request('getAvailableDevices', 'me/player/devices') + if resp: + return resp + else: + logger.error('no devices returned') + return None def get_player(self): logger.info("retrieved") - return self._make_get_request('getPlayer', 'me/player') + resp = self._make_get_request('getPlayer', 'me/player') + if resp: + return resp + else: + logger.error('no player returned') + return None def get_device_id(self, devicename): logger.info(f"{devicename}") - return next((i for i in self.get_available_devices()['devices'] if i['name'] == devicename), None)['id'] + resp = self.get_available_devices() + if resp: + return next((i for i in resp['devices'] if i['name'] == devicename), None)['id'] + else: + logger.error('no devices returned') + return None def play(self, uri=None, uris=None, deviceid=None): @@ -178,6 +216,8 @@ class Network: raise Exception('need either context uri or uris') req = self._make_put_request('play', 'me/player/play', params=params, json=payload) + if req is None: + logger.error('error playing') def pause(self, deviceid=None): @@ -189,6 +229,8 @@ class Network: params = None req = self._make_put_request('pause', 'me/player/pause', params=params) + if req is None: + logger.error('error pausing') def next(self, deviceid=None): @@ -200,6 +242,8 @@ class Network: params = None req = self._make_post_request('next', 'me/player/next', params=params) + if req is None: + logger.error('error skipping') def set_shuffle(self, state, deviceid=None): @@ -211,6 +255,8 @@ class Network: params['device_id'] = deviceid req = self._make_put_request('setShuffle', 'me/player/shuffle', params=params) + if req is None: + logger.error(f'error setting shuffle {state}') def set_volume(self, volume, deviceid=None): @@ -224,9 +270,15 @@ class Network: params['device_id'] = deviceid req = self._make_put_request('setVolume', 'me/player/volume', params=params) + if req: + return req + else: + logger.error(f'error setting volume {volume}') + return None else: logger.error(f"{volume} not accepted value") + return None def replace_playlist_tracks(self, playlistid, uris): @@ -239,10 +291,13 @@ class Network: req = self._make_put_request('replacePlaylistTracks', f'playlists/{playlistid}/tracks', json=json, headers=headers) if req is not None: - resp = req.json() if len(uris) > 100: - self.add_playlist_tracks(playlistid, uris[100:]) + return self.add_playlist_tracks(playlistid, uris[100:]) + + return req + else: + logger.error(f'error replacing playlist tracks, total: {len(uris)}') def change_playlist_details(self, playlistid, name=None, public=None, collaborative=None, description=None): @@ -264,8 +319,16 @@ class Network: if description is not None: json['description'] = description - req = self._make_put_request('changePlaylistDetails', f'playlists/{playlistid}', json=json, headers=headers) - return req + if len(json) == 0: + logger.warning('update dictionairy length 0') + return None + else: + req = self._make_put_request('changePlaylistDetails', f'playlists/{playlistid}', json=json, headers=headers) + if req: + return req + else: + logger.error('error updating details') + return None def add_playlist_tracks(self, playlistid, uris): @@ -280,9 +343,17 @@ class Network: if req is not None: resp = req.json() + snapshots = [resp] + if len(uris) > 100: - self.add_playlist_tracks(playlistid, uris[100:]) + snapshots += self.add_playlist_tracks(playlistid, uris[100:]) + + return snapshots + + else: + logger.error(f'error retrieving tracks {playlistid}, total: {len(uris)}') + return [] def get_recommendations(self, tracks=None, artists=None, response_limit=10): @@ -297,4 +368,13 @@ class Network: random.shuffle(artists) params['seed_artists'] = artists[:100] - return self._make_get_request('getRecommendations', 'recommendations', params=params) + if len(params) == 1: + logger.warning('update dictionairy length 0') + return None + else: + resp = self._make_get_request('getRecommendations', 'recommendations', params=params) + if resp: + return resp + else: + logger.error('error getting recommendations') + return None