Building an iPod: Part 2 — Integrating Spotify

Krisztian Seres
6 min readApr 12, 2021

In the first part of our “Building an iPod” series we took a small glimpse at the click wheel and user interface. We implemented the iPod’s user interface using Angular as our frontend framework. In this part we’ll make it display albums, playlists songs and of course — we’ll play some songs using Spotify.

I need to write a few words about Spotify’s API cause it’s a tricky one. Spotify gives us its music playing service in two parts:

  • First you must log your users in with a Spotify premium account, then you can initialize the Spotify Connect device in a browser window. The Spotify Connect device is playback device that can be controlled. It can play music, can have a queue and its volume can be set. Actually every the device where the user is logged in is a Spotify Player whether it’s an app on a PC, a phone or a smart speaker.
  • Second, you can control any of the user’s devices with its REST based Web API. This API can be invoked with the logged in user’s JWT access token. And every time you’d like to play some music you must explicitly tell which playback device must obey your command. Since it’s a RESTful API this is where things get complicated. We can only approach realtime behaviour, but we cannot reach it.

User Authentication

In order to use Spotify’s services you must authenticate your users. We must write a backend application that replicates the following steps:

Spotify authorization flow. It’s not as complicated as it looks.

First register a Spotify Application in the Developer Dashboard. Get you App’s client Id and client secret.

I’ll share node.js code for the Spotify Authorization service that can be assembled using Spotify’s Web API Tutorial. In the end I’ll look something like this (remember to insert your own client id and client secret):

Let’s suppose you host the authorization service on http://localhost:8888 and your web app on https://localhost:4200. With the authorization service up & running just place a link on your web application that takes your user to http://localhost:8888/login.

When the user clicks the login link the authorization flow will starts. In case the user logs in successfully Spotify redirects your user to the web app with the ‘spotify_access_token’ and ‘spotify_refresh_token’ set in the callback url’s querystring. Store both values and now you can request Spotify’s services.
Whenever you have a token expired REST API error just call the authorization service’s /refresh_token endpoint with the ‘spotify_refresh_token’ you stored upon the callback.

Abstract music player

Everytime a user makes an action that changes the playback state we make a request to a streaming service. Right now the streaming engine is Spotify but later on we’ll include Apple Music as well. We need to make the streaming service’s logic independent to the streaming engine thus we need to put an interface between the two.

StreamingService has a set of general operations that be interpreted on any streaming platform. StreamingService commands a streaming engine to do things based on the user’s commands. StreamingService should know nothing about the streaming technology it is operating on, we do this by issuing any command through an interface called IEngine.

The implementation of the IEngine interface is the actual music player engine that makes requests to a specific streaming provider. For now we’ll work on the SpotifyEngine.

SpotifyEngine can do everything that is required for an iPod playback after we properly implement the IEngine interface.

Playback device

Spotify’s Web API controls a playback device.

SpotifyEngine has a DeviceService as well which encapsulates the device we are controlling. Every time we’d like to play something we must tell Spotify the id of the playback device we want the track to be played on.

We can even tell Spotify to transfer the playback to another device. We do it like this:

Transferring the playback from one device to another looks like this:

Reading the user’s music catalog

This is how we get the user’s liked albums and assemble them into an AlbumModel.

The AlbumModel has a list TrackModels which store information about each specific track of the album.

The approach is the same for getting the user’s albums, playlists, artists or liked songs. In the end we’ll be dealing with a collection of TrackModels.

The TrackModel encapsulates every information that is required for playing the track and correctly displaying the playback state.

The Playback

Playing a track is easy. Synchronizing playback state is tricky.

Spotify’s Web API lacks a realtime channel that could provide us with useful information about the current playback state. This feature would be extremely useful for us, but unfortunately Spotify ignores and closes every feature request in the topic.

The Spotify Connect player instance can provide us with realtime information about the playback states since Spotify Connect player is actually responsible for playing the tracks.

We could rely on the Spotify Connect player instance, but it is not supported on mobile devices.

This means we can only play music on desktop operating systems within our application. And more importantly— we are stuck with Spotify Web API to remotely control Spotify Connect devices. This makes our job challenging because in order to display our playback state we need to make an approximation between every update request. With no other tool at our hands these approximations are based on JavaScript timeout calls.

Every time the a playback event occurs such as:

  • play, pause
  • end of track, end of album, new track
  • seek (where we‘d like to play the song at a specific timestamp)

we need to update our playback state with an API call. We cannot just update our playback state in every second — we have to be mindful about the amount of API calls we make, because we might get throttled by Spotify. The rate limits ia approximately 800,000 to 1,000,000 requests per 24 hours

The following SpotifyEngine snippet includes the code for playing a track and updating the current playback state:

The response for the CurrentlyPlaying API call tells us where our playback is actually at so we can use that information to synchronize our view of music playback. We set up a timer that request an update when the track’s playback ends. This way we’ll have somewhat of an approximation about what is going on on the remotely controlled device.

Seek

A probable use case for music playback is seek. On the currently playing screen when we hit the center button we turn on seek. By scrolling with the click wheel we can rewind or fast-forward songs.

Seek works just like as it does on an iPod

We seek a track by sending a put request to Spotify. This will command the playback device to change its playback position.

After the successful response we must update our playback state by sending a CurrentlyPlaying API request to Spotify.

Setting the volume

Some playback devices are supporting volume change requests. This enables us to use the iPod interface to increase decrease the playback volume.

Works like a charm.

Next up

We’ll continue our series by intergating Apple Music next to Spotify. We’ll compare Spotify’s request-response based solution to Apple’s singleton music player instance that can provide us real-time playback stats.

Apple’s MusicKit JS works by instantiating a player instance inside the browser. After authorization this instance can be used to read the user’s iCloud music library. Then the player instance can be told what to play and it can provide us realtime playback stats.

How much smoother is Apple’s solution? We’ll see.

Hint: By a lot!

You can try webPod here.

--

--

Krisztian Seres

Co-Founder of Enlight Digital Studios. Software Engineer.