A long-time student of mine and I have been exploring the world of music through the lens of code lately. She is in-between semesters and, like myself, the itch to build things is ever-present. In the previous semester, she had written most of a tkinter
interface to generate some matplotlib
graphs based off of a particular Spotify dataset. It was actually impressive, considering she is not a developer by trade (works in a completely different industry), has never done things like this on her own, and built it completely off of a combo of her own knowledge and StackOverflow entries. I've been guiding her towards her second degree, in Computer Science, and having watched her grow, I can say she has come a long way, and that ANYBODY can write code, even traditionally "non-computer" people! :D
So, the class for that project ended, but we had a lot of fun cleaning up her code, moving things around, and getting it into a state of higher-quality. There are a lot of small-things that come with experience and knowledge and proper education that she had not taken the time to enforce in her own code-writing, such as "proper" variable naming, as well as some knowledge on tkinter
layouts, that ultimately did not impact the outcome or her own ability to just "make something that works", and you don't have to be a pro to get that far. A lot of my own childhood-level programming activities came with the aggression and fervor to simply "do" without considering the long-term impact of poor variable naming choices. Thankfully, her project was not so large that it was not a problem to step through and "clean things up". She could now understand her own code from a different perspective, without having to think in terms of "what does this variable do?", since things have been named closer to "properly".
So, in the spirit of "fun", and in preparation for her next semester, we have begun exploring Spotify's API to see what can be done with it. We came across an interesting function available, get_recommendations
, that we found can and does return track recommends that are present in the original dataset that she was working with. This was a very cool discovery! I typically hate Spotify's UI, but I love programming, and I love making rudimentary, crude text CLIs, so this works out great for puzzle-pieces to a project we have not quite fully thought-out yet, but knowing how things are built, and my own sense of curiosity, we are digging in to see what is what.
Along the way, we have discovered that Spotify uses a weird internal ID system to tag assets in their database: artists, playlists, and tracks, at least, and probably other things as well.
But, it is useful to know, because Spotify's UI provides a means to click and get an artist ID. I'm not sure why an end-user would need this...maybe Spotify has some tool on their site that provides functionality given an artist ID.
But, I didn't want that. I don't wanna have to use Spotify's UI...AT ALL! So, I figured the API should have a way to programmatically get an Artist ID, which I could later pass to the recommendation function.
Ideally, I can type in an artist name, and get their ID back, and that is exactly what I've done so far.
Before you can get that from Spotify however, you have to first authenticate with their API endpoint.
To do THAT, you need to get your own client_id
and client_secret
, encode the string client_id:client_secret
(the ID and secret with a colon between them) in base64, and attach it as part of a header to an API access token request.
First, go here and create a new Spotify application in order to get your own client_id
and client_secret
. Spotify tracks your user requests with some neat graphs, it is kind of cool.
https://developer.spotify.com/dashboard/applications
Next, save the client_id
and client_secret
in their own text files: client_id.txt
and client_secret.txt
.
The following code snippet illustrates how one can use this setup to get an access token from Spotify. You'll need this token in order to request from search
.
import requests as r
from sys import argv, exit
from base64 import b64encode
import json
def get_auth_key():
headers = {}
client_id = ""
client_secret = ""
with open("client_id.txt", "r") as infile:
client_id = infile.read()
client_id = client_id[ 0 : len(client_id) - 1]
with open("client_secret.txt", "r") as infile:
client_secret = infile.read()
client_secret = client_secret[ 0 : len(client_secret) - 1]
client_str = f"{client_id}:{client_secret}"
client_str_bytes = client_str.encode('ascii')
client_str = b64encode( client_str_bytes )
client_str = client_str.decode('ascii')
auth_header = f"Basic {client_str}"
headers['Authorization'] = auth_header
data = {
"grant_type" : "client_credentials"
}
url = "https://accounts.spotify.com/api/token"
myreq = r.post(url, headers=headers, data=data)
status_code = myreq.status_code
content = myreq.content.decode('ascii')
json_data = json.loads(content)
access_token = json_data['access_token']
return access_token
Once you have an access token, you can pass that to the search
quest. Below is an example function that does what is needed to get a parsable response.
def do_request():
artist_name = argv[1]
url = f"https://api.spotify.com/v1/search?type=artist&q={artist_name}"
headers = {
"Accept" : "application/json",
"Content-Type" : "application/json",
}
auth_key = get_auth_key()
headers['Authorization'] = f"Bearer {auth_key}"
myreq = r.get(url, headers=headers)
content = myreq.content
status_code = myreq.status_code
if status_code != 200:
print("Error: status code:", status_code)
exit(-1)
json_data = json.loads(content)
#json_str = json.dumps(json_data, indent=2)
#print(json_str)
return json_data
Once you have the JSON response, you can format it as you wish. Here is an example of how to format the response to list search results with artist name, spotify ID, and genre list all on their own lines.
def format_json_data(json_data):
artists = json_data['artists']
items = artists['items']
for item in items:
name = item['name']
artist_id = item['id']
genres = ",".join( item['genres'] )
if genres=="":
continue
print(name, artist_id, genres)
The last part of this script is just to make sure the user passes an artist name on the command line.
def check_params():
if len(argv)!=2:
print("Usage: python3 search.py <artist>")
exit(-1)
def main():
check_params()
json_data = do_request()
format_json_data( json_data )
if __name__ == "__main__":
main()
This script is located at https://github.com/mikedesu/spotify_test/blob/master/search.py, and will be part of a growing set of programmatic Spotify operations, among other things, that may or may not be used to build cool things involving music.
Very soon, this script will be combined with the test.py
in the same repo in order to pull down recommendations given an artist name. Spotify can actually process up to 5 seed artists in such a request, and that example can be easily extended.
I need coffee before our meeting. Take it easy until next time!
Need help with Python or some other programming language? Hit me up on Wyzant
Top comments (0)