Files
NovaSonic/CHANGELOG.md
T
HoneyandClaude Opus 4.8 e69fbaf75e fix(android): back focus lost after tabbing out — re-assert overlay back callbacks on resume
With NowPlaying (or an ABS player) open, backgrounding and resuming the app made the next back
press pop the NavHost invisibly to Home before dismissing the overlay. Returning from the
background re-registers the NavController's own OnBackPressedCallback on top of the dispatcher,
stealing back-priority from the overlays.

AppNavigation now bumps a resumeTick on Lifecycle.Event.ON_RESUME and keys every overlay
DisposableEffect (NowPlaying, ABS player, ABS item, Erkunden overlay) on resumeTick +
currentBackEntry, so each overlay re-adds its callback above the NavController after a resume or
navigation. Needs on-device verification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 10:00:04 +02:00

44 KiB
Raw Blame History

Changelog

All notable changes to NovaSonic are documented here. Format follows Keep a Changelog. 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 (Deezer fallback) ✅ ✅
Global search source Deezer Deezer
Same-name artist disambiguation ✅ ✅
Artist fan count shown ✅ ✅
30s preview control bar ✅ ✅
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 — ✅
Offline download (albums/playlists) ❌ ✅
Offline download (single songs) ❌ ✅
Offline playback ❌ ✅
Offline detail / manage downloads ❌ ✅
Player bar on all screens ✅ ✅
Romaji / Japanese sort ✅ ❌
Settings (Navidrome, ABS, etc.) ✅ ✅

[1.3] — 2026-07-31

Big change: the entire metadata/search stack moved from Apple's iTunes to Deezer — on web, backend downloads and Android. Apple's iTunes /search returns 403 from server/datacenter IPs, which had broken global search, charts, artist/album/single downloads and the metadata-editor autofill in production. Deezer is free, needs no key, allows more requests, and returns explicit versions + real artist images + track durations. Web and Android now share the same source, so an artist resolves to one consistent name everywhere.

