HoneyandClaude Sonnet 4.6 f97e31178c fix: revert MusicService debounce — it was causing the desync, not fixing it
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>
2026-05-20 23:51:15 +02:00

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

  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
S
Description
No description provided
Readme
5 MiB
0 Stars 0 Watchers 0 Forks
2026-09-02 09:26:49 +02:00
Languages
Kotlin 47.4%
JavaScript 40.1%
CSS 5.5%
HTML 5.4%
Python 0.8%
Other 0.8%