dev → main: Android UPnP/Sonos transport parity + server stream URL extension #79
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?
Merges accumulated
devwork tomain. 1242 commits since last merge.Headline changes
Android UPnP/Sonos transport parity (M8)
A full sweep across
MinstrelForwardingPlayer,PlayerController,OutputPickerController,RemotePlayerState, and the polling/state machinery. Major fixes in this batch:queue[0..currentIndex]first, background-append rest. Cuts startup ~17s → 2-5s.getPlayWhenReady, fixonTaskRemoved).ON_RESUMEso refocus catches up within RTT, not the full poll cycle.remoteState.trackNumbermatch instead of a static timer; 5s safety timeout.seekToTrack(idx+1)on Sonos and restarted whatever was playing on app resume.effectiveDurationusesdesiredIdxso duration tracks the displayed title during track changes.restricted="true",id="-1", Rincon namespace,<desc cdudn>(some firmwares still ignore content for external HTTPS URIs).Server: stream URL gets file extension
/api/tracks/{id}/stream.{ext}(Option A from task #610). Without the extension Sonos's URL probe couldn't identify the format, defaultedTrackDuration=0, and any seek triggered an immediate auto-advance. Backward-compatible: old/streamroute still works.Shuffle on system playlist tile play (Home + Playlists list)
Tile play button shuffles client-side after
systemShuffleserver call so rotation still advances. Snackbar + offline gating on Playlists list parity.Cross-platform drift audit batches (1-7)
Closes a long list of small-to-medium drift issues across server, web, Android, Flutter. Notable:
/api/meprofile shape, GC sweep for 5 lifecycle gaps, scrobble queue retention, library SSR redirect, Android cross-device unlike, Android user-switch hygiene, cold-boot resume autoplay opt-out + awaitReady race, radio exclude cap.Risk
Largest area is the Android UPnP rework — operator has been verifying on-device throughout. Server change is route-additive (no removed paths) with updated tests.
Test plan
TrackDurationis now a real value (was always0:00:00)dev(all checks passing at8cd2383a)🤖 Generated with Claude Code
Move local ExoPlayer pause from OutputPickerController.selectUpnp into MinstrelForwardingPlayer.onActiveChanged (handler.post { delegate.pause() }). This guarantees the pause hits ExoPlayer before the holder is live, eliminating the async race that caused SOAP fault 701 on Sonos when pause() was dispatched via playerController after holder.active was already set. Also adds per-poll Timber.w before initialPreQueueDone for diagnostics. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>