library: address artist art review findings

Cap the resolver at four covers, re-rank after deduplication so duplicate covers do not consume the grid budget, carry a thumbnail per fallback step, and advance to the grid when both the full image and its thumbnail fail. Negative-cache failed provider-image fetches per artist URL, clear materialized downloaded covers when all downloads are removed, and drop redundant plumbing.
This commit is contained in:
austinried
2026-10-20 09:11:20 +00:00
parent 18118492c7
commit 783d7f2ebc
10 changed files with 109 additions and 45 deletions
@@ -682,6 +682,27 @@ class LibraryDaoTest {
assertEquals(listOf("art-2", "art-1"), dao.artistOnce(sourceId, "ar-1")!!.downloadedCoverArts)
}
@Test
fun artistArtDeduplicatesCoversBeforeApplyingTheFourCoverBudget() =
runTest {
val sourceId = source()
val dao = db.libraryDao()
dao.upsertArtists(listOf(artist(sourceId, "ar-1", "Artist", albumCount = 6)))
dao.upsertAlbums(
listOf("A", "A", "O", "B", "C", "D").mapIndexed { index, cover ->
album(sourceId, "al-$index", "Album $index", year = 2021L - index, starred = null)
.copy(artistId = "ar-1", coverArt = cover)
},
)
dao.recomputeArtistArt(sourceId)
assertEquals(
listOf("A", "O", "B", "C"),
dao.artistOnce(sourceId, "ar-1")!!.coverArts,
)
}
@Test
fun artistArtMaterializationUsesTheCreditIndexes() =
runTest {
@@ -346,7 +346,10 @@ class DownloadRepositoryTest {
runTest {
seedLibrary()
writeFile(1, "s1", "audio")
runBlocking { repository.download(1, "s1") }
runBlocking {
repository.download(1, "s1")
db.libraryDao().upsertArtists(listOf(artist(ARTIST_ART).copy(downloadedCoverArts = listOf(ALBUM_ART))))
}
val id = engine.requests.single().first
runBlocking { repository.removeSource(1) }
@@ -354,6 +357,13 @@ class DownloadRepositoryTest {
assertTrue(engine.cancelled.contains(id))
assertFalse(file(1, "s1").exists())
assertTrue(allRows().isEmpty())
assertTrue(
db
.libraryDao()
.artistOnce(1, "ar1")!!
.downloadedCoverArts
.isEmpty(),
)
}
@Test
@@ -334,11 +334,14 @@ class AppScreenshotTest {
),
art = CoverArtRef("art-al-kid-a", "test:art-al-kid-a"),
artThumbnail = CoverArtRef("art-al-kid-a", "test:art-al-kid-a"),
artCovers =
Fixtures.albums
.take(3)
.mapNotNull { it.coverArt }
.map { CoverArtRef(it, "test:$it") },
heroArt =
ArtistArt(
covers =
Fixtures.albums
.take(3)
.mapNotNull { it.coverArt }
.map { CoverArtRef(it, "test:$it") },
),
artwork = artworkColorsFromSeed(Color.rgb(120, 80, 200)),
coverArt = { id, _ -> id?.let { CoverArtRef(it, "test:$it") } },
onBack = {},
@@ -76,8 +76,16 @@ class ArtistArtTest {
}
val artistImage: (String, String?) -> CoverArtRef? = { _, _ -> null }
resolveArtistArt(artist(coverArts = listOf("a")), coverArt, artistImage, offline = false, forIcon = false)
assertEquals(listOf(false), thumbnails)
val single =
resolveArtistArt(
artist(coverArts = listOf("a")),
coverArt,
artistImage,
offline = false,
forIcon = false,
)
assertEquals(listOf(true, false), thumbnails)
assertEquals("a", single.thumbnail?.url)
thumbnails.clear()
resolveArtistArt(artist(coverArts = listOf("a", "b")), coverArt, artistImage, offline = false, forIcon = false)