Overview
Zmusic is an Android music player that streams straight from YouTube with no official API — just NewPipeExtractor scraping the web frontend. It behaves like a real music app: search, library, Daily Mix, mood-based playlists, gapless playback, synced lyrics, and offline downloads, without a paid subscription or storing every song locally.
Engineering highlights
Streams from YouTube with no official API — pure web scraping via NewPipeExtractor, with a documented fallback path for when YouTube enforces SABR/poToken and audio streams disappear.
Spotify-level gapless playback: the next track is pre-queued and playback errors are attributed to the correct song by media ID, instead of a naive setMediaItem call.
A WebView-based poToken engine standing by as a fallback, bridged through a mutex with an hourly expiry — defensive engineering built around a data source it doesn't control.
Hardened release build: R8 + scoped StringFog, ProGuard rules tuned for Hilt/Room/Media3, shipped via dual CI — debug on every push, signed release with a mapping file on tag.
Tech stack
Language & UI
Architecture & DI
Media
Data source
Architecture
com/zaaam/Zmusic/ ├── ui/player/PlayerScreen.kt # ~1,100 lines — the largest screen ├── service/MusicService.kt # MediaSessionService + ExoPlayer ├── data/MusicRepository.kt # search() / getStreamUrl() — the core ├── util/QueueManager.kt # @Singleton, source of truth for the queue ├── util/DailyMixGenerator.kt # 40% familiar + 40% mood + 20% discovery └── di/AppModule.kt # Hilt bindings
Compose UI → per-screen ViewModel (Hilt) → QueueManager / MusicRepository → MusicService (ExoPlayer).
Features
- Search and stream any track on YouTube, with gapless playback
- Daily Mix, 6 mood-based playlists, Smart Playlists, and a Wrapped recap
- Dynamic UI accent color pulled from the track's cover art
- Synced LRC lyrics, 5-band equalizer, sleep timer with fade-out
- Offline downloads, floating bubble player, home-screen widget
Development process
Solo build, developed and tested entirely on-device via Termux/proot Ubuntu — no laptop involved. Went through a full UI redesign into the current "Dark Plum" system, and the lyrics provider was swapped out after the original got blocked on Indonesian networks.
Challenges
Staying playable when the data source is scraped rather than official: YouTube's SABR/poToken rollout killed audio streams outright, so the app needed a logcat decision tree — and a fallback muxed stream — just to stay functional across backend changes.
What I learned
How much of a "real" streaming app — gapless playback, offline downloads, a recommendation engine — can be built solo from a phone, and how to design defensively around a data source you don't control.
