f97e31178cb9c6e569e2b99efb9e1f01fb7a6f9a
The 120ms debounce on JS-initiated transitions (seek/setQueue) delayed updateStateSnapshot(), leaving a stale window where getState() returned the old index. If an AUTO callback fired during that window and the JS onTrackChanged handler called getState(), it would read the stale index and overwrite the correct player bar state with the wrong song. Fix: always fire onTrackChanged immediately so updateStateSnapshot() runs synchronously. The JS-side getState() reconciliation (from the previous commit) then reliably gets the settled native index every time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NovaSonic
A self-hosted music streaming PWA 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 (albums, favorites) with chip filters
- Search/filter bar inside playlist detail and favorites (instant, client-side)
- Synced and plain lyrics via LRCLIB (auto-fetched per track)
Playback
- Queue management with shuffle, repeat (all / once), and radio mode (endless random)
- Now Playing overlay with swipe-to-dismiss
- Lock screen media controls (Android / iOS via Media Session API)
- Gapless-style queue with prev/next, skip forward/back
Discovery
- iTunes / Last.fm integration — charts, global search, 30-second previews, similar artists, recommendations
- Spotify playlist importer — paste a Spotify playlist URL to preview and download all tracks, enriched with iTunes metadata and cover art
Downloads (yt-dlp backend)
- Download individual tracks, full albums, or entire artists from YouTube / SoundCloud
- Spotify playlist importer downloads with iTunes-enriched metadata (album, year, track number, genre, cover art)
- Duplicate detection — skips tracks already present on the server
- Lyrics embedded into downloaded files (LRCLIB)
Playlists
- Create, edit, and delete playlists
- Edit playlist name, description, cover art, and public/private toggle
- Add / remove tracks; reorder via drag and drop
Audiobookshelf Integration
- Connect to your Audiobookshelf server (URL + API key)
- Browse and stream audiobooks from your ABS library
- Full player: chapters list, playback speed, sleep timer (preset or custom minutes), progress sync
App
- PWA — installable on Android / iOS / desktop, offline-capable via service worker
- Android APK (WebView wrapper with native feel)
- Multiple color themes
- Dark theme, mobile-first responsive layout
- German UI
Project Structure
Music_App/
├── src/ # ES modules (Vite entry)
│ ├── state.js # Shared mutable state (S object)
│ ├── utils.js # md5, esc, formatDur, toast
│ ├── api.js # Subsonic API, Navidrome REST, iTunes API
│ ├── player.js # Playback, transport, queue, media session, preview
│ ├── views.js # Navigation, page loaders, card makers, search
│ ├── ui.js # Modals, overlays, sidebar, settings, context menu
│ ├── nowplaying.js # Now Playing overlay
│ ├── abs.js # Audiobookshelf integration
│ ├── spotify.js # Spotify playlist importer
│ ├── components.js # Shared UI components
│ ├── themes.js # Theme switching
│ ├── pwa.js # Service worker registration + update banner
│ └── main.js # App init, audio wiring, window globals
├── backend/
│ ├── novasonic-server.js # Node.js download backend (yt-dlp + ffmpeg)
│ └── lib/
│ └── metadata.js # iTunes lookup, lyrics fetch, artwork download
├── public/ # Static assets (copied as-is to dist/)
│ ├── sw.js # Service worker
│ ├── manifest.json # PWA manifest
│ ├── icon.svg # App icon
│ └── config.js # Runtime config placeholder (generated by Docker)
├── android/ # Android WebView APK wrapper
├── index.html # App shell + Vite entry point
├── styles.css # All styles
├── vite.config.js
├── Dockerfile # Multi-stage: Node build → nginx serve
├── docker-compose.yml
├── nginx.conf
└── docker-entrypoint.sh # Injects NS_SERVER_URL / NS_BACKEND_URL at startup
Docker Setup
Frontend only
docker compose up -d
Environment variables (set in docker-compose.yml or via -e):
| Variable | Description |
|---|---|
NS_SERVER_URL |
Pre-fills the Navidrome server URL on the login screen |
NS_BACKEND_URL |
Pre-fills the download backend URL |
With download backend
The backend requires yt-dlp and ffmpeg. A separate docker-compose.yml lives in backend/.
cd backend
docker compose up -d
Environment variables for the backend:
| Variable | Default | Description |
|---|---|---|
PORT |
3700 |
Port the backend listens on |
MUSIC_PATH |
/music |
Path to your Navidrome music folder (mount as volume) |
AUDIO_FMT |
mp3 |
Output format (mp3, flac, opus, …) |
AUDIO_QUALITY |
0 |
yt-dlp audio quality (0 = best) |
MAX_PARALLEL |
2 |
Maximum concurrent downloads |
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
First Launch
- Open the app in a browser
- Enter your Navidrome server URL, username and password
- Optionally add a Last.fm API key (Settings) for recommendations and similar artists
- Optionally add the backend URL (Settings) to enable yt-dlp downloads
- Optionally connect Audiobookshelf (Settings) to stream audiobooks
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%