3c5048f017814077838cf62ecd0261daf90ca8b6
iTunes search endpoint always returns cleaned versions. Added itunesLookupArtistSongs() using /lookup?explicit=Yes which returns the actual explicit tracks. Used in artist profile (global search) and artist detail page (library). Album track lookup also gets explicit=Yes. 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
- Stream music from your Navidrome / Subsonic library
- Browse artists, albums, playlists, starred tracks and random picks
- Queue management with shuffle, repeat (all / once) and radio mode (endless random)
- Now Playing overlay with swipe-to-dismiss and lock screen media controls (Android/iOS)
- iTunes / Last.fm integration — charts, global search, 30s previews, recommendations
- Download tracks from YouTube/SoundCloud via yt-dlp directly into your music library
- Duplicate detection — skips tracks that already exist on the server
- Edit playlists (name, description, cover art, public toggle)
- PWA — installable, offline-capable via service worker
- Dark theme, mobile-first responsive layout
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
│ ├── pwa.js # Service worker registration + update banner
│ └── main.js # App init, audio wiring, window globals
├── 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)
├── backend/
│ └── novasonic-server.js # Node.js download backend (yt-dlp + ffmpeg)
├── 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
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%