Fix: bereits sauber benannte Folgen werden vom Collect nicht mehr verkrueppelt (Miniserien)
Bei Serien, deren per-Episode-Ordner nur einen Episode-only-Token + Titel tragen
("Show.E01.Titel...-GRP", KEIN S01), benannte der Collect eine vom Auto-Rename bereits
korrekt benannte Datei ("Show.S01E01...-GRP.mkv") neu — und haengte den Staffel/Folgen-
Token HINTER die Scene-Gruppe ("...-GRP.S01E01"). In der Library stand dann der Episoden-
titel + ein angehaengtes S01E01 statt sauber S01E01 (gemeldet fuer "Steven Spielbergs Taken").
decideAutoRenameBaseName behaelt im Guard-B-Zweig "Ziel-Ordner ohne SxxExx" jetzt die
QUELLE, wenn sie ein nicht obfuskierter Scene-Name ist (sie traegt dort den einzigen echten
SxxExx-Token) — statt den Token an den Ordnernamen anzuhaengen. Obfuskierte/rohe Quellen
werden weiter aus dem Ordner sauber benannt. Wirkt in Collect und Auto-Rename.
Adversarial (Workflow) abgesichert: der Diskriminator ist allein "Quelle obfuskiert?" —
die Praefix-Laenge ist KEIN Kriterium, sonst fielen kurze Serien (ER, V, 24, Yu) durch und
zeigten denselben Bug. Regressionstest mit ER.S01E01 gepinnt. 4 Unit- + 1 Integrationstest.
This commit is contained in:
parent
7b39b33cc7
commit
07b034440b
@ -1502,6 +1502,19 @@ export function decideAutoRenameBaseName(
|
|||||||
if (targetBaseName && sourceEpisodeToken) {
|
if (targetBaseName && sourceEpisodeToken) {
|
||||||
const targetEpisodeToken = extractEpisodeToken(targetBaseName);
|
const targetEpisodeToken = extractEpisodeToken(targetBaseName);
|
||||||
if (!targetEpisodeToken) {
|
if (!targetEpisodeToken) {
|
||||||
|
// Die QUELLE traegt einen sauberen SxxExx-Token, der Ziel-Ordner aber nur einen
|
||||||
|
// Episoden-Titel mit Episode-only-Token (Ordner "Show.E01.Titel...-GRP", Quelle bereits
|
||||||
|
// "Show.S01E01...-GRP" — z.B. Miniserien, die der Auto-Rename schon korrekt benannt hat).
|
||||||
|
// Den Quell-Token an den Ordnernamen anzuhaengen erzeugt einen verkrueppelten Namen
|
||||||
|
// ("...-GRP.S01E01" — Token HINTER der Gruppe). In DIESEM Zweig traegt die Quelle den
|
||||||
|
// EINZIGEN SxxExx-Token (der Ordner hat keinen) → ist die Quelle ein sauberer, NICHT
|
||||||
|
// obfuskierter Scene-Name, ist sie autoritativ → behalten, den Ordner NICHT verwenden.
|
||||||
|
// (Die Laenge des Serien-Praefixes ist KEIN Kriterium: kurze Serien wie "ER"/"V"/"24"
|
||||||
|
// sind genauso autoritativ; nur Obfuskierung soll den Ordner gewinnen lassen.) Greift
|
||||||
|
// v.a. im Collect, der sonst den fertigen Auto-Rename-Namen wieder zerstoeren wuerde.
|
||||||
|
if (!looksLikeObfuscatedSceneFileName(sourceName)) {
|
||||||
|
return { kind: "skip", reason: "source-better", targetBaseName };
|
||||||
|
}
|
||||||
const insertedEpisode = targetBaseName.replace(
|
const insertedEpisode = targetBaseName.replace(
|
||||||
/(^|[._\-\s])(s\d{1,2})(?=[A-Za-z0-9])/i,
|
/(^|[._\-\s])(s\d{1,2})(?=[A-Za-z0-9])/i,
|
||||||
`$1${sourceEpisodeToken}.`
|
`$1${sourceEpisodeToken}.`
|
||||||
|
|||||||
@ -307,3 +307,29 @@ abgelehnt (kein Over-Firing). v1.7.180-Fallback nutzt jetzt dieselben Module-Con
|
|||||||
Auto-Rename UND Collect (beide via decideAutoRenameBaseName). 5 Unit- + 1 Collect-Integrationstest.
|
Auto-Rename UND Collect (beide via decideAutoRenameBaseName). 5 Unit- + 1 Collect-Integrationstest.
|
||||||
**Methodik-Lektion:** Die naheliegende Hypothese (a/b-Suffix) per Diagnose-Test widerlegt, BEVOR gefixt —
|
**Methodik-Lektion:** Die naheliegende Hypothese (a/b-Suffix) per Diagnose-Test widerlegt, BEVOR gefixt —
|
||||||
das Lookahead genau gelesen statt angenommen. Spart einen Fix am falschen Ort.
|
das Lookahead genau gelesen statt angenommen. Spart einen Fix am falschen Ort.
|
||||||
|
|
||||||
|
## 2026-06-05 — Collect zerstoert fertigen S01E01-Namen via Episoden-Titel-Ordner (Miniserie)
|
||||||
|
**Symptom (rename-session 2026-06-05):** Miniserie "Steven Spielbergs Taken" landete als
|
||||||
|
"...E01.Hinter.dem.Himmel...-GTVG.S01E01.mkv" (Episodentitel + hinten angehaengtes S01E01) statt sauber
|
||||||
|
"...S01E01...-GTVG.mkv". User: "keine Staffel, nur Episodentitel".
|
||||||
|
**Root Cause (diagnostisch bewiesen):** Auto-Rename benannte korrekt zu "...S01E01...-GTVG.mkv" (kombiniert
|
||||||
|
S01 aus dem Paket/Season-Ordner + E01 aus der Quelle). Der COLLECT (deriveCleanCollectFileName ->
|
||||||
|
decideAutoRenameBaseName) leitet die Datei NEU ab — Quelle ist nun der schon-saubere Name. Der per-Episode-
|
||||||
|
Ordner traegt aber nur einen Episode-only-Token + Titel ("...E01.Hinter.dem.Himmel...-GTVG", KEIN S01).
|
||||||
|
buildAutoRenameBaseName nimmt den Ordner (Gruppen-Suffix -GTVG vorhanden). In Guard B `if (!targetEpisodeToken)`
|
||||||
|
wird der Quell-Token an den Ordnernamen ANGEHAENGT (applyEpisodeTokenToFolderName) -> "...-GTVG.S01E01"
|
||||||
|
(Token HINTER der Gruppe = verkrueppelt). Der Root-Guard greift NICHT, weil der Season-Ordner einen S01-Token
|
||||||
|
liefert (anyFolderHasSeasonOrEpisode=true).
|
||||||
|
**Fix:** In Guard B, im `!targetEpisodeToken`-Zweig VOR dem Anhaengen: ist die QUELLE ein NICHT
|
||||||
|
obfuskierter Scene-Name (`!looksLikeObfuscatedSceneFileName(sourceName)`), dann
|
||||||
|
`return {kind:"skip", reason:"source-better"}` -> Collect behaelt den fertigen Namen. In diesem Zweig
|
||||||
|
traegt die Quelle den EINZIGEN SxxExx-Token (Ordner hat keinen) -> obfuskiert? -> Ordner gewinnt (Append),
|
||||||
|
sauber? -> Quelle gewinnt. Greift NUR im `!targetEpisodeToken`-Zweig (Ordner ohne SxxExx); safari
|
||||||
|
(Ordner MIT Token) unberuehrt. 4 Unit- + 1 Collect-Integrationstest. tsc 6 (Baseline), 700/700 gruen, Build gruen.
|
||||||
|
**Methodik:** Erst Diagnose (decideAutoRenameBaseName mit Collect-Inputs) -> exakt der mangled Name
|
||||||
|
reproduziert. Per User-Wunsch adversarial via Workflow gegengeprueft (ultracode, 3 Lenses + Synthese).
|
||||||
|
**Adversarialer Befund (Workflow fing's):** Mein erster Guard hatte einen ZWEITEN Konjunkt
|
||||||
|
`hasMeaningfulSeriesPrefix(sourceBaseName)` (>=3 Alpha vor S0x). Der ist sachfremd: KURZE Serien (ER, V,
|
||||||
|
24, Yu) fallen durch -> selber verkrueppelter Name. Gestrichen -> nur `!obfuskiert` gaten. Lehre: ein
|
||||||
|
zusaetzlicher "klingt-vernuenftig"-Konjunkt (Praefix-Laenge) kann eine ganze reale Klasse (Kurz-Titel)
|
||||||
|
stumm ausschliessen; adversariale Verifikation mit konkretem Gegenbeispiel (ER.S01E01) hat's gefunden.
|
||||||
|
|||||||
@ -1103,3 +1103,51 @@ describe("complete episode folder WITHOUT group suffix (codec/resolution only)",
|
|||||||
expect(decision.kind).toBe("skip");
|
expect(decision.kind).toBe("skip");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("collect must not mangle an already-clean SxxExx name via an episode-title folder", () => {
|
||||||
|
const hash = "c284d9d9072eaf3ac314d05f951dd115";
|
||||||
|
// Echter Bug (rename-session 2026-06-05): Miniserie "Steven Spielbergs Taken". Auto-Rename
|
||||||
|
// benannte korrekt zu "...S01E01...-GTVG". Der per-Episode-Ordner traegt aber nur einen
|
||||||
|
// Episode-only-Token + Titel ("...E01.Hinter.dem.Himmel...-GTVG", KEIN S01). Der Collect leitete
|
||||||
|
// daraus neu ab und HAENGTE den Quell-Token verkrueppelt an: "...-GTVG.S01E01" → in der Library
|
||||||
|
// stand dann "E01.Titel...S01E01" statt sauber "S01E01".
|
||||||
|
const epFolder = "Steven.Spielbergs.Taken.E01.Hinter.dem.Himmel.German.720p.HDTV.x264-GTVG";
|
||||||
|
const pkgFolder = "Steven.Spielbergs.Taken.S01.German.720p.HDTV.x264-GTVG";
|
||||||
|
const cleanSource = "Steven.Spielbergs.Taken.S01E01.German.720p.HDTV.x264-GTVG";
|
||||||
|
|
||||||
|
it("keeps the clean source (skip) instead of appending the token to the episode-title folder", () => {
|
||||||
|
const decision = decideAutoRenameBaseName([epFolder, pkgFolder], cleanSource + ".mkv", cleanSource, epFolder, pkgFolder);
|
||||||
|
expect(decision.kind).toBe("skip");
|
||||||
|
// NICHT der verkrueppelte "...-GTVG.S01E01"-Name.
|
||||||
|
expect(JSON.stringify(decision)).not.toContain("GTVG.S01E01");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still cleans a JUNK/obfuscated source via an episode-title folder (append path intact, no skip)", () => {
|
||||||
|
// Obfuskierter Hoster-Name (obf=true) → meine Klausel greift NICHT (sie behaelt nur saubere
|
||||||
|
// Quellen). Mit Season-Ordner als Kontext (Root-Guard ok) wird der Token weiter angewandt:
|
||||||
|
// die Quelle wird NICHT roh behalten. Pruegt, dass der Fix nicht zu breit ist.
|
||||||
|
const epFolder = "Show.E05.Die.Sache.German.720p.HDTV.x264-GRP";
|
||||||
|
const seasonFolder = "Show.S01.German.720p.HDTV.x264-GRP";
|
||||||
|
const decision = decideAutoRenameBaseName([epFolder, seasonFolder], "scn-show7-S01E05.mkv", "scn-show7-S01E05", epFolder, seasonFolder);
|
||||||
|
expect(decision.kind).toBe("rename");
|
||||||
|
expect(extractEpisodeToken((decision as any).baseName)).toBe("S01E05");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does NOT affect a folder that already carries an SxxExx token (safari S04E08a stays a rename)", () => {
|
||||||
|
const folder = "Fluss-Monster.S04E08a.Am.Essequibo.Teil.1.German.DOKU.SATRiP.XviD";
|
||||||
|
const decision = decideAutoRenameBaseName([folder, hash], "safari-fm-s04e08a.avi", "safari-fm-s04e08a", hash, hash);
|
||||||
|
expect(decision).toEqual({ kind: "rename", baseName: folder });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps a clean SHORT-prefix series source (ER) instead of the crippled token append", () => {
|
||||||
|
// Adversarial-Befund: die Praefix-Laenge darf KEIN Kriterium sein. Kurze Serien (ER, V, 24, Yu)
|
||||||
|
// sind genauso autoritativ; mit dem alten `hasMeaningfulSeriesPrefix`-Konjunkt (>=3 Alpha vor S0x)
|
||||||
|
// waere ER durchgefallen -> selber verkrueppelter Name wie der gemeldete Bug.
|
||||||
|
const epFolder = "ER.E01.Tag.und.Nacht.German.720p.HDTV.x264-GROUP";
|
||||||
|
const seasonFolder = "ER.S01.German.720p.HDTV.x264-GROUP";
|
||||||
|
const cleanSource = "ER.S01E01.German.720p.HDTV.x264-GROUP";
|
||||||
|
const decision = decideAutoRenameBaseName([epFolder, seasonFolder], cleanSource + ".mkv", cleanSource, epFolder, seasonFolder);
|
||||||
|
expect(decision.kind).toBe("skip");
|
||||||
|
expect(JSON.stringify(decision)).not.toContain("GROUP.S01E01");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -9752,6 +9752,71 @@ describe("download manager", () => {
|
|||||||
void manager;
|
void manager;
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
|
it("collect KEEPS the clean SxxExx name and does NOT mangle it via an episode-title folder (Taken S01E01)", async () => {
|
||||||
|
// Echter Bug (rename-session 2026-06-05): Auto-Rename hatte die Datei bereits korrekt zu
|
||||||
|
// "...S01E01...-GTVG.mkv" benannt. Der per-Episode-Ordner traegt nur "E01" + Titel (kein S01).
|
||||||
|
// Der Collect leitete daraus neu ab und haengte den Token verkrueppelt an
|
||||||
|
// ("...-GTVG.S01E01"). Erwartung: der saubere S01E01-Name bleibt unangetastet.
|
||||||
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
|
||||||
|
tempDirs.push(root);
|
||||||
|
|
||||||
|
const packageName = "Steven.Spielbergs.Taken.S01.German.720p.HDTV.x264-GTVG";
|
||||||
|
const outputDir = path.join(root, "downloads", packageName);
|
||||||
|
const extractDir = path.join(root, "extract", packageName);
|
||||||
|
const epFolder = "Steven.Spielbergs.Taken.E01.Hinter.dem.Himmel.German.720p.HDTV.x264-GTVG";
|
||||||
|
const cleanName = "Steven.Spielbergs.Taken.S01E01.German.720p.HDTV.x264-GTVG.mkv";
|
||||||
|
const epDir = path.join(extractDir, epFolder);
|
||||||
|
fs.mkdirSync(epDir, { recursive: true });
|
||||||
|
// Datei liegt bereits SAUBER benannt vor (so wie Auto-Rename sie hinterlassen hat).
|
||||||
|
fs.writeFileSync(path.join(epDir, cleanName), Buffer.alloc(4096, 5));
|
||||||
|
|
||||||
|
const session = emptySession();
|
||||||
|
const packageId = `${packageName}-pkg`;
|
||||||
|
const createdAt = Date.now() - 60_000;
|
||||||
|
session.packageOrder = [packageId];
|
||||||
|
session.packages[packageId] = {
|
||||||
|
id: packageId,
|
||||||
|
name: packageName,
|
||||||
|
outputDir,
|
||||||
|
extractDir,
|
||||||
|
status: "completed",
|
||||||
|
itemIds: [],
|
||||||
|
cancelled: false,
|
||||||
|
enabled: true,
|
||||||
|
createdAt,
|
||||||
|
updatedAt: createdAt
|
||||||
|
};
|
||||||
|
|
||||||
|
const mkvLibraryDir = path.join(root, "mkv-library");
|
||||||
|
const manager = new DownloadManager(
|
||||||
|
{
|
||||||
|
...defaultSettings(),
|
||||||
|
outputDir: path.join(root, "downloads"),
|
||||||
|
extractDir: path.join(root, "extract"),
|
||||||
|
autoExtract: true,
|
||||||
|
autoRename4sf4sj: true,
|
||||||
|
collectMkvToLibrary: true,
|
||||||
|
mkvLibraryDir,
|
||||||
|
enableIntegrityCheck: false,
|
||||||
|
cleanupMode: "none"
|
||||||
|
},
|
||||||
|
session,
|
||||||
|
createStoragePaths(path.join(root, "state"))
|
||||||
|
);
|
||||||
|
|
||||||
|
await (manager as any).collectMkvFilesToLibrary(packageId, session.packages[packageId], undefined, false);
|
||||||
|
|
||||||
|
// Sauberer S01E01-Name bleibt; KEIN verkrueppelter "...E01.Titel...S01E01"-Name.
|
||||||
|
expect(fs.existsSync(path.join(mkvLibraryDir, cleanName))).toBe(true);
|
||||||
|
const mangled = `${epFolder}.S01E01.mkv`;
|
||||||
|
expect(fs.existsSync(path.join(mkvLibraryDir, mangled))).toBe(false);
|
||||||
|
// Nichts mit dem Episoden-Titel im Library-Ordner.
|
||||||
|
const inLib = fs.readdirSync(mkvLibraryDir);
|
||||||
|
expect(inLib.some((n) => /Hinter\.dem\.Himmel/i.test(n))).toBe(false);
|
||||||
|
|
||||||
|
void manager;
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
it("deferred final pass renames fresh files before collecting them (no scene names in library)", async () => {
|
it("deferred final pass renames fresh files before collecting them (no scene names in library)", async () => {
|
||||||
// Folge-Fund zu 18eada9 (verifiziert via Advisor-Gate): 18eada9 schloss den
|
// Folge-Fund zu 18eada9 (verifiziert via Advisor-Gate): 18eada9 schloss den
|
||||||
// "frische Datei landet unbenannt"-Bug nur fuer den HYBRID-Pfad (deferFreshFiles=true
|
// "frische Datei landet unbenannt"-Bug nur fuer den HYBRID-Pfad (deferFreshFiles=true
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user