mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
fix(participants): preserve later non-empty CreditedAs on dedup merge
Participants.add deduplicates by ID+SubRole. The same artist appearing on multiple tracks of one album would silently keep only the first-seen CreditedAs, so the album-level participants JSON could end up with a credit from one track applied to all tracks of that album. Change the dedup to update the existing entry's CreditedAs when a later occurrence has a non-empty value. Per-track media_file participants are unaffected (each track's JSON is built independently). The album-level merge result is inherently lossy when tracks differ, but no longer silently drops a meaningful credit based on arrival order.
This commit is contained in:
parent
1cf5a9f215
commit
c898f0e2a9
@ -156,16 +156,25 @@ func (p Participants) Merge(other Participants) {
|
||||
}
|
||||
|
||||
func (p Participants) add(role Role, participants ...Participant) {
|
||||
seen := make(map[string]struct{}, len(p[role]))
|
||||
for _, artist := range p[role] {
|
||||
seen[artist.ID+artist.SubRole] = struct{}{}
|
||||
seen := make(map[string]int, len(p[role]))
|
||||
for i, artist := range p[role] {
|
||||
seen[artist.ID+artist.SubRole] = i
|
||||
}
|
||||
for _, participant := range participants {
|
||||
key := participant.ID + participant.SubRole
|
||||
if _, ok := seen[key]; !ok {
|
||||
seen[key] = struct{}{}
|
||||
p[role] = append(p[role], participant)
|
||||
if idx, ok := seen[key]; ok {
|
||||
// Same artist/sub-role seen before. The merge (e.g. building
|
||||
// album.Participants from per-track participants) is inherently
|
||||
// lossy when tracks differ on CreditedAs, but silently dropping
|
||||
// the later value can mean the wrong credit ends up on the album.
|
||||
// Prefer the most recently observed non-empty CreditedAs.
|
||||
if participant.CreditedAs != "" {
|
||||
p[role][idx].CreditedAs = participant.CreditedAs
|
||||
}
|
||||
continue
|
||||
}
|
||||
seen[key] = len(p[role])
|
||||
p[role] = append(p[role], participant)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -116,6 +116,40 @@ var _ = Describe("Participants", func() {
|
||||
RoleAlbumArtist: []Participant{_p("3", "AlbumArtist1"), _p("4", "AlbumArtist2"), _p("7", "AlbumArtist3"), _p("8", "AlbumArtist4")},
|
||||
}))
|
||||
})
|
||||
|
||||
It("upgrades CreditedAs from a later occurrence when the dedup key matches", func() {
|
||||
// Same artist (same ID+SubRole) credited differently on two tracks.
|
||||
// The merge is inherently lossy; the later non-empty credit wins so
|
||||
// it isn't silently dropped just because of arrival order.
|
||||
p1 := Participants{
|
||||
RoleArtist: []Participant{
|
||||
{Artist: Artist{ID: "a1", Name: "Aphex Twin"}, CreditedAs: "AFX"},
|
||||
},
|
||||
}
|
||||
p2 := Participants{
|
||||
RoleArtist: []Participant{
|
||||
{Artist: Artist{ID: "a1", Name: "Aphex Twin"}, CreditedAs: "Aphex Twin"},
|
||||
},
|
||||
}
|
||||
p1.Merge(p2)
|
||||
Expect(p1[RoleArtist]).To(HaveLen(1))
|
||||
Expect(p1[RoleArtist][0].CreditedAs).To(Equal("Aphex Twin"))
|
||||
})
|
||||
|
||||
It("does not overwrite an existing CreditedAs with an empty one", func() {
|
||||
p1 := Participants{
|
||||
RoleArtist: []Participant{
|
||||
{Artist: Artist{ID: "a1", Name: "Aphex Twin"}, CreditedAs: "AFX"},
|
||||
},
|
||||
}
|
||||
p2 := Participants{
|
||||
RoleArtist: []Participant{
|
||||
{Artist: Artist{ID: "a1", Name: "Aphex Twin"}}, // empty CreditedAs
|
||||
},
|
||||
}
|
||||
p1.Merge(p2)
|
||||
Expect(p1[RoleArtist][0].CreditedAs).To(Equal("AFX"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Hash", func() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user