diff --git a/model/album.go b/model/album.go index 578486aa3..3d2c9b7d1 100644 --- a/model/album.go +++ b/model/album.go @@ -1,7 +1,6 @@ package model import ( - "strings" "time" "github.com/navidrome/navidrome/utils/slice" @@ -61,6 +60,10 @@ func (a Album) CoverArtID() ArtworkID { return artworkIDFromAlbum(a) } +func (a Album) ArtistIDs() []string { + return []string{a.ArtistID, a.AlbumArtistID} +} + type DiscID struct { AlbumID string `json:"albumId"` ReleaseDate string `json:"releaseDate"` @@ -95,7 +98,7 @@ func (als Albums) ToAlbumArtist() Artist { func (als Albums) ArtistIDs() []string { var ids []string for _, al := range als { - ids = append(ids, strings.Split(al.AllArtistIDs, " ")...) + ids = append(ids, al.ArtistIDs()...) } return ids } diff --git a/server/api/api.go b/server/api/api.go index 42bac0779..011825747 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -73,21 +73,18 @@ func (a *Router) GetTracks(ctx context.Context, request GetTracksRequestObject) } baseUrl := baseResourceUrl(ctx, "tracks") links, meta := buildPaginationLinksAndMeta(int32(cnt), request.Params, baseUrl) - resources := includedResources{ctx: ctx, ds: a.ds, includes: request.Params.Include} - err = resources.AddArtists(mfs.ArtistIDs()...) - if err != nil { - return nil, err + + resources := newIncludedResources(ctx, a.ds, request.Params.Include) + resources.Artists(mfs.ArtistIDs()...) + resources.Albums(mfs.AlbumIDs()...) + + response := GetTracks200JSONResponse{ + Data: toAPITracks(mfs), + Links: links, + Meta: &meta, } - err = resources.AddAlbums(mfs.AlbumIDs()...) - if err != nil { - return nil, err - } - return GetTracks200JSONResponse{ - Data: toAPITracks(mfs), - Links: links, - Meta: &meta, - Included: resources.Build(), - }, nil + response.Included, err = resources.Build() + return response, err } func (a *Router) GetTrack(ctx context.Context, request GetTrackRequestObject) (GetTrackResponseObject, error) { @@ -95,19 +92,16 @@ func (a *Router) GetTrack(ctx context.Context, request GetTrackRequestObject) (G if err != nil { return nil, err } - resources := includedResources{ctx: ctx, ds: a.ds, includes: request.Params.Include} - err = resources.AddArtists(mf.ArtistID, mf.AlbumArtistID) - if err != nil { - return nil, err + + resources := newIncludedResources(ctx, a.ds, request.Params.Include) + resources.Artists(mf.ArtistID, mf.AlbumArtistID) + resources.Albums(mf.AlbumID) + + response := GetTrack200JSONResponse{ + Data: toAPITrack(*mf), } - err = resources.AddAlbums(mf.AlbumID) - if err != nil { - return nil, err - } - return GetTrack200JSONResponse{ - Data: toAPITrack(*mf), - Included: resources.Build(), - }, nil + response.Included, err = resources.Build() + return response, err } func (a *Router) GetAlbums(ctx context.Context, request GetAlbumsRequestObject) (GetAlbumsResponseObject, error) { @@ -122,17 +116,17 @@ func (a *Router) GetAlbums(ctx context.Context, request GetAlbumsRequestObject) } baseUrl := baseResourceUrl(ctx, "albums") links, meta := buildPaginationLinksAndMeta(int32(cnt), request.Params, baseUrl) - resources := includedResources{ctx: ctx, ds: a.ds, includes: request.Params.Include} - err = resources.AddArtists(albums.ArtistIDs()...) - if err != nil { - return nil, err + + resources := newIncludedResources(ctx, a.ds, request.Params.Include) + resources.Artists(albums.ArtistIDs()...) + + response := GetAlbums200JSONResponse{ + Data: toAPIAlbums(albums), + Links: links, + Meta: &meta, } - return GetAlbums200JSONResponse{ - Data: toAPIAlbums(albums), - Links: links, - Meta: &meta, - Included: resources.Build(), - }, nil + response.Included, err = resources.Build() + return response, err } func (a *Router) GetAlbum(ctx context.Context, request GetAlbumRequestObject) (GetAlbumResponseObject, error) { @@ -140,19 +134,16 @@ func (a *Router) GetAlbum(ctx context.Context, request GetAlbumRequestObject) (G if err != nil { return nil, err } - resources := includedResources{ctx: ctx, ds: a.ds, includes: request.Params.Include} - err = resources.AddArtists(album.ArtistID, album.AlbumArtistID) - if err != nil { - return nil, err + + resources := newIncludedResources(ctx, a.ds, request.Params.Include) + resources.Artists(album.ArtistID, album.AlbumArtistID) + resources.Tracks(album.ID) + + response := GetAlbum200JSONResponse{ + Data: toAPIAlbum(*album), } - err = resources.AddTracks(album.ID) - if err != nil { - return nil, err - } - return GetAlbum200JSONResponse{ - Data: toAPIAlbum(*album), - Included: resources.Build(), - }, nil + response.Included, err = resources.Build() + return response, err } func (a *Router) GetArtists(ctx context.Context, request GetArtistsRequestObject) (GetArtistsResponseObject, error) { diff --git a/server/api/includes.go b/server/api/includes.go index 919e59908..ffd86ba39 100644 --- a/server/api/includes.go +++ b/server/api/includes.go @@ -14,14 +14,46 @@ type includedResources struct { ds model.DataStore includes *includeSlice resources []IncludedResource + ids map[ResourceType][]string } -func (i *includedResources) AddTracks(albumIds ...string) error { - if i.includes == nil || !slices.Contains(*i.includes, string(ResourceTypeTrack)) { - return nil +func newIncludedResources(ctx context.Context, ds model.DataStore, includes *includeSlice) *includedResources { + i := &includedResources{ + ctx: ctx, + ds: ds, + includes: includes, } - sort.Strings(albumIds) - slices.Compact(albumIds) + if includes != nil { + i.ids = make(map[ResourceType][]string) + for _, inc := range *includes { + i.ids[ResourceType(inc)] = []string{} + } + } + return i +} + +func (i *includedResources) Tracks(trackIds ...string) { + if i.ids == nil || i.ids[ResourceTypeTrack] == nil { + return + } + i.ids[ResourceTypeTrack] = append(i.ids[ResourceTypeTrack], trackIds...) +} + +func (i *includedResources) Albums(albumIds ...string) { + if i.ids == nil || i.ids[ResourceTypeAlbum] == nil { + return + } + i.ids[ResourceTypeAlbum] = append(i.ids[ResourceTypeAlbum], albumIds...) +} + +func (i *includedResources) Artists(artistIds ...string) { + if i.ids == nil || i.ids[ResourceTypeArtist] == nil { + return + } + i.ids[ResourceTypeArtist] = append(i.ids[ResourceTypeArtist], artistIds...) +} + +func (i *includedResources) addTracks(albumIds []string) error { tracks, err := i.ds.MediaFile(i.ctx).GetAll(model.QueryOptions{Filters: squirrel.Eq{"album_id": albumIds}}) if err != nil { return err @@ -34,12 +66,7 @@ func (i *includedResources) AddTracks(albumIds ...string) error { return nil } -func (i *includedResources) AddAlbums(albumIds ...string) error { - if i.includes == nil || !slices.Contains(*i.includes, string(ResourceTypeAlbum)) { - return nil - } - sort.Strings(albumIds) - slices.Compact(albumIds) +func (i *includedResources) addAlbums(albumIds []string) error { albums, err := i.ds.Album(i.ctx).GetAll(model.QueryOptions{Filters: squirrel.Eq{"id": albumIds}}) if err != nil { return err @@ -52,12 +79,7 @@ func (i *includedResources) AddAlbums(albumIds ...string) error { return nil } -func (i *includedResources) AddArtists(artistIds ...string) error { - if i.includes == nil || !slices.Contains(*i.includes, string(ResourceTypeArtist)) { - return nil - } - sort.Strings(artistIds) - slices.Compact(artistIds) +func (i *includedResources) addArtists(artistIds []string) error { artists, err := i.ds.Artist(i.ctx).GetAll(model.QueryOptions{Filters: squirrel.Eq{"artist.id": artistIds}}) if err != nil { return err @@ -70,9 +92,32 @@ func (i *includedResources) AddArtists(artistIds ...string) error { return nil } -func (i *includedResources) Build() *[]IncludedResource { +func (i *includedResources) Build() (*[]IncludedResource, error) { if i.includes == nil { - return nil + return nil, nil } - return &i.resources + for _, typ := range *i.includes { + ids := i.ids[ResourceType(typ)] + sort.Strings(ids) + slices.Compact(ids) + if len(ids) == 0 { + continue + } + switch ResourceType(typ) { + case ResourceTypeAlbum: + if err := i.addAlbums(ids); err != nil { + return nil, err + } + case ResourceTypeArtist: + if err := i.addArtists(ids); err != nil { + return nil, err + } + case ResourceTypeTrack: + if err := i.addTracks(ids); err != nil { + return nil, err + } + } + } + + return &i.resources, nil }