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)