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
2 changed files with 27 additions and 4 deletions
Showing only changes of commit 283706c4f8 - Show all commits
@@ -1,5 +1,6 @@
package com.fabledsword.minstrel.library.widgets
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -49,10 +50,11 @@ fun AlbumCard(
Box(
modifier = Modifier
.size(144.dp)
.clip(RoundedCornerShape(FabledSwordFlatTokens.radiusSm)),
.clip(RoundedCornerShape(FabledSwordFlatTokens.radiusSm))
.background(MaterialTheme.colorScheme.surfaceVariant),
contentAlignment = Alignment.Center,
) {
if (album.coverUrl.isEmpty()) {
if (album.id.isEmpty()) {
Icon(
imageVector = Lucide.Disc3,
contentDescription = null,
@@ -60,8 +62,15 @@ fun AlbumCard(
modifier = Modifier.size(56.dp),
)
} else {
// Use displayCoverUrl so cached-only albums (which
// drop coverUrl in the entity→domain mapper) still
// hit /api/albums/{id}/cover via the BaseUrlInterceptor
// placeholder rewrite. On load failure (album with
// no cover server-side) the Box's surfaceVariant
// background shows through — a Disc3 error-slot
// fallback is a future refinement.
AsyncImage(
model = album.coverUrl,
model = album.displayCoverUrl,
contentDescription = album.title,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
@@ -19,4 +19,18 @@ data class AlbumRef(
val trackCount: Int = 0,
val durationSec: Int = 0,
val coverUrl: String = "",
)
) {
/**
* Cover URL the UI should render. Returns the server-provided
* `coverUrl` when populated (regular API responses always carry it),
* otherwise derives `/api/albums/{id}/cover` via the placeholder
* host that `BaseUrlInterceptor` rewrites to the live server URL.
*
* The fallback path matters because the cached_albums entity →
* AlbumRef mapper drops coverUrl — without this property,
* AlbumCards rendered from cache (Library Albums tab, ArtistDetail
* grid, Home rows on cold-start) would show no artwork at all.
*/
val displayCoverUrl: String
get() = coverUrl.ifEmpty { "http://placeholder.invalid/api/albums/$id/cover" }
}