Use NodeJS to fetch currently playing Spotify song

Use NodeJS to fetch currently playing Spotify song

ยท

12 min read

Good day !

In this article I'm covering a topic which I stumpled over while I was trying to improve my live streams overall quality on Twitch : How can I show my viewers what I am currently listening to on Spotify?

There are already a few solutions out there in the wild which solve this problem perfectly fine. However, since it's a rather simple problem, there is nothing wrong with coding my own solution, for learning or just for fun.

Prerequisites

  • I am using NodeJS > 14 for this project
  • I am using NPM as package manager for NodeJS
  • I am using OBS for streams, and
  • an OBS text file source to actually bring the information into stream
  • I am using my own Spotify Account

Contents

โš  Please note I stripped away error handling from the code, just do get a better overview of it and simplify things for this guide. I suggest to implement proper error handling if you tend to use it.

1. Setup NodeJS project

mkdir spotify-obs
cd spotify-obs
npm init -y
npm install axios express

๐Ÿ‘† I create a new project folder, and initialize a NodeJS project with npm init. Note -y as optional flag here to skip all the unwanted questions during initialization process. After the NodeJS project is initialized I continue with installing the two dependencies named axios and express.

// on windows
fsutil file createnew api.js 0
fsutil file createnew refresh_token.txt 0
fsutil file createnew song.txt 0

// linux
touch api.js
touch refresh_token.txt
touch song.txt

๐Ÿ‘† For the simplicity of this project I try to keep the code in one file (api.js), so I go ahead and create api.js file in the root directory, also refresh_token.txt and song.txt to store some text, but more on that later ...

2. Setup Spotify Developer Account

