Build a Spotify Widget

Display what you're currently listening to with the Spotify API

August 19, 2020

0 views


Interested in this project?

Continue Learning

Spotify Integration Project

Overview

For this project we are going to utilize the Spotify Web API to acquire the title, artist, and link to our currently playing song on Spotify through a Node.js script. We will then format and display the data obtained by our script in an html file, and push our project such that it is publicly hosted by Netlify via the use of serverless lambda functions.

Check out a demo of what you're about to create here! However, instead of displaying what I'm listening to on Spotify, your project will display what you are listening to.

Authentication with Spotify

First, we need to acquire some of the account information needed to access personal data via the Spotify API. To make a get request for your currently playing song, and for a majority of other requests to the Spotify API, you will need an access token. To generate your access token, you will first need a client id, client secret, refresh uri, and refresh token.

Navigate to Spotify's developer page. This will become your homepage for any applications you want to create using Spotify data. Log-in to an existing account or or create a new account (any account type will suffice). On your developer dashboard, click CREATE AN APP, fill out the fields for App name and App description, check the verification statements, and create your app. This will bring you to a dashboard for the app where you can find your client id & client secret (click show client secret to find it). Store your client id & client secret such that they are easily accessible for use later in the project. Click EDIT SETTINGS and fill in the field for Redirect URIs. Enter http://localhost:8888/callback and store this address as your redirect uri for later use. Click SAVE at the bottom of the pop-up screen.

The final piece of information you will need is a refresh token. Spotify, and many other services use a system of refresh and access tokens for user authentication. Given a refresh token, you are able to regenerate an access token (which invalidates in a set amount of time) as is needed to view your data through the Spotify API. Using a refresh token allows your program to restore access to your account's data without you having to manually authenticate every time. To obtain a refresh token, we're going to use Spotify's Quickstart Authentication Code.

  • Install Node.js if you don't already have it.
  • Clone this GitHub repository.
  • Open up the app.js file from the authorization_code folder in your IDE of choice (I did everything in Visual Studio Code for this demo).
  • Add your client id, client secret, and redirect uri into the client_id, client_secret, and redirect_uri variables, respectively.
  • Find the scope variable and change it to "user-read-currently-playing user-read-recently-played". Since we want to display what we are currently listening to, we need to adjust our scopes to be reflective of this. Specifically, we need to add user-read-currently-playing and user-read-recently-played so that the access/refresh tokens returned by the script enable us to pull our currently or recently playing information. You can find the documentation for a currently playing track request here, and for a recently played track request here.

Note: We may need to access recently played song data so that your program does not display "undefined" information when you are not currently playing music.

  • In your terminal, navigate into the authorization_code folder, and run npm install to install all necessary dependencies for the script to run.
  • Run the command node app.js to run the app.js file script.
  • Your terminal should output: "listening on 8888". In your browser navigate to http://localhost:8888/. This is the local server your script is now running on. You should see a screen that asks you to Log in with Spotify. Click that button, and log into the Spotify account of the data you want to display in your project.
  • After logging in, you should see a new screen containing the details of the Spotify app you created and the scopes we put in the script. Click AGREE at the bottom of the page.
  • A new page should again appear with some of your Spotify account information displayed. Obtain your refresh token from the oAuth info section, and store it for later use.

Note: If you've run into problems with obtaining your refresh token, you can refer to this link which is Spotify's Web API Quickstart Guide. If you do follow their instructions, you will still need to change the scope variable to "user-read-currently-playing user-read-recently-played", otherwise your refresh token will not be valid for obtaining the data we want.

Set-up your Environment

Now that you have the account information needed to make a request to the Spotify API and acquire your Spotify data, you need to set up a project and script that will actually execute this.

  • Create a new project folder.
  • Open up your terminal, navigate to the project you've just created, and run the following commands:
    Create a default package.json file for your project:
    npm init -y
    Installs the modules we're going to use in this application:
    npm install express netlify-lambda serverless-http request dotenv

Add the following files to the root directory of your project:

  • A boilerplate index.html file:
