From ed3a7b8f4ef2ec2aead4b9f1e3f0819d470ed96f Mon Sep 17 00:00:00 2001 From: goon-foss Date: Fri, 18 Sep 2026 13:04:14 +0200 Subject: [PATCH] fix(merge): tytul z TPDB/StashDB nie przegrywa z dluzszym tytulem z tuba Zasada "dluzszy tytul wygrywa" podmieniala czysty tytul kanoniczny na tubowy z doklejonym studiem, obsada i data ("Milfy 26 09 16 Koda Monroe Voluptuous Therapist ..."). Pierwszy przebieg studio_title_dedup zrobil tak z 491 scenami; tytuly przywrocone z external_records. Teraz kanon wygrywa z tubem, a dluzszy tytul liczy sie tylko miedzy scenami tego samego rodzaju. Co-Authored-By: Claude Opus 5 --- app/resolve/scene_merge.py | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/app/resolve/scene_merge.py b/app/resolve/scene_merge.py index 5d041ff..0289651 100644 --- a/app/resolve/scene_merge.py +++ b/app/resolve/scene_merge.py @@ -41,6 +41,18 @@ def _is_canonical(session: Session, source_id: uuid.UUID | None) -> bool: return src is not None and src.kind in (SourceKind.tpdb, SourceKind.stashdb) +def _has_canonical_ref(session: Session, scene_id: uuid.UUID) -> bool: + return session.execute( + select(SceneExternalRef.scene_id) + .join(Source, Source.id == SceneExternalRef.source_id) + .where( + SceneExternalRef.scene_id == scene_id, + Source.kind.in_((SourceKind.tpdb, SourceKind.stashdb)), + ) + .limit(1) + ).first() is not None + + class MergeError(Exception): pass @@ -60,12 +72,18 @@ def merge_scenes( if keep is None or drop is None: raise MergeError("scene not found") + # Sprawdzamy PRZED przepieciem refow, potem obie strony wygladalyby na kanoniczne. + keep_canonical = _has_canonical_ref(session, keep_id) + drop_canonical = _has_canonical_ref(session, drop_id) + _move_external_refs(session, keep_id=keep_id, drop_id=drop_id) _move_performers(session, keep_id=keep_id, drop_id=drop_id) _move_tags(session, keep_id=keep_id, drop_id=drop_id) _move_fingerprints(session, keep_id=keep_id, drop_id=drop_id) _move_playback_sources(session, keep_id=keep_id, drop_id=drop_id) - _coalesce_canonical_fields(keep, drop) + _coalesce_canonical_fields( + keep, drop, keep_canonical=keep_canonical, drop_canonical=drop_canonical + ) session.delete(drop) session.flush() @@ -223,7 +241,9 @@ def _move_playback_sources(session: Session, *, keep_id: uuid.UUID, drop_id: uui ) -def _coalesce_canonical_fields(keep: Scene, drop: Scene) -> None: +def _coalesce_canonical_fields( + keep: Scene, drop: Scene, *, keep_canonical: bool = False, drop_canonical: bool = False +) -> None: """Wypełnij braki w `keep` polami z `drop`. Nie nadpisuje istniejących wartości.""" if not keep.description and drop.description: keep.description = drop.description @@ -237,7 +257,16 @@ def _coalesce_canonical_fields(keep: Scene, drop: Scene) -> None: keep.release_date = drop.release_date if not keep.studio_id and drop.studio_id: keep.studio_id = drop.studio_id - if drop.title and len(drop.title) > len(keep.title or ""): + # Tytul z TPDB/StashDB to prawdziwy tytul sceny. Tube dokleja do niego studio, obsade + # i date ("Milfy 26 09 16 Koda Monroe Voluptuous Therapist ..."), wiec sama zasada + # "dluzszy wygrywa" podmieniala czysty tytul na zasmiecony: 491 scen kanonicznych przy + # pierwszym przebiegu studio_title_dedup (2026-09-18). Dluzszy wygrywa juz tylko + # miedzy scenami tego samego rodzaju. + take_drop_title = bool(drop.title) and ( + (drop_canonical and not keep_canonical) + or (drop_canonical == keep_canonical and len(drop.title) > len(keep.title or "")) + ) + if take_drop_title: keep.title = drop.title keep.title_normalized = drop.title_normalized # created_at = najwcześniejsze "first seen" obu scen. NEW badge (apka) bazuje na