# Changelog All notable changes to NovaSonic are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Versioning: two-part `MAJOR.MINOR` — each release bumps the minor (features and fixes together). --- ## Platform Parity Matrix | Feature | Web (PWA) | Android | |----------------------------------|:---------:|:-------:| | Music playback (Navidrome) | ✅ | ✅ | | Seek bar / scrubbing | ✅ | ✅ | | Queue management | ✅ | ✅ | | Shuffle / Repeat | ✅ | ✅ | | Radio mode (similar songs) | ✅ | ✅ | | Now Playing overlay | ✅ | ✅ | | Dominant color background | ✅ | ✅ | | Cover art scale animation | ❌ | ✅ | | Synced lyrics (LRCLIB) | ✅ | ✅ | | Lyrics autoscroll | ✅ | ✅ | | Star / favorite song | ✅ | ✅ | | Multi-artist navigation | ✅ | ✅ | | Album detail page | ✅ | ✅ | | Artist page (Navidrome) | ✅ | ✅ | | Artist page (iTunes fallback) | ✅ | ❌ | | Concert events (Ticketmaster) | ✅ | ✅ | | Genre browsing | ✅ | ✅ | | Global search | ✅ | ✅ | | Playlist browsing | ✅ | ✅ | | Playlist create/edit | ✅ | ✅ | | Playlist cover upload | ✅ | ✅ | | Spotify / YouTube importer | ✅ | ✅ | | Metadata editor (tags + cover) | ✅ | ❌ | | Song delete (from disk) | ✅ | ❌ | | Import notification | ✅ | ✅ | | Import notification → nav | ✅ | ✅ | | ABS (Audiobookshelf) playback | ✅ | ✅ | | ABS chapter navigation | ✅ | ✅ | | ABS sleep timer | ✅ | ✅ | | ABS playback speed | ✅ | ✅ | | ABS library search | ✅ | ✅ | | ABS book descriptions (HTML) | ✅ | ✅ | | Media notification | ✅ | ✅ | | ABS notification (modern style) | ❌ | ✅ | | Android Auto | — | ✅ | | Romaji / Japanese sort | ✅ | ❌ | | Settings (Navidrome, ABS, etc.) | ✅ | ✅ | --- ## [0.8] — 2026-06-19 ### Removed - **Backend** — squid.wtf (Qobuz) download backend removed; downloads now exclusively use yt-dlp. `DOWNLOAD_BACKEND` and `SQUID_QUALITY` env vars are no longer read. ### Changed - **Backend** — Download cascade "Mittelweg": when a track's duration is known but no result matches the duration window, the cascade no longer falls back to blind title-only/no-filter grabs (which pulled same-titled videos by other uploaders). It now relaxes only the duration constraint while still requiring the artist channel — and if nothing matches, the job fails cleanly with "Nicht eindeutig gefunden — bitte Direktlink im Importer nutzen" instead of silently saving the wrong song - **Backend** — Duration match margin tightened from ±15% to ±10% (min 10s) to reject near-miss false positives - **Backend** — YTM search uses the `web_music` player client so YouTube Music "Art Track" / topic uploads are reachable (the previous `android_vr` client reported many as "video not available") - **Backend** — Search query uses a space between artist and title ("MINA recognize") instead of a dash, matching how the YTM search box ranks the exact track ### Fixed - **Backend** — YTM search query: album name was appended for short-title tracks, causing duplicated words (e.g. `"MINA - recognize recognize Single"`); album is now only appended for Japanese titles, matching spotdl behaviour - **Backend** — Downloaded filename now strips the release suffix → `01 - Artist - Title.mp3` (no `- Single`/`- EP` in the filename); the album folder keeps the full album name. The embedded title tag is cleaned the same way - **Backend** — Duration metadata from the iTunes resolver now reaches the yt-dlp duration filter for all importer paths (single, album, playlist, global search), so the right-length track is preferred - **Backend** — Metadata editor / song delete: file resolution now tolerates a release-suffix mismatch between Navidrome's stored stem and the on-disk filename; the delete "file not found" error now reports the actual resolved path for diagnosis - **Backend** — Download no longer aborts the search cascade when a single result is geo-blocked or unavailable (e.g. "This video is not available") — it continues to the next candidate --- ## [0.7] — 2026-06-18 ### Added - **Android** — Playlist create: "+" FAB on the Playlists tab opens a dialog to create a new playlist - **Android** — Playlist edit dialog (pencil icon): edit name, description, and upload cover — all in one place; matches web edit modal - **Android** — Playlist delete: trash icon in the playlist hero with confirmation dialog; navigates back to library on success - **Android** — Playlist song removal: edit-mode toggle in the playlist action row reveals a "−" button per song; removes via `updatePlaylist.view?songIndexToRemove` - **Android** — Playlist cover upload: image picker in the edit dialog uploads via Navidrome REST API (`POST /api/playlist/{id}/image`) - **Android** — ABS notification: current chapter name shown as the main title; book title moves to the artist line; updates automatically on chapter change without interrupting playback - **Android** — Home "Zuletzt gehört" section: recently played albums row matching the web's `type=recent` row - **Android** — Library Albums: sort chips (Neueste / Älteste / A-Z); Neueste uses `type=newest` from Navidrome (add-date order); scrolls to top on sort change - **Android** — Artist fast-scroll index bar: draggable letter strip on the right side of the Artists list with letter bubble while dragging - **Web** — Playlist edit modal: "Cover entfernen" button removed ### Fixed - **Android** — In-app notification bell: notifications were never delivered after navigating away from the Importer screen (poll ran in `viewModelScope`, cancelled on navigation); polling now uses `NotificationStore.startPoll()` with an app-lifetime scope - **Android** — Home hero card: pool now matches web — union of `frequent(5) ∪ recentlyPlayed(5)` deduped, random pick; label is "MEISTGESPIELT" or "ZULETZT GEHÖRT" (was hardcoded "AKTUELL") - **Android** — Library Albums "Neueste": was incorrectly sorting by release year client-side; now uses `type=newest` from Navidrome (recently added to library) - **Android** — Artist screen crash: duplicate key in `LazyColumn` when Navidrome returns the same song ID twice in `topSongs` - **Android** — "Alle Alben anzeigen" crash: duplicate key when two albums share name + year - **Android** — "Alle Alben anzeigen": albums were left-aligned inside grid cells; now centered - **Android** — "Alle Alben anzeigen" search bar styling matches app design - **Android** — ABS notification button order: Samsung One UI mapping corrected; now shows Kapitel zurück | 30s zurück | Play/Pause | 30s vor | Kapitel vor --- ## [0.6] — 2026-06-17 ### Added - **Android** — Lyrics preload on NowPlaying open; lyrics load in background while on Player tab, so switching to Lyrics tab is instant ### Fixed - **Android** — Home hero card: play button now sits to the right of title/artist, not below - **Android** — ABS media notification now uses Media3's modern media card style (blurred cover art background) instead of a plain notification; identical look to the music notification - **Android** — ABS notification buttons corrected: now shows exactly Kapitel zurück | 30s zurück | Play/Pause | 30s vor | Kapitel vor in both compact and expanded view; previously showed double back-button and 30s-forward was cut off - **Android** — Lyrics autoscroll switches from fixed tween to spring animation (DampingRatioNoBouncy, StiffnessLow); rapid line changes no longer produce choppy stop-and-restart scrolling ### Internal - **Android** — ABS notification rebuilt using a custom `DefaultMediaNotificationProvider` subclass (`AbsAwareNotificationProvider`) registered via `setMediaNotificationProvider()`; overrides `getMediaButtons()` to return exactly 5 session-command buttons when ABS is active, bypassing `setCustomLayout()` which only appends to (not replaces) the standard skip-prev/play-pause/skip-next buttons - **Android** — Removed abandoned `AbsPlayerService.kt` (unreferenced, unregistered, referenced missing drawable) --- ## [0.5] — 2026-06 ### Added - **Android** — ABS full playback: chapter list, playback speed control, sleep timer, progress sync to ABS server - **Android** — ABS 30s skip-back / skip-forward buttons in media notification (compact + expanded view) - **Android** — ABS library search bar in Books tab (server-side search via `/api/libraries/{id}/search`, 400ms debounce) - **Android** — Notification sheet: shows import/download history with status icons; "Zum Album →" / "Öffnen →" button navigates to album or playlist on tap - **Web** — Notification sheet: "Zum Album →" link button after download completes - **Android** — NowPlaying lyrics: 200ms lookahead for better sync feel (matches web) ### Fixed - **Web + Android** — ABS book descriptions: HTML tags (`

