From 02a210252333324242ea44de31581c2e0ce4a52a Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 16 Aug 2026 22:38:59 +0200 Subject: [PATCH] =?UTF-8?q?fix(plex):=20S5a=20review=20round=202=20?= =?UTF-8?q?=E2=80=94=20dup-HU=20guard=20+=20tests=20for=20dup-guid=20&=20g?= =?UTF-8?q?uid-keep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Correctness: _sync_overlay now first-wins on a guid (a `seen` set) so a duplicate HU entry sharing a guid can't overwrite the applied Hungarian metadata or double-count `overlaid`. - Test-coverage: cover the earlier dup-guid fix (both editions of a shared-guid pair get *_hu, counted once) and the guid-keep guard (_apply_item preserves a prior guid when the listing carries none, still updates on a real one). Full backend suite 276 passed / 2 skipped. --- backend/app/plex/sync.py | 4 +++- backend/tests/test_plex_overlay.py | 38 +++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/backend/app/plex/sync.py b/backend/app/plex/sync.py index 2f6f0479..8a76317f 100644 --- a/backend/app/plex/sync.py +++ b/backend/app/plex/sync.py @@ -463,11 +463,13 @@ def _sync_overlay( for group in by_guid.values(): for row in group: row.title_hu = row.summary_hu = row.genres_hu = None + seen: set[str] = set() # first HU entry for a guid wins; a dup HU item can't overwrite or double-count for meta in _paginate(plex, lib.plex_key, item_type): g = _canonical_guid(meta) targets = by_guid.get(g) if g else None - if not targets: + if not targets or g in seen: continue + seen.add(g) title_hu = (meta.get("title") or "").strip() or None summary_hu = meta.get("summary") genres_hu = _tags(meta, "Genre") diff --git a/backend/tests/test_plex_overlay.py b/backend/tests/test_plex_overlay.py index 909fc972..b49a31c7 100644 --- a/backend/tests/test_plex_overlay.py +++ b/backend/tests/test_plex_overlay.py @@ -10,7 +10,7 @@ from sqlalchemy import func, select import app.plex.sync as syncmod from app import sysconfig from app.models import PlexItem, PlexLibrary -from app.plex.sync import _canonical_guid, _sync_overlay +from app.plex.sync import _apply_item, _canonical_guid, _sync_overlay from app.routes.plex.browse import _content_libs @@ -170,6 +170,42 @@ def test_overlay_reconcile_clears_stale_hu_when_twin_vanishes(db): assert row.title_hu is None and row.summary_hu is None and row.genres_hu is None +def test_apply_item_keeps_prior_guid_when_listing_lacks_guid(): + row = PlexItem(rating_key="m1", kind="movie", guid="tmdb:1") + _apply_item(row, lib_id=7, kind="movie", meta={"title": "Heat"}) # no Guid array + assert row.guid == "tmdb:1" # the prior guid survives a guid-less listing + _apply_item(row, lib_id=7, kind="movie", meta={"title": "Heat", "Guid": [{"id": "tmdb://2"}]}) + assert row.guid == "tmdb:2" # a real guid in the listing still updates it + + +def test_overlay_applies_to_all_editions_sharing_a_guid(db): + en_lib = PlexLibrary(plex_key="4", title="Movies", kind="movie") + hu_lib = PlexLibrary(plex_key="9", title="Filmek", kind="movie", metadata_lang="hu") + db.add_all([en_lib, hu_lib]) + db.flush() + hu_lib.overlay_of = en_lib.id + ed1 = PlexItem(rating_key="a", library_id=en_lib.id, kind="movie", title="Heat", guid="tmdb:1") + ed2 = PlexItem(rating_key="b", library_id=en_lib.id, kind="movie", title="Heat (Director's Cut)", guid="tmdb:1") + db.add_all([ed1, ed2]) + db.commit() + + # Two HU entries share the guid — first wins, no double count. + stub = _StubPlex( + [ + _hu_movie("h1", "Öldöklés", "első", ["Akció"], "tmdb://1"), + _hu_movie("h2", "Öldöklés (dup)", "második", ["Krimi"], "tmdb://1"), + ] + ) + stats = {"overlaid": 0} + _sync_overlay(db, stub, hu_lib, en_lib.id, stats) + db.commit() + db.refresh(ed1) + db.refresh(ed2) + assert ed1.title_hu == "Öldöklés" and ed2.title_hu == "Öldöklés" # both editions overlaid + assert ed1.genres_hu == ["Akció"] and ed2.genres_hu == ["Akció"] # first HU entry won + assert stats["overlaid"] == 1 # counted once for the guid, not per edition or per dup HU item + + def test_content_libs_excludes_overlay_libraries(db): en_lib = PlexLibrary(plex_key="4", title="Movies", kind="movie") en_shows = PlexLibrary(plex_key="1", title="TV Shows", kind="show")