Making a better mobile Spotify app
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.
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!
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!
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.
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:
- I don't have a Spotify account
- Librespot is implemented in Rust and Python. I can't use rust in an Android app and I refuse to write Python.
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.
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!
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:
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...
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!