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 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-09-18 13:04:14 +02:00
parent 7fea132668
commit ed3a7b8f4e

View file

@ -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