`, `

`) now stripped before display - **Android** — NowPlaying background: switched from `darkMutedSwatch` to `vibrantSwatch` (55% darken) for more visible color tinting - **Android** — NowPlaying top bar: fixed layout overflow with navigation labels - **Android** — Lyrics scroll: removed Phase 2 "drift" animation; now simply centers active line (matches web `scrollTo({ behavior: 'smooth' })`) - **Android** — ABS skip: library list skip fixed on first load --- ## [0.4] — 2026-05 ### Added - **Android** — Full native app rewrite in Kotlin / Jetpack Compose (replaces React Native / Expo attempt) - **Android** — Media3 / ExoPlayer music playback with `MusicService` (`MediaLibraryService`) - **Android** — NowPlaying screen: cover art with scale animation (Apple Music style), seek bar with drag scrubbing, queue tab, lyrics tab - **Android** — Dominant color background in NowPlaying (Palette API, animated on song change) - **Android** — Multi-artist display with per-artist tap-to-navigate in NowPlaying - **Android** — Synced lyrics via LRCLIB (parallel requests, in-memory cache) - **Android** — ABS (Audiobookshelf) integration: login, library browse, book detail, chapter playback - **Android** — Android Auto support: browsable tree (Recent, Favorites, Playlists), play-all / shuffle pseudo-items - **Android** — Radio mode: auto-queues similar songs via Navidrome `getSimilarSongs` - **Android** — Global search (Navidrome) - **Android** — Genre browsing with play button per genre - **Android** — Concert events via Ticketmaster on artist page - **Android** — Spotify / YouTube importer (connects to backend) - **Android** — Notification sheet in-app (import/download job progress) - **Android** — Settings: Navidrome URL/credentials, ABS URL/token, theme, display name ### Fixed - **Android** — Login flash on startup (credentials loaded before nav resolves) - **Android** — Album name casing normalization --- ## [0.3] — 2025-Q4 ### Added - **Web** — Artist page: hero banner, iTunes + Navidrome album categorization, all-songs view - **Web** — Concert events via Ticketmaster (replaces Bandsintown); 30min server-side cache - **Web** — Playlist editing: name, description, public toggle, cover image upload - **Web** — Global search: artist cards with images, explicit badge (E) - **Web** — Romaji display for Japanese artists/albums (Navidrome `sortName` tag) - **Web** — iTunes album detail page (for tracks not in Navidrome) - **Web** — Mobile search bar in header (tap icon) - **Web** — Smooth progress bar scrubbing with touch drag ### Fixed - **Web** — Multi-artist splitting: handles `·`, `/`, `;` separators; each name navigates to own artist profile - **Web** — Lyrics: LRCLIB fuzzy fallback, title-only search fallback, parallel requests, in-memory cache - **Web** — Lyrics scrollbar hidden, fade edges on active line - **Web** — Artwork: hi-res cover embedded; consistent `album_artist` tag for Navidrome grouping - **Web** — `feat.`/`ft.`/`featuring` converted to `/` in artist tag for correct Navidrome multi-artist split --- ## [0.2] — 2025-Q3 ### Added - **Web** — Spotify / YouTube / SoundCloud importer: resolve link → yt-dlp download → optional Navidrome playlist creation - **Web** — iTunes per-track artwork lookup for playlist downloads - **Web** — Synced lyrics via LRCLIB in Now Playing overlay (200ms lookahead) - **Web** — Lyrics embedding in downloaded track metadata - **Web** — Explicit metadata tag + E badge in search - **Web** — Disc number and compilation metadata tags - **Web** — Genre metadata from iTunes `primaryGenreName` - **Web** — SW update banner (notify user, no force-reload) - **Web** — Duplicate detection before download (shows toast on skip) - **Web** — Player drag feedback; swipe-up player bar opens Now Playing ### Fixed - **Web** — Spotify importer: multi-artist parsing, GraphQL embed format, locale URL support, public web-player token - **Web** — Playlist cover auto-refresh after upload - **Web** — Service worker: network-first strategy, does not intercept cross-origin requests, auto-reloads all PWA windows on SW update - **Web** — Bottom navigation: full-width edge-to-edge layout --- ## [0.1] — 2025-Q2 ### Added - **Web** — Initial PWA music player (Vite + ES modules) - **Web** — Navidrome Subsonic API integration (albums, songs, playlists, search, cover art) - **Web** — Album and playlist browsing - **Web** — Basic player (play/pause, skip, shuffle, repeat) - **Web** — Media Session API (lock screen / notification controls) - **Web** — Service worker with offline support - **Web** — Docker setup: nginx frontend + Node.js backend + Navidrome - **Web** — Home screen: hero card (last played album), genre grid, recently added, most played, discover --- ## Planned / Backlog ### Features - **Android** — Playlist drag-and-drop / sort order - **Android** — Artist page iTunes fallback (for artists not in Navidrome) - **Android** — Romaji / Japanese sort support - **Web + Android** — Backend settings UI rework (move, reorganize) - **Web + Android** — yt-dlp backend update / maintenance ### Known Bugs - **Backend (download)** — Duplicate detection is filename-only and exact: a track re-downloads when the cleaned filename changes (e.g. `… - recognize - Single.mp3` vs `… - recognize.mp3`), leaving two copies. Should match on normalized artist+title (ignore release suffix / variant / track prefix), not the raw filename. - **Backend (download)** — No size/length safety cap: a wrong long video can be saved as a huge file (observed a 52 MB mp3 ≈ 28 min vs the correct 4 MB ~3 min). Needs a `--max-filesize` / duration cap as defense-in-depth. - **Backend (metadata/sorting)** — iTunes `collectionArtistName` (album artist) is not captured, so `album_artist` can be wrong on compilations / various-artists releases, splitting albums and mis-sorting them in Navidrome. - **Metadata editor (autofill)** — The iTunes / MusicBrainz autofill buttons don't populate the Album-Artist field (and iTunes autofill fills no artist at all). Multi-artist lookup is also inconsistent: iTunes splits the artist on `; , &`, MusicBrainz on `, ; & •`, neither handles `/`, `feat.`, `ft.`. Both should use one robust "first artist only" splitter for the search. - **Backend (download)** — Re-downloading a playlist re-fetches some already-present tracks: the duplicate check matches the exact computed path only (same album folder, exact track-number prefix, exact resolved artist/title). Any drift between runs — different iTunes/romaji name, a different track number, a different album folder, or a `- Piano Version`/`(var)` variant suffix — makes the file look new. The check is also case-sensitive (filename-existence on a case-sensitive Linux FS), so `DJ OKAWARI` vs `DJ Okawari` misses too. Should match by normalized, case-insensitive first-artist + title across the artist's folders, not the exact path. - **Backend (download)** — The no-duration branch (tracks where iTunes returns no duration) has **no Mittelweg protection**: it still falls through to title-only / no-filter grabs and pulls wrong covers by other uploaders (observed: `Yorushika – 車窓` saved as `Piano Echoes – Shasou`). The channel-required relaxation should apply to the no-duration branch too.