Release v1.0.11: fix multi-episode match for end-numbered and dual titles.

Combined pairs are recognized when the SxxExx tag uses the last episode number or the filename lists both story titles separated by dashes.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Bulk Renamer
2026-07-03 18:34:24 -05:00
co-authored by Cursor
parent a6339467d4
commit fce5ad64e5
3 changed files with 36 additions and 8 deletions
+1 -1
View File
@@ -1 +1 @@
1.0.10
1.0.11
+32 -7
View File
@@ -269,21 +269,46 @@ def _combined_title_variants(name: str) -> list[str]:
return variants
def _combined_match_score(fnorm: str, combined_name: str) -> float:
def _combined_match_score(fnorm: str, combined_name: str, file_title: str = "") -> float:
variants = _combined_title_variants(combined_name)
return max((_similarity(fnorm, v) for v in variants), default=0.0)
best = max((_similarity(fnorm, v) for v in variants), default=0.0)
if file_title and _dual_title_matches_combined(file_title, combined_name):
best = max(best, 0.96)
return best
def _combined_allowed_for_file(parsed: dict, target: EpisodeTarget) -> bool:
"""Only treat as multi-episode when the file's episode code fits the range start."""
def _dual_title_matches_combined(file_title: str, combined_name: str) -> bool:
"""True when the filename lists both stories in a combined-order pair."""
file_parts = [p.strip() for p in re.split(r"\s+-\s+", file_title) if p.strip()]
combined_parts = split_combined_title(combined_name)
if len(file_parts) < 2 or len(combined_parts) < 2:
return False
matched = 0
for fp in file_parts[: len(combined_parts)]:
fn = normalize_title(fp)
if max(_similarity(fn, normalize_title(cp)) for cp in combined_parts) >= 0.72:
matched += 1
return matched >= 2
def _combined_allowed_for_file(
parsed: dict,
target: EpisodeTarget,
combined_name: str = "",
) -> bool:
"""Treat as multi-episode when the episode tag or dual title fits the aired range."""
if target.span <= 1:
return True
if parsed.get("span", 1) > 1:
return True
file_title = parsed.get("title") or ""
if combined_name and _dual_title_matches_combined(file_title, combined_name):
return True
file_ep = parsed.get("old_first")
if file_ep is None:
return False
return file_ep == target.episode
end = target.episode_end if target.episode_end is not None else target.episode
return file_ep == target.episode or file_ep == end
def _range_overlaps(
@@ -390,9 +415,9 @@ def match_filenames_to_episodes(
for _cep, target, variants in combined_entries:
if not fnorm:
continue
if not _combined_allowed_for_file(parsed, target):
if not _combined_allowed_for_file(parsed, target, _cep.name):
continue
best = _combined_match_score(fnorm, _cep.name)
best = _combined_match_score(fnorm, _cep.name, raw_title)
best = _apply_season_hint(best, target, parsed)
pairs.append((best, fname, target, raw_title, _cep.name))
+3
View File
@@ -0,0 +1,3 @@
HSRename 1.0.11
- Fix multi-episode detection when filename uses end episode (S01E06 for E05-E06)
- Detect dual titles separated by " - " (Otto 3000 - Night Prowler) as combined pairs