diff --git a/app/api/scenes.py b/app/api/scenes.py index 580c5bb..ee5de79 100644 --- a/app/api/scenes.py +++ b/app/api/scenes.py @@ -26,7 +26,7 @@ from app.api.schemas import ( from app.db import get_session from app.api.device import LEGACY_DEVICE, get_device_id from app.models.favorite_scene import FavoriteScene -from app.models.performer import Performer +from app.models.performer import Performer, PerformerExternalRef from app.models.play_progress import ScenePlayProgress from app.models.playback_source import PlaybackSource from app.models.scene import Scene, SceneExternalRef, ScenePerformer, SceneTag @@ -526,6 +526,31 @@ def similar_scenes( perf_ids = session.execute( select(ScenePerformer.performer_id).where(ScenePerformer.scene_id == scene_id) ).scalars().all() + # Odsiej ŚMIECIOWYCH performerów: kategorie/studia podszywające się pod performera + # (over-attribution z luźnego tube-searcha — np. "Big Tits", "Brazzers", "Ball Licking"). + # Sygnał: slug pokrywa się z tagiem I performer NIE ma kanonicznego refa (tpdb/stashdb). + # Prawdziwi performerzy z tym samym tagiem (tuby tagują sceny nazwiskiem) mają kanoniczny + # ref → zostają. Bez tego similar klastrował się po "Ball Licking" zamiast po treści. + if perf_ids: + junk = set( + session.execute( + select(Performer.id).where( + Performer.id.in_(perf_ids), + exists(select(1).select_from(Tag).where(Tag.slug == Performer.slug)), + ~exists( + select(1) + .select_from(PerformerExternalRef) + .join(Source, Source.id == PerformerExternalRef.source_id) + .where( + PerformerExternalRef.performer_id == Performer.id, + Source.kind.in_([SourceKind.tpdb, SourceKind.stashdb]), + ) + ), + ) + ).scalars().all() + ) + if junk: + perf_ids = [p for p in perf_ids if p not in junk] tag_ids = session.execute( select(SceneTag.tag_id).where(SceneTag.scene_id == scene_id) ).scalars().all()