Adopt "Title (YYYY)" pool naming and clean Filmotéka link names
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"""Pool file naming convention.
|
||||
|
||||
Every movie in the pool is named ``Title (YYYY).ext`` — a title followed by a
|
||||
four-digit year in parentheses. The year lets two films of the same name live
|
||||
side by side and makes the filename self-describing (the year can be recovered
|
||||
without any metadata). These helpers build, validate and parse that stem (the
|
||||
filename without its extension).
|
||||
"""
|
||||
import re
|
||||
|
||||
# A trailing four-digit year in parentheses. The title is non-greedy so a title
|
||||
# that itself contains parentheses keeps them: "Já, robot (2004)" but also
|
||||
# "Kurz (special) (2019)" → title "Kurz (special)", year 2019.
|
||||
_POOL_STEM_RE = re.compile(r"^(?P<title>.+?) \((?P<year>\d{4})\)$")
|
||||
_FOUR_DIGITS = re.compile(r"\d{4}")
|
||||
|
||||
|
||||
def is_valid_year(year: object) -> bool:
|
||||
"""True when ``year`` is (or stringifies to) exactly four digits."""
|
||||
return bool(re.fullmatch(r"\d{4}", str(year).strip()))
|
||||
|
||||
|
||||
def canonical_pool_stem(title: str, year: object) -> str:
|
||||
"""Build the canonical pool stem ``Title (YYYY)``.
|
||||
|
||||
Raises:
|
||||
ValueError: the title is empty or the year is not four digits.
|
||||
"""
|
||||
title = (title or "").strip()
|
||||
year_s = str(year).strip()
|
||||
if not title:
|
||||
raise ValueError("Název filmu nesmí být prázdný.")
|
||||
if not re.fullmatch(r"\d{4}", year_s):
|
||||
raise ValueError(f"Rok musí být čtyřmístné číslo, ne „{year}“.")
|
||||
return f"{title} ({year_s})"
|
||||
|
||||
|
||||
def parse_pool_stem(stem: str) -> tuple[str, int] | None:
|
||||
"""Split a canonical stem into ``(title, year)``; ``None`` if it isn't one."""
|
||||
match = _POOL_STEM_RE.match((stem or "").strip())
|
||||
if not match:
|
||||
return None
|
||||
return match.group("title").strip(), int(match.group("year"))
|
||||
|
||||
|
||||
def is_canonical_pool_stem(stem: str) -> bool:
|
||||
"""True when ``stem`` matches the ``Title (YYYY)`` convention."""
|
||||
return _POOL_STEM_RE.match((stem or "").strip()) is not None
|
||||
|
||||
|
||||
def year_from_filename(name: str) -> str | None:
|
||||
"""Best-effort four-digit year guessed from a raw (release) filename.
|
||||
|
||||
Prefers a parenthesised year (``… (1999) …``) and otherwise falls back to the
|
||||
last standalone 19xx/20xx run. Used only to pre-fill the import dialog — the
|
||||
user confirms it. Returns the year as a string, or ``None``.
|
||||
"""
|
||||
paren = re.search(r"\((19\d{2}|20\d{2})\)", name)
|
||||
if paren:
|
||||
return paren.group(1)
|
||||
loose = re.findall(r"(?<!\d)(19\d{2}|20\d{2})(?!\d)", name)
|
||||
return loose[-1] if loose else None
|
||||
Reference in New Issue
Block a user