fix(subsonic): mark AlbumID3 songCount and created as required

The Subsonic API spec defines songCount and created as required attributes
on AlbumID3, but they were tagged with omitempty in our response struct,
allowing them to be silently dropped from responses (e.g. when songCount
was 0). Created was also a *time.Time, which compounded the omitempty
behavior.

Remove omitempty from both fields and change Created from *time.Time to
time.Time so they are always serialized, matching the spec contract that
clients rely on. The buildAlbumID3 helper and its tests are updated for
the non-pointer Created, and the AlbumWithSongsID3 snapshots are
regenerated to include the now-always-present fields.
This commit is contained in:
Deluan
2026-05-22 18:42:17 -03:00
parent 74185dc6d1
commit 8897ec918e
9 changed files with 18 additions and 15 deletions
+1 -1
View File
@@ -441,7 +441,7 @@ func buildAlbumID3(ctx context.Context, album model.Album) responses.AlbumID3 {
dir.PlayCount = album.PlayCount
dir.Year = int32(cmp.Or(album.MaxOriginalYear, album.MaxYear))
dir.Genre = album.Genre
dir.Created = new(albumCreatedAt(album))
dir.Created = albumCreatedAt(album)
if album.Starred {
dir.Starred = album.StarredAt
}
+5 -8
View File
@@ -576,30 +576,27 @@ var _ = Describe("helpers", func() {
t := time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC)
al := model.Album{ID: "a1", Name: "A", CreatedAt: t}
dir := buildAlbumID3(ctx, al)
Expect(dir.Created).ToNot(BeNil())
Expect(*dir.Created).To(Equal(t))
Expect(dir.Created).To(Equal(t))
})
It("falls back to UpdatedAt when CreatedAt is zero", func() {
updated := time.Date(2019, 5, 6, 7, 8, 9, 0, time.UTC)
al := model.Album{ID: "a2", Name: "A", UpdatedAt: updated}
dir := buildAlbumID3(ctx, al)
Expect(dir.Created).ToNot(BeNil())
Expect(*dir.Created).To(Equal(updated))
Expect(dir.Created).To(Equal(updated))
})
It("falls back to ImportedAt when CreatedAt and UpdatedAt are zero", func() {
imported := time.Date(2021, 8, 9, 10, 11, 12, 0, time.UTC)
al := model.Album{ID: "a3", Name: "A", ImportedAt: imported}
dir := buildAlbumID3(ctx, al)
Expect(dir.Created).ToNot(BeNil())
Expect(*dir.Created).To(Equal(imported))
Expect(dir.Created).To(Equal(imported))
})
It("never leaves Created nil even when all timestamps are zero", func() {
It("leaves Created as zero time when all timestamps are zero", func() {
al := model.Album{ID: "a4", Name: "A"}
dir := buildAlbumID3(ctx, al)
Expect(dir.Created).ToNot(BeNil())
Expect(dir.Created.IsZero()).To(BeTrue())
})
})
@@ -8,7 +8,9 @@
"id": "1",
"name": "album",
"artist": "artist",
"songCount": 0,
"duration": 292,
"created": "0001-01-01T00:00:00Z",
"genre": "rock",
"userRating": 4,
"genres": [
@@ -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">
<album id="1" name="album" artist="artist" duration="292" genre="rock" userRating="4" musicBrainzId="1234" isCompilation="true" sortName="sorted album" displayArtist="artist1 &amp; artist2" explicitStatus="clean" version="Deluxe Edition">
<album id="1" name="album" artist="artist" songCount="0" duration="292" created="0001-01-01T00:00:00Z" genre="rock" userRating="4" musicBrainzId="1234" isCompilation="true" sortName="sorted album" displayArtist="artist1 &amp; artist2" explicitStatus="clean" version="Deluxe Edition">
<genres name="rock"></genres>
<genres name="progressive"></genres>
<discTitles disc="1" title="disc 1"></discTitles>
@@ -7,6 +7,8 @@
"album": {
"id": "",
"name": "",
"duration": 0
"songCount": 0,
"duration": 0,
"created": "0001-01-01T00:00:00Z"
}
}
@@ -1,3 +1,3 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true">
<album id="" name="" duration="0"></album>
<album id="" name="" songCount="0" duration="0" created="0001-01-01T00:00:00Z"></album>
</subsonic-response>
@@ -7,7 +7,9 @@
"album": {
"id": "",
"name": "",
"songCount": 0,
"duration": 0,
"created": "0001-01-01T00:00:00Z",
"userRating": 0,
"genres": [],
"musicBrainzId": "",
@@ -1,3 +1,3 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="v0.55.0" openSubsonic="true">
<album id="" name="" duration="0"></album>
<album id="" name="" songCount="0" duration="0" created="0001-01-01T00:00:00Z"></album>
</subsonic-response>
+2 -2
View File
@@ -251,10 +251,10 @@ type AlbumID3 struct {
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
ArtistId string `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
CoverArt string `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
SongCount int32 `xml:"songCount,attr,omitempty" json:"songCount,omitempty"`
SongCount int32 `xml:"songCount,attr" json:"songCount"`
Duration int32 `xml:"duration,attr" json:"duration"`
PlayCount int64 `xml:"playCount,attr,omitempty" json:"playCount,omitempty"`
Created *time.Time `xml:"created,attr,omitempty" json:"created,omitempty"`
Created time.Time `xml:"created,attr" json:"created"`
Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"`
Year int32 `xml:"year,attr,omitempty" json:"year,omitempty"`
Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"`