def get_saved_tracks(sp: Spotify) -> List[Dict[str, Any]]: """Returns the list of tracks saved in user library""" tracks = [] # type: List[Dict[str, Any]] results = sp.current_user_saved_tracks(limit=50) tracks.extend(results["items"]) while results["next"]: results = sp.next(results) tracks.extend(results["items"]) return tracks
def likedSongsCreateVibe(self, token): # Authenticate spotifyObject = Spotify(auth=token) spotifyUser = spotifyObject.current_user() username = spotifyUser['id'] # create genre dictionary genreDict = {} # get songs from LikedSongs playlistSongs = spotifyObject.current_user_saved_tracks(limit=50) pprint(playlistSongs) numberOfSongs = int(playlistSongs['total']) totalAdded = int(0) songObjectsList = [] songID_List = [] while (numberOfSongs > totalAdded): playlistSongs = spotifyObject.current_user_saved_tracks(limit=50, offset=totalAdded) for i in range(0, len(playlistSongs['items'])): try: songURI = playlistSongs['items'][i]['track']['uri'] songID = playlistSongs['items'][i]['track']['id'] songID_List.append(songID) songFeatures = spotifyObject.audio_features(songURI) genreDict = self.genreVibe(spotifyObject=spotifyObject, songId=songID, genreDict=genreDict) songObj = [songFeatures[0]['acousticness'], songFeatures[0]['danceability'], songFeatures[0]['energy'], songFeatures[0]['instrumentalness'], songFeatures[0]['liveness'], songFeatures[0]['speechiness'], round(songFeatures[0]['tempo'] / 180, 6), songFeatures[0]['valence']] songObjectsList.append(songObj) except: print("song was removed from spotify sorry") totalAdded = totalAdded + 50 #Create Clusters and Return Answer centroidsAndError = self.KMeansVibe(data=songObjectsList) centroidsAndError_GenreDict = [centroidsAndError, genreDict, songID_List] return centroidsAndError_GenreDict
def get_library(username: str, sp: spotipy.Spotify) -> list: print("Getting tracks from user library..") trackList = [] library = sp.current_user_saved_tracks() for item in library['items']: track = item['track'] trackList.append( dict(id=track['id'], artist=track['artists'][0]['name'], name=track['name'])) return trackList
def __clear_favorites(spot_client: spotipy.Spotify): with yaspin(text='Removing favorites...', color='yellow') as spinner: while True: response = spot_client.current_user_saved_tracks(limit=50) if not response: break favorites = [] for item in response['items']: favorites.append(item['track']['id']) if len(favorites) > 0: spot_client.current_user_saved_tracks_delete(favorites) else: break spinner.ok('✅ ')
def get_unadded_songs(dt_threshold: dt, client: spotipy.Spotify) -> deque: """ finds all songs that were added past the last date """ song_ids = deque() chunks, offset = 20, 0 while True: songs_liked = client.current_user_saved_tracks(chunks, offset) for song in songs_liked['items']: if dt_threshold < dt.strptime( song['added_at'], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=tz.utc): song_ids.append(song['track']['id']) else: return song_ids # edge case: user has less liked songs than the chunk size if len(songs_liked['items']) < chunks: return song_ids offset += chunks
def get_library(spotify: spotipy.Spotify): """Load library from the users saved songs.""" try: with open("artists.json", "r") as f: artists = set(json.load(f)) artists_loaded = True except FileNotFoundError: artists_loaded = False try: with open("songs.json", "r") as f: songs = tuple(tuple(song) for song in json.load(f)) songs_loaded = True except FileNotFoundError: songs_loaded = False if songs_loaded and artists_loaded: return artists, songs # 50 is max limit for api results = get_all(spotify, spotify.current_user_saved_tracks(limit=50)) if not artists_loaded: artists = set() if not songs_loaded: songs = [] for result in results: track = result["track"] artist = track["artists"][0]["name"].lower() if not artists_loaded: artists.add(artist) if not songs_loaded: songs = get_songs(results) results = get_all(spotify, spotify.user_playlist_tracks(None, INSTR_ID)) inst_songs = get_songs(results) songs = tuple(song for song in songs if song not in inst_songs) with open("artists.json", "w") as f: json.dump(list(artists), f) with open("songs.json", "w") as f: json.dump(songs, f) return artists, tuple(songs)
class SpotifyDumper: def __init__(self): self.sp = Spotify(auth_manager=SpotifyOAuth(scope="user-library-read")) def dump_saved_songs(self, spotify_user_id: str): with track_writer() as w: for track in self._get_liked_artists(): w.write_track(track) def _get_liked_artists(self) -> Iterable[Track]: results = self.sp.current_user_saved_tracks() yield from self._consume_all(_parse_saved_tracks_chunk, results) def _consume_all(self, parser: Callable[[dict], Iterable[T]], result: dict) -> Iterable[T]: yield from parser(result) while result['next']: result = self.sp.next(result) yield from parser(result)
class AuthTestSpotipy(unittest.TestCase): """ These tests require user authentication - provide client credentials using the following environment variables :: 'SPOTIPY_CLIENT_USERNAME' 'SPOTIPY_CLIENT_ID' 'SPOTIPY_CLIENT_SECRET' 'SPOTIPY_REDIRECT_URI' """ playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx" playlist_new_id = "spotify:playlist:7GlxpQjjxRjmbb3RP2rDqI" four_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB", "4VrWlk8IQxevMvERoX08iC", "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"] two_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB"] other_tracks = ["spotify:track:2wySlB6vMzCbQrRnNGOYKa", "spotify:track:29xKs5BAHlmlX1u4gzQAbJ", "spotify:track:1PB7gRWcvefzu7t3LJLUlf"] album_ids = ["spotify:album:6kL09DaURb7rAoqqaA51KU", "spotify:album:6RTzC0rDbvagTSJLlY7AKl"] bad_id = 'BAD_ID' @classmethod def setUpClass(self): if sys.version_info >= (3, 2): # >= Python3.2 only warnings.filterwarnings( "ignore", category=ResourceWarning, # noqa message="unclosed.*<ssl.SSLSocket.*>") missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV)) if missing: raise Exception( ('Please set the client credentials for the test application' ' using the following environment variables: {}').format( CCEV.values())) self.username = os.getenv(CCEV['client_username']) self.scope = ( 'playlist-modify-public ' 'user-library-read ' 'user-follow-read ' 'user-library-modify ' 'user-read-private ' 'user-top-read ' 'user-follow-modify ' 'user-read-recently-played ' 'ugc-image-upload' ) self.token = prompt_for_user_token(self.username, scope=self.scope) self.spotify = Spotify(auth=self.token) # Helper def get_or_create_spotify_playlist(self, playlist_name): playlists = self.spotify.user_playlists(self.username) while playlists: for item in playlists['items']: if item['name'] == playlist_name: return item playlists = self.spotify.next(playlists) return self.spotify.user_playlist_create( self.username, playlist_name) # Helper def get_as_base64(self, url): import base64 return base64.b64encode(requests.get(url).content).decode("utf-8") def test_track_bad_id(self): try: self.spotify.track(self.bad_id) self.assertTrue(False) except SpotifyException: self.assertTrue(True) def test_basic_user_profile(self): user = self.spotify.user(self.username) self.assertTrue(user['id'] == self.username.lower()) def test_current_user(self): user = self.spotify.current_user() self.assertTrue(user['id'] == self.username.lower()) def test_me(self): user = self.spotify.me() self.assertTrue(user['id'] == self.username.lower()) def test_user_playlists(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 5) def test_user_playlist_tracks(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) for playlist in playlists['items']: user = playlist['owner']['id'] pid = playlist['id'] results = self.spotify.user_playlist_tracks(user, pid) self.assertTrue(len(results['items']) >= 0) def test_current_user_saved_albums(self): # List albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) > 1) # Add self.spotify.current_user_saved_albums_add(self.album_ids) # Contains self.assertTrue( self.spotify.current_user_saved_albums_contains( self.album_ids) == [ True, True]) # Remove self.spotify.current_user_saved_albums_delete(self.album_ids) albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) > 1) def test_current_user_playlists(self): playlists = self.spotify.current_user_playlists(limit=10) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 10) def test_user_playlist_follow(self): self.spotify.user_playlist_follow_playlist( 'plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [ self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertTrue(follows[0], 'is following') self.spotify.user_playlist_unfollow( 'plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [ self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertFalse(follows[0], 'is no longer following') def test_current_user_saved_tracks(self): tracks = self.spotify.current_user_saved_tracks() self.assertTrue(len(tracks['items']) > 0) def test_current_user_save_and_unsave_tracks(self): tracks = self.spotify.current_user_saved_tracks() total = tracks['total'] self.spotify.current_user_saved_tracks_add(self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total - total == len(self.four_tracks)) tracks = self.spotify.current_user_saved_tracks_delete( self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total == total) def test_categories(self): response = self.spotify.categories() self.assertTrue(len(response['categories']) > 0) def test_category_playlists(self): response = self.spotify.categories() for cat in response['categories']['items']: cat_id = cat['id'] response = self.spotify.category_playlists(category_id=cat_id) if len(response['playlists']["items"]) > 0: break self.assertTrue(True) def test_new_releases(self): response = self.spotify.new_releases() self.assertTrue(len(response['albums']) > 0) def test_featured_releases(self): response = self.spotify.featured_playlists() self.assertTrue(len(response['playlists']) > 0) def test_current_user_follows(self): response = self.spotify.current_user_followed_artists() artists = response['artists'] self.assertTrue(len(artists['items']) > 0) def test_current_user_top_tracks(self): response = self.spotify.current_user_top_tracks() items = response['items'] self.assertTrue(len(items) > 0) def test_current_user_top_artists(self): response = self.spotify.current_user_top_artists() items = response['items'] self.assertTrue(len(items) > 0) def test_current_user_recently_played(self): # No cursor res = self.spotify.current_user_recently_played() self.assertTrue(len(res['items']) <= 50) played_at = res['items'][0]['played_at'] # Using `before` gives tracks played before res = self.spotify.current_user_recently_played( before=res['cursors']['after']) self.assertTrue(len(res['items']) <= 50) self.assertTrue(res['items'][0]['played_at'] < played_at) played_at = res['items'][0]['played_at'] # Using `after` gives tracks played after res = self.spotify.current_user_recently_played( after=res['cursors']['before']) self.assertTrue(len(res['items']) <= 50) self.assertTrue(res['items'][0]['played_at'] > played_at) def test_user_playlist_ops(self): sp = self.spotify # create empty playlist playlist = self.get_or_create_spotify_playlist( 'spotipy-testing-playlist-1') playlist_id = playlist['id'] # remove all tracks from it sp.user_playlist_replace_tracks( self.username, playlist_id, []) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 0) self.assertTrue(len(playlist['tracks']['items']) == 0) # add tracks to it sp.user_playlist_add_tracks( self.username, playlist_id, self.four_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 4) self.assertTrue(len(playlist['tracks']['items']) == 4) # remove two tracks from it sp.user_playlist_remove_all_occurrences_of_tracks(self.username, playlist_id, self.two_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 2) self.assertTrue(len(playlist['tracks']['items']) == 2) # replace with 3 other tracks sp.user_playlist_replace_tracks(self.username, playlist_id, self.other_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 3) self.assertTrue(len(playlist['tracks']['items']) == 3) def test_playlist(self): # New playlist ID pl = self.spotify.playlist(self.playlist_new_id) self.assertTrue(pl["tracks"]["total"] > 0) # Old playlist ID pl = self.spotify.playlist(self.playlist) self.assertTrue(pl["tracks"]["total"] > 0) def test_playlist_tracks(self): # New playlist ID pl = self.spotify.playlist_tracks(self.playlist_new_id, limit=2) self.assertTrue(len(pl["items"]) == 2) self.assertTrue(pl["total"] > 0) # Old playlist ID pl = self.spotify.playlist_tracks(self.playlist, limit=2) self.assertTrue(len(pl["items"]) == 2) self.assertTrue(pl["total"] > 0) def test_playlist_upload_cover_image(self): pl1 = self.get_or_create_spotify_playlist('spotipy-testing-playlist-1') plid = pl1['uri'] old_b64 = pl1['images'][0]['url'] # Upload random dog image r = requests.get('https://dog.ceo/api/breeds/image/random') dog_base64 = self.get_as_base64(r.json()['message']) self.spotify.playlist_upload_cover_image(plid, dog_base64) # Image must be different pl1 = self.spotify.playlist(plid) new_b64 = self.get_as_base64(pl1['images'][0]['url']) self.assertTrue(old_b64 != new_b64) def test_playlist_cover_image(self): pl = self.get_or_create_spotify_playlist('spotipy-testing-playlist-1') plid = pl['uri'] res = self.spotify.playlist_cover_image(plid) self.assertTrue(len(res) > 0) first_image = res[0] self.assertTrue('width' in first_image) self.assertTrue('height' in first_image) self.assertTrue('url' in first_image) def test_user_follows_and_unfollows_artist(self): # Initially follows 1 artist res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 1) # Follow 2 more artists artists = ["6DPYiyq5kWVQS4RGwxzPC7", "0NbfKEOTQCcwd6o7wSDOHI"] self.spotify.user_follow_artists(artists) res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 3) # Unfollow these 2 artists self.spotify.user_unfollow_artists(artists) res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 1) def test_user_follows_and_unfollows_user(self): # TODO improve after implementing `me/following/contains` users = ["11111204", "xlqeojt6n7on0j7coh9go8ifd"] # Follow 2 more users self.spotify.user_follow_users(users) # Unfollow these 2 users self.spotify.user_unfollow_users(users) def test_deprecated_starred(self): pl = self.spotify.user_playlist(self.username) self.assertTrue(pl["tracks"] is None) self.assertTrue(pl["owner"] is None) def test_deprecated_user_playlist(self): # Test without user due to change from # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/ pl = self.spotify.user_playlist(None, self.playlist) self.assertTrue(pl["tracks"]["total"] > 0) def test_deprecated_user_playlis(self): # Test without user due to change from # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/ pl = self.spotify.user_playlist_tracks(None, self.playlist, limit=2) self.assertTrue(len(pl["items"]) == 2) self.assertTrue(pl["total"] > 0)
def main(): clientRedirect = "http://localhost/" username = "******" scope = "playlist-read-collaborative " \ "playlist-read-private " \ "user-library-read " \ "playlist-modify-public " \ "playlist-modify-private" token = util.prompt_for_user_token(username, scope, clientID, clientSecret, clientRedirect) spotify = Spotify(auth=token) result = spotify.current_user_saved_tracks(offset=0, limit=50) data = {} songs = {} exceptions = ("Depeche Mode", "Grant Miller", "Madonna", "Ministry", "The Beach Boys", "Mickey & Sylvia", "The Clovers", "Village People", "Frank Sinatra", "Rodríguez", "The Bangles", "U2", "UB40", "Tom Petty", "Faces", "Bobby McFerrin", "Dion", "Fancy", "Eddy Huntington", "Michael Jackson", "OutKast", "Gorillaz", "Diddy", "Lipps Inc.", "Chuck Berry", "Marvin Gaye", "The Kinks", "Count Basie", "Player", "Steve Lawrence", "Nelly", "The Killers", "Billy Idol", "Haddaway", "Blondie") dontwant = ("Emile Van Krieken") '''while result["next"]: for track in result["items"]: songs.update(track["track"]["name"]) result = spotify.next(result) data = {username: songs}''' num = 0 playlist_id = "" for playlist in spotify.current_user_playlists()["items"]: if playlist["name"] == "Computer Generated Old 2": playlist_id = playlist["id"] exceptions_list = [] while result["next"]: for track in result["items"]: num = num + 1 print(num) track = track["track"] '''songs.update({track["uri"]: {"track": track["name"], "artist": track["artists"][0]["name"], "artist uri": track["artists"][0]["uri"], "album": track["album"]["name"], "album uri": track["album"]["uri"] } })''' album = spotify.album(track["album"]["id"]) '''if (int(album["release_date"][0:4]) < 2000 or track["artists"][0]["name"] in exceptions): #and int(album["release_date"][0:4]) > 2006)\ #print(track["uri"]) print(track["id"]) print(track["name"]) spotify.user_playlist_add_tracks("karan.arora.28", playlist_id, [track["uri"]])''' if (int(album["release_date"][0:4]) < 2006) or track["artists"][0]["name"] in exceptions: spotify.user_playlist_add_tracks("karan.arora.28", playlist_id, [track["uri"]]) if track["artists"][0]["name"] not in exceptions_list: exceptions_list.append(track["artists"][0]["name"]) print(exceptions_list) '''else: #pid = getPlaylistIDByName(spotify, "Old??") pid = "2qSyS6sDfEGSS38cn4GR8U" if trackInPlaylist(spotify, track["name"], pid): print(track["name"]) print(track["artists"][0]["name"]) num = num +1 print(num)''' result = spotify.next(result) #spotify. #spotify.user_playlist_create(clientID, "Python Old", False, "") #albumuri #artist Name and URI #when track was added #track name and URI #From this data, get when album was released, get genres, #username -> playlists -> songs -> songs contain all the data about genres and artists, etc. '''data = songs
class AuthTestSpotipy(unittest.TestCase): """ These tests require user authentication - provide client credentials using the following environment variables :: 'SPOTIPY_CLIENT_USERNAME' 'SPOTIPY_CLIENT_ID' 'SPOTIPY_CLIENT_SECRET' 'SPOTIPY_REDIRECT_URI' """ playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx" four_tracks = [ "spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB", "4VrWlk8IQxevMvERoX08iC", "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf" ] two_tracks = [ "spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB" ] other_tracks = [ "spotify:track:2wySlB6vMzCbQrRnNGOYKa", "spotify:track:29xKs5BAHlmlX1u4gzQAbJ", "spotify:track:1PB7gRWcvefzu7t3LJLUlf" ] bad_id = 'BAD_ID' @classmethod def setUpClass(self): missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV)) if missing: raise Exception( 'Please set the client credentials for the test application using the following environment variables: {}' .format(CCEV.values())) self.username = os.getenv(CCEV['client_username']) self.scope = ('playlist-modify-public ' 'user-library-read ' 'user-follow-read ' 'user-library-modify ' 'user-read-private ' 'user-top-read') self.token = prompt_for_user_token(self.username, scope=self.scope) self.spotify = Spotify(auth=self.token) def test_track_bad_id(self): try: track = self.spotify.track(self.bad_id) self.assertTrue(False) except SpotifyException: self.assertTrue(True) def test_basic_user_profile(self): user = self.spotify.user(self.username) self.assertTrue(user['id'] == self.username.lower()) def test_current_user(self): user = self.spotify.current_user() self.assertTrue(user['id'] == self.username.lower()) def test_me(self): user = self.spotify.me() self.assertTrue(user['id'] == self.username.lower()) def test_user_playlists(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 5) def test_user_playlist_tracks(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) for playlist in playlists['items']: user = playlist['owner']['id'] pid = playlist['id'] results = self.spotify.user_playlist_tracks(user, pid) self.assertTrue(len(results['items']) >= 0) def user_playlist_tracks(self, user, playlist_id=None, fields=None, limit=100, offset=0): # known API issue currently causes this test to fail # the issue is that the API doesn't currently respect the # limit paramter self.assertTrue(len(playlists['items']) == 5) def test_current_user_saved_tracks(self): tracks = self.spotify.current_user_saved_tracks() self.assertTrue(len(tracks['items']) > 0) def test_current_user_saved_albums(self): albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) > 0) def test_current_user_playlists(self): playlists = self.spotify.current_user_playlists(limit=10) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 10) def test_user_playlist_follow(self): self.spotify.user_playlist_follow_playlist('plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertTrue(follows[0], 'is following') self.spotify.user_playlist_unfollow('plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertFalse(follows[0], 'is no longer following') def test_current_user_save_and_unsave_tracks(self): tracks = self.spotify.current_user_saved_tracks() total = tracks['total'] self.spotify.current_user_saved_tracks_add(self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total - total == len(self.four_tracks)) tracks = self.spotify.current_user_saved_tracks_delete( self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total == total) def test_categories(self): response = self.spotify.categories() self.assertTrue(len(response['categories']) > 0) def test_category_playlists(self): response = self.spotify.categories() for cat in response['categories']['items']: cat_id = cat['id'] response = self.spotify.category_playlists(category_id=cat_id) if len(response['playlists']["items"]) > 0: break self.assertTrue(True) def test_new_releases(self): response = self.spotify.new_releases() self.assertTrue(len(response['albums']) > 0) def test_featured_releases(self): response = self.spotify.featured_playlists() self.assertTrue(len(response['playlists']) > 0) def test_current_user_follows(self): response = self.spotify.current_user_followed_artists() artists = response['artists'] self.assertTrue(len(artists['items']) > 0) def test_current_user_top_tracks(self): response = self.spotify.current_user_top_tracks() items = response['items'] self.assertTrue(len(items) > 0) def test_current_user_top_artists(self): response = self.spotify.current_user_top_artists() items = response['items'] self.assertTrue(len(items) > 0) def get_or_create_spotify_playlist(self, playlist_name): playlists = self.spotify.user_playlists(self.username) while playlists: for item in playlists['items']: if item['name'] == playlist_name: return item['id'] playlists = self.spotify.next(playlists) playlist = self.spotify.user_playlist_create(self.username, playlist_name) playlist_id = playlist['uri'] return playlist_id def test_user_playlist_ops(self): # create empty playlist playlist_id = self.get_or_create_spotify_playlist( 'spotipy-testing-playlist-1') # remove all tracks from it self.spotify.user_playlist_replace_tracks(self.username, playlist_id, []) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 0) self.assertTrue(len(playlist['tracks']['items']) == 0) # add tracks to it self.spotify.user_playlist_add_tracks(self.username, playlist_id, self.four_tracks) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 4) self.assertTrue(len(playlist['tracks']['items']) == 4) # remove two tracks from it self.spotify.user_playlist_remove_all_occurrences_of_tracks( self.username, playlist_id, self.two_tracks) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 2) self.assertTrue(len(playlist['tracks']['items']) == 2) # replace with 3 other tracks self.spotify.user_playlist_replace_tracks(self.username, playlist_id, self.other_tracks) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 3) self.assertTrue(len(playlist['tracks']['items']) == 3)
import spotipy.util as util from spotipy import Spotify from youtube_dl import YoutubeDL import os username = os.environ.get('SPOTIFY_USERNAME') scope = 'user-library-read' client_id = os.environ.get('CLIENT_ID') client_secret = os.environ.get('CLIENT_SECRET') token = util.prompt_for_user_token(username, scope, client_id=client_id, client_secret=client_secret,\ redirect_uri='http://localhost:5000') sp = Spotify(auth=token) saved_tracks = sp.current_user_saved_tracks(limit=20) songs = list() while saved_tracks: for track in saved_tracks['items']: name = track['track']['name'] artists = list() for artist in track['track']['artists']: artists.append(artist['name']) songs.append(name + ' ' + ' '.join(artists)) print('done....') if saved_tracks['next']: saved_tracks = sp.next(saved_tracks) else: saved_tracks = None
class SpotifySubscriber: """ Main class. Description necessary. Default storage dir: ../storage """ def __init__( self, user_id: str = None, storage_dir: str = os.path.join( os.path.dirname(os.path.abspath(__file__)), "storage"), ): self.user_id: str = user_id # Playlists are stored by ID in dictionaries for quick lookup self.user_playlists: dict = {} self.followed_playlists: dict = {} self.subscribed_playlists: dict = {} # This is the playlist in which all new songs will be pushed self.subscription_feed: SubscriptionFeed = None loaded = False save_path = os.path.join(storage_dir, "storage.p") # If a save file exists, load it. if os.path.isfile(save_path): self._load(save_path) loaded = True # Since we need the user_id, we cannot continue if it was not specified and we did not obtain it from a save file. elif user_id is None: raise Exception( "No save file found and no user_id specified! Please specify user_id." ) # If there is no save file, there may not be a storage directory either elif not os.path.isdir(storage_dir): os.mkdir(storage_dir) # We deliberately set these after loading, so they may be updated if we move the save file to a different location. self.storage_dir = storage_dir self._save_path = save_path self._cache_path = os.path.join(self.storage_dir, ".cache-{}".format(self.user_id)) self._feed_log_path = os.path.join(self.storage_dir, "feed_log.npy") # If no save file exists, load the client secret and ID from file so that we can request a token. if not loaded: self._load_client_secrets() # Refresh token and create spotify object self.token = self._get_token(self.user_id) self.sp = Spotify(auth=self.token) # If not loaded from save file, perform initial setup if not loaded: if self.user_id != "jneeven": self._follow_user("jneeven") self.subscription_feed = SubscriptionFeed(self.sp, self.user_id) self.refresh_user_playlists() self._save() # Load a token from the cache or request one from the spotify API. Will open the browser to ask for permission if necessary. def _get_token(self, username: str): """ Alternatively, I can set these variables using the command line too: export SPOTIPY_CLIENT_ID='your-spotify-client-id' export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret' export SPOTIPY_REDIRECT_URI='your-app-redirect-url' """ scopes = " ".join([ "user-read-recently-played", "user-library-modify", "playlist-read-private", "playlist-modify-public", "playlist-modify-private", "user-library-read", "playlist-read-collaborative", "user-read-playback-state", "user-follow-read", "user-top-read", "user-read-currently-playing", "user-follow-modify", ]) token = sp_util.prompt_for_user_token( username, scopes, client_id=self._client_id, client_secret=self._client_secret, redirect_uri="http://localhost", cache_path=self._cache_path, ) return token # Load the client secret and ID from client_data.json def _load_client_secrets(self): data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "client_data.json") if not os.path.isfile(data_path): raise FileNotFoundError( "client_data.json does not exist in src folder!") with open(data_path, "r") as data_file: info = json.load(data_file) self._client_id = info["client_id"] self._client_secret = info["client_secret"] # Save the entire object to a storage file def _save(self): pickle.dump(self, open(self._save_path, "wb")) # Load the storage file and overwrite the current object attributes def _load(self, save_path: str): load_obj = pickle.load(open(save_path, "rb")) # Overwrite own attributes with the ones we just loaded for attr, val in load_obj.__dict__.items(): self.__dict__[attr] = val # Obtain the playlists the user owns or follows (both private and public) def refresh_user_playlists(self): self.user_playlists = {} self.followed_playlists = {} # Only obtains 50 playlists at a time, is API limit playlists_data = self.sp.user_playlists(self.user_id) while playlists_data: for playlist in playlists_data["items"]: # If we own a playlist but it's collaborative, we treat it as a followed one since we might be interested in updates. if playlist["owner"]["id"] != self.user_id or playlist[ "collaborative"]: self.followed_playlists[playlist["id"]] = playlist else: self.user_playlists[playlist["id"]] = playlist # If there were more playlists than we just received, query the next batch if playlists_data["next"]: playlists_data = self.sp.next(playlists_data) else: break # Subscribe to multiple playlists based on their ID or patterns in their names. def subscribe_to_playlists(self, playlist_ids: list = [], contains: list = []): new_subscriptions = False for playlist_id in playlist_ids: if playlist_id not in self.followed_playlists.keys(): raise Exception( "Cannot subscribe to playlist with id {}. Either the user owns it or does not follow it." .format(playlist_id)) if playlist_id not in self.subscribed_playlists.keys(): playlist = self.followed_playlists[playlist_id] tracks = self._get_playlist_tracks(playlist["owner"]["id"], playlist_id) self.subscribed_playlists[playlist_id] = SubscribedPlaylist( playlist, tracks) new_subscriptions = True safe_print("Subscribed to playlist {} by {}".format( playlist["name"], playlist["owner"]["id"])) # Lowercase pattern matching with the playlist name for pattern in contains: pattern = pattern.lower() for playlist in self.followed_playlists.values(): if (pattern in playlist["name"].lower() and playlist["id"] not in self.subscribed_playlists.keys()): tracks = self._get_playlist_tracks(playlist["owner"]["id"], playlist["id"]) self.subscribed_playlists[ playlist["id"]] = SubscribedPlaylist(playlist, tracks) new_subscriptions = True safe_print("Subscribed to playlist {} by {}".format( playlist["name"], playlist["owner"]["id"])) # Only save if we actually changed something. TODO: Save these in a separate file. if new_subscriptions: self._save() # Unsubscribe from multiple playlists based on their ID or patterns in their names. def unsubscribe_from_playlists(self, playlist_ids: list = [], contains: list = []): removed_subscriptions = False for playlist_id in playlist_ids: if playlist_id not in self.subscribed_playlists.keys(): raise Exception( "Cannot unsubscribe from playlist with id {}, because the user is not subscripted to it." .format(playlist_id)) playlist = self.subscribed_playlists[playlist_id] removed_subscriptions = True safe_print("Unsubscribed from playlist {} by {}".format( playlist.name, playlist.owner_id)) del self.subscribed_playlists[playlist_id] # Lowercase pattern matching with the playlist name subscribed_playlists = list(self.subscribed_playlists.values()) for pattern in contains: pattern = pattern.lower() for playlist in subscribed_playlists: if pattern in playlist.name.lower(): removed_subscriptions = True safe_print("Unsubscribed from playlist {} by {}".format( playlist.name, playlist.owner_id)) del self.subscribed_playlists[playlist.id] # Only save if we actually changed something. TODO: Save these in a separate file. if removed_subscriptions: self._save() # Print an overview of the playlist the user owns, follows and is subscribed to. def print_playlists(self, own=False, follow=False, subscribed=True): if own: safe_print("Own playlists:") for playlist in self.user_playlists.values(): safe_print(playlist["name"]) if follow: safe_print("\nFollowed playlists:") for playlist in self.followed_playlists.values(): safe_print(playlist["name"], playlist["owner"]["id"]) if subscribed: safe_print("\nCurrently subscribed to the following playlists:") for playlist in self.subscribed_playlists.values(): safe_print(playlist) safe_print() # Check the subscribed playlists for new songs and add them to the feed list. def update_feed(self, add_own=False): """ Add_own denotes whether to add songs that the user added to a playlist themselves. This may happen for example in collaborative playlists. """ last_update = self.subscription_feed.last_update track_ids = [] num_added_tracks = 0 for playlist_id, playlist in self.subscribed_playlists.items(): # safe_print("Checking playlist {}".format(playlist.name)) new_tracks, snapshot = self._get_playlist_tracks( playlist.owner_id, playlist_id, min_timestamp=last_update, compare_snapshot=playlist.snapshot_id, return_snapshot=True, ) # Update the playlist snapshot so that we quickly know if it has changed next time playlist.snapshot_id = snapshot added = 0 for track in new_tracks: if track.id == None: print(f"Track with id None: {track}") if add_own or track.added_by != self.user_id: try: # Only add the track if it wasn't already in the list when we subbed if track.id not in playlist.track_ids.keys(): track_ids.append(track.id) # Add the ID to the track ID list so we know not to add it in the future playlist.track_ids[track.id] = datetime.utcnow() added += 1 # TODO: correctly upgrade objects if storage consists of SubscribedPlaylists without ID list. except AttributeError: track_ids.append(track.id) added += 1 if added > 0: safe_print("Obtained {} new tracks from playlist {}!".format( added, playlist.name)) if len(track_ids) > 0: unique_ids = np.unique(track_ids) # If a feed log exists, filter all track IDs that have already been added to the feed before. if os.path.exists(self._feed_log_path): feed_log = pickle.load(open(self._feed_log_path, "rb")) filtered_indices = np.where( ~np.isin(unique_ids, feed_log["track_ids"])) unique_ids = unique_ids[filtered_indices] # We can add at most 100 tracks to a playlist in a single request. if unique_ids.size <= 100: self.sp.user_playlist_add_tracks(self.user_id, self.subscription_feed.id, unique_ids) else: # Split array into near-equal sections that are smaller than 100 tracks for id_array in np.array_split( unique_ids, np.ceil(unique_ids.size / 100).astype(int)): self.sp.user_playlist_add_tracks(self.user_id, self.subscription_feed.id, id_array) num_added_tracks = unique_ids.size self._log_feed_updates(unique_ids) # Update the timestamp and save to file self.subscription_feed.last_update = datetime.utcnow() self._save() return num_added_tracks # Get all tracks in the specified playlist, added after min_timestamp. # If snapshot is provided, ignore playlists of which the snapshot hasn't changed. def _get_playlist_tracks( self, playlist_owner_id: str, playlist_id: str, min_timestamp: datetime = None, compare_snapshot: str = None, return_snapshot=False, ): data = self.sp.user_playlist(playlist_owner_id, playlist_id, fields="tracks, snapshot_id") return_tracks = [] if not min_timestamp: min_timestamp = datetime.fromtimestamp(0) # If the snapshot is still the same, there is nothing interesting for us to see. # NOTE: certain playlists like 'Brain Food' seem to have a different snapshot every time. if compare_snapshot and data["snapshot_id"] == compare_snapshot: # safe_print("Snapshot still the same, ignoring list contents.") return [], data["snapshot_id"] tracks = data["tracks"] while tracks: for track in tracks["items"]: added_at = track["added_at"] # Somehow, it's possible that we receive an empty track. IDK if this is a spotipy bug or what if track["track"] is None or track["track"]["id"] is None: print("WARNING: encountered None track! Ignoring.") continue timestamp = datetime.strptime(added_at, "%Y-%m-%dT%H:%M:%SZ") if timestamp > min_timestamp: return_tracks.append(Track(track, playlist_id)) # safe_print("Found track with name {} and timestamp {} ( > {})".format(track_name, timestamp, min_timestamp)) if tracks["next"]: tracks = self.sp.next(tracks) else: break if return_snapshot: return return_tracks, data["snapshot_id"] return return_tracks # Get all tracks from the users library and own playlists (including sub feed). def _get_all_user_tracks(self): tracks = {} self._get_user_library_tracks() for playlist_id, playlist in self.user_playlists.items(): tracks = self._get_playlist_tracks(playlist["owner"]["id"], playlist_id) for track in tracks: tracks[track.id] = track def _get_user_library_tracks(self): tracks = {} data = self.sp.current_user_saved_tracks() while data: for track in data["items"]: print(track["track"].keys()) exit(0) exit(0) # for playlist in playlists_data['items']: # # If we own a playlist but it's collaborative, we treat it as a followed one since we might be interested in updates. # if playlist['owner']['id'] != self.user_id or playlist['collaborative']: # self.followed_playlists[playlist['id']] = playlist # else: # self.user_playlists[playlist['id']] = playlist # # If there were more playlists than we just received, query the next batch # if playlists_data['next']: # playlists_data = self.sp.next(playlists_data) # else: # break # Store the track ids we just added to the feed in the log file. def _log_feed_updates(self, track_ids: np.ndarray): """ Log is dict with the following info: track_ids: numpy array of track ids that were added to the feed timestamps: timestamp at which each of the tracks was added to the feed """ if os.path.exists(self._feed_log_path): feed_log = pickle.load(open(self._feed_log_path, "rb")) else: feed_log = {"track_ids": [], "timestamps": []} # Create, append and overwrite timestamps new_timestamps = np.repeat(datetime.utcnow(), track_ids.size) timestamp_array = np.append(feed_log["timestamps"], new_timestamps) feed_log["timestamps"] = timestamp_array # Append and overwrite track_ids track_id_array = np.append(feed_log["track_ids"], track_ids) feed_log["track_ids"] = track_id_array pickle.dump(feed_log, open(self._feed_log_path, "wb")) # Print the tracks and timestamps saved in the feed log. def print_feed_log(self): if not os.path.exists(self._feed_log_path): print("No feed log exists yet!") return feed_log = pickle.load(open(self._feed_log_path, "rb")) num_tracks = len(feed_log["track_ids"]) print("Found {} tracks in log.".format(num_tracks)) batch_size = 50 tracks = [] start_idx = 0 print("Requesting track info...") for start_idx in tqdm(range(0, num_tracks, batch_size)): end_idx = start_idx + batch_size track_ids = feed_log["track_ids"][start_idx:end_idx] tracks += self.sp.tracks(track_ids)["tracks"] start_idx = end_idx for track, timestamp in zip(tracks, feed_log["timestamps"]): safe_print("{} - {} - {} - {}".format(track["artists"][0]["name"], track["name"], timestamp, track["id"])) # Follow a user def _follow_user(self, username: str): self.sp.user_follow_users([username]) """ STUBS
class SpotifyTracks: def __init__(self): self.spotify = Spotify(auth=client_manager.get_token) def get_cleaned_tracks_data(self, results): ''' Get useful information from the results Parameters: - results: json/dictionary containing tracks data ''' songs = [] for item in results['items']: track = item['track'] if 'track' in item else item album = track['album']['name'] artist = track['artists'][0]['name'] title = track['name'] image = track['album']['images'][0]['url'] songs.append(Song(title, artist, album, image)) return songs def get_playlist_tracks(self, playlist_id): ''' Get a list of tracks of a playlist. Parameters: - playlist_id: the id of the playlist ''' query = f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks" response = requests.get(url=query, headers={ "Content-Type": "application/json", "Authorization": f'Bearer {client_manager.get_token}' }) results = response.json() return self.get_cleaned_tracks_data(results) def get_user_saved_tracks(self, limit=10): ''' Get a list of the user saved tracks. Parameters: - playlist_id: the id of the playlist - limit: the number of tracks to return (max=50, default=10) ''' offset = 0 saved_tracks = [] while offset < limit: results = self.spotify.current_user_saved_tracks(limit=50, offset=offset) partial_results = self.get_cleaned_tracks_data(results) if not partial_results: break saved_tracks += partial_results offset += 50 return saved_tracks def search_track(self, artist_name, song_name): ''' Get a particular track info Parameters: - artist_name: the name of the artist - song_name: the name of the song ''' results = self.spotify.search( q=f'artist:{artist_name} track:{song_name}', type='track', limit=1) return self.get_cleaned_tracks_data(results['tracks'])
class SpotifyTracks: def __init__(self): self.spotify = Spotify(auth=token) def get_cleaned_track_data(self, item: dict) -> Union[Song, None]: ''' Get required useful information from the results Eg: Title, artist, album, imgurl Parameters: - item: json/dictionary containing track data ''' try: track = item['track'] if 'track' in item else item album = track['album']['name'] artist = track['artists'][0]['name'] title = track['name'] imgurl = track['album']['images'][0]['url'] return Song(vidurl=None, title=title, artist=artist, album=album, imgurl=imgurl) except Exception as e: print(e) return None def get_playlist_tracks(self, playlist_id: str, limit: int = None) -> Iterator[Song]: ''' Get a list of tracks of a playlist. Parameters: - playlist_id: the id of the playlist ''' offset = 0 if limit is None: limit = 10000 fetched = 0 while offset < limit: query = f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks?offset={offset}&limit=50" response = requests.get(url=query, headers={ "Content-Type": "application/json", "Authorization": f'Bearer {token}' }) results = response.json() if not response.ok: break if "items" not in results or not results["items"]: return for item in results['items']: fetched += 1 yield self.get_cleaned_track_data(item) if fetched >= limit: return offset += 50 def get_user_saved_tracks(self, limit: int = None) -> Iterator[Song]: ''' Get a list of the user saved tracks. Parameters: - playlist_id: the id of the playlist - limit: the number of tracks to return. defualt: gets all liked songs ''' offset = 0 if limit is None: limit = 10000 fetched = 0 while offset < limit: results = self.spotify.current_user_saved_tracks(offset=offset, limit=50) if "items" not in results or not results["items"]: return for item in results['items']: fetched += 1 yield self.get_cleaned_track_data(item) if fetched >= limit: return offset += 50 def search_track(self, artist_name: str, song_name: str) -> Song: ''' Get a particular track info Parameters: - artist_name: the name of the artist - song_name: the name of the song ''' results = self.spotify.search( q=f'artist:{artist_name} track:{song_name}', type='track', limit=1) return self.get_cleaned_track_data(results['tracks']['items'][0])
class AuthTestSpotipy(unittest.TestCase): """ These tests require user authentication - provide client credentials using the following environment variables :: 'SPOTIPY_CLIENT_USERNAME' 'SPOTIPY_CLIENT_ID' 'SPOTIPY_CLIENT_SECRET' 'SPOTIPY_REDIRECT_URI' """ playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx" playlist_new_id = "spotify:playlist:7GlxpQjjxRjmbb3RP2rDqI" four_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB", "4VrWlk8IQxevMvERoX08iC", "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"] two_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB"] other_tracks = ["spotify:track:2wySlB6vMzCbQrRnNGOYKa", "spotify:track:29xKs5BAHlmlX1u4gzQAbJ", "spotify:track:1PB7gRWcvefzu7t3LJLUlf"] album_ids = ["spotify:album:6kL09DaURb7rAoqqaA51KU", "spotify:album:6RTzC0rDbvagTSJLlY7AKl"] bad_id = 'BAD_ID' @classmethod def setUpClass(self): missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV)) if missing: raise Exception( ('Please set the client credentials for the test application' ' using the following environment variables: {}').format( CCEV.values())) self.username = os.getenv(CCEV['client_username']) self.scope = ( 'playlist-modify-public ' 'user-library-read ' 'user-follow-read ' 'user-library-modify ' 'user-read-private ' 'user-top-read ' 'user-follow-modify' ) self.token = prompt_for_user_token(self.username, scope=self.scope) self.spotify = Spotify(auth=self.token) def test_track_bad_id(self): try: self.spotify.track(self.bad_id) self.assertTrue(False) except SpotifyException: self.assertTrue(True) def test_basic_user_profile(self): user = self.spotify.user(self.username) self.assertTrue(user['id'] == self.username.lower()) def test_current_user(self): user = self.spotify.current_user() self.assertTrue(user['id'] == self.username.lower()) def test_me(self): user = self.spotify.me() self.assertTrue(user['id'] == self.username.lower()) def test_user_playlists(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 5) def test_user_playlist_tracks(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) for playlist in playlists['items']: user = playlist['owner']['id'] pid = playlist['id'] results = self.spotify.user_playlist_tracks(user, pid) self.assertTrue(len(results['items']) >= 0) # known API issue currently causes this test to fail # the issue is that the API doesn't currently respect the # limit parameter # def user_playlist_tracks(self, user, playlist_id=None, fields=None, # limit=100, offset=0): # self.assertTrue(len(playlists['items']) == 5) def test_current_user_saved_albums(self): # List albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) == 1) # Add self.spotify.current_user_saved_albums_add(self.album_ids) # Contains self.assertTrue( self.spotify.current_user_saved_albums_contains( self.album_ids) == [ True, True]) # Remove self.spotify.current_user_saved_albums_delete(self.album_ids) albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) == 1) def test_current_user_playlists(self): playlists = self.spotify.current_user_playlists(limit=10) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 10) def test_user_playlist_follow(self): self.spotify.user_playlist_follow_playlist( 'plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [ self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertTrue(follows[0], 'is following') self.spotify.user_playlist_unfollow( 'plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [ self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertFalse(follows[0], 'is no longer following') def test_current_user_saved_tracks(self): tracks = self.spotify.current_user_saved_tracks() self.assertTrue(len(tracks['items']) > 0) def test_current_user_save_and_unsave_tracks(self): tracks = self.spotify.current_user_saved_tracks() total = tracks['total'] self.spotify.current_user_saved_tracks_add(self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total - total == len(self.four_tracks)) tracks = self.spotify.current_user_saved_tracks_delete( self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total == total) def test_categories(self): response = self.spotify.categories() self.assertTrue(len(response['categories']) > 0) def test_category_playlists(self): response = self.spotify.categories() for cat in response['categories']['items']: cat_id = cat['id'] response = self.spotify.category_playlists(category_id=cat_id) if len(response['playlists']["items"]) > 0: break self.assertTrue(True) def test_new_releases(self): response = self.spotify.new_releases() self.assertTrue(len(response['albums']) > 0) def test_featured_releases(self): response = self.spotify.featured_playlists() self.assertTrue(len(response['playlists']) > 0) def test_current_user_follows(self): response = self.spotify.current_user_followed_artists() artists = response['artists'] self.assertTrue(len(artists['items']) > 0) def test_current_user_top_tracks(self): response = self.spotify.current_user_top_tracks() items = response['items'] self.assertTrue(len(items) > 0) def test_current_user_top_artists(self): response = self.spotify.current_user_top_artists() items = response['items'] self.assertTrue(len(items) > 0) def get_or_create_spotify_playlist(self, playlist_name): playlists = self.spotify.user_playlists(self.username) while playlists: for item in playlists['items']: if item['name'] == playlist_name: return item['id'] playlists = self.spotify.next(playlists) playlist = self.spotify.user_playlist_create( self.username, playlist_name) playlist_id = playlist['uri'] return playlist_id def test_user_playlist_ops(self): sp = self.spotify # create empty playlist playlist_id = self.get_or_create_spotify_playlist( 'spotipy-testing-playlist-1') # remove all tracks from it sp.user_playlist_replace_tracks( self.username, playlist_id, []) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 0) self.assertTrue(len(playlist['tracks']['items']) == 0) # add tracks to it sp.user_playlist_add_tracks( self.username, playlist_id, self.four_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 4) self.assertTrue(len(playlist['tracks']['items']) == 4) # remove two tracks from it sp.user_playlist_remove_all_occurrences_of_tracks(self.username, playlist_id, self.two_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 2) self.assertTrue(len(playlist['tracks']['items']) == 2) # replace with 3 other tracks sp.user_playlist_replace_tracks(self.username, playlist_id, self.other_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 3) self.assertTrue(len(playlist['tracks']['items']) == 3) def test_playlist(self): # New playlist ID pl = self.spotify.playlist(self.playlist_new_id) self.assertTrue(pl["tracks"]["total"] > 0) # Old playlist ID pl = self.spotify.playlist(self.playlist) self.assertTrue(pl["tracks"]["total"] > 0) def test_user_follows_and_unfollows_artist(self): # Initially follows 1 artist res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 1) # Follow 2 more artists artists = ["6DPYiyq5kWVQS4RGwxzPC7", "0NbfKEOTQCcwd6o7wSDOHI"] self.spotify.user_follow_artists(artists) res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 3) # Unfollow these 2 artists self.spotify.user_unfollow_artists(artists) res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 1) def test_user_follows_and_unfollows_user(self): # TODO improve after implementing `me/following/contains` users = ["11111204", "xlqeojt6n7on0j7coh9go8ifd"] # Follow 2 more users self.spotify.user_follow_users(users) # Unfollow these 2 users self.spotify.user_unfollow_users(users)
# ------------------------------------------------------------------------ # Setup # ------------------------------------------------------------------------ external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] username = os.getenv("SPOTIFY_USER_ID") SCOPE = "user-library-read" token = util.prompt_for_user_token(username, SCOPE) sp = Spotify(token) app = dash.Dash(__name__, external_stylesheets=external_stylesheets, title="Reminiscify") r = sp.current_user_saved_tracks() df = get_liked_songs(sp) # df = pd.read_csv("liked_songs.csv") df["Added At"] = pd.to_datetime(df["Added At"]) df["Day Num"] = df["Added At"].dt.weekday df["Day"] = df["Added At"].dt.day_name() days_df = df.groupby(["Day Num", "Day"]).size().reset_index(name="Songs Saved") fig = px.bar(days_df, x="Day", y="Songs Saved") # ------------------------------------------------------------------------ # Layout # ------------------------------------------------------------------------ app.layout = html.Div([