feat: M3 weighted shuffle v1 — real /api/radio with scoring #21

Merged
bvandeusen merged 262 commits from dev into main 2026-04-27 11:00:29 -04:00
Showing only changes of commit c720dff310 - Show all commits
@@ -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.