fix(output): treat / and \ as folder separators on any OS

Split folder templates on both separators and format each segment separately so sanitize_filename never rewrites the divider; validate folder.* templates per-segment.
This commit is contained in:
sp4rk.y
2026-06-25 16:03:27 -06:00
parent 4539f63106
commit 2ed243aae4
5 changed files with 21 additions and 26 deletions
+5 -1
View File
@@ -213,7 +213,11 @@ class Config:
warnings.warn(f"Unknown template variable '{var}' in {template_type} template")
test_template = re.sub(r"\{[^}]+\}", "TEST", template_str)
if re.search(unsafe_chars, test_template):
if template_type.startswith("folder"):
unsafe_segment = any(re.search(unsafe_chars, seg) for seg in re.split(r"[\\/]", test_template))
else:
unsafe_segment = bool(re.search(unsafe_chars, test_template))
if unsafe_segment:
warnings.warn(f"Template '{template_type}' may contain filesystem-unsafe characters")
if not template_str.strip():
+4 -7
View File
@@ -102,15 +102,12 @@ class Episode(Title):
if folder:
template = config.get_folder_template("series")
if template:
formatter = TemplateFormatter(template)
context = self._build_template_context(media_info, show_service)
context["season"] = f"S{self.season:02}"
folder_name = formatter.format(context)
separators = re.sub(r"\{[^}]*\}", "", template)
spacer = "." if "." in separators and " " not in separators else " "
return "/".join(sanitize_filename(p, spacer) for p in folder_name.split("/") if p)
segments = [
TemplateFormatter(seg).format(context) for seg in re.split(r"[\\/]", template) if seg.strip()
]
return "/".join(s for s in segments if s)
series_template = config.output_template.get("series")
if series_template:
+4 -6
View File
@@ -62,13 +62,11 @@ class Movie(Title):
if folder:
template = config.get_folder_template("movies")
if template:
formatter = TemplateFormatter(template)
context = self._build_template_context(media_info, show_service)
folder_name = formatter.format(context)
separators = re.sub(r"\{[^}]*\}", "", template)
spacer = "." if "." in separators and " " not in separators else " "
return "/".join(sanitize_filename(p, spacer) for p in folder_name.split("/") if p)
segments = [
TemplateFormatter(seg).format(context) for seg in re.split(r"[\\/]", template) if seg.strip()
]
return "/".join(s for s in segments if s)
name = f"{self.name}"
if self.year:
name += f" ({self.year})"
+4 -6
View File
@@ -170,13 +170,11 @@ class Song(Title):
# legacy "songs" folder template, then to "{artist} - {album} ({year})".
template = config.get_folder_template("albums") or config.get_folder_template("songs")
if template:
formatter = TemplateFormatter(template)
context = self._build_template_context(media_info, show_service)
folder_name = formatter.format(context)
separators = re.sub(r"\{[^}]*\}", "", template)
spacer = "." if "." in separators and " " not in separators else " "
return sanitize_filename(folder_name, spacer)
segments = [
TemplateFormatter(seg).format(context) for seg in re.split(r"[\\/]", template) if seg.strip()
]
return "/".join(s for s in segments if s)
name = f"{self.artist} - {self.album}"
if self.year:
name += f" ({self.year})"
+4 -6
View File
@@ -97,13 +97,11 @@ class Song(Title):
if folder:
template = config.get_folder_template("songs")
if template:
formatter = TemplateFormatter(template)
context = self._build_template_context(media_info, show_service)
folder_name = formatter.format(context)
separators = re.sub(r"\{[^}]*\}", "", template)
spacer = "." if "." in separators and " " not in separators else " "
return "/".join(sanitize_filename(p, spacer) for p in folder_name.split("/") if p)
segments = [
TemplateFormatter(seg).format(context) for seg in re.split(r"[\\/]", template) if seg.strip()
]
return "/".join(s for s in segments if s)
name = f"{self.artist} - {self.album}"
if self.year:
name += f" ({self.year})"