Auto-rename safety net: never strip valid SxxExx episode token
Real-world scenario from user logs: package "Drei.Meter.ueber.dem.Himmel.
S01GERMAN.DL.720P.WEB.X264-WAYNE" (note malformed S01GERMAN with no
separator) caused the auto-renamer to strip the source's S01E01..S01E08
episode tokens because SCENE_SEASON_ONLY_RE doesn't match a season followed
by an immediate letter (no separator).
Result: all 8 episodes in the season pack collapsed to the same target name
and collided in the MKV library with (2)(3)(4)(5)(6)(7)(8) suffixes.
Fix: After buildAutoRenameBaseNameFromFoldersWithOptions, check if the
source filename has a valid episode token. If yes:
1. If target has NO episode token: try to insert it via regex replacement
(Sxx<garbage> -> SxxExx.<garbage>), then via applyEpisodeTokenToFolderName.
If both fail, skip the rename entirely (preserve source name).
2. If target has a DIFFERENT episode token: skip the rename (mislabel risk).
This guard is the last line of defense against the helper's regex
limitations on malformed package names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
90473b13cb
commit
1dfb486145
@@ -3465,7 +3465,7 @@ export class DownloadManager extends EventEmitter {
|
||||
folderCandidates.push(extra);
|
||||
}
|
||||
}
|
||||
const targetBaseName = buildAutoRenameBaseNameFromFoldersWithOptions(folderCandidates, sourceBaseName, {
|
||||
let targetBaseName = buildAutoRenameBaseNameFromFoldersWithOptions(folderCandidates, sourceBaseName, {
|
||||
forceEpisodeForSeasonFolder: true
|
||||
});
|
||||
const resolveRenameItem = (...extra: Array<string | null | undefined>): { item: DownloadItem | null; matchedBy: string | null } => {
|
||||
@@ -3474,6 +3474,60 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
return this.inferItemForMediaLog(pkg, sourcePath, sourceName, folderCandidates.join(" "), targetBaseName || "", ...extra);
|
||||
};
|
||||
// SAFETY NET: Never strip a valid SxxExx token from the source filename.
|
||||
// If the source already has an episode token but the computed target lost it
|
||||
// (e.g. malformed package name "S01GERMAN" with no separator), preserve the
|
||||
// episode by either inserting it into the target or skipping the rename entirely.
|
||||
// Without this guard, all episodes from a malformed pack collapse to one name
|
||||
// and collide with (2)(3)(4) suffixes in the MKV library.
|
||||
const sourceEpisodeToken = extractEpisodeToken(sourceBaseName);
|
||||
if (targetBaseName && sourceEpisodeToken) {
|
||||
const targetEpisodeToken = extractEpisodeToken(targetBaseName);
|
||||
if (!targetEpisodeToken) {
|
||||
// Try to insert the source's episode token: replace "Sxx<garbage>" with "SxxExx.<garbage>"
|
||||
const insertedEpisode = targetBaseName.replace(
|
||||
/(^|[._\-\s])(s\d{1,2})(?=[A-Za-z0-9])/i,
|
||||
`$1${sourceEpisodeToken}.`
|
||||
);
|
||||
if (insertedEpisode !== targetBaseName && extractEpisodeToken(insertedEpisode)) {
|
||||
logger.info(`Auto-Rename Safety: Episode-Token in Target eingefuegt: ${targetBaseName} -> ${insertedEpisode}`);
|
||||
targetBaseName = insertedEpisode;
|
||||
} else {
|
||||
const repaired = applyEpisodeTokenToFolderName(targetBaseName, sourceEpisodeToken);
|
||||
if (repaired && extractEpisodeToken(repaired)) {
|
||||
logger.info(`Auto-Rename Safety: Episode-Token via applyToken: ${targetBaseName} -> ${repaired}`);
|
||||
targetBaseName = repaired;
|
||||
} else {
|
||||
logger.warn(`Auto-Rename Safety: Skipping rename - target wuerde Episode-Token verlieren (source=${sourceBaseName}, target=${targetBaseName})`);
|
||||
if (pkg) {
|
||||
const resolved = resolveRenameItem();
|
||||
this.logRenameProcess(pkg, "WARN", "auto-rename", "Auto-Rename uebersprungen: Episode-Token wuerde verloren gehen", {
|
||||
sourcePath,
|
||||
sourceName,
|
||||
sourceEpisodeToken,
|
||||
targetBaseName
|
||||
}, resolved.item, resolved.matchedBy);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (targetEpisodeToken !== sourceEpisodeToken) {
|
||||
// Target has a DIFFERENT episode token than source — that's a clear sign
|
||||
// the rename is wrong (would mislabel the episode). Skip to be safe.
|
||||
logger.warn(`Auto-Rename Safety: Skipping rename - Episode-Token Mismatch (source=${sourceEpisodeToken}, target=${targetEpisodeToken})`);
|
||||
if (pkg) {
|
||||
const resolved = resolveRenameItem();
|
||||
this.logRenameProcess(pkg, "WARN", "auto-rename", "Auto-Rename uebersprungen: Episode-Token Mismatch", {
|
||||
sourcePath,
|
||||
sourceName,
|
||||
sourceEpisodeToken,
|
||||
targetEpisodeToken,
|
||||
targetBaseName
|
||||
}, resolved.item, resolved.matchedBy);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!targetBaseName) {
|
||||
if (pkg) {
|
||||
this.logPackageForPackage(pkg, "WARN", "Auto-Rename übersprungen: kein Zielname", {
|
||||
|
||||
Reference in New Issue
Block a user