37 lines
1.5 KiB
Python
37 lines
1.5 KiB
Python
import re
|
||
|
||
# only path separators and control chars are truly forbidden on Linux; keep
|
||
# everything else literal so names like "Die drei ???" survive as-is
|
||
_FORBIDDEN = re.compile(r"[/\\\x00-\x1f]")
|
||
|
||
|
||
def sanitize(part: str, max_len: int = 120) -> str:
|
||
part = _FORBIDDEN.sub("", part).strip(" .")
|
||
part = re.sub(r"\s+", " ", part)
|
||
return part[:max_len].strip(" .") or "Unknown"
|
||
|
||
|
||
def render_template(template: str, request) -> str:
|
||
"""Render a naming template against a BookRequest. Each path segment is sanitized."""
|
||
author = (request.authors or "").split(",")[0].strip() or "Unknown Author"
|
||
values = {
|
||
"Author": author,
|
||
"Authors": request.authors or author,
|
||
"Title": request.title or "Unknown Title",
|
||
"Year": str(request.year) if request.year else "",
|
||
"Narrator": getattr(request, "narrator", "") or "",
|
||
"Series": request.series or "",
|
||
"Volume": f"{request.volume:02d}" if request.volume is not None else "",
|
||
}
|
||
segments = []
|
||
for seg in template.split("/"):
|
||
for key, val in values.items():
|
||
seg = seg.replace("{" + key + "}", val)
|
||
# drop empty parenthesized/dangling bits from missing values, e.g. "Title ()"
|
||
seg = re.sub(r"\(\s*\)", "", seg)
|
||
seg = re.sub(r"(-|–)\s*$", "", seg).strip()
|
||
if not seg:
|
||
continue # drop path levels that are empty (e.g. {Series} without a series)
|
||
segments.append(sanitize(seg))
|
||
return "/".join(segments) or "Unknown"
|