"""Scala te same odcinki hentai z hentaimama i xanimeporn (2026-10-15). Tytuly hentaimama mialy do 15.10 ogon "- Watch Online Free in HD - Hentaimama", a xanime dokleja "[Sub-ENG]", wiec ten sam odcinek byl dwiema scenami. Para = scena tylko z hentaimama i scena tylko z xanimeporn o tym samym title_normalized po zdjeciu "sub eng"/"raw"/"uncensored" z konca. Scalamy wylacznie pary jeden do jednego (scena hentaimama zostaje, ma dlugosc i miniature). python scripts/merge_hentai_episode_dupes.py # podglad python scripts/merge_hentai_episode_dupes.py --apply """ from __future__ import annotations import argparse import logging from collections import defaultdict import sqlalchemy as sa from app.db import session_scope from app.resolve.scene_merge import merge_scenes log = logging.getLogger(__name__) _PAIRS_SQL = """ with x as ( select s.id, regexp_replace(s.title_normalized, '\\s+(sub eng|eng sub|english sub|raw|uncensored)$', '') k from scenes s where exists (select 1 from playback_sources p where p.scene_id = s.id and p.origin = 'tube:xanimeporn') and not exists (select 1 from playback_sources p where p.scene_id = s.id and p.origin = 'tube:hentaimama') ), h as ( select s.id, s.title_normalized k from scenes s where exists (select 1 from playback_sources p where p.scene_id = s.id and p.origin = 'tube:hentaimama') and not exists (select 1 from playback_sources p where p.scene_id = s.id and p.origin = 'tube:xanimeporn') ) select h.id as keep_id, x.id as drop_id, h.k from x join h on h.k = x.k """ def run(apply: bool) -> dict[str, int]: with session_scope() as s: rows = s.execute(sa.text(_PAIRS_SQL)).all() by_keep: dict = defaultdict(list) by_drop: dict = defaultdict(list) for keep_id, drop_id, _k in rows: by_keep[keep_id].append(drop_id) by_drop[drop_id].append(keep_id) pairs = [(k, d, t) for k, d, t in rows if len(by_keep[k]) == 1 and len(by_drop[d]) == 1] c = {"candidates": len(rows), "one_to_one": len(pairs), "merged": 0, "errors": 0} for keep_id, drop_id, title in pairs[:5]: print(f"{title!r}: keep {keep_id} <- {drop_id}") if not apply: return c for keep_id, drop_id, _t in pairs: try: with session_scope() as s: merge_scenes(s, keep_id=keep_id, drop_id=drop_id, resolved_by="merge_hentai_episode_dupes") s.commit() c["merged"] += 1 except Exception as e: c["errors"] += 1 log.warning("merge %s <- %s padl: %s", keep_id, drop_id, e) if c["merged"] % 200 == 0 and c["merged"]: log.info("hentai dupes %s", c) return c def main() -> int: logging.basicConfig(level=logging.INFO) p = argparse.ArgumentParser() p.add_argument("--apply", action="store_true") args = p.parse_args() print(run(args.apply)) return 0 if __name__ == "__main__": raise SystemExit(main())