Changed

  • Web + Android + Backend — Global search, album/artist lookups, artist top tracks and the "Neu & Beliebt" charts all run on Deezer (api.deezer.com). Results are mapped to the existing item shape, so the UI is unchanged; artist cards now show real photos (iTunes artist entities had none)
  • Backend (download) — Single-track, album and artist downloads, plus the metadata-editor autofill, now enrich from Deezer:
    • backend/lib/deezer.js — new module: lookupTrackMeta, fetchAlbumTracks, searchAlbumTracks, fetchArtistAlbums
    • Album/artist album_artist still comes from the album-level artist (Deezer album.artist), keeping soundtracks from splitting
    • Genre comes from the Deezer album (one album-level genre); per-track artist is preserved for mixed-artist albums
    • The autofill endpoint moved /api/itunes/lookup → /api/meta/lookup
  • Web + Android — All user-visible "iTunes" / "Apple Music" labels are now "Deezer" (search badge, charts badge, preview bar, editor autofill button, artist "populär" heading)
  • Backend — Removed the dead iTunes /search proxy and the Apple charts RSS proxy; the Deezer proxy (/api/deezer/*) now also serves charts and the editor lookup
  • Web — Renamed the leftover itunes* frontend identifiers to deezer* (itunesSearch → deezerSearch, itunesLookup* → deezerLookup*, itunesData/itunesArtistId → deezerData/deezerArtistId, the itunes-album-detail route/DOM ids, the badge-itunes CSS class, …). Purely internal — no behaviour change; only the factual "Apple's iTunes /search 403" comments keep the old name

Added

  • Web + Android — Same-name artist disambiguation: Deezer's artist search isn't ranked by popularity, so a name collision (5 artists named "Joji") returned a niche one first — the artist page then loaded the wrong discography and owned songs didn't show as downloaded. Artists are now sorted by Deezer fan count, and on a local artist page (web) with a real collision the pick is tie-broken by song overlap with your library (each artist has different songs). The fan count is shown on search artist cards and on both the Deezer and local Navidrome artist pages (web + Android)
  • Android — Global 30s-preview control bar: a preview started on an album/artist/Explore screen used to keep playing with no way to stop it once you navigated away (the local artist page had its own uncontrollable preview too). All previews now run through one app-level player with a bar (above the mini player, on every screen) to pause/resume/stop it; starting real playback cancels the preview
  • Backend — Unit tests for the Deezer mapping/matching logic (backend/test/deezer.test.js, run with npm test), incl. per-track numbering, mixed-artist album-artist stability, explicit flags and the Japanese-title match guard. A live smoke test runs with RUN_LIVE=1

Fixed

  • Android — After tabbing out and back into the app with NowPlaying (or an ABS player) open, pressing back first popped the NavHost invisibly to Home and only then dismissed the overlay — the focus was lost. Returning from the background re-registers the NavController's own back callback on top of the dispatcher, stealing back-priority from the overlays. The overlay back-callbacks are now re-asserted on ON_RESUME (and on navigation), so back closes the overlay first again
  • Web + Android — A finished-download notification led to the wrong song: it resolved the album by name via search3, and when the exact artist match wasn't found (e.g. the new album isn't scanned yet), it fell back to a same-named album by a different artist (or the first result). Several artists share an album title ("I LIKE IT!"), so tapping the notification opened a completely different song. Now the artist must match when known (fuzzy for multi-artist/album-artist drift); otherwise it shows "wird evtl. gerade gescannt" instead of opening a wrong album
  • Web — Downloading a not-yet-owned album from an artist page searched YouTube for "Unknown Artist <album>" and failed. Deezer's /artist/{id}/albums omits the artist object, so the discography cards had an empty artist name → the modal fell back to the literal "Unknown Artist". Fixed by backfilling the known artist name in deezerLookupArtistAlbums and defaulting the download modal to Album for album cards (was always Track)
  • Web + Android — Owned albums were wrongly shown as "Unvollständig 1 / 0 Tracks" on the artist page. Deezer's /artist/{id}/albums list omits nb_tracks, so the total read as 0 and every matched album looked incomplete. Web now resolves the real track count from the Deezer album endpoint for matched (owned) albums only and treats an unknown total as complete; Android treats an unknown total as complete (never a false partial)

Notes

  • Deploy: git pull, then rebuild the frontend (docker compose up -d --build novasonic-frontend) and restart the backend (docker compose restart novasonic-backend). Service worker bumped to ns-v54
  • Titles a user picked stay as chosen; only derived fields (album, album-artist, genre, year, artwork) follow Deezer

[1.2] — 2026-07-25

Follow-up to 1.1: web history navigation, correct download tagging for mixed-artist albums, and UI polish.

Added

  • Android — Directional bottom-nav tab transitions: the slide follows the tapped tab's position (a tab to the right slides in from the right, one to the left from the left) instead of a fixed, sometimes-random-feeling direction. Applied across all four enter/exit/pop transitions; detail-screen push/pop is unchanged
  • Web — Pressing Enter in the importer link field starts the import (same as the Importieren button)

Fixed

  • Web — Browser / mouse Forward navigation now works (previously only Back did): popstate applies the target history entry (nsIdx) for both directions, and the back handler no longer pushState()s over the browser's forward stack. In-app back buttons route through history.back() so the browser and app stacks stay in sync
  • Web — Deleting a song or playlist refreshes the current view immediately (drops the cached list) instead of leaving the removed item visible for up to ~3 minutes
  • Web — Album detail header showed the first track's artist (e.g. "TWICE" on a soundtrack) instead of the album artist; it now reads the album's own album-artist fields from getAlbum (matches the Android app)
  • Web — Metadata autofill (iTunes / MusicBrainz) normalizes the trailing " & " in a multi-artist credit ("A, B & C" → "A; B; C"), but only when the value is already a list, so band names like "Simon & Garfunkel" stay intact
  • Android — Erkunden (Apple) overlay back no longer sometimes jumped to Home: the root-level back callback is re-asserted on every navigation, so it can't lose back-priority to the NavHost after an album drill-down or tab switch
  • Backend (download) — Mixed-artist albums search per track: the YTM/YouTube query and channel filter use each track's own performer, so soundtrack tracks by other artists (TWICE's "Strategy", MeloMance's "Love, Maybe") are found instead of failing with "Nicht eindeutig gefunden"
  • Backend (download) — album_artist is derived from iTunes' album-level collectionArtistName (identical across the album) instead of the request's artist — which could be the lead single's performer, so a whole KPop soundtrack was tagged album_artist = TWICE. Applies to album and artist downloads; each track still gets its own per-track artist tag
  • Backend (download) — Official audio is matched by yt-dlp's artist metadata, not just the channel name, so uploads on label channels (BANGTANTV, HYBE, "… - Topic") win over fan reuploads — e.g. BTS "Pied Piper" now grabs the official track. (channel_is_verified was rejected as a signal: lyric videos and fan MVs are also on verified channels)
  • Backend (download) — Western artists are no longer tagged with the Japanese store's katakana album-artist ("DJスネイク" for DJ Snake): the JP-first iTunes lookup now keeps Japanese names only for Japanese songs and uses the US/English names otherwise (affects the album_artist tag and the on-disk folder)
  • Backend (download) — Release year is back-filled from iTunes on single-track downloads where the source omitted it (the year tag was previously left empty)

Changed

  • Web + Android — Playing a song from search results queues only that song; Radio mode (if on) fills the rest — closer to Spotify. Album / playlist play still queues the whole list
  • Android — NowPlaying track info is ordered title → album → artists, matching the web player
  • Web — NowPlaying favorite star is vertically centered with the whole title / album / artist block instead of pinned to the title line

[1.1] — 2026-07-19

Polish release: Android navigation and offline resilience, download accuracy for artists with stylised names, and a fix for the lyrics outage caused by lrclib rate-limiting.

Added

  • Android — Mini player is now visible on every screen (album, artist, playlist detail), not only the top-level tabs; the tab bar stays top-level-only
  • Android — Song search results (local library) got the 3-dot context menu: play, insert next / queue, add to playlist, favorite, go to album, song info, download
  • Android — Artist images in the Apple-Music (Erkunden) search: iTunes artist entities carry no artwork at all, so each artist now borrows the cover of one of their albums; a person icon replaces the empty grey circle where none is found
  • Web + Android — Playlist cover art is shown in the "add to playlist" picker, so playlists are identifiable at a glance
  • Web — "Now playing" scrobble is sent at track start (scrobble?submission=false), so Navidrome shows the current track while it plays instead of only on completion

Fixed

  • Web + Android — Lyrics stopped loading entirely: a burst of requests (prefetch + the 4-request cascade + fast skipping) got the client IP throttled by lrclib, so every lookup hung until it timed out. Fixed with per-song in-flight de-duplication, a prefetch debounced to 1.5 s after track start, a descriptive User-Agent, and a longer 8 s timeout
  • Android — App lag and stutter while scrolling and switching tabs: coverArtUrl() and streamUrl() built a fresh Ktor HttpClient for every cover-art request and never closed it — heavy allocation and GC churn. Both now reuse the cached client
  • Android — Star / favorite gave no visual feedback: a global StarState now flips the icon optimistically the moment it's tapped (everywhere it appears) and rolls back if the API call fails
  • Android — Back from an Erkunden (Apple) artist or album jumped straight to Home. Those screens are in-screen overlays on the erkunden destination and their BackHandler lost back priority to the NavHost, which popped the whole tab. Back is handled at the app root now: album → artist, artist → search
  • Android — The Erkunden artist's "show all albums" view was lost when drilling into an album and back (it lived in local remember state); the open section now lives in the ViewModel and survives both route and overlay round-trips
  • Android — Album back-navigation from a local artist returned to the collapsed artist page instead of the "all albums" view it was opened from
  • Android — The mini player sat behind the phone's gesture/navigation bar on detail screens, where there is no tab bar to absorb the system inset
  • Android — The download sheet for new songs and the song context sheet opened only half-expanded, hiding the download button behind the system navigation bar until dragged up; both open fully now
  • Android — The Offline tab was blocked by the online library's loading spinner, so downloads were unreachable while the library loaded or the server was unreachable. It reads from the local Room DB and now loads independently
  • Backend (download) — Artist lookups ignored leading aesthetic punctuation (e.g. .diedlonely), so the iTunes match missed, fell through to a title-only match, and grabbed an unrelated same-titled track — whose wrong duration then also misled the YouTube search filter. Leading non-alphanumeric characters are trimmed before lookup
  • Backend (metadata) — The metadata editor showed "[Unknown Album]" where the library shows "Singles": tracks downloaded without an album had no album tag written at all. The album tag now always gets a value (defaulting to Singles), and file resolution falls back to findExistingTrack for stale or rebased paths
  • Web — Now Playing showed a stale cover after rapid skips
  • Web — Home row scroll arrows caused layout thrash while scrolling: arrow visibility is a rAF-throttled class toggle now, with getBoundingClientRect() only on resize / content change

Changed

  • Android — Favorite is a heart everywhere; the Now Playing screen still used a star
  • Android — Playlist detail actions are uniform, equal-width and icon-only (play / shuffle / edit / download). Since the offline-download button was added, four buttons shared one row and the Play/Shuffle labels were being clipped away
  • Android — The "add to playlist" search field matches the styling of the other search bars
  • Web — Track switching uses a gapless hand-off between two <audio> elements, making skips effectively instant (it does not fix the Windows media-tile flicker — see Known Bugs)

[1.0] — 2026-06-25

🎉 First stable release. NovaSonic is feature-complete across web (PWA) and Android: streaming, lyrics, artist pages, playlists, importer/downloads, Audiobookshelf, and — new in this release — full offline downloads on Android. Further work lands as point releases.

Added

  • Android — Offline downloads: download albums and playlists to device storage with a single tap; album download button is in the album hero row (▶ / shuffle / ↓), playlist button next to the edit toggle. Tapping again resumes / re-downloads any missing songs (already-downloaded tracks are skipped)
  • Android — Offline tab in Library (position 2, next to Playlists): lists downloaded groups with cover art, track count, and an in-progress bar; shows total MB used
  • Android — Offline detail screen: tap a group to view its songs (with cover art, like the online playlist), play / shuffle, and remove individual songs from the device via the edit toggle. Works fully offline (reads from Room, no network)
  • Android — Offline playback: tap ▶ on a fully-downloaded group to play locally; downloaded songs are also served from disk automatically when opened from any album or playlist view
  • Android — Downloaded badge: albums and playlists in the Library tabs show a ↓-in-circle badge on the cover when fully downloaded
  • Android — Duplicate-safe storage: a song shared by an album and a playlist is stored once (keyed by song ID) and reference-counted on delete — removing one group keeps the file if another still needs it
  • Web + Android — Lyrics prefetch: lyrics are fetched the moment a track starts playing (not only when the Lyrics tab is opened), so opening Lyrics is instant. The lrclib cache is warmed on every song change
  • Web (PWA) — Desktop install polish: manifest.json now ships PNG icons (192/512) for a crisp taskbar/Start-menu icon, drops the portrait lock so the window resizes freely, and adds a stable app id — installs as a standalone desktop window (no browser chrome)
  • Web — Keyboard shortcuts (desktop): Space = play/pause, ←/→ = seek 5 s, Shift+←/→ = previous/next track, ↑/↓ = volume. Ignored while typing in inputs/search fields
  • Web — Horizontal rows (Home) now have hover scroll arrows; only the scrollable direction is shown (no left arrow at the start, no right arrow at the end)

Fixed

  • Android — Download crash on Android 15 (targetSdk 35): FOREGROUND_SERVICE_TYPE_DATA_SYNC is banned for apps targeting API 35; WorkManager's expedited path used it and the resulting exception killed the app ~1.5 s after tapping download. Fixed by dropping setExpedited() and all foreground-service use from DownloadWorker — downloads run as silent background work with progress shown in the Offline tab
  • Android — Offline detail screen flashed open then immediately returned: its initial empty state (before Room emitted) was mistaken for "deleted". Now shows a brief spinner until loaded, and only leaves when the last song is actually removed
  • Android — Lyrics autoscroll: rewrote centering to use the active line's measured position in a LazyColumn instead of a fixed line-height estimate. Eliminates the stutter and the downward drift on wrapped (multi-line) lyrics — the active line now tracks smoothly and sits at a natural ~40 %-from-top reading anchor
  • Web — Home now refreshes on an explicit Home click (matching Android): the album rows re-fetch and the random row re-randomizes instead of serving the 5-minute cache. Back/forward navigation still keeps the cached state
  • Web (PWA) — Media Session metadata is now mutated in place instead of replaced with a new MediaMetadata each track (reduces churn). Note: this alone does not stop the Windows media tile from briefly closing/reopening on skip — see Known Bugs

Internal

  • Android — Offline system: Room DB (novasonic_offline.db) with downloaded_tracks, download_groups, group_track_cross_refs; OfflineRepository holds hot StateFlow maps for O(1) local-path lookups; DownloadWorker streams audio + cover art via HttpURLConnection to context.filesDir/offline/{audio|covers}/. MusicRepository.streamUrl() checks local files first, so playback uses the file:// URI on every entry point

[0.9] — 2026-06-22

Added

  • UI — New NovaSonic app icon: 5-bar equalizer waveform with blue→purple gradient (#7eb8ff → #8b3cf7) on dark background — replaces the generic music-note placeholder
  • UI — Favicon link added to <head> with SVG + PNG fallback (icon-192.png) so browser tabs show the icon
  • UI — Android launcher icon (ic_launcher.xml) updated to match the new equalizer design using the same blue→purple gradient via aapt:attr
  • UI (web) — Sidebar logo updated from ♫ emoji to the new equalizer SVG (icon.svg)
  • nginx — /app/ alias added so reverse-proxy deployments can resolve static assets (icon, manifest) under the /app prefix without Vite base changes

Fixed

  • UI — Player bar star/favorite button now vertically centred with the full track info block (title + album + artist), not pinned to the top of the title line
  • UI — Shuffle status badge no longer shifts when the player bar is active (removed overflow:hidden from .player-info-wrap that was clipping the status row)
  • UI (mobile) — Duplicate NovaSonic logo + name removed from mobile header next to the search bar
  • Backend (download) — Duplicate detection now matches case-insensitively (DJ OKAWARI vs DJ Okawari) and across all album folders under the artist folder, comparing normalized title without track prefix or release/variant suffix — prevents re-downloading songs whose filename drifted between runs
  • Backend (download) — iTunes collectionArtistName (album artist) is now captured and passed through the download pipeline as the album_artist tag; fixes compilation albums showing "Various Artists" for some tracks and the individual track artist for others
  • Metadata editor (autofill) — iTunes button now fills all available fields: title (trackName), artist (artistName), album-artist (collectionArtistName), album, genre, year, track #, disc #, total tracks, cover
  • Metadata editor (autofill) — MusicBrainz button now fills album-artist (from release artist-credit) and disc # in addition to the existing fields

Changed

  • Backend — Japanese tracks now stored with original Japanese names in folders, filenames, and all main tags (title/artist/album/album_artist); Romaji goes only into sort tags (TITLESORT / ARTISTSORT / ALBUMARTISTSORT / ALBUMSORT) for sorting and search in Navidrome. Previously, romaji was applied to filenames and tags too, causing name drift, duplicate misses, and wrong iTunes resolutions (e.g. "Hisaishi Yuzuru" instead of "Joe Hisaishi")
  • Backend — Metadata editor: files and album/artist folders are renamed using the original name typed in the form (may be Japanese), not romaji; sort tags still receive the romaji conversion
  • Backend — fix-artist-sort script now uses the original (kanji) artist name for folder and filename; artistSort (romaji) is only written to ARTISTSORT / ALBUMARTISTSORT sort tags
  • Backend — album_artist tag in downloaded tracks now consistently uses mainArtist (the iTunes-resolved album artist) rather than the raw per-track artist. This prevents Navidrome from splitting one album into multiple when track artists vary (e.g. compilations)
  • Backend (download) — No-duration cascade now ends after step 4 (same Mittelweg philosophy as the with-duration branch): no title-only or filter-free fallback grabs that pull wrong covers (observed: Yorushika 車窓 → Piano Echoes Shasou)
  • Backend (download) — --max-filesize 30M added to all yt-dlp calls; Mittelweg/no-duration steps also cap at 2× expected duration — a 28-min video can no longer be saved as a ~52 MB mp3

Investigated (album splitting root causes)

  1. Missing album_artist tag / inconsistent value across tracks — fixed above (mainArtist + collectionArtistName)
  2. tagAlbumArtist was using rawArtist (per-track Spotify name) instead of mainArtist — fixed
  3. Album name comes from iTunes collectionName — consistent within a collection, no fix needed
  4. Year from iTunes releaseDate — album-level date, consistent within a collection
  5. MBIDs not set — still missing; not the primary splitting cause

  • Backend — Japanese tracks now stored with original Japanese names in folders, filenames, and all main tags (title/artist/album/album_artist); Romaji goes only into sort tags (TITLESORT / ARTISTSORT / ALBUMARTISTSORT / ALBUMSORT) for sorting and search in Navidrome. Previously, romaji was applied to filenames and tags too, causing name drift, duplicate misses, and wrong iTunes resolutions (e.g. "Hisaishi Yuzuru" instead of "Joe Hisaishi")
  • Backend — Metadata editor: files and album/artist folders are renamed using the original name typed in the form (may be Japanese), not romaji; sort tags still receive the romaji conversion
  • Backend — album_artist tag in downloaded tracks now consistently uses mainArtist (the iTunes-resolved album artist) rather than the raw per-track artist. This prevents Navidrome from splitting one album into multiple when track artists vary (e.g. compilations)
  • Backend (download) — No-duration cascade now ends after step 4 (same Mittelweg philosophy as the with-duration branch): no title-only or filter-free fallback grabs that pull wrong covers (observed: Yorushika 車窓 → Piano Echoes Shasou)
  • Backend (download) — --max-filesize 30M added to all yt-dlp calls; Mittelweg/no-duration steps also cap at 2× expected duration — a 28-min video can no longer be saved as a ~52 MB mp3

Fixed

  • Backend (download) — Duplicate detection now matches case-insensitively (DJ OKAWARI vs DJ Okawari) and across all album folders under the artist folder, comparing normalized title without track prefix or release/variant suffix — prevents re-downloading songs whose filename drifted between runs
  • Backend (download) — iTunes collectionArtistName (album artist) is now captured and passed through the download pipeline as the album_artist tag; fixes compilation albums showing "Various Artists" for some tracks and the individual track artist for others
  • Metadata editor (autofill) — iTunes button now fills all available fields: title (trackName), artist (artistName), album-artist (collectionArtistName), album, genre, year, track #, disc #, total tracks, cover
  • Metadata editor (autofill) — MusicBrainz button now fills album-artist (from release artist-credit) and disc # in addition to the existing fields

Investigated (Point 4 — album splitting)

Primary causes identified:

  1. Missing album_artist tag / inconsistent value across tracks — fixed above (mainArtist + collectionArtistName)
  2. tagAlbumArtist was using rawArtist (per-track Spotify name) instead of mainArtist — fixed
  3. Album name comes from iTunes collectionName — consistent within a collection, no fix needed
  4. Year from iTunes releaseDate — album-level date, consistent within a collection
  5. MBIDs not set — still missing; not the primary splitting cause

[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 (<p>, </p>) 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

  • Web + Android — Discover Mixes (auto-download, temporary) — themed/random mixes for discovering unknown music, Spotify-style (e.g. "Rock", "Charts", "Random Discovery"). Idea:
    • Generate a mix from a theme/genre/charts (sources: Last.fm / ListenBrainz similar + iTunes charts), then auto-download the tracks via the yt-dlp backend so they play locally.
    • Temporary by default: tracks auto-delete after a configurable time (e.g. 7 days) so the library doesn't fill up with stuff you didn't keep.
    • Keep what you like: per-track (or whole-mix) option to promote into the permanent library instead of letting it expire.
    • Goal: completely random/unfamiliar music you don't already know — like Spotify's curated/discovery playlists, but self-hosted.
    • Open questions: where downloaded-but-temporary tracks live vs. the main library (separate folder + retention job), how Navidrome should/shouldn't index them, and dedupe against tracks you already own.
  • Web + Android — Artist following + new-release & concert notifications — follow artists and get notified when they drop something new or play nearby. Idea:
    • A follow list of artists (stored per user, e.g. in app settings / a small backend table).
    • New-release alerts: periodic check for new albums/singles by followed artists (sources: iTunes lookup / MusicBrainz / Last.fm), surfaced in the existing in-app notification bell (and optionally push).
    • Concert alerts: reuse the existing Ticketmaster events integration to notify when a followed artist has nearby/upcoming shows.
    • Ties into the importer: a "download this new release" action straight from the notification.
  • 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

  • Android (lyrics) — Synced-lyrics auto-scroll is not smooth: it follows playback and highlights the right line, but the list jumps to it rather than gliding. Web autoscroll is smooth. Not yet fixed.
  • Web (PWA, desktop) — The Windows media tile (SMTC) briefly closes and reopens when skipping tracks. Swapping the <audio> element's source leaves a momentary gap with no active media output, so Windows tears down and rebuilds the tile. A real fix needs one continuous source (MSE), which requires streaming an MSE-compatible format. The native Android app is unaffected. (The two-element gapless handoff added in 1.1 makes skips effectively instant but does not fix this — switching elements still ends and restarts the OS session.)
  • Backend (download) — The duplicate check requires at least a firstArtist folder match. If the same song was previously stored under a very different artist name (e.g. before an iTunes-name fix), it will not be detected as a duplicate and will be re-downloaded.
  • Backend (metadata/sorting) — MusicBrainz release['artist-credit'] is often absent in the standard recording search response (requires inc=artist-credits which can slow the endpoint). The albumArtist field from MusicBrainz autofill may fall back to the recording artist rather than the release artist.
  • Backend (metadata/sorting) — MBIDs are not embedded in downloaded tracks; Navidrome cannot use them for authoritative album grouping (low priority — album splitting is mostly solved via consistent album_artist).