# NovaSonic A self-hosted music streaming PWA and Android app for Navidrome / Subsonic-compatible servers. Built with Vite + vanilla ES modules (web) and Jetpack Compose + Media3/ExoPlayer (Android). --- ## Features ### Music Library - Stream music from your Navidrome / Subsonic library - Browse artists, albums, playlists, starred tracks, genres, and random picks - Search across songs, albums, and artists - Star and context menu on every song row - Play count display (optional) ### Lyrics - Synced and plain lyrics via lrclib.net, auto-fetched per track - Fallback chain: exact match → no-duration match → fuzzy artist+title search → title-only search - In-memory cache per session ### Artist Pages - Hero card with artist photo and blurred background - Bio, similar artists, top songs from Last.fm - Concert events via Ticketmaster - Albums cross-referenced against iTunes catalog — shows downloaded vs. missing tracks - Artist cards with images in search results ### Playback - Queue with shuffle, repeat (all / one), and radio mode (endless similar songs) - Now Playing overlay with synced lyrics, queue view, star, and multi-artist links - Gapless-style playback — next track pre-buffered before current track ends - Lock screen / notification media controls - Scrobbling to Navidrome (configurable delay) ### Discovery - iTunes / Last.fm — charts, global search, 30-second previews, similar artists - Song Importer — paste a Spotify, YouTube, YouTube Music, or SoundCloud link to download ### Downloads (yt-dlp backend) - Download tracks, albums, or entire artists from YouTube Music / SoundCloud / Spotify - iTunes metadata enrichment — album art, year, track number, genre - Duplicate detection, standard folder/filename structure ### Metadata Editor - Edit tags (title, artist, album, genre, year, track, disc, cover art) - File renamed and moved automatically; Navidrome rescan triggered on save ### Playlists - Create, edit, delete playlists; add / remove tracks ### Audiobookshelf Integration - Browse libraries and audiobooks on both Web and Android - Stream audiobooks with chapter-by-chapter navigation - Playback speed control (0.5× – 2.0×) - Sleep timer - Progress sync — current position saved back to ABS server every 30 s - Chapter track mode — progress bar shows current chapter length instead of full book (toggle in Settings) - Tab/section hidden until ABS is configured in Settings ### App — Web PWA - Installable on Android / iOS / desktop - Multiple color themes, German UI ### App — Android (native Jetpack Compose) - Media3 / ExoPlayer backend - OS notification with playback controls - Last played song shown in mini player bar on restart; tap to resume --- ## Project Structure ``` NovaSonic/ ├── src/ │ ├── player/ │ │ ├── player.js # Playback, queue, transport, media session, preload │ │ └── nowplaying.js # Now Playing overlay + synced lyrics │ ├── views/ │ │ ├── detail.js # Artist / album detail pages │ │ ├── views.js # Navigation, page loaders, search │ │ ├── events.js # Ticketmaster concert events │ │ ├── home.js # Home page │ │ ├── search.js # Search, artist/album navigation │ │ ├── playlists.js # Playlist management │ │ ├── spotify.js # Spotify importer │ │ ├── youtube.js # YouTube importer │ │ ├── soundcloud.js # SoundCloud importer │ │ ├── navigation.js # Client-side router │ │ └── abs.js # Audiobookshelf integration │ ├── ui/ │ │ ├── ui.js # Modals, sidebar, context menu │ │ ├── components.js # Song rows, cards, shared UI │ │ ├── downloads.js # Download modal + job tracker │ │ ├── metadata.js # Metadata editor │ │ ├── modals.js # Song info, events modals │ │ ├── notify.js # Toast notifications │ │ └── settings.js # Settings page │ ├── state.js # Shared mutable state (S object) │ ├── utils.js # Helpers (md5, esc, formatDur, toast) │ ├── api.js # Subsonic API, iTunes API, cover art │ └── main.js # App init, audio wiring, window globals ├── backend/ │ ├── novasonic-server.js # Express API: downloads, metadata, events proxy, ABS proxy │ └── lib/ │ ├── config.js # Env var → config object │ ├── jobs.js # In-memory download job store │ ├── metadata.js # iTunes lookup, artwork, filename helpers │ ├── ytdlp.js # yt-dlp download + ffmpeg metadata embedding │ ├── squid.js # Qobuz (squid.wtf) download backend │ ├── downloader.js # Unified downloadTrack / downloadAlbumTracks │ └── romaji.js # Japanese → romaji search index ├── android/ # Native Android app (Jetpack Compose + Media3) │ └── app/src/main/kotlin/com/novasonic/app/ │ ├── api/ # SubsonicClient, AbsClient, LrcLibClient, ItunesClient │ ├── data/ # PrefsStore (DataStore), MusicRepository │ ├── player/ # PlayerController, MusicService (Media3) │ ├── ui/ │ │ ├── screen/ # HomeScreen, NowPlayingScreen, AbsScreen, SettingsScreen, … │ │ ├── component/ # MiniPlayer, SongRow, SongContextSheet, … │ │ └── theme/ # Color, Type, Theme │ └── viewmodel/ # ViewModel per screen ├── public/ │ ├── sw.js # Service worker │ ├── manifest.json # PWA manifest │ └── config.js # Runtime config (generated by Docker entrypoint) ├── index.html ├── styles.css ├── vite.config.js ├── Dockerfile ├── docker-compose.yml ├── nginx.conf └── docker-entrypoint.sh # Injects NAVIDROME_URL / BACKEND_URL at runtime ``` --- ## Setup ### 1. Clone and configure ```bash git clone cd NovaSonic cp .env.example .env ``` Edit `.env`: ```env NAVIDROME_URL=https://music.yourdomain.com BACKEND_URL=https://novasonic-api.yourdomain.com MUSIC_PATH=/srv/navidrome/music AUDIO_FMT=mp3 # mp3 | flac | opus | best AUDIO_QUALITY=0 # 0 = best (yt-dlp) MAX_PARALLEL=2 DOWNLOAD_BACKEND=auto # ytdlp | squid | auto SQUID_QUALITY=mp3 # mp3 | flac | hi-res TICKETMASTER_API_KEY= # optional — free key from developer.ticketmaster.com ``` ### 2. Start ```bash docker compose up -d ``` Frontend on port 8080, backend on port 3700. Route both through your reverse proxy. ### 3. First launch 1. Enter your Navidrome server URL, username, and password 2. Optionally add a Last.fm API key in Settings for bios and recommendations 3. Optionally connect Audiobookshelf in Settings 4. Optionally add the NovaSonic backend URL in Settings to enable downloads --- ## Environment Variables ### Frontend container | Variable | Description | |---|---| | `NAVIDROME_URL` | Pre-fills the Navidrome server URL on the login screen | | `BACKEND_URL` | Pre-fills the download backend URL on the login screen | ### Backend container | Variable | Default | Description | |---|---|---| | `PORT` | `3700` | Port the backend listens on | | `MUSIC_PATH` | `/music` | Absolute path to your Navidrome music folder | | `AUDIO_FMT` | `mp3` | Download format (`mp3`, `flac`, `opus`, `best`) | | `AUDIO_QUALITY` | `0` | yt-dlp audio quality (0 = best) | | `MAX_PARALLEL` | `2` | Max concurrent download jobs | | `DOWNLOAD_BACKEND` | `auto` | `ytdlp` / `squid` / `auto` | | `SQUID_QUALITY` | `mp3` | Qobuz quality: `mp3`, `flac`, `hi-res` | | `TICKETMASTER_API_KEY` | — | Free key from [developer.ticketmaster.com](https://developer.ticketmaster.com) | --- ## Development ```bash npm install npm run dev # Vite dev server with HMR npm run build # Production build → dist/ npm run preview # Preview the production build locally ```