# 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