marcandClaude Opus 5 624832959d fix(android): Now-Playing an Fold und Flip anpassen — Deckel, Hoehenmass, Rollbereich
Die Seite hatte keine Breitengrenze: das Cover war fillMaxWidth() mal aspectRatio(1f).
Aufgeklappt sind das ueber 800 dp, womit Titel, Fortschritt und Knoepfe aus dem Bild
geschoben wurden. Das Cover nimmt jetzt das Kleinste aus Breite, Hoehe abzueglich
Restbedarf und 400 dp; Titelblock/Fortschritt/Knoepfe sind auf 480 dp gedeckelt (wie
.np-inner im Web), Liedtext und Warteschlange auf 560 dp.

Auf kurzen Anzeigen (Flip-Frontdisplay, Querformat, kleine Handys) wurde die Knopfreihe
abgeschnitten. Jetzt schrumpfen dort Abstaende, Reiter und Abspielknopf, und die
Player-Seite laesst sich rollen, wenn es weiterhin nicht reicht: der Inhalt ist
mindestens so hoch wie die Seite, passt er, wird zentriert, passt er nicht, waechst er
darueber hinaus. Die Wischgeste zum Schliessen bleibt, weil ein Rollbereich ohne Weg die
Geste nicht verbraucht.

Entschieden wird nach Maszen, nicht nach Gerätenamen: schmal < 400 dp, kurz < 560 dp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 18:47:03 +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.3 MiB
0 Stars 0 Watchers 0 Forks
2026-09-02 09:26:49 +02:00
Languages
Kotlin 47.4%
JavaScript 40%
CSS 5.6%
HTML 5.4%
Python 0.8%
Other 0.8%