From 8d92ecc51b91131350957df02a7a0b3cde48c9f5 Mon Sep 17 00:00:00 2001 From: goon-foss Date: Mon, 27 Jul 2026 15:18:38 +0200 Subject: [PATCH] fix(performers): zdjecie scraperowych przypisan z nazw 1-2 znakowych "A", "L", "Ro", "Be", "Js" mialy po 250-290 scen kazdy. To artefakty parsowania tytulow przez tube-search, ale NIE wszystkie: "Cj", "PD" i "JD" to realne krotkie pseudonimy z obsada potwierdzona w TPDB i StashDB, z prawdziwymi partnerami scenicznymi (Cj z Lucy Belle i Denisem Martim, PD z Victoria Love). Dlatego nie kasujemy tu performera, tylko przypisania, o ktorych WIEMY ze dokleil je scraper. Reszta rozstrzyga sie sama: komu nic nie zostanie, ten znika. To pierwsze uzycie provenance z migracji 0026 - bez niej nie dalo sie odroznic "Cj" z obsady TPDB od "Cj" wylusknietego z tytulu. Rozklad byl jednoznaczny: z 9404 przypisan 9232 (98,2 proc.) ze scrapera, 141 z kanonu, 31 nieznanych. Wiersze z NULL-em zostawiamy, nie zgadujemy. Wynik: 9232 przypisan zdjetych, 20 performerow zniknelo calkiem, 31 zostalo z sama prawdziwa obsada (Cj 41, PD 38, JD 20, reszta <=10). Dopiete do joba junk_performers, zeby nie odroslo. Co-Authored-By: Claude Opus 5 --- app/scheduler/junk_performers.py | 65 +++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/app/scheduler/junk_performers.py b/app/scheduler/junk_performers.py index de77d03..608b8d5 100644 --- a/app/scheduler/junk_performers.py +++ b/app/scheduler/junk_performers.py @@ -70,6 +70,13 @@ DEFAULT_MIN_TAG_RATIO = 2 #: Poniżej tego stosunek jest szumem — za mało scen, żeby cokolwiek przesądzać. DEFAULT_MIN_SCENES_FOR_RATIO = 50 +#: Nazwy 1-2 znakowe („A", „L", „Ro", „Js") to artefakty parsowania tytułów, ale +#: kilka to realne krótkie pseudonimy (Cj, PD, JD mają obsadę potwierdzoną w TPDB +#: i StashDB, z prawdziwymi partnerami scenicznymi). Dlatego NIE kasujemy tu +#: performera, tylko przypisania, o których WIEMY, że dokleił je scraper — reszta +#: rozstrzyga się sama: komu nic nie zostanie, ten znika. +MAX_ARTIFACT_NAME_LEN = 2 + _SELECT_SQL = """ WITH junk AS ( SELECT pf.id, pf.canonical_name, pf.slug @@ -122,6 +129,40 @@ def find_junk_performers( return list(ids) +_PRUNE_ARTIFACT_SQL = """ +DELETE FROM scene_performers sp +USING sources s, performers p +WHERE p.id = sp.performer_id + AND length(trim(p.canonical_name)) <= :max_len + AND s.id = sp.source_id + AND s.kind = 'scraper' +""" + +_DROP_EMPTY_ARTIFACT_SQL = """ +DELETE FROM performers p +WHERE length(trim(p.canonical_name)) <= :max_len + AND NOT EXISTS (SELECT 1 FROM scene_performers sp WHERE sp.performer_id = p.id) +""" + + +def prune_artifact_name_attributions( + session: Session, *, max_len: int = MAX_ARTIFACT_NAME_LEN +) -> tuple[int, int]: + """Zdejmij scraperowe przypisania z nazw 1-2 znakowych i usuń tych, którym nic nie + zostało. Kanoniczne przypisania zostają nietknięte — patrz MAX_ARTIFACT_NAME_LEN. + + Wymaga wypełnionego `scene_performers.source_id` (migracja 0026): bez provenance nie + dałoby się odróżnić „Cj" z obsady TPDB od „Cj" wyłuskanego z tytułu przez tube-search. + Wiersze z NULL-em (sceny wieloźródłowe) zostawiamy — nie zgadujemy.""" + pruned = int( + session.execute(text(_PRUNE_ARTIFACT_SQL), {"max_len": max_len}).rowcount or 0 + ) + dropped = int( + session.execute(text(_DROP_EMPTY_ARTIFACT_SQL), {"max_len": max_len}).rowcount or 0 + ) + return pruned, dropped + + def run_junk_performer_cleanup( *, min_foreign: int = DEFAULT_MIN_FOREIGN_PERFORMERS, commit: bool = True ) -> dict[str, int]: @@ -132,15 +173,19 @@ def run_junk_performer_cleanup( with session_scope() as session: ids = find_junk_performers(session, min_foreign=min_foreign) - if not ids: - return {"found": 0, "deleted": 0} - deleted = 0 + deleted = pruned = dropped = 0 if commit: - deleted = int( - session.execute( - text("DELETE FROM performers WHERE id = ANY(:ids)"), {"ids": ids} - ).rowcount or 0 - ) + if ids: + deleted = int( + session.execute( + text("DELETE FROM performers WHERE id = ANY(:ids)"), {"ids": ids} + ).rowcount or 0 + ) + pruned, dropped = prune_artifact_name_attributions(session) session.commit() - log.info("junk performers: znaleziono %d, skasowano %d", len(ids), deleted) - return {"found": len(ids), "deleted": deleted} + log.info( + "junk performers: znaleziono %d, skasowano %d; nazwy 1-2 znakowe: zdjęto %d " + "przypisań, usunięto %d pustych", + len(ids), deleted, pruned, dropped, + ) + return {"found": len(ids), "deleted": deleted, "pruned": pruned, "dropped": dropped}