<!DOCTYPE html> 
<html> 
    <head> 
        <meta charset="UTF-8"/> 
        <title>*insert title here*</title> 
        <link href="style.css" rel="stylesheet">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
     
    <body> 
    </body> 
</html>
  • An empty style.css file.
  • A .gitignore file with node_modules and functions added to it. When we push this repository to GitHub later, any file included in your .gitignore file will not be pushed.
  • A src folder with a blank app.js file in it.
  • A netlify.toml file with the following code added to it:
[build]
    functions = "functions"

Note: Because we will be hosting this application using Netlify's serverless functions, there are some special set-up items we'll have to include. This netlify.toml file will tell Netlify where to find your compiled serverless functions.

Now you're ready and set-up to write the script!

Write the Script

Open up the blank app.js file in the src. Here is where we're going to write the script which actually makes requests to the Spotify API and returns the currently playing data we want to display.

Express is a very commonly used Node framework for interacting with apis through requests and responses. We will be using it to interact with the Spotify API, so include the express module: const express = require('express');

In using Netlify functions to run our script, we use something called serverless functions. Include this serverless-http module to do so: const serverless = require('serverless-http');

Create an express application: const app = express();

Set up a router: const router = express.Router(); router.get("/", (req, response) => {}); app.use("/.netlify/functions/app", router);

Finish set-up for serverless functions using the express app we created: module.exports.handler = serverless(app);

So far, this is what the script should look like:

const express = require('express');
const serverless = require('serverless-http');

const app = express();

const router = express.Router();


router.get("/", (req, response) => {
    // TODO
});


app.use("/.netlify/functions/app", router);
module.exports.handler = serverless(app);


Now we want to fill out the get route:

We need to include the request module to actually make a request to Spotify's API: const request = require('request');

Now we're going to include our client id, client secret, and refresh token, as we will need this information to make the request:

const client_id = "your_client_id_here";
const client_secret = "your_client_secret_here";
const refresh_token = "your_refresh_token_here";

Using these account variables, we construct a new variable, authOptions in the format that Spotify specifies which we can then use to acquire our access token:

var  authOptions  =  {
    url:  'https://accounts.spotify.com/api/token',
    headers:  {  'Authorization':  'Basic '  + (new  Buffer(client_id  +  ':'  +  client_secret).toString('base64')) },
    form:  {  grant_type:  'refresh_token',  refresh_token:  refresh_token  }
};

request.post(authOptions, function(error, res) {
    var header = { 'Authorization': 'Bearer ' + (JSON.parse(res.body)).access_token };
    // TODO
});

At the end of this section, header contains a formatted version of our access token.

TODO: We are ready to make a request for what we are currently playing on Spotify (this goes directly under the declaration of the header variable):

var song_name, artist, song_url;

request({ url:  "https://api.spotify.com/v1/me/player/currently-playing?", headers: header }, function(error, res, body)  {
    if((res.statusCode  ==  204) || JSON.parse(body).currently_playing_type != "track") {
        // you are not currently listening to Spotify, use recently played instead
        request({ url:"https://api.spotify.com/v1/me/player/recently-played?type=track&limit=1", headers: header }, function(error, res, body) {
            // TODO #1: recently played
        });
    } else {
        // TODO #2: currently playing
    }
});

Here we are attempting to make a request to what we are currently listening to on Spotify. In the case that there is nothing playing, or that what is playing is not a song, we instead make a new request to get the last song we listened to (using recently played). All the information we need is stored in the body of one of these requests. We just need to format and return the exact data we want and the script will be done!

Todo #1: recently played

var body_text = JSON.parse(body);
var track = body_text.items[0].track;
song_name = track.name;
artist = track.artists[0].name;
song_url = track.external_urls.spotify;
response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });

Todo #2: currently playing

var body_text = JSON.parse(body);
song_name = body_text.item.name;
artist = (body_text.item.artists)[0].name;
song_url = body_text.item.external_urls.spotify;
response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });

In both versions, we parse the json body returned by our request. We then extract the song_name, artist and song_url from these parsed bodies and return the entire get with this data formatted as a json object.

