mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-08 10:27:08 +02:00
fix(jellyfin): sort the user-root /Items libraries by SortBy and SortOrder before paging
This commit is contained in:
parent
c9ba02777a
commit
de4e15c4f1
2 changed files with 53 additions and 0 deletions
|
|
@ -428,6 +428,7 @@ func (api *Router) queryItems(ctx context.Context, r *http.Request) (itemsResult
|
|||
if err != nil {
|
||||
return itemsResult{}, err
|
||||
}
|
||||
sortViews(views, q.sortBy, q.sortOrder)
|
||||
offset := max(q.offset, 0)
|
||||
return materialized(result(paginate(views, offset, q.limit), len(views), offset)), nil
|
||||
}
|
||||
|
|
@ -665,6 +666,39 @@ func paginate(items []dto.BaseItemDto, offset, limit int) []dto.BaseItemDto {
|
|||
return items
|
||||
}
|
||||
|
||||
// sortViews applies SortBy/SortOrder to the user-root libraries, sorted in memory since a user only
|
||||
// has a handful; without a usable key they keep the repository order.
|
||||
func sortViews(views []dto.BaseItemDto, sortBy, order string) {
|
||||
var cmps []func(a, b dto.BaseItemDto) int
|
||||
for key := range strings.SplitSeq(sortBy, ",") {
|
||||
switch strings.ToLower(strings.TrimSpace(key)) {
|
||||
case "sortname", "name":
|
||||
cmps = append(cmps, func(a, b dto.BaseItemDto) int {
|
||||
return strings.Compare(strings.ToLower(a.SortName), strings.ToLower(b.SortName))
|
||||
})
|
||||
case "datecreated":
|
||||
cmps = append(cmps, func(a, b dto.BaseItemDto) int { return strings.Compare(a.DateCreated, b.DateCreated) })
|
||||
}
|
||||
}
|
||||
if len(cmps) == 0 {
|
||||
return
|
||||
}
|
||||
// As in applySort, the first SortOrder value applies to every key.
|
||||
first, _, _ := strings.Cut(order, ",")
|
||||
desc := strings.EqualFold(first, "Descending")
|
||||
slices.SortStableFunc(views, func(a, b dto.BaseItemDto) int {
|
||||
for _, c := range cmps {
|
||||
if r := c(a, b); r != 0 {
|
||||
if desc {
|
||||
return -r
|
||||
}
|
||||
return r
|
||||
}
|
||||
}
|
||||
return 0
|
||||
})
|
||||
}
|
||||
|
||||
// interleave merges per-type item lists round-robin: one item from each list in turn, preserving
|
||||
// each list's own order, so no single type dominates the head of a mixed-type result.
|
||||
func interleave(lists [][]dto.BaseItemDto) []dto.BaseItemDto {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,25 @@ var _ = Describe("Items", func() {
|
|||
Expect(res.Items[0].Id).To(Equal(dto.EncodeLibraryID(2)))
|
||||
})
|
||||
|
||||
DescribeTable("sorts the libraries before paging them",
|
||||
func(query string, wantID int) {
|
||||
ds.Library().(*tests.MockLibraryRepo).SetData(model.Libraries{{ID: 1, Name: "Music"}, {ID: 2, Name: "Audiobooks"}})
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/Items?"+query, nil).
|
||||
WithContext(ctxUserWithLibraries(model.Libraries{{ID: 1, Name: "Music"}, {ID: 2, Name: "Audiobooks"}}))
|
||||
invoke(api.getItems, w, r)
|
||||
var res dto.QueryResult
|
||||
Expect(json.Unmarshal(w.Body.Bytes(), &res)).To(Succeed())
|
||||
Expect(res.TotalRecordCount).To(Equal(2))
|
||||
Expect(res.Items).To(HaveLen(1))
|
||||
Expect(res.Items[0].Id).To(Equal(dto.EncodeLibraryID(wantID)))
|
||||
},
|
||||
Entry("by name ascending", "SortBy=SortName&Limit=1", 2),
|
||||
Entry("by name descending", "SortBy=SortName&SortOrder=Descending&Limit=1", 1),
|
||||
Entry("descending, second page", "SortBy=Name&SortOrder=Descending&StartIndex=1&Limit=1", 2),
|
||||
Entry("unknown key keeps the repository order", "SortBy=Bogus&Limit=1", 1),
|
||||
)
|
||||
|
||||
It("treats an unknown IncludeItemTypes as absent", func() {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/Items?IncludeItemTypes=music", nil).WithContext(ctxUser())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue