goon/app/normalize/movies.py
goon-foss f6081669a0 fix(movies): okladki pandanetwork z Adult Empire zamiast wygasajacego wp.com
Zgloszenie 6799253d: filmy Tushy Raw bez okladek, linki dzialaja. Okladki
sieci pandanetwork (i*.wp.com/pandanetwork.club/.../<numer>h.jpg) to kopie
okladek Adult Empire pod tym samym numerem produktu; zrodlo oddaje 404,
a pamiec wp.com wygasa (40 z 40 losowych martwych). Normalizacja nowych
filmow podmienia adres na imgs1cdn.adultempire.com/products/<NN>/<numer>h.jpg.

Jednorazowo na produkcji przepisane 51 900 okladek (kopia starych adresow
w movies_poster_backup_20261012), probka 80 z 80 zywych z VPS. Zgodnosc
numeru z filmem sprawdzona po tytulach i wzrokowo na okladkach.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:02:39 +02:00

86 lines
2.9 KiB
Python

"""RawMovie → NormalizedMovie. Reuse normalize_studio/performer/tag z scenes.py."""
from __future__ import annotations
import re
from dataclasses import dataclass, field
from datetime import date
from app.connectors.base import RawMovie, RawMovieChapter, RawPlaybackSource
from app.normalize.scenes import (
NormalizedPerformer,
NormalizedStudio,
NormalizedTag,
normalize_performer,
normalize_studio,
normalize_tag,
)
from app.normalize.text import clean_description, normalize, slugify, unescape_html
# Okladki z sieci pandanetwork (i*.wp.com/pandanetwork.club/.../<numer>h.jpg) to kopie okladek
# Adult Empire pod tym samym numerem produktu. Zrodlo oddaje juz 404, a pamiec podreczna wp.com
# wygasa: 2026-10-12 40 z 40 losowych okladek martwych, zgloszenie 6799253d (Tushy Raw bez
# okladek). Oryginal z Adult Empire jest pod numerem z nazwy pliku (sprawdzone wzrokowo i po
# tytulach na probce).
_PANDA_POSTER_RE = re.compile(r"pandanetwork\.club/.*/(\d{6,8})h(?:-\d)?\.jpe?g", re.IGNORECASE)
def stable_poster_url(url: str | None) -> str | None:
if not url:
return url
m = _PANDA_POSTER_RE.search(url)
if not m:
return url
pid = m.group(1)
return f"https://imgs1cdn.adultempire.com/products/{pid[-2:]}/{pid}h.jpg"
@dataclass
class NormalizedMovie:
external_id: str
title: str
title_normalized: str
slug: str
release_year: int | None
release_date: date | None
description: str | None
duration_sec: int | None
director: str | None
country: str | None
rating: float | None
poster_url: str | None
backdrop_url: str | None
url: str | None
studio: NormalizedStudio | None = None
performers: list[NormalizedPerformer] = field(default_factory=list)
tags: list[NormalizedTag] = field(default_factory=list)
chapters: list[RawMovieChapter] = field(default_factory=list)
playback_sources: list[RawPlaybackSource] = field(default_factory=list)
cross_source_refs: dict[str, str] = field(default_factory=dict)
def normalize_movie(raw: RawMovie) -> NormalizedMovie:
title = unescape_html(raw.title)
return NormalizedMovie(
external_id=raw.external_id,
title=title,
title_normalized=normalize(title),
slug=slugify(title),
release_year=raw.release_year,
release_date=raw.release_date,
description=clean_description(raw.description),
duration_sec=raw.duration_sec,
director=raw.director,
country=raw.country,
rating=raw.rating,
poster_url=stable_poster_url(raw.poster_url),
backdrop_url=raw.backdrop_url,
url=raw.url,
studio=normalize_studio(raw.studio) if raw.studio else None,
performers=[normalize_performer(p) for p in raw.performers],
tags=[normalize_tag(t) for t in raw.tags],
chapters=list(raw.chapters),
playback_sources=list(raw.playback_sources),
cross_source_refs=dict(raw.cross_source_refs),
)