VIVI Music is a pure-Kotlin Android application built with Jetpack Compose for the UI layer, ExoPlayer for audio playback, Room for local data persistence, Hilt for dependency injection, and a set of dedicated Gradle modules that isolate third-party integrations. This page walks through each architectural layer so contributors can navigate the codebase with confidence.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/vivizzz007/vivi-music/llms.txt
Use this file to discover all available pages before exploring further.
Module Layout
The project is a multi-module Gradle build rooted atsettings.gradle.kts. Each module has a single, well-defined responsibility.
:app
The main Android application module. Contains all Compose screens, ViewModels, the Room database, playback service, DI graph, and every feature screen under
ui/screens/.:innertube
Reverse-engineered YouTube Music API client. Handles search, browse, watch endpoints, stream URL resolution, and cipher deobfuscation. Used by
:app for all online content.:lastfm
Last.fm REST API client. Exposes scrobble, now-playing, and loved-track endpoints.
:app calls this when EnableLastFMScrobblingKey is on.:kizzy
Discord Rich Presence gateway. Manages a WebSocket connection to Discord’s gateway to update the user’s activity when
EnableDiscordRPCKey is on.:jiosaavn
JioSaavn streaming integration. Provides search, song resolution, and audio stream decryption for the JioSaavn source. Controlled by
EnableSaavnStreamingKey.:shazamkit
Song recognition module wrapping ShazamKit. Powers the in-app recognition feature accessible from the search screen.
:canvas / :vivimusiccanvas / :applecanvas
Animated canvas rendering.
:canvas provides the core infrastructure; :vivimusiccanvas and :applecanvas implement distinct visual styles (VIVI-native and Apple Music–style backdrops respectively).:lyricsProvider
Unified lyrics fetching layer. Aggregates results from LRCLib, Kugou, Musixmatch, BetterLyrics, SimpMusic, YouLyPlus, and Paxsenix behind a single interface, respecting the
PreferredLyricsProviderKey and LyricsProviderOrderKey preferences.:artistvideo
Artist video loading and playback helpers. Provides the video surface and data-fetching logic used on artist screens when
ShowArtistVideoKey or ShowArtistBackgroundVideoKey is enabled.:spotify
Spotify playlist import. Handles OAuth flow and playlist transfer into the local Room database.
Playback Layer
The audio engine is the heart of VIVI Music. It lives entirely in the:app module under playback/.
MusicService
MusicService extends MediaLibraryService (from Media3) and is the process-lifetime owner of all playback state. Key responsibilities:
- ExoPlayer host — creates and configures the primary
ExoPlayerinstance, plus secondary and fading players used during crossfade transitions. - Audio focus — requests and responds to
AudioFocusRequestevents, pausing playback when focus is lost and resuming when it returns. - Stream resolution — uses a
ResolvingDataSourceto intercept playback requests and resolve YouTube Music video IDs to signed DASH/HLS stream URLs via the:innertubemodule. - Cipher deobfuscation — decodes scrambled stream URLs using logic controlled by
EnableAutoCipherFetchKeyandCipherLastUpdatedKey. - Play event recording — writes an
Evententity to the Room database for every completed track, powering the listening history and statistics features. - Notification — builds a rich media notification with playback controls using
DefaultMediaNotificationProvider. - Crossfade — manages volume ramps between the primary, secondary, and fading players when
CrossfadeEnabledKeyis on.
Audio Processing Pipeline
ExoPlayer routes audio through a chain of processors before it reaches the audio sink:PlayerConnection
PlayerConnection is instantiated by the UI layer after it binds to MusicService via MusicBinder. It bridges Compose-reactive state flows to the underlying ExoPlayer:
- Exposes
StateFlowproperties (isPlaying,playbackState,currentSong,queueWindows,currentWindowIndex) that Compose screens observe directly. - Provides safe accessor methods (
playQueue,addToQueue,togglePlayPause,seekTo) that guard against calling the player before it has finished initialising. - Implements
Player.Listenerto keep all flows in sync with player callbacks.
Queue Types
Queue strategies live inplayback/queues/ and implement the Queue interface:
| Class | Usage |
|---|---|
ListQueue | A static ordered list of MediaItems (used for playlists and manual queuing) |
YouTubeQueue | A single YouTube Music video that auto-loads the radio for that track |
YouTubeAlbumRadio | Loads a YouTube Music album radio |
YouTubePlaylistQueue | Streams from a YouTube Music playlist endpoint |
LocalAlbumRadio | Radio seeded from a locally saved album |
EmptyQueue | A no-op queue used as a safe initial state |
Database Layer
All persistent user data is stored in a Room database (MusicDatabase). The database entities are in db/entities/:
| Entity | Description |
|---|---|
SongEntity | Core song record: video ID, title, artists, duration, thumbnail URL, like state, download state |
AlbumEntity | Album metadata: browse ID, title, year, thumbnail |
ArtistEntity | Artist record: channel ID, name, thumbnail, subscriber count |
PlaylistEntity | Playlist header: name, browse ID, thumbnail, YouTube sync state |
PlaylistSongMap | Many-to-many join table between playlists and songs, with custom sort position |
SongAlbumMap | Many-to-many join between songs and albums |
SongArtistMap | Many-to-many join between songs and artists |
LyricsEntity | Cached lyrics (LRC or plain text) keyed by video ID |
FormatEntity | Cached stream format info (itag, bitrate, MIME, loudness) keyed by video ID |
Event | A play-event record: song ID, timestamp, play duration. Used for statistics and Top Songs |
SearchHistory | Recent search queries |
RecognitionHistory | Results from the ShazamKit song recognition feature |
RelatedSongMap | Edges between songs for the radio/autoplay graph |
db/daos/ — one DAO per entity cluster, exposing Flow-returning query methods that Compose ViewModels collect.
User preferences (theme, audio quality, playback behaviour, feature toggles) are stored separately in a Jetpack DataStore instance accessed through the dataStore extension on Context. All preference keys are centralised in constants/PreferenceKeys.kt.
UI Layer
All UI is built with Jetpack Compose. There is no XML layout or View-based code in the main app flow.Screens
Screens are organised underui/screens/:
HomeScreen— quick picks, recently played, new releasesSearchScreen— local and online search with source toggleLibraryScreen— tabbed view covering Songs, Albums, Artists, Playlists, and MixAlbumScreen— album detail with track list and expressive album art design (whenUseExpressiveAlbumDesignKeyis on)ArtistScreen— artist detail with videos, discography, and subscriber countPlayerScreen— full-screen player with canvas backdrop, lyrics, and controlsExploreScreen— mood/genre browsing, charts, and new releasesHistoryScreen— listening history timelineSettingsScreenand all sub-screens undersettings/
Navigation
Navigation is handled byNavigationBuilder.kt using a NavHost with routes defined as a sealed class hierarchy in Screens.kt. Each destination is a string route (for example, "settings/backup_restore/autobackup"), and the builder wires each route to its Composable and scrollBehavior.
ViewModels
Each screen has a corresponding ViewModel inviewmodels/ (for example, HomeViewModel, AlbumViewModel, BackupRestoreViewModel). ViewModels use Hilt’s @HiltViewModel annotation and inject the Room database, DataStore, and network clients directly.
Dependency Injection
VIVI Music uses Hilt (Dagger-based DI for Android) throughout. The DI graph is defined indi/:
AppModule.kt— provides the RoomMusicDatabase, DataStore,SimpleCacheinstances (player cache and download cache), OkHttpClient, and theYouTubeinnertube client.NetworkModule.kt— provides the OkHttpClient with proxy support (whenProxyEnabledKeyis on) and IP version selection (IpVersionKey).Qualifiers.kt— defines@PlayerCacheand@DownloadCachequalifier annotations to distinguish the twoSimpleCacheinstances.
Network Layer
Online content is fetched through several routes::innertubemodule — wraps the unofficial YouTube Music internal API. All requests use OkHttp with optional proxy and IPv4/IPv6 selection. Cipher deobfuscation is handled here, keeping stream URL keys fresh automatically.:jiosaavnmodule — a separate OkHttp client for JioSaavn search and stream resolution.:lastfmmodule — standard REST calls to the Last.fm API using the API key baked intoBuildConfig.:lyricsProvidermodule — fans out requests to multiple lyrics APIs and returns the first successful result according to the user’s preferred provider order.