marcandClaude Opus 5 6d8a8a1d5e fix(backend): Neuauflage-Jahreszahl nicht als Pflicht-Keyword fordern
Folgekorrektur zu 02b5905: "… - 2020 Remaster" steht auf der Veroeffentlichung,
aber nie im YouTube-Titel — als Pflicht-Keyword haette die Zahl den richtigen
Upload abgelehnt. Jahreszahlen neben einem Qualifier (remaster/version/edition/
anniversary/mono/stereo) fallen daher wieder raus; ein Titel, der selbst eine
Jahreszahl ist ("1999", "Berlin 2020"), behaelt sie. Gegen 29 Beispieltitel
gemessen verhalten sich 24 exakt wie vorher.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 22:20:44 +02:00

NovaSonic

A self-hosted music streaming PWA and Android app for Navidrome / Subsonic-compatible servers.
Built with Vite + vanilla ES modules (web) and Jetpack Compose + Media3/ExoPlayer (Android).


Features

  • Library — Stream from Navidrome / Subsonic; browse artists, albums, playlists, genres, starred, and random; search; star + context menu on every row.
  • Playback — Queue with shuffle, repeat, and radio mode (endless similar songs); gapless-style preload; lock-screen / notification controls; Navidrome scrobbling.
  • Lyrics — Synced and plain lyrics via lrclib.net, auto-fetched and cached per session.
  • Artist pages — Hero card, bio / similar artists / top songs, Ticketmaster events, and a Deezer cross-reference showing owned vs. missing tracks. Same-name artists can be pinned (Deezer search or a pasted Deezer link/id) to fix the wrong-discography and wrong-photo problem; a pinned artist's similar list comes from Deezer.
  • Playlists — Create, rename, delete, add/remove tracks, and upload a cover (Web + Android).
  • Importer & downloads — Paste a Spotify / YouTube / YouTube Music / SoundCloud link to download via yt-dlp, with Deezer metadata enrichment and duplicate detection.
  • Metadata editor (web) — Edit tags + cover art; files are renamed/moved and Navidrome is rescanned on save.
  • Offline (Android) — Download albums, playlists, or single songs to private storage; an Offline tab to play and manage them (works without a server connection); downloaded tracks play from disk on every screen.
  • Listening stats — A separate, self-hosted scrobble service records timestamped plays (multi-user; Navidrome only keeps a play count). A statistics view (Web + Android, from Settings) shows totals, top artists/tracks/albums with cover art, a day/month/year play series, and a 24h listening clock, plus a Musik-Match that compares two users' taste. Runs in addition to Navidrome/Last.fm scrobbling.
  • Audiobookshelf — Browse and stream audiobooks with chapter navigation, speed control, sleep timer, and progress sync.

Web is an installable PWA (themes, German UI); Android is native Jetpack Compose + Media3/ExoPlayer with a mini player, media/ABS notifications, and Android Auto. See CHANGELOG.md for the full web/Android parity matrix.


Project Structure

NovaSonic/
├── src/
│   ├── player/
│   │   ├── player.js          # Playback, queue, transport, media session, preload
│   │   ├── nowplaying.js      # Now Playing overlay + synced lyrics
│   │   └── scrobbleLog.js     # Play-log capture + offline queue → scrobble service
│   ├── views/
│   │   ├── detail.js          # Artist / album detail pages
│   │   ├── views.js           # Navigation, page loaders, search
│   │   ├── events.js          # Ticketmaster concert events
│   │   ├── home.js            # Home page
│   │   ├── search.js          # Search, artist/album navigation
│   │   ├── playlists.js       # Playlist management
│   │   ├── spotify.js         # Spotify importer
│   │   ├── youtube.js         # YouTube importer
│   │   ├── soundcloud.js      # SoundCloud importer
│   │   ├── navigation.js      # Client-side router
│   │   └── abs.js             # Audiobookshelf integration
│   ├── ui/
│   │   ├── ui.js              # Modals, sidebar, context menu
│   │   ├── components.js      # Song rows, cards, shared UI
│   │   ├── downloads.js       # Download modal + job tracker
│   │   ├── metadata.js        # Metadata editor
│   │   ├── modals.js          # Song info, events modals
│   │   ├── notify.js          # Toast notifications
│   │   ├── stats.js           # Listening statistics view (overlay)
│   │   └── settings.js        # Settings page
│   ├── state.js               # Shared mutable state (S object)
│   ├── utils.js               # Helpers (md5, esc, formatDur, toast)
│   ├── api.js                 # Subsonic API, Deezer API, cover art
│   └── main.js                # App init, audio wiring, window globals
├── backend/
│   ├── novasonic-server.js    # Express API: downloads, metadata, events proxy, ABS proxy
│   └── lib/
│       ├── config.js          # Env var → config object
│       ├── jobs.js            # In-memory download job store
│       ├── deezer.js          # Deezer metadata: track/album/artist lookups
│       ├── metadata.js        # Artwork, filename helpers, duplicate detection
│       ├── ytdlp.js           # yt-dlp download + ffmpeg metadata embedding
│       ├── downloader.js      # Unified downloadTrack / downloadAlbumTracks
│       └── romaji.js          # Japanese → romaji search index
├── scrobble/                  # Isolated listening-stats service (own container + DB)
│   └── scrobble-server.js     # node:sqlite play-log: ingest / history / stats / health
├── android/                   # Native Android app (Jetpack Compose + Media3)
│   └── app/src/main/kotlin/com/novasonic/app/
│       ├── api/               # SubsonicClient, AbsClient, LrcLibClient, ItunesClient (Deezer-backed)
│       ├── data/              # PrefsStore (DataStore), MusicRepository, StarState
│       ├── offline/           # Room DB, DAO, OfflineRepository, DownloadWorker (offline downloads)
│       ├── player/            # PlayerController, MusicService (Media3)
│       ├── ui/
│       │   ├── screen/        # HomeScreen, NowPlayingScreen, AbsScreen, SettingsScreen, …
│       │   ├── component/     # MiniPlayer, SongRow, SongContextSheet, …
│       │   └── theme/         # Color, Type, Theme
│       └── viewmodel/         # ViewModel per screen
├── public/
│   ├── sw.js                  # Service worker
│   ├── manifest.json          # PWA manifest
│   └── config.js              # Runtime config (generated by Docker entrypoint)
├── index.html
├── styles.css
├── vite.config.js
├── Dockerfile
├── docker-compose.yml         # Standard: zieht die fertigen Images aus der Registry
├── docker-compose.dev.yml     # Variante: baut lokal aus den Dockerfiles
├── nginx.conf
└── docker-entrypoint.sh       # Injects NAVIDROME_URL / BACKEND_URL / SCROBBLE_URL at runtime

