mirror of
https://github.com/navidrome/navidrome.git
synced 2026-07-02 07:12:36 +00:00
feat(subsonic): serve CreditedAs as name in contributor lists
This commit is contained in:
parent
8b5e50a1aa
commit
e2fd0959bd
@ -92,6 +92,21 @@ func (p ParticipantList) Join(sep string) string {
|
||||
}), sep)
|
||||
}
|
||||
|
||||
// JoinCredited joins the credited names of the participants with sep.
|
||||
// Falls back to Name when CreditedAs is empty.
|
||||
func (p ParticipantList) JoinCredited(sep string) string {
|
||||
return strings.Join(slice.Map(p, func(part Participant) string {
|
||||
n := part.CreditedAs
|
||||
if n == "" {
|
||||
n = part.Name
|
||||
}
|
||||
if part.SubRole != "" {
|
||||
return n + " (" + part.SubRole + ")"
|
||||
}
|
||||
return n
|
||||
}), sep)
|
||||
}
|
||||
|
||||
type Participants map[Role]ParticipantList
|
||||
|
||||
// Add adds the artists to the role, ignoring duplicates.
|
||||
|
||||
@ -271,7 +271,7 @@ func osChildFromMediaFile(ctx context.Context, mf model.MediaFile) *responses.Op
|
||||
child.DisplayAlbumArtist = mf.AlbumArtist
|
||||
child.AlbumArtists = artistRefs(mf.Participants[model.RoleAlbumArtist])
|
||||
var contributors []responses.Contributor
|
||||
child.DisplayComposer = mf.Participants[model.RoleComposer].Join(consts.ArtistJoiner)
|
||||
child.DisplayComposer = mf.Participants[model.RoleComposer].JoinCredited(consts.ArtistJoiner)
|
||||
for role, participants := range mf.Participants {
|
||||
if role == model.RoleArtist || role == model.RoleAlbumArtist {
|
||||
continue
|
||||
@ -282,7 +282,7 @@ func osChildFromMediaFile(ctx context.Context, mf model.MediaFile) *responses.Op
|
||||
SubRole: participant.SubRole,
|
||||
Artist: responses.ArtistID3Ref{
|
||||
Id: participant.ID,
|
||||
Name: participant.Name,
|
||||
Name: participantDisplayName(participant),
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -296,11 +296,20 @@ func artistRefs(participants model.ParticipantList) []responses.ArtistID3Ref {
|
||||
return slice.Map(participants, func(p model.Participant) responses.ArtistID3Ref {
|
||||
return responses.ArtistID3Ref{
|
||||
Id: p.ID,
|
||||
Name: p.Name,
|
||||
Name: participantDisplayName(p),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// participantDisplayName returns CreditedAs if set, otherwise the canonical Name.
|
||||
// Legacy rows (pre-rescan) have empty CreditedAs and continue to show canonical.
|
||||
func participantDisplayName(p model.Participant) string {
|
||||
if p.CreditedAs != "" {
|
||||
return p.CreditedAs
|
||||
}
|
||||
return p.Name
|
||||
}
|
||||
|
||||
func fakePath(mf model.MediaFile) string {
|
||||
builder := strings.Builder{}
|
||||
|
||||
|
||||
@ -317,6 +317,45 @@ var _ = Describe("helpers", func() {
|
||||
Expect(child.Title).To(Equal(""))
|
||||
})
|
||||
})
|
||||
|
||||
It("returns CreditedAs as the contributor name when present, with canonical artist ID", func() {
|
||||
mf := model.MediaFile{
|
||||
ID: "song-1",
|
||||
Participants: model.Participants{
|
||||
model.RoleArtist: model.ParticipantList{
|
||||
{Artist: model.Artist{ID: "canon-1", Name: "Planetary Assault Systems"}, CreditedAs: "PAS"},
|
||||
},
|
||||
model.RoleComposer: model.ParticipantList{
|
||||
{Artist: model.Artist{ID: "canon-2", Name: "Real Composer"}, CreditedAs: "R. Composer"},
|
||||
},
|
||||
},
|
||||
}
|
||||
child := childFromMediaFile(context.Background(), mf)
|
||||
Expect(child.OpenSubsonicChild).NotTo(BeNil())
|
||||
Expect(child.OpenSubsonicChild.Artists).To(HaveLen(1))
|
||||
Expect(child.OpenSubsonicChild.Artists[0].Id).To(Equal("canon-1"))
|
||||
Expect(child.OpenSubsonicChild.Artists[0].Name).To(Equal("PAS"))
|
||||
|
||||
Expect(child.OpenSubsonicChild.Contributors).To(HaveLen(1))
|
||||
Expect(child.OpenSubsonicChild.Contributors[0].Artist.Id).To(Equal("canon-2"))
|
||||
Expect(child.OpenSubsonicChild.Contributors[0].Artist.Name).To(Equal("R. Composer"))
|
||||
|
||||
Expect(child.OpenSubsonicChild.DisplayComposer).To(Equal("R. Composer"))
|
||||
})
|
||||
|
||||
It("falls back to canonical Name when CreditedAs is empty (legacy participant rows)", func() {
|
||||
mf := model.MediaFile{
|
||||
ID: "song-2",
|
||||
Participants: model.Participants{
|
||||
model.RoleArtist: model.ParticipantList{
|
||||
{Artist: model.Artist{ID: "canon-3", Name: "Some Artist"}}, // no CreditedAs
|
||||
},
|
||||
},
|
||||
}
|
||||
child := childFromMediaFile(context.Background(), mf)
|
||||
Expect(child.OpenSubsonicChild).NotTo(BeNil())
|
||||
Expect(child.OpenSubsonicChild.Artists[0].Name).To(Equal("Some Artist"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("osChildFromMediaFile", func() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user