Files
supersonic/backend/mediaprovider/jellyfin/artistiterator.go
T
Michael Manganiello 14bfaac7a2 feat: Add sorting options for Artists
Include both `Album Count` and `Random` sortings for the Artists page.

This change also fixes the Artist search feature in Jellyfin, which was
broken because of an open issue on Jellyfin [1]. The current workaround
is to do the filtering locally after receiving all results from Jellyfin
API.

Also, as the Jellyfin API doesn't include a sorting option for
`AlbumCount`, we need to disable pagination when that option is
selected, to receive all artists, and run the sorting locally.

[1] Related issue: https://github.com/jellyfin/jellyfin/issues/8222
2024-04-13 21:04:51 -03:00

127 lines
3.7 KiB
Go

package jellyfin
import (
"slices"
"github.com/dweymouth/go-jellyfin"
"github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/backend/mediaprovider/helpers"
"github.com/dweymouth/supersonic/sharedutil"
)
const (
ArtistSortAlbumCount string = "Album Count"
ArtistSortNameAZ string = "Name (A-Z)"
ArtistSortRandom string = "Random"
)
func (j *jellyfinMediaProvider) ArtistSortOrders() []string {
return []string{
ArtistSortAlbumCount,
ArtistSortNameAZ,
ArtistSortRandom,
}
}
func (j *jellyfinMediaProvider) IterateArtists(sortOrder string, filter mediaprovider.ArtistFilter) mediaprovider.ArtistIterator {
var jfSort jellyfin.Sort
var disablePagination bool
var sortFn func([]*jellyfin.Artist) []*jellyfin.Artist
if sortOrder == "" {
sortOrder = ArtistSortNameAZ // default
}
switch sortOrder {
case ArtistSortAlbumCount:
// Pagination needs to be disabled, to retrieve all results in a single request, and correctly sort them.
disablePagination = true
sortFn = func(artists []*jellyfin.Artist) []*jellyfin.Artist {
slices.SortStableFunc(artists, func(a, b *jellyfin.Artist) int {
return b.AlbumCount - a.AlbumCount
})
return artists
}
case ArtistSortNameAZ:
jfSort.Field = jellyfin.SortByName
jfSort.Mode = jellyfin.SortAsc
case ArtistSortRandom:
jfSort.Field = jellyfin.SortByRandom
}
fetcher := makeArtistFetchFn(
func(offs, limit int) ([]*jellyfin.Artist, error) {
if disablePagination && offs > 0 {
return nil, nil
}
var paging jellyfin.Paging
if !disablePagination {
paging = jellyfin.Paging{StartIndex: offs, Limit: limit}
}
return j.client.GetAlbumArtists(jellyfin.QueryOpts{
Sort: jfSort,
Paging: paging,
})
},
sortFn,
)
return helpers.NewArtistIterator(fetcher, filter, j.prefetchCoverCB)
}
func (j *jellyfinMediaProvider) SearchArtists(searchQuery string, filter mediaprovider.ArtistFilter) mediaprovider.ArtistIterator {
// TODO: Jellyfin API is not returning search results for artists.
// Uncomment the following code once the issue is resolved.
// Related issue: https://github.com/jellyfin/jellyfin/issues/8222
// fetcher := makeArtistFetchFn(
// func(offs, limit int) ([]*jellyfin.Artist, error) {
// log.Printf("Searching for artists: %s", searchQuery)
// sr, err := j.client.Search(searchQuery, jellyfin.TypeArtist, jellyfin.Paging{StartIndex: offs, Limit: limit})
// if err != nil {
// return nil, err
// }
// log.Printf("Found %d artists", len(sr.Artists))
// for _, a := range sr.Artists {
// log.Printf("Artist: %s", a.Name)
// }
// return sr.Artists, nil
// },
// nil,
// )
// return helpers.NewArtistIterator(fetcher, filter, j.prefetchCoverCB)
modifiedFilter := filter.Clone()
modifiedOptions := modifiedFilter.Options()
modifiedOptions.SearchQuery = searchQuery
modifiedFilter.SetOptions(modifiedOptions)
fetcher := makeArtistFetchFn(
func(offs, limit int) ([]*jellyfin.Artist, error) {
return j.client.GetAlbumArtists(jellyfin.QueryOpts{
Sort: jellyfin.Sort{
Field: jellyfin.SortByName,
Mode: jellyfin.SortAsc,
},
Paging: jellyfin.Paging{StartIndex: offs, Limit: limit},
})
},
nil,
)
return helpers.NewArtistIterator(fetcher, modifiedFilter, j.prefetchCoverCB)
}
func makeArtistFetchFn(
fetchFn func(offset, limit int) ([]*jellyfin.Artist, error),
sortFn func([]*jellyfin.Artist) []*jellyfin.Artist,
) helpers.ArtistFetchFn {
return func(offset, limit int) ([]*mediaprovider.Artist, error) {
ar, err := fetchFn(offset, limit)
if err != nil {
return nil, err
}
if sortFn != nil {
ar = sortFn(ar)
}
return sharedutil.MapSlice(ar, toArtist), nil
}
}