class SpotifyHistoryMonitor(object): """ """ def __init__(self, username): self._dynamodb = boto3.resource('dynamodb') self._load_configuration() self._spotify = Spotify(auth=self._access_token) self._plays = None self._username = username def _load_configuration(self): """ Get the latest configuration from DynamoDB """ configuration = self._dynamodb.Table('configuration') response = configuration.get_item(Key={'scope': 'spotify'}) self._access_token = response['Item']['access_token'] self._refresh_token = response['Item']['refresh_token'] self._client_id = response['Item']['client_id'] self._client_secret = response['Item']['client_secret'] def _save_configuration(self): """ Save the current configuration to DynamoDB """ configuration = self._dynamodb.Table('configuration') configuration.put_item(Item={'scope': 'spotify', 'access_token': self._access_token, 'refresh_token': self._refresh_token, 'client_id': self._client_id, 'client_secret': self._client_secret}) def _renew_tokens(self): spotify_oauth = SpotifyOAuth(self._client_id, self._client_secret, redirect_uri=None, scope=RECENTLY_PLAYED_SCOPE) tokens = spotify_oauth.refresh_access_token(self._refresh_token) self._access_token = tokens['access_token'] self._refresh_token = tokens['refresh_token'] self._spotify = Spotify(auth=self._access_token) self._save_configuration() print('renewed tokens') def _retrieve_plays(self): """ Retrieve up to 50 recently played tracks. The API is currently limited to return 50 entries. If the access_token has expired, attempt to renew it and save to DynamoDB """ try: recents = self._spotify._get("me/player/recently-played", limit=50) except SpotifyException as se: if 'The access token expired' in se.msg: self._renew_tokens() recents = self._spotify._get("me/player/recently-played", limit=50) else: raise self._plays = recents['items'] @property def plays(self): if not self._plays: self._retrieve_plays() return self._plays def save_new_plays(self): spotify_plays = self._dynamodb.Table('spotify_plays') response = spotify_plays.query(KeyConditionExpression=Key('user').eq(self._username), ProjectionExpression='played_at', ScanIndexForward=False, #reverse order Limit=1) if response['Count'] == 0: plays_to_write = self.plays else: last_played_at = response['Items'][0]['played_at'] plays_to_write = [p for p in self.plays if p['played_at'] > last_played_at] with spotify_plays.batch_writer() as writer: for play in plays_to_write: play_with_key = {'user':self._username} play_with_key.update(play) writer.put_item(play_with_key) return len(plays_to_write)
class SpotifyConnection(object): def __init__(self, user_data): self.user_name = user_data['user_name'] token = spotipy.util.prompt_for_user_token( self.user_name, scope='user-read-recently-played', client_id=user_data['client_id'], client_secret=user_data['client_secret'], redirect_uri=user_data['redirect_uri']) self.client = Spotify(auth=token) self.db = self.init_db() def init_db(self): return PostgreSQLConnection() def get_artist(self, artist_id): artist = self.db.session.query(Artist).get(artist_id) if artist: return artist else: artist_response = self.client.artist(artist_id) artist = Artist() artist.artist_id = artist_id artist.artist_data = artist_response self.db.save_instance(artist) print("> Artist {} was not in database.".format( artist.artist_data['name'])) return self.db.session.query(Artist).get(artist_id) def get_album(self, album_id): album = self.db.session.query(Album).get(album_id) if album: return album else: album_response = self.client.album(album_id) album = Album() album.album_data = album_response album.album_id = album_response['id'] # Artists for album_artist_response in album_response['artists']: album.artists.append( self.get_artist(album_artist_response['id'])) self.db.save_instance(album) print("> Album {} was not in database.".format( album.album_data['name'])) return self.db.session.query(Album).get(album_id) def get_track(self, track_id): track = self.db.session.query(Track).get(track_id) if track: return track else: response = self.client.track(track_id) track = Track() track.track_id = track_id track.track_data = response # Album track.album = self.get_album(response['album']['id']) # Artists for artist_response in response['artists']: track.artists.append(self.get_artist(artist_response['id'])) # Audio feature audio_feature_response = self.client.audio_features(track_id)[0] if audio_feature_response: # Some tracks do not have audio features track.audio_feature_data = audio_feature_response print("> Track {} was not in database.".format( track.track_data['name'])) self.db.save_instance(track) return self.db.session.query(Track).get(track_id) def get_play_from_played_at_utc_and_track_id(self, played_at_utc, track_id): played_at_utc = convert_played_at_from_response_to_datetime( played_at_utc) played_at_utc = set_timezone_to_datetime(played_at_utc, timezone='UTC') played_at_cet = convert_datetime_from_timezone_to_timezone( played_at_utc, from_tz_code='UTC', to_tz_code='CET') # Play play = Play() play.user_name = self.user_name play.played_at_utc_timestamp = played_at_utc.timestamp() * 1000 play.played_at_utc = played_at_utc play.played_at_cet = played_at_cet play.day = played_at_cet.day play.month = played_at_cet.month play.year = played_at_cet.year play.hour = played_at_cet.hour play.minute = played_at_cet.minute play.second = played_at_cet.second play.day_of_week = played_at_cet.weekday() play.week_of_year = played_at_cet.date().isocalendar()[1] # Track track = self.get_track(track_id) play.track = track play.track_id = track_id return play def _get_play_tuples_from_response(self, response): plays = [] for item in response['items']: play_tuple = (item['played_at'], item['track']['id']) plays.append(play_tuple) return plays def _get_play_tuples(self, limit=50, after=None): play_tuples = [] response = self.client._get('me/player/recently-played', after=after, limit=limit) play_tuples.extend(self._get_play_tuples_from_response(response)) while response and 'next' in response: response = self.client.next(response) if response: play_tuples.extend( self._get_play_tuples_from_response(response)) return play_tuples def extract_plays(self): print("* Extracting latest plays of {}.".format(self.user_name)) play_tuples = self._get_play_tuples() for played_at, track_id in play_tuples: play = self.get_play_from_played_at_utc_and_track_id( played_at, track_id) self.db.save_play(play)