# NovaSonic A self-hosted music streaming PWA and Android app for Navidrome / Subsonic-compatible servers. Built with Vite + vanilla ES modules, served via nginx in Docker. --- ## Features ### Music Library - Stream music from your Navidrome / Subsonic library - Browse artists, albums, playlists, starred tracks, and random picks - Sort and filter views with chip filters - Search / filter bar inside playlist detail and favorites (instant, client-side) - Synced and plain lyrics via LRCLIB (auto-fetched per track) - Play count display — optional toggle in Settings - Star and context menu on every song row ### Artist Pages - Full-bleed hero card — artist photo as portrait + blurred background (desktop); full-bleed image with name overlay (mobile) - Bio, similar artists, top songs pulled from Last.fm via Navidrome - Concert events via Ticketmaster (upcoming shows with venue, date, ticket link) - Albums cross-referenced against iTunes catalog — shows what's downloaded, partially downloaded, or missing - Quick actions: play top songs, shuffle all, open concerts ### Playback - Queue management with shuffle, repeat (all / one), and radio mode (endless random) - Spotify-style shuffle — pre-shuffles the full queue so every song plays exactly once - Now Playing overlay with swipe-to-dismiss - Gapless-style playback — next track pre-buffered 60 seconds before current track ends - Lock screen / notification media controls (Android / iOS / desktop) - Scrobbling — reported to Navidrome (configurable: 15 s / 30 s / ⅓ / ⅔ / end) ### Discovery - iTunes / Last.fm integration — charts, global search, 30-second previews, similar artists, recommendations - Song Importer — paste a Spotify, YouTube, YouTube Music, or SoundCloud link to preview and download tracks ### Downloads (yt-dlp backend) - Download individual tracks, full albums, or entire artists - Sources: YouTube Music (primary), YouTube, SoundCloud, Spotify (via importer) - iTunes enrichment — album, year, track number, genre, 1000×1000 cover art - Duplicate detection — skips tracks already present on the server - Standard filename: `01 - Artist - Title.ext`, organised into `AlbumArtist/Album/` folders ### Metadata Editor - Edit title, artist, album artist, album, genre, year, track number, disc number - Update embedded cover art via URL or file upload - File renamed and moved automatically on save - Navidrome rescan triggered after every save ### Playlists - Create, edit, and delete playlists - Edit name, description, cover art, public/private toggle - Add / remove tracks; reorder via drag and drop ### Audiobookshelf Integration - Browse and stream audiobooks from your Audiobookshelf server - Chapters list, playback speed, sleep timer, progress sync ### App - PWA — installable on Android / iOS / desktop - Android APK — native WebView wrapper with Media3 / ExoPlayer backend - Native OS notification with skip buttons - Pre-buffers last played track on cold start for instant resume - Artwork pre-warming so notification never shows blank cover - Multiple color themes, dark mode, mobile-first responsive layout - German UI --- ## Project Structure ``` NovaSonic/ ├── src/ │ ├── player/ │ │ ├── player.js # Playback, queue, transport, media session, preload │ │ └── nowplaying.js # Now Playing overlay │ ├── views/ │ │ ├── detail.js # Artist / album detail pages │ │ ├── views.js # Navigation, page loaders, search │ │ ├── events.js # Ticketmaster concert events │ │ ├── home.js # Home page, artist image cache │ │ ├── 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, settings, 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 # 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 │ └── lib/ │ ├── config.js # Env var → config object │ ├── jobs.js # In-memory download job store │ ├── metadata.js # iTunes lookup, artwork, lyrics, 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/ # Android APK (WebView + Media3/ExoPlayer) ├── 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 # absolute path to your music folder on the host 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 ``` This starts both the frontend (nginx on port 8080) and the backend (Node.js on port 3700). Route both ports through your reverse proxy. ### 3. First launch 1. Open the app and enter your Navidrome server URL, username and password 2. Optionally add a Last.fm API key in Settings for recommendations and artist bios 3. Optionally connect Audiobookshelf in Settings to stream audiobooks --- ## 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 (mounted as volume) | | `AUDIO_FMT` | `mp3` | Download output 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` (tries Qobuz first, falls back to yt-dlp) | | `SQUID_QUALITY` | `mp3` | Qobuz quality: `mp3`, `flac`, `hi-res` | | `TICKETMASTER_API_KEY` | — | Free API key from [developer.ticketmaster.com](https://developer.ticketmaster.com) — enables concert events on artist pages | --- ## 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 ```