"Natural Tits", "Big Black", "Anal Milf", "Cum Swallow", "Fat Ass" i 94
inne siedzialy w tabeli performers z tagow tubow wciaganych jako obsada.
Ugryzly 2026-08-14 przy rozplataniu zlepkow: regula uznala "Big Black" za
osobe wymieniona w tytule i dala mu 124 sceny.
TRZY OCZYWISTE HEURYSTYKI SPRAWDZONE I ODRZUCONE, kazda z kontrprzykladem:
1. "nazwa jest tez tagiem" -> prawdziwe dla Lauren Phillips, Julii Ann,
Nikki Benz (tuby robia tagi z nazwisk), 7625 z 74150 nazw
2. "nie ma kanonu" -> TPDB SAM MA "Big Black" i "Lesbian Love"
jako performerow, a realna "Mena Mason" refow nie ma
3. "tag >> performer" -> dziala dla "Natural Tits" (2147x) i "Rough
Sex" (1552x), ale "Big Black" ma 0,2x, "Lesbian Love" 0,0x
Kryterium jest wiec STRUKTURALNE i nie zalezy od zadnej z tych baz: cala
nazwa zlozona wylacznie ze slow opisujacych tresc. Kandydatow 101, czyli
na tyle malo, ze lista zostala PRZEJRZANA RECZNIE.
Przeglad wylapal falszywki: "Dick Nasty" ma refy z szesciu zrodel, w tym
StashDB, i jest realnym meskim performerem (obsada "Sugar Daddy 17" obok
Dave'a Cummingsa). "King Cock" wykluczony z ostroznosci. Oba w
_JEDNAK_LUDZIE, z uzasadnieniem w kodzie.
Wynik: 11 793 przypisania zdjete od 99 kategorii, do archiwum. Same rekordy
performerow ZOSTAJA: scene_count spadl do 0, wiec i tak wypadaja z list
z only_with_content, a skasowanie rekordu zerwaloby refy kanoniczne przy
czyms, co TPDB u siebie trzyma jako performera.
Kontrola: Big Black/Natural Tits/Anal Milf/Fat Ass -> 0 scen, Lauren
Phillips 2146 i Dick Nasty 69 nietkniete.
147 lines
6 KiB
Python
147 lines
6 KiB
Python
"""Zdejmuje przypisania „performerów", którzy są nazwami KATEGORII, nie ludźmi.
|
||
|
||
W tabeli `performers` siedzą pozycje w rodzaju „Natural Tits", „Big Black", „Anal Milf",
|
||
„Cum Swallow" czy „Fat Ass". Biorą się z tagów tubów wciąganych jako obsada. Ugryzły
|
||
2026-08-14 przy rozplataniu zlepków: reguła uznała „Big Black" za osobę wymienioną
|
||
w tytule i dała mu 124 sceny.
|
||
|
||
**Trzy oczywiste heurystyki sprawdzone i ODRZUCONE**, każda z konkretnym kontrprzykładem:
|
||
|
||
1. „nazwa jest też tagiem" — prawdziwe dla Lauren Phillips, Julii Ann i Nikki Benz,
|
||
bo tuby robią tagi z nazwisk (7625 z 74150 nazw).
|
||
2. „nie ma refów kanonicznych" — **TPDB SAM MA** „Big Black" i „Lesbian Love" jako
|
||
performerów, a realna „Mena Mason" refów nie ma.
|
||
3. „tag ma dużo więcej scen niż performer" — działa dla „Natural Tits" (2147×) i
|
||
„Rough Sex" (1552×), ale „Big Black" ma 0,2×, a „Lesbian Love" 0,0×.
|
||
|
||
Dlatego kryterium jest STRUKTURALNE i nie zależy od żadnej z tych baz: **cała nazwa
|
||
składa się wyłącznie ze słów opisujących treść**. „Big Black" to big + black, „Natural
|
||
Tits" to natural + tits, a „Lauren Phillips" nie ma ani jednego takiego członu.
|
||
|
||
Kandydatów wychodzi 101, łącznie ~12 tys. przypisań, i lista jest na tyle krótka, że
|
||
została **przejrzana ręcznie**. Stąd `_JEDNAK_LUDZIE`: słownik trafia też w prawdziwe
|
||
osoby o „opisowych" pseudonimach.
|
||
|
||
Uruchomienie (kontener worker):
|
||
python -m scripts.prune_category_performers # dry-run
|
||
python -m scripts.prune_category_performers --yes # wykonaj
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import logging
|
||
import re
|
||
|
||
from sqlalchemy import text
|
||
|
||
from app.db import session_scope
|
||
|
||
log = logging.getLogger(__name__)
|
||
|
||
#: Słowa opisujące TREŚĆ, nie osobę. Świadomie bez imion i nazwisk.
|
||
_SLOWA_KATEGORII = set(
|
||
"""
|
||
anal ass tits boobs cock dick pussy cum cumshot creampie blowjob handjob footjob
|
||
milf teen mature granny mom stepmom stepsis sister brother wife girlfriend
|
||
black white asian latina ebony indian arab russian czech japanese korean thai
|
||
big small huge tiny massive giant thick fat skinny petite busty curvy
|
||
hardcore softcore rough gentle deep double triple first best hot sexy horny nasty
|
||
lesbian gay straight bi trans shemale ladyboy
|
||
fuck fucking fucked sex porn xxx video clip scene movie
|
||
blonde brunette redhead ginger
|
||
threesome foursome gangbang orgy bukkake facial swallow
|
||
squirt squirting shower golden piss pee
|
||
bdsm bondage domination submission fetish kink
|
||
natural fake real amateur pro professional
|
||
queen king lover girl boy babe slut whore bitch
|
||
pov casting audition massage yoga gym office nurse teacher student
|
||
young old
|
||
""".split()
|
||
)
|
||
|
||
#: Wyjątki znalezione przy ręcznym przeglądzie 101 kandydatów. To PRAWDZIWE osoby,
|
||
#: które mają pecha do pseudonimu:
|
||
#: „Dick Nasty" — refy z SZEŚCIU źródeł, w tym StashDB; realny męski performer,
|
||
#: widziany w obsadzie „Sugar Daddy 17" obok Dave'a Cummingsa.
|
||
#: „King Cock" — cztery źródła i niejednoznaczne sceny; wykluczony z ostrożności,
|
||
#: bo koszt błędu (skasowanie realnej osoby) jest wyższy niż zysk.
|
||
_JEDNAK_LUDZIE = frozenset({"dick nasty", "king cock"})
|
||
|
||
|
||
def _norm(s: str | None) -> str:
|
||
return re.sub(r"[^a-z0-9]+", " ", (s or "").lower()).strip()
|
||
|
||
|
||
def _kandydaci(session) -> list[tuple]:
|
||
out = []
|
||
for pid, nm in session.execute(
|
||
text("SELECT id, canonical_name FROM performers")
|
||
).all():
|
||
n = _norm(nm)
|
||
toks = n.split()
|
||
if len(toks) < 2 or n in _JEDNAK_LUDZIE:
|
||
continue
|
||
if all(t in _SLOWA_KATEGORII for t in toks):
|
||
out.append((pid, nm))
|
||
return out
|
||
|
||
|
||
def run(*, apply: bool) -> dict[str, int]:
|
||
with session_scope() as session:
|
||
kand = _kandydaci(session)
|
||
ids = [k[0] for k in kand]
|
||
if not ids:
|
||
print("brak kandydatow")
|
||
return {"performers": 0, "attributions": 0}
|
||
|
||
n = session.execute(
|
||
text(
|
||
"SELECT count(*) FROM scene_performers WHERE performer_id = ANY(:ids)"
|
||
),
|
||
{"ids": ids},
|
||
).scalar_one()
|
||
print(f"kategorii: {len(kand)} | ich przypisan: {n}")
|
||
|
||
if not apply:
|
||
for _pid, nm in sorted(kand, key=lambda x: x[1])[:12]:
|
||
print(f" {nm}")
|
||
print(" ... (dry-run)")
|
||
return {"performers": len(kand), "attributions": int(n)}
|
||
|
||
# Archiwum przed kasowaniem, ta sama tabela co reszta sprzatania.
|
||
session.execute(
|
||
text(
|
||
"""
|
||
INSERT INTO scene_performers_pruned
|
||
(scene_id, performer_id, source_id, performer_name, scene_title)
|
||
SELECT sp.scene_id, sp.performer_id, sp.source_id, p.canonical_name, s.title
|
||
FROM scene_performers sp
|
||
JOIN performers p ON p.id = sp.performer_id
|
||
JOIN scenes s ON s.id = sp.scene_id
|
||
WHERE sp.performer_id = ANY(:ids)
|
||
ON CONFLICT DO NOTHING
|
||
"""
|
||
),
|
||
{"ids": ids},
|
||
)
|
||
deleted = session.execute(
|
||
text("DELETE FROM scene_performers WHERE performer_id = ANY(:ids)"),
|
||
{"ids": ids},
|
||
).rowcount
|
||
# Same rekordy performerow ZOSTAJA (scene_count spadnie do 0 przy najblizszym
|
||
# `refresh_taxonomy_counts`, wiec i tak wypadna z list z `only_with_content`).
|
||
# Kasowanie rekordu zerwaloby refy kanoniczne, a tego nie chcemy przy czyms,
|
||
# co TPDB u siebie trzyma jako performera.
|
||
print(f"zdjeto {deleted} przypisan od {len(kand)} kategorii")
|
||
return {"performers": len(kand), "attributions": int(deleted or 0)}
|
||
|
||
|
||
def main() -> None:
|
||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--yes", action="store_true", help="wykonaj (bez tego dry-run)")
|
||
run(apply=ap.parse_args().yes)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|