From 08e62bbb168181ff9b67cb7337e3c3ded891b72b Mon Sep 17 00:00:00 2001 From: npeter83 Date: Thu, 1 Oct 2026 10:43:59 +0200 Subject: [PATCH] =?UTF-8?q?fix(plex):=20E30=20review=20round=203=20?= =?UTF-8?q?=E2=80=94=20a=20dead=20headshot=20breaks=20the=20Plex-failing?= =?UTF-8?q?=20streak,=20410=20held=20by=20a=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/plex/people.py | 5 ++-- backend/tests/test_plex_people_enrich.py | 24 ++++++++++++++----- ...Order.test.ts => channelOrder.dom.test.ts} | 0 3 files changed, 21 insertions(+), 8 deletions(-) rename frontend/src/lib/{channelOrder.test.ts => channelOrder.dom.test.ts} (100%) diff --git a/backend/app/plex/people.py b/backend/app/plex/people.py index afac6525..84c65eb2 100644 --- a/backend/app/plex/people.py +++ b/backend/app/plex/people.py @@ -244,7 +244,8 @@ def warm_portraits(db: Session, limit: int) -> dict: ).tuples() if thumb } - done = dead = plex_failed = down = 0 # `down` counts in a row, `plex_failed` in all + # `down` counts Plex failing in a row (any answer from Plex breaks it), `plex_failed` in all + done = dead = plex_failed = down = 0 with plex: for pid in people_by_credits(db, "portrait", None): if done + dead >= limit or down >= _PLEX_DOWN_AFTER: @@ -264,7 +265,7 @@ def warm_portraits(db: Session, limit: int) -> dict: # Plex failing, which must neither mark the file nor keep the run going. cause = exc.__cause__ if isinstance(cause, httpx.HTTPStatusError) and cause.response.status_code in _DEAD: - dead += 1 + dead, down = dead + 1, 0 # Plex answered: it is up, the streak is broken imgcache.mark_miss(key) else: plex_failed, down = plex_failed + 1, down + 1 diff --git a/backend/tests/test_plex_people_enrich.py b/backend/tests/test_plex_people_enrich.py index 4666d6b8..33ff2fb7 100644 --- a/backend/tests/test_plex_people_enrich.py +++ b/backend/tests/test_plex_people_enrich.py @@ -117,13 +117,15 @@ def test_the_batch_stores_the_hungarian_biography(db): class _Plex: """A PlexClient stand-in. `down`: urls Plex does not answer for (True: none); `dead`: urls Plex - answers "not found" for (a dead CDN file); `status`: an error status Plex answers every url with.""" + answers `gone` for (a dead CDN file: "not found"); `status`: an error status Plex answers every + other url with.""" - def __init__(self, down=False, dead=(), status=None): + def __init__(self, down=False, dead=(), status=None, gone=404): self.asked: list[tuple[str, int | None]] = [] self.down = down self.dead = set(dead) self.status = status + self.gone = gone def __enter__(self): return self @@ -135,7 +137,7 @@ class _Plex: self.asked.append((url, width)) if self.down is True or (self.down and url in self.down): raise plex_people.PlexError("down") from httpx.ConnectError("refused") - code = 404 if url in self.dead else self.status + code = self.gone if url in self.dead else self.status if code: req = httpx.Request("GET", url) status = httpx.HTTPStatusError( @@ -236,13 +238,23 @@ def test_plex_failing_between_successes_does_not_end_the_run(db, cache_dir, plex assert plex_people.warm_portraits(db, 100) == {"cached": 4, "failed": 4} -def test_dead_headshots_are_left_alone_and_the_rest_still_land(db, cache_dir, plex_on): +def test_plex_failing_between_dead_files_does_not_end_the_run(db, cache_dir, plex_on): + _faces(db, 8) + plex_on["plex"] = _Plex( # down, dead, down, dead, …: Plex answers in between + down={CDN + f"{i}.jpg" for i in (0, 2, 4, 6)}, dead={CDN + f"{i}.jpg" for i in (1, 3, 5, 7)} + ) + assert plex_people.warm_portraits(db, 100) == {"cached": 0, "failed": 8} + assert len(plex_on["plex"].asked) == 8 + + +@pytest.mark.parametrize("gone", [404, 410]) +def test_dead_headshots_are_left_alone_and_the_rest_still_land(db, cache_dir, plex_on, gone): _faces(db, 8) dead = {CDN + f"{i}.jpg" for i in range(6)} # more dead files than any failure limit - plex_on["plex"] = _Plex(dead=dead) + plex_on["plex"] = _Plex(dead=dead, gone=gone) assert plex_people.warm_portraits(db, 100) == {"cached": 2, "failed": 6} - plex_on["plex"] = again = _Plex(dead=dead) + plex_on["plex"] = again = _Plex(dead=dead, gone=gone) assert plex_people.warm_portraits(db, 100) == {"cached": 0} assert again.asked == [] # dead ones marked, live ones cached: nothing to ask for diff --git a/frontend/src/lib/channelOrder.test.ts b/frontend/src/lib/channelOrder.dom.test.ts similarity index 100% rename from frontend/src/lib/channelOrder.test.ts rename to frontend/src/lib/channelOrder.dom.test.ts