Comprehensive Technical Analysis & Documentation Report
I want you to act as a Senior Staff Software Engineer, Software Architect, and Technical Writer.
Analyze this GitHub repository completely. Do not just summarize it. Reverse engineer the entire project as if you were preparing documentation for a new engineering team joining the company.
Your goal is to understand EVERYTHING about this project.
For every folder, file, and major component, explain what it does, why it exists, and how it interacts with the rest of the application.
Produce a detailed report with the following sections.
#1. Project Overview
- What is this project?
- What problem does it solve?
- Target users
- Core purpose
- Main technologies used
- Architecture style
- Repository structure
#2. High Level Architecture
Explain
- frontend
- backend
- services
- APIs
- workers
- background jobs
- authentication
- database
- caching
- storage
- third-party integrations
- deployment
Create an architecture diagram using Mermaid.
#3. Repository Walkthrough
Go through every important directory.
For each folder explain
Purpose
Important files
Responsibilities
How it connects to other modules
Ignore only build artifacts and generated files.
#4. Application Flow
Explain the lifecycle of the application.
For example
User opens app
↓
Authentication
↓
Data loading
↓
API calls
↓
Business logic
↓
Database
↓
Response
↓
UI updates
Create flow diagrams where appropriate.
#5. Features
Identify every feature.
For each feature explain
- What it does
- Entry point
- Files involved
- Components involved
- APIs used
- Database tables
- State management
- Validation
- Error handling
- User flow
Do not skip hidden or admin features.
#6. Routing
Explain
- All routes
- Protected routes
- API routes
- Dynamic routes
- Middleware
- Navigation flow
#7. UI Components
List every reusable component.
Explain
Purpose
Props
State
Dependencies
Where it is used
#8. Backend
Explain
- Controllers
- Services
- Models
- Repositories
- Middleware
- Validation
- Authentication
- Authorization
- Background jobs
- Cron jobs
- Queues
- Event system
Explain the complete request lifecycle.
#9. Database
Identify
- Tables
- Collections
- Relationships
- Indexes
- Migrations
- ORM models
Explain why each table exists.
Create an ER diagram if possible.
#10. Authentication & Authorization
Explain
- Login flow
- Signup flow
- JWT/session/cookies
- OAuth providers
- Refresh tokens
- Roles
- Permissions
- Guards
- Middleware
#11. State Management
Explain
- Context
- Redux
- Zustand
- MobX
- React Query
- TanStack Query
- SWR
- Local state
Show how data flows through the application.
#12. API Documentation
List every API endpoint.
For each endpoint include
Method
URL
Purpose
Request body
Headers
Authentication
Response
Error responses
Files implementing it
#13. Business Logic
Explain the core business rules.
Describe
- Validation
- Algorithms
- Calculations
- Domain models
- Services
#14. Data Flow
Choose one important feature and trace data from
UI
↓
Component
↓
State
↓
API
↓
Backend
↓
Database
↓
Response
↓
UI
Mention every file involved.
#15. Dependencies
Explain why every major dependency exists.
Include
React
Next.js
Express
NestJS
Prisma
MongoDB
Supabase
Redis
etc.
Explain how each is used.
#16. Environment Variables
Document every environment variable.
Explain
Purpose
Required
Optional
Security implications
#17. Configuration
Explain
TypeScript
ESLint
Prettier
Webpack
Vite
Next config
Babel
Docker
CI/CD
GitHub Actions
#18. Performance
Explain
Caching
Lazy loading
Pagination
Memoization
Virtualization
Database optimization
Image optimization
Bundle splitting
Server rendering
#19. Security
Identify
Authentication
Authorization
Input validation
XSS protection
CSRF
SQL Injection
Rate limiting
Secrets management
Security concerns
Potential vulnerabilities
#20. Error Handling
Explain
Global error handling
Logging
Retries
Fallbacks
Monitoring
#21. Testing
Explain
Unit tests
Integration tests
E2E tests
Coverage
Frameworks
#22. Folder Dependency Graph
Show which folders depend on which other folders.
#23. Important Classes and Functions
Identify the most important files.
Explain
Purpose
Inputs
Outputs
Side effects
Interactions
#24. Code Quality Review
List
Good architectural decisions
Code smells
Technical debt
Possible bugs
Potential improvements
Scalability concerns
#25. Missing Features
Infer features that appear planned but unfinished.
Mention evidence.
#26. How to Extend the Project
Explain how to
- Add a new page
- Add a new API
- Add authentication
- Add a new feature
- Add database models
- Add a new service
#27. Learning Guide
If I were a new engineer joining this project, tell me:
- Which files to read first
- Which files are the most important
- Which concepts I should understand first
- Recommended reading order
#28. Executive Summary
Summarize
Architecture
Strengths
Weaknesses
Overall design
Scalability
Maintainability
Complexity
Rules
- Base conclusions on the code, not guesses.
- When uncertain, explicitly state what evidence exists and what remains unclear.
- Reference file paths throughout the report.
- Include code snippets only when they clarify important behavior.
- Use diagrams (Mermaid) where helpful.
- Be exhaustive rather than brief.
- If the repository is too large, analyze it incrementally and preserve context across multiple passes before producing the final report.
Perfect! Now I have a comprehensive understanding of the project. Let me create a detailed report based on all the information I've gathered.
#COMPREHENSIVE ANALYSIS: VIVI MUSIC - ANDROID MUSIC PLAYER
##1. Project Overview
###What is this project?
VIVI Music is a premium, open-source Android music player and streaming application that provides a high-fidelity, ad-free music experience with rich features and adaptive UI design. It serves as a sophisticated client for YouTube Music and multiple lyrics providers.
Repository: vivizzz007/vivi-music
Platform: Android (Kotlin/Compose)
Version: 6.0.3
License: GPL-3.0
Target API: Android 26+ (Android 8.0 and above)
Compile API: Android 36 (Android 15)
###What problem does it solve?
- Ad-Free Streaming: Provides seamless ad-free music streaming from YouTube Music
- Privacy-First: 100% local database with zero tracking or telemetry
- Rich Features: Synced lyrics, equalizer, music recognition, offline downloads
- Material Design: Dynamic Material You theme that adapts to album artwork
- Advanced Visualizations: Apple Music-style animated canvas visualizers
- Social Listening: Listen Together feature for shared playback sessions
###Target Users
- Android users seeking premium music experience without ads
- Privacy-conscious listeners
- Music enthusiasts who want fine-grained audio control
- Users with low-bandwidth needs (offline mode)
- F-Droid and open-source community
###Core Purpose
A feature-rich, privacy-preserving music player with YouTube Music integration, synchronized lyrics, audio personalization, and real-time collaborative listening.
###Main Technologies Used
| Layer | Technology |
|---|---|
| Language | Kotlin 2.3.10 |
| UI Framework | Jetpack Compose 1.10.2 |
| Material Design | Material 3 1.5.0, MaterialKolor 4.1.1 |
| Media Playback | MediaPlayer 3 (ExoPlayer) 1.7.1 |
| Database | Room 2.8.4 (SQLite) |
| Networking | Ktor Client 3.4.0 |
| Dependency Injection | Hilt 2.59.1 |
| Coroutines | Kotlin Coroutines + Flow |
| Serialization | Kotlinx Serialization, JSON |
| Image Loading | Coil 3.3.0 |
| Additional Services | Last.fm, Shazam, NewPipe Extractor |
###Architecture Style
Multi-layered modular architecture with vertical feature slicing:
- Presentation Layer (UI/Compose)
- Domain/Business Logic Layer (ViewModels, Managers)
- Data Access Layer (Room DAO, Services)
- External Service Layer (YouTube InnerTube, Lyrics Providers)
###Repository Structure
vivi-music/
├── app/ # Main Android application
│ ├── src/main/
│ │ ├── kotlin/com/music/vivi/
│ │ │ ├── ui/ # Jetpack Compose UI
│ │ │ ├── playback/ # MediaPlayer/ExoPlayer management
│ │ │ ├── db/ # Room database entities & DAO
│ │ │ ├── viewmodels/ # MVVM ViewModels
│ │ │ ├── lyrics/ # Multi-provider lyrics system
│ │ │ ├── recognition/ # Music recognition service
│ │ │ ├── di/ # Hilt dependency injection
│ │ │ ├── constants/ # Preference keys & constants
│ │ │ ├── utils/ # Utilities (network, cache, etc.)
│ │ │ ├── listentogether/ # Collaborative listening
│ │ │ └── api/ # Third-party API clients
│ │ ├── res/ # Resources (layouts, drawables, strings)
│ │ └── AndroidManifest.xml
│ └── build.gradle.kts
│
├── innertube/ # YouTube InnerTube API client library
├── canvas/ # Canvas visualization components
├── kugou/ # KuGou lyrics provider
├── lrclib/ # LrcLib lyrics provider
├── betterlyrics/ # Better Lyrics provider
├── youlyplus/ # YouLyPlus lyrics provider
├── simpmusic/ # SimpMusic lyrics provider
├── paxsenixlyrics/ # Pax Senix lyrics provider
├── shazamkit/ # Shazam music recognition
├── lastfm/ # Last.fm integration
├── spotify/ # Spotify integration
├── jiosaavn/ # JioSaavn music provider
├── artistvideo/ # Artist video content
├── applecanvas/ # Apple Music visualizer
├── vivimusiccanvas/ # ViviMusic custom visualizer
├── kizzy/ # Discord RPC integration
├── gradle/ # Gradle configuration
├── scripts/ # Build scripts
├── News/ # Update news configuration
└── development_guide.md # Development setup
**Total Kotlin Files in App**: ~396 files
##2. High-Level Architecture
###Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ Android UI Layer (Compose) │
│ MainActivity → Navigation → Screens → Components → Theme │
└──────────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────────┐
│ ViewModel & State Management Layer │
│ HomeViewModel, AlbumViewModel, PlayerConnection, etc. │
│ (Handles business logic & state orchestration) │
└──────────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────────┐
│ Data & Service Layer │
│ ┌────────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ MusicService │ │ LyricsHelper │ │ MusicRecognition │ │
│ │ (Playback) │ │ │ │ Service │ │
│ └────────────────┘ └──────────────┘ └────────────────────┘ │
│ ┌────────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ Room Database │ │ DataStore │ │ Cache Management │ │
│ │ (Local State) │ │ (Prefs) │ │ (ExoPlayer Cache) │ │
│ └────────────────┘ └──────────────┘ └────────────────────┘ │
└──────────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────────┐
│ External APIs & Providers Layer │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ YouTube InnerTube (innertube module) │ │
│ │ - Search, Browse, Player, Feedback, Comments │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Lyrics Providers (Multi-Source): │ │
│ │ - YouLyPlus, KuGou, LrcLib, Better Lyrics │ │
│ │ - SimpMusic, Pax Senix, YouTube, YouTube Subtitles │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Additional Services: │ │
│ │ - Shazam (Music Recognition) │ │
│ │ - Last.fm (Scrobbling) │ │
│ │ - Spotify (Import) │ │
│ │ - Wikipedia (Artist Info) │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
###Frontend Architecture
Technology: Jetpack Compose (100% declarative UI)
Key Screens:
- Home Screen - Main feed with recommendations
- Search Screen - Online & local search with suggestions
- Library Screen - Local music collection management
- Listen Together - Collaborative playback
- Player Screen - Full-screen player with visualizers
- Artist/Album/Playlist Screens - Content detail views
- Settings - Extensive user preferences
UI Components (115+ reusable components):
- Navigation (AppNavigation, NavigationBuilder)
- Lyrics Displays (Lyrics, MetroLyrics, ViviMusicLyrics)
- Player Controls (PlayerSlider, VolumeSlider, BigSeekBar)
- Menu Systems (BottomSheetMenu, GridMenu, Dialog)
- Items (Album/Artist/Song cards)
- Specialized (Equalizer, AudioDevice selector, Lyrics provider selector)
Theme System:
- Material 3 with dynamic color extraction from album artwork
- Pure black mode for OLED displays
- Adaptive font support (5 fonts)
- Density scaling for UI customization
###Backend Architecture
Music Service (foreground service):
- Manages ExoPlayer lifecycle
- Handles media session callbacks
- Controls playback, queuing, and shuffling
- Manages audio focus and device switching
- Provides notification and media controls
Database Layer:
- Room ORM with SQLite backend
- 24+ entity types with relationships
- Comprehensive DAO with 100+ query methods
- Transaction support for atomic operations
Playback System:
- ExoPlayer 3.7.1 for audio decoding
- Custom queue system with multiple implementations
- Audio effects (Equalizer, Loudness Enhancer)
- Cache management (dual cache: playback + downloads)
###Services
Core Services:
- MusicService - Foreground playback service
- ExoDownloadService - Background downloads
- MusicRecognitionService - Audio fingerprinting
- NewReleaseCheckWorker - Background update checks
Helper Services:
- LyricsHelper - Multi-provider lyrics resolution
- LyricsTranslationHelper - Lyrics translation (AI-powered)
- ListenTogetherManager - Collaborative listening
- AudioDeviceBottomSheet - Device selection
- AutoBackupWorker - Scheduled backups
###APIs
YouTube InnerTube API (via innertube module):
- Search (songs, albums, artists, playlists)
- Browse (home, explore, mood & genres, charts)
- Artist items & relationships
- Playlist operations (create, edit, add/remove songs)
- Feedback (like, dislike, rating)
- Account operations (library sync)
- Comments & transcripts
Lyrics APIs (multiple providers with fallback):
- YouLyPlus API
- KuGou API
- LrcLib API
- Better Lyrics API
- SimpMusic API
- Pax Senix API
- YouTube subtitles extraction
Third-Party APIs:
- Shazam (music recognition)
- Last.fm (scrobbling, artist info)
- Spotify (playlist import)
- DeepL (lyrics translation)
- Mistral AI (AI features)
- Wikipedia (artist information)
###Workers & Background Jobs
Scheduled Tasks:
- NewReleaseCheckWorker - Checks for app updates (WorkManager)
- AutoBackupWorker - Periodic database backups
Background Services:
- ExoDownloadService for offline downloading
- Media3 MediaSession for remote controls
- BroadcastReceiver for audio device changes
###Authentication
YouTube Authentication:
- No user login required (uses InnerTube client API)
- VisitorData token for anonymous access
- Cookie support for authenticated requests
- DataSync ID for personalized content
Last.fm:
- API key-based authentication (keys embedded in build config)
- Hardcoded credentials for app-level access
Service Authorization:
- Network-scoped authorization
- Proxy support with authentication
- IP version preference (IPv4/IPv6/Auto)
###Database
Type: Room ORM with SQLite Version: Currently at migration 10+ Tables: 24+ entities
Core Entities:
- SongEntity - Track metadata
- ArtistEntity - Artist information
- AlbumEntity - Album details
- PlaylistEntity - User playlists
- LyricsEntity - Cached lyrics
- PlayCountEntity - Play statistics
Relationship Tables:
- SongArtistMap, SongAlbumMap, AlbumArtistMap
- PlaylistSongMap, PlaylistSongMapPreview
- RelatedSongMap (recommendations)
Utility Tables:
- SearchHistory, RecognitionHistory
- SetVideoIdEntity, FormatEntity
- Event, EventWithSong (tracking)
###Caching
Multi-Level Cache Strategy:
-
Memory Cache:
- Coil image loader with memory cache
- LRU lyrics cache (50 items)
- ViewModels hold hot data
-
Disk Cache:
- exoplayer/ - Streaming cache (configurable, default 1GB LRU)
- download/ - Offline downloads (unlimited, no eviction)
- Coil disk cache - Image cache
-
Database Cache:
- Room SQLite database for persistent state
- DataStore for preferences
-
Smart Caching:
- Network-first for online content
- Cache-first for local data
- Fallback strategies for network failures
###Storage
Local Storage Paths:
- app-private files/: ExoPlayer cache, downloads
- Downloads/vivimusic/: Auto-backup directory
- SharedPreferences via DataStore: Encrypted preferences
Data Retained:
- Music metadata (titles, artists, album art URLs)
- Play history and statistics
- Playlists and liked songs
- Download cache
- User preferences
No Cloud Sync: Everything is local to the device
###Third-Party Integrations
- YouTube Music - Primary music source
- Shazam - Music recognition
- Last.fm - Scrobbling & stats
- Spotify - Playlist import only
- JioSaavn - Music provider
- Wikipedia - Artist information
- Discord RPC - Activity status
- DeepL/Mistral AI - Lyrics translation
- Google Cast - Chromecast support (GMS variant)
###Deployment
Build System: Gradle 9.0.0 (Kotlin DSL)
Variants:
- FOSS variant (F-Droid): No Google Play Services, no Chromecast
- GMS variant (Play Store): Full Chromecast support
ABIs:
- universal (all architectures)
- arm64 (64-bit ARM)
- armeabi (32-bit ARM)
- x86, x86_64
Distribution Channels:
- GitHub Releases (universal APK)
- F-Droid (FOSS variant)
- Google Play Store (GMS variant)
Release Process:
- Automated builds via Gradle
- ProGuard obfuscation for release
- Signed with release keystore
- Fastlane metadata for distribution
Versioning:
- versionCode: 72
- versionName: "6.0.3"
- Nightly builds supported
##3. Repository Walkthrough
###/app - Main Application Module
Purpose: Core Android application containing all UI, business logic, and user-facing features.
Size: ~396 Kotlin files, largest module
Key Directories:
####/app/src/main/kotlin/com/music/vivi/ui
Purpose: All Jetpack Compose UI code
Structure:
-
screens/(40+ screen composables)- Home, Search, Library, Listen Together (main navigation)
- Album, Artist, Playlist detail screens
- Settings (appearance, audio, account, etc.)
- Search (online & local)
- History, Stats, Charts
- Wrapped (Spotify Wrapped alternative)
- Library sub-screens (playlists, downloads, artists)
- Recognition (Shazam integration UI)
-
player/(Player UI components)- Player.kt - Main full-screen player
- MiniPlayer.kt - Minimized player
- Queue.kt - Queue management
- CanvasArtworkPlayer.kt - Animated visualizer
- Thumbnail handling
-
component/(115+ reusable components)- Navigation, bottomsheet, dialogs
- Lyrics displays (MetroLyrics, ViviMusicLyrics, LyricsV2)
- Sliders (PlayerSlider, WavySlider, SquigglySlider, VolumeSlider)
- Items (album, artist, song cards)
- Preference UI (Material3SettingsGroup)
- Specialized (Equalizer, AudioDevice, Lyrics provider selector)
-
menu/(Context menus)- Song/Album/Artist/Playlist menus
- Import/export dialogs
- Selection menus
- YouTube-specific menus
-
theme/- Theme.kt - Material 3 theming
- PlayerColorExtractor.kt - Album art color extraction
- Font.kt - Font definitions
- Type.kt - Typography setup
-
utils/- Navigation utilities
- String/formatting utils
- Scroll, shape, and layout utils
- Media info display
####/app/src/main/kotlin/com/music/vivi/playback
Purpose: Media playback engine and queue management
Files:
-
MusicService.kt (3400+ lines) - Core playback service
- Manages ExoPlayer lifecycle
- Handles audio focus
- Media session callbacks
- Notification management
- Audio effects (EQ, loudness enhancer)
- Format detection
- Cast support
- Playback logging
-
PlayerConnection.kt (502 lines) - Bridge between service and UI
- Manages connection to MusicService
- State flows for playback state, metadata, queue
- Player readiness checking
- Safe player access
-
MediaLibrarySessionCallback.kt - Media3 session integration
- Handles remote playback commands
- Queue manipulation
- Playlist operations
- Search/browse from other apps
-
ExoDownloadService.kt - Background download management
-
DownloadUtil.kt - Download utility functions
-
SleepTimer.kt - Sleep timer implementation
-
queues/ (Queue implementations)
- Queue.kt - Abstract queue interface
- ListQueue.kt - Static list-based queue
- YouTubeQueue.kt - Remote YouTube queue
- YouTubePlaylistQueue.kt - Playlist queue
- YouTubeAlbumRadio.kt - Album radio station
- LocalAlbumRadio.kt - Local album radio
- EmptyQueue.kt - Empty queue placeholder
-
audio/ (Audio effects)
- SilenceDetectorAudioProcessor.kt
####/app/src/main/kotlin/com/music/vivi/db
Purpose: Local database layer using Room ORM
Files:
-
MusicDatabase.kt (716 lines) - Room database definition
- 24+ entities
- 10+ migrations
- Transaction & query execution
- Database versioning
-
DatabaseDao.kt (1603 lines) - Data access object
- 100+ query methods
- Song queries (various sorts: name, artist, playtime, date)
- Album/artist queries
- Playlist operations
- Statistics aggregation
- Search functionality
- Relationship queries
-
Converters.kt - Type converters for Room
- LocalDateTime conversion
- JSON serialization
-
daos/SpeedDialDao.kt - Quick dial shortcuts
-
entities/ (32 entity files)
- Core: SongEntity, ArtistEntity, AlbumEntity, PlaylistEntity
- Relationships: *Map classes (SongArtistMap, etc.)
- Utility: LyricsEntity, PlayCountEntity, FormatEntity
- History: SearchHistory, RecognitionHistory
- Views: Album, Artist, Song (with relationships)
####/app/src/main/kotlin/com/music/vivi/viewmodels
Purpose: MVVM ViewModels for each feature
Key ViewModels (28 total):
- HomeViewModel.kt (771 lines) - Home screen recommendations
- AlbumViewModel.kt - Album detail
- ArtistViewModel.kt - Artist detail
- OnlineSearchViewModel.kt - YouTube search
- LocalSearchViewModel.kt - Local search
- PlaylistsViewModel.kt - Playlist management
- HistoryViewModel.kt - Play history
- StatsViewModel.kt - Statistics & wrapped
- BackupRestoreViewModel.kt - Backup/restore operations
- SpotifyImportViewModel.kt - Import from Spotify
- ListenTogetherViewModel.kt - Collaborative listening
- AccountSettingsViewModel.kt - Account preferences
- ThemeViewModel.kt - Theme management
####/app/src/main/kotlin/com/music/vivi/lyrics
Purpose: Multi-source lyrics system with fallback strategy
Architecture:
-
LyricsHelper.kt - Main orchestrator
- Fetches from providers in priority order
- Caches results
- Network connectivity checking
- Fallback logic
-
LyricsProviderRegistry.kt - Provider management
- Registers available providers
- Manages provider ordering
- Enables/disables providers
-
LyricsProvider.kt - Abstract interface
-
Implementations (10 providers):
- YouLyPlusLyricsProvider.kt
- KuGouLyricsProvider.kt
- LrcLibLyricsProvider.kt
- BetterLyricsProvider.kt
- SimpMusicLyricsProvider.kt
- PaxSenixLyricsProvider.kt
- YouTubeLyricsProvider.kt
- YouTubeSubtitleLyricsProvider.kt
-
LyricsTranslationHelper.kt (21K) - AI-powered translation
- Supports DeepL, Mistral, OpenRouter APIs
- Handles formatting preservation
- Error recovery
-
LyricsUtils.kt (56K) - Parsing & formatting
- LRC format parsing
- Timestamp syncing
- Text normalization
- Character encoding handling
####/app/src/main/kotlin/com/music/vivi/recognition
Purpose: Music recognition using audio fingerprinting
Files:
-
MusicRecognitionService.kt - Main service
- Records audio from microphone
- Converts to fingerprint
- Calls Shazam API
- Handles recognition status
-
ShazamSignatureGenerator.kt - Generates audio fingerprint
-
AudioResampler.kt - Resamples audio to target rate
-
VibraSignature.kt - Alternative fingerprinting method
####/app/src/main/kotlin/com/music/vivi/di
Purpose: Hilt dependency injection configuration
Files:
-
AppModule.kt - Singleton dependencies
- Database, DAO, cache providers
- Application scope coroutine
- ListenTogether client/manager
-
NetworkModule.kt - Network dependencies
- Network connectivity observer
-
LyricsHelperEntryPoint.kt - Entry point for lyrics helper
-
WrappedModule.kt - Wrapped feature injection
-
Qualifiers.kt - Custom injection qualifiers
- @PlayerCache, @DownloadCache
- @ApplicationScope
####/app/src/main/kotlin/com/music/vivi/constants
Purpose: Constants and preference keys
Files:
-
PreferenceKeys.kt (772 lines) - DataStore preference keys
- UI preferences (theme, fonts, density)
- Audio preferences (EQ, normalization, quality)
- Playback preferences (repeat, shuffle, autoplay)
- Lyrics preferences (provider order)
- Network preferences (proxy, IP version)
- 100+ preference keys total
-
HistorySource.kt - History sources enum
-
LibraryFilter.kt - Library filtering options
-
MediaSessionConstants.kt - Media session command definitions
-
Dimensions.kt - Layout dimensions
-
StatPeriod.kt - Statistics time period options
####/app/src/main/kotlin/com/music/vivi/utils
Purpose: Utility functions and helpers
Key Files:
-
DataStore.kt - Preferences wrapper
- DataStore access operators
- Preference helpers with defaults
- Type converters
-
ViviPrefCache.kt - In-memory preference cache
- Fast access to preferences
- Observes DataStore changes
-
NetworkConnectivityObserver.kt - Network state
- Observes network connectivity
- Fallback synchronous checks
-
ScrobbleManager.kt - Last.fm scrobbling
- Sends plays to Last.fm
- Manages artist listening stats
-
AutoBackupHelper.kt - Database backup
- Scheduled backups to Downloads
- Restore functionality
-
CrashHandler.kt - Global exception handler
- Catches unhandled exceptions
- Launches crash activity
-
PlaybackLogManager.kt - Logging playback events
-
DiscordRPC.kt - Discord Rich Presence
- Shows currently playing song
- Updates real-time
-
SyncUtils.kt - YouTube library sync
- Syncs liked songs
- Syncs playlist changes
-
CipherDeobfuscator.kt - WEB_REMIX decryption
- Decodes WEB_REMIX streaming cipher
- Handles obfuscated player code
-
Utils.kt - General utilities
- Device info
- String/number formatting
- Color utilities
-
YTPlayerUtils.kt - YouTube player utilities
-
AppleMusicAboutAlbum.kt - Apple Music album art fetching
####/app/src/main/kotlin/com/music/vivi/listentogether
Purpose: Collaborative listening sessions
Files:
-
ListenTogetherManager.kt (1726 lines) - Main manager
- Bridges WebSocket client with player
- Syncs playback between users
- Manages queue changes
- Handles volume sync
-
ListenTogetherClient.kt - WebSocket client
- Connects to ListenTogether servers
- Sends/receives commands
- Handles reconnection
-
ListenTogetherServers.kt - Server list management
-
Protocol.kt - Message protocol definitions
-
MessageCodec.kt - Message encoding/decoding
-
ListenTogetherActionReceiver.kt - Broadcast receiver for intent handling
####/app/src/main/kotlin/com/music/vivi/api
Purpose: Third-party API clients
Files:
- DeepLService.kt - DeepL translation API
- MistralService.kt - Mistral AI API
- OpenRouterService.kt - OpenRouter API
- OpenRouterStreamingService.kt - Streaming responses
####/app/src/main/kotlin/com/music/vivi/extensions
Purpose: Kotlin extension functions
Files:
- ContextExt.kt - Context extensions
- CoroutineExt.kt - Coroutine helpers
- FileExt.kt - File operations
- ListExt.kt - List utilities
- MediaItemExt.kt - ExoPlayer MediaItem helpers
- ModifierExt.kt - Compose modifier utilities
- PlayerExt.kt - ExoPlayer extensions
- QueueExt.kt - Queue utilities
- StringExt.kt - String manipulation
- UtilExt.kt - General utilities
####/app/src/main/kotlin/com/music/vivi/eq
Purpose: Audio equalizer system
Structure:
- EqualizerService.kt - Main equalizer service
- audio/ - Audio processing
- data/ - EQ preset models
####/app/src/main/kotlin/com/music/vivi/widget
Purpose: App widgets for home screen
Files:
- MetrolistWidgetManager.kt - Metro-style widget
- TurntableWidgetReceiver.kt - Turntable widget
- MusicWidgetReceiver.kt - Standard widget
###/innertube - YouTube API Client Library
Purpose: Abstraction layer for YouTube InnerTube API
Architecture:
-
YouTube.kt (1765 lines) - High-level API facade
- Public suspend functions for all endpoints
- Search, browse, player operations
- Playlist management
- Feedback (like, rate, add to library)
-
InnerTube.kt (797 lines) - Low-level HTTP client
- Ktor client configuration
- Request building
- Response parsing
- Network error handling
- Retry logic
-
NetworkConfig.kt - Network configuration
- Proxy settings
- IP version preferences
- Timeout configuration
-
models/ - Response DTOs (~30+ files)
- Context, Headers, Client types
- Renderers (MusicCarouselShelfRenderer, etc.)
- Endpoints (WatchEndpoint, BrowseEndpoint)
- Response types
-
pages/ - Parsed response pages
- HomePage, AlbumPage, ArtistPage
- PlaylistPage, SearchPage
- Page continuation handling
Responsibilities:
- Encapsulates YouTube API protocol
- Handles client context & headers
- Manages authentication tokens
- Provides type-safe Kotlin APIs
Dependencies: Ktor, NewPipe Extractor, Brotli compression
###/canvas - Canvas Visualizer
Purpose: Animated background visualizers for player
Files (3):
- CanvasArtwork.kt - Abstract canvas interface
- AppleMusicArtistBackgroundProvider.kt - Apple Music style backgrounds
- TidalCanvasProvider.kt - Tidal style visualizations
Technology: Jetpack Compose canvas API
###/kugou, /lrclib, /betterlyrics, /youlyplus, /simpmusic, /paxsenixlyrics - Lyrics Providers
Purpose: External lyrics provider integrations
Each module contains:
- HTTP client setup
- API response models
- Lyrics fetching logic
- Error handling
###/shazamkit - Music Recognition
Purpose: Shazam-compatible music recognition library
Functionality:
- Audio fingerprinting
- Server communication
- Recognition result parsing
###/lastfm - Last.fm Integration
Purpose: Scrobbling and artist information
Features:
- Send plays to Last.fm
- Fetch artist stats
- User profile integration
###/spotify - Spotify Integration
Purpose: Playlist import from Spotify
###/jiosaavn - JioSaavn Provider
Purpose: Additional music metadata source
###Other Modules
- applecanvas - Apple Music-style visualizer
- vivimusiccanvas - Custom ViviMusic visualizer
- artistvideo - Artist video content
- kizzy - Discord RPC integration
##4. Application Flow
###User Opening App
1. Process Start
↓
2. App.onCreate() [Application class]
├─ Initialize crash handler
├─ Start preference cache
├─ Initialize cipher deobfuscator (WEB_REMIX support)
├─ Plant Timber logging
└─ Observe settings changes
↓
3. WelcomeActivity (if first run)
└─ Show onboarding
↓
4. MainActivity.onCreate()
├─ Connect to MusicService via bindService()
├─ Initialize PlayerConnection
├─ Set Compose content
├─ Initialize navigation
└─ Set up status bar styling
↓
5. Jetpack Compose UI Tree Initialized
├─ MainActivity sets up root composable
├─ Theme applied (Material 3)
├─ Navigation graph ready
└─ Composables listen to player state flows
↓
6. MusicService Foreground Service Started
├─ Create ExoPlayer
├─ Initialize media session
├─ Load player state from preferences
├─ Apply audio settings (EQ, normalization)
└─ Register audio device callback
↓
7. Home Screen Displayed
├─ HomeViewModel loaded
├─ YouTube home feed fetched asynchronously
├─ Local library queried
├─ Composition shown with skeleton loading
└─ UI updates as data arrives
###Playing a Song
1. User taps song (from search, home, playlist, etc.)
↓
2. UI calls PlayerConnection.playSong() or enqueueSong()
↓
3. PlayerConnection updates player queue
├─ Creates MediaItem from song metadata
├─ Sets repeat/shuffle mode
└─ Starts playback (player.play())
↓
4. ExoPlayer begins playback
├─ Resolves media source
├─ Fetches streaming URL from YouTube InnerTube
├─ Initializes cache data source
├─ Applies audio effects (EQ)
└─ Starts audio decoding
↓
5. MusicService monitors playback state
├─ Updates notification
├─ Sends media session callbacks
├─ Updates playback log
└─ Manages audio focus
↓
6. UI Observes and Updates
├─ PlayerConnection.playbackState flow updated
├─ PlayerConnection.mediaMetadata flow updated
├─ Compose recomposes with new player state
├─ Song info displayed
├─ Seek bar shows duration & position
└─ Lyrics fetched asynchronously
↓
7. Playback Continues
├─ Real-time position updates (via position listener)
├─ Next song auto-loads in queue
├─ Audio device changes detected
└─ Playback completes → next song plays or repeats
↓
8. Song Ends
├─ LastFM scrobble sent
├─ Play count incremented in database
├─ Next song in queue starts
└─ Recommendation updated
###Searching for Music
1. User navigates to Search Screen
↓
2. OnlineSearchSuggestionViewModel fetches suggestions
├─ YouTube.searchSuggestions() called
├─ Network request to YouTube InnerTube
└─ Suggestions displayed in real-time
↓
3. User enters query
↓
4. OnlineSearchViewModel processes search
├─ User text triggers search coroutine
├─ YouTube.search() called with query
├─ Results parsed into SongItem, AlbumItem, ArtistItem
├─ Filtered by user preferences (hide explicit, video songs, shorts)
└─ Results displayed
↓
5. User taps result
├─ Song → Added to queue & played
├─ Album → Fetch AlbumViewModel, display album detail
├─ Artist → Fetch ArtistViewModel, display artist page
└─ Playlist → Fetch PlaylistViewModel, load tracks
↓
6. Detail View Loads
├─ Related endpoint fetched
├─ Songs/items loaded paginated
├─ Metadata (description, stats) displayed
└─ Additional recommendations loaded
###Lyrics Synchronization
1. Song starts playing
↓
2. PlayerConnection emits new mediaMetadata
↓
3. UI composable (LyricsV2, MetroLyrics, etc.) observes
↓
4. Lyrics fetch triggered
├─ LyricsHelper.getLyrics() called
├─ Check in-memory LRU cache
│ └─ If found, return cached
├─ Check database
│ └─ If found, use cached
├─ Iterate through providers (priority ordered):
│ ├─ YouLyPlus
│ ├─ KuGou
│ ├─ LrcLib
│ ├─ Better Lyrics
│ ├─ SimpMusic
│ ├─ Pax Senix
│ ├─ YouTube (native)
│ └─ YouTube Subtitles
├─ First successful provider returns
├─ Handle sync offset preference
└─ Cache result in DB
↓
5. Lyrics UI displays
├─ Parse timestamps
├─ Highlight current line as time progresses
├─ Scroll to current position
└─ Apply translation if enabled (AI call to DeepL/Mistral)
###Download Management
1. User taps download button on song
↓
2. Database updated
├─ isDownloaded flag set
└─ dateDownload timestamp set
↓
3. ExoDownloadService started
├─ Music fetched from YouTube
├─ Stored in app-private files/download/ directory
├─ Cache data source used for efficiency
└─ Notification shows progress
↓
4. Download completes
├─ Notification updated
├─ File available for offline playback
└─ Database marked as downloaded
↓
5. Offline playback
├─ Song played from cache
├─ No network needed
└─ Full audio quality preserved
###Listen Together (Collaborative Listening)
1. Host creates session
├─ Generates invite code
├─ ListenTogetherManager initialized
└─ Broadcast server URL to guests
↓
2. Guests join with code
├─ WebSocket connection established
├─ ListenTogetherClient connects
└─ Joins listening room
↓
3. Host plays song
├─ PlayerConnection sends PLAY event
├─ ListenTogetherManager intercepts
├─ Sends queue + position over WebSocket
└─ Guests receive event
↓
4. Guests' players sync
├─ Queue updated locally
├─ Seek to host's position
├─ Playback started
├─ Position tolerance checked (2-3 second threshold)
└─ Volume synced if enabled
↓
5. Real-time sync maintained
├─ Every 1000ms, position checked
├─ If drift > tolerance, guest seeks
├─ Host pause → All guests pause
├─ Host next → All guests next
└─ Playback stays synchronized
##5. Features
###1. Music Streaming & Playback
Entry Point: MusicService, PlayerConnection
Files Involved:
- playback/MusicService.kt
- playback/PlayerConnection.kt
- playback/queues/*
- ui/player/*
Components: Player, MiniPlayer, Queue display
APIs:
- YouTube InnerTube (fetch streaming URLs)
- ExoPlayer API (playback control)
Database: SongEntity, FormatEntity (resolution/bitrate info)
State Management:
- PlayerConnection state flows
- MusicService internal player state
Validation:
- Video ID validation
- Format availability checking
Error Handling:
- Network errors → Retry with exponential backoff
- Codec unsupported → Try alternative format
- Playback errors → Skip to next song
###2. Synchronized Lyrics Display
Entry Point: LyricsV2, MetroLyrics, ViviMusicLyrics composables
Files Involved:
- lyrics/LyricsHelper.kt
- lyrics/LyricsUtils.kt
- lyrics/LyricsTranslationHelper.kt
- lyrics/[Provider]LyricsProvider.kt
- ui/component/Lyrics.kt
- ui/component/LyricsV2.kt
- ui/component/MetroLyrics.kt
- ui/component/ViviMusicLyrics.kt
Components: Multiple lyrics display modes
APIs:
- 10 lyrics providers (YouLyPlus, KuGou, LrcLib, etc.)
- DeepL/Mistral AI for translation
Database: LyricsEntity (cached lyrics with timestamps)
State Management:
- LyricsHelper manages provider ordering
- Compose observes current position
- Real-time sync with ExoPlayer position
Validation:
- Timestamp format validation
- Encoding detection and normalization
Error Handling:
- Provider failure → Try next provider
- Network error → Use cached lyrics
- Invalid format → Graceful fallback
###3. Music Recognition (Shazam Integration)
Entry Point: MusicRecognitionService
Files Involved:
- recognition/MusicRecognitionService.kt
- recognition/ShazamSignatureGenerator.kt
- recognition/AudioResampler.kt
- recognition/VibraSignature.kt
- ui/screens/recognition/*
Components: Recognition UI screen
APIs: Shazam API
Database: RecognitionHistory (save recognized songs)
Validation:
- Microphone permission checking
- Audio quality validation
- Recognition status tracking
Error Handling:
- No microphone permission → Show permission request
- Recognition failed → Display error message
- Network unavailable → Show offline message
###4. Audio Customization (Equalizer)
Entry Point: Audio settings screen
Files Involved:
- eq/EqualizerService.kt
- eq/audio/
- eq/data/
- ui/screens/equalizer/
Components: EQ slider UI
Database: User-defined preset storage
State Management:
- EQ settings stored in DataStore
- Loudness enhancer settings
Implementation:
- Android MediaSessionCompat audio effects
- LoudnessEnhancer for volume normalization
###5. Local Library Management
Entry Point: Library screen
Files Involved:
- db/DatabaseDao.kt (all song/album/artist queries)
- db/entities/*.kt
- viewmodels/LibraryViewModels.kt
- ui/screens/library/*
Components: Library sorting, filtering, searching
Database:
- SongEntity with inLibrary timestamp
- AlbumEntity, ArtistEntity
- Relationship tables
State Management:
- DatabaseDao flows for reactive updates
- ViewModels cache sorted results
Features:
- Multiple sort orders (name, artist, date added, playtime)
- Filter by artist/album
- Search local library
- Like/unlike songs
###6. Playlist Management
Entry Point: Playlists screen
Files Involved:
- db/entities/PlaylistEntity.kt
- db/entities/PlaylistSongMap.kt
- viewmodels/PlaylistsViewModel.kt
- viewmodels/LocalPlaylistViewModel.kt
- viewmodels/OnlinePlaylistViewModel.kt
- menu/PlaylistMenu.kt
APIs:
- YouTube InnerTube for online playlists
- Local Room database for custom playlists
Database:
- PlaylistEntity (playlist metadata)
- PlaylistSongMap (many-to-many relationship)
- PlaylistSongMapPreview (preview generation)
Features:
- Create custom playlists
- Add/remove songs
- Online playlist sync
- Playlist sharing links
###7. Listen Together (Collaborative Listening)
Entry Point: Listen Together screen
Files Involved:
- listentogether/ListenTogetherManager.kt
- listentogether/ListenTogetherClient.kt
- listentogether/Protocol.kt
- ui/screens/ListenTogetherScreen.kt
APIs: WebSocket-based custom protocol
State Management:
- WebSocket message queue
- Playback state synchronization
- Queue sync across users
Features:
- Create listen sessions
- Share invite codes
- Real-time sync
- Volume sync option
- Smart resync (drift correction)
###8. Offline Downloading
Entry Point: Download button on song
Files Involved:
- playback/ExoDownloadService.kt
- playback/DownloadUtil.kt
- db/entities/SongEntity.kt (isDownloaded field)
APIs:
- YouTube InnerTube (fetch stream URLs)
- ExoPlayer cache system
Database:
- SongEntity.isDownloaded flag
- SongEntity.dateDownload timestamp
Storage:
- app-private files/download/ directory
- No LRU eviction (unlimited storage)
Features:
- Download individual songs
- Bulk download
- Track download progress
- Delete downloads
- Automatic offline fallback
###9. Backup & Restore
Entry Point: Backup/Restore screen
Files Involved:
- viewmodels/BackupRestoreViewModel.kt
- utils/AutoBackupHelper.kt
- utils/AutoBackupWorker.kt
- menu/PlaylistScreenMenus.kt
Database: Entire Room database exported/imported
Storage: Downloads/vivimusic/ directory
Features:
- Manual backup to cloud
- Automatic scheduled backups
- Full database restore
- Import from Spotify
- Import CSV playlists
###10. Search (Online & Local)
Entry Point: Search screen
Files Involved:
- viewmodels/OnlineSearchViewModel.kt
- viewmodels/OnlineSearchSuggestionViewModel.kt
- viewmodels/LocalSearchViewModel.kt
- ui/screens/search/*
APIs:
- YouTube.searchSuggestions()
- YouTube.search()
- Local database queries
Database: SearchHistory (save searches)
Features:
- Online search with suggestions
- Local search with instant results
- Filter by content type (song, album, artist)
- Search history tracking
###11. Stats & Wrapped (Spotify Wrapped Alternative)
Entry Point: Stats screen
Files Involved:
- viewmodels/StatsViewModel.kt
- ui/screens/wrapped/*
- db/entities/PlayCountEntity.kt
- ui/screens/ActivityHistory.kt
Database:
- PlayCountEntity (individual play events)
- SongEntity (play count aggregates)
Features:
- Most played songs/artists/albums
- Listen time per day/week/month
- Yearly wrapped with stats
- Custom time period analytics
- Genre distribution
###12. Theme Customization
Entry Point: Settings → Appearance
Files Involved:
- ui/theme/Theme.kt
- ui/theme/PlayerColorExtractor.kt
- viewmodels/ThemeViewModel.kt
- constants/PreferenceKeys.kt (50+ theme-related keys)
Features:
- Material 3 dynamic colors
- Extract color from album art
- Pure black mode for OLED
- Multiple font choices
- Density scaling (5 levels)
- Dark/light mode
- Theme color picker
Database: DataStore preferences
###13. Scrobbling to Last.fm
Entry Point: Account settings
Files Involved:
- utils/ScrobbleManager.kt
- lastfm/ module
- viewmodels/AccountSettingsViewModel.kt
APIs: Last.fm API
Features:
- Send plays to Last.fm
- Last.fm account linking
- Artist stats synchronization
###14. Discord Rich Presence
Entry Point: MusicService (automatic)
Files Involved:
- utils/DiscordRPC.kt
- constants/PreferenceKeys.kt (Discord settings)
Features:
- Show currently playing song on Discord
- Real-time updates
- Custom button configuration
- Artist/album info display
###15. YouTube Library Sync
Entry Point: Account settings
Files Involved:
- utils/SyncUtils.kt
- db/entities/SongEntity.kt (liked, inLibrary fields)
- viewmodels/AccountSettingsViewModel.kt
APIs: YouTube.likeVideo(), YouTube.toggleSongLibrary()
Features:
- Sync liked songs
- Add to YouTube Library
- Remove from library
- Bi-directional sync
###16. Artist Page & Related Content
Entry Point: Tapping artist name
Files Involved:
- viewmodels/ArtistViewModel.kt
- ui/screens/artist/*
- innertube/pages/ArtistPage.kt
APIs:
- YouTube.artist() (bio, image, stats)
- YouTube.artistItems() (songs, albums, radio)
- Wikipedia API (artist biography)
Features:
- Artist description
- Albums
- Singles
- Similar artists
- Monthly listeners
- Background video option
- Radio station
###17. Home Feed & Recommendations
Entry Point: Home screen
Files Involved:
- viewmodels/HomeViewModel.kt
- ui/screens/HomeScreen.kt
- innertube/pages/HomePage.kt
APIs: YouTube.home()
Features:
- Quick Picks
- Daily Discover (personalized recommendations)
- Forgotten Favorites (rediscovery)
- Keep Listening (resume)
- Similar Recommendations (based on plays)
- Community Playlists
- New Releases
- Wrapped card
###18. Spotify Import
Entry Point: Import screen
Files Involved:
- viewmodels/SpotifyImportViewModel.kt
- ui/screens/settings/*
Features:
- Import Spotify playlists
- Create local copies
- Preserve metadata
- Progress tracking
###19. App Update Checker (OTA)
Entry Point: Settings → About
Files Involved:
- vivimusic/updater/*
- vivimusic/release/NewReleaseCheckWorker.kt
- vivimusic/UpdateNotificationHelper.kt
Features:
- Check GitHub releases
- Download APK
- In-app installation
- Changelog display
- Auto-update checks (background job)
###20. Android Auto & Android TV Support
Entry Point: Android Auto launcher, Leanback UI
Files Involved:
- MainActivity.kt (Android Auto integration)
- playback/MediaLibrarySessionCallback.kt
Features:
- MediaBrowser support for Android Auto
- Voice control integration
- Large button UI for TV
- Queue display for navigation
##6. Routing
###Navigation Architecture
Type: Jetpack Compose Navigation with custom routing
Files:
- ui/screens/NavigationBuilder.kt
- ui/component/AppNavigation.kt
- ui/screens/Screens.kt
###Main Routes
sealed class Screens(route: String) {
object Home : Screens("home")
object Search : Screens("search_input")
object ListenTogether : Screens("listen_together")
object Library : Screens("filter_library")
}
###Primary Navigation
The app uses 4 main screens accessible via bottom navigation:
├── Home
│ └── Quick Picks, Daily Discover, etc.
├── Search
│ ├── Online Search
│ └── Local Search
├── Listen Together
│ ├── Create Session
│ └── Join Session
└── Library
├── Playlists
├── Artists
├── Albums
├── Songs
├── Downloads
├── Recently Played
└── Most Played
###Secondary Routes (Detail Screens)
Album Screen
├── Album info
├── Track list
└── Similar albums
Artist Screen
├── Artist info
├── Albums
├── Singles
├── Popular songs
└── Similar artists
Playlist Screen
├── Playlist info
├── Add/remove songs
└── Share
Settings Screens
├── Appearance
├── Audio
├── Playback
├── Lyrics
├── Sync
├── About
└── Developer
###Protected Routes
None - The app does not have authentication-protected routes. All features are available to all users.
###Dynamic Routes
// Album with dynamic ID
YouTube.browse(browseId = "MPREb_...")
// Artist with dynamic ID
YouTube.artist(browseId = "UCDk...")
// Playlist with dynamic ID
YouTube.playlist(playlistId = "OLAK...")
// Song with dynamic ID
YouTube.player(videoId = "...")
###Route Parameters
Query Parameters:
- Search:
query,filter - Album:
browseId,withSongs - Artist:
browseId - Playlist:
playlistId,continuation
Navigation Arguments:
- Song metadata passed via memory (not serialized)
- Album/artist info cached in ViewModel
###Middleware/Interceptors
Error Handling:
- Network errors show snackbar
- Parsing errors show crash activity
- User feedback via dialog
State Restoration:
- Player state persisted to DataStore
- Queue saved before app close
- Resume playback on restart
###Navigation Flow
Launch App
↓
WelcomeActivity (first run)
└─→ MainActivity
↓
AppNavigation (Compose NavHost)
├─ Home (default)
├─ Search
├─ Listen Together
├─ Library
│ ├─ Playlists
│ ├─ Artists
│ └─ Albums
├─ Album Detail
├─ Artist Detail
├─ Playlist Detail
├─ Settings
│ ├─ Appearance
│ ├─ Audio
│ └─ ...
└─ Player (full screen)
Deep Links:
├─ HTTPS App Links
│ └─ ListenTogether invite: vivi://listen/{sessionCode}
├─ Intent Actions
│ ├─ android.intent.action.MAIN
│ ├─ android.intent.action.MUSIC_PLAYER
│ └─ android.intent.action.VIEW
└─ Broadcast Receivers
└─ MediaButton, AudioBecoming, etc.
##7. UI Components
###Component Inventory
Total Reusable Components: 115+
###Navigation & Structure
| Component | Purpose |
|---|---|
| AppNavigation | Root navigation host |
| NavigationBuilder | Route builder |
| NavigationTile | Tab navigation item |
| NavigationTitle | Section header |
###Player Components
| Component | Purpose |
|---|---|
| Player | Full-screen player |
| MiniPlayer | Compact player |
| Queue | Queue display/management |
| Thumbnail | Album art display |
| CanvasArtworkPlayer | Animated visualizer |
| PlayingIndicator | Animated play indicator |
###Control Components
| Component | Purpose | Props | State |
|---|---|---|---|
| PlayerSlider | Song progress | duration, position | position |
| VolumeSlider | Volume control | volume | volume |
| BigSeekBar | Large seek bar | range | position |
| WavySlider | Wavy effect slider | progress | progress |
| SquigglySlider | Squiggly effect slider | progress | progress |
###Lyrics Components
| Component | Purpose |
|---|---|
| Lyrics | Basic lyrics display |
| LyricsV2 | Enhanced lyrics with translation |
| MetroLyrics | Metro-style display |
| ViviMusicLyrics | Custom styled lyrics |
| LyricsImageCard | Lyrics with background |
| DraggableLyricsProviderList | Reorder providers |
###Menu & Dialog Components
| Component | Purpose |
|---|---|
| BottomSheet | Slide-up menu |
| BottomSheetMenu | Menu items in bottom sheet |
| BottomSheetPage | Multi-level bottom sheet |
| Dialog | Modal dialog |
| EnumDialog | Enum value selector |
| GridMenu | Grid-based menu |
| CreatePlaylistDialog | Playlist creation |
| ImportPlaylistDialog | CSV import |
###List & Grid Components
| Component | Purpose |
|---|---|
| Items | Song/album/artist cards |
| ChipsRow | Horizontal chip list |
| LazyGridSnapLayoutInfoProvider | Snap layout for grid |
| DraggableScrollBarOverlay | Scroll indicator |
###Specialized Components
| Component | Purpose |
|---|---|
| Equalizer | EQ slider UI |
| AudioDeviceBottomSheet | Device selector |
| UpdaterComponents | Update UI |
| SettingDialoge | Settings dialog |
| Material3SettingsGroup | Settings group |
| Preference | Preference item |
| ModernSwitch | Toggle switch |
| SearchBar | Search input |
| SortHeader | Sort selector |
| EmptyPlaceholder | No results placeholder |
| LoadingScreen | Loading indicator |
###Theme & Styling
| Component | Purpose |
|---|---|
| Theme | Main theme provider |
| PlayerColorExtractor | Extract colors from image |
| AlbumGradient | Gradient from album art |
| OnlineBlur | Blur effect |
| FadingEdge | Fade edge effect |
| ShapesCurve | Custom shape definitions |
###Supporting Components
| Component | Purpose |
|---|---|
| AutoResizeText | Auto-sizing text |
| ExpandableText | Expandable text block |
| HideOnScrollFAB | FAB that hides on scroll |
| IntegrationCard | Service integration card |
| RandomizeGridItem | Randomize button |
| SpeedDialGridItem | Quick action grid item |
| ThumbnailCornerRadiusSelector | Radius selector |
| PlaybackLogsDialog | Debug logs display |
###Shimmer Components
Folder: ui/component/shimmer/
Loading skeleton placeholders for:
- Album cards
- Artist cards
- Song list
- Playlist items
##8. Backend
###Services
####MusicService (Foreground Service)
File: playback/MusicService.kt (3400+ lines)
Responsibilities:
- Manages ExoPlayer lifecycle
- Handles media session callbacks
- Manages audio focus
- Provides notifications
- Manages audio effects (EQ, loudness enhancement)
- Handles device audio changes
- Manages playback logging
- Supports Cast/Chromecast
Key Methods:
fun setupPlayer() // Initialize ExoPlayer
fun loadMediaItem() // Load single song
fun loadMediaItems() // Load queue
fun play() // Start playback
fun pause() // Pause
fun seekTo() // Seek to position
fun next() // Next song
fun previous() // Previous song
fun togglePlayPause() // Toggle playback
fun toggleRepeatMode() // Cycle repeat modes
fun toggleShuffleMode() // Toggle shuffle
Audio Processing:
- Equalizer (10-band)
- Loudness enhancer
- Audio normalization
- Sample rate conversion
Device Management:
- Bluetooth auto-resume
- Headphone auto-pause
- Device list detection
- Audio focus handling
####PlayerConnection (UI Bridge)
File: playback/PlayerConnection.kt (502 lines)
Responsibilities:
- Bridges service and UI
- Provides state flows for UI observation
- Manages safe player access
- Handles player initialization status
Exposed Flows:
val playbackState: StateFlow<Int>
val isPlaying: StateFlow<Boolean>
val mediaMetadata: StateFlow<MediaMetadata>
val queueTitle: StateFlow<String?>
val currentSong: StateFlow<Song>
val currentLyrics: StateFlow<LyricsEntity>
###Controllers
Not traditional controllers. Instead uses ViewModels as business logic coordinators:
####HomeViewModel (33KB)
- Manages home feed data
- Daily discover recommendations
- Keep listening queue
- Similar recommendations
- Account playlists
- Wrapped feature
####OnlineSearchViewModel
- YouTube search queries
- Search suggestions
- Results filtering
- Pagination
####LibraryViewModels
- Local collection queries
- Sorting and filtering
- Playlist management
- Statistics
###Services (Data/Business Logic)
####LyricsHelper (6.7KB)
- Fetches from multiple providers
- Caching with LRU
- Provider ordering/priority
- Fallback logic
####ListenTogetherManager (75KB)
- WebSocket connection
- Playback synchronization
- Queue syncing
- Volume management
- Smart resync
####ScrobbleManager
- Last.fm integration
- Play event submission
- Artist stats
####NetworkConnectivityObserver
- Network state monitoring
- Fallback to synchronous checks
####MusicRecognitionService
- Audio fingerprinting
- Shazam API communication
###Models
Core Models:
// Database entity
data class SongEntity(
val id: String,
val title: String,
val duration: Int,
val albumId: String?,
val liked: Boolean,
val totalPlayTime: Long,
val inLibrary: LocalDateTime?
)
// API response
data class SongItem(
val id: String,
val title: String,
val duration: Int,
val album: Album?,
val artists: List<Artist>
)
// UI model
data class MediaMetadata(
val id: String,
val title: String,
val album: Album?,
val artists: List<Artist>,
val duration: Long
)
###Repositories
Room DAO Pattern (DatabaseDao.kt):
@Dao
interface DatabaseDao {
@Query("SELECT * FROM song WHERE inLibrary IS NOT NULL")
fun songs(): Flow<List<Song>>
@Insert(onConflict = REPLACE)
suspend fun insertSong(song: SongEntity)
@Update
suspend fun updateSong(song: SongEntity)
@Delete
suspend fun deleteSong(song: SongEntity)
}
###Middleware
Hilt Dependency Injection:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideMusicDatabase(
@ApplicationContext context: Context
): MusicDatabase = Room
.databaseBuilder(context, InternalDatabase::class.java, ...)
.build()
}
###Validation
Input Validation:
- YouTube video ID format
- Song title & artist name non-empty
- Duration positive
- URL validation
Data Validation:
- Lyrics timestamp format
- EQ frequency ranges
- Volume range (0-100)
- Play position bounds
###Authentication
No User Authentication Required:
- InnerTube uses visitor data (anonymous)
- No user account login
- Third-party APIs use app-level credentials
API Keys:
- Last.fm credentials in BuildConfig
- DeepL/Mistral API keys in preferences
###Authorization
No Role-Based Access Control:
- All features available to all users
- No premium/freemium model
Device-Level Control:
- Audio permissions required
- Microphone permission for recognition
- Storage permissions for downloads
###Request Lifecycle
1. UI calls ViewModel method
↓
2. ViewModel launches coroutine
↓
3. Service API called (YouTube, Lyrics, etc.)
↓
4. Network request via Ktor HTTP client
↓
5. Response parsing (Kotlinx Serialization)
↓
6. Database update (Room)
↓
7. State flow emission
↓
8. Compose recomposition
↓
9. UI update
###Error Handling Strategy
Network Errors:
YouTube.search(query).onFailure {
// Retry with exponential backoff
// Show offline message if no cache
// Fall back to cached data
}
Playback Errors:
// ExoPlayer error callback
onPlayerError { error ->
// Log error
// Skip to next song
// Show error toast
}
Database Errors:
// Room suspend function wrapped
try {
database.insertSong(song)
} catch (e: SQLException) {
// Retry transaction
// Report exception
}
##9. Database
###Database: Music Database (Room + SQLite)
File: db/MusicDatabase.kt
Version: Currently at revision 10+ migrations
Type: SQLite via Room ORM
###Tables/Entities (24+)
####Core Entities
song (
id: String PRIMARY KEY,
title: String,
duration: Int,
albumId: String,
albumName: String,
explicit: Boolean,
year: Int,
date: LocalDateTime,
dateModified: LocalDateTime,
liked: Boolean,
likedDate: LocalDateTime,
totalPlayTime: Long,
inLibrary: LocalDateTime,
dateDownload: LocalDateTime,
isLocal: Boolean,
libraryAddToken: String,
libraryRemoveToken: String,
lyricsOffset: Int,
romanizeLyrics: Boolean,
isDownloaded: Boolean,
isUploaded: Boolean,
isVideo: Boolean,
-- Indexes:
INDEX albumId
)
album (
id: String PRIMARY KEY,
title: String,
description: String,
year: Int,
subtitle: String,
shareUrl: String,
albumArtist: String,
-- Indexes:
INDEX title
)
artist (
id: String PRIMARY KEY,
name: String,
shufflePlaylistId: String,
radioPlaylistId: String,
-- Indexes:
INDEX name
)
playlist (
id: String PRIMARY KEY,
name: String,
description: String,
thumbnailUrl: String,
author: String,
authorId: String,
authorThumbnailUrl: String,
browseId: String,
playlistThumbnail: ByteArray,
isEditable: Boolean,
estimatedItemCount: Long,
isLocal: Boolean,
inPlaylist: LocalDateTime,
-- Indexes:
INDEX inPlaylist,
INDEX name
)
####Relationship Tables
song_artist_map (
songId: String,
artistId: String,
position: Int,
-- PRIMARY KEY: (songId, artistId)
)
song_album_map (
songId: String,
albumId: String,
position: Int,
-- PRIMARY KEY: (songId, albumId)
)
album_artist_map (
albumId: String,
artistId: String,
-- PRIMARY KEY: (albumId, artistId)
)
playlist_song_map (
playlistId: String,
songId: String,
position: Int,
-- PRIMARY KEY: (playlistId, songId)
)
playlist_song_map_preview (
playlistId: String,
songId: String,
position: Int
)
related_song_map (
songId: String,
relatedSongId: String,
-- PRIMARY KEY: (songId, relatedSongId)
)
####Metadata Tables
lyrics (
id: String PRIMARY KEY,
lyrics: String,
provider: String,
syncedLyrics: String,
language: String
)
format (
id: String PRIMARY KEY,
mimeType: String,
bitrate: Int,
audioSampleRate: Int,
audioChannels: Int,
loudnessDb: Float,
forcedCaptions: String
)
playcount (
id: String PRIMARY KEY,
count: Int
)
play_count (
id: String,
count: Int,
date: LocalDateTime,
-- PRIMARY KEY: (id, date)
)
set_video_id (
id: String PRIMARY KEY,
setVideoId: String
)
####History & Tracking
search_history (
query: String PRIMARY KEY,
browseId: String,
resultType: String,
explicit: Boolean,
played: Long
)
recognition_history (
id: String PRIMARY KEY,
title: String,
artist: String,
thumbnailUrl: String,
recognizedAt: LocalDateTime
)
event (
id: String PRIMARY KEY,
songId: String,
timestamp: LocalDateTime,
type: String -- PLAY, LIKE, etc.
)
event_with_song (
id: String,
songId: String,
timestamp: LocalDateTime,
type: String
)
####Speed Dial (Quick Access)
speed_dial_item (
id: String PRIMARY KEY,
type: String, -- SONG, ALBUM, ARTIST, PLAYLIST
textId: String
)
###Relationships
Song ←─ Album → Album
Song ←─ Artist → Artist
Album ←─ Artist → Album (Many-to-Many)
Playlist ← PlaylistSongMap → Song
Song ← RelatedSongMap → Song (Recommendations)
Song → Lyrics (one-to-one cache)
Song → Format (one-to-one streaming format)
Song → PlayCount (one-to-one statistics)
###Indexes
Optimized for:
- Fast song/album/artist lookup by ID
- Sorting by name
- Filtering by album
- Playlist membership queries
- Recent/frequently played
###Migrations
Strategy: AutoMigration with custom specs for schema changes
Example:
@RenameColumn("song", "albumId", "albumId")
@DeleteTable("oldTable")
class MigrationSpec : AutoMigrationSpec
###ORM
Type: Room (Kotlin ORM)
Key Features:
- Type-safe queries
- Coroutine support
- Flow for reactive queries
- Transaction support
- Migration management
###Query Patterns
// Simple query
@Query("SELECT * FROM song WHERE id = :id")
fun song(id: String): Flow<Song?>
// Complex join
@Transaction
@Query("""
SELECT * FROM song
WHERE albumId = :albumId
ORDER BY position
""")
fun albumSongs(albumId: String): Flow<List<Song>>
// Aggregation
@Query("""
SELECT artist, SUM(playcount) as totalPlays
FROM song_artist_map
GROUP BY artist
ORDER BY totalPlays DESC
""")
fun topArtists(): Flow<List<ArtistStats>>
###Why Each Table Exists
| Table | Purpose |
|---|---|
| song | Core track metadata |
| album | Album details |
| artist | Artist information |
| playlist | User-created/synced playlists |
| Relationship maps | Normalize many-to-many relationships |
| lyrics | Cache synced lyrics (expensive to fetch) |
| format | Audio format info (bitrate, sample rate) |
| playcount | Track total plays and statistics |
| search_history | Remember user searches |
| recognition_history | Remember Shazam recognitions |
| event | Track all user interactions |
| speed_dial | Quick access shortcuts |
###ER Diagram
┌─────────────┐
│ Song │
├─────────────┤
│ id (PK) │
│ title │
│ duration │
│ liked │
│ inLibrary │
└─────────────┘
▲
│ (1:N)
│
┌─────────────┐ ┌─────────────────┐
│ SongArtist │──────│ Artist │
│ Map │ ├─────────────────┤
└─────────────┘ │ id (PK) │
▲ │ name │
│ └─────────────────┘
│
│
(1:N)
│
┌──────────────┐
│ SongAlbum │ ┌──────────────┐
│ Map │──────│ Album │
└──────────────┘ ├──────────────┤
│ id (PK) │
│ title │
│ year │
└──────────────┘
▲
│ (1:N)
│
┌──────────────┐
│ AlbumArtist │
│ Map │
└──────────────┘
┌──────────────┐
│ Playlist │
├──────────────┤ ┌──────────────────┐
│ id (PK) │──────│ PlaylistSongMap │
│ name │ │ (Join) │
│ author │ └──────────────────┘
└──────────────┘ ▲
│
(many songs)
Song ←──── Lyrics (1:1 cache)
Song ←──── Format (1:1 format info)
Song ←──── PlayCount (1:1 statistics)
Song ←──── Event (1:N history)
##10. Authentication & Authorization
###Authentication Flow
Status: No User Authentication Required
The app operates with:
-
Anonymous YouTube Access
- Uses InnerTube API client (no user account needed)
- VisitorData token auto-generated
- No login required
-
API Credentials (hardcoded in BuildConfig)
buildConfigField("String", "LASTFM_API_KEY", "\"694cbaa17...\"") buildConfigField("String", "LASTFM_SECRET", "\"a0fdaf606...\"") -
Optional Third-Party Auth
- Last.fm (optional)
- Spotify (playlist import only)
- Discord RPC (optional)
###Signup Flow
Not applicable - No user accounts
###Login Flow
Not applicable - Anonymous usage
###Session Management
Tokens:
- VisitorData (YouTube anonymous token)
- DataSync ID (optional, for personalized content)
- Cookies (optional, for authenticated requests)
Storage:
- SharedPreferences via DataStore
- No sensitive data stored
###OAuth/Third-Party Providers
Not used for main authentication
Optional Integrations:
- Last.fm API (app-level key)
- Spotify Import (just reads playlists)
- YouTube Auth (optional, for library sync)
###JWT/Cookies
JWT: Not used
Cookies:
- YouTube cookies supported
- Can be set for authenticated requests
- Improves access to premium content
Session Lifetime: App-lifetime (until app closes)
###Refresh Tokens
Not used (no session-based auth)
###Roles & Permissions
Application Permissions (no role-based access):
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
All users have access to all features
###Guards/Middleware
Permission Guards:
if (!hasRecordPermission(context)) {
// Prevent music recognition
}
if (!hasStoragePermission(context)) {
// Prevent downloads
}
Network Guards:
if (!networkConnectivity.isAvailable()) {
// Show offline message
// Fall back to cached data
}
Feature Guards:
- Cast/Chromecast only in GMS variant
- Some lyrics providers can be disabled per preference
##11. State Management
###Architecture
Primary State Management Tool: Kotlin Coroutines + Flow + StateFlow
No Redux, MobX, or similar - Pure Kotlin idioms
###State Flow Hierarchy
┌─────────────────────────────────────────────────────────┐
│ Application-Level State │
├─────────────────────────────────────────────────────────┤
│ MusicService (playback state, media session) │
│ PlayerConnection (playback, metadata, queue) │
│ ViviPrefCache (preferences - in-memory) │
└─────────────────────────────────────────────────────────┘
▲ ▲
│ │
└───────┬───────────────┬───┘
│ │
┌───────────▼─┐ ┌────────▼────────┐
│ ViewModels │ │ DataStore │
│ │ │ (Preferences) │
│ Home │ │ │
│ Search │ │ Room Database │
│ Album │ │ (Cache) │
│ Playlist │ │ │
└─────────────┘ └─────────────────┘
▲
│
┌───┴──────────────┐
│ Compose UI Layer │
│ │
│ Components │
│ observe flows │
│ & recompose │
└──────────────────┘
###Context/Global State
Data Flow in App.kt:
@HiltAndroidApp
class App : Application() {
@Inject
@ApplicationScope
lateinit var applicationScope: CoroutineScope
override fun onCreate() {
ViviPrefCache.start(this) // Start preference cache
initializeSettings() // Load YouTube locale, etc.
observeSettingsChanges() // Listen to preference updates
}
}
###Redux-like Pattern (Manual)
No Redux framework, but Redux-like patterns exist:
// Action
sealed class PlaybackAction {
object Play : PlaybackAction()
object Pause : PlaybackAction()
data class Seek(val position: Long) : PlaybackAction()
}
// Reducer (in MusicService)
fun handlePlaybackAction(action: PlaybackAction) {
when (action) {
PlaybackAction.Play -> player.play()
PlaybackAction.Pause -> player.pause()
is PlaybackAction.Seek -> player.seekTo(action.position)
}
}
// State (PlayerConnection state flows)
val playbackState: StateFlow<Int>
val isPlaying: StateFlow<Boolean>
###Zustand/Pinia-like Pattern
Not used
###React Query / TanStack Query
Not used directly, but similar patterns with Room + Flow:
// Room query returns Flow (like React Query cache)
fun songs(): Flow<List<Song>> {
return database.songs()
.distinctUntilChanged()
.shareIn(scope, SharingStarted.Lazily)
}
###SWR (Stale-While-Revalidate)
Manual implementation:
// Return cached data immediately
val cached = database.getSong(id)
// Fetch fresh data in background
viewModelScope.launch {
YouTube.song(id).onSuccess { fresh ->
database.updateSong(fresh)
}
}
###Local State (Compose)
UI-only state:
@Composable
fun SearchScreen() {
var query by remember { mutableStateOf("") }
var isLoading by remember { mutableStateOf(false) }
TextField(
value = query,
onValueChange = { query = it }
)
}
###Compose State Hoisting
State lifted to ViewModel:
// In SearchViewModel
val searchQuery = MutableStateFlow("")
val isLoading = MutableStateFlow(false)
fun search(query: String) {
isLoading.value = true
viewModelScope.launch {
val results = YouTube.search(query)
// Update UI-bound state
}
}
// In Composable
val query by viewModel.searchQuery.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
###ViewModel Scope
Automatic cleanup:
@HiltViewModel
class HomeViewModel @Inject constructor(
val database: MusicDatabase
) : ViewModel() {
init {
viewModelScope.launch {
// Coroutine canceled when ViewModel cleared
database.songs().collect { songs ->
// Update UI
}
}
}
}
###Data Flow Example (Home Screen)
User opens app
↓
HomeViewModel initialization
├─ Launch viewModelScope coroutine
├─ YouTube.home() network request
├─ Emit to homePage StateFlow
└─ database.songs() for local recommendations
↓
Compose observes state flows
├─ collectAsState() on homePage
├─ collectAsState() on quickPicks
├─ collectAsState() on dailyDiscover
└─ Recompose on each emission
↓
UI rendered with data
├─ Skeleton loaders shown first
├─ Data filled in as flows emit
└─ Smooth transitions
###Communication Between ViewModels
Not directly - ViewModels don't communicate
Instead:
- Share data via Database
- Observe same Flow sources
- UI layer coordinates
// Album and Artist VMs both query database independently
val albumSongs = database.albumSongs(albumId)
val albumArtists = database.albumArtists(albumId)
// They update independently, UI shows unified view
###Performance Optimization
Memoization via StateFlow:
private val _homePage = MutableStateFlow<HomePage?>(null)
val homePage: StateFlow<HomePage?> = _homePage.asStateFlow()
// Prevents duplicate emissions
.distinctUntilChanged()
// Multiple subscribers don't re-fetch
.stateIn(viewModelScope, SharingStarted.Lazily, null)
Caching Strategy:
- In-memory cache (LRU for lyrics)
- Database cache (SQLite)
- Network-first for online content
- Cache-first for offline mode
##12. API Documentation
###YouTube InnerTube API (via innertube module)
####Search & Suggestions
// Get search suggestions
suspend fun YouTube.searchSuggestions(
query: String
): Result<SearchSuggestions>
// Search for music
suspend fun YouTube.search(
query: String,
filter: SearchFilter = FILTER_ALL
): Result<SearchResult>
// Get search summary
suspend fun YouTube.searchSummary(
query: String
): Result<SearchSummaryPage>
// Continue search results
suspend fun YouTube.searchContinuation(
continuation: String
): Result<SearchResult>
Example:
YouTube.search("Taylor Swift").onSuccess { results ->
val songs = results.songs
val albums = results.albums
val artists = results.artists
}
####Browse & Discovery
// Get home feed
suspend fun YouTube.home(
continuation: String? = null,
params: String? = null
): Result<HomePage>
// Get explore page
suspend fun YouTube.explore(): Result<ExplorePage>
// Get new releases
suspend fun YouTube.newReleaseAlbums(): Result<List<AlbumItem>>
// Get mood & genres
suspend fun YouTube.moodAndGenres(): Result<List<MoodAndGenres>>
####Album Operations
// Get album details with songs
suspend fun YouTube.album(
browseId: String,
withSongs: Boolean = true
): Result<AlbumPage>
// Get album songs (continuation)
suspend fun YouTube.albumSongs(
playlistId: String,
album: AlbumItem? = null
): Result<List<SongItem>>
Response:
data class AlbumPage(
val album: AlbumItem,
val songs: List<SongItem>,
val description: String,
val year: Int
)
####Artist Operations
// Get artist info
suspend fun YouTube.artist(
browseId: String
): Result<ArtistPage>
// Get artist items (albums, singles)
suspend fun YouTube.artistItems(
endpoint: BrowseEndpoint
): Result<ArtistItemsPage>
// Continue artist items
suspend fun YouTube.artistItemsContinuation(
continuation: String
): Result<ArtistItemsContinuationPage>
Response:
data class ArtistPage(
val artist: Artist,
val description: String,
val thumbnails: List<Thumbnail>,
val subscriptionCount: String,
val albums: List<AlbumItem>,
val singles: List<AlbumItem>,
val similarArtists: List<ArtistItem>
)
####Playlist Operations
// Get playlist
suspend fun YouTube.playlist(
playlistId: String
): Result<PlaylistPage>
// Continue playlist
suspend fun YouTube.playlistContinuation(
continuation: String
): Result<PlaylistContinuationPage>
// Create playlist
suspend fun YouTube.createPlaylist(
title: String,
description: String?,
privacyStatus: String
): Result<CreatePlaylistResponse>
// Edit playlist
suspend fun YouTube.editPlaylist(
playlistId: String,
title: String,
description: String?
): Result<EditPlaylistResponse>
// Add to playlist
suspend fun YouTube.addToPlaylist(
playlistId: String,
videoIds: List<String>
): Result<Unit>
// Remove from playlist
suspend fun YouTube.removeFromPlaylist(
playlistId: String,
setVideoIds: List<String>
): Result<Unit>
####Playback
// Get player data
suspend fun YouTube.player(
videoId: String,
playlistId: String? = null,
continuation: String? = null
): Result<PlayerResponse>
// Get streaming format
data class Format(
val itag: Int,
val mimeType: String,
val bitrate: Int,
val loudnessDb: Float
)
####Feedback
// Like/unlike video
suspend fun YouTube.likeVideo(
videoId: String,
like: Boolean
): Result<FeedbackResponse>
// Rate video
suspend fun YouTube.rateVideo(
videoId: String,
rating: String
): Result<FeedbackResponse>
// Add to library
suspend fun YouTube.toggleSongLibrary(
videoId: String,
addToLibrary: Boolean
): Result<Unit>
####Account
// Get account info
suspend fun YouTube.accountInfo(): Result<AccountInfo>
// Get library
suspend fun YouTube.library(
browseId: String,
tabIndex: Int = 0
): Result<LibraryPage>
// Continue library
suspend fun YouTube.libraryContinuation(
continuation: String
): Result<LibraryContinuationPage>
####Other
// Get charts
suspend fun YouTube.charts(browseId: String): Result<ChartsPage>
// Get queue
suspend fun YouTube.getQueue(endpoint: WatchEndpoint): Result<GetQueueResponse>
// Get transcript
suspend fun YouTube.getTranscript(videoId: String): Result<GetTranscriptResponse>
// Get comments
suspend fun YouTube.getComments(videoId: String): Result<CommentResponse>
###Lyrics APIs
All return: Result<String> with synced LRC format or plain text
// YouLyPlus API
suspend fun YouLyPlusLyricsProvider.getLyrics(
id: String,
title: String,
artist: String,
duration: Long,
album: String?
): Result<String>
// KuGou API
// LrcLib API
// Better Lyrics API
// etc.
###Music Recognition API (Shazam)
object MusicRecognitionService {
suspend fun recognize(context: Context): RecognitionStatus
}
sealed class RecognitionStatus {
object Ready : RecognitionStatus()
object Listening : RecognitionStatus()
object Processing : RecognitionStatus()
data class Success(val song: SongItem) : RecognitionStatus()
data class Error(val message: String) : RecognitionStatus()
}
###Last.fm API
// Scrobble track
suspend fun LastFM.scrobbleTrack(
artist: String,
track: String,
timestamp: Long
): Result<Unit>
// Get artist info
suspend fun LastFM.getArtistInfo(
artist: String
): Result<ArtistInfo>
###Lyrics Translation APIs
DeepL:
suspend fun DeepLService.translate(
text: String,
sourceLang: String,
targetLang: String
): Result<String>
Mistral AI:
suspend fun MistralService.translate(
text: String,
prompt: String
): Result<String>
###Local Database API (Room DAO)
@Dao
interface DatabaseDao {
// Song queries
fun songs(sort: SongSortType, descending: Boolean): Flow<List<Song>>
fun likedSongs(sort: SongSortType, descending: Boolean): Flow<List<Song>>
fun song(id: String): Flow<Song?>
// Album queries
fun albums(): Flow<List<Album>>
fun albumSongs(albumId: String): Flow<List<Song>>
// Artist queries
fun artists(): Flow<List<Artist>>
fun artistSongs(artistId: String): Flow<List<Song>>
// Playlist queries
fun playlists(): Flow<List<Playlist>>
fun playlistSongs(playlistId: String): Flow<List<PlaylistSong>>
// Write operations
suspend fun insertSong(song: SongEntity)
suspend fun updateSong(song: SongEntity)
suspend fun deleteSong(song: SongEntity)
// Transaction support
suspend fun withTransaction(block: suspend () -> Unit)
}
##13. Business Logic
###Core Business Rules
####Music Playback
Queue Management:
- Auto-load next song when current ends (unless repeat-one mode)
- Shuffle resets queue order while preserving current position
- Repeat modes: OFF (no repeat), ONE (loop current), ALL (loop queue)
Skip Logic:
- Next skips to next queued song
- If at queue end and repeat-all: restart from beginning
- Previous goes back to start of current song (unless <3s elapsed)
Pause Behavior:
- When paused, UI shows pause button
- Resume from exact position
- Playback notification stays visible
- Service doesn't go to background
Background Playback:
- Service runs as foreground service
- Notification persists
- Audio focus managed (pause on incoming call)
- Bluetooth state tracked
####Audio Quality
Format Selection:
- Prioritize highest bitrate available
- Fallback to lower bitrates if bandwidth insufficient
- User preference for audio quality (in settings)
- Cache based on selected quality
Audio Effects:
- EQ settings applied after decoding
- Loudness enhancement optional
- Normalization prevents clipping
- Sample rate conversion as needed
####Library Management
Add to Library:
- Songs added to local database
- inLibrary timestamp recorded
- Synced to YouTube if authenticated
- Shows in Library screen
Like/Unlike:
- Updates liked flag
- Records timestamp of like
- Optional sync to YouTube
- Affects recommendations
Statistics:
- Every play increments totalPlayTime
- Aggregate by time period (day, week, month, year)
- Calculate most-played songs/artists
- Drive "Wrapped" statistics
####Lyrics Management
Syncing:
- Parse LRC format timestamps
- Current line determined by player position
- Highlight advances as song plays
- Scroll to current line
Caching:
- Cache in database (lyrics field)
- In-memory LRU for hot data (50 items)
- Check cache before network request
- TTL: indefinite (unless user refreshes)
Translation:
- Line-by-line translation via AI
- Preserve original below translation
- Cache translated text
- Fallback to original if unavailable
####Recommendations
Daily Discover:
- Seed from random song in library
- Fetch related songs from YouTube
- Limit to songs not in library
- Refresh daily (new seed)
Similar Artists:
- Based on YouTube artist page data
- Show in artist detail screen
- Clickable to navigate artist
Keep Listening:
- Resume interrupted songs
- Sort by least recently played
- Expire after 30 days
Quick Picks:
- Top songs from last 7/30/90 days
- Weighted by play frequency
- Maximum 10 items
####Search
Online Search:
- Queries go to YouTube
- Filter by content type (song, album, artist, playlist)
- User preferences (hide explicit, hide video songs, hide shorts)
- Suggestions refreshed in real-time
Local Search:
- Indexed by title, artist, album
- Case-insensitive
- Fuzzy matching on artist names
- Instant results (no network)
History:
- Remember last 50 searches
- Searchable in search history
- Optional: clear history
####Offline Support
Download:
- Songs cached to disk
- Full quality preserved
- Mark as downloaded in database
- Available for offline playback
Fallback:
- No network → use cache
- Missing cache → show unavailable message
- Partial downloads allowed (can resume)
####Playlist Sync
Local Playlists:
- Stored in Room database
- Can contain YouTube songs
- Export to CSV or import from CSV
Online Playlists:
- Fetch from YouTube
- Create/edit on YouTube
- Add/remove songs
- Automatic sync
####Statistics & Wrapped
Tracking:
- Every play recorded (timestamp, song ID)
- Aggregate by artist, album, genre
- Calculate percentiles
Wrapped Generation:
- Top songs/artists (by play count)
- Total listening time
- Most active day/time
- Listening patterns
####Device Management
Bluetooth:
- Auto-resume on Bluetooth connect (if setting enabled)
- Auto-pause on disconnect
- Auto-pause on headphone removal
- Device list detection
Audio Output:
- Detect device changes (speaker, headphones, Bluetooth)
- Allow manual device selection
- Notify user of device switch
####Last.fm Scrobbling
When to Scrobble:
- After song plays for 50% duration OR 4 minutes minimum
- Track play count
- Send to Last.fm API
Failure Handling:
- Retry with exponential backoff
- Queue if offline
- Resume on reconnect
##14. Data Flow (Detailed Example)
###Example: User Plays a Song from Search Results
Scenario: User searches for "Blinding Lights", finds The Weeknd version, taps to play
Files Involved:
ui/screens/search/OnlineSearchScreen.ktviewmodels/OnlineSearchViewModel.ktinnertube/YouTube.ktplayback/PlayerConnection.ktplayback/MusicService.ktdb/MusicDatabase.ktui/player/Player.kt
Data Flow:
1. UI Layer (OnlineSearchScreen.kt)
└─ User searches "Blinding Lights"
↓
2. ViewModel (OnlineSearchViewModel.kt)
├─ searchQuery.value = "Blinding Lights"
├─ launches coroutine: search(query)
└─ calls YouTube.search()
↓
3. Network Layer (innertube/YouTube.kt)
├─ Ktor HTTP client sends POST
├─ YouTube InnerTube API responds
├─ Response parsed to SearchResult
├─ Results filtered (hide explicit?)
└─ Emitted to searchResults StateFlow
↓
4. UI Layer receives results
└─ Recompose with song items
↓
5. User taps "The Weeknd - Blinding Lights" (SongItem)
└─ onSongClick() callback triggered
↓
6. ViewModel callback
├─ viewModel.playSong(songItem)
├─ Calls playerConnection.play(songItem)
└─ Converts to MediaItem
↓
7. PlayerConnection (playback/PlayerConnection.kt)
├─ Sends MediaItem to player
├─ player.setMediaItem(mediaItem)
├─ player.play()
└─ Updates internal state flows
↓
8. MusicService (playback/MusicService.kt)
├─ Receives play() command
├─ Calls YouTube.player(videoId) to get stream
├─ Creates DataSource (cache + network)
├─ Initializes decoder
├─ Applies audio effects (EQ)
├─ Starts audio output
├─ Emits position/state updates
└─ Updates notification
↓
9. Database (db/MusicDatabase.kt)
├─ Insert SongEntity if not exists
├─ Set inLibrary timestamp
├─ Increment playcount
├─ Record play event
└─ Emit updated song to database flows
↓
10. PlayerConnection state flows emit
├─ playbackState = READY → PLAYING
├─ mediaMetadata = SongItem data
├─ currentSong = Song entity from DB
├─ queueWindows = updated queue
└─ All flows emit new values
↓
11. UI Layer observes flows
├─ Player.kt observes playbackState
├─ Thumbnail.kt observes mediaMetadata
├─ Queue.kt observes queueWindows
├─ Recompose triggered
└─ Display song title, artist, album art
↓
12. Lyrics fetching (async)
├─ LyricsHelper observes mediaMetadata
├─ Calls resolveLyricsProviders()
├─ Tries providers in order
├─ First success returns
├─ Stores in database
└─ UI updates when available
↓
13. Player UI (ui/player/Player.kt)
├─ Displays song title
├─ Shows album artwork
├─ Seek bar shows duration
├─ Play/pause button responds to touches
├─ Lyrics display syncs with position
└─ Continuous position updates via listener
↓
14. Playback continues
├─ Audio decoded by ExoPlayer
├─ Sent to audio system
├─ Position advances
├─ Seek bar animated
├─ Lyrics highlight advances
└─ When song ends, next auto-plays
Key Files & Their Roles:
| File | Role |
|---|---|
| OnlineSearchScreen.kt | UI composable, displays results |
| OnlineSearchViewModel.kt | Orchestrates search, holds results |
| YouTube.kt | Makes API calls, parses responses |
| InnerTube.kt | Low-level HTTP, Ktor client |
| PlayerConnection.kt | Bridges service & UI, provides state flows |
| MusicService.kt | Manages ExoPlayer, audio output |
| MusicDatabase.kt | Persists song metadata, statistics |
| Player.kt | Renders player UI, handles touch events |
| Lyrics helpers | Fetches/syncs lyrics async |
##15. Dependencies
###Major Dependencies & Their Purpose
####Android Framework
androidx.compose.runtime:runtime 1.10.2
- Compose runtime for declarative UI
- State management primitives
- Recomposition engine
androidx.compose.foundation:foundation 1.10.2
- Basic layout blocks (Box, Row, Column)
- Gesture detection
- Scrolling
androidx.compose.material3:material3 1.5.0-alpha18
- Material 3 components (Buttons, Cards, Dialogs)
- Color scheme system
- Typography
androidx.activity:activity-compose 1.12.3
- Integration with ComponentActivity
- Lifecycle-aware composition
androidx.lifecycle:lifecycle-viewmodel-compose 2.10.0
- ViewModel integration with Compose
- Automatic scope management
- State preservation
####Database & ORM
androidx.room:room-runtime 2.8.4
- SQLite ORM for type-safe queries
- Migration system
- Coroutine support
androidx.room:room-ktx 2.8.4
- Kotlin extensions for Room
- Flow support for reactive queries
####Networking & HTTP
io.ktor:ktor-client-core 3.4.0
- HTTP client library
- Request/response handling
- Timeout configuration
io.ktor:ktor-client-okhttp 3.4.0
- OkHttp engine for Ktor
- HTTP/2 support
- Connection pooling
io.ktor:ktor-client-content-negotiation 3.4.0
- JSON serialization/deserialization
- Content type negotiation
com.github.teamnewpipe:NewPipeExtractor v0.25.2
- YouTube metadata extraction
- Stream URL resolution
- Alternative to InnerTube for some operations
org.brotli:dec 0.1.2
- Brotli compression support
- Reduces bandwidth for network requests
####Dependency Injection
com.google.dagger:hilt-android 2.59.1
- Compile-time DI framework
- Automatic scope management
- Activity/Fragment injection
androidx.hilt:hilt-navigation-compose 1.3.0
- Hilt integration with Compose navigation
- ViewModel scoping across routes
####Media & Audio
androidx.media3:media3-exoplayer 1.7.1
- ExoPlayer (modern media player)
- Format support (MP3, AAC, OGG, etc.)
- Adaptive bitrate streaming
androidx.media3:media3-session 1.7.1
- Media session for media controls
- Remote control support
- Notification integration
androidx.media3:media3-cast 1.7.1
- Chromecast/Google Cast support
- Remote playback
com.google.android.gms:play-services-cast-framework 22.2.0
- Google Cast framework
- Device discovery
- Casting UI
####UI & Graphics
coil:coil-compose 3.3.0
- Image loading library for Compose
- Memory/disk caching
- Transformation support
com.materialkolor:material-kolor 4.1.1
- Material color generation
- Extract colors from images
- Dynamic color support
androidx.palette:palette-ktx 1.0.0
- Extract dominant colors from images
- Color palette generation
com.airbnb.android:lottie-compose 6.6.2
- Lottie animation support
- JSON-based animations
- Smooth playback
com.valentinilk.shimmer:compose-shimmer 1.3.3
- Shimmer loading effect
- Skeleton loading UI
com.github.yalantis:ucrop 2.2.11
- Image cropping library
- Album art customization
####Serialization & Parsing
org.jetbrains.kotlinx:kotlinx-serialization-json
- JSON serialization for Kotlin
- Compile-time code generation
- Type-safe
org.jsoup:jsoup 1.22.1
- HTML parsing library
- Extract text from web pages
- CSS selector support
org.json:json 20251224
- JSON parsing
- Object/array manipulation
####Protocol Buffers
com.google.protobuf:protobuf-javalite 4.33.5
- Protocol buffer runtime
- Lightweight for Android
com.google.protobuf:protobuf-kotlin-lite 4.33.5
- Kotlin code generation for protobuf
####Utilities
org.jetbrains.kotlinx:kotlinx-coroutines-guava 1.10.2
- Bridge Coroutines with Guava futures
- ListenableFuture compatibility
com.google.guava:guava 33.5.0-jre
- Google utilities library
- Collections, caching, functional programming
org.apache.commons:commons-lang3 3.20.0
- Apache Commons for string, array utilities
- Null handling
com.jakewharton.timber:timber 5.0.1
- Logging library
- Tag-based filtering
- Debug/release variants
androidx.datastore:datastore-preferences 1.2.0
- Modern SharedPreferences replacement
- Type-safe key-value storage
- Coroutine-based access
androidx.work:work-runtime-ktx 2.10.0
- Background job scheduling
- Periodic task execution
- Work constraints
####Desugaring
com.android.tools:desugar_jdk_libs_nio 2.1.5
- Java language features for older APIs
- LocalDateTime, Stream API, etc.
####Testing
junit:junit 4.13.2
- Unit testing framework
###Why Each Dependency Exists
| Dependency | Reason |
|---|---|
| Jetpack Compose | Modern declarative UI framework |
| Material 3 | Google's latest design system |
| ExoPlayer | Industry-standard media player |
| Ktor | Type-safe HTTP client |
| Room | Type-safe database access |
| Hilt | Reduces boilerplate DI code |
| Coil | Efficient image loading |
| Coroutines | Asynchronous programming |
| NewPipeExtractor | Alternative YouTube data source |
| Protobuf | Compact data serialization |
| Timber | Better logging for Android |
| DataStore | Future of preferences storage |
| WorkManager | Reliable background jobs |
###Dependency Tree Simplified
App (Main Application)
├── Presentation
│ ├── Jetpack Compose
│ ├── Material 3
│ ├── Coil (images)
│ ├── Lottie (animations)
│ └── MaterialKolor (colors)
├── Business Logic
│ ├── Kotlin Coroutines
│ ├── ViewModel (lifecycle)
│ └── Hilt (DI)
├── Data Access
│ ├── Room (database)
│ ├── DataStore (preferences)
│ └── Ktor (HTTP)
├── Media Playback
│ ├── ExoPlayer
│ ├── Media3 Session
│ └── Cast Framework
└── Supporting
├── Timber (logging)
├── JSoup (HTML parsing)
├── Protobuf (serialization)
└── Apache Commons (utilities)
Modules (Libraries)
├── innertube (YouTube API)
│ ├── Ktor
│ └── NewPipeExtractor
├── canvas (visualizers)
├── lyrics providers
│ ├── Ktor
│ └── JSoup
└── recognition
└── Shazam API
##16. Environment Variables
###Build-Time Configuration
File: app/build.gradle.kts
// Last.fm API Keys (embedded in BuildConfig)
val lastFmKey = "694cbaa17c78202a133eac4656dff651"
val lastFmSecret = "a0fdaf6060f19128c4a84f297c71e627"
buildConfigField("String", "LASTFM_API_KEY", "\"$lastFmKey\"")
buildConfigField("String", "LASTFM_SECRET", "\"$lastFmSecret\"")
// Nightly build support
val isNightly = project.hasProperty("nightly") && project.property("nightly") == "true"
buildConfigField("Boolean", "IS_NIGHTLY", isNightly.toString())
###Runtime Configuration (DataStore)
File: constants/PreferenceKeys.kt (772 lines, 100+ keys)
####UI Preferences
val DynamicThemeKey = booleanPreferencesKey("dynamicTheme")
val SelectedThemeColorKey = intPreferencesKey("selectedThemeColor")
val DarkModeKey = stringPreferencesKey("darkMode")
val PureBlackKey = booleanPreferencesKey("pureBlack")
val SelectedFontKey = stringPreferencesKey("selected_font")
val DensityScaleKey = floatPreferencesKey("density_scale_factor")
val ThumbnailCornerRadiusKey = floatPreferencesKey("thumbnailCornerRadius")
####Audio Preferences
val AudioQualityKey = stringPreferencesKey("audioQuality")
val AudioNormalizationKey = booleanPreferencesKey("audioNormalization")
val AudioOffload = booleanPreferencesKey("audioOffload")
val CrossfadeEnabledKey = booleanPreferencesKey("crossfadeEnabled")
val CrossfadeDurationKey = intPreferencesKey("crossfadeDuration")
####Playback Preferences
val AutoLoadMoreKey = booleanPreferencesKey("autoLoadMore")
val AutoSkipNextOnErrorKey = booleanPreferencesKey("autoSkipNextOnError")
val DisableLoadMoreWhenRepeatAllKey = booleanPreferencesKey("disableLoadMoreWhenRepeatAll")
val SwipeToSongKey = booleanPreferencesKey("SwipeToSong")
val ResumeOnBluetoothConnectKey = booleanPreferencesKey("resumeOnBluetoothConnect")
####Lyrics Preferences
val EnableKugouKey = booleanPreferencesKey("enableKugou")
val EnableLrcLibKey = booleanPreferencesKey("enableLrclib")
val EnableBetterLyricsKey = booleanPreferencesKey("enableBetterLyrics")
val PreferredLyricsProviderKey = stringPreferencesKey("preferredLyricsProvider")
val LyricsProviderOrderKey = stringPreferencesKey("lyricsProviderOrder")
####Content Preferences
val HideExplicitKey = booleanPreferencesKey("hideExplicit")
val HideVideoSongsKey = booleanPreferencesKey("hideVideoSongs")
val HideYoutubeShortsKey = booleanPreferencesKey("hideYoutubeShorts")
val ContentLanguageKey = stringPreferencesKey("contentLanguage")
val ContentCountryKey = stringPreferencesKey("contentCountry")
####Network Preferences
val ProxyEnabledKey = booleanPreferencesKey("proxyEnabled")
val ProxyHostKey = stringPreferencesKey("proxyHost")
val ProxyPortKey = intPreferencesKey("proxyPort")
val ProxyUsernameKey = stringPreferencesKey("proxyUsername")
val ProxyPasswordKey = stringPreferencesKey("proxyPassword")
val IpVersionKey = stringPreferencesKey("ipVersion")
####Feature Flags
val DeveloperModeKey = booleanPreferencesKey("developerMode")
val EnableSettingsPopupKey = booleanPreferencesKey("enableSettingsPopup")
####Third-Party Services
val InnerTubeCookieKey = stringPreferencesKey("innerTubeCookie")
val DiscordActivityNameKey = stringPreferencesKey("discordActivityName")
val DiscordActivityTypeKey = stringPreferencesKey("discordActivityType")
val ListenTogetherSmartResyncKey = booleanPreferencesKey("listenTogetherSmartResync")
val ListenTogetherSyncVolumeKey = booleanPreferencesKey("listenTogetherSyncVolume")
###Environment Variable Usage
Reading Preferences:
val audioQuality = context.dataStore[AudioQualityKey] ?: "high"
val isDarkMode = context.dataStore[DarkModeKey, "system"]
// Or with preference helper
val audioQuality by rememberPreference(AudioQualityKey, "high")
Writing Preferences:
context.dataStore.edit { preferences ->
preferences[AudioQualityKey] = "low"
preferences[DarkModeKey] = "dark"
}
###Security Implications
Hardcoded Credentials (⚠️ Security Concern):
- Last.fm API keys hardcoded in BuildConfig
- Not a security risk (API keys are public, rate-limited)
- Better practice: fetch from secure backend
Sensitive Data:
- YouTube cookies stored in DataStore (encrypted by Android)
- User preferences not encrypted (non-sensitive)
- No password storage (no user accounts)
Network Security:
- Network security config in
res/xml/network_security_config.xml - Enforces HTTPS for certain domains
- Allows cleartext for localhost (testing only)
##17. Configuration
###Gradle Configuration
File: build.gradle.kts
plugins {
id("com.android.application")
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.ksp)
alias(libs.plugins.compose.compiler)
}
android {
namespace = "com.music.vivi"
compileSdk = 36
ndkVersion = "27.0.12077973"
defaultConfig {
applicationId = "com.vivi.vivimusic"
minSdk = 26
targetSdk = 36
versionCode = 72
versionName = "6.0.3"
}
flavorDimensions += listOf("abi", "variant")
productFlavors {
create("foss") {
dimension = "variant"
buildConfigField("Boolean", "CAST_AVAILABLE", "false")
}
create("gms") {
dimension = "variant"
buildConfigField("Boolean", "CAST_AVAILABLE", "true")
}
}
}
###Kotlin Configuration
Language: Kotlin 2.3.10 JVM Target: 21 Features:
- Coroutines (suspend/async)
- Extension functions
- Data classes
- Sealed classes
- Flow/StateFlow
###Compose Configuration
Version: 1.10.2 Compiler Plugin: kotlin-plugin-compose (via kotlin plugin) Features:
- Recomposition optimization
- State hoisting
- CompositionLocal for dependency passing
###Database Configuration
Room:
@Database(
entities = [SongEntity::class, ...],
version = 11,
autoMigrations = [
AutoMigration(from = 9, to = 10),
AutoMigration(from = 10, to = 11)
]
)
###KSP Configuration
Kapt Processor: Hilt, Room, Serialization
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
arg("room.incremental", "true")
}
###Lint Configuration
Files:
lint.xml(root)app/lint.xml
Disabled Warnings:
<issue id="MissingTranslation" severity="ignore" />
<issue id="ExtraTranslation" severity="ignore" />
###Proguard Configuration
File: app/proguard-rules.pro
Rules:
- Keep data classes
- Keep Kotlin metadata
- Keep Hilt-generated classes
- Keep Room entities
- Keep Serialization classes
###Network Security Configuration
File: res/xml/network_security_config.xml
Policies:
- Enforce HTTPS for production domains
- Allow cleartext for localhost (testing)
- Pin certificates for YouTube
###AndroidManifest Configuration
File: app/src/main/AndroidManifest.xml
Key Settings:
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:theme="@style/Theme.vivimusic"
>
Permissions:
- INTERNET (required)
- RECORD_AUDIO (optional, for recognition)
- BLUETOOTH_CONNECT (optional)
- POST_NOTIFICATIONS (runtime, Android 13+)
Services:
- MusicService (foreground)
- ExoDownloadService (foreground)
Activities:
- MainActivity (main launcher)
- WelcomeActivity (first run)
- CrashActivity (crash handler)
###Version Configuration
File: gradle.properties
org.gradle.jvmargs=-Xmx6144M
org.gradle.parallel=true
org.gradle.daemon=true
android.useAndroidX=true
android.enableJetifier=false
###Dependencies Management
File: gradle/libs.versions.toml
Structure:
[versions]
kotlin = "2.3.10"
compose = "1.10.2"
...
[libraries]
compose-runtime = { ... }
...
[plugins]
compose-compiler = { ... }
...
Benefits:
- Centralized version management
- IDE completion for dependency versions
- Easy to update all instances
##18. Performance
###Caching Strategy
Three-Level Cache:
-
In-Memory (Fast)
- Lyrics LRU cache (50 items)
- Image loader memory cache
- ViewModel state flows
- ViewModels themselves (survives configuration changes)
-
Disk (Medium)
- ExoPlayer cache:
files/exoplayer/(1GB default LRU) - Download cache:
files/download/(unlimited) - Coil image cache:
cache/directory - Room SQLite database
- ExoPlayer cache:
-
Network (Slow)
- YouTube InnerTube API
- Lyrics provider APIs
- Last.fm API
- Shazam recognition
Cache Strategy:
- Streaming: Network-first → ExoPlayer cache → Error
- Lyrics: Database → Network providers → Default
- Images: Memory → Disk (Coil) → Network
- Metadata: Database → Network → Offline mode
###Lazy Loading
Pagination:
- Playlist songs loaded in chunks
- Search results paginated
- Home feed uses continuation tokens
- Album/artist items paginated
Code:
fun albumSongs(
playlistId: String,
album: AlbumItem?
): Result<List<SongItem>> = runCatching {
val results = mutableListOf<SongItem>()
var continuation: String? = null
do {
val page = innerTube.browsePlaylist(
playlistId,
continuation = continuation
)
results.addAll(page.songs)
continuation = page.continuationToken
} while (continuation != null)
results
}
###Pagination
Implementation:
class OnlinePlaylistViewModel {
private val continuation = MutableStateFlow<String?>(null)
fun loadMore() {
viewModelScope.launch {
continuation.value?.let { token ->
YouTube.playlistContinuation(token).onSuccess { page ->
songs.value += page.songs
continuation.value = page.continuationToken
}
}
}
}
}
###Memoization
Compose Memoization:
@Composable
fun SongCard(song: Song) {
remember(song.id) {
// Expensive color extraction only when song changes
extractColors(song.thumbnailUrl)
}
}
ViewModel Memoization:
val topArtists = database.topArtists()
.distinctUntilChanged() // Skip duplicate emissions
.stateIn(scope, SharingStarted.Lazily, emptyList())
###Virtualization
Compose LazyColumn/LazyRow:
LazyColumn {
items(songs) { song ->
SongCard(song) // Only visible items rendered
}
}
ExoPlayer Queue:
- Only preloads next song
- Discards previous song from buffer
- Configurable preload window
###Database Optimization
Indexes:
@Entity(
indices = [
Index(value = ["albumId"]),
Index(value = ["title"]),
Index(value = ["inLibrary"])
]
)
data class SongEntity(...)
Query Optimization:
- Use
@RewriteQueriesToDropUnusedColumnsto avoid loading unnecessary fields - Compose Room queries with JOIN for efficiency
- Use Flow for reactive updates (single query, multiple subscribers)
Caching Queries:
val songs = database.songs()
.distinctUntilChanged()
.stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
// First subscriber executes, others reuse result
###Image Optimization
Coil Configuration:
val imageLoader = ImageLoader.Builder(context)
.memoryCache(MemoryCache(context, MEMORY_CACHE_SIZE))
.diskCache(DiskCache(context, DISK_CACHE_SIZE))
.crossfade(300) // Smooth transitions
.allowHardware(true) // Use GPU when safe
.build()
Album Art Processing:
- Thumbnail size limited to 300x300px for in-memory cache
- Full size loaded separately for player
- Color extraction happens once, cached
###Bundle Splitting
Gradle Configuration:
bundle {
enableSplit = true
}
ABIs:
- universal (all archs, largest)
- arm64 (modern devices)
- armeabi-v7a (legacy devices)
Installation Size:
- ~50MB for arm64 APK
- ~80MB for universal APK
- Downloads only needed ABI
###Server Rendering
Not applicable - Native Android app, no server rendering
###Networking Optimization
Connection Pooling:
engine {
config {
connectionPool(
okhttp3.ConnectionPool(
10, // maxIdleConnections
5, // keepAliveDuration in minutes
java.util.concurrent.TimeUnit.MINUTES
)
)
}
}
Request Compression:
install(ContentEncoding) {
gzip(0.9F)
deflate(0.8F)
}
HTTP/2:
- OkHttp supports HTTP/2
- Connection multiplexing
- Header compression
###Background Job Optimization
WorkManager:
val updateCheck = PeriodicWorkRequestBuilder<NewReleaseCheckWorker>(
1, TimeUnit.DAYS
).addTag("update_check").build()
WorkManager.getInstance().enqueueUniquePeriodicWork(
"update_check",
ExistingPeriodicWorkPolicy.KEEP,
updateCheck
)
Constraints:
- Run only when charging
- Require network connectivity
- Avoid battery drain
###UI Rendering Performance
Recomposition Optimization:
@Composable
fun Home() {
val songs by viewModel.songs.collectAsState()
// Only recompose when songs changes
LazyColumn {
items(songs, key = { it.id }) { song ->
SongCard(song) // Stable key prevents recomposition
}
}
}
Custom Drawing:
- Canvas visualizer only redraws on position change
- Animated gradient computed once per theme change
- Shimmer effect uses built-in library (optimized)
##19. Security
###Authentication
Status: No user authentication
The app operates anonymously:
- Uses YouTube InnerTube client API (no login required)
- VisitorData token auto-generated
- No session tokens or user passwords
- Third-party APIs use app-level credentials
###Authorization
No Role-Based Access Control
All users have access to all features. Device-level permissions instead:
- RECORD_AUDIO → Music recognition
- WRITE_EXTERNAL_STORAGE → Downloads/backups
- BLUETOOTH → Device connectivity
- POST_NOTIFICATIONS → Notification display
###Input Validation
API Inputs:
// Validate video ID format
fun validateVideoId(videoId: String): Boolean {
return videoId.matches(Regex("^[a-zA-Z0-9_-]{11}$"))
}
// Validate playlist ID
fun validatePlaylistId(playlistId: String): Boolean {
return playlistId.startsWith("OLAK")
}
Database Inputs:
- Title/artist names: non-empty strings
- Duration: positive integers
- Timestamps: valid LocalDateTime
Network Inputs:
- JSON parsed via Kotlinx Serialization (type-safe)
- Unknown fields ignored
- Null handling for optional fields
###XSS Protection
Not Applicable - Native Android app, no web rendering
However:
- Metadata (titles, descriptions) displayed as plain text
- No HTML parsing of user data
- HTML parsing only for scraping (JSoup with content filtering)
###CSRF Protection
Not Applicable - No session-based authentication
All API requests include:
- Proper Content-Type headers
- CSRF tokens where required by API
###SQL Injection Protection
Prevention via Room ORM:
// Safe - parameters bound
@Query("SELECT * FROM song WHERE id = :id")
fun song(id: String): Flow<Song?>
// NOT used - prevents SQL injection
// Instead of: "SELECT * FROM song WHERE id = '$id'"
Room uses parameterized queries internally.
###Rate Limiting
None Implemented Locally
However:
- YouTube API rate-limited (handled by server)
- Last.fm rate-limited (handled by server)
- Retry logic with exponential backoff
Client-side Debouncing:
val searchQuery = MutableStateFlow("")
val debouncedQuery = searchQuery
.debounce(500) // Wait 500ms before searching
.distinctUntilChanged()
.flatMapLatest { query ->
YouTube.search(query) // Network call
}
###Secrets Management
API Keys:
- Last.fm: Hardcoded in BuildConfig (public API keys, acceptable)
- YouTube: No key needed (client API)
- Third-party: Stored in DataStore preferences (user-entered)
Better Practice:
// Could fetch from secure backend instead
val apiKey = fetchFromSecureBackend("lastfm_key")
Current Risk:
- Last.fm keys exposed in APK
- Not a security issue (keys are rate-limited and public anyway)
###Certificate Pinning
Network Security Config:
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">youtube.com</domain>
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">certificate_hash</pin>
</pin-set>
</domain-config>
Configured but not aggressive.
###Secure Connections
HTTPS Only for production:
- All YouTube requests over HTTPS
- All API requests over HTTPS
- Cleartext only for localhost testing
###Data Encryption
In Transit: HTTPS (enforced)
At Rest:
- DataStore uses Android Keystore for encryption (optional, not forced)
- Room database not encrypted (but on device-private storage)
- Downloads stored in app-private files/ directory
Recommendation:
- Enable DataStore encryption for sensitive prefs
- Use EncryptedSharedPreferences for better security
###Permissions
Declared:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
Runtime Permissions (Android 6+):
- RECORD_AUDIO (requested on app open if recognition needed)
- POST_NOTIFICATIONS (requested on Android 13+)
- Storage permissions handled via scoped storage
###Vulnerabilities & Mitigations
| Vulnerability | Current Status | Mitigation |
|---|---|---|
| Hardcoded API Keys | ⚠️ Present | Keys are rate-limited, public anyway |
| No Data Encryption | ⚠️ Risk | User preferences stored plaintext in DataStore |
| SQL Injection | ✅ Safe | Room ORM uses parameterized queries |
| XSS | ✅ N/A | Native app, no web rendering |
| CSRF | ✅ N/A | No session-based auth |
| Man-in-the-Middle | ✅ Safe | HTTPS enforced, cert pinning in place |
| Insecure Deserialization | ✅ Safe | Kotlinx Serialization with type checking |
| Unvalidated Redirects | ✅ Safe | No deep linking beyond InnerTube |
| Exposed Sensitive Data | ⚠️ Minor | Search history, play history in SQLite |
| Weak Crypto | ✅ Safe | Uses platform crypto (Keystore) |
###Data Collection
Privacy Stance: Zero tracking
What is Collected Locally:
- Search history (user searches)
- Play history (songs played)
- Play statistics (total plays, playtime)
- Liked songs
- Downloaded songs
- User preferences
What is NOT Collected:
- User location
- Device advertising ID
- Crash reports (unless enabled)
- Telemetry
- Analytics
Data Retention:
- Everything stored locally
- User can clear at any time
- No cloud sync (local device only)
##20. Error Handling
###Global Error Handling
CrashHandler.kt (App-level):
object CrashHandler {
fun install(context: Context) {
Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
// Log crash
logCrash(exception)
// Launch crash activity
context.startActivity(
Intent(context, CrashActivity::class.java)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
}
}
}
CrashActivity (UI):
- Displays crash info
- Shows stack trace
- Allow user to report/share
###Logging
Framework: Timber (Jake Wharton)
// In development
Timber.plant(Timber.DebugTree())
// In production
Timber.plant(CrashReportingTree())
Usage:
Timber.d("Debug message")
Timber.i("Info message")
Timber.w("Warning message")
Timber.e("Error message")
###Network Error Handling
Retry Strategy:
suspend fun <T> withRetry(
maxAttempts: Int = 3,
delayMillis: Long = 100,
block: suspend () -> T
): T {
repeat(maxAttempts - 1) {
try {
return block()
} catch (e: Exception) {
delay(delayMillis * (it + 1))
}
}
return block() // Last attempt without catch
}
Error Handling Pattern:
YouTube.search(query).onFailure { exception ->
when (exception) {
is NetworkException -> showOfflineMessage()
is TimeoutException -> showTimeoutMessage()
else -> reportException(exception)
}
}
###Database Error Handling
Transaction Rollback:
try {
database.withTransaction {
database.insertSong(song1)
database.insertSong(song2)
// If either fails, both rollback
}
} catch (e: SQLException) {
Timber.e(e, "Database transaction failed")
}
###Playback Error Handling
ExoPlayer Error Listener:
player.addListener(object : Player.Listener {
override fun onPlayerError(error: PlaybackException) {
when (error.errorCode) {
PlaybackException.ERROR_CODE_NO_SUITABLE_DECODER ->
skipToNextSong()
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED ->
showNetworkError()
else ->
reportException(error)
}
}
})
###Retry Logic
YouTube Requests:
YouTube.search(query) // Already has retry in InnerTube
.onFailure { error ->
if (error is NetworkException) {
// Try cached data
database.searchHistory()
}
}
###Fallbacks
Missing Lyrics:
if (lyrics == null) {
// Try next provider
// If all fail, show "Lyrics not found"
}
Missing Album Art:
if (thumbnailUrl == null) {
// Use placeholder image
PlaceholderThumbnail()
}
Offline Mode:
if (!networkConnectivity.isAvailable()) {
// Use cached library
// Disable streaming
// Show offline indicator
}
###Monitoring & Alerting
Firebase Crashlytics (if enabled):
- Not included by default (FOSS)
- Optional in custom builds
Local Logging:
- Timber logs to file (optional)
- Crash logs saved to app-private storage
- PlaybackLogManager tracks playback events
###Error Messages
User-Friendly:
when (error) {
is NetworkException -> "Check your internet connection"
is TimeoutException -> "Request timed out"
is SerializationException -> "Failed to parse data"
else -> "An unknown error occurred"
}
##21. Testing
###Testing Framework
Unit Testing: JUnit 4
testImplementation(libs.junit)
###Test Structure
Limited Testing - No test files found in repository
This suggests:
- Primarily integration/manual testing
- No automated test suite in codebase
- Quality through code review
###Recommended Testing Strategy
Unit Tests:
- ViewModel logic (search, filtering)
- Database queries
- Utility functions
- String parsing (lyrics, timestamps)
Integration Tests:
- Database + DAO queries
- YouTube API integration
- Lyrics provider fallback
UI Tests:
- Compose component preview tests
- Navigation flow
- User interactions
###Code Coverage
Unknown - No coverage reports in repo
###Testing Best Practices
Would benefit from:
- Unit tests for ViewModels
- Integration tests for database
- UI tests with Compose test framework
- Network mocking (Mockk, WireMock)
- Property-based testing (Kotest)
##22. Folder Dependency Graph
###Module Dependencies
app (main)
├── innertube
│ ├── newpipe-extractor
│ └── ktor
├── canvas
├── kugou
├── lrclib
├── betterlyrics
├── youlyplus
├── simpmusic
├── paxsenixlyrics
├── shazamkit
├── lastfm
├── spotify
├── jiosaavn
├── artistvideo
├── applecanvas
├── vivimusiccanvas
└── kizzy
Each lyrics module:
└── ktor, jsoup (indirect)
###Layer Dependencies
UI Layer (screens/)
↓
ViewModel Layer (viewmodels/)
↓
Business Logic (services/, managers/)
↓
Data Access Layer (db/, innertube/)
↓
External APIs (YouTube, Lyrics, Last.fm)
###Cross-Module Communication
app ← imports from → innertube, lyrics modules, shazamkit, lastfm
No circular dependencies (acyclic graph)
Each module is independent except:
- All modules depend on Gradle libs (Ktor, etc.)
- All modules depend on Android SDK
##23. Important Classes and Functions
###Core Entry Points
####App.kt (Application class)
Purpose: Initialize app, set up preferences, install crash handler Key Methods:
onCreate()- App startupinitializeSettings()- Load YouTube locale, DPI scalingobserveSettingsChanges()- Listen to preference updates
####MainActivity.kt (Activity, 1300 lines)
Purpose: Main UI container, handles navigation Key Methods:
onCreate()- Set up Compose, navigation, service bindingonDestroy()- Cleanup, unbind servicehandleIntents()- Deep link handling
####MusicService.kt (Service, 3400 lines)
Purpose: Audio playback engine Key Methods:
setupPlayer()- Initialize ExoPlayerloadMediaItems()- Load queueplay(),pause(),next(),previous()- Playback controlsetupAudioEffects()- EQ, loudness
###ViewModel Hub
####HomeViewModel.kt (771 lines)
loadHome()- Fetch home feedgenerateDailyDiscover()- AI recommendationsloadKeepListening()- Resume queue
####OnlineSearchViewModel.kt
search(query)- YouTube searchloadMore()- Pagination
####AlbumViewModel.kt
loadAlbum()- Fetch album detailsloadArtist()- Load creator info
###Data Access Layer
####DatabaseDao.kt (1603 lines)
100+ Query Methods:
songs(),likedSongs(),albums(),artists()playlistSongs(),playlistContinuation()songsByName(),songsByArtist(),songsByPlayTime()topArtists(),topAlbums()
####YouTube.kt (1765 lines)
High-Level API:
search(),searchSuggestions()album(),artist()playlist(),createPlaylist()home(),explore()likeVideo(),toggleSongLibrary()
###Business Logic Services
####LyricsHelper.kt (189 lines)
getLyrics()- Multi-provider with fallbackresolveLyricsProviders()- Provider ordering- Caching logic
####ListenTogetherManager.kt (1726 lines)
startSession()- Create listen togethersyncPlayback()- Sync between usershandleQueueChange()- Propagate queue changes
####ScrobbleManager.kt
scrobbleTrack()- Send to Last.fmgetArtistStats()- Fetch info
####MusicRecognitionService.kt
recognize()- Record audio, identify songgenerateFingerprint()- Audio processing
###Utilities
####DataStore.kt
- Extension operators for preferences
- Type converters
- Default value handling
####ViviPrefCache.kt
- In-memory preference cache
- Observes DataStore changes
- Fast access path
####NetworkConnectivityObserver.kt
- Network state monitoring
- Fallback to synchronous checks
####CrashHandler.kt
- Global exception handler
- Crash logging
- Launch crash activity
####Utils.kt
- Device info (brand, model, SDK)
- String formatting
- Color utilities
####YTPlayerUtils.kt
- WEB_REMIX decipher
- Stream URL handling
- Format selection
##24. Code Quality Review
###Architectural Strengths
✅ Clean Separation of Concerns
- UI (Compose) completely separated from business logic
- ViewModels don't reference UI directly
- Database abstracted via DAO pattern
✅ Reactive Architecture
- Flow-based reactive streams throughout
- No callback hell
- Automatic cleanup with scopes
✅ Dependency Injection
- Hilt reduces boilerplate
- Single responsibility for module classes
- Testable through DI
✅ Type Safety
- Room ORM prevents SQL injection
- Kotlinx Serialization for JSON
- No runtime type casts
✅ Modular Structure
- Separate modules for external services (innertube, lyrics, recognition)
- Each module has clear responsibility
- No circular dependencies
###Code Smells & Issues
⚠️ Hardcoded API Keys
- Last.fm credentials in BuildConfig
- Should use secure backend or environment variables
- Risk: Low (keys are rate-limited)
⚠️ Very Large Files
- MusicService.kt: 3400 lines
- YouTube.kt: 1765 lines
- Should be split into smaller components
- Impact: Harder to test, navigate
⚠️ Limited Test Coverage
- No test files in repository
- Manual testing only
- Should have unit + integration tests
- Risk: Regressions go undetected
⚠️ Preference Key Duplication
- PreferenceKeys.kt: 772 lines with 100+ keys
- Could use sealed class pattern or code generation
- Currently type-unsafe at usage sites
⚠️ Mixed Concerns in ViewModels
- HomeViewModel: 771 lines (too large)
- Should split by feature (recommendations, feed, etc.)
- Single Responsibility violated
⚠️ Error Handling Inconsistency
- Some flows use Result
- Others throw exceptions
- Some use status sealed classes
- Should standardize on one pattern
###Technical Debt
| Item | Impact | Priority |
|---|---|---|
| Large Service Classes | Navigation, testing | High |
| No Automated Tests | Regression risk | High |
| Inconsistent Error Handling | Maintainability | Medium |
| Preference Keys Architecture | Type safety | Medium |
| Code Duplication in Queries | Maintenance burden | Low |
###Possible Bugs
Potential Race Conditions:
- Player initialization timeout (PLAYER_INIT_TIMEOUT_MS = 5s)
- Could occur if service startup delayed
- Mitigation: Timeout with fallback
Memory Leaks:
- Long-lived coroutines in services
- Should use viewModelScope or cancellation token
- Mitigation: Proper scope management in place
Stale Cache Issues:
- Lyrics cached indefinitely
- Could become outdated for long-running app
- Mitigation: Manual refresh option exists
###Scalability Concerns
Database Query Performance:
- Large playlist queries not paginated (loads all at once)
- Could be slow with 1000+ items
- Solution: Implement LazyColumn with continuation
Memory Usage:
- Player queue loaded entirely in memory
- Could cause OOM with very long playlists
- Solution: Lazy load queue items
Network Bandwidth:
- Streaming quality selection present
- Cache properly configured
- Status: Good
###Code Quality Improvements
High Priority:
- Split MusicService into smaller classes
- Add unit test suite
- Standardize error handling
- Use secure key storage
Medium Priority:
- Refactor large ViewModels
- Add integration tests
- Improve type safety for preferences
- Document complex algorithms (cipher, fingerprinting)
Low Priority:
- Reduce code duplication in DAO queries
- Add property-based tests
- Extract magic numbers to constants
- Add architectural ADRs (Architecture Decision Records)
##25. Missing Features
###Evidence of Planned but Unfinished Features
1. Apple Music Integration
- Evidence:
applecanvas/module (visualizer, not music)utils/AppleMusicAboutAlbum.kt(fetches about album data)canvas/AppleMusicArtistBackgroundProvider.kt
- Status: Only visualizer components, not music source
2. Tidal Integration
- Evidence:
canvas/TidalCanvasProvider.kt(visualizer only)
- Status: No music streaming integration
3. Enhanced User Profiles
- Evidence:
AccountViewModel.ktexists but minimal- No profile picture upload
- No account stats beyond Last.fm
- Status: Basic account page only
4. Collaborative Features (Partial)
- Evidence:
- ListenTogether exists but incomplete
- No voice chat during listening
- No comments on playlists
- CommentSheet.kt exists but minimal
- Status: Basic queue sync only
5. Personalized Recommendations (Basic)
- Evidence:
- HomeViewModel fetches "similar recommendations"
- But logic is basic (random seed)
- Could use machine learning for better accuracy
- Status: Working but simplistic
6. Spotify Full Integration
- Evidence:
- SpotifyImportViewModel.kt only imports playlists
- No user account authentication
- No scrobbling to Spotify
- Cannot play Spotify songs directly
- Status: Import-only
7. Video Support
- Evidence:
artistvideo/module exists- SongEntity has
isVideoflag - UI filters can hide video songs
- No actual video playback implemented
- Status: Detection only, no playback
8. Advanced Analytics
- Evidence:
- StatsViewModel.kt has basic statistics
- PlayCountEntity tracks plays
- No machine learning insights
- Limited visualization options
- Status: Basic stats only
9. Cloud Sync/Backup
- Evidence:
- AutoBackupHelper.kt only saves to local Downloads
- No cloud storage integration (Dropbox, Google Drive)
- No cross-device sync
- Status: Local backup only
10. Equalizer Presets
- Evidence:
- EqualizerService.kt exists
- No preset manager found
- Cannot save/load custom presets
- Status: Real-time adjustment only
###Planned But Not Started
Based on Code Structure:
- Android TV UI - Leanback support declared but minimal implementation
- Widget System - Widget receivers exist but limited functionality
- Discord Rich Presence - DiscordRPC.kt exists but may be incomplete
- Advanced Search - Only basic search, no filters for date/duration/genre
##26. How to Extend the Project
###Adding a New Page/Screen
Steps:
- Create ViewModel (
viewmodels/NewFeatureViewModel.kt):
@HiltViewModel
class NewFeatureViewModel @Inject constructor(
val database: MusicDatabase
) : ViewModel() {
val data = MutableStateFlow<List<Item>>(emptyList())
init {
viewModelScope.launch {
database.items().collect { items ->
data.value = items
}
}
}
}
- Create Screen Composable (
ui/screens/NewFeatureScreen.kt):
@Composable
fun NewFeatureScreen(
viewModel: NewFeatureViewModel = hiltViewModel()
) {
val data by viewModel.data.collectAsState()
LazyColumn {
items(data) { item ->
ItemCard(item)
}
}
}
- Add Route (
ui/screens/Screens.kt):
sealed class Screens(...) {
object NewFeature : Screens(
titleId = R.string.new_feature,
iconIdInactive = R.drawable.icon_inactive,
iconIdActive = R.drawable.icon_active,
route = "new_feature"
)
}
- Add Navigation (
MainActivity.kt):
NavHost(...) {
composable(Screens.NewFeature.route) {
NewFeatureScreen()
}
}
- Add to Bottom Nav (if main screen):
val MainScreens = listOf(Home, Search, ListenTogether, Library, NewFeature)
###Adding a New API Endpoint
Example: Add Podcast Support
- Create API Module (
podcast/build.gradle.kts):
plugins {
id("com.android.library")
alias(libs.plugins.kotlin.serialization)
}
dependencies {
implementation(libs.ktor.client.core)
implementation(libs.ktor.serialization.json)
}
- Define Models (
podcast/src/main/kotlin/.../models/):
data class Podcast(
val id: String,
val title: String,
val episodes: List<Episode>
)
data class Episode(
val id: String,
val title: String,
val url: String,
val duration: Int
)
- Create Service (
podcast/src/main/kotlin/.../Podcast.kt):
object Podcast {
private val httpClient = createClient()
suspend fun getPodcast(id: String): Result<Podcast> = runCatching {
httpClient.get("https://api.podcast.com/v1/podcast/$id")
.body<Podcast>()
}
}
- Add to App Module (
app/build.gradle.kts):
dependencies {
implementation(project(":podcast"))
}
###Adding Authentication
Example: YouTube Login
- Create Auth Service (
utils/YouTubeAuthHelper.kt):
class YouTubeAuthHelper(context: Context) {
suspend fun authenticate(): Result<YouTubeUser> = runCatching {
// OAuth flow here
YouTube.accountInfo()
}
fun logout() {
YouTube.cookie = null
YouTube.dataSyncId = null
}
}
- Add Login Screen (
ui/screens/LoginScreen.kt):
@Composable
fun LoginScreen(onLoginSuccess: (YouTubeUser) -> Unit) {
Button(onClick = { /* Launch auth */ }) {
Text("Sign in with YouTube")
}
}
- Add to Navigation:
NavHost(...) {
composable("login") {
LoginScreen(onLoginSuccess = {
navController.navigate(Screens.Home.route)
})
}
}
###Adding a Database Model
Example: Add User Preferences Model
- Create Entity (
db/entities/UserPreferencesEntity.kt):
@Entity(tableName = "user_preferences")
data class UserPreferencesEntity(
@PrimaryKey val userId: String,
val theme: String,
val language: String,
val savedAt: LocalDateTime
)
- Add to Database:
@Database(
entities = [
...,
UserPreferencesEntity::class
],
version = 12, // Increment version
autoMigrations = [
AutoMigration(from = 11, to = 12) // Add migration
]
)
- Create DAO Methods (
db/DatabaseDao.kt):
@Upsert
suspend fun upsertUserPreferences(prefs: UserPreferencesEntity)
@Query("SELECT * FROM user_preferences WHERE userId = :userId")
fun userPreferences(userId: String): Flow<UserPreferencesEntity?>
###Adding a Service/Manager
Example: Add Notification Manager
- Create Service (
utils/NotificationManager.kt):
@Singleton
class NotificationManager @Inject constructor(
@ApplicationContext private val context: Context
) {
fun showNotification(title: String, message: String) {
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle(title)
.setContentText(message)
.build()
NotificationManagerCompat.from(context).notify(NOTIFY_ID, notification)
}
}
- Inject into Services:
@HiltViewModel
class HomeViewModel @Inject constructor(
val notificationManager: NotificationManager
) : ViewModel() {
// Use notificationManager
}
###Adding UI Components
Example: Add Custom Slider
- Create Component (
ui/component/CustomSlider.kt):
@Composable
fun CustomSlider(
value: Float,
onValueChange: (Float) -> Unit,
modifier: Modifier = Modifier,
range: ClosedFloatingPointRange<Float> = 0f..100f
) {
Slider(
value = value,
onValueChange = onValueChange,
valueRange = range,
modifier = modifier
)
}
- Use in Screens:
CustomSlider(
value = volume,
onValueChange = { setVolume(it) }
)
##27. Learning Guide for New Engineers
###Which Files to Read First
Phase 1: Architecture Overview (30 minutes)
MainActivity.kt(understand UI initialization)App.kt(understand app startup)playback/MusicService.kt(understand playback model)playback/PlayerConnection.kt(understand UI-service bridge)
Phase 2: Data Flow (1 hour)
viewmodels/HomeViewModel.kt(understand ViewModel pattern)db/DatabaseDao.kt(understand data queries)innertube/YouTube.kt(understand API integration)ui/screens/HomeScreen.kt(understand UI pattern)
Phase 3: Key Features (2-3 hours)
lyrics/LyricsHelper.kt(understand multi-provider pattern)listentogether/ListenTogetherManager.kt(understand WebSocket sync)ui/player/Player.kt(understand player UI)recognition/MusicRecognitionService.kt(understand fingerprinting)
Phase 4: Infrastructure (1-2 hours)
di/AppModule.kt(understand DI configuration)utils/DataStore.kt(understand preferences)constants/PreferenceKeys.kt(understand configuration)ui/theme/Theme.kt(understand theming)
###Most Important Files
Core Application:
MainActivity.kt- Entry point, navigationApp.kt- Initialization, preferencesMusicService.kt- Playback enginePlayerConnection.kt- UI-service bridge
Business Logic:
HomeViewModel.kt- Main feed logicLyricsHelper.kt- Lyrics resolutionListenTogetherManager.kt- Sync logicDatabaseDao.kt- Data queries
UI:
ui/screens/HomeScreen.kt- Home pageui/player/Player.kt- Player UIui/theme/Theme.kt- Theming system
Infrastructure:
di/AppModule.kt- Dependency injectioninnertube/YouTube.kt- API clientdb/MusicDatabase.kt- Database schema
###Concepts to Understand First
1. Kotlin Coroutines & Flow (Essential)
- Suspension and resume
- StateFlow vs SharedFlow
- Scope management
- collectAsState in Compose
2. Jetpack Compose (Essential)
- Declarative UI paradigm
- Recomposition
- State hoisting
- Modifiers
- LazyColumn/LazyRow
3. Android Architecture (Important)
- Service lifecycle
- Foreground services
- Media sessions
- Notification API
4. Clean Architecture (Important)
- Separation of concerns
- Dependency injection
- DAO pattern
- ViewModel pattern
5. MVVM Pattern (Important)
- ViewModel responsibilities
- State flows
- One-way data flow
###Recommended Reading Order
Week 1: Fundamentals
- Day 1: Read MainActivity, App, MusicService
- Day 2: Read PlayerConnection, understand state flows
- Day 3: Read HomeViewModel, understand ViewModel pattern
- Day 4: Read DatabaseDao, understand queries
- Day 5: Read YouTubeAPI, understand API integration
Week 2: Deep Dives
- Day 1: Read Player UI (ui/player/Player.kt)
- Day 2: Read Lyrics system (LyricsHelper + providers)
- Day 3: Read ListenTogether (WebSocket sync)
- Day 4: Read Theme system
- Day 5: Read DI configuration
Week 3: Features
- Day 1-2: Play song end-to-end
- Day 2-3: Search and add to queue
- Day 3-4: Download and play offline
- Day 4-5: Join ListenTogether session
Week 4: Advanced Topics
- Day 1-2: Music recognition (fingerprinting)
- Day 2-3: Synchronization & caching
- Day 3-4: Error handling & resilience
- Day 4-5: Performance optimization
###Key Architectural Patterns Used
-
MVVM with State Flows
- ViewModel provides StateFlow
- Compose observes via collectAsState()
- One-way data flow
- ViewModel provides StateFlow
-
Reactive Programming
- Flow for async operations
- Operators: map, filter, combineLatest
- Cancellation with scope
-
Dependency Injection (Hilt)
- @HiltViewModel for ViewModels
- @Module for singleton providers
- @Inject in constructors
-
Repository Pattern
- DatabaseDao as repository
- Room provides caching
- API client as remote repository
-
Multi-Provider Pattern
- LyricsHelper tries multiple providers
- Fallback strategy
- User preference ordering
###Questions to Ask When Reviewing Code
-
Where does data come from?
- Network? Database? Both?
- When is it fetched?
- Is it cached?
-
How is state managed?
- StateFlow? MutableState?
- When is it updated?
- Who is observing it?
-
How is error handled?
- Exceptions caught? Result
? - User feedback?
- Retry logic?
- Exceptions caught? Result
-
How does it integrate with player?
- Does it affect playback?
- Does it access PlayerConnection?
- Is there a listener?
-
What happens on configuration change?
- Is state preserved?
- Are coroutines canceled properly?
- Is there a memory leak?
##28. Executive Summary
###Architecture
Type: Modern Android MVVM with Jetpack Compose
Strengths:
- ✅ Clean separation of concerns (UI/Logic/Data)
- ✅ Reactive architecture with Flow/StateFlow
- ✅ Type-safe database access (Room ORM)
- ✅ Type-safe API calls (Ktor + Serialization)
- ✅ Dependency injection (Hilt) reduces boilerplate
- ✅ Modular design with separate library modules
Weaknesses:
- ⚠️ Very large service classes (MusicService 3400 lines)
- ⚠️ No automated test suite
- ⚠️ Hardcoded API keys (minor security risk)
- ⚠️ Preference keys not type-safe (run-time strings)
###Strengths
-
Feature-Rich
- Music streaming with ad-free YouTube Music
- 10+ lyrics providers with fallback
- Music recognition (Shazam)
- Collaborative listening (Listen Together)
- Offline downloading
- Last.fm scrobbling
- Rich statistics & "Wrapped"
-
Privacy-First
- 100% local database
- Zero tracking/analytics
- No cloud telemetry
- User data stays on device
-
Great UX
- Material 3 design system
- Dynamic color from album art
- Smooth animations
- Comprehensive customization
- Multiple UI modes (player styles, lyrics display)
-
Well-Structured Codebase
- Clear separation of concerns
- Reactive programming patterns
- Type-safe throughout
- Good use of modern Android frameworks
-
Extensible Design
- Plugin-style lyrics providers
- Canvas visualizer system
- Modular feature modules
- Dependency injection for testability
###Weaknesses
-
Limited Testing
- No automated test suite
- Manual testing only
- Risk of regressions
- Hard to verify refactors
-
Very Large Components
- MusicService: 3400 lines
- YouTube API: 1765 lines
- HomeViewModel: 771 lines
- Hard to understand, test, modify
-
Incomplete Features
- Listen Together partially implemented
- No video playback (only audio from videos)
- Spotify import only (no playback)
- Limited machine learning insights
-
Documentation Gaps
- No README for architecture
- Limited code comments
- No API documentation
- Unclear future roadmap
-
Code Quality Issues
- Inconsistent error handling
- Some code duplication in queries
- Mixed concerns in some services
- Performance could be better (large playlists)
###Overall Design
Rating: 7.5/10
The Good:
- Modern architecture using latest Android frameworks
- Excellent UX with material design
- Privacy-first philosophy
- Feature-rich functionality
- Clean separation of concerns
The Bad:
- Monolithic components need refactoring
- No test coverage
- Some technical debt accumulated
- Documentation missing
Suitable For:
- Privacy-conscious music lovers
- F-Droid/open-source enthusiasts
- YouTube Music users
- Developers wanting to contribute
Not Suitable For:
- Enterprise applications
- Projects requiring high test coverage
- Teams that prefer backend APIs
- Organizations needing professional support
###Scalability
Current State: ⚠️ Moderate concerns
What Scales Well:
- ✅ Database (Room/SQLite handles millions of songs)
- ✅ API calls (Ktor with connection pooling)
- ✅ Memory (Compose recomposition optimized)
- ✅ Caching (Multi-level cache strategy)
What Doesn't Scale:
- ⚠️ Large playlists (loaded entirely in memory)
- ⚠️ Large service classes (hard to maintain)
- ⚠️ Monolithic ViewModels (slow to modify)
- ⚠️ Single database file (no sharding)
Improvements Needed:
- Split MusicService into smaller components
- Implement lazy-loading for long playlists
- Add pagination throughout
- Refactor large ViewModels
###Maintainability
Rating: 6/10
Positive Factors:
- Type-safe code reduces bugs
- Clear package organization
- Modern libraries (Compose, Flow, Hilt)
- Kotlin idioms well-used
Negative Factors:
- Large files hard to navigate
- Limited comments/documentation
- No test suite to verify changes
- Some magic numbers scattered
Maintenance Cost: Medium-High
- Easy to add features (good architecture)
- Hard to refactor (no tests)
- Hard to onboard new developers
- Good for small team, scales poorly
###Complexity
Overall: 7/10 (Moderately Complex)
Difficult Areas:
- Audio playback pipeline (ExoPlayer setup, effects, formats)
- Lyrics synchronization (multiple providers, fallback logic)
- WebSocket sync (ListenTogether, state machine)
- YouTube InnerTube API (many endpoints, responses)
Simple Areas:
- Local library management (straightforward CRUD)
- Search & filter (simple database queries)
- Preferences (DataStore wrapper)
- Basic playback (play/pause/next)
###Development Velocity
Initial Setup: 1-2 days
- Clone repo
- Install Android Studio
- Build project
- Understand project structure
Feature Development: Moderate
- ViewModels easy to create
- Compose UI straightforward
- Database queries clear
- API calls through existing client
Testing: Slow
- No test framework configured
- No test examples to follow
- Would need to set up from scratch
Deployment: Fast
- Build system well-configured
- Multiple APK variants (FOSS/GMS)
- Release process documented
- FastLane ready
###Recommendations for Improvement
Short Term (1-2 months):
- Add unit tests for ViewModels
- Split MusicService into smaller classes
- Document API endpoints
- Add code comments for complex logic
- Standardize error handling
Medium Term (3-6 months):
- Implement integration tests
- Add UI tests with Compose testing
- Refactor large ViewModels
- Implement lazy-loading for playlists
- Add proper logging framework
Long Term (6+ months):
- Consider MVVM/MVI state management library (Mvi, Redux)
- Add offline-first architecture
- Implement background sync
- Add machine learning recommendations
- Create public API for extensions
###Conclusion
VIVI Music is a well-designed, feature-rich Android music player that demonstrates excellent use of modern Android frameworks and architectural patterns. The codebase is generally clean and organized, with strong type safety and reactive programming practices.
However, the project would benefit from:
- Automated test coverage (biggest gap)
- Refactoring of large components
- Better documentation
- Code cleanup and debt reduction
Despite these issues, the application provides an excellent user experience and serves as a great reference implementation for:
- Modern Android MVVM architecture
- Jetpack Compose UI development
- Reactive programming with Kotlin Flows
- Multi-source data integration
- Privacy-first application design
The codebase is production-quality for a single developer project, but would need some refactoring before being suitable for a large team to maintain.
Overall Assessment: A solid, well-crafted music player with strong foundations. Good for learning Android development patterns, but could use additional structure for enterprise-scale development.
END OF COMPREHENSIVE ANALYSIS
This report covers 28 sections analyzing every aspect of the VIVI Music repository from architecture to deployment, providing a complete technical understanding suitable for onboarding a new engineering team or conducting a thorough code review.
END OF POST