SMOLNET PORTAL home about changes
back to gemlog

Displaying your currently playing Spotify song in your gemini capsule using only bash

18.02.23
see what I'm currently listening to! (diptera.casa)


Hey there! I've been busy this week figuring out how to do this thing and I got it to work in the end, so here's how I did it. An important thing to remember is that I'm no expert in these matters, so there will be better ways to do this, but this way also works.

I am just that determined to not learn any programming language


Step 1/2: Get refreshing Access Token

This part is excellently documented on the Spotify developers site, but I don't understand JavaScript and so I did it in bash.

Official Spotify Guide to Authorization Code Flow (https://developer.spotify.com)


Spotify Developers account

To gain access to your currently playing track, you need a Spotify account (wow). If you've got that, you should follow the following link, making a developers account:

https://developers.spotify.com/dashboard (https://developers.spotify.com)


After logging in you will need to create an App and add a redirect URI to it under the green settings button. For our purposes this uri can be anything, the only important part here is consistency later on.

From this page you will later need the following information:

  • Client ID
  • Secret ID
  • the redirect URI you chose


So keep the page open or note these strings somewhere.

Access Code

There are 3 types of Access Tokens you can get: an Implicit grant doesn't require an API but it expires after 1h so that won't make sense in the long run. Using client credentials (from your API) alone will not give you access to your currently playing track, so you will need your user account to give your app the permission to refresh the token on its own, without getting involved more than once.
In this step you will give your app exactly this permission by generating an Access Code.

For this request you'll need to specify:

  • Client ID
  • redirect URI
  • scope (user-read-currently-playing)
  • response type (code)


In the official documentation this is done automatically but it can be done manually as well. If you don't feel like doing that you can also run this bash script:

#!bin/bash
read -r -p "Client ID:" clientid
read -r -p "Redirect URI:" uri
echo "https://accounts.spotify.com/authorize?\
scope=user-read-currently-playing\
&response_type=code\
&client_id=$clientid\
&redirect_uri=$uri"

Be sure to match the Redirect URI exactly to what you added in the previous step. Open the output in a Browser that supports JavaScript (so not lynx), login and allow access. You'll be redirected to 'https://example.com/?code=' followed by your access code. Copy it any move on.

Auth Token and Refresh Token

From here on out it's pure bash, baby.
Using the access code you'll request an access token and get a refresh token at the same time, which you'll use to automatically refresh the token from there on.
You may run this script (fill in the data):

#!/bin/bash

# Fill In
## file to store your ever changing access token in
accesstoken=./access
## file to store your refresh token
refreshtoken=./token
## client id, secret id and redirect uri
id=
secret=
uri=

# code
    if [[ $(cat $refreshtoken) = "" ]]; then
read -p "access code: " code
echo $(curl -XPOST \
-d client_id=$id \
-d code=$code \
-d client_secret=$secret \
-d grant_type=authorization_code \
-d redirect_uri=$uri \
https://accounts.spotify.com/api/token \
| grep -o -P '.{0}refresh_token.{134}' \
| sed 's/refresh_token":"//') > $refreshtoken

    else
echo $(curl -XPOST \
-d client_id=$id \
-d client_secret=$secret \
-d grant_type=refresh_token \
-d refresh_token=$(cat $refreshtoken) \
-d redirect_uri=$uri \
https://accounts.spotify.com/api/token \
| grep -o -P '.{0}access_token.{215}' \
| sed 's/access_token":"//') > $accesstoken
fi

After using your access code on first use you can now automate this script to run every hour via cronjob, like this perhaps:

0 * * * * bash /path/to/script > /dev/null 2>&1

Remember not to share your refresh token with people you don't trust, as it not only gives you the power to view your currently playing song but also control of your player (pausing, skipping, playing a certain song, those things) and they could really annoy you with it.

Once you've cleared all that you can expunge the whole thing from your mind and focus on your gemini capsule for step 2.

Step 2/2: Display song info on gemini

For this you will need a gemini capsule with CGI support. My capsule (this capsule), for example, is served by agate and only supports static content, which is why my spotify script lives in a second capsule I made solely for playing around with scripts. That capsule is served by jetforce (in case you need a recommendation)

Jetforce server (https://github.com)


Getting song info

For this part you will have to make a request to https://api.spotify.com/v1/me/player/currently-playing, specifying the content type and your authorization. If you send this request:

curl -XGET \
"https://api.spotify.com/v1/me/player/currently-playing" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $(cat /path/to/token)"

You'll get a loong response that starts something like this:

{
  "timestamp" : 1676732845600,
  "context" : null,
  "progress_ms" : 127890,
  "item" : {
    "album" : {
      "album_type" : "album",
      "artists" : [ {
        "external_urls" : {
          "spotify" : "https://open.spotify.com/artist/0cbL6CYnRqpAxf1evwUVQD"
        },
        "href" : "https://api.spotify.com/v1/artists/0cbL6CYnRqpAxf1evwUVQD",
        "id" : "0cbL6CYnRqpAxf1evwUVQD",
        "name" : "Die Ärzte",
        "type" : "artist",
        "uri" : "spotify:artist:0cbL6CYnRqpAxf1evwUVQD"
      } ],
      "available_markets" : [
(...)

This will give you all sorts of information you can play around with. I'll just show you what sort of things I'm doing with it at the time of this writing, aka how to display your currently playing song exactly like me.

Generating gemtext

First write the output to an extra file so your script doesn't curl for every single bit of information.

## file that contains the renewing access token
auth=$(cat /var/spotify/access_token)
## file to write output to
spotify=/var/spotify/current

echo -e "$(curl -X "GET" \
"https://api.spotify.com/v1/me/player/currently-playing" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $auth")"  > $spotify

Then start to format the information you want. I started with the track name. For this you can just get the artist and track name from the output file but that sounded like more work so I get them from the spotify track <title> tag.

For this I firstly got the track url from the json output.

track=$(cat $spotify \
	| grep "spotify.com/track" -m1 \
	| sed -e 's/url//' \
	-e 's/,//' \
	-e 's/://' \
	-e 's/ //g' \
	-e 's/"spotify"//' \
	-e 's/"//g' )

Then on the page I fetched the inside of the title tags and deleted everything from it that I didn't want

# start of gemini page
printf '20 text/gemini\r\n'

if [[ $(cat $spotify) = "" ]]; then
	echo "Not listening to Spotify."

else
	echo -e "
	## Listening to
	> $(curl $track \
	|  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si' \
	| sed -e 's/| Spotify//' \
	-e 's/song and lyrics by //' \
	-e 's/&#x27;/’/g' -e 's/&amp;/\&/g' \
	-e 's/، /, /g')"

	echo -e "=> $track Listen along on Spotify"
(fi)

When getting the track info from the title you'll need to anticipate what html codes (such as &amp; for &) might pop up in there and replace them.

I also added the information whether or not my music is paused:

if [[ $(cat $spotify | grep "paus") != "" ]]
then
	echo "Track is paused."
fi


There's not much more info I found all that interesting to include, apart from the release date, using the same method as for the track above, replacing 'spotify.com/track' with 'release' in the grep command.

I also included some media, which is re-downloaded every time someone visits the page (depending on your computer and traffic to your capsule this may not be a great idea).

## files to dowload image and mp3 clip to
dir='/path/spotify'
image=$dir/spotify.png
mp3=$dir/clip.mp3

wget -O $image $(cat $spotify \
	| tac | tac \
	| grep "i.scdn" -m1 \
	| sed -e 's/url//' -e 's/,//' -e 's/://' -e 's/ //g' -e 's/"//g' )
echo "=> /spotify/spotify.png Album Cover"

echo -e "
wget -O $mp3 $(cat $spotify \
	| grep "preview_url" -m1 \
	| sed -e 's/preview_url//' -e 's/://' -e 's/ //g' -e 's/"//g' -e 's/,//')
echo '=> /spotify/clip.mp3 Preview'


And that actually about covers it. Allow the script to be executed, add it to your cgi directory and voilà, your currently playing song is displayed in your capsule.

Closing remarks


My bash skills aren't exactly the best so much of what you see here could probably be done better differently. Still, if someone wants to do this Spotify thing and didn't really understand this short guide, they can contact me and I'll try to answer your questions.

email (mailto://)


I think some cool things could be done with this, perhaps using some more scopes to display things such as playlists, liked songs etc. You could even make a gemini page to control your player (locally or protected by client certificate) which is not practical but possible! And you could do it all in bash too ;^)

Response: 20 (Success), text/gemini
Original URLgemini://brachycera.diptera.casa/gemlog/Displaying-your-c...
Status Code20 (Success)
Content-Typetext/gemini; charset=utf-8; lang=en