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) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 00:59:07 -04:00
parent 6967d62c09
commit c720dff310
@@ -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.