From 15e1394fa337fbb6d54f51d5358507bfba41a615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deluan=20Quint=C3=A3o?= <deluan@navidrome.org> Date: Fri, 22 Dec 2023 21:03:55 -0500 Subject: [PATCH] Implement `originalReleaseDate` in OpenSubsonic responses. (#2733) See https://github.com/opensubsonic/open-subsonic-api/pull/80 --- server/subsonic/helpers.go | 20 ++++++++++++++++++ server/subsonic/helpers_test.go | 11 ++++++++++ ...mWithSongsID3 with data should match .JSON | 5 +++++ ...umWithSongsID3 with data should match .XML | 1 + ...thSongsID3 without data should match .JSON | 3 ++- ...ithSongsID3 without data should match .XML | 4 +++- server/subsonic/responses/responses.go | 21 ++++++++++++------- server/subsonic/responses/responses_test.go | 3 ++- 8 files changed, 58 insertions(+), 10 deletions(-) diff --git a/server/subsonic/helpers.go b/server/subsonic/helpers.go index 706a12c8b..46d1ab310 100644 --- a/server/subsonic/helpers.go +++ b/server/subsonic/helpers.go @@ -7,6 +7,7 @@ import ( "mime" "net/http" "sort" + "strconv" "strings" "github.com/navidrome/navidrome/consts" @@ -244,6 +245,24 @@ func childrenFromAlbums(ctx context.Context, als model.Albums) []responses.Child return children } +// toItemDate converts a string date in the formats 'YYYY-MM-DD', 'YYYY-MM' or 'YYYY' to an OS ItemDate +func toItemDate(date string) responses.ItemDate { + itemDate := responses.ItemDate{} + if date == "" { + return itemDate + } + parts := strings.Split(date, "-") + if len(parts) > 2 { + itemDate.Day, _ = strconv.Atoi(parts[2]) + } + if len(parts) > 1 { + itemDate.Month, _ = strconv.Atoi(parts[1]) + } + itemDate.Year, _ = strconv.Atoi(parts[0]) + + return itemDate +} + func buildItemGenres(genres model.Genres) []responses.ItemGenre { itemGenres := make([]responses.ItemGenre, len(genres)) for i, g := range genres { @@ -301,5 +320,6 @@ func buildAlbumID3(ctx context.Context, album model.Album) responses.AlbumID3 { dir.MusicBrainzId = album.MbzAlbumID dir.IsCompilation = album.Compilation dir.SortName = album.SortAlbumName + dir.OriginalReleaseDate = toItemDate(album.OriginalDate) return dir } diff --git a/server/subsonic/helpers_test.go b/server/subsonic/helpers_test.go index 898ca2a6c..8b33ebda4 100644 --- a/server/subsonic/helpers_test.go +++ b/server/subsonic/helpers_test.go @@ -57,4 +57,15 @@ var _ = Describe("helpers", func() { Expect(buildDiscSubtitles(context.Background(), album)).To(Equal(expected)) }) }) + + DescribeTable("toItemDate", + func(date string, expected responses.ItemDate) { + Expect(toItemDate(date)).To(Equal(expected)) + }, + Entry("1994-02-04", "1994-02-04", responses.ItemDate{Year: 1994, Month: 2, Day: 4}), + Entry("1994-02", "1994-02", responses.ItemDate{Year: 1994, Month: 2}), + Entry("1994", "1994", responses.ItemDate{Year: 1994}), + Entry("19940201", "", responses.ItemDate{}), + Entry("", "", responses.ItemDate{}), + ) }) diff --git a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .JSON b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .JSON index 15ddbbcea..b7ba2d1dd 100644 --- a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .JSON +++ b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .JSON @@ -31,6 +31,11 @@ "title": "disc 2" } ], + "originalReleaseDate": { + "year": 1994, + "month": 2, + "day": 4 + }, "song": [ { "id": "1", diff --git a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .XML b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .XML index cfe3da99d..fd37f83f9 100644 --- a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .XML +++ b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 with data should match .XML @@ -4,6 +4,7 @@ <genres name="progressive"></genres> <discTitles disc="1" title="disc 1"></discTitles> <discTitles disc="2" title="disc 2"></discTitles> + <originalReleaseDate year="1994" month="2" day="4"></originalReleaseDate> <song id="1" 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" isVideo="false" bpm="127" comment="a comment" sortName="sorted song" mediaType="song" musicBrainzId="4321"> <genres name="rock"></genres> <genres name="progressive"></genres> diff --git a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .JSON b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .JSON index 9508e14ba..8a5b87f24 100644 --- a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .JSON +++ b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .JSON @@ -12,6 +12,7 @@ "musicBrainzId": "", "isCompilation": false, "sortName": "", - "discTitles": [] + "discTitles": [], + "originalReleaseDate": {} } } diff --git a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .XML b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .XML index 58e03c04b..2b748953a 100644 --- a/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .XML +++ b/server/subsonic/responses/.snapshots/Responses AlbumWithSongsID3 without data should match .XML @@ -1,3 +1,5 @@ <subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0" openSubsonic="true"> - <album id="" name="" userRating="0" musicBrainzId="" isCompilation="false" sortName=""></album> + <album id="" name="" userRating="0" musicBrainzId="" isCompilation="false" sortName=""> + <originalReleaseDate></originalReleaseDate> + </album> </subsonic-response> diff --git a/server/subsonic/responses/responses.go b/server/subsonic/responses/responses.go index f93a5967b..eb5a8c883 100644 --- a/server/subsonic/responses/responses.go +++ b/server/subsonic/responses/responses.go @@ -218,13 +218,14 @@ type AlbumID3 struct { Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"` // OpenSubsonic extensions - Played *time.Time `xml:"played,attr,omitempty" json:"played,omitempty"` - UserRating int32 `xml:"userRating,attr" json:"userRating"` - Genres ItemGenres `xml:"genres" json:"genres"` - MusicBrainzId string `xml:"musicBrainzId,attr" json:"musicBrainzId"` - IsCompilation bool `xml:"isCompilation,attr" json:"isCompilation"` - SortName string `xml:"sortName,attr" json:"sortName"` - DiscTitles DiscTitles `xml:"discTitles" json:"discTitles"` + Played *time.Time `xml:"played,attr,omitempty" json:"played,omitempty"` + UserRating int32 `xml:"userRating,attr" json:"userRating"` + Genres ItemGenres `xml:"genres" json:"genres"` + MusicBrainzId string `xml:"musicBrainzId,attr" json:"musicBrainzId"` + IsCompilation bool `xml:"isCompilation,attr" json:"isCompilation"` + SortName string `xml:"sortName,attr" json:"sortName"` + DiscTitles DiscTitles `xml:"discTitles" json:"discTitles"` + OriginalReleaseDate ItemDate `xml:"originalReleaseDate" json:"originalReleaseDate"` } type ArtistWithAlbumsID3 struct { @@ -492,3 +493,9 @@ func marshalJSONArray[T any](v []T) ([]byte, error) { a := v return json.Marshal(a) } + +type ItemDate struct { + Year int `xml:"year,attr,omitempty" json:"year,omitempty"` + Month int `xml:"month,attr,omitempty" json:"month,omitempty"` + Day int `xml:"day,attr,omitempty" json:"day,omitempty"` +} diff --git a/server/subsonic/responses/responses_test.go b/server/subsonic/responses/responses_test.go index 17ea9ec3d..47804d6ba 100644 --- a/server/subsonic/responses/responses_test.go +++ b/server/subsonic/responses/responses_test.go @@ -176,7 +176,8 @@ var _ = Describe("Responses", func() { Id: "1", Name: "album", Artist: "artist", Genre: "rock", Genres: []ItemGenre{{Name: "rock"}, {Name: "progressive"}}, MusicBrainzId: "1234", IsCompilation: true, SortName: "sorted album", - DiscTitles: DiscTitles{{Disc: 1, Title: "disc 1"}, {Disc: 2, Title: "disc 2"}}, + DiscTitles: DiscTitles{{Disc: 1, Title: "disc 1"}, {Disc: 2, Title: "disc 2"}}, + OriginalReleaseDate: ItemDate{Year: 1994, Month: 2, Day: 4}, } t := time.Date(2016, 03, 2, 20, 30, 0, 0, time.UTC) songs := []Child{{