Here is the complete script

const express = require('express');
const serverless = require('serverless-http');

const app = express();

const router = express.Router();


router.get("/", (req, response) => {

    const request = require('request');

    const client_id = "your_client_id_here";
    const client_secret = "your_client_secret_here";
    const refresh_token = "your_refresh_token_here";

    var authOptions = {
        url: 'https://accounts.spotify.com/api/token',
        headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
        form: { grant_type: 'refresh_token', refresh_token: refresh_token }
    };

    request.post(authOptions, function(error, res) {
        var header = { 'Authorization': 'Bearer ' + (JSON.parse(res.body)).access_token };
        var song_name, artist, song_url;
        request({ url: "https://api.spotify.com/v1/me/player/currently-playing?", headers: header }, function(error, res, body) {
            if((res.statusCode == 204) || JSON.parse(body).currently_playing_type != "track") {
                request({ url:"https://api.spotify.com/v1/me/player/recently-played?type=track&limit=1", headers: header }, function(error, res, body) {
                    var body_text = JSON.parse(body);
                    var track = body_text.items[0].track;
                    song_name = track.name;
                    artist = track.artists[0].name;
                    song_url = track.external_urls.spotify;
                    response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });
                });
            } else {
                var body_text = JSON.parse(body);
                song_name = body_text.item.name;
                artist = (body_text.item.artists)[0].name;
                song_url = body_text.item.external_urls.spotify;
                response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });
            }
        });

    });

});


app.use("/.netlify/functions/app", router);
module.exports.handler = serverless(app);

Now that it's written, we are going to check that our script returns a json object containing the data we expect it to. To do so, we need to modify our default package.json file, replacing anything in the scripts section with the following (between the curly braces)

"start":  "./node_modules/.bin/netlify-lambda serve src",
"build":  "./node_modules/.bin/netlify-lambda build src"

The start option will allow you to run the script locally, and the build option will be used by Netlify to set up your project.

In your terminal, navigate back to the root directory of your project, and run the command npm run start to run the script. The terminal should output Lambda server is listening on 9000, so navigate to http://localhost:9000/.netlify/functions/app where your project should be running locally. When you open the page, you should see something like this displayed in the upper left corner (instead filled in with the information of whatever song you are/were listening to)

{"song_name":"Best Of You (feat. Elle King)","artist":"Andy Grammer","song_url":"https://open.spotify.com/track/4bsqIuMHtvLCzpYAyV3044"}

Try going to your Spotify account and changing the song, then refreshing this page to see the script working and updating to match!

Now that we've verified that our script is returning the json data it should, we're going to modify our client_id, client_secret, and refresh token variables to the following

const client_id = process.env.CLIENT_ID;
const client_secret = process.env.CLIENT_SECRET;
const refresh_token = process.env.REFRESH_TOKEN;

Since this is data we won't want to be publicly available in our GitHub repository, we're going to set these variables through Netlify as environment variables instead of directly writing them into our script. Make sure to continue storing the actual values of your client_id, client secret, and refresh token for later when we deploy our project.

Display your Data

Although our script is now written, we are not actually doing anything with the data it obtains when run. Here's how we can allow our index.html file to read in and display the data provided by the script

First, we want to add some code to our index.html file as a sort of template to be filled in with the data we retrieve from the script. You can format this however you want, but here's how I did it

<p> listening to <u><a target="_blank" id="song_title"></a></u> by <span id="artist"></span> </p>

This may look a little confusing, so this is what is happening:

  • All my data is displayed in a single line that looks like this: listening to insert song title here by insert artist name here
  • You'll notice that my a tag and span tags currently have nothing in them. These blanks will soon be filled by the data we get from our script.

Now, we want to add a script tag to the end of the body of our index.html file:

<script></script>

Inside the script tag, let's create a XMLHttpRequest object var request = new XMLHttpRequest(); XMLHttpRequest objects allow us to make HTTP requests between our site and the server.

Now, we want to use our request variable to make a get request.

