goon/app/normalize/movies.py
goon-foss d2b3378d93 fix(ingest): czysc opisy z encji i znacznikow HTML, title_normalized zawsze przez normalize
Opisy scen szly do bazy z `&#34;`, `&lt;br&gt;` i surowymi `<br />`, a apka i strona
SEO pokazuja opis jako tekst, wiec bylo to widac doslownie. clean_description
dekoduje encje, `<br>` i koniec akapitu zamienia na nowa linie, a znane znaczniki
wycina (`<3` zostaje tekstem). Wpiete w normalize_scene i normalize_movie.

bulk_rescrape_hqporner.py i repair_truncated_titles.py liczyly title_normalized
jako lower(title), przez co 6005 scen mialo interpunkcje w kluczu dopasowania.
Oba licza teraz normalize() i dekoduja encje w tytule.

Skrypty: fix_html_entity_titles.py przemianowany na fix_html_entities.py z
przebiegiem po opisach; nowy fix_title_normalized.py przelicza klucz tam, gdzie
rozjechal sie z normalize(title).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 15:59:39 +02:00

67 lines
2.2 KiB
Python

"""RawMovie → NormalizedMovie. Reuse normalize_studio/performer/tag z scenes.py."""
from __future__ import annotations
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
@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=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),
)