artists: browse and download the tracks a guest artist is credited on

A song's artist can differ from its album artist (compilations, guest
tracks). The artist page was built only from albums where the artist is
the album artist, so opening such an artist showed a blank page. Attribute
a song to both its album artist and its credited track artist across the
artist album list, shuffle queue, download status and the artists list
filter/badges, and show the listed album count on the detail page.
This commit is contained in:
austinried
2026-10-04 01:49:55 +00:00
parent d1ec74ad1e
commit 2b08ae3b63
6 changed files with 205 additions and 33 deletions
@@ -491,6 +491,73 @@ class LibraryDaoTest {
)
}
@Test
fun anArtistPageListsAlbumsWhereTheArtistIsOnlyACreditedTrack() =
runTest {
val sourceId = source()
val dao = db.libraryDao()
dao.upsertAlbums(listOf(album(sourceId, "al-1", "Compilation", year = null, starred = null)))
dao.upsertSongs(
listOf(
song(sourceId, "s1", "One", starred = null),
song(sourceId, "s2", "Two", starred = null).copy(artistId = "ar-guest"),
),
)
assertEquals(listOf("al-1"), dao.albumsForArtist(sourceId, "ar-1").first().map { it.id })
assertEquals(listOf("al-1"), dao.albumsForArtist(sourceId, "ar-guest").first().map { it.id })
db.downloadDao().upsert(SongDownload(sourceId, "s1", DownloadStatus.Completed))
assertEquals(emptyList<String>(), dao.albumsForArtistDownloaded(sourceId, "ar-guest").first().map { it.id })
db.downloadDao().upsert(SongDownload(sourceId, "s2", DownloadStatus.Completed))
assertEquals(listOf("al-1"), dao.albumsForArtistDownloaded(sourceId, "ar-guest").first().map { it.id })
}
@Test
fun downloadedArtistsIncludeOneCreditedOnlyOnADownloadedTrack() =
runTest {
val sourceId = source()
val dao = db.libraryDao()
dao.upsertArtists(
listOf(
artist(sourceId, "ar-1", "Album Artist", albumCount = 1),
artist(sourceId, "ar-guest", "Guest", albumCount = 0),
),
)
dao.upsertAlbums(listOf(album(sourceId, "al-1", "Compilation", year = null, starred = null)))
dao.upsertSongs(
listOf(
song(sourceId, "s1", "One", starred = null),
song(sourceId, "s2", "Two", starred = null).copy(artistId = "ar-guest"),
),
)
db.downloadDao().upsert(SongDownload(sourceId, "s1", DownloadStatus.Completed))
assertEquals(
listOf("Album Artist"),
dao.artistsByName(sourceId, 0, "", downloadedFilter = 1).page().map { it.name },
)
assertEquals(
listOf("Album Artist"),
dao.downloadedArtists(sourceId).page().map { it.name },
)
db.downloadDao().upsert(SongDownload(sourceId, "s2", DownloadStatus.Completed))
val filtered = dao.artistsByName(sourceId, 0, "", downloadedFilter = 1).page()
assertEquals(listOf("Album Artist", "Guest"), filtered.map { it.name })
assertEquals(listOf(1L, 1L), filtered.map { it.albumCount })
assertEquals(
setOf("Album Artist", "Guest"),
dao
.downloadedArtists(sourceId)
.page()
.map { it.name }
.toSet(),
)
}
@Test
fun downloadedFilterReportsTheDownloadedAlbumCount() =
runTest {
@@ -623,6 +623,76 @@ class DownloadRepositoryTest {
assertEquals(listOf("s1", "s2"), requestedSongIds())
}
@Test
fun aGuestArtistCountsAndDownloadsOnlyTheTracksCreditedToThem() =
runTest {
seedLibrary()
runBlocking {
db.libraryDao().upsertSongs(
listOf(
Song(
sourceId = 1,
id = "s2",
albumId = "al1",
artistId = "ar-guest",
title = "Song 2",
album = "Album",
artist = "Guest",
duration = 100,
track = 2,
disc = 1,
starred = null,
genre = null,
),
),
)
db.libraryDao().upsertArtists(
listOf(Artist(sourceId = 1, id = "ar-guest", name = "Guest", albumCount = 0, starred = null, coverArt = null)),
)
}
assertEquals(ListDownloadStatus(total = 1, downloaded = 0, downloading = 0), statusOf(DownloadList.Artist, "ar-guest"))
runBlocking { repository.downloadAll(1, DownloadList.Artist, "ar-guest") }
assertEquals(listOf("s2"), requestedSongIds())
}
@Test
fun theArtistListBadgesCountADownloadedGuestTrackForTheGuest() =
runTest {
seedLibrary()
runBlocking {
db.libraryDao().upsertSongs(
listOf(
Song(
sourceId = 1,
id = "s2",
albumId = "al1",
artistId = "ar-guest",
title = "Song 2",
album = "Album",
artist = "Guest",
duration = 100,
track = 2,
disc = 1,
starred = null,
genre = null,
),
),
)
db.libraryDao().upsertArtists(
listOf(Artist(sourceId = 1, id = "ar-guest", name = "Guest", albumCount = 0, starred = null, coverArt = null)),
)
db.downloadDao().upsert(SongDownload(1, "s2", DownloadStatus.Completed))
}
val artists = runBlocking { repository.statuses(1, DownloadList.Artist).first() }
assertEquals(ListDownloadStatus(total = 2, downloaded = 1), artists["ar1"])
assertEquals(ListDownloadStatus(total = 1, downloaded = 1), artists["ar-guest"])
}
@Test
fun cancellingWhileAListIsStillQueuingRemovesTheWholeList() =
runTest {