Download albums/playlists to private device storage (Room + WorkManager, HttpURLConnection streaming). New Offline tab lists groups with progress; tapping a group opens a detail screen (cover-art rows, play/shuffle, per-song delete from device). Downloaded tracks play from disk on every screen. Album/playlist cards show a downloaded badge; download button resumes missing songs. Song storage is dedup'd by ID and reference-counted on delete. Fixes: Android 15 download crash (dropped banned DATA_SYNC foreground service); offline detail flash-back (initial empty state no longer mistaken for deleted). Docs: CHANGELOG 0.10 + parity matrix; condensed README Features. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
25 KiB
25 KiB
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 (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 | — | ✅ |
| Offline download (albums/playlists) | ❌ | ✅ |
| Offline playback | ❌ | ✅ |
| Offline detail / manage downloads | ❌ | ✅ |
| Romaji / Japanese sort | ✅ | ❌ |
| Settings (Navidrome, ABS, etc.) | ✅ | ✅ |
[0.10] — 2026-06-25
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
Fixed
- Android — Download crash on Android 15 (targetSdk 35):
FOREGROUND_SERVICE_TYPE_DATA_SYNCis 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 droppingsetExpedited()and all foreground-service use fromDownloadWorker— 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
Internal
- Android — Offline system:
RoomDB (novasonic_offline.db) withdownloaded_tracks,download_groups,group_track_cross_refs;OfflineRepositoryholds hotStateFlowmaps for O(1) local-path lookups;DownloadWorkerstreams audio + cover art viaHttpURLConnectiontocontext.filesDir/offline/{audio|covers}/.MusicRepository.streamUrl()checks local files first, so playback uses thefile://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 viaaapt: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/appprefix without Vitebasechanges
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:hiddenfrom.player-info-wrapthat 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 thealbum_artisttag; 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-sortscript now uses the original (kanji) artist name for folder and filename;artistSort(romaji) is only written to ARTISTSORT / ALBUMARTISTSORT sort tags - Backend —
album_artisttag in downloaded tracks now consistently usesmainArtist(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 30Madded 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)
- Missing
album_artisttag / inconsistent value across tracks — fixed above (mainArtist + collectionArtistName) tagAlbumArtistwas usingrawArtist(per-track Spotify name) instead ofmainArtist— fixed- Album name comes from iTunes
collectionName— consistent within a collection, no fix needed - Year from iTunes
releaseDate— album-level date, consistent within a collection - 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_artisttag in downloaded tracks now consistently usesmainArtist(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 30Madded 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 thealbum_artisttag; 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:
- Missing
album_artisttag / inconsistent value across tracks — fixed above (mainArtist + collectionArtistName) tagAlbumArtistwas usingrawArtist(per-track Spotify name) instead ofmainArtist— fixed- Album name comes from iTunes
collectionName— consistent within a collection, no fix needed - Year from iTunes
releaseDate— album-level date, consistent within a collection - 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_BACKENDandSQUID_QUALITYenv 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_musicplayer client so YouTube Music "Art Track" / topic uploads are reachable (the previousandroid_vrclient 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/- EPin 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=recentrow - Android — Library Albums: sort chips (Neueste / Älteste / A-Z); Neueste uses
type=newestfrom 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 usesNotificationStore.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=newestfrom Navidrome (recently added to library) - Android — Artist screen crash: duplicate key in
LazyColumnwhen Navidrome returns the same song ID twice intopSongs - 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
DefaultMediaNotificationProvidersubclass (AbsAwareNotificationProvider) registered viasetMediaNotificationProvider(); overridesgetMediaButtons()to return exactly 5 session-command buttons when ABS is active, bypassingsetCustomLayout()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
darkMutedSwatchtovibrantSwatch(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
sortNametag) - 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_artisttag for Navidrome grouping - Web —
feat./ft./featuringconverted 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) — The duplicate check requires at least a
firstArtistfolder 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 (requiresinc=artist-creditswhich 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).