Skip to content

Fix (Un)Played filter correctness and performance - #17463

Merged
crobibero merged 2 commits into
jellyfin:masterfrom
Shadowghost:fix-unplayed-filter
Aug 1, 2026
Merged

crobibero merged 2 commits into
jellyfin:masterfrom
Shadowghost:fix-unplayed-filter

Conversation

@Shadowghost

@Shadowghost Shadowghost commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Changes
A folder's played state was computed two different ways that disagreed on the empty case:

  • the DTO and Folder.IsPlayed treat a folder with no countable children as played
  • the SQL filter required "at least one child AND all children played"

A series whose episodes have all been deleted therefore rendered with a played checkmark and UnplayedItemCount: 0 while simultaneously being returned by the Unplayed filter and excluded from the Played one.

All three filter paths now match on absence of an unplayed descendant instead, so filter results and displayed state can no longer diverge.

Perf stats for a warm sample on 240k episodes:

mix endpoint before after delta
balanced series unplayed / played 0.161 / 0.158 0.092 / 0.093 −43% / −41%
balanced boxset unplayed / played 0.028 / 0.024 0.008 / 0.008 −71% / −67%
unplayed ≫ played series unplayed / played 0.118 / 0.112 0.046 / 0.042 −61% / −62%
unplayed ≫ played boxset unplayed / played 0.026 / 0.024 0.008 / 0.008 −69% / −67%
played ≫ unplayed series unplayed / played 0.244 / 0.247 0.190 / 0.197 −22% / −20%
played ≫ unplayed boxset unplayed / played 0.026 / 0.026 0.007 / 0.008 −73% / −69%

Code assistance
Used Claude Code and Opus 5 for creating the Benchmarks and extract the query shapes.

Issues
Fixes #17284
Fixes #16826

@jellyfin-bot

Copy link
Copy Markdown
Contributor

Jellyfin API (v 12.0.0)


Result


No differences. Specifications are equivalent

@wilberfan

Copy link
Copy Markdown

Confirming this fixes the bug for me. I'm the reporter on #17284 — here's what I did to verify.

Original repro: My workflow is download episode → watch → mark played → delete the file via the Jellyfin client (not out-of-band on disk). After doing this repeatedly, ~95% of my TV library ended up as series with zero remaining episode files, still showing a "played" badge, but persisting in Filters=IsUnplayed results.

Confirmed via API on 12.0-rc3 before this fix:

GET /Users/{userId}/Items/{seriesId}
"UserData": { "UnplayedItemCount": 0, "Played": true, ... }
"RecursiveItemCount": 0,
"ChildCount": 1

but the same series was still returned by:

GET /Users/{userId}/Items?ParentId={library}&IncludeItemTypes=Series&Recursive=true&Filters=IsUnplayed

— exactly the DTO-vs-SQL-filter disagreement on the empty-children case described in this PR.

Verification: Built pr-17463 on top of master against a clean throwaway instance (isolated datadir, own port, no shared state with my production server). Reproduced the same scenario from scratch — watched and deleted an episode, leaving a series with zero remaining episodes — and the Unplayed filter now correctly excludes it. Matches the fix description exactly.

Thanks for turning this around so fast.

@Shadowghost
Shadowghost force-pushed the fix-unplayed-filter branch from 1821073 to eed664b Compare July 28, 2026 18:42
@enter-a-random-username

Copy link
Copy Markdown
Contributor

@Shadowghost filtered by clicking a tag and now applying a filter is still broken.

If I filter for played, some played shows are missing and also other way around.

@crobibero crobibero left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically an ABI break, but no plugins use it so I think it is fine

@theguymadmax theguymadmax moved this from Todo to Approved in Jellyfin v12 Jul 29, 2026
@sonarqubecloud

Copy link
Copy Markdown

@enter-a-random-username

enter-a-random-username commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #16826 as well - nice - works

@crobibero
crobibero merged commit 030031d into jellyfin:master Aug 1, 2026
18 of 19 checks passed
@github-project-automation github-project-automation Bot moved this from Approved to Done in Jellyfin v12 Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

6 participants