My music downloader
On some days, I just need music to function. But as the unemployed college student I am, I’m too poor to afford Spotify premium. I wanted to create a free music player that works out of the box, with my music saved locally. So I built InstaPlayer.
There was one key library that enabled this whole app and it’s SpotiFLAC. It enables me to download songs from multiple providers for free, and I built my app around it. Please give the repo a star!
Architecture
There are two parts. The first is a mobile app written in React Native. The second is a small backend that I self-host behind a Caddy reverse proxy, which does the actual downloading. They talk to each other over a normal REST API, plus a WebSocket for live progress updates (This project helped me learna about websockets for me first time).
A download goes like this:
- The app sends a Spotify playlist link to the backend, reaching it through Caddy reverse proxy.
- The backend hands the link to the SpotiFLAC engine, which finds the matching lossless file for each track.
- As soon as a song finishes, the backend tells the app over the WebSocket.
- The app pulls that song and its album art down to the phone.
- Once the app has everything, it tells the backend to delete its copy to save storage.
The VPS I rented for the backend had only 50GB, so the last step is pretty important.
Song matching
The interesting problem is how the backend actually finds a song. SpotiFLAC handles this part and this is what I understand it does: Spotify does not let you download anything, so I cannot pull the file from there. Instead, every track carries an ISRC, which is a standard code that identifies a recording no matter which service it lives on. The backend reads the ISRC from the Spotify track, then uses it to find the exact same recording on Tidal, Qobuz, or Amazon, where it can grab the lossless version.
Those sources are community-run and go down fairly often, so a good chunk of the work went into the backend knowing whether it can download right now, and telling the app before you waste time trying. The app shows this as a simple status light, and it will not start a download when the light is red.
What I learned
This was the first time I had to think about a lot of these things, so here are the bits that stuck with me.
manifest.json This project is the first time I implement what I call the manifest.json architecture, which is a json file storing information of each job that allows the client to transfer songs independently and asynchronously from the backend instead of having to wait for it
Using SQLite as the job store. I’ve only used NoSQL (Firebase, MongoDB) and Object (Cloudflare R2, AWS S3) database before. But for this project, I needed something light and works natively, so I chose SQLite.
Reverse proxy with Caddy. I usually use Nginx but I tried out Caddy for its simplicity. It handles HTTPS, TLS and encryption for me with only a few lines of config. I might switch to this from now on!
Building the engine from source instead of installing the package. I first used the Python package for SpotiFLAC, but it kept breaking, so instead I itegrated the upstream Go engine directly into the backend. This took uptime from 20% to 50%, which I’m very happy with.
Update: One serious caveat with trying to integrate the Go binary into my app is that I have to deal with whatever rate-limiting method added upstream. As of now (07/18/26), a new release has just dropped that adds Cloudflare Turnstile capcha. This completely breaks my app and I’m dealing with it now. I might have to revert to the Python Module, which I really don’t want to.