Setup

1. Clone and configure

git clone <repo>
cd NovaSonic
cp .env.example .env

Edit .env:

NAVIDROME_URL=https://music.yourdomain.com
BACKEND_URL=https://novasonic-api.yourdomain.com
SCROBBLE_URL=https://novasonic-scrobble.yourdomain.com   # listening-stats service

MUSIC_PATH=/srv/navidrome/music

AUDIO_FMT=mp3          # mp3 | flac | opus | best
AUDIO_QUALITY=0        # 0 = best (yt-dlp)
MAX_PARALLEL=2

TICKETMASTER_API_KEY=  # optional — free key from developer.ticketmaster.com

2. Start

docker compose pull
docker compose up -d

Zieht die drei fertigen Images aus der Registry — nichts wird auf dem Server gebaut. Dafür in der .env REGISTRY (und optional TAG) setzen; die Images heißen $REGISTRY/novasonic-frontend|-backend|-scrobble. Neue Stände lädt man von der Dev-Maschine mit npm run push-images hoch (bzw. docker build -t $REGISTRY/<name>:latest <context> && docker push … — Kontexte: . (frontend), backend/, scrobble/).

Selbst bauen statt ziehen (keine Registry nötig):

docker compose -f docker-compose.dev.yml up -d --build

Baut lokal aus den Dockerfiles; die Deps stecken in den Images, also sind Restarts sofort da und Rebuilds nach einem git pull dank Layer-Cache schnell.

Frontend on port 8080, backend on port 3700, scrobble service on port 3710. Route each through your reverse proxy. Important: the scrobble subdomain must not sit behind an SSO / forward-auth gateway — it authenticates itself via Navidrome (ping.view), and a gateway in front redirects the app's API calls to a login page.

3. First launch

  1. Enter your Navidrome server URL, username, and password
  2. Optionally add a Last.fm API key in Settings for bios and recommendations
  3. Optionally connect Audiobookshelf in Settings
  4. Optionally add the NovaSonic backend URL in Settings to enable downloads
  5. Optionally add the NovaSonic scrobble URL in Settings to record listening stats

Environment Variables

Frontend container

Variable Description
NAVIDROME_URL Pre-fills the Navidrome server URL on the login screen
BACKEND_URL Pre-fills the download backend URL on the login screen
SCROBBLE_URL Pre-fills the listening-stats (scrobble) service URL on the login screen

Backend container

Variable Default Description
PORT 3700 Port the backend listens on
MUSIC_PATH /music Absolute path to your Navidrome music folder
AUDIO_FMT mp3 Download format (mp3, flac, opus, best)
AUDIO_QUALITY 0 yt-dlp audio quality (0 = best)
MAX_PARALLEL 2 Max concurrent download jobs
TICKETMASTER_API_KEY — Free key from developer.ticketmaster.com

Scrobble container

Variable Default Description
PORT 3710 Port the scrobble service listens on
DATA_PATH /data Where the play-log SQLite DB is stored (own volume)
NAVIDROME_URL — Used to verify user credentials via ping.view (multi-user auth)

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
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%