Skip to content

fix(tags): read Emby tags from TagItems — ToDelete state was wrong on the wall#51

Merged
tcconnally merged 1 commit into
mainfrom
fix/emby-tag-shape
Jul 15, 2026
Merged

fix(tags): read Emby tags from TagItems — ToDelete state was wrong on the wall#51
tcconnally merged 1 commit into
mainfrom
fix/emby-tag-shape

Conversation

@tcconnally

Copy link
Copy Markdown
Owner

Problem

Emby serves applied tags under TagItems ([{Name, Id}]) and returns the legacy Tags string list as null — on both the item list and detail endpoints. Probed live on greg (2026-07-15): the two files just tagged ToDelete come back as Tags: None, TagItems: [{Name: "ToDelete", Id: 21516}] via the exact query fetch_items uses.

Three sites read item["Tags"] directly and all get it wrong:

  • cell.py:629 — the red ToDelete button computes checked=False for an already-tagged clip loaded from the library. The indicator lies: tagged clips look untagged.
  • cell.py _toggle_tag — reads item["Tags"] to decide add-vs-remove; setdefault("Tags", []) returns the null, so list(None) raises on a library-loaded tagged clip (and mis-toggles even where it doesn't crash).
  • wall.py update_tags — builds the POST body from Tags, so it can silently drop a tag it never saw.

Fix

One pure helper urls.tag_names(item) that prefers TagItems, falling back to Tags (string list or dict list), used at all three sites. _toggle_tag now keeps both shapes in sync in the local dict so the helper reflects the new state on the next read.

The write path is unchanged — Emby rebuilds TagItems from the POSTed Tags string list (verified: the manual tag applied earlier today persisted and shows up in the cleanup pipeline's Tags=ToDelete query).

Tests

6 new in test_urls.py (pure, no Qt/Emby): the real Emby shape (Tags=null + TagItems), TagItems precedence, both fallbacks, empty/missing safety, and the untagged-vs-tagged checked computation that reproduces the user-visible symptom. Suite green at v10.13.2.

Context

Found while tagging two soak-identified problem files for deletion — the "did it persist?" check initially read the wrong field, which surfaced this latent bug. Tagging itself works end-to-end; this is purely the read-back/display side.

Verify

Deterministic + unit-tested, so no soak needed. Rebuild (build.ps1, pwsh 7) and confirm the log Runtime: banner reads v10.13.2; the red tag glyph should now light for already-tagged clips on load.

🤖 Generated with Claude Code

… the wall

Emby serves applied tags under `TagItems` ([{Name,Id}]) and returns the
legacy `Tags` string list as null on BOTH the item list and detail endpoints
(probed live on greg 2026-07-15). Three sites read `item["Tags"]` directly:

- cell.py:629 — the red ToDelete button computed `checked=False` for an
  already-tagged clip loaded from the library (confirmed: the two files just
  tagged came back Tags=None / TagItems=[ToDelete] via the exact fetch_items
  query, so the indicator lied).
- cell.py _toggle_tag — read `item["Tags"]` to decide add-vs-remove;
  `setdefault("Tags", [])` returns the null, so `list(None)` would raise on a
  library-loaded tagged clip (and mis-toggle even if it didn't).
- wall.py update_tags — built the POST body from `Tags`, so it could drop a
  tag it never saw.

Fix: one pure helper `urls.tag_names(item)` that prefers TagItems and falls
back to Tags (string list or dict list), used at all three sites. `_toggle_tag`
now keeps both shapes in sync in the local dict so the helper reflects the new
state on the next read. The WRITE path is unchanged — Emby rebuilds TagItems
from the posted `Tags` string list (verified: the manual tag persisted).

Tests: 6 in test_urls.py covering the real Emby shape (Tags=null +
TagItems), precedence, both fallbacks, empty/missing safety, and the
untagged-vs-tagged checked computation. Suite green at v10.13.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tcconnally
tcconnally merged commit a18efda into main Jul 15, 2026
2 checks passed
@tcconnally
tcconnally deleted the fix/emby-tag-shape branch July 15, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant