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)
Ejemplo n.º 2
0
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)