request.open("GET", "/.netlify/functions/app", true);

The first parameter indicates we are making a "GET" request as we are attempting to retrieve data. The router in our script was set-up to run the get request we need at the route /.netlify/functions/app, so as indicated by the second parameter, this is where the XMLHttpRequest object is redirected. Finally, the third parameter being true indicates this to be an asynchronous request. This means we can wait for the request to be filled to continue with the script, and the browser will continue functioning as normal.

Next, we send our request

request.send(); 

Since our GET request was made asynchronously, we need to set up an onload function for when it returns with data

request.onload = function() { 
    //this function should be empty right now 
}

Now, we want to fill out the onload function so that it updates our HTML file with the information we want to display that we now have retrieved from the script and stored in request

document.getElementById("song_title").innerHTML = JSON.parse(request.responseText).song_name;
document.getElementById("artist").innerHTML = JSON.parse(request.responseText).artist; 
document.getElementById("song_title").setAttribute("href", JSON.parse(request.responseText).song_url);

Each of these three lines accesses an element from the HTML file by it's id using document.getElementById("insert_id_here"), then assigns a certain attribute of that element (inner HTML or href) to a part of the parsed JSON request object. To replace the text of a certain element in the html file, use the .innerHTML property. This is what we did to fill out the song name and artist. To set the href for an element, use setAttribute(*attribute you want to set*, *parameter from json*).

Here is the completed index.html file

<!DOCTYPE  html>
<html>

    <head>
        <meta  charset="UTF-8"/>
        <title>*insert title here*</title>
        <link  href="style.css"  rel="stylesheet">
        <meta  name="viewport"  content="width=device-width, initial-scale=1.0">
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200&display=swap" rel="stylesheet">
    </head>
        
    <body>
        <p>listening to <u><a  target="_blank"  id="song_title"></a></u> by <span  id="artist"></span></p>

        <script>
            var  request  =  new  XMLHttpRequest();
            request.open("GET",  "/.netlify/functions/app",  true);
            request.send();
            request.onload  =  function()  {
                document.getElementById("song_title").innerHTML  =  JSON.parse(request.responseText).song_name;
                document.getElementById("artist").innerHTML  =  JSON.parse(request.responseText).artist;
                document.getElementById("song_title").setAttribute("href",  JSON.parse(request.responseText).song_url);
            }
        </script>
    </body>

</html>

Feel free to add whatever styling you want to your project! I just added the following code to my style.css file

body  {
    text-align:  center;
    font-size:  40px;
    padding:  35vh  10px  40vh  10px;
    background-color:  #cbe9ee;
}

#song_title,  #artist  {
    text-decoration:  none;
    color:  #569fac;
} 

#song_title:hover  {
    opacity:  0.6;
}

I also changed the font, so I imported one from Google Fonts and added <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200&display=swap" rel="stylesheet"> to the head of my html file and font-family: 'Nunito', sans-serif; to the body section of my css file.

Host your project publicly through Netlify

Now that our script is written, and our html file is set-up to display information retrieved by the script, we want to push our project to Netlify so we can display this information to anyone. This is how you are able to see whatever I am currently listening to in the demo for this project.

First, push your project to a new GitHub repository. Next, open Netlify and sign-up or log-in to an account. Navigate to your personal homepage and click New Site from Git. Select GitHub from the options for Continuous Deployment, then find and select the repository from GitHub you just pushed your project into. In the Basic build settings section, fill out the Build command as npm run build.

Since we are inserting our client id, client secret, and refresh token into our script in the form of environment variables, we now need to add them to our Netlify project. Towards the bottom of the page, click Show advanced.

  • Add a New variable with Key CLIENT_ID and Value as your client id.
  • Add a New variable with Key CLIENT_SECRET and Value as your client secret
  • Add a New variable with Key REFRESH_TOKEN and Value as your refresh token.

Click Deploy site and your Netlify site should deploy with a link within about a minute.

CONGRATS!! You can now share your Netlify link with anyone, and they'll be able to see whatever you're listening to on Spotify :)

Comments (0)