fix(android): UPnP forwarding -- route mint failures through drop, no double drop
android / Build + lint + test (push) Failing after 1m25s
android / Build + lint + test (push) Failing after 1m25s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+20
-7
@@ -101,18 +101,30 @@ class MinstrelForwardingPlayer(
|
|||||||
|
|
||||||
override fun seekToNextMediaItem() {
|
override fun seekToNextMediaItem() {
|
||||||
val active = holder.active.value
|
val active = holder.active.value
|
||||||
super.seekToNextMediaItem()
|
if (active == null) {
|
||||||
if (active != null) {
|
super.seekToNextMediaItem()
|
||||||
scope.launch { syncCurrentItemToRemote(active) }
|
return
|
||||||
}
|
}
|
||||||
|
// Advance the local cursor first so syncCurrentItemToRemote can read
|
||||||
|
// the new currentMediaItem on the application looper. ExoPlayer stays
|
||||||
|
// paused while UPnP is active (playWhenReady=false invariant), so this
|
||||||
|
// does not produce local audio.
|
||||||
|
super.seekToNextMediaItem()
|
||||||
|
scope.launch { syncCurrentItemToRemote(active) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun seekToPreviousMediaItem() {
|
override fun seekToPreviousMediaItem() {
|
||||||
val active = holder.active.value
|
val active = holder.active.value
|
||||||
super.seekToPreviousMediaItem()
|
if (active == null) {
|
||||||
if (active != null) {
|
super.seekToPreviousMediaItem()
|
||||||
scope.launch { syncCurrentItemToRemote(active) }
|
return
|
||||||
}
|
}
|
||||||
|
// Advance the local cursor first so syncCurrentItemToRemote can read
|
||||||
|
// the new currentMediaItem on the application looper. ExoPlayer stays
|
||||||
|
// paused while UPnP is active (playWhenReady=false invariant), so this
|
||||||
|
// does not produce local audio.
|
||||||
|
super.seekToPreviousMediaItem()
|
||||||
|
scope.launch { syncCurrentItemToRemote(active) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCurrentPosition(): Long =
|
override fun getCurrentPosition(): Long =
|
||||||
@@ -135,8 +147,8 @@ class MinstrelForwardingPlayer(
|
|||||||
|
|
||||||
private suspend fun syncCurrentItemToRemote(active: ActiveUpnp) {
|
private suspend fun syncCurrentItemToRemote(active: ActiveUpnp) {
|
||||||
val mediaId = handlerEvaluate { delegate.currentMediaItem?.mediaId } ?: return
|
val mediaId = handlerEvaluate { delegate.currentMediaItem?.mediaId } ?: return
|
||||||
val token = runCatching { tokens.mint(mediaId) }.getOrNull() ?: return
|
|
||||||
runCatching {
|
runCatching {
|
||||||
|
val token = tokens.mint(mediaId)
|
||||||
active.avTransport.setAVTransportURIWithMetadata(
|
active.avTransport.setAVTransportURIWithMetadata(
|
||||||
uri = token.url,
|
uri = token.url,
|
||||||
mime = token.mime,
|
mime = token.mime,
|
||||||
@@ -148,6 +160,7 @@ class MinstrelForwardingPlayer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSoapFailure(active: ActiveUpnp, t: Throwable) {
|
private fun handleSoapFailure(active: ActiveUpnp, t: Throwable) {
|
||||||
|
pollJob?.cancel()
|
||||||
Timber.w(t, "UPnP transport call failed on %s", active.routeName)
|
Timber.w(t, "UPnP transport call failed on %s", active.routeName)
|
||||||
remoteState.applyError(t)
|
remoteState.applyError(t)
|
||||||
handler.post { onDrop(active.routeName) }
|
handler.post { onDrop(active.routeName) }
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class RemotePlayerState {
|
|||||||
@Volatile var currentTrackUri: String = ""; private set
|
@Volatile var currentTrackUri: String = ""; private set
|
||||||
@Volatile var lastError: Throwable? = null; private set
|
@Volatile var lastError: Throwable? = null; private set
|
||||||
|
|
||||||
private var consecutivePollFailures: Int = 0
|
@Volatile private var consecutivePollFailures: Int = 0
|
||||||
|
|
||||||
fun applyPositionInfo(positionMs: Long, durationMs: Long, trackUri: String) {
|
fun applyPositionInfo(positionMs: Long, durationMs: Long, trackUri: String) {
|
||||||
this.positionMs = positionMs
|
this.positionMs = positionMs
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ data class RouteSnapshot(
|
|||||||
* - [OutputRoute.Protocol.SYSTEM] — MediaRouter.selectRoute (built-in,
|
* - [OutputRoute.Protocol.SYSTEM] — MediaRouter.selectRoute (built-in,
|
||||||
* wired, Bluetooth)
|
* wired, Bluetooth)
|
||||||
* - [OutputRoute.Protocol.UPNP] — mint a signed stream token via
|
* - [OutputRoute.Protocol.UPNP] — mint a signed stream token via
|
||||||
* [CastApi.streamToken], drive the discovered renderer with
|
* [StreamTokenProvider.mint], drive the discovered renderer with
|
||||||
* AVTransport.SetAVTransportURI + Play, pause local playback so
|
* AVTransport.SetAVTransportURI + Play, pause local playback so
|
||||||
* audio yields to the network speaker
|
* audio yields to the network speaker
|
||||||
* - [OutputRoute.Protocol.CAST] / [OutputRoute.Protocol.SONOS] —
|
* - [OutputRoute.Protocol.CAST] / [OutputRoute.Protocol.SONOS] —
|
||||||
|
|||||||
Reference in New Issue
Block a user