Commit Graph
227 Commits
Author SHA1 Message Date
austinried b171b0b33c stream sync with SAX and write the library per batch
The DOM-based sync materialised the whole library in memory before
writing: SubsonicSource accumulated every paged response into one list
and SubsonicClient built a full DOM per response, so peak memory grew
with the library size (hundreds of MB for large libraries).

Replace it with a streaming pipeline:

- SubsonicXml is now a SAX reader that maps entities straight from the
  stream. Album disc titles are accumulated from child elements and
  attached when the album closes.
- SubsonicClient exposes stream()/check() instead of returning a DOM.
- MusicSource returns batches as Flow<List<T>>; SubsonicSource emits one
  batch per response page with backpressure so only one batch is held.
- SyncService syncs each entity sequentially, commits one transaction
  per batch, and prunes with an existing-minus-seen stale set. Playlist
  song tails are pruned once the whole flow has been counted, and
  orphaned playlist_songs rows are removed with a single delete.

Sync is no longer atomic: on failure the committed batches remain and
the queue's library cache is now invalidated on failure too.
2026-10-04 00:29:58 +00:00
austinried e8db675470 make now playing a NavHost destination
The overlay fought the NavHost over the back: OnBackPressedDispatcher hands the back
to the last-registered enabled callback, and the NavHost registers its own as part of
its composition, so it intermittently popped the route underneath. A destination lets
the back stack own it.

- add Routes.NOW_PLAYING with a vertical slide, opened by tapping the mini player or
  flicking it up; back or a downward fling pops it
- hide the mini player while on the now-playing route so it fills the screen
- the static scrim now follows the route instead of a drag progress
- drop the nowPlayingOpen/progress overlay state and its test
2026-10-04 00:29:58 +00:00
austinried fd37c3ad86 fix back popping the route under now playing; disc header spacing; dial back the fab glyph
- register the now playing and queue back callback from a LaunchedEffect instead of BackHandler: the NavHost registers its own callback from a LaunchedEffect, so a BackHandler composed before it loses the back and pops the route underneath
- NowPlayingBackTest registers the overlay handler before the NavHost (the losing order) and asserts back closes the overlay without popping the route
- more top padding on disc headers, except the first which sits flush
- LIST_OPTIONS_WEIGHT 0.15 -> 0.11
2026-10-04 00:29:58 +00:00
austinried d43dd51b15 songs type label, disc header alignment, now playing back test
- Now Playing: the type line reads "songs" for the library songs list, source line stays "Library"
- Album detail: align the disc icon over the track numbers and the disc label with the track titles (16dp start + 18dp icon + 16dp gap, matching the ListItem columns)
- Add NowPlayingBackTest covering back-from-overlay not popping the destination
2026-10-04 00:29:58 +00:00
austinried 2e925f2f78 ui nits: cover placeholder ratio, now playing scrim/title, add-server keyboard, titlebars, search dismiss, fab icon
- CoverArt: use the thumbnail's intrinsic ratio until the full-res painter loads, so non-square art no longer snaps
- Now Playing: move the top scrim out of the sliding screen into MainNavigation, static under the status bar, fading in as the screen settles at the top
- Now Playing: titlebar shows the playback source (album/playlist name, or Library) instead of "Now playing"
- Add server: keep the test/save bar above the keyboard on first launch
- Titlebars: use the theme default title style instead of headlineMedium
- Library: clicking an item in a searched tab dismisses the search
- Library FAB: use the AutoMirrored Sort icon at 28dp so the glyph reads a little thicker
2026-10-04 00:29:58 +00:00
austinried 2552e1a10c match the disc header text case-insensitively in the screenshot test 2026-10-04 00:29:58 +00:00
austinried b8f8297c9b make disc headers read as section labels
The header was the same size as the track title, so render it uppercase
labelLarge with letter spacing to separate it from the tracks. Use the
outlined FiberSmartRecord icon.
2026-10-04 00:29:58 +00:00
austinried 33e47e9654 suffix repeated disc titles with their disc number
When two discs of an album share a title, showing "Live" alone is
ambiguous, so render "Live: Disc 1"/"Live: Disc 2". Unique titles stay
as-is. Also drop the header from titleLarge to a semibold titleMedium so
longer titles fit on one line.
2026-10-04 00:29:57 +00:00
austinried 863c17be7d show album disc titles instead of generic disc numbers
Navidrome and gonic expose OpenSubsonic discTitles on the album response.
Parse them into a transient Album.discTitles map and persist each album's
titles in a child discs table (sourceId, albumId, disc, title) cascading
from albums. Songs keep only their disc number, and the album screen joins
by disc to render a title when one exists, falling back to "Disc N".

Titles are read opportunistically: a server that doesn't send discTitles
just leaves the table empty, so no API version negotiation is needed.
Titles apply to single- and multi-disc albums, are pruned when removed,
and are stored per album.

