The Problem
YouTube Music is blocked in many regions and the official Android client offers limited customization. Developers who want a self‑hosted, extensible player must either reverse‑engineer the official app or build a new client from scratch, both of which are time‑consuming and error‑prone.
What This Does
Metrolist is a full‑featured Android client that talks to YouTube Music via the undocumented “Innertube” API (see the innertube/ module). Core UI lives in app/src/main/kotlin/com/metrolist/music/, with the application class in App.kt and the launch activity in MainActivity.kt. Playback is handled by MusicService.kt and PlayerConnection.kt, while the local Room database is defined in MusicDatabase.kt and a suite of *Dao.kt files. Lyrics, equalizer, and “listen‑together” features are provided by dedicated provider classes under lyrics/, eq/, and listentogether/.
How It Is Wired
Execution starts when Android launches app/src/main/kotlin/com/metrolist/music/MainActivity.kt. The activity creates the Dagger/Hilt component defined in AppModule.kt, which injects MusicService (a foreground MediaLibrarySessionCallback). MusicService builds an ExoPlayer instance and registers PlayerConnection to expose playback controls to the UI. UI components such as CastButton.kt and SleepTimer.kt call into PlayerConnection for state changes.
When a track is requested, MusicService calls DeepLService.kt, MistralService.kt, or OpenRouterService.kt (AI‑powered translation) via Retrofit, then streams the media URL returned by the innertube client (innertube/build.gradle.kts contains the network layer). The stream is fed to ExoPlayer; optional audio processing passes through CustomEqualizerAudioProcessor.kt and BiquadFilter.kt. Lyrics are resolved by LyricsProviderRegistry.kt, which selects a concrete provider (YouTubeLyricsProvider.kt, BetterLyricsProvider.kt, etc.) based on the current track metadata.
Database writes flow through the DAO interfaces (SongDao.kt, PlaylistDao.kt, …) to MusicDatabase.kt, which persists user library, playback history, and offline cache. No circular module dependencies were found; the three internal Gradle modules (app, innertube, kizzy) import nothing from each other, keeping the import graph flat.
How To Use It
# Clone the repository
git clone https://github.com/moses-y/Metrolist.git
cd Metrolist
# Build with Gradle (wrapper included)
./gradlew :app:assembleRelease
The Gradle wrapper (gradlew) and app/build.gradle.kts define all required SDK versions and dependencies. No additional configuration files (e.g., API keys) are committed; the app expects a YouTube account login at runtime. To install the generated APK, use adb install app/build/outputs/apk/release/app-release.apk.
Real‑World Use
A corporate device in a restricted region can install the signed APK, log into a corporate‑managed Google account, and stream YouTube Music without a VPN. The app’s “listen together” service (ListenTogetherManager.kt) can be pointed at an internal signaling server to enable synchronized playback for team‑wide playlists.
Code Health & Issues
- HIGH – GitHub Actions are pinned to tags (
gradle/actions/setup-gradle@v5,ilharp/sign-android-release@v2.0.0). Tags can change, exposing secrets. Fix: replace tags with 40‑character commit SHAs. - MEDIUM – Workflows lack explicit
permissionsforGITHUB_TOKEN. Fix: addpermissions: contents: readand scope per‑job needs. - MEDIUM – Test coverage is minimal (1 test file vs. 541 source files). Fix: add unit tests for high‑fan‑in modules such as
MusicService.ktandLyricsProviderRegistry.kt. - LOW – No
timeout-minuteson CI jobs, risking overlapping runs. Fix: set sensible timeouts. - LOW – Repository lacks convention files (
.editorconfig,.gitattributes, formatter config). Fix: add them to enforce consistent style. - MEASUREMENT – 60 instances of deep nesting (max indentation depth 10) in files like
CastConnectionHandler.kt,CastButton.kt, andApp.kt, increasing cognitive load. Fix: refactor with guard clauses and extract inner blocks.
The Bottom Line
Metrolist delivers a capable, open‑source YouTube Music client with a clear modular layout and solid Android architecture, but it suffers from high code complexity, scant automated testing, and CI hygiene issues. It is a solid foundation for developers needing a customizable player, provided they are comfortable refactoring the nested code and improving test coverage.