8f7f1e263527456f38920d43e7c5f4670cb79fde
Hero now only waits for getArtist (fast local Navidrome) before revealing. getArtistImage (Last.fm lookup) was the 2-4s bottleneck when uncached — it now resolves async and patches in the artist photo after the hero is already visible. Album card, album detail header, and home hero now display the raw album name from Navidrome instead of converting to romaji when showRomaji is on. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 intoAlbumArtist/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
git clone <repo>
cd NovaSonic
cp .env.example .env
Edit .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
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
- Open the app and enter your Navidrome server URL, username and password
- Optionally add a Last.fm API key in Settings for recommendations and artist bios
- 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 — enables concert events on artist pages |
Development
npm install
npm run dev # Vite dev server with HMR
npm run build # Production build → dist/
npm run preview # Preview the production build locally
Releases
1
NovaSonic 1.26
Latest
Languages
Kotlin
47.4%
JavaScript
40.1%
CSS
5.5%
HTML
5.4%
Python
0.8%
Other
0.8%