5 min read

Making a better mobile Spotify app

Making a better mobile Spotify app
Photo by sgcdesignco / Unsplash

Okay, to begin with, the title is kind of misleading. It's not a "better" app, I'm not great at making mobile apps at all, so it will definitely be worse than the official app.

But!

As someone who doesn't want to pay for music, wants some kind of automated recommendations, but no ads (and offline listening support), there isn't any other choice.

I mean, yeah, I could just download MP3s off of YouTube (or go one step further and use other tools to get higher quality music), use last.fm for recommendations and call it a day.

But come on, where's the fun part of that?

So, let's get started with this.

⚠️
Everything under this box violates the Spotify User Guidelines. I am not responsible for anyone getting banned for following this blog post as a tutorial.

Step 1: Where do we start digging?

Sometimes I wish that I had a rooted phone, so I could use mitmproxy at its full potential, but unfortunately, I own a Huawei, and I can't really change my phone at this point. So, reverse engineering the mobile app is out of question.

But hey, Spotify lets us listen to songs from another place!

Introducing, the Spotify Web Player!

The Spotify Web Player with the browser's development tools open
The Spotify Web Player!

Now, now, I know this isn't something special. Everyone knows that his exists, but how many people have written an Android app using the API this player uses? I don't know, I didn't check.

That aside, let's log in with code using only this API!

The login request needs a ReCaptcha token

I have changed my mind, let's just make the user enter their own cookie.

Now, the API still uses a Bearer token, but it has to get that token somewhere, so let's investigate the API requests.

The web client's request it regularly makes to have a live access token

Bingo.

Just prefixing this with Bearer and using it in the Authorization field is enough to give us full access to the API. It also seems like it only needs the sp_dc cookie, if it's invalid, or not there, we will get an "anonymous" token, that still works unless you want to listen to songs.

Anyway, I implemented the token retrieving & refreshing logic in Android Studio. So, let's get to the fun part.

Step 2: How the hell does Spotify even play the songs?

Just like every other paid service (except Crunchyroll, if you know how to talk to their API, but that's a whole different post), Spotify also uses DRM. I've seen some mentions about how you can play songs with a premium account using librespot, but the thing is:

  1. I don't have a Spotify account
  2. Librespot is implemented in Rust and Python. I can't use rust in an Android app and I refuse to write Python.
ℹ️
I have found out that there's a Java version of Librespot, but I couldn't get it to work in a way I wanted it to work, so I'm not using it

So, let's find out a way to play these songs. To do this, we can just click any song/playlist and watch as the Network tab fills up, and oh boy, does it fill up.

There's just way too many tracking stuff here

Don't let this distract you though, since just by looking around, we can find 2 requests that interest us!

On the left image, you can see some metadata. I have no idea what most of that means but I do know that the PSSH value is used for decryption.

On the right side, we can see the URLs to play the song.

Well, what are we waiting for, let's throw it into ExoPlayer, and get a source error!

As I've said, Spotify has DRM (no way!) so we can't just do that. We can, however, tell ExoPlayer to use Spotify's license server to decrypt it. I'm pretty sure that we will need the PSSH, but let's just try without that.

Classic ExoPlayer error

The playback fails because we get a 401 from the license server- wait what?

A 401? Not 400 or 404?

After checking the DevTools a bit more, we can see that the license server is authorized.

Meh, we can just pass the token to the DRM Configuration in the ExoPlayer's MediaItem. Now, it didn't work the first try, and I'm pretty sure that I still need the PSSH, so let's just just watch it fail again!

Or not!

The playback actually starts, and plays the song to the end! Well, turns out for whatever reason we didn't even need the PSSH at all!

Well, there IS a little problem. My logcat is filled with this:

E/libc: Access denied finding property "kirin.drm.info"

I have no idea what that means, but hey, I can listen to music, so I can ignore it!.. let's google it just to be sure though...

Of course it doesn't

Well, it's almost 2 AM. There's still more stuff to do, but that's for future me. So let's give some break.

In the next post, we will hopefully have actual media playback & controls. Stay tuned for that!