fix(android): show artist images in Apple-Music search (Erkunden)
iTunes artist entities carry no artwork, so search results rendered as bare grey circles. Mirror the web PWA: after showing results, fetch one album per artist and borrow its cover (entity=album, limit=1), applied only if the user is still viewing the same artist set. Also give ArtistCard a Person-icon fallback instead of an empty grey dot, and fall back to the artist id for Subsonic (library) artist covers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
03bc736725
commit
03046cd631
2 files changed
+49
-7
No files matched your search
@@ -283,7 +283,9 @@ private fun LibrarySearchContent(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
items(artists, key = { it.id }) { a ->
|
items(artists, key = { it.id }) { a ->
|
||||||
ArtistCard(name = a.name, imageUrl = coverUrl(a.coverArt)) { onArtistClick(a) }
|
// search3 artist results usually omit coverArt → fall back to the artist
|
||||||
|
// id, which Navidrome's getCoverArt accepts and resolves to the artist image.
|
||||||
|
ArtistCard(name = a.name, imageUrl = coverUrl(a.coverArt ?: a.id)) { onArtistClick(a) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
@@ -597,15 +599,27 @@ private fun ArtistCard(name: String, imageUrl: String?, onClick: () -> Unit) {
|
|||||||
.width(80.dp)
|
.width(80.dp)
|
||||||
.clickable(onClick = onClick),
|
.clickable(onClick = onClick),
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
Box(
|
||||||
model = imageUrl,
|
contentAlignment = Alignment.Center,
|
||||||
contentDescription = name,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(72.dp)
|
.size(72.dp)
|
||||||
.clip(androidx.compose.foundation.shape.CircleShape)
|
.clip(androidx.compose.foundation.shape.CircleShape)
|
||||||
.background(NsSurface2),
|
.background(NsSurface2),
|
||||||
)
|
) {
|
||||||
|
if (imageUrl != null) {
|
||||||
|
AsyncImage(
|
||||||
|
model = imageUrl,
|
||||||
|
contentDescription = name,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Person, contentDescription = null,
|
||||||
|
tint = NsOnSurface2, modifier = Modifier.size(32.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Spacer(Modifier.height(6.dp))
|
Spacer(Modifier.height(6.dp))
|
||||||
Text(
|
Text(
|
||||||
name, fontFamily = Outfit, fontSize = 12.sp, fontWeight = FontWeight.Medium,
|
name, fontFamily = Outfit, fontSize = 12.sp, fontWeight = FontWeight.Medium,
|
||||||
|
|||||||
@@ -110,18 +110,46 @@ class ExploreViewModel(app: Application) : AndroidViewModel(app) {
|
|||||||
try {
|
try {
|
||||||
val resp = ItunesClient.search(term)
|
val resp = ItunesClient.search(term)
|
||||||
val results = resp.results
|
val results = resp.results
|
||||||
|
val artists = results.filter { it.wrapperType == "artist" }
|
||||||
_state.value = _state.value.copy(
|
_state.value = _state.value.copy(
|
||||||
artists = results.filter { it.wrapperType == "artist" },
|
artists = artists,
|
||||||
albums = results.filter { it.wrapperType == "collection" },
|
albums = results.filter { it.wrapperType == "collection" },
|
||||||
tracks = results.filter { it.wrapperType == "track" && it.kind == "song" },
|
tracks = results.filter { it.wrapperType == "track" && it.kind == "song" },
|
||||||
isSearching = false,
|
isSearching = false,
|
||||||
)
|
)
|
||||||
|
enrichArtistArt(artists)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_state.value = _state.value.copy(isSearching = false, error = e.message)
|
_state.value = _state.value.copy(isSearching = false, error = e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iTunes artist entities carry no artwork, so search results render as grey circles. Mirror the
|
||||||
|
* web PWA: fetch one album per artist and borrow its cover. Runs after the main results are shown
|
||||||
|
* (list appears instantly) and only applies if the user is still viewing the same artist set.
|
||||||
|
*/
|
||||||
|
private fun enrichArtistArt(artists: List<ItunesItem>) {
|
||||||
|
if (artists.none { it.artworkUrl100.isNullOrBlank() && it.artistName != null }) return
|
||||||
|
viewModelScope.launch {
|
||||||
|
val enriched = artists.map { a ->
|
||||||
|
async {
|
||||||
|
if (!a.artworkUrl100.isNullOrBlank()) return@async a
|
||||||
|
val name = a.artistName ?: return@async a
|
||||||
|
val art = runCatching {
|
||||||
|
ItunesClient.search(name, entity = "album", limit = 1).results
|
||||||
|
.firstOrNull { it.wrapperType == "collection" }?.artworkUrl100
|
||||||
|
}.getOrNull()
|
||||||
|
if (art != null) a.copy(artworkUrl100 = art) else a
|
||||||
|
}
|
||||||
|
}.awaitAll()
|
||||||
|
val cur = _state.value.artists
|
||||||
|
if (cur.map { it.artistId } == artists.map { it.artistId }) {
|
||||||
|
_state.value = _state.value.copy(artists = enriched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun loadAlbum(album: ItunesItem) {
|
fun loadAlbum(album: ItunesItem) {
|
||||||
val id = album.collectionId ?: return
|
val id = album.collectionId ?: return
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|||||||
Reference in new issue
Block a user