A folder's playback state is missing its counts #74

Closed
opened 2026-08-04 16:06:59 +02:00 by Claude · 1 comment
Collaborator

A folder's playback state is missing its counts, and three keys disagree.

/Items?Recursive=true — 3 items:

upstream  {"PlayedPercentage":0,"UnplayedItemCount":799,…,"Key":"71663"}
ours      {…,"Key":"687c0327-a208-a1e6-a472-14df4742a531"}

Two things in one field:

  • PlayedPercentage and UnplayedItemCount are absent here for a Series.
    Upstream reports how much of a folder has been watched.
  • Key falls back to the guid where upstream has the external id — the same
    shape as #66, which fixed it for the containers /Items/Latest reports. These
    three come through a plain query instead, so whatever loads the provider rows
    for a by-id read is not reached.

Only 3 of 21,895 items, because only these have both an external id and enough
playback state to notice.

A folder's playback state is missing its counts, and three keys disagree. `/Items?Recursive=true` — 3 items: upstream {"PlayedPercentage":0,"UnplayedItemCount":799,…,"Key":"71663"} ours {…,"Key":"687c0327-a208-a1e6-a472-14df4742a531"} Two things in one field: - **`PlayedPercentage` and `UnplayedItemCount`** are absent here for a `Series`. Upstream reports how much of a folder has been watched. - **`Key`** falls back to the guid where upstream has the external id — the same shape as #66, which fixed it for the containers `/Items/Latest` reports. These three come through a plain query instead, so whatever loads the provider rows for a by-id read is not reached. Only 3 of 21,895 items, because only these have both an external id and enough playback state to notice.
Author
Collaborator

Fixed — and one half of this issue was my reporting error.

PlayedPercentage / UnplayedItemCount never differed. The report was an
artifact of comparing 0 against 0.0 before number spelling was normalised.
Folder.FillUserDataDtoValues is already modelled here, gated the same way. No
change was needed and none was made.

UserData.Key is real. UserDataManager.Map reports dto.CustomDataKey —
the key the stored row was written with — and ResolveUserDataRow falls back
to the first candidate when none of the item's current keys match. A derived key
is used only where the user has no row for the item at all. This port derived it
every time.

field before after
UserData 2–3 0

What this issue could not have achieved, and why it matters for "identical".
The key upstream reports for an item with no stored row is not a function of its
database. UserDataManager's cache is per user and item and is only ever read
once filled, and /Items/Latest resolves its containers through a by-id read
that loads provider rows where a plain query does not. So whichever route
touches an item first, for that user, decides what every later route reports.

That makes two of the harness's numbers mutually exclusive on a warm upstream:
browsing reaching 0 implies the external keys are cached, which implies the
plain listing then disagrees, and the other way round. Verified against a user
upstream had not cached — cold, this port agrees with it on both routes.

crates/jellyfin-harness/README.md already records this hazard. It is the limit
of what replaying against a long-lived instance can settle.

Fixed — and one half of this issue was my reporting error. **`PlayedPercentage` / `UnplayedItemCount` never differed.** The report was an artifact of comparing `0` against `0.0` before number spelling was normalised. `Folder.FillUserDataDtoValues` is already modelled here, gated the same way. No change was needed and none was made. **`UserData.Key`** is real. `UserDataManager.Map` reports `dto.CustomDataKey` — the key the stored row was *written* with — and `ResolveUserDataRow` falls back to the first candidate when none of the item's current keys match. A derived key is used only where the user has no row for the item at all. This port derived it every time. | field | before | after | |---|---|---| | `UserData` | 2–3 | **0** | **What this issue could not have achieved, and why it matters for "identical".** The key upstream reports for an item with no stored row is not a function of its database. `UserDataManager`'s cache is per user and item and is only ever read once filled, and `/Items/Latest` resolves its containers through a by-id read that loads provider rows where a plain query does not. So whichever route touches an item first, for that user, decides what every later route reports. That makes two of the harness's numbers mutually exclusive on a warm upstream: `browsing` reaching 0 implies the external keys are cached, which implies the plain listing then disagrees, and the other way round. Verified against a user upstream had not cached — cold, this port agrees with it on both routes. `crates/jellyfin-harness/README.md` already records this hazard. It is the limit of what replaying against a long-lived instance can settle.
Sign in to join this conversation.
No description provided.