From c720dff310bbc5f7aa2bed91e18ba48975ad3070 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 30 May 2026 00:59:07 -0400 Subject: [PATCH] fix(android): resolve relative stream_url before handing to Media3 Server emits stream_url as a relative path (/api/tracks/{id}/stream, internal/api/convert.go:75). PlayerController.toMediaItem passed it raw to setUri, so OkHttpDataSource saw a host-less URI and silently failed -- queue UI loaded but no audio played. Route streamUrl through the same resolver used for covers (resolveServerImageUrl), which prepends the placeholder.invalid host that BaseUrlInterceptor rewrites to the live server with the auth cookie. The setCustomCacheKey(id) still keys the SimpleCache by trackId, so cache residency is unaffected by the URL form. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../com/fabledsword/minstrel/player/PlayerController.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/fabledsword/minstrel/player/PlayerController.kt b/android/app/src/main/java/com/fabledsword/minstrel/player/PlayerController.kt index 2e9504b7..c9523274 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/player/PlayerController.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/player/PlayerController.kt @@ -10,6 +10,7 @@ import androidx.media3.session.MediaController import androidx.media3.session.SessionToken import com.fabledsword.minstrel.di.ApplicationScope import com.fabledsword.minstrel.models.TrackRef +import com.fabledsword.minstrel.shared.widgets.resolveServerImageUrl import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel @@ -272,9 +273,15 @@ class PlayerController @Inject constructor( if (source != null) setExtras(sourceExtras(source)) } .build() + // Server's stream_url is a relative path (/api/tracks/{id}/stream); + // resolve to the placeholder.invalid form so OkHttpDataSource (sharing + // BaseUrlInterceptor) rewrites it to the live server with the auth + // cookie. Same resolution as covers — naming "ServerImage" is + // misleading and flagged for DRY follow-up. + val resolvedUri = resolveServerImageUrl(streamUrl) ?: streamUrl return MediaItem.Builder() .setMediaId(id) - .setUri(streamUrl) + .setUri(resolvedUri) // Key the Media3 SimpleCache by trackId (not the stream URL) so // cache residency can be queried per track for the cached dot + // offline pools, independent of URL/host rewrites.