Skip to main content

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.

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.

Module Layout

The project is a multi-module Gradle build rooted at settings.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 ExoPlayer instance, plus secondary and fading players used during crossfade transitions.
  • Audio focus — requests and responds to AudioFocusRequest events, pausing playback when focus is lost and resuming when it returns.
  • Stream resolution — uses a ResolvingDataSource to intercept playback requests and resolve YouTube Music video IDs to signed DASH/HLS stream URLs via the :innertube module.
  • Cipher deobfuscation — decodes scrambled stream URLs using logic controlled by EnableAutoCipherFetchKey and CipherLastUpdatedKey.
  • Play event recording — writes an Event entity 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 CrossfadeEnabledKey is on.

Audio Processing Pipeline

ExoPlayer routes audio through a chain of processors before it reaches the audio sink:
ExoPlayer
  └─ SilenceSkippingAudioProcessor   (when SkipSilenceKey is on)
  └─ SonicAudioProcessor             (playback speed / pitch)
  └─ CustomEqualizerAudioProcessor   (EQ profiles from EQProfileRepository)
  └─ DefaultAudioSink
        └─ LoudnessEnhancer          (audio normalisation, when AudioNormalizationKey is on)

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 StateFlow properties (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.Listener to keep all flows in sync with player callbacks.

Queue Types

Queue strategies live in playback/queues/ and implement the Queue interface:
ClassUsage
ListQueueA static ordered list of MediaItems (used for playlists and manual queuing)
YouTubeQueueA single YouTube Music video that auto-loads the radio for that track
YouTubeAlbumRadioLoads a YouTube Music album radio
YouTubePlaylistQueueStreams from a YouTube Music playlist endpoint
LocalAlbumRadioRadio seeded from a locally saved album
EmptyQueueA 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/:
EntityDescription
SongEntityCore song record: video ID, title, artists, duration, thumbnail URL, like state, download state
AlbumEntityAlbum metadata: browse ID, title, year, thumbnail
ArtistEntityArtist record: channel ID, name, thumbnail, subscriber count
PlaylistEntityPlaylist header: name, browse ID, thumbnail, YouTube sync state
PlaylistSongMapMany-to-many join table between playlists and songs, with custom sort position
SongAlbumMapMany-to-many join between songs and albums
SongArtistMapMany-to-many join between songs and artists
LyricsEntityCached lyrics (LRC or plain text) keyed by video ID
FormatEntityCached stream format info (itag, bitrate, MIME, loudness) keyed by video ID
EventA play-event record: song ID, timestamp, play duration. Used for statistics and Top Songs
SearchHistoryRecent search queries
RecognitionHistoryResults from the ShazamKit song recognition feature
RelatedSongMapEdges between songs for the radio/autoplay graph
DAOs live in 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 under ui/screens/:
  • HomeScreen — quick picks, recently played, new releases
  • SearchScreen — local and online search with source toggle
  • LibraryScreen — tabbed view covering Songs, Albums, Artists, Playlists, and Mix
  • AlbumScreen — album detail with track list and expressive album art design (when UseExpressiveAlbumDesignKey is on)
  • ArtistScreen — artist detail with videos, discography, and subscriber count
  • PlayerScreen — full-screen player with canvas backdrop, lyrics, and controls
  • ExploreScreen — mood/genre browsing, charts, and new releases
  • HistoryScreen — listening history timeline
  • SettingsScreen and all sub-screens under settings/
Navigation is handled by NavigationBuilder.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 in viewmodels/ (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 in di/:
  • AppModule.kt — provides the Room MusicDatabase, DataStore, SimpleCache instances (player cache and download cache), OkHttpClient, and the YouTube innertube client.
  • NetworkModule.kt — provides the OkHttpClient with proxy support (when ProxyEnabledKey is on) and IP version selection (IpVersionKey).
  • Qualifiers.kt — defines @PlayerCache and @DownloadCache qualifier annotations to distinguish the two SimpleCache instances.

Network Layer

Online content is fetched through several routes:
  • :innertube module — 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.
  • :jiosaavn module — a separate OkHttp client for JioSaavn search and stream resolution.
  • :lastfm module — standard REST calls to the Last.fm API using the API key baked into BuildConfig.
  • :lyricsProvider module — fans out requests to multiple lyrics APIs and returns the first successful result according to the user’s preferred provider order.

Build docs developers (and LLMs) love