Patch batch on top of v2026.05.13.0. Started as a hotfix for the
player + Discover regressions; grew to include the drift-first
migration of Library Artists/Albums + systemPlaylistsStatus and the
Library tab pre-warm. All small contained changes against the same
release.
Player fixes
Queue button on the now-playing screen did nothing. MinstrelAudio
Handler never overrode skipToQueueItem; BaseAudioHandler's default
is empty so taps in QueueScreen silently no-op'd. Override now
rebuilds the source list via setQueueFromTracks(_lastTracks,
initialIndex) so the targeted item plays cleanly even when its
source hadn't been built yet by the background fill. (3e7b258)
Previous track bled through after tapping a new one.
setQueueFromTracks awaits _buildAudioSource before swapping the
player's source list; on cache miss that wait can be a few hundred
ms during which the old source kept playing while the UI had
already flipped to the new title/artist. Now pause()s the player
at the start of setQueueFromTracks. (3e7b258)
Mini player cover flickered on track change. Audio handler
broadcasts MediaItem twice on every track change (once with
artUri=null, then with it once AlbumCoverCache resolves the path).
AnimatedSwitcher (180ms crossfade) around the cover smooths the
swap. (6a08d94)
Full-player track change: art + backdrop snapped in.
AnimatedSwitcher fade completed before FileImage decoded the new
bytes, and albumColorProvider's loading state dragged the gradient
through fs.obsidian. precacheImage on the new file:// artUri so
bytes are ready when AnimatedSwitcher swaps in; _lastDominant held
across builds so the gradient stays on the previous album's color
until the new extraction resolves. (2ebe622)
Discover playlist
Adjacent duplicate tracks. interleaveBuckets never tracked
seen IDs. On single-user servers the empty crossUser bucket
pushes slots to dormant + random, so a track that's both a
dormant-artist pick and a random-unheard pick lands at d_i and
r_i — producing [X, X, …] back-to-back. Dedup by track ID across
all buckets during interleave; priority is bucket order. Existing
stale duplicates in drift clear at the next playlist rebuild.
(c29d25d)
Library + UI
Library Artists circles squeezed in the 3-column grid.
ArtistCard hardcoded 124×124 avatar — got constrained
horizontally to ~93dp by narrow cells but kept its 124dp height,
so ClipOval produced a vertical ellipse. Mirrors AlbumCard's width param pattern; _ArtistsTab uses LayoutBuilder + mainAxis
Extent. (bf0ef5e)
Drift-first Library tabs. _libraryArtistsProvider and
_libraryAlbumsProvider migrate from REST-paginated AsyncNotifier
to drift-first StreamProvider over cached_artists/cached_albums.
SyncController already populates the rows; cacheFirst handles
the cold-install fallback. Drops loadMore+NotificationListener —
GridView.builder lazily realizes visible cells so the full
sorted list up front is fine. (2a18e91)
systemPlaylistsStatusProvider drift-first. New
CachedSystemPlaylistsStatus single-row table (schema 7) holds
the last /api/me/system-playlists-status response so the home
Playlists row paints with the prior status instantly instead of
flickering through empty while REST resolves. (2a18e91)
Library tab pre-warm. ref.listen on all 5 tab providers in
LibraryScreen.build subscribes them upfront so swiping between
tabs feels instant rather than each tab paying its own cold-cache
cost on first visit. (2a18e91)
Cleanup
404 log noise from missing playlist covers. Playlist
collages aren't generated until the build job runs on a playlist
with tracks; that window produces 404s from
/api/playlists/:id/cover. ServerImage now filters expected 404s
out of dev console output; auth/connectivity errors still surface.
(6efb315)
Pull the new server container image — Discover dedup affects
new playlist builds; existing duplicates clear at the next
03:00-local refresh.
In-app updater should pick this up cleanly (versionCode bumped
1 → 2).
Patch batch on top of v2026.05.13.0. Started as a hotfix for the
player + Discover regressions; grew to include the drift-first
migration of Library Artists/Albums + systemPlaylistsStatus and the
Library tab pre-warm. All small contained changes against the same
release.
## Player fixes
- **Queue button on the now-playing screen did nothing.** MinstrelAudio
Handler never overrode skipToQueueItem; BaseAudioHandler's default
is empty so taps in QueueScreen silently no-op'd. Override now
rebuilds the source list via setQueueFromTracks(_lastTracks,
initialIndex) so the targeted item plays cleanly even when its
source hadn't been built yet by the background fill. (`3e7b258`)
- **Previous track bled through after tapping a new one.**
setQueueFromTracks awaits _buildAudioSource before swapping the
player's source list; on cache miss that wait can be a few hundred
ms during which the old source kept playing while the UI had
already flipped to the new title/artist. Now pause()s the player
at the start of setQueueFromTracks. (`3e7b258`)
- **Mini player cover flickered on track change.** Audio handler
broadcasts MediaItem twice on every track change (once with
artUri=null, then with it once AlbumCoverCache resolves the path).
AnimatedSwitcher (180ms crossfade) around the cover smooths the
swap. (`6a08d94`)
- **Full-player track change: art + backdrop snapped in.**
AnimatedSwitcher fade completed before FileImage decoded the new
bytes, and albumColorProvider's loading state dragged the gradient
through fs.obsidian. precacheImage on the new file:// artUri so
bytes are ready when AnimatedSwitcher swaps in; _lastDominant held
across builds so the gradient stays on the previous album's color
until the new extraction resolves. (`2ebe622`)
## Discover playlist
- **Adjacent duplicate tracks.** interleaveBuckets never tracked
seen IDs. On single-user servers the empty crossUser bucket
pushes slots to dormant + random, so a track that's both a
dormant-artist pick and a random-unheard pick lands at d_i and
r_i — producing [X, X, …] back-to-back. Dedup by track ID across
all buckets during interleave; priority is bucket order. Existing
stale duplicates in drift clear at the next playlist rebuild.
(`c29d25d`)
## Library + UI
- **Library Artists circles squeezed in the 3-column grid.**
ArtistCard hardcoded 124×124 avatar — got constrained
horizontally to ~93dp by narrow cells but kept its 124dp height,
so ClipOval produced a vertical ellipse. Mirrors AlbumCard's
`width` param pattern; _ArtistsTab uses LayoutBuilder + mainAxis
Extent. (`bf0ef5e`)
- **Drift-first Library tabs.** _libraryArtistsProvider and
_libraryAlbumsProvider migrate from REST-paginated AsyncNotifier
to drift-first StreamProvider over cached_artists/cached_albums.
SyncController already populates the rows; cacheFirst handles
the cold-install fallback. Drops loadMore+NotificationListener —
GridView.builder lazily realizes visible cells so the full
sorted list up front is fine. (`2a18e91`)
- **systemPlaylistsStatusProvider drift-first.** New
CachedSystemPlaylistsStatus single-row table (schema 7) holds
the last /api/me/system-playlists-status response so the home
Playlists row paints with the prior status instantly instead of
flickering through `empty` while REST resolves. (`2a18e91`)
- **Library tab pre-warm.** ref.listen on all 5 tab providers in
LibraryScreen.build subscribes them upfront so swiping between
tabs feels instant rather than each tab paying its own cold-cache
cost on first visit. (`2a18e91`)
## Cleanup
- **404 log noise from missing playlist covers.** Playlist
collages aren't generated until the build job runs on a playlist
with tracks; that window produces 404s from
/api/playlists/:id/cover. ServerImage now filters expected 404s
out of dev console output; auth/connectivity errors still surface.
(`6efb315`)
## Operator notes
- Drift schema bumps 6 → 7 (CachedSystemPlaylistsStatus table).
Migrations are in-place.
- Pull the new server container image — Discover dedup affects
new playlist builds; existing duplicates clear at the next
03:00-local refresh.
- In-app updater should pick this up cleanly (versionCode bumped
1 → 2).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Patch batch on top of v2026.05.13.0. Started as a hotfix for the
player + Discover regressions; grew to include the drift-first
migration of Library Artists/Albums + systemPlaylistsStatus and the
Library tab pre-warm. All small contained changes against the same
release.
Player fixes
Queue button on the now-playing screen did nothing. MinstrelAudio
Handler never overrode skipToQueueItem; BaseAudioHandler's default
is empty so taps in QueueScreen silently no-op'd. Override now
rebuilds the source list via setQueueFromTracks(_lastTracks,
initialIndex) so the targeted item plays cleanly even when its
source hadn't been built yet by the background fill. (
3e7b258)Previous track bled through after tapping a new one.
setQueueFromTracks awaits _buildAudioSource before swapping the
player's source list; on cache miss that wait can be a few hundred
ms during which the old source kept playing while the UI had
already flipped to the new title/artist. Now pause()s the player
at the start of setQueueFromTracks. (
3e7b258)Mini player cover flickered on track change. Audio handler
broadcasts MediaItem twice on every track change (once with
artUri=null, then with it once AlbumCoverCache resolves the path).
AnimatedSwitcher (180ms crossfade) around the cover smooths the
swap. (
6a08d94)Full-player track change: art + backdrop snapped in.
AnimatedSwitcher fade completed before FileImage decoded the new
bytes, and albumColorProvider's loading state dragged the gradient
through fs.obsidian. precacheImage on the new file:// artUri so
bytes are ready when AnimatedSwitcher swaps in; _lastDominant held
across builds so the gradient stays on the previous album's color
until the new extraction resolves. (
2ebe622)Discover playlist
seen IDs. On single-user servers the empty crossUser bucket
pushes slots to dormant + random, so a track that's both a
dormant-artist pick and a random-unheard pick lands at d_i and
r_i — producing [X, X, …] back-to-back. Dedup by track ID across
all buckets during interleave; priority is bucket order. Existing
stale duplicates in drift clear at the next playlist rebuild.
(
c29d25d)Library + UI
Library Artists circles squeezed in the 3-column grid.
ArtistCard hardcoded 124×124 avatar — got constrained
horizontally to ~93dp by narrow cells but kept its 124dp height,
so ClipOval produced a vertical ellipse. Mirrors AlbumCard's
widthparam pattern; _ArtistsTab uses LayoutBuilder + mainAxisExtent. (
bf0ef5e)Drift-first Library tabs. _libraryArtistsProvider and
_libraryAlbumsProvider migrate from REST-paginated AsyncNotifier
to drift-first StreamProvider over cached_artists/cached_albums.
SyncController already populates the rows; cacheFirst handles
the cold-install fallback. Drops loadMore+NotificationListener —
GridView.builder lazily realizes visible cells so the full
sorted list up front is fine. (
2a18e91)systemPlaylistsStatusProvider drift-first. New
CachedSystemPlaylistsStatus single-row table (schema 7) holds
the last /api/me/system-playlists-status response so the home
Playlists row paints with the prior status instantly instead of
flickering through
emptywhile REST resolves. (2a18e91)Library tab pre-warm. ref.listen on all 5 tab providers in
LibraryScreen.build subscribes them upfront so swiping between
tabs feels instant rather than each tab paying its own cold-cache
cost on first visit. (
2a18e91)Cleanup
collages aren't generated until the build job runs on a playlist
with tracks; that window produces 404s from
/api/playlists/:id/cover. ServerImage now filters expected 404s
out of dev console output; auth/connectivity errors still surface.
(
6efb315)Operator notes
Migrations are in-place.
new playlist builds; existing duplicates clear at the next
03:00-local refresh.
1 → 2).