Overview¶
Spooty is a single-activity native Android app written in Kotlin that wraps the
Spotify Web Player (https://open.spotify.com) inside a hardened WebView and bends it to
your liking, the same way Spicetify does on desktop.
The core idea¶
Instead of talking Spotify's private APIs, Spooty loads the real web player the browser would, and layers custom behavior on top:
- A WebViewClient keeps all navigation in-app; external links leave via the system browser, nothing else ever renders inside the app.
- An Injector (running in
onPageFinished) loadscore-spooty.js,core-spooty.css, the ad engine and the active theme into the page — guarded by awindow.__spootyInjectedflag so each asset is applied exactly once. - A JSON bridge (
JsBridge, every method annotated@JavascriptInterface) is the only JS→native channel: settings, playback state to the lockscreen, ad-mode updates. - A foreground MediaSession service keeps the process alive for background audio while
the WebView's own
<audio>element does all the real playback.
It is a View-based app (no Compose) with a deliberately small surface area:
few screens, thin framework code, and pure, unit-tested logic in inject/, settings/ and
the ad-mode plumbing.
What it is not¶
- Not a hack of Spotify's backend. No private API calls, no reverse-engineered endpoints, no network-level ad blocking.
- Not a distributed app. It's built for you, on your machine (or on CI), and sideloaded.
- Not a re-hosted stream. Native ExoPlayer is never used to play Spotify audio; the
<audio>element inside the WebView is the source of truth.
Personal, non-distributed¶
This project exists to be useful to its maintainer. It bypasses scaffolds and ad flows of the official mobile apps, which means it walks close to the edge of Spotify's Terms of Service. Treat it as a personal experiment: don't publish or distribute it, and keep the accounts you log into as your own.
Where to go next¶
- Check the feature tour.
- Read how it works for the injection & ad-engine internals.
- See the architecture for the component map and the invariants that must never be broken.
- Build it yourself with Build & test.