fix(performers): skasowanie 277 smieciowych performerow + job zapobiegajacy odrastaniu
Kategorie i studia podszywajace sie pod osoby: "Creampie" (269 scen), "Natural tits" (670), "reverse cowgirl" (315), "69", "Brazzers", "Blacked", "Twistys". Razem 277 rekordow i 33 784 przypisan do scen. Zrodlo: _search_base filtruje wyniki tube-searcha warunkiem "slug zawiera >=1 token z zapytania", a resolve_performer tworzy performera z dowolnej nazwy podanej przez tube. Zadnej bramki po drodze. KRYTERIUM JEST WASKIE CELOWO. To uzywane w app/api/scenes.py do de-rankingu (slug pokrywa sie z tagiem I brak refa tpdb/stashdb) daje 3111 trafien, ale probka pokazala ~60% falszywek dla nazw wielowyrazowych: wpadaja w nie Hatano Yui, Davina Raines, Michelle Ferrari, Clanddi, SolaZola, Kate Kuray. Zalozenie "prawdziwy performer ma ref kanoniczny" nie dziala dla JAV, amatorek i debiutantek. Do de-rankingu wystarcza, do kasowania nie. Rozstrzyga promiskuitycznosc taga: ile ROZNYCH innych performerow pojawia sie na scenach z tym samym slugiem. Zmierzone: prawdziwe osoby maksimum 20 (Julia Reaves), wiekszosc 0-4, Hatano Yui 0. Kategorie: Deepthroat 39541, Creampie 33661, riding 16284. Prog 50 lezy ponad 2x powyzej najwyzszej prawdziwej osoby. Odrzucilem tez sygnal "slug rowny studiu", bo wciagal marki osobiste (Katekuray, Julia Reaves). Tresc nie ginie: te slugi dalej istnieja jako tagi (creampie przy 176 tys. scen), przestaja tylko udawac osoby. 16 968 scen zostalo bez obsady, co dokłada 1,8 procent do istniejacych 943 tys. scen bez obsady. Job co 24h zamiast bramki w resolve_performer: nie dotyka goracej sciezki ingestu, a smieci odrastaja wolno. Ta sama logika co przy blackliscie phashy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
121e6aa3f5
commit
02ff9c7ff1
4 changed files with 126 additions and 0 deletions
|
|
@ -123,6 +123,12 @@ class Settings(BaseSettings):
|
|||
sched_phash_blacklist_hours: int = Field(
|
||||
default=24, validation_alias="GOON_SCHED_PHASH_BLACKLIST_HOURS"
|
||||
)
|
||||
# Śmieciowi performerzy — kategorie i studia podszywające się pod osoby („Creampie",
|
||||
# „Natural tits", „Brazzers"). Audyt 2026-07-27: 277 rekordów, 33 784 przypisania.
|
||||
# Odrastają, bo tube-search dokleja performera po jednym tokenie. 24h. 0 = off.
|
||||
sched_junk_performers_hours: int = Field(
|
||||
default=24, validation_alias="GOON_SCHED_JUNK_PERFORMERS_HOURS"
|
||||
)
|
||||
# Ingest freshness watchdog — alert do Sentry gdy aktywny tube (origin
|
||||
# tube:<sitetag>) przestał dawać nowe sceny > próg. Łapie zamrożenie
|
||||
# pojedynczego origin, którego globalny monitor (jeden Source "tube-scraper") nie
|
||||
|
|
|
|||
|
|
@ -292,6 +292,21 @@ def _job_phash_blacklist() -> None:
|
|||
log.exception("[scheduler] phash blacklist failed")
|
||||
|
||||
|
||||
def _job_junk_performers() -> None:
|
||||
"""Kasuje kategorie i studia podszywające się pod performerów („Creampie",
|
||||
„Natural tits", „Brazzers"). Audyt 2026-07-27: 277 rekordów, 33 784 przypisania.
|
||||
Odrastają, bo tube-search dokleja performera po jednym tokenie z zapytania, a
|
||||
resolve_performer tworzy go z dowolnej nazwy podanej przez tube."""
|
||||
log.info("[scheduler] junk performers starting")
|
||||
try:
|
||||
from app.scheduler.junk_performers import run_junk_performer_cleanup
|
||||
|
||||
_run_with_timeout(run_junk_performer_cleanup, label="junk-performers")
|
||||
log.info("[scheduler] junk performers done")
|
||||
except Exception:
|
||||
log.exception("[scheduler] junk performers failed")
|
||||
|
||||
|
||||
def _job_title_duration_dedup() -> None:
|
||||
"""Scal missing-merge dupy (ten sam performer + identyczny tytuł + długość) których
|
||||
bulk_dedup nie łapie (tube re-scrape / cross-tube np. porn00 vs xnxx, reports
|
||||
|
|
@ -498,6 +513,17 @@ def build_scheduler(cfg: dict[str, Any]) -> BlockingScheduler:
|
|||
)
|
||||
log.info("scheduler: phash blacklist every %dh", cfg["phash_blacklist_hours"])
|
||||
|
||||
if cfg.get("junk_performers_hours"):
|
||||
sched.add_job(
|
||||
_job_junk_performers,
|
||||
IntervalTrigger(hours=cfg["junk_performers_hours"], start_date=INTERVAL_ANCHOR),
|
||||
id="junk_performers",
|
||||
replace_existing=True,
|
||||
max_instances=1,
|
||||
coalesce=True,
|
||||
)
|
||||
log.info("scheduler: junk performers every %dh", cfg["junk_performers_hours"])
|
||||
|
||||
if cfg.get("title_dedup_hours"):
|
||||
sched.add_job(
|
||||
_job_title_duration_dedup,
|
||||
|
|
|
|||
91
app/scheduler/junk_performers.py
Normal file
91
app/scheduler/junk_performers.py
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
"""Kasowanie śmieciowych performerów — kategorii i studiów podszywających się pod osoby.
|
||||
|
||||
Skąd się biorą: `_search_base` filtruje wyniki tube-searcha warunkiem „slug zawiera
|
||||
≥1 token z zapytania", a `resolve_performer` tworzy performera z dowolnej nazwy podanej
|
||||
przez tube. W efekcie „Creampie", „Natural tits", „reverse cowgirl", „69", „Brazzers"
|
||||
i „Blacked" wylądowały w tabeli `performers`. Audyt 2026-07-27: 277 takich rekordów z
|
||||
33 784 przypisaniami do scen.
|
||||
|
||||
**Kryterium jest wąskie celowo, bo szerokie kasuje ludzi.** Kryterium używane w
|
||||
`app/api/scenes.py` do de-rankingu (slug pokrywa się z tagiem I brak refa tpdb/stashdb)
|
||||
daje 3111 trafień, ale przy próbkowaniu okazało się mieć ~60% fałszywek dla nazw
|
||||
wielowyrazowych: wpadają w nie **Hatano Yui, Davina Raines, Michelle Ferrari, Clanddi,
|
||||
SolaZola, Kate Kuray**. Powód: założenie „prawdziwy performer ma ref kanoniczny" nie
|
||||
działa dla JAV, amatorek i debiutantek, których nie ma w TPDB. Do de-rankingu to
|
||||
wystarcza (fałszywka = gorszy ranking), do kasowania nie.
|
||||
|
||||
Rozstrzyga dopiero **promiskuityczność taga**: ile RÓŻNYCH innych performerów pojawia
|
||||
się na scenach otagowanych tym samym slugiem. Nazwisko współwystępuje głównie ze swoim
|
||||
właścicielem i partnerami ze sceny, kategoria z całym katalogiem. Zmierzone:
|
||||
|
||||
prawdziwe osoby Hatano Yui 0, Clanddi 0, Chyna 0, Kwini 2, Katekuray 9,
|
||||
SolaZola 4, Julia Reaves 20 (maksimum 20)
|
||||
kategorie Deepthroat 39541, Creampie 33661, riding 16284, Skinny 9443
|
||||
|
||||
Próg 50 leży ponad dwukrotnie powyżej najwyższej prawdziwej osoby. Dodatkowo chronimy
|
||||
nazwy w formacie „Imię Nazwisko", bo sygnał studia (odrzucony) wciągał marki osobiste.
|
||||
|
||||
Job, nie bramka w `resolve_performer`: kasowanie cyklem nie dotyka gorącej ścieżki
|
||||
ingestu, a śmieci i tak odrastają wolno. Ta sama logika co przy [[phash_blacklist]].
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
#: Ponad 2× powyżej najwyższej zmierzonej prawdziwej osoby (Julia Reaves, 20).
|
||||
DEFAULT_MIN_FOREIGN_PERFORMERS = 50
|
||||
|
||||
_SELECT_SQL = """
|
||||
WITH junk AS (
|
||||
SELECT pf.id, pf.canonical_name, pf.slug
|
||||
FROM performers pf
|
||||
WHERE EXISTS (SELECT 1 FROM tags t WHERE t.slug = pf.slug)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM performer_external_refs r JOIN sources s ON s.id = r.source_id
|
||||
WHERE r.performer_id = pf.id AND s.kind IN ('tpdb', 'stashdb')
|
||||
)
|
||||
-- „Imię Nazwisko" zostaje: marki osobiste (Kate Kuray, Julia Reaves) wyglądają
|
||||
-- jak studio, ale są osobami.
|
||||
AND pf.canonical_name !~ '^[A-Z][a-z]+ [A-Z][a-z]+$'
|
||||
)
|
||||
SELECT j.id FROM junk j
|
||||
WHERE (
|
||||
SELECT count(DISTINCT sp.performer_id)
|
||||
FROM tags t JOIN scene_tags st ON st.tag_id = t.id
|
||||
JOIN scene_performers sp ON sp.scene_id = st.scene_id
|
||||
WHERE t.slug = j.slug AND sp.performer_id <> j.id
|
||||
) >= :min_foreign
|
||||
"""
|
||||
|
||||
|
||||
def find_junk_performers(session: Session, *, min_foreign: int = DEFAULT_MIN_FOREIGN_PERFORMERS):
|
||||
return [r[0] for r in session.execute(text(_SELECT_SQL), {"min_foreign": min_foreign})]
|
||||
|
||||
|
||||
def run_junk_performer_cleanup(
|
||||
*, min_foreign: int = DEFAULT_MIN_FOREIGN_PERFORMERS, commit: bool = True
|
||||
) -> dict[str, int]:
|
||||
"""Skasuj śmieciowych performerów. `scene_performers` leci kaskadą — sceny i tagi
|
||||
zostają, więc treść nie ginie: „creampie" nadal jest tagiem przy 176 tys. scen,
|
||||
tylko przestaje udawać osobę."""
|
||||
from app.db import session_scope
|
||||
|
||||
with session_scope() as session:
|
||||
ids = find_junk_performers(session, min_foreign=min_foreign)
|
||||
if not ids:
|
||||
return {"found": 0, "deleted": 0}
|
||||
deleted = 0
|
||||
if commit:
|
||||
deleted = int(
|
||||
session.execute(
|
||||
text("DELETE FROM performers WHERE id = ANY(:ids)"), {"ids": ids}
|
||||
).rowcount or 0
|
||||
)
|
||||
session.commit()
|
||||
log.info("junk performers: znaleziono %d, skasowano %d", len(ids), deleted)
|
||||
return {"found": len(ids), "deleted": deleted}
|
||||
|
|
@ -219,6 +219,9 @@ def run_forever() -> int:
|
|||
# Blacklista zdegenerowanych phashy — zaślepki dzielone przez setki scen (audyt
|
||||
# 2026-07-27). Bez tego zaślepka jest zawsze najbliższym trafieniem w Path 3.
|
||||
"phash_blacklist_hours": getattr(settings, "sched_phash_blacklist_hours", 24) or None,
|
||||
# Śmieciowi performerzy — kategorie/studia udające osoby (audyt 2026-07-27).
|
||||
# Kryterium wąskie celowo: szerokie kasuje realnych ludzi bez refa w TPDB.
|
||||
"junk_performers_hours": getattr(settings, "sched_junk_performers_hours", 24) or None,
|
||||
# Taxonomy scene_count refresh — denormalizacja liczników (perf fix 0019).
|
||||
"taxonomy_counts_hours": getattr(settings, "sched_taxonomy_counts_hours", 3) or None,
|
||||
# Ingest freshness watchdog — per-origin alert do Sentry (report 14f3a655).
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue