ChildCount on albums, seasons and playlists is the played count upstream, not the child count #90

Closed
opened 2026-08-12 08:51:41 +02:00 by Claude · 1 comment
Collaborator

Found while closing the EnableUserData half of #86, and outside it.

With user data on, upstream reports ChildCount: 0 and RecursiveItemCount: 0 for anchor albums that really hold 10 and 15 tracks. We report the real 10 and 15.

DtoService sets dto.ChildCount = dto.RecursiveItemCount for MusicAlbum, Season and Playlist before falling back to LinkedChildren.Length and then GetChildCount — so for those three types ChildCount takes the played/total count in preference to the real child count, and that count is 0 on a library whose media is not on the host. Ours is always the direct-child count.

There are two things in it:

  • The value. For albums and seasons the two counts usually coincide, which is why an anchor run agrees row for row on most of them.
  • The presence. Ours gates child_count on Fields=ChildCount. Upstream emits it for those three types whenever user data is on and RecursiveItemCount was asked for, whether or not Fields named it.

Nothing in portable.json asks for it, so no scenario sees either half.

Found while closing the `EnableUserData` half of #86, and outside it. With user data **on**, upstream reports `ChildCount: 0` and `RecursiveItemCount: 0` for anchor albums that really hold 10 and 15 tracks. We report the real 10 and 15. `DtoService` sets `dto.ChildCount = dto.RecursiveItemCount` for `MusicAlbum`, `Season` and `Playlist` before falling back to `LinkedChildren.Length` and then `GetChildCount` — so for those three types `ChildCount` takes the played/total count in preference to the real child count, and that count is 0 on a library whose media is not on the host. Ours is always the direct-child count. There are two things in it: - **The value.** For albums and seasons the two counts usually coincide, which is why an anchor run agrees row for row on most of them. - **The presence.** Ours gates `child_count` on `Fields=ChildCount`. Upstream emits it for those three types whenever user data is on and `RecursiveItemCount` was asked for, whether or not `Fields` named it. Nothing in `portable.json` asks for it, so no scenario sees either half.
Author
Collaborator

Closed for albums and seasons, and deliberately not done for playlists.

An album and a season now report what sits below them as their child count, whether or not Fields named ChildCount, falling back to their direct children where the user's state was not filled in — which is upstream's three-step chain minus the step it cannot reach.

Measured on a season holding 13 children of which 4 are neither folders nor unaired:

request upstream ours
Fields=ChildCount 13, no recursive count same
Fields=RecursiveItemCount 4 / 4 same
Fields=ChildCount,RecursiveItemCount 4 / 4 same
either, with EnableUserData=false 13, no recursive count same

A playlist is left out, and the issue's own framing needs correcting on two points.

First, the reason the anchor's albums report 0: not that the media is off the host, as this issue says, but that its AncestorIds table holds no rows naming a MusicAlbum at all — 0 descendants against 10 direct children. Every album on that library reports 0 whatever either server does, which is precisely why the album numbers cannot tell a correct implementation from one that hardcodes 0. The season above can, and does.

Second, a playlist's contents are LinkedChildren, not descendants — and that feeds all three steps of upstream's chain, not just the middle one. Nothing in this port reads that table, so what sits below a playlist is always nothing. Including playlists would answer 0 where the count is simply unknown, which is worse than answering the direct children; so counts_below_as_children names an album and a season only, and says why.

Two adjacent things noticed and left alone, neither this issue's: an audio playlist reports a played state from its children where Playlist.SupportsPlayedStatus => MediaType == Video says it should not, and BoxSet has the same LinkedChildren blind spot for its recursive count.

Closed for albums and seasons, and deliberately **not** done for playlists. An album and a season now report what sits below them as their child count, whether or not `Fields` named `ChildCount`, falling back to their direct children where the user's state was not filled in — which is upstream's three-step chain minus the step it cannot reach. Measured on a season holding 13 children of which 4 are neither folders nor unaired: | request | upstream | ours | |---|---|---| | `Fields=ChildCount` | `13`, no recursive count | same | | `Fields=RecursiveItemCount` | `4` / `4` | same | | `Fields=ChildCount,RecursiveItemCount` | `4` / `4` | same | | either, with `EnableUserData=false` | `13`, no recursive count | same | **A playlist is left out, and the issue's own framing needs correcting on two points.** First, the reason the anchor's albums report `0`: not that the media is off the host, as this issue says, but that its `AncestorIds` table holds **no rows naming a `MusicAlbum` at all** — 0 descendants against 10 direct children. Every album on that library reports 0 whatever either server does, which is precisely why the album numbers cannot tell a correct implementation from one that hardcodes 0. The season above can, and does. Second, a playlist's contents are `LinkedChildren`, not descendants — and that feeds all three steps of upstream's chain, not just the middle one. Nothing in this port reads that table, so what sits below a playlist is always nothing. Including playlists would answer `0` where the count is simply unknown, which is worse than answering the direct children; so `counts_below_as_children` names an album and a season only, and says why. Two adjacent things noticed and left alone, neither this issue's: an audio playlist reports a played state from its children where `Playlist.SupportsPlayedStatus => MediaType == Video` says it should not, and `BoxSet` has the same `LinkedChildren` blind spot for its recursive count.
Sign in to join this conversation.
No description provided.