Klasa "prawdziwy performer o pospolitej nazwie" okazala sie zawierac 211 rekordow, ktore w ogole nie sa osobami, tylko kategoriami z refem kanonicznym: "Big Tits" (931 scen), "Big Ass" (640), "Hardcore" (222), "Blonde" (238), "Masturbation". TPDB i StashDB SAME zawieraja takie wpisy, wiec ref kanoniczny ich nie oczyszcza. Kryterium promiskuitycznosci z poprzedniego commita tu NIE dziala i sprawdzenie tego bylo kluczowe: w tej populacji obcy mierzy popularnosc, nie smieciowosc. Przy wartosciach 50-59 stoja Adriana Chechik (4201 scen), Lauren Phillips, Jane Wilde i Jordi El Nino Polla. Moja wczesniejsza walidacja progu 50 byla obciazona, bo grupa bez refa to sami malo znani. Rozstrzyga stosunek scen-z-tagiem do scen-performera, i to z uzasadnienia, nie z probki: nazwisko uzyte jako wlasny tag daje stosunek ~1 Z DEFINICJI, bo tag siedzi dokladnie na jej scenach. Potwierdzone na markach osobistych: Hotwifevictoria 1,02, Rossmexicana 0,99, Sexwithmilfstella 0,87. Wielkie gwiazdy jeszcze nizej, maksimum 0,35 wsrod 921 pewnych osob. Kategorie: Hardcore 1720, Masturbation 1570, Blonde 770. Prog 2 + wymog >=50 scen (ponizej stosunek jest szumem). Skasowane 211 rekordow / ~32 tys. przypisan w dwoch przebiegach. Job idempotentny. Ogon klasy jest zdrowy: z 8116 pozostalych 92% miesci sie ponizej 0,35. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
146 lines
6.8 KiB
Python
146 lines
6.8 KiB
Python
"""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.
|
||
|
||
**Drugie kryterium: tag przytłacza performera.** Promiskuityczność zawodzi dla śmieci,
|
||
które mają ref kanoniczny — bo TPDB i StashDB SAME zawierają wpisy „Hardcore", „Blonde",
|
||
„Masturbation", „Amateur". Co gorsza, `obcy` mierzy tam popularność, nie śmieciowość:
|
||
przy wartości 50-59 stoją Adriana Chechik (4201 scen), Lauren Phillips i Jane Wilde.
|
||
|
||
Rozstrzyga stosunek `scen z tagiem / scen performera`. Nazwisko-tag siedzi głównie na
|
||
scenach swojej właścicielki, więc stosunek jest ułamkiem; kategoria wisi na całym
|
||
katalogu, więc idzie w setki:
|
||
|
||
Hardcore 1720,6 Masturbation 1570,4 Blonde 770,0 Anal Creampie 31,4
|
||
najwyższy wśród 921 PEWNYCH osób (ref kanoniczny + ≥500 scen): **0,35**
|
||
|
||
**Próg 2 wynika z zasady, nie z próbki.** Nazwisko użyte jako własny tag daje stosunek
|
||
≈1 Z DEFINICJI, bo tag siedzi dokładnie na scenach swojej właścicielki — potwierdzone na
|
||
twórcach z własną marką: Hotwifevictoria 1,02, Rossmexicana 0,99, Sexwithmilfstella 0,87.
|
||
Wielkie gwiazdy mają jeszcze mniej (tuby tagują tylko część ich scen). Stosunek ≥2
|
||
oznacza więc, że słowo jest doklejane do scen, które do tej osoby nie należą.
|
||
|
||
Wymóg ≥50 scen odcina szum przy małych liczbach (przy 11 scenach „Denzel" wychodził na
|
||
161, ale to za mało danych, żeby przesądzać). Przy progu 2 na 78 trafień dwa są
|
||
dyskusyjne („Teddy", „Grey" mogą być nazwiskami) — ale i one mają tag rozlany na 3×
|
||
więcej scen, niż mają przypisań, więc ich atrybucje i tak są w większości słowne.
|
||
|
||
Job, nie bramka w `resolve_performer`: kasowanie cyklem nie dotyka gorącej ścieżki
|
||
ingestu, a śmieci i tak odrastają wolno. Dotyczy to zwłaszcza tego drugiego kryterium,
|
||
bo tamte rekordy odtwarza sam ingest TPDB/StashDB. 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
|
||
|
||
#: 2× powyżej naturalnej wartości dla nazwiska użytego jako własny tag (≈1).
|
||
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
|
||
|
||
_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
|
||
"""
|
||
|
||
|
||
_RATIO_SQL = """
|
||
SELECT pf.id
|
||
FROM performers pf
|
||
WHERE EXISTS (SELECT 1 FROM tags t WHERE t.slug = pf.slug)
|
||
AND (SELECT count(*) FROM scene_performers sp WHERE sp.performer_id = pf.id) >= :min_scenes
|
||
AND (SELECT count(*) FROM scene_tags st JOIN tags t ON t.id = st.tag_id WHERE t.slug = pf.slug)::numeric
|
||
/ NULLIF((SELECT count(*) FROM scene_performers sp WHERE sp.performer_id = pf.id), 0) >= :min_ratio
|
||
"""
|
||
|
||
|
||
def find_junk_performers(
|
||
session: Session,
|
||
*,
|
||
min_foreign: int = DEFAULT_MIN_FOREIGN_PERFORMERS,
|
||
min_ratio: int = DEFAULT_MIN_TAG_RATIO,
|
||
min_scenes: int = DEFAULT_MIN_SCENES_FOR_RATIO,
|
||
):
|
||
"""Suma dwóch niezależnych kryteriów — pierwsze łapie śmieci bez refa kanonicznego,
|
||
drugie te, które ref mają (bo TPDB/StashDB też zawierają „Hardcore" czy „Blonde")."""
|
||
ids = {r[0] for r in session.execute(text(_SELECT_SQL), {"min_foreign": min_foreign})}
|
||
ids |= {
|
||
r[0]
|
||
for r in session.execute(
|
||
text(_RATIO_SQL), {"min_ratio": min_ratio, "min_scenes": min_scenes}
|
||
)
|
||
}
|
||
return list(ids)
|
||
|
||
|
||
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}
|