Commit Graph

1676 Commits

Author SHA1 Message Date
bvandeusen 4d0a0b8e09 fix(android): throttle UPnP extend with 50ms delay per successful AddURIToQueue
android / Build + lint + test (push) Successful in 4m10s
Closes Scribe #611. The 2026-06-04 logcat showed 33 consecutive AddURIToQueue failures clustered at ~10ms intervals once the burst hit offset 39 -- characteristic of Sonos's burst-add rate-limit. 50ms between successful adds adds ~5s to the 100-track background extension but eliminates the burst rejection. Next reproduction with the SOAP fault detail logging (audit commit c5b326c6) will confirm the fault code if any tracks still fail.
2026-06-04 11:03:55 -04:00
bvandeusen 6184c62721 feat(android): MediaSession picks up UPnP state via direct listener invocation
android / Build + lint + test (push) Has been cancelled
Closes Scribe #606. Two pieces: MediaMetadata gets durationMs (lock-screen scrubber gets a known total even when wrapped ExoPlayer is paused under UPnP); MinstrelForwardingPlayer keeps an externalListeners registry that mirrors super.addListener so we can directly invoke onIsPlayingChanged / onPlaybackStateChanged / onMediaItemTransition when remoteState mutates. Fires from pollOnce + play()/pause() onSuccess. dedup via lastNotified guards so we don't spam events at 1Hz when nothing changed.
2026-06-04 11:02:51 -04:00
bvandeusen 28300e19fd perf(ci): turn off Postgres durability in integration tests
test-go / test (push) Successful in 28s
test-go / integration (push) Successful in 4m27s
TRUNCATE-everything ResetDB before every test forces a commit fsync; the CI DB is rebuilt each run so durability buys nothing. ALTER SYSTEM via docker exec (the services: block can't override the postgres command line). Non-fatal so a perms surprise degrades to slow, never red.

Per the playbook the operator shared from another project (~17x speedup observed there). Measure before/after in the next two CI runs.
2026-06-04 08:31:06 -04:00
bvandeusen 024493f2a7 refactor(server): unify stream URL builders + MIME tables + cover-path helper
test-go / test (push) Successful in 28s
test-go / integration (push) Has been cancelled
Closes Scribe #614, #615, server half of #616 surfaced by the 2026-06-04 divergent-provider audit.

- streamURL helper now used everywhere /api/tracks/{id}/stream is built (was inline concat in playlists.go and cast_token.go); add streamURLWithExt for the .ext cast variant.

- audioContentType in media.go is the canonical file_format -> MIME lookup; mimeForFormat in cast_token.go is now a thin wrapper that overrides the unknown-format fallback to audio/mpeg (Sonos rejects octet-stream). Adds mpeg/vorbis/wave aliases. Subsonic's contentTypeForFormat stays frozen per docs.

- coverart.ResolveAlbumPath extracted; api and subsonic both delegate to it.
2026-06-04 08:29:51 -04:00
bvandeusen edd198cdf5 fix(server): collage drawScaled uses center-crop instead of stretch
test-go / test (push) Successful in 27s
test-go / integration (push) Has been cancelled
2026-06-04 08:22:59 -04:00
bvandeusen 8cd2383a42 test(server): seed track for cast-token tests + assert file-ext in URL
test-go / test (push) Successful in 29s
test-go / integration (push) Successful in 9m34s
2026-06-04 07:44:33 -04:00
bvandeusen 27bd38e005 feat(server): stream URL gets file extension so Sonos can probe duration
test-go / test (push) Successful in 37s
test-go / integration (push) Failing after 10m34s
2026-06-04 07:29:28 -04:00
bvandeusen aa23a72693 fix(android): effectiveDuration uses desiredIdx so duration tracks the displayed title
android / Build + lint + test (push) Successful in 4m2s
2026-06-04 07:17:17 -04:00
bvandeusen 4021938046 fix(android): polling tick no longer force-syncs controller -- avoids Sonos seek-to-0
android / Build + lint + test (push) Successful in 3m26s
2026-06-04 07:06:07 -04:00
bvandeusen 7486bc2444 fix(android): split tickPositionPoll into helpers for detekt complexity
android / Build + lint + test (push) Has been cancelled
2026-06-04 07:04:01 -04:00
bvandeusen ee8a1fdc93 fix(android): event-driven pending-transport clear (Sonos ack OR 5s safety)
android / Build + lint + test (push) Failing after 1m12s
2026-06-04 07:00:22 -04:00
bvandeusen 8e578d2068 fix(android): 2s transport-ack lockout so Prev/Next isn't undone by stale poll
android / Build + lint + test (push) Successful in 3m25s
2026-06-04 06:55:39 -04:00
bvandeusen cacb280832 fix(android): polling tick track update is forward-only so user Next isn't undone
android / Build + lint + test (push) Successful in 3m27s
2026-06-04 06:50:35 -04:00
bvandeusen 36054506c2 fix(android): polling tick owns track-change updates when delegate.seekTo silent
android / Build + lint + test (push) Successful in 3m26s
2026-06-04 06:46:38 -04:00
bvandeusen 5db90844cb fix(android): DIDL-Lite includes Rincon namespace + cdudn desc for Sonos
android / Build + lint + test (push) Successful in 3m39s
2026-06-04 06:34:00 -04:00
bvandeusen d5437d517e fix(android): single break in extend loop for detekt LoopWithTooManyJumpStatements
android / Build + lint + test (push) Successful in 3m52s
2026-06-04 01:07:57 -04:00
bvandeusen 3085d6f409 fix(android): DIDL-Lite restricted=true / id=-1 so Sonos accepts metadata
android / Build + lint + test (push) Failing after 1m33s
2026-06-04 00:59:31 -04:00
bvandeusen c5b326c620 fix(android): UPnP extend captures SOAP fault, aborts after 3 failures
android / Build + lint + test (push) Has been cancelled
2026-06-04 00:56:48 -04:00
bvandeusen 389c896d65 fix(android): force poll on ON_RESUME + cap interpolation drift at 5s
android / Build + lint + test (push) Has been cancelled
2026-06-04 00:55:10 -04:00
bvandeusen 41230b5afb fix(android): single jump per polling loop for detekt LoopWithTooManyJumpStatements
android / Build + lint + test (push) Successful in 4m0s
2026-06-04 00:42:35 -04:00
bvandeusen c245b1ef0b fix(android): hold UI patch until cursor sync lands; reanchor on track flip
android / Build + lint + test (push) Failing after 1m21s
2026-06-04 00:37:34 -04:00
bvandeusen 2425a305eb fix(android): interpolate UPnP position between polls + suppress post-seek race
android / Build + lint + test (push) Successful in 4m5s
2026-06-04 00:16:57 -04:00
bvandeusen 88b161193d fix(android): TrackRef.durationSec is final fallback so duration is never 0
android / Build + lint + test (push) Successful in 5m54s
2026-06-04 00:03:48 -04:00
bvandeusen 9628ed1749 fix(android): duration falls back to local while Sonos hasn't reported one
android / Build + lint + test (push) Has been cancelled
2026-06-04 00:00:45 -04:00
bvandeusen 85926f4ec0 fix(android): keep service alive when UPnP is playing -- override playWhenReady
android / Build + lint + test (push) Successful in 3m53s
2026-06-03 23:51:54 -04:00
bvandeusen 47b0894ad6 test(android): update RemotePlayerState threshold expectation to 30
android / Build + lint + test (push) Has been cancelled
2026-06-03 23:50:37 -04:00
bvandeusen e62fac3a0e fix(android): onEvents reads UPnP state so resume keeps duration
android / Build + lint + test (push) Has been cancelled
2026-06-03 23:48:52 -04:00
bvandeusen eae5dcad23 fix(android): correct ErrorCopy import path in PlaylistPlayback
android / Build + lint + test (push) Failing after 6m2s
2026-06-03 23:42:18 -04:00
bvandeusen 3576e241c0 fix(android): split playPlaylistShuffled to satisfy detekt ReturnCount
android / Build + lint + test (push) Failing after 3m42s
2026-06-03 23:37:30 -04:00
bvandeusen 8f89279fa4 fix(android): UPnP survives screen-off + cursor catches up to Sonos
android / Build + lint + test (push) Has been cancelled
2026-06-03 23:36:43 -04:00
bvandeusen b1a66f18bd feat(android): shuffle on system playlist tile play -- Home + Playlists list
android / Build + lint + test (push) Failing after 1m30s
2026-06-03 23:29:42 -04:00
bvandeusen 6a7958c921 fix(android): debounce non-PLAYING poll to suppress UPnP track-change flicker
android / Build + lint + test (push) Successful in 4m13s
2026-06-03 23:19:29 -04:00
bvandeusen 33285b53c6 fix(android): refresh uiState.isPlaying from remoteState during UPnP
android / Build + lint + test (push) Successful in 4m1s
2026-06-03 23:08:32 -04:00
bvandeusen 87ad7f4dc2 feat(android): lazy queue activation + loading spinner during UPnP load
android / Build + lint + test (push) Successful in 3m55s
Part A: split loadQueueOnSonos into an initial phase (tracks[0..currentIndex]
only, then SetAV+Seek+Play) plus a background extendQueueOnSonos coroutine
that appends the remainder after activation. Reduces the UPnP activation
block from ~17s (100 tracks serial) to ~200ms (1 track at currentIndex=0).
Background extension cancels cleanly when activeUpnpHolder.active changes.

Part B: add PlayerUiState.isUpnpLoading (target set, active null). Projected
inline in onEvents so it stays consistent with the rest of the snapshot, plus
a separate combine(target, active) collector that updates uiState between
player-event fires. NowPlayingScreen.TransportRow and MiniPlayer.MiniRow
replace the play/pause icon with a CircularProgressIndicator while loading
and disable the button tap to prevent premature commands to the Sonos queue.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:56:06 -04:00
bvandeusen 9c0013f4b6 fix(android): defer holder.active until SOAP wired -- drop transport during load
android / Build + lint + test (push) Successful in 4m34s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:46:57 -04:00
bvandeusen 6129536153 diag(android): log ForwardingPlayer transport overrides on entry
android / Build + lint + test (push) Successful in 3m50s
2026-06-03 22:32:33 -04:00
bvandeusen e6c3c959fa fix(android): handleZeroDurationIfNeeded ReturnCount within cap
android / Build + lint + test (push) Successful in 3m45s
2026-06-03 22:25:53 -04:00
bvandeusen e011b04e04 fix(android): remove pollLoop cursor sync -- races with queue load and SOAP
android / Build + lint + test (push) Has been cancelled
2026-06-03 22:24:34 -04:00
bvandeusen e2866795ef fix(android): skip zero-duration auto-advance during UPnP playback
android / Build + lint + test (push) Failing after 1m26s
2026-06-03 22:18:11 -04:00
bvandeusen e20d7b1438 fix(android): correct SOAPACTION assertions in next/previous tests
android / Build + lint + test (push) Successful in 3m58s
2026-06-03 22:06:54 -04:00
bvandeusen 2a098a78fe feat(android): Sonos queue mode -- ClearQueue + AddURIToQueue + x-rincon-queue
android / Build + lint + test (push) Failing after 9m9s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 21:55:03 -04:00
bvandeusen ece37e9a92 fix(android): remove pollLoop natural-advance -- races with selectUpnp and skip
android / Build + lint + test (push) Successful in 4m1s
Polling alone cannot distinguish Sonos auto-advancing via SetNextAVTransportURI
from URI changes we made ourselves via syncCurrentItemToRemote.  This produced
two races: (1) activation race -- first poll returns stale URI from prior
session, second returns new URI, false-positive fires and double-advances the
cursor; (2) user-skip race -- skip's syncCurrentItemToRemote changes the URI,
next poll sees the change and fires again.  Remove the detection block and
previousTrackUri capture from pollOnce entirely.  pollLoop is now a pure
state-tracker (position + transport state) plus the one-shot initial pre-queue
gate.  GENA event subscriptions to AVTransport LastChange are the correct fix;
deferred to its own slice (see parity-map).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 21:42:01 -04:00
bvandeusen 8a1203c4a1 fix(android): revert x-rincon-mp3radio Sonos URI -- plain https for music tracks
android / Build + lint + test (push) Successful in 4m13s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 21:31:31 -04:00
bvandeusen 487d1bd430 fix(android): SOAP/UPnP parsers enable processNamespaces -- correct name extraction
android / Build + lint + test (push) Successful in 4m4s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 20:58:59 -04:00
bvandeusen 5c99341b34 fix(android): test assertions use JUnit Jupiter for lazy-message Supplier<String>
android / Build + lint + test (push) Failing after 3m31s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 20:51:28 -04:00
bvandeusen 8fe3308afd fix(android): detekt -- parseHhMmSs ReturnCount/MagicNumber + readUntilEndTag jumps
android / Build + lint + test (push) Failing after 3m5s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 20:45:00 -04:00
bvandeusen 96594ba52b fix(android): Sonos ZGT robust extraction -- escaped + nested element fallback + diag
android / Build + lint + test (push) Failing after 1m29s
2026-06-03 20:40:52 -04:00
bvandeusen 2c61d7a333 fix(android): Sonos UDN comparison strips _MR/_MS suffix -- coordinator routing
android / Build + lint + test (push) Failing after 2m41s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 20:29:08 -04:00
bvandeusen 8652b86f40 fix(android): Sonos x-rincon-mp3radio URI transform for SetAVTransportURI
android / Build + lint + test (push) Failing after 1m20s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 20:20:25 -04:00
bvandeusen 75132a2afe diag(android): log raw SOAP response body for first 3 UPnP polls
android / Build + lint + test (push) Failing after 1m22s
Add optional onRawResponse callback to SoapClient; loggingSoapClient
factory emits the first 6 GetPositionInfo/GetTransportInfo bodies
(3 poll cycles) at WARN so release logs capture them. Wire into
transportFor so every AVTransportClient for a new UPnP session logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 20:12:15 -04:00