feat(subsonic): add groupings field to OpenSubsonic Child response

Include the ID3 grouping tag in OpenSubsonic responses as an array of
strings, per opensubsonic/open-subsonic-api#232. The grouping tag was
already being extracted and stored in MediaFile.Tags via mappings.yaml
aliases (GRP1, GROUPING, ©grp, wm/contentgroupdescription), so this
change only adds the field to the response struct and populates it in
both song and album child builders.

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2026-05-10 18:34:45 -03:00
parent 569de4cd23
commit f12e75aa11
10 changed files with 31 additions and 6 deletions

View File

@ -266,6 +266,7 @@ func osChildFromMediaFile(ctx context.Context, mf model.MediaFile) *responses.Op
child.BitDepth = int32(mf.BitDepth)
child.Genres = toItemGenres(mf.Genres)
child.Moods = mf.Tags.Values(model.TagMood)
child.Groupings = mf.Tags.Values(model.TagGrouping)
child.DisplayArtist = mf.Artist
child.Artists = artistRefs(mf.Participants[model.RoleArtist])
child.DisplayAlbumArtist = mf.AlbumArtist
@ -375,6 +376,7 @@ func osChildFromAlbum(ctx context.Context, al model.Album) *responses.OpenSubson
child.MusicBrainzId = al.MbzAlbumID
child.Genres = toItemGenres(al.Genres)
child.Moods = al.Tags.Values(model.TagMood)
child.Groupings = al.Tags.Values(model.TagGrouping)
child.DisplayArtist = al.AlbumArtist
child.Artists = artistRefs(al.Participants[model.RoleAlbumArtist])
child.DisplayAlbumArtist = al.AlbumArtist

View File

@ -56,7 +56,10 @@
"displayAlbumArtist": "Display album artist",
"contributors": [],
"displayComposer": "",
"explicitStatus": "explicit"
"explicitStatus": "explicit",
"groupings": [
"Soundtrack"
]
}
]
}

View File

@ -9,6 +9,7 @@
<artists id="artist-2" name="Artist 2"></artists>
<albumArtists id="album-artist-1" name="Artist 1"></albumArtists>
<albumArtists id="album-artist-2" name="Artist 2"></albumArtists>
<groupings>Soundtrack</groupings>
</album>
</albumList>
</subsonic-response>

View File

@ -165,7 +165,11 @@
}
],
"displayComposer": "composer 1 \u0026 composer 2",
"explicitStatus": "clean"
"explicitStatus": "clean",
"groupings": [
"Soundtrack",
"Live"
]
},
{
"id": "2",
@ -210,7 +214,8 @@
"displayAlbumArtist": "",
"contributors": [],
"displayComposer": "",
"explicitStatus": ""
"explicitStatus": "",
"groupings": []
}
]
}

View File

@ -32,6 +32,8 @@
<contributors role="role2" subRole="subrole4">
<artist id="2" name="artist2"></artist>
</contributors>
<groupings>Soundtrack</groupings>
<groupings>Live</groupings>
</song>
<song id="2" isDir="true" title="title" album="album" artist="artist" track="1" year="1985" genre="Rock" coverArt="1" size="8421341" contentType="audio/flac" suffix="flac" starred="2016-03-02T20:30:00Z" transcodedContentType="audio/mpeg" transcodedSuffix="mp3" duration="146" bitRate="320">
<replayGain trackGain="0" albumGain="0" trackPeak="0" albumPeak="0" baseGain="0" fallbackGain="0"></replayGain>

View File

@ -110,7 +110,11 @@
}
],
"displayComposer": "composer 1 \u0026 composer 2",
"explicitStatus": "clean"
"explicitStatus": "clean",
"groupings": [
"Soundtrack",
"Live"
]
},
{
"id": "",
@ -141,7 +145,8 @@
"displayAlbumArtist": "",
"contributors": [],
"displayComposer": "",
"explicitStatus": ""
"explicitStatus": "",
"groupings": []
}
],
"id": "1",

View File

@ -24,6 +24,8 @@
<contributors role="composer">
<artist id="4" name="composer2"></artist>
</contributors>
<groupings>Soundtrack</groupings>
<groupings>Live</groupings>
</child>
<child id="" isDir="false" title="">
<replayGain trackGain="0" albumGain="0" trackPeak="0" albumPeak="0" baseGain="0" fallbackGain="0"></replayGain>

View File

@ -28,7 +28,8 @@
"displayAlbumArtist": "",
"contributors": [],
"displayComposer": "",
"explicitStatus": ""
"explicitStatus": "",
"groupings": []
}
],
"id": "",

View File

@ -189,6 +189,7 @@ type OpenSubsonicChild struct {
Contributors Array[Contributor] `xml:"contributors,omitempty" json:"contributors"`
DisplayComposer string `xml:"displayComposer,attr,omitempty" json:"displayComposer"`
ExplicitStatus string `xml:"explicitStatus,attr,omitempty" json:"explicitStatus"`
Groupings Array[string] `xml:"groupings,omitempty" json:"groupings"`
}
type Songs struct {

View File

@ -224,6 +224,7 @@ var _ = Describe("Responses", func() {
Isrc: []string{"ISRC-1", "ISRC-2"},
BPM: 127, ChannelCount: 2, SamplingRate: 44100, BitDepth: 16,
Moods: []string{"happy", "sad"},
Groupings: []string{"Soundtrack", "Live"},
ReplayGain: ReplayGain{TrackGain: gg.P(1.0), AlbumGain: gg.P(2.0), TrackPeak: gg.P(3.0), AlbumPeak: gg.P(4.0), BaseGain: gg.P(5.0), FallbackGain: gg.P(6.0)},
DisplayArtist: "artist 1 & artist 2",
Artists: []ArtistID3Ref{
@ -320,6 +321,7 @@ var _ = Describe("Responses", func() {
Comment: "a comment", MediaType: MediaTypeSong, MusicBrainzId: "4321", SortName: "sorted song",
Isrc: []string{"ISRC-1"},
Moods: []string{"happy", "sad"},
Groupings: []string{"Soundtrack", "Live"},
ReplayGain: ReplayGain{TrackGain: gg.P(1.0), AlbumGain: gg.P(2.0), TrackPeak: gg.P(3.0), AlbumPeak: gg.P(4.0), BaseGain: gg.P(5.0), FallbackGain: gg.P(6.0)},
BPM: 127, ChannelCount: 2, SamplingRate: 44100, BitDepth: 16,
DisplayArtist: "artist1 & artist2",
@ -424,6 +426,7 @@ var _ = Describe("Responses", func() {
ItemGenre{Name: "Genre 2"},
},
Moods: []string{"mood1", "mood2"},
Groupings: []string{"Soundtrack"},
DisplayArtist: "Display artist",
Artists: Array[ArtistID3Ref]{
ArtistID3Ref{Id: "artist-1", Name: "Artist 1"},