From 4341b025ff7a128d29e89aadff849653baf83356 Mon Sep 17 00:00:00 2001 From: Deluan Date: Mon, 6 Jul 2026 08:57:40 -0400 Subject: [PATCH] fix(jellyfin): honor the isFavorite query param for favorites filtering Finamp's artist 'Favourite tracks' widget requests favorites via the standalone isFavorite=true query param, not Filters=IsFavorite, so the filter was ignored and non-favorited tracks were returned. Detect both forms. (The widget's reshuffling is Finamp's explicit SortBy=Random.) --- server/jellyfin/README.md | 3 ++- server/jellyfin/e2e/annotations_test.go | 8 ++++++++ server/jellyfin/items.go | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/server/jellyfin/README.md b/server/jellyfin/README.md index 91340fffd..c806a699e 100644 --- a/server/jellyfin/README.md +++ b/server/jellyfin/README.md @@ -70,7 +70,8 @@ authenticated user has access to; a library (or item within it) the user cannot for scoping, an artist id when browsing into an artist's albums, or an album id when browsing into an album's tracks); `AlbumArtistIds`/`ArtistIds`/`contributingArtistIds` (an artist's albums or tracks — Finamp's artist screen sends these *alongside* `ParentId=`); `SearchTerm`; -`Filters=IsFavorite`; `SortBy`/`SortOrder`; `StartIndex`/`Limit`; and `Ids` (batch fetch by id). +favorites-only (`Filters=IsFavorite` or the standalone `isFavorite=true`); `SortBy`/`SortOrder`; +`StartIndex`/`Limit`; and `Ids` (batch fetch by id). ## Implemented endpoints diff --git a/server/jellyfin/e2e/annotations_test.go b/server/jellyfin/e2e/annotations_test.go index a5b1edeb7..b194d3e06 100644 --- a/server/jellyfin/e2e/annotations_test.go +++ b/server/jellyfin/e2e/annotations_test.go @@ -46,6 +46,14 @@ var _ = Describe("Annotations", func() { Expect(q.Items[0].Name).To(Equal("Abbey Road")) }) + It("filters to favorites via the isFavorite query param (Finamp's artist widget form)", func() { + // Finamp's "Favourite tracks" widget sends isFavorite=true as a query param (not + // Filters=IsFavorite), combined with ArtistIds. + post("/Users/admin-1/FavoriteItems/"+enc(songID("Help!")), "") + q := queryResult(get("/Items?IncludeItemTypes=Audio&Recursive=true&ArtistIds=" + enc(artistID("The Beatles")) + "&isFavorite=true")) + Expect(names(q.Items)).To(ConsistOf("Help!")) + }) + It("returns 404 when favoriting an unknown item", func() { Expect(post("/Users/admin-1/FavoriteItems/"+enc("nope"), "").Code).To(Equal(http.StatusNotFound)) }) diff --git a/server/jellyfin/items.go b/server/jellyfin/items.go index 5e5b5fc02..7ae0dcc09 100644 --- a/server/jellyfin/items.go +++ b/server/jellyfin/items.go @@ -51,7 +51,10 @@ func (api *Router) queryItems(ctx context.Context, r *http.Request) (dto.QueryRe } parentId := dto.DecodeID(p.StringOr("ParentId", "")) search := p.StringOr("SearchTerm", "") - favOnly := strings.Contains(p.StringOr("Filters", ""), "IsFavorite") + // Clients express "favorites only" two ways: Jellyfin's Filters=IsFavorite, and the standalone + // isFavorite=true query param (Finamp's artist "Favourite tracks" widget uses the latter). + favOnly := strings.Contains(p.StringOr("Filters", ""), "IsFavorite") || + p.BoolOr("IsFavorite", false) || p.BoolOr("isFavorite", false) sortBy := p.StringOr("SortBy", "") sortOrder := p.StringOr("SortOrder", "") offset := p.IntOr("StartIndex", 0)