I go over to the Spotify Developer Console ( https://developer.spotify.com/dashboard/ ) and login with my personal Spotify account. I follow the instructions in the dashboard to create a new app.

I should give it a meaningful name and description so I won't forget why it's there. Once I got the app in the dashboard created, I write down the client-id and client-secret provided. Also I shouldn't share them with anyone ! โš 

Spotify Dashboard

The key to success when trying to read user information from Spotify (or any other web app using the same authorization flow) is to have the access_token and the refresh_token of the user. The only way to get these is actually through a login form that pops up in your browser, asking you for permission on behalf of a spotify integration. But that's not really convenient is it ? I don't want to go through the same authorization process over and over again every time I start my project. So I need a way to keep the refresh_token in my backend once I have it - let's keep that in mind

With the refresh_token we can get as many access_token we want

3. Start coding !

I open up the api.js file I created earlier and begin with adding all dependencies at the top :

const axios = require('axios');
const express = require('express');
const fs = require('fs');
const app = express();

// we need to base64 encode the app credientials for later requests to the Spotify API
const buff = Buffer.from('<your_client_id>' + ': ' + '<your_client_secret>');
const base64data = buff.toString('base64');

4. Setup express server - so I (the user) can authorize

// the login route should redirect the user to the auth page of Spotify,
// once the auth is complete, it sends the user back to the /callback route (below)
app.get('/login', function(req, res) {
  const scope = 'user-read-currently-playing';
  const redirect_uri = 'http://localhost:8888/callback';
  const client_id = ''; /* insert client_id you got before from Spotify */
  res.redirect('https://accounts.spotify.com/authorize' +
    '?response_type=code' +
    '&client_id=' + client_id +
    '&scope=' + encodeURIComponent(scope) +
    '&redirect_uri=' + encodeURIComponent(redirect_uri));
});

app.get('/callback', async function(req, res) {
  // the auth_code will be included in the /callback route request
  // as query parameter "code"
  const auth_code = req.query.code;

  // now we use the auth_code to actually get the access_token,
  // and further more the refresh_token
  const options = {
    url : 'https://accounts.spotify.com/api/token',
    method : 'post',
    headers : {
      authorization : `Basic ${base64data}`, /* remember ? */
      contentType : 'application/x-www-form-urlencoded'
    },
    params : {
      grant_type : 'authorization_code',
      code : auth_code
    }
  };

  const response = await axios(options);
  const refresh_token = response.data.refresh_token; 
  // Now we have the precious refresh_token, let's store it 
  fs.writeFileSync('./refresh_token.txt', refresh_token);
  return res.end('Success ! You can close this tab now!');
});

app.listen(8888, () => {
   console.log('App listening on http://localhost:8888');
});

๐Ÿ˜• ๐Ÿค” So what the hell did I just setup here ? Basically what I did is, I defined 2 express-routes (endpoints):

  • localhost:8888/login : If I would start the application now and open up my browser to this endpoint it would redirect me to the Spotify authorization page of my Spotify Integration I setup earlier in the Dashboard. Once I gave permission Spotify will send me back again to the specified redirect uri localhost:8888/callback. That's why I setup the next endpoint
  • localhost:8888/callback : So after successful authorization, Spotify will redirect my request back to this endpoint (I am still in the browser) along with some query parameters attached. Within the route controller it's very easy to access those query parameters ( req.query ). This is how I get the auth_code. One last step requires to swap the auth_code for the beloved access_token or request_token.

๐Ÿ”— Checkout Spotify Auth-Flow to see it in detail

๐Ÿ”— Checkout Express if you haven't hear about it yet

5. Create the main loop of the program

In the stream I don't want to show what I am currently listening to only once - I want to be able to show it all the time with updated information. That means my little program needs to keep requesting this information from Spotify constantly.

async function main () {
  // ...
}

setInterval(main, 2000);

Within this looping main function I now can actually start to fetch some information from Spotify using the refresh_token I obtained before.

โ“ Why don't I use the access_token ? Well actually I will use both. The access_token is only valid for a limited time. So before every Spotify API request, I create a fresh one, using the refresh_token. The refresh_token stays valid. And with the access_token I will get the information from Spotify. So there are always 2 requests involved.


async function main () {

  // first request to get the access_token
  const refresh_token = fs.readFileSync('./refresh_token.txt', 'utf8');
  const auth_response = await.axios({
    url : 'https://accounts.spotify.com/api/token',
    method : 'post',
    headers : {
      authorization : `Basic ${base64data}`,
      contentType : 'application/x-www-form-urlencoded'
    },
    params : {
      grant_type : 'refresh_token',
      refresh_token : refresh_token
    }
  });
  const access_token = auth_response.data.access_token;

  // second request to get information from Spotify
  const spotify = await.axios({
    url : 'https://api.spotify.com/v1/me/player/currently-playing',
    method : 'get',
    headers : {
      authorization : `Bearer ${access_token}`
    }
  });
  const artist = spotify.data.item.artists[0].name;
  const song = spotify.data.item.name;

  // save info to local text file
  fs.writeFileSync('./song.txt', `${song} by ${artist}`);
}

โš  Please note in order to get the currently playing song from Spotify, you actually need to be playing a song in Spotify, otherwise the returned data will be null

6. Add Text Source to an OBS Scene

Now that I have the information I need stored in a txt file, I can use it as text source inside my OBS scene. I have prepared some screenshots to describe this steps :

โ“ The steps might be a bit different depending on the OBS Version or Client you are using. Please note : I am using "Streamlabs OBS" ( LINK ). But this process should also work with Vanilla OBS and XSplit !

spotify_2.jpg

๐Ÿ‘† I start off by adding a new Source to my Scene.
๐Ÿ‘‡ As new Source I choose "Text"

spotify_3.jpg

spotify_4.jpg

๐Ÿ‘† In the Source options I am looking for "Read from file" and activate it.
๐Ÿ‘‡ This already should give me a preview.

spotify_5.jpg

spotify_6.jpg

๐ŸŽ‰ If I did everything correct, I will have my self-updating Spotify information inside my OBS Scene. Yay !

๐Ÿ”— If you want to checkout the Github Repository for this project - for testing or even to contribute - go here : https://github.com/turbopasi/spotify-current-track

Conclusion

If you made it until the end : thanks for reading ! I hope it helped you for your own project. If you got any questions don't hesitate to ask OR open up a ticket at https://github.com/turbopasi/spotify-current-track/issues !