fix(subsonic): always include mandatory title field in Child responses

Removed `omitempty` from the `Title` struct tag in the `Child` response
type. The Subsonic/OpenSubsonic API spec requires `title` to be a
mandatory field, but songs with empty titles caused the field to be
omitted entirely, crashing clients like Symfonium during sync.

Ref: https://support.symfonium.app/t/app-gets-stuck-on-syncing-large-database/13004/8
This commit is contained in:
Deluan
2026-03-15 13:36:26 -04:00
parent 69e7d163fc
commit a887521d7a
10 changed files with 18 additions and 6 deletions
+8
View File
@@ -309,6 +309,14 @@ var _ = Describe("helpers", func() {
Expect(child.Artist).To(Equal("Test Artist")) Expect(child.Artist).To(Equal("Test Artist"))
}) })
}) })
Context("when MediaFile has an empty title", func() {
It("still includes the title field in the response", func() {
mf.Title = ""
child := childFromMediaFile(ctx, mf)
Expect(child.Title).To(Equal(""))
})
})
}) })
Describe("osChildFromMediaFile", func() { Describe("osChildFromMediaFile", func() {
@@ -9,6 +9,7 @@
{ {
"id": "1", "id": "1",
"isDir": false, "isDir": false,
"title": "",
"bpm": 0, "bpm": 0,
"comment": "", "comment": "",
"sortName": "sort name", "sortName": "sort name",
@@ -1,6 +1,6 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true"> <subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true">
<albumList> <albumList>
<album id="1" isDir="false" sortName="sort name" mediaType="album" musicBrainzId="00000000-0000-0000-0000-000000000000" displayArtist="Display artist" displayAlbumArtist="Display album artist" explicitStatus="explicit"> <album id="1" isDir="false" title="" sortName="sort name" mediaType="album" musicBrainzId="00000000-0000-0000-0000-000000000000" displayArtist="Display artist" displayAlbumArtist="Display album artist" explicitStatus="explicit">
<genres name="Genre 1"></genres> <genres name="Genre 1"></genres>
<genres name="Genre 2"></genres> <genres name="Genre 2"></genres>
<moods>mood1</moods> <moods>mood1</moods>
@@ -115,6 +115,7 @@
{ {
"id": "", "id": "",
"isDir": false, "isDir": false,
"title": "",
"bpm": 0, "bpm": 0,
"comment": "", "comment": "",
"sortName": "", "sortName": "",
@@ -25,7 +25,7 @@
<artist id="4" name="composer2"></artist> <artist id="4" name="composer2"></artist>
</contributors> </contributors>
</child> </child>
<child id="" isDir="false"> <child id="" isDir="false" title="">
<replayGain trackGain="0" albumGain="0" trackPeak="0" albumPeak="0" baseGain="0" fallbackGain="0"></replayGain> <replayGain trackGain="0" albumGain="0" trackPeak="0" albumPeak="0" baseGain="0" fallbackGain="0"></replayGain>
</child> </child>
</directory> </directory>
@@ -8,7 +8,8 @@
"child": [ "child": [
{ {
"id": "1", "id": "1",
"isDir": false "isDir": false,
"title": ""
} }
], ],
"id": "", "id": "",
@@ -1,5 +1,5 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true"> <subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true">
<directory id="" name=""> <directory id="" name="">
<child id="1" isDir="false"></child> <child id="1" isDir="false" title=""></child>
</directory> </directory>
</subsonic-response> </subsonic-response>
@@ -9,6 +9,7 @@
{ {
"id": "1", "id": "1",
"isDir": false, "isDir": false,
"title": "",
"bpm": 0, "bpm": 0,
"comment": "", "comment": "",
"sortName": "", "sortName": "",
@@ -1,5 +1,5 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true"> <subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true">
<directory id="" name=""> <directory id="" name="">
<child id="1" isDir="false"></child> <child id="1" isDir="false" title=""></child>
</directory> </directory>
</subsonic-response> </subsonic-response>
+1 -1
View File
@@ -135,7 +135,7 @@ type Child struct {
Id string `xml:"id,attr" json:"id"` Id string `xml:"id,attr" json:"id"`
Parent string `xml:"parent,attr,omitempty" json:"parent,omitempty"` Parent string `xml:"parent,attr,omitempty" json:"parent,omitempty"`
IsDir bool `xml:"isDir,attr" json:"isDir"` IsDir bool `xml:"isDir,attr" json:"isDir"`
Title string `xml:"title,attr,omitempty" json:"title,omitempty"` Title string `xml:"title,attr" json:"title"`
Name string `xml:"name,attr,omitempty" json:"name,omitempty"` Name string `xml:"name,attr,omitempty" json:"name,omitempty"`
Album string `xml:"album,attr,omitempty" json:"album,omitempty"` Album string `xml:"album,attr,omitempty" json:"album,omitempty"`
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"` Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`