fix(scanner): remove stale role associations when artist role changes. Fix #4242

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan
2025-12-16 06:38:50 -05:00
parent c7ac0e4414
commit 2d7b716834
2 changed files with 68 additions and 2 deletions

View File

@@ -512,6 +512,70 @@ var _ = Describe("AlbumRepository", func() {
// Clean up the test album created for this test // Clean up the test album created for this test
_, _ = albumRepo.executeSQL(squirrel.Delete("album").Where(squirrel.Eq{"id": album.ID})) _, _ = albumRepo.executeSQL(squirrel.Delete("album").Where(squirrel.Eq{"id": album.ID}))
}) })
It("removes stale role associations when artist role changes", func() {
// Regression test for issue #4242: Composers displayed in albumartist list
// This happens when an artist's role changes (e.g., was both albumartist and composer,
// now only composer) and the old role association isn't properly removed.
// Create an artist that will have changing roles
artist := &model.Artist{
ID: "role-change-artist-1",
Name: "Role Change Artist",
OrderArtistName: "role change artist",
}
err := createArtistWithLibrary(artistRepo, artist, 1)
Expect(err).ToNot(HaveOccurred())
// Create album with artist as both albumartist and composer
album := &model.Album{
LibraryID: 1,
ID: "test-album-role-change",
Name: "Test Album Role Change",
AlbumArtistID: "role-change-artist-1",
AlbumArtist: "Role Change Artist",
Participants: model.Participants{
model.RoleAlbumArtist: {
{Artist: model.Artist{ID: "role-change-artist-1", Name: "Role Change Artist"}},
},
model.RoleComposer: {
{Artist: model.Artist{ID: "role-change-artist-1", Name: "Role Change Artist"}},
},
},
}
err = albumRepo.Put(album)
Expect(err).ToNot(HaveOccurred())
// Verify initial state: artist has both albumartist and composer roles
expected := []albumArtistRecord{
{ArtistID: "role-change-artist-1", Role: "albumartist", SubRole: ""},
{ArtistID: "role-change-artist-1", Role: "composer", SubRole: ""},
}
verifyAlbumArtists(album.ID, expected)
// Now update album so artist is ONLY a composer (remove albumartist role)
album.Participants = model.Participants{
model.RoleComposer: {
{Artist: model.Artist{ID: "role-change-artist-1", Name: "Role Change Artist"}},
},
}
err = albumRepo.Put(album)
Expect(err).ToNot(HaveOccurred())
// Verify that the albumartist role was removed - only composer should remain
// This is the key test: before the fix, the albumartist role would remain
// causing composers to appear in the albumartist filter
expectedAfter := []albumArtistRecord{
{ArtistID: "role-change-artist-1", Role: "composer", SubRole: ""},
}
verifyAlbumArtists(album.ID, expectedAfter)
// Clean up
_, _ = artistRepo.executeSQL(squirrel.Delete("artist").Where(squirrel.Eq{"id": artist.ID}))
_, _ = albumRepo.executeSQL(squirrel.Delete("album").Where(squirrel.Eq{"id": album.ID}))
})
}) })
}) })

View File

@@ -51,8 +51,10 @@ func unmarshalParticipants(data string) (model.Participants, error) {
} }
func (r sqlRepository) updateParticipants(itemID string, participants model.Participants) error { func (r sqlRepository) updateParticipants(itemID string, participants model.Participants) error {
ids := participants.AllIDs() // Delete all existing participant entries for this item.
sqd := Delete(r.tableName + "_artists").Where(And{Eq{r.tableName + "_id": itemID}, NotEq{"artist_id": ids}}) // This ensures stale role associations are removed when an artist's role changes
// (e.g., an artist was both albumartist and composer, but is now only composer).
sqd := Delete(r.tableName + "_artists").Where(Eq{r.tableName + "_id": itemID})
_, err := r.executeSQL(sqd) _, err := r.executeSQL(sqd)
if err != nil { if err != nil {
return err return err