library: keep artist paging off song and album writes

The fallback art reads albums and credits, but a successful sync invalidates through the sources row, so observing those tables only adds refreshes for play, star, and sync writes that do not change artist art.
This commit is contained in:
austinried
2026-10-19 12:39:01 +00:00
parent bb71f4889d
commit 4b3a7d1dfa
2 changed files with 3 additions and 65 deletions
@@ -10,7 +10,6 @@ import androidx.room3.Transaction
import androidx.room3.Upsert
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
import com.subtracks.data.model.Album
import com.subtracks.data.model.AlbumArtistRef
import com.subtracks.data.model.AlbumSongItem
import com.subtracks.data.model.Artist
import com.subtracks.data.model.Disc
@@ -19,7 +18,6 @@ import com.subtracks.data.model.Playlist
import com.subtracks.data.model.PlaylistSong
import com.subtracks.data.model.PlaylistSongItem
import com.subtracks.data.model.Song
import com.subtracks.data.model.SongArtistRef
import com.subtracks.data.model.Source
import kotlinx.coroutines.flow.Flow
@@ -643,16 +641,9 @@ interface LibraryDao {
@RawQuery(observedEntities = [Album::class])
fun albumsPaging(query: RoomRawQuery): PagingSource<Int, Album>
@RawQuery(
observedEntities = [
Artist::class,
Source::class,
Album::class,
AlbumArtistRef::class,
Song::class,
SongArtistRef::class,
],
)
// The fallback art reads albums and credits, but a successful sync writes sources after the
// library changes, so observing artists and sources avoids invalidating on every song/album write.
@RawQuery(observedEntities = [Artist::class, Source::class])
fun artistsPaging(query: RoomRawQuery): PagingSource<Int, Artist>
@RawQuery(observedEntities = [Playlist::class])
@@ -652,59 +652,6 @@ class LibraryDaoTest {
)
}
@Test
fun artistArtChangesInvalidateTheArtistPagingSource() =
runTest {
val sourceId = source()
val dao = db.libraryDao()
dao.upsertArtists(listOf(artist(sourceId, "ar-1", "Artist", albumCount = 1)))
dao.upsertAlbums(listOf(album(sourceId, "al-1", "Album", year = null, starred = null)))
val albumInvalidated = AtomicBoolean(false)
val albumPaging = dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.Name, false))
albumPaging.registerInvalidatedCallback { albumInvalidated.set(true) }
albumPaging.page()
dao.upsertAlbums(
listOf(album(sourceId, "al-1", "Album", year = null, starred = null).copy(coverArt = "cover")),
)
awaitUntil("artist paging observes album writes") { albumInvalidated.get() }
val songInvalidated = AtomicBoolean(false)
val songPaging = dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.Name, false))
songPaging.registerInvalidatedCallback { songInvalidated.set(true) }
songPaging.page()
dao.upsertSongs(listOf(song(sourceId, "s-1", "Song", starred = null)))
awaitUntil("artist paging observes song writes") { songInvalidated.get() }
val albumCreditInvalidated = AtomicBoolean(false)
val albumCreditPaging = dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.Name, false))
albumCreditPaging.registerInvalidatedCallback { albumCreditInvalidated.set(true) }
albumCreditPaging.page()
db.useWriterConnection { connection ->
connection.usePrepared(
"INSERT INTO album_artists (sourceId, albumId, position, artistId, name) " +
"VALUES ($sourceId, 'al-1', 0, 'ar-1', 'Artist')",
) { it.step() }
}
awaitUntil("artist paging observes album artist writes") { albumCreditInvalidated.get() }
val songCreditInvalidated = AtomicBoolean(false)
val songCreditPaging = dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.Name, false))
songCreditPaging.registerInvalidatedCallback { songCreditInvalidated.set(true) }
songCreditPaging.page()
db.useWriterConnection { connection ->
connection.usePrepared(
"INSERT INTO song_artists (sourceId, songId, position, artistId, name) " +
"VALUES ($sourceId, 's-1', 0, 'ar-1', 'Artist')",
) { it.step() }
}
awaitUntil("artist paging observes song artist writes") { songCreditInvalidated.get() }
}
@Test
fun albumCreditIdsFindCreditsThroughArtistIndexes() =
runTest {