Schema 10 -> 11.
2026-10-04 00:29:57 +00:00
austinried d36c752c72 test the tab row reflows in a single direction during a switch
Drives the pager across the Artists to Playlists transition and asserts the
Songs icon only moves one way. Fails on the min-width clamp version (738-727-745)
and passes on the natural-48dp version. LibraryTabs is internal so the test can
drive its PagerState.
2026-10-04 00:29:57 +00:00
austinried 5b88792f72 address the code review findings
- offer a Sync button on a truly empty library tab so an empty library is not stranded (pull-to-refresh needs scrollable content)
- do not close the search field when it auto-syncs from a tab with an empty term
- make rememberArtworkColors nullable so the no-track fallbacks are live and the idle theme is the monochrome background
- measure the tab overlay content offset so the colour clip accounts for the touch-target centering
- put the pull indicator below the status-bar scrim, and seed the tab bar height estimate to avoid a first-frame inset jump
- collapse the two equal clearance constants, drop the redundant search imePadding, reuse the hoisted artwork for the mini player, and de-duplicate the mini player screenshots
2026-10-04 00:29:57 +00:00
austinried e031e92e2d pull to refresh, hide the bar while searching, animate the progress inset, circular warm damping
- replace the library sync button with pull-to-refresh on every tab
- hide the tab bar while searching so only the search field sits above the keyboard
- animate the mini player progress bar between full bleed and 8dp inset rounded ends as the tab bar comes and goes
- make the warm-hue saturation damping continuous across the 0/360 hue wrap
2026-10-04 00:29:57 +00:00
austinried 10b21303d2 address the adversarial review findings
- restore the library sync control removed when the tab bar was reworked, with a test that clicks it
- lift the search field above the tab bar instead of overlapping it
- give the tabs and settings/sync buttons 48dp touch targets
- drop the duplicated label semantics on tabs
- animate the library bottom inset so the bar does not jump when the mini player enters/exits
- remove imports and a parameter left dead by the header rework
2026-10-04 00:29:57 +00:00
austinried ab39fc259e overlay a translucent header onto the library content 2026-10-04 00:29:57 +00:00
austinried 2464c26c79 replace the collapsing library title with expanding pill tabs 2026-10-04 00:29:56 +00:00
austinried d1b5aaeeb7 smooth the warm damping so switching artwork does not flash 2026-10-04 00:29:56 +00:00
austinried 5e00bf86a8 lift warm tonal surfaces to the bright gradient tone 2026-10-04 00:29:56 +00:00
austinried c56ae3f0ed revert the warm hue harmonisation 2026-10-04 00:29:56 +00:00
austinried a54a54ffa4 rotate warm artwork hues out of the brown/olive range for dark tones 2026-10-04 00:29:56 +00:00
austinried 8ab4cbb0d0 tint the library background and fab from the current artwork 2026-10-04 00:29:56 +00:00
austinried 0e55ce65f5 fix the restored position display, detail controls, mini player colour and source bar inset 2026-10-04 00:29:56 +00:00
austinried ef5bf40bf7 drop the unused FTS5 search index 2026-10-04 00:29:56 +00:00
austinried 605bd1a6c6 songs queue follows the applied sort and starred filter; clear a tab's search when leaving it 2026-10-04 00:29:56 +00:00
austinried d55406c85e address review: keep per-tab search visible and clearable, fix direction arrow, guard scroll reset, harden date parsing 2026-10-04 00:29:56 +00:00
austinried e99ed1dc45 song added sort, robust scroll reset, restyled search and filter controls 2026-10-04 00:29:55 +00:00
austinried 266b677ece parse Subsonic dates without DatatypeFactory, reset list scroll on option change, fix search bar over the keyboard 2026-10-04 00:29:55 +00:00
austinried 63f0549181 tristate starred filter, filter-aware empty state, keyboard-safe search bar 2026-10-04 00:29:55 +00:00
austinried a2d42f7957 rework the library lists: inline filter, playlist updated, starred sort, clearer controls 2026-10-04 00:29:55 +00:00
austinried 9a99977342 add sort, filters and search to the library 2026-10-04 00:29:55 +00:00
austinried 31179f1287 use titleLarge for disc headings and number album tracks 2026-10-04 00:29:55 +00:00
austinried 9ca6273b82 show disc headings in multi-disc album track lists 2026-10-04 00:29:55 +00:00
austinried 21df098d2a stop the viewport fill growing when a short list is scrolled 2026-10-04 00:29:55 +00:00
austinried e7f8032250 address fourth review: reset the saved position per track and serialize cursor writes 2026-10-04 00:29:55 +00:00
austinried 20ab30920f persist and restore the playback position within the track 2026-10-04 00:29:55 +00:00
austinried 88d0113191 start the queue unshuffled from the play buttons 2026-10-04 00:29:55 +00:00
austinried a8f1b30d67 start a shuffled queue from the album and playlist shuffle buttons 2026-10-04 00:29:55 +00:00
austinried db58887567 load the queue around the current track and scroll to it 2026-10-04 00:29:55 +00:00
austinried e1171d7588 key the queue now-playing highlight by song id 2026-10-04 00:29:55 +00:00
austinried 59d20d4eda address third review: rebuild the window when the shuffle is dropped, atomic queue version 2026-10-04 00:29:55 +00:00
austinried b2f654e10f address second review: shuffle cache invalidation, locking, ended latch 2026-10-04 00:29:55 +00:00
austinried 2c333195ef address shuffle review findings 2026-10-04 00:29:55 +00:00
austinried 2b59635402 cover jumping within a shuffled queue without reshuffling 2026-10-04 00:29:55 +00:00
austinried 7236ce76d4 rebuild the queue window from the shuffled snapshot when starting a shuffled queue 2026-10-04 00:29:55 +00:00
austinried 8bd07e4332 store the shuffle order in a table with a cache; keep previous at the start unless repeating 2026-10-04 00:29:55 +00:00
austinried b5799d7202 use rounded on variants for the shuffle and repeat icons 2026-10-04 00:29:55 +00:00
austinried a755de5356 add repeat and shuffle modes and fix duplicate track ordering 2026-10-04 00:29:55 +00:00
austinried ffd5f58aa9 revert the library header colour and gradient 2026-10-04 00:29:55 +00:00
austinried 5535a41f5d show the now playing gradient behind the library lists 2026-10-04 00:29:55 +00:00
austinried d7434b80cc match the remaining app bar titles and cover the switch off state 2026-10-04 00:29:54 +00:00
austinried d26936c251 theme the artist header from cover art and tune the mini player 2026-10-04 00:29:54 +00:00