goon/scripts/merge_hentai_episode_dupes.py
goon-foss dcde8c8d66 chore(scripts): scalanie tych samych odcinkow hentaimama i xanimeporn
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:17:52 +02:00

79 lines
2.9 KiB
Python

"""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())