pythonspotipy

Problem with adding songs of certain artist to spotify playlist using spotipy


I'm building basic spotipy code that is creating playlist containing top songs of given artist. Code works for some artists, but e.g. for Adele it adds songs that aren't Adele's. Here is part of my code:

def get_songs_uris(self, top_artist_name, top_songs):
    for i in range(len(top_songs)):
        songs_data = self.sp.search(
            q=f"artist: {top_artist_name}, track: {top_songs[i]}",
            type="track", limit=1)
        self.songs_URIs.append(songs_data["tracks"]["items"][0]["uri"])
    return self.songs_URIs

Where top_artist_name="Adele" and top_songs is list of titles of most popular Adele's songs.

Code runs okay, but...

Playlist is created and all songs are added, but e.g. song "Someone Like You" have artist "Mason" instead of "Adele". Some other tracks are indeed Adele's, but not all.


Solution

  • Artist ID

    An artist given may have more than meaning. It could be id (e.g. 4dpARuHxo51G3z768sgnrY) or uri (spotify:artist:4dpARuHxo51G3z768sgnrY). If not and we have only the name, we need to obtain one of those possibly via searching. I presume there should be some user interaction, because Spotify's search is somewhat unpredictable and not always reproducible.

    # client = spotipy.Spotify(...)
    
    # may raise SpotifyException
    response = client.search(q="Adele", type="artist", limit=1)
    artists = response['artists']['items']
    
    # may raise IndexError
    artist = artists[0]
    
    # may raise KeyError
    artist_id = artist['id']
    artist_uri = artist['uri']
    

    Artist's Top Tracks

    Spotify provides an endpoint for acquiring artist's top track (by country) and spotipy has it implemented. Processing is somewhat straightforward:

    country = 'PL'
    
    # may raise SpotifyException
    response = client.artist_top_tracks(artist_id, country)
    tracks = response['tracks']
    
    for track in tracks:
        self.songs_URIs.append(track['uri']
    

    But it's only 10 tracks per an artist and may not meet your needs.

    Filter search results

    Another idea is to try filtering search results of tracks by an artist. Here is a sample implementation of that idea:

    offset = 0
    limit = 50
    artist_tracks = {}  # name => uri
    track_popularity = collections.defaultdict(lambda: -1)  # name => popularity value
    
    while offset < 1000:
        try:
            response = client.search(q="artist:Adele", type="track", 
                                     limit=50, offset=offset)
        except SpotifyException:
            break
    
        try:
            tracks = response['tracks']['items']
        except (TypeError, KeyError):
            break
    
        for track in tracks:
            # skip that tracks that do not relate to an artist we look for
            if not any(artist_id == artist['id'] for artist in track['artists']):
                continue
    
            track_name = track['name']
            if track_popularity[track_name] < track['popularity']:
                track_popularity[track_name] = track['popularity']
                artist_tracks[track_name] = track['uri']
    
        offset += limit
    

    Can apply further actions to the result collected:

    data = [(track_popularity[title], title, artist_tracks[title])
            for title in artist_tracks]
    data.sort(key=lambda item: (-item[0], item[1]))
    for popularity, title, uri in data[:5]:
        print(f"{popularity}: {title} ({uri})")
    

    Sample output:

    86: Set Fire to the Rain (spotify:track:3di5hcvxxciiqwMH1jarhY)
    86: Skyfall (spotify:track:6VObnIkLVruX4UVyxWhlqm)
    84: Easy On Me (spotify:track:0gplL1WMoJ6iYaPgMCL0gX)
    84: Love In The Dark (spotify:track:0DuWDLjriRPjDRoPgaCslY)
    84: Rolling in the Deep (spotify:track:4OSBTYWVwsQhGLF9NHvIbR)
    

    The result resembles a quick reference, however some tracks are missing due to a different Spotify's data. E.g. metadata of Ricsta - Be Divine feat. Adele doesn't hold any connection to Adele. But it could be not relevant to the top songs.