Fix auto-rename episode pollution and deferred nested extraction abort

- Fix episode-token pollution: packageExtraCandidates included ALL item
  filenames, causing resolveEpisodeTokenForAutoRename to pick up episode
  tokens from unrelated files (e.g. S01E07 from 4sf-...-s01e07 applied
  to all hrs-...-101/102/103 files). This also caused (2)(3) MKV
  suffixes when multiple files were renamed to the same wrong episode.
  Now only the package name (outputDir) is used as extra candidate.
- Fix deferred nested extraction missing abort signal: the nested
  extractPackageArchives call in runDeferredPostExtraction did not
  receive deferredController.signal, making it unabortable on
  stop/cancel/restart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe 2026-03-22 16:20:47 +01:00
parent 6dcd3796e9
commit 78fef627bb

View File

@ -3281,23 +3281,16 @@ export class DownloadManager extends EventEmitter {
}
let renamed = 0;
// Collect additional folder candidates from package metadata (outputDir, item filenames)
// Collect additional folder candidates from package metadata (outputDir only).
// Item filenames are intentionally excluded: they contain episode tokens from
// OTHER files in the package, which pollute resolveEpisodeTokenForAutoRename
// and cause all files to receive the same wrong episode number.
const packageExtraCandidates: string[] = [];
if (pkg) {
const outputBase = path.basename(pkg.outputDir || "");
if (outputBase) {
packageExtraCandidates.push(outputBase);
}
for (const itemId of pkg.itemIds) {
const item = this.session.items[itemId];
if (item?.fileName) {
const itemBase = path.basename(item.fileName, path.extname(item.fileName));
const stripped = itemBase.replace(/\.part\d+$/i, "").replace(/\.vol\d+[+\d]*$/i, "");
if (stripped) {
packageExtraCandidates.push(stripped);
}
}
}
}
for (const sourcePath of videoFiles) {
@ -10626,6 +10619,7 @@ export class DownloadManager extends EventEmitter {
removeLinks: false,
removeSamples: false,
passwordList: this.settings.archivePasswordList,
signal: deferredController.signal,
packageId,
onlyArchives: new Set(nestedCandidates.map((p) => process.platform === "win32" ? path.resolve(p).toLowerCase() : path.resolve(p))),
maxParallel: this.settings.maxParallelExtract || 2,