174 lines
7.7 KiB
Markdown
174 lines
7.7 KiB
Markdown
# NovaSonic
|
||
|
||
A self-hosted music streaming PWA for Navidrome / Subsonic-compatible servers.
|
||
Built with Vite + vanilla ES modules, served via nginx in Docker.
|
||
|
||
---
|
||
|
||
## Features
|
||
|
||
### Home
|
||
- Hero card — large banner featuring a random pick from your most-played and recently played albums, labelled "Meistgespielt" or "Zuletzt gehört" accordingly
|
||
- Genre browser — top 15 genres (by song count) as a colored card grid; click to browse songs, hover-play to instantly shuffle the genre
|
||
|
||
### 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)
|
||
- Play count display — optional toggle in Settings; always shown on artist pages
|
||
- Star and context-menu on every song row (artist top songs, all-songs view, playlists, albums)
|
||
|
||
### Playback
|
||
- Queue management with shuffle, repeat (all / once), 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; queue tab stays active when selecting a song from the queue
|
||
- Lock screen media controls (Android / iOS via Media Session API)
|
||
- Gapless-style queue with prev/next, skip forward/back
|
||
- Scrobbling — plays are reported to Navidrome after 15 seconds of continuous playback
|
||
|
||
### Discovery
|
||
- iTunes / Last.fm integration — charts, global search, 30-second previews, similar artists, recommendations
|
||
- Song Importer — paste a link from Spotify, YouTube, YouTube Music, or SoundCloud to preview and download tracks, enriched with iTunes metadata and cover art
|
||
|
||
### Downloads (yt-dlp backend)
|
||
- Download individual tracks, full albums, or entire artists
|
||
- Sources: YouTube Music (primary), YouTube, SoundCloud, Spotify (via playlist importer)
|
||
- iTunes enrichment — album, year, track number, genre, 1000×1000 cover art
|
||
- Duplicate detection — skips tracks already present on the server
|
||
- Standard filename convention: `{01 - }Artist1, Artist2 - Title.ext`
|
||
- Files organised into `MusicRoot/AlbumArtist/Album/` folders automatically
|
||
|
||
### Metadata Editor
|
||
- Edit title, artist, album artist, album, genre, year, track number, disc number from within the app
|
||
- Update embedded cover art via URL or file upload
|
||
- Artist separator normalization — accepts `•`, `;`, `,` as input; stores as `;` (Navidrome/MusicBrainz style)
|
||
- File renamed automatically on save to match the standard naming convention
|
||
- File moved to the correct `AlbumArtist/Album/` folder when those fields change
|
||
- Empty folders cleaned up after a move
|
||
- Navidrome rescan triggered automatically after every save
|
||
|
||
### 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 Media3/ExoPlayer backend)
|
||
- Robust Android queue sync — song identity verified via media ID on every resume to prevent stale-state mismatches
|
||
- 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, metadata editor
|
||
│ ├── nowplaying.js # Now Playing overlay
|
||
│ ├── abs.js # Audiobookshelf integration
|
||
│ ├── spotify.js # Spotify importer (resolve + download)
|
||
│ ├── youtube.js # YouTube / YouTube Music importer
|
||
│ ├── soundcloud.js # SoundCloud importer
|
||
│ ├── components.js # Shared UI components (song rows, cards)
|
||
│ ├── themes.js # Theme switching
|
||
│ ├── pwa.js # Service worker registration + update banner
|
||
│ └── main.js # App init, audio wiring, window globals
|
||
├── backend/
|
||
│ ├── novasonic-server.js # Express API: download jobs, metadata edit, resolvers
|
||
│ ├── spotify_resolve.py # Spotify page scraper (track/album/playlist metadata)
|
||
│ └── lib/
|
||
│ ├── config.js # Backend config (env vars, paths, format)
|
||
│ ├── jobs.js # In-memory job store + log helper
|
||
│ ├── metadata.js # iTunes lookup, artwork download, lyrics fetch, filename helpers
|
||
│ ├── ytdlp.js # yt-dlp download cascade + ffmpeg metadata embedding
|
||
│ ├── squid.js # Squid/Qobuz download backend
|
||
│ ├── downloader.js # Unified downloadTrack / downloadAlbumTracks entry point
|
||
│ └── romaji.js # Japanese → romaji index for search
|
||
├── 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
|
||
|
||
```bash
|
||
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/`.
|
||
|
||
```bash
|
||
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
|
||
|
||
```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
|
||
```
|
||
|
||
---
|
||
|
||
## First Launch
|
||
|
||
1. Open the app in a browser
|
||
2. Enter your Navidrome server URL, username and password
|
||
3. Optionally add a Last.fm API key (Settings) for recommendations and similar artists
|
||
4. Optionally add the backend URL (Settings) to enable yt-dlp downloads
|
||
5. Optionally connect Audiobookshelf (Settings) to stream audiobooks
|