diff --git a/backend/app/plex/sync.py b/backend/app/plex/sync.py index 7c298920..72ccbab1 100644 --- a/backend/app/plex/sync.py +++ b/backend/app/plex/sync.py @@ -309,13 +309,15 @@ def _apply_facet_fields(row, meta: dict) -> None: # person-page cast filter for names past the top ~20. The watermark gate below (`people_enriched_at # is None`) normally protects this, but it is NOT enough on its own: a migration that resets the # watermark re-opens the regression (0065 nulled it, which is exactly how the S5b fix got undone in - # prod). So guard STRUCTURALLY too — union the listing cast onto whatever is stored, never dropping a - # name we already have. On a fresh row (empty `cast_names`) this seeds the top ~20; on a row whose - # watermark was reset but whose fuller cast survives, it keeps the fuller cast. Robust to any future - # watermark reset, not just this one. + # prod). So guard STRUCTURALLY too, by LENGTH: take the fresh listing UNLESS it is shorter than what + # is stored. A stored cast longer than the ~20-cap listing can only be an enriched fuller list, so + # keep it (robust to any watermark reset); at or below the cap the stored value is itself + # listing-sourced, so replacing it with the current listing stays self-correcting when Plex edits + # the cast (a pure union never would). if row.people_enriched_at is None: - merged = list(dict.fromkeys((row.cast_names or []) + (_tags(meta, "Role", limit=20) or []))) - row.cast_names = merged or None + listing_cast = _tags(meta, "Role", limit=20) + if len(row.cast_names or []) <= len(listing_cast or []): + row.cast_names = listing_cast people = list(dict.fromkeys((row.directors or []) + (row.cast_names or []))) row.people_text = " ".join(people) or None