stuff

How to get now playing album art from Spotify API

In a previous post, I hacked this by getting the album artwork jpg from my amplifier. Unfortunately, upgrading to a new amp ‘improved’ its operating system and the artwork vanished, so I had to bite the bullet to get it straight from Spotify. This is far more painful than it needs to be and Spotify could do better (see footnote).

So, with a bit of help (actually a lot of help) from ChatGPT-4* and many rabbit holes explored, I’ve now got a call directly to the Spotify API of “now playing”.

To get going, set up a developer account in Spotify, create a new app and then get hold of

  • Client ID
  • Client secret

You must also set a Redirect URIs because Spotify requires you to use a web browser to authenticate: it’s not possible to do entirely on the command line (no, you can’t use CURL either, apparently).

This means you need to be able to put a web page somewhere that you can access. I decided to use one of my public web servers, so I can get the image from anywhere, not just at home, but you can use localhost.

Luckily there’s a Python library that does things. So, install python

pip install spotipy

Also, to use this we also need to add it to a Python virtual environment using this:

pip install virtualenv
python3 -m venv myenv
source myenv/bin/activate  
pip freeze > requirements.txt

And using source myenv/bin/activate to recall.

After many iterations (thanks ai), we get to a script:

get_spot.py

import spotipy
from spotipy.oauth2 import SpotifyOAuth

# Add your credentials here
SPOTIPY_CLIENT_ID = 'your_spotify_client_id'
SPOTIPY_CLIENT_SECRET = 'your_spotify_client_secret'
SPOTIPY_REDIRECT_URI = 'your_redirect_uri'
USERNAME = 'your_username'
SCOPE = "user-read-currently-playing"

# Create Spotify object with permissions
oauth = SpotifyOAuth(client_id=SPOTIPY_CLIENT_ID,
                     client_secret=SPOTIPY_CLIENT_SECRET,
                     redirect_uri=SPOTIPY_REDIRECT_URI,
                     username=USERNAME,
                     scope=SCOPE,
                     open_browser=False)

token_info = oauth.get_cached_token()

# Check if token needs to be refreshed
if token_info and oauth.is_token_expired(token_info):
    token_info = oauth.refresh_access_token(token_info['refresh_token'])

sp = spotipy.Spotify(auth_manager=oauth)

# Get current playing
current_playing = sp.current_user_playing_track()

# Ensure that a track is playing
if current_playing is not None:
    # Get the album art
    album_art = current_playing['item']['album']['images'][0]['url']
    print(album_art)
else:
    print("No track is currently playing.")

When you first run this, using

python get_spot.py

it’ll give you a URL to copy/paste into a browser so you can authenticate.

You need to have this as a web page at the RedirectURI

<div id="authorizationCode"></div>
  <script>
    function handleAuthorizationCode() {
      const queryString = window.location.search;
      const urlParams = new URLSearchParams(queryString);
      const authorizationCode = urlParams.get('code');

      const authorizationCodeDiv = document.getElementById('authorizationCode');
      if (authorizationCode) {
        authorizationCodeDiv.innerHTML = `${authorizationCode}`;
        // Do something with the code (e.g., store it for later use)
        // You can also make a POST request to your server to exchange it for an access token
      } else {
        authorizationCodeDiv.innerHTML = 'No authorization code found in the URL.';
        // Handle the case when the user denies permission or other errors
      }
    }

    handleAuthorizationCode();
  </script>

Then access it, via <your URL>/<file>?code=<your_ClientID>

You need to copy the ‘return URL‘ (not the code it renders) that it generates and paste it back onto the command line prompt.

After you’ve done that, you can run it again and it’ll (hopefully) give you a URL directly to the artwork on Spotify’s CDN, for example:

https://i.scdn.co/image/ab67616d0000b2737a9230e1ede602936d5056c8

We can then use THAT URL instead of the one I was getting from the amplifier in the previous post.

And, finally, we want to cron this as we did with the local example from last time.

spot.sh (this time, on the server)

#!/bin/bash

folder='/{path}/spotify'
cd $folder

# Activate the virtual environment and get the album art URL
source $folder/myenv/bin/activate
albumart=$(python $folder/get.py)

# Check if the Python script executed successfully
if [ $? -ne 0 ]; then
  echo "Error: Failed to execute the Python script."
  echo "empty" > status.flag
  exit 1
fi

# Download the album art, or exit with an error message if curl fails
if ! curl -o nowtmp.jpg $albumart; then
  echo "Failed to download album art"
  echo "empty" > status.flag
  exit 1 
fi

# Move the temporary image to 'now.jpg'
mv nowtmp.jpg now.jpg

file now.jpg | awk '{print $2}' > status.flag

# Compute the MD5 checksums of the current and live images
current_checksum=$(md5sum now.jpg  | awk '{print $1}')
live_checksum=$(md5sum live.jpg | awk '{print $1}')

# If the checksums differ, update the live image
if [ "$current_checksum"  != "$live_checksum" ]; then
  cp now.jpg live.jpg
fi 

Then, on the pi, we can do this:

up-spot.sh

#!/bin/bash
cd /{path}/hcm

server=`curl -k https://{server}/{folder}/status.flag`
echo server $server

if [  "$server" = "JPEG" ]; then
 if [ `cat status.flag` = "no" ]; then 
	 echo "yes" > status.flag
  	 cp album-cover-live-safe.html album-cover.html
	 ./refresh.sh
 fi	 
 else 
 if [ `cat status.flag` = "yes" ]; then	 
	 echo "no" > status.flag
 	 cp clock-analogue.html album-cover.html
	 ./refresh.sh
 fi
fi

and put that into cron on the pi with:

*       *       *       *       *       /{path}/up-spot.sh > /dev/null 2>&1

and then

test-refresh.sh becomes

#!/bin/bash
cd /{local path}
hifi=`cat hifi.ip`
albumart="https://{server}/{path}/live.jpg"

nowtmp="nowtmp.jpg"
curl -k $albumart > $nowtmp
wait 

if [ -f "$nowtmp" ]; then 
# echo `date` "file exists: " `du -sk $nowtmp` "and" `md5sum $nowtmp` >> log
 mv nowtmp.jpg now.jpg # do as a step in case latency leads to partial download tests
 x=`md5sum now.jpg  | awk '{print $1}'`
 y=`md5sum live.jpg | awk '{print $1}'`


 if [ "$x" != "$y" ] ; then
#   echo "updating" `date`  $x $y >> log
  cp now.jpg live.jpg
   ./refresh.sh
 fi

fi

See the previous post for more on other bits of script and cron.

* I started with “write a shell script to get the ‘now playing’ album art from my spotify account”

Footnote

As an aside, I found that this nice chap has done a *really simple* integration where you just authorise his app and it tells you what’s playing. Unfortunately, it’s not generating the images (just the titles) and I didn’t want to rely on a 3rd party service [hint: Spotify, just make it this easy, good grief!]