Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Two features on
dev, both CI-green (android.yml run 2380, test-web run 2371).#171 — UPnP/Sonos: one authoritative playback cursor
The local ExoPlayer cursor and the Sonos renderer were two competing sources of truth during a cast. The local cursor lagged (forward-only, index-based sync, skipped during load/re-cast) and two writers of the current index fought — so the in-app player flickered to the pre-cast track and the notification metadata went stale.
MediaItem.mediaId— both directions, survives queue-reload index wobble.onEventsis the sole writer of the index/track. One source, no stomp.#1596 — heart/like button in the full-screen player queue
The queue ("up next") rows in the full-screen player were the one track-list surface without a like heart, on both clients. Added the shared LikeButton to the web
QueueTrackRowand the AndroidQueueScreenrow (set-based liked-ids, same idiom as the detail screens). Liked state stays sourced from the shared store by track id — no data-model change.Verification
devhead.🤖 Generated with Claude Code
The full-screen player's queue ("up next") track rows were the one track-list surface missing the like heart that TrackRow/PlaylistTrackRow (web) and playlist/album/artist detail (Android) already carried. Web: render the shared <LikeButton> in QueueTrackRow between the row body and the remove button (serves both the /now-playing aside and the mobile QueueDrawer, same component). LikeButton already stops click propagation so it won't trigger play-on-click. Android: PlayerViewModel now exposes likedTrackIds (set-based, the same idiom as the detail VMs) + toggleLikeTrack; QueueScreen threads liked/onToggleLike through QueueList → QueueRow, which renders the shared LikeButton after the duration. Liked state stays sourced from LikesRepository by track.id — no TrackRef data-model change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>The local ExoPlayer cursor and the Sonos renderer were two competing sources of truth for "what's playing" during a cast. The delegate cursor lagged (forward-only, index-based, size-capped sync, skipped during every load/re-cast window), and TWO writers of PlayerUiState.queueIndex fought: PlayerController.onEvents (reading the lagging cursor) stomped the Sonos-derived index the position tick published, so the in-app player flickered to the pre-cast track and the notification metadata went stale. Step 1 — MinstrelForwardingPlayer.syncLocalCursorToRemote (replaces maybeSyncLocalCursor): align the paused delegate cursor to the track the renderer is actually playing, matched by track-id parsed from the Sonos TrackURI (/api/tracks/{id}/stream) against delegate MediaItem.mediaId (== TrackRef.id). Both directions; survives queue-reload index wobble; nearest-occurrence tiebreak for duplicate tracks; falls back to the Sonos Track index; suppressed during load and while a user transport is pending Sonos's ack. The cursor is now the single authoritative "current track" that both the in-app UI (onEvents) and the notification (getCurrentMediaItem) read. Step 2 — PlayerController: the position tick now patches only position/duration/play-pause/buffer; onEvents is the sole writer of queueIndex/currentTrack. Removed desiredQueueIndex, the forward-only trackChanged path, and publishTickIfChanged. One writer, no stomp. Part of milestone #171 (unify local + UPnP behind one cursor). Fixes the flicker + stale-notification symptoms; supersedes #1211/#608/#612. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>