library: keep display-only artists out of the artist lists

Filter the paged, search and home artist lists to artists referenced by the
plural credits, falling back to the full list while the relation tables are
still empty. Display names keep using the singular tag and navigation keeps
using the plural refs.
This commit is contained in:
austinried
2026-10-18 05:51:55 +00:00
parent 0c2e85134b
commit 4761772b9a
3 changed files with 54 additions and 6 deletions
@@ -93,9 +93,18 @@ private const val DOWNLOADED_ARTIST_ALBUMS =
"WHERE song_artists.sourceId = :sourceId AND song_artists.artistId = artists.id " + "WHERE song_artists.sourceId = :sourceId AND song_artists.artistId = artists.id " +
"AND sd.status = 'Completed' AND songs.albumId IS NOT NULL" "AND sd.status = 'Completed' AND songs.albumId IS NOT NULL"
internal const val ARTIST_CREDITED =
"((NOT EXISTS (SELECT 1 FROM album_artists WHERE sourceId = artists.sourceId) " +
"AND NOT EXISTS (SELECT 1 FROM song_artists WHERE sourceId = artists.sourceId)) " +
"OR EXISTS (SELECT 1 FROM album_artists aa " +
"WHERE aa.sourceId = artists.sourceId AND aa.artistId = artists.id) " +
"OR EXISTS (SELECT 1 FROM song_artists sa " +
"WHERE sa.sourceId = artists.sourceId AND sa.artistId = artists.id))"
internal const val ARTISTS_FILTER = internal const val ARTISTS_FILTER =
"FROM artists WHERE sourceId = :sourceId " + "FROM artists WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " + "AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND $ARTIST_CREDITED " +
"AND (:downloadedFilter = 0 OR EXISTS (" + "AND (:downloadedFilter = 0 OR EXISTS (" +
"SELECT 1 FROM ($ARTIST_DOWNLOAD_ROWS) dl " + "SELECT 1 FROM ($ARTIST_DOWNLOAD_ROWS) dl " +
"WHERE dl.id = artists.id AND dl.status = 'Completed')) " + "WHERE dl.id = artists.id AND dl.status = 'Completed')) " +
@@ -325,7 +334,7 @@ interface LibraryDao {
): Flow<List<Album>> ): Flow<List<Album>>
@Query( @Query(
"SELECT * FROM artists WHERE sourceId = :sourceId AND played IS NOT NULL " + "SELECT * FROM artists WHERE sourceId = :sourceId AND played IS NOT NULL AND $ARTIST_CREDITED " +
"ORDER BY played DESC, sortName COLLATE NOCASE, id LIMIT :limit", "ORDER BY played DESC, sortName COLLATE NOCASE, id LIMIT :limit",
) )
fun recentlyPlayedArtists( fun recentlyPlayedArtists(
@@ -343,7 +352,7 @@ interface LibraryDao {
): Flow<List<Album>> ): Flow<List<Album>>
@Query( @Query(
"SELECT * FROM artists WHERE sourceId = :sourceId AND playCount > 0 " + "SELECT * FROM artists WHERE sourceId = :sourceId AND playCount > 0 AND $ARTIST_CREDITED " +
"ORDER BY playCount DESC, sortName COLLATE NOCASE, id LIMIT :limit", "ORDER BY playCount DESC, sortName COLLATE NOCASE, id LIMIT :limit",
) )
fun mostPlayedArtists( fun mostPlayedArtists(
@@ -392,7 +401,7 @@ interface LibraryDao {
): Flow<List<Album>> ): Flow<List<Album>>
@Query( @Query(
"SELECT * FROM artists WHERE sourceId = :sourceId AND $ARTIST_SEARCH_FILTER " + "SELECT * FROM artists WHERE sourceId = :sourceId AND $ARTIST_SEARCH_FILTER AND $ARTIST_CREDITED " +
"ORDER BY sortName COLLATE NOCASE, id LIMIT :limit", "ORDER BY sortName COLLATE NOCASE, id LIMIT :limit",
) )
fun searchArtists( fun searchArtists(
@@ -803,10 +812,16 @@ interface LibraryDao {
@Query("SELECT * FROM albums WHERE sourceId = :sourceId ORDER BY created DESC, sortName COLLATE NOCASE, id") @Query("SELECT * FROM albums WHERE sourceId = :sourceId ORDER BY created DESC, sortName COLLATE NOCASE, id")
fun homeRecentlyAddedAlbums(sourceId: Long): PagingSource<Int, Album> fun homeRecentlyAddedAlbums(sourceId: Long): PagingSource<Int, Album>
@Query("SELECT * FROM artists WHERE sourceId = :sourceId AND played IS NOT NULL ORDER BY played DESC, sortName COLLATE NOCASE, id") @Query(
"SELECT * FROM artists WHERE sourceId = :sourceId AND played IS NOT NULL AND $ARTIST_CREDITED " +
"ORDER BY played DESC, sortName COLLATE NOCASE, id",
)
fun homeRecentlyPlayedArtists(sourceId: Long): PagingSource<Int, Artist> fun homeRecentlyPlayedArtists(sourceId: Long): PagingSource<Int, Artist>
@Query("SELECT * FROM artists WHERE sourceId = :sourceId AND playCount > 0 ORDER BY playCount DESC, sortName COLLATE NOCASE, id") @Query(
"SELECT * FROM artists WHERE sourceId = :sourceId AND playCount > 0 AND $ARTIST_CREDITED " +
"ORDER BY playCount DESC, sortName COLLATE NOCASE, id",
)
fun homeMostPlayedArtists(sourceId: Long): PagingSource<Int, Artist> fun homeMostPlayedArtists(sourceId: Long): PagingSource<Int, Artist>
@Query( @Query(
@@ -22,6 +22,7 @@ internal const val ALBUMS_FILTER_BASE =
internal const val ARTISTS_FILTER_BASE = internal const val ARTISTS_FILTER_BASE =
"FROM artists WHERE sourceId = ? " + "FROM artists WHERE sourceId = ? " +
"AND (? = 0 OR (? = 1 AND starred IS NOT NULL) OR (? = 2 AND starred IS NULL)) " + "AND (? = 0 OR (? = 1 AND starred IS NOT NULL) OR (? = 2 AND starred IS NULL)) " +
"AND $ARTIST_CREDITED " +
"AND (? = '' " + "AND (? = '' " +
"OR (length(?) >= 3 AND rowid IN (SELECT rowid FROM artist_search " + "OR (length(?) >= 3 AND rowid IN (SELECT rowid FROM artist_search " +
"WHERE artist_search MATCH '\"' || replace(?, '\"', '\"\"') || '\"')) " + "WHERE artist_search MATCH '\"' || replace(?, '\"', '\"\"') || '\"')) " +
@@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.After import org.junit.After
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -58,7 +59,7 @@ class SyncServiceTest {
FakeMusicSource( FakeMusicSource(
artists = listOf(artist("a1"), artist("a2")), artists = listOf(artist("a1"), artist("a2")),
albums = listOf(album("al1")), albums = listOf(album("al1")),
songs = listOf(song("s1"), song("s2")), songs = listOf(song("s1"), song("s2").copy(artistId = "a2")),
playlists = listOf(playlist("p1")), playlists = listOf(playlist("p1")),
playlistSongs = listOf(PlaylistSong(1, "p1", "s1", 0)), playlistSongs = listOf(PlaylistSong(1, "p1", "s1", 0)),
) )
@@ -1064,6 +1065,37 @@ class SyncServiceTest {
assertEquals("Zulu", db.libraryDao().songOnce(1, "s1")!!.artistSort) assertEquals("Zulu", db.libraryDao().songOnce(1, "s1")!!.artistSort)
} }
@Test
fun theArtistsListHidesNamesThatAreOnlyDisplayStrings() =
runTest {
insertSource()
val source =
FakeMusicSource(
artists = listOf(artist("a1", "Alpha"), artist("combo", "Alpha & Beta")),
albums = listOf(album("al1")),
songs = listOf(song("s1")),
)
SyncService(db, source).sync()
assertEquals(
listOf("a1"),
db
.libraryDao()
.artistsByName(1, 0, "")
.allRows()
.map { it.id },
)
assertEquals(
listOf("a1"),
db
.libraryDao()
.searchArtists(1, "alpha", 10)
.first()
.map { it.id },
)
}
@Test @Test
fun everyAlbumArtistAndTrackArtistReachesTheSharedAlbum() = fun everyAlbumArtistAndTrackArtistReachesTheSharedAlbum() =
runTest { runTest {