Files
silo-server/internal/catalog/browse_backdrop_test.go
cf4e080bf4 fix(jellycompat): Wholphin (jellyfin-sdk-kotlin) playback & genre compatibility (#100)
* fix(jellycompat): match MediaSourceId across UUID formats (compact vs dashed)

* fix(jellycompat): honor ImageTypes=Backdrop as a filter on /Items

Wholphin genre cards request /Items?imageTypes=Backdrop&limit=1&sortBy=Random
and assume every returned item has a backdrop. Silo ignored ImageTypes, so a
random pick could lack a backdrop (BackdropImageTags: null), crashing Wholphin.

Push the filter down to the catalog browse SQL
(NULLIF(BTRIM(backdrop_path),'') IS NOT NULL) so random/limited selections only
ever consider backdrop-having items; empty genres correctly return [].

* fix(jellycompat): case-insensitive PlaySessionId + api_key in stream auth

Wholphin's jellyfin-sdk-kotlin builds its own direct-play URL
(/Videos/{id}/stream?static=true&playSessionId=...&mediaSourceId=...) with a
lowercase 'playSessionId', no api_key, and no auth header (ExoPlayer's data
source drops it). PlaybackSessionAuth read 'PlaySessionId'/'PlaySessionID'
case-sensitively, so the fallback never matched -> 401 on every direct-play
stream -> forced (often failing) transcode fallback. Resolve PlaySessionId via
newCaseInsensitiveQuery, and likewise accept case-variant api_key in
ExtractToken.

* fix(jellycompat): support Wholphin season item queries

---------

Co-authored-by: Quick <31828688+Quick104@users.noreply.github.com>
2026-06-08 21:05:33 -04:00

32 lines
1.1 KiB
Go

package catalog
import (
"strings"
"testing"
)
// TestBuildBrowsePlan_RequireBackdrop asserts the ImageTypes=Backdrop filter
// (BrowseFilters.RequireBackdrop) renders the backdrop-presence predicate into
// the WHERE clause, and is absent otherwise. Guards against a future refactor
// of buildBrowsePlan silently dropping the condition.
func TestBuildBrowsePlan_RequireBackdrop(t *testing.T) {
const predicate = "NULLIF(BTRIM(mi.backdrop_path), '') IS NOT NULL"
repo := &BrowseRepository{}
plan, earlyEmpty, err := repo.buildBrowsePlan(BrowseFilters{Type: "movie", RequireBackdrop: true})
if err != nil || earlyEmpty {
t.Fatalf("buildBrowsePlan(RequireBackdrop) err=%v earlyEmpty=%v", err, earlyEmpty)
}
if !strings.Contains(plan.whereClause, predicate) {
t.Fatalf("RequireBackdrop=true: whereClause missing predicate.\ngot: %s", plan.whereClause)
}
plan, _, err = repo.buildBrowsePlan(BrowseFilters{Type: "movie"})
if err != nil {
t.Fatalf("buildBrowsePlan err=%v", err)
}
if strings.Contains(plan.whereClause, predicate) {
t.Fatalf("RequireBackdrop unset: predicate should be absent.\ngot: %s", plan.whereClause)
}
}