Files
psp-preservation-tool/app/namer.py
T
2026-09-06 22:30:13 -04:00

82 lines
2.4 KiB
Python

"""Build canonical filenames: ``Title (Region) (tags) (Serial).iso``.
Revision (``(v1.03)``), disc (``(Disc 1)``) and edition tags are preserved;
language tags (``(En,Fr,De)``) are dropped; leading articles are moved back in
front (``Sims 2, The`` -> ``The Sims 2``).
"""
import re
_ILLEGAL = re.compile(r'[<>:"/\\|?*\x00-\x1f]')
_TAG_RE = re.compile(r"\s*\(([^()]*)\)\s*$")
_ARTICLE_RE = re.compile(r"^(.+),\s*(The|A|An)(?:\s+-\s+(.+))?$", re.IGNORECASE)
_LANG_CODES = {
"en", "fr", "de", "es", "it", "nl", "pt", "sv", "no", "da", "fi",
"ja", "ko", "zh", "pl", "ru", "el", "tr", "cs", "hu", "hi", "ar", "th",
}
def _is_language_tag(inner):
parts = [p.strip().lower() for p in inner.split(",")]
return bool(parts) and all(p in _LANG_CODES for p in parts)
def _split_tags(name):
"""Split trailing parenthetical tags; keep non-language tags, drop languages."""
title = name
kept = []
while True:
m = _TAG_RE.search(title)
if not m:
break
inner = m.group(1).strip()
if _is_language_tag(inner):
title = title[: m.start()]
else:
kept.insert(0, f"({inner})")
title = title[: m.start()]
return title.strip(), kept
def region_from_name(name):
"""Extract the region from a release name (first non-language tag).
Used when a DAT leaves the region field empty and embeds it in the name
(e.g. ``Oceans 11 (Japan, Europe)``).
"""
_, kept = _split_tags(str(name))
if not kept:
return ""
inner = kept[0][1:-1].strip()
return inner
def _reorder_article(title):
m = _ARTICLE_RE.match(title)
if m:
prefix, article, suffix = m.group(1), m.group(2), m.group(3)
result = f"{article} {prefix}"
if suffix:
result += f" - {suffix}"
return result
return title
def canonical_name(release, serial):
name = release["name"]
region = release.get("region")
if region:
for r in [x.strip() for x in region.split(",") if x.strip()]:
name = re.sub(rf"\s*\(\s*{re.escape(r)}\s*\)", "", name, flags=re.IGNORECASE)
title, tags = _split_tags(name)
title = _reorder_article(title)
parts = [title]
if region and region != "World":
parts.append(f"({region})")
parts.extend(tags)
if serial:
parts.append(f"({serial})")
name = _ILLEGAL.sub(" ", " ".join(parts)).strip()
name = re.sub(r"\s+", " ", name)
return f"{name}.iso"