Files
rippy/rippy-windows/test/ablegen.test.ts
HitonabiandClaude Opus 5 e7e33e9021 feat(v5): jede Spur einzeln anhaken — Codec und Kanäle je Tonspur, Untertitel genauso
WAS: Der Auswahl-Dialog zeigt je Titel jede Ton- und Untertitelspur, die
der Info-Lauf gemessen hat („German · DTS-HD MA · Surround 5.1", „German ·
DD · Stereo (Kommentar)", „German · PGS (nur erzwungene)"), vorbelegt wie
bisher (je Wunschsprache die erste Tonspur, alle Untertitel der Wunsch-
sprachen) — angehakt wird einzeln. Die Untertitel-Vorwahl je Extra ist
jetzt eine Spur. Der Parser liest dazu SINFO-Attribute 2 (Anordnung),
22 (Flags) und markiert abgeleitete Spuren (Bit 0x800, an der Kizuna-
Disc gemessen): DTS-Kerne werden nicht angeboten, „nur erzwungene"-
Untertitel schon. Die Ablage scannt die Roh-Datei und findet die
gewählten MakeMKV-Spuren gestuft wieder (Sprache + Codec-Familie +
Kanäle → … → Sprache), HandBrake bekommt `--audio 1,3 --subtitle 2,1
--subtitle-default=1`; Fehlendes wird gemeldet, scheitert der Scan,
gelten die Sprachen der gewählten Spuren als Liste. MakeMKVs Auswahl-
regel lässt Stereo-Doppel jetzt stehen (nur -sel:havecore); Rippys alte
Regel wird in der Registry ersetzt, eine fremde bleibt.
Dazu F31 (Durchsicht): `--aencoder copy` ohne `--audio-copy-mask`
verengt HandBrakeCLI 1.11.2 auf „copy:aac" — AC3/DTS/TrueHD wurden seit
5.0 in AAC umgerechnet. Beide Wege setzen die Maske jetzt ausdrücklich
(gemessen 12.09.2026: AC3 bleibt AC3, Passthru-Zeile nennt alle Codecs).
WARUM: Commander-Entscheid 12.09.2026 („Jede Spur einzeln anhaken") —
bisher ließ sich je Sprache nur „alles oder nichts" wählen, und der
Kommentar-Ton fehlte in der Roh-Datei (havemulti). Gemessen am
mitgelieferten HandBrakeCLI mit ffmpeg-gebauter Quelle
(test/messung.spuren.test.ts, RIPPY_MESSUNG=1): --audio 1,3 → jpn und
deu-Kommentar, --subtitle 2,1 --subtitle-default=1 → eng (default) vor
deu, --subtitle none → keine Spur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 22:11:27 +02:00

486 lines
21 KiB
TypeScript

// Tests von Kompression + Ablage (kern/ablauf/ablegen.ts, 5.4.0) — mit
// nachgebildetem HandBrake, das die Ausgabedatei wirklich SCHREIBT (die
// Serien-Umbenennung braucht sie). Die Regeln: Kompression nur wenn
// gewollt UND HandBrake da, sonst VERSCHIEBEN; ein Kompressions-Fehler
// lässt den verlustfreien Rip STEHEN und sagt wo er liegt; Sprachen je
// Datei erreichen HandBrake; Folgen von Hand schlagen die Laufzeit-Raterei.
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { KompressionsAuftrag, RohDatei } from '../src/kern/ablauf/auftrag'
import type { Zuordnung } from '../src/kern/metadaten/zuordnung'
import { LEERER_TREFFER } from '../src/kern/metadaten/zuordnung'
const hb = vi.hoisted(() => ({
aufrufe: [] as Array<Record<string, unknown>>,
ergebnis: 'success' as 'success' | 'error' | 'cancelled',
}))
vi.mock('../src/kern/komprimieren/handbrake', async () => {
const fs = await import('node:fs')
return {
komprimieren: vi.fn(async (o: { ausgabe: string; aufFortschritt?: (p: number) => void }) => {
hb.aufrufe.push({ ...o })
o.aufFortschritt?.(50)
if (hb.ergebnis === 'cancelled') return { status: 'cancelled' }
if (hb.ergebnis === 'error') return { status: 'error', fehler: 'HandBrake endete mit Code 3' }
fs.writeFileSync(o.ausgabe, 'encoded')
return { status: 'success', ausgabe: o.ausgabe }
}),
}
})
import { komprimierenUndAblegen, type AblageUmgebung } from '../src/kern/ablauf/ablegen'
let wegwerf: string[] = []
afterEach(() => {
hb.aufrufe = []
hb.ergebnis = 'success'
for (const ordner of wegwerf.splice(0)) rmSync(ordner, { recursive: true, force: true })
})
function zuordnung(typ: 'movie' | 'tv', titel: string, jahr: number | null): Zuordnung {
return {
titel,
jahr,
confidence: 0.95,
meta: { ...LEERER_TREFFER, typ, id: 149, titel, jahr, quelle: 'tmdb', laufzeitMinuten: 124 },
}
}
function aufbau(dateinamen: string[], rollen: Array<RohDatei['rolle']> = []): {
ablage: string
roh: string
dateien: RohDatei[]
} {
const ablage = mkdtempSync(join(tmpdir(), 'rippy-ablegen-'))
wegwerf.push(ablage)
const roh = join(ablage, 'roh', 'bluray-2026-08-30T12-00-00')
mkdirSync(roh, { recursive: true })
const dateien = dateinamen.map((name, i) => {
const pfad = join(roh, name)
writeFileSync(pfad, 'roh'.repeat(10))
return {
pfad,
titelNr: i,
rolle: rollen[i] ?? 'hauptfilm',
dauerS: 3167,
groesseBytes: 30,
audioSprachen: [],
untertitelSprachen: [],
}
})
return { ablage, roh, dateien }
}
function auftrag(teil: Partial<KompressionsAuftrag> & { rohOrdner: string; dateien: RohDatei[] }): KompressionsAuftrag {
return {
vorgangId: 'bluray-2026-08-30T12-00-00',
laufwerk: 'G',
discTyp: 'bluray',
zuordnung: null,
staffel: null,
ersteFolge: null,
discNr: null,
fingerprint: 'LABEL|33',
lesefehler: false,
hinweise: [],
begonnen: '2026-08-30T12:00:00Z',
preset: '',
zielOrdner: '',
...teil,
}
}
function umgebung(ablage: string, überschreiben: Partial<AblageUmgebung> = {}): AblageUmgebung {
return {
handbrake: () => 'C:\\hb\\HandBrakeCLI.exe',
ablage: () => ablage,
einstellungen: () => ({ audioSprachen: 'deu,eng', untertitelSprachen: 'deu' }),
jetzt: () => new Date('2026-08-30T13:00:00Z'),
spurenScannen: async () => null,
...überschreiben,
}
}
const KEIN_ABBRUCH = new AbortController().signal
describe('komprimierenUndAblegen', () => {
it('komprimiert in Filme/<Titel (Jahr)> mit NFO und meldet die Phasen', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const bibliothek: string[] = []
const phasen: string[] = []
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, zuordnung: zuordnung('movie', 'Akira', 1988) }),
umgebung(ablage, { bibliothekEintragen: (e) => bibliothek.push(e.titel) }),
(s) => phasen.push(s.phase),
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fertig')
expect(ergebnis.komprimiert).toBe(true)
expect(ergebnis.zielOrdner).toBe(join(ablage, 'Filme', 'Akira (1988)'))
expect(existsSync(join(ergebnis.zielOrdner, 'movie.nfo'))).toBe(true)
expect(ergebnis.dateien).toEqual([join(ergebnis.zielOrdner, 'Akira (1988).mkv')])
expect(phasen).toContain('komprimiert')
expect(phasen.at(-1)).toBe('legt-ab')
expect(bibliothek).toEqual(['Akira'])
expect(ergebnis.text).toContain('komprimiert nach')
})
it('Kompression abgewählt: Roh-Dateien werden VERSCHOBEN, mit NFO — der Roh-Ordner bleibt leer zurück', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, discTyp: 'uhd', zuordnung: zuordnung('movie', 'Akira', 1988) }),
umgebung(ablage, { einstellungen: () => ({ transcodePresetUhd: 'keine' }) }),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fertig')
expect(ergebnis.komprimiert).toBe(false)
expect(ergebnis.text).toContain('ohne Kompression')
expect(existsSync(join(ergebnis.zielOrdner, 'Akira (1988).mkv'))).toBe(true)
expect(existsSync(join(ergebnis.zielOrdner, 'movie.nfo'))).toBe(true)
expect(readdirSync(roh)).toEqual([])
expect(hb.aufrufe).toHaveLength(0)
})
it('ohne HandBrake: ebenfalls verschieben, nicht liegen lassen', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien }),
umgebung(ablage, { handbrake: () => '' }),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.komprimiert).toBe(false)
expect(readdirSync(roh)).toEqual([])
expect(ergebnis.zielOrdner).toBe(join(ablage, 'Filme', 'bluray-2026-08-30T12-00-00'))
})
it('Kompressions-Fehler: Roh bleibt stehen und wird GENANNT', async () => {
hb.ergebnis = 'error'
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const ergebnis = await komprimierenUndAblegen(auftrag({ rohOrdner: roh, dateien }), umgebung(ablage), () => {}, KEIN_ABBRUCH)
expect(ergebnis.status).toBe('fehler')
expect(ergebnis.text).toContain('bleibt stehen')
expect(ergebnis.text).toContain(roh)
expect(existsSync(dateien[0].pfad)).toBe(true)
})
it('Abbruch mittendrin: abgebrochen, Roh bleibt', async () => {
hb.ergebnis = 'cancelled'
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const ergebnis = await komprimierenUndAblegen(auftrag({ rohOrdner: roh, dateien }), umgebung(ablage), () => {}, KEIN_ABBRUCH)
expect(ergebnis.status).toBe('abgebrochen')
expect(existsSync(dateien[0].pfad)).toBe(true)
})
it('Sprachen je Datei erreichen HandBrake — fremder Ton schaltet die deutschen Untertitel vor', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv', 'title_t01.mkv'])
dateien[0].audioSprachen = ['jpn']
dateien[0].untertitelSprachen = ['eng', 'deu']
const ergebnis = await komprimierenUndAblegen(auftrag({ rohOrdner: roh, dateien }), umgebung(ablage), () => {}, KEIN_ABBRUCH)
expect(ergebnis.status).toBe('fertig')
expect(hb.aufrufe).toHaveLength(2)
// Datei 0: eigene Wahl — deu nach vorn, Vorwahl an.
expect(hb.aufrufe[0].audioSprachen).toEqual(['jpn'])
expect(hb.aufrufe[0].untertitelSprachen).toEqual(['deu', 'eng'])
expect(hb.aufrufe[0].untertitelStandard).toBe(true)
// Datei 1: nichts gewählt — die globale Einstellung, keine Vorwahl.
expect(hb.aufrufe[1].audioSprachen).toEqual(['deu', 'eng'])
expect(hb.aufrufe[1].untertitelSprachen).toEqual(['deu'])
expect(hb.aufrufe[1].untertitelStandard).toBe(false)
})
it('der Scan der Roh-Datei meldet fehlende Sprachen als Hinweis', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
dateien[0].audioSprachen = ['jpn']
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien }),
umgebung(ablage, {
spurenScannen: async () => ({ audio: [{ nr: 1, lang: 'deu', beschreibung: 'Deutsch', codec: 'dtshd', kanaele: 6 }], untertitel: [], dauerS: 3167 }),
}),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.hinweise.join(' ')).toContain('Tonspur jpn ist NICHT in der Roh-Datei')
})
it('Serie von Hand: Staffel 1, ab Folge 3 → S01E03 und S01E04, Warnung bei TMDb-Abweichung', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv', 'title_t01.mkv'], ['folge', 'folge'])
dateien[1].dauerS = 3249
const ergebnis = await komprimierenUndAblegen(
auftrag({
rohOrdner: roh,
dateien,
zuordnung: zuordnung('tv', 'Spartacus', 2010),
staffel: 1,
ersteFolge: 3,
}),
umgebung(ablage, {
// Folge 4 ist bei TMDb 20 min kürzer — das gibt eine Warnung, aber
// die Hand des Nutzers gilt.
episodenLaufzeiten: async () => [
[3, 3200],
[4, 2000],
],
}),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fertig')
expect(ergebnis.zielOrdner).toBe(join(ablage, 'Serien', 'Spartacus', 'Season 01'))
expect(readdirSync(ergebnis.zielOrdner).filter((n) => n.endsWith('.mkv')).sort()).toEqual([
'Spartacus S01E03.mkv',
'Spartacus S01E04.mkv',
])
expect(ergebnis.dateien.map((d) => d.split(/[\\/]/).pop())).toEqual(['Spartacus S01E03.mkv', 'Spartacus S01E04.mkv'])
expect(ergebnis.hinweise.join(' ')).toContain('Folge 4: Datei 54 min, TMDb 33 min')
expect(existsSync(join(ergebnis.zielOrdner, 'tvshow.nfo'))).toBe(true)
})
it('Serie ohne Vorgabe: Laufzeit-Zuordnung wie bisher — nur bei eindeutigem Treffer', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv', 'title_t01.mkv'], ['folge', 'folge'])
dateien[0].dauerS = 3167
dateien[1].dauerS = 3249
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, zuordnung: zuordnung('tv', 'Spartacus', 2010), staffel: 2 }),
umgebung(ablage, {
episodenLaufzeiten: async () => [
[1, 3180],
[2, 3240],
],
}),
() => {},
KEIN_ABBRUCH,
)
expect(readdirSync(ergebnis.zielOrdner).filter((n) => n.endsWith('.mkv')).sort()).toEqual([
'Spartacus S02E01.mkv',
'Spartacus S02E02.mkv',
])
})
it('Extras liegen im Unterordner extras\\ neben dem Hauptfilm', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv', 'title_t05.mkv'], ['hauptfilm', 'extra'])
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, zuordnung: zuordnung('movie', 'Akira', 1988) }),
umgebung(ablage),
() => {},
KEIN_ABBRUCH,
)
expect(existsSync(join(ergebnis.zielOrdner, 'Akira (1988).mkv'))).toBe(true)
expect(existsSync(join(ergebnis.zielOrdner, 'extras', 'Akira (1988) - Extra 01.mkv'))).toBe(true)
})
it('Wiederholen: der vorgegebene Zielordner wird benutzt, kein zweiter angelegt', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const ziel = join(ablage, 'Filme', 'Akira (1988)')
mkdirSync(ziel, { recursive: true })
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, zuordnung: zuordnung('movie', 'Akira', 1988), zielOrdner: ziel, preset: 'Fast 1080p30' }),
umgebung(ablage),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.zielOrdner).toBe(ziel)
expect(ergebnis.preset).toBe('Fast 1080p30')
expect(readdirSync(join(ablage, 'Filme'))).toEqual(['Akira (1988)'])
})
it('der Fortschritt ist über beide Dateien gewichtet', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv', 'title_t01.mkv'])
dateien[0].groesseBytes = 3
dateien[1].groesseBytes = 1
const prozente: number[] = []
await komprimierenUndAblegen(auftrag({ rohOrdner: roh, dateien }), umgebung(ablage), (s) => prozente.push(s.prozent), KEIN_ABBRUCH)
// 50 % der ersten (3/4) = 37, 50 % der zweiten = 87.
expect(prozente).toContain(37)
expect(prozente).toContain(87)
})
})
describe('Wiederholen ersetzt die alte Fassung', () => {
it('Serie: die alten S01E03/E04 fallen erst nach dem Encode, dann greift die Umbenennung', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv', 'title_t01.mkv'], ['folge', 'folge'])
const ziel = join(ablage, 'Serien', 'Spartacus', 'Season 01')
mkdirSync(ziel, { recursive: true })
const alt3 = join(ziel, 'Spartacus S01E03.mkv')
const alt4 = join(ziel, 'Spartacus S01E04.mkv')
writeFileSync(alt3, 'alt')
writeFileSync(alt4, 'alt')
const ergebnis = await komprimierenUndAblegen(
auftrag({
rohOrdner: roh,
dateien,
zuordnung: zuordnung('tv', 'Spartacus', 2010),
staffel: 1,
ersteFolge: 3,
zielOrdner: ziel,
ersetzt: [alt3, alt4],
}),
umgebung(ablage),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fertig')
expect(readdirSync(ziel).filter((n) => n.endsWith('.mkv')).sort()).toEqual(['Spartacus S01E03.mkv', 'Spartacus S01E04.mkv'])
expect(readFileSync(alt3, 'utf8')).toBe('encoded')
expect(ergebnis.hinweise.join(' ')).toContain('Alte Fassung ersetzt')
expect(ergebnis.hinweise.join(' ')).not.toContain('existiert schon')
})
it('scheitert der zweite Lauf, bleibt die alte Fassung stehen', async () => {
hb.ergebnis = 'error'
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const ziel = join(ablage, 'Filme', 'Akira (1988)')
mkdirSync(ziel, { recursive: true })
const alt = join(ziel, 'title_t00.mkv')
writeFileSync(alt, 'alt')
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, zuordnung: zuordnung('movie', 'Akira', 1988), zielOrdner: ziel, ersetzt: [alt] }),
umgebung(ablage),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fehler')
expect(readFileSync(alt, 'utf8')).toBe('alt')
})
})
// ── 5.7.0: Funde der Durchsicht vom 12.09.2026 ─────────────────────────
describe('komprimierenUndAblegen — Durchsicht 5.7.0', () => {
// F3: Disc 2 derselben Staffel landet im selben Season-Ordner wie Disc 1.
// Vorher zählte die Umbenennung ALLE MKVs des Ordners („4 Dateien, 2
// Zuordnungen") und tat nichts — der Serien-Merker lief ins Leere.
it('F3: zweite Disc in denselben Season-Ordner — nur ihre Folgen werden benannt', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv', 'title_t01.mkv'], ['folge', 'folge'])
const season = join(ablage, 'Serien', 'Spartacus', 'Season 01')
mkdirSync(season, { recursive: true })
writeFileSync(join(season, 'Spartacus S01E01.mkv'), 'disc1')
writeFileSync(join(season, 'Spartacus S01E02.mkv'), 'disc1')
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, zuordnung: zuordnung('tv', 'Spartacus', 2010), staffel: 1, ersteFolge: 3 }),
umgebung(ablage),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fertig')
expect(readdirSync(season).filter((n) => n.endsWith('.mkv')).sort()).toEqual([
'Spartacus S01E01.mkv',
'Spartacus S01E02.mkv',
'Spartacus S01E03.mkv',
'Spartacus S01E04.mkv',
])
expect(readFileSync(join(season, 'Spartacus S01E01.mkv'), 'utf8')).toBe('disc1')
expect(ergebnis.folgenVergeben).toEqual([3, 4])
})
// F3: Liegt im Season-Ordner noch ein title_t00.mkv der ersten Disc
// (deren Umbenennung ausfiel), darf die zweite es NICHT überschreiben.
it('F3: gleichnamige Datei im Ziel wird nie überschrieben — „(2)" statt Verlust', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'], ['folge'])
const season = join(ablage, 'Serien', 'Spartacus', 'Season 01')
mkdirSync(season, { recursive: true })
writeFileSync(join(season, 'title_t00.mkv'), 'disc1-unbenannt')
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien, zuordnung: zuordnung('tv', 'Spartacus', 2010), staffel: 1 }),
umgebung(ablage),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fertig')
expect(readFileSync(join(season, 'title_t00.mkv'), 'utf8')).toBe('disc1-unbenannt')
expect(existsSync(join(season, 'title_t00 (2).mkv'))).toBe(true)
expect(ergebnis.dateien.map((d) => d.split(/[\\/]/).pop())).toEqual(['title_t00 (2).mkv'])
})
})
// ── 5.7.0: Spuren einzeln ──────────────────────────────────────────────────
describe('Spuren einzeln (5.7.0)', () => {
const jpnHd = { nr: 1, lang: 'jpn', sprache: 'Japanese', codec: 'DTS-HD MA', kanaele: '6', layout: 'Surround 5.1', beschreibung: '', abgeleitet: false }
const deuKommentar = { nr: 4, lang: 'deu', sprache: 'German', codec: 'DD', kanaele: '2', layout: 'Stereo', beschreibung: 'DD Stereo German (Kommentar)', abgeleitet: false }
const deuPgs = { nr: 5, lang: 'deu', sprache: 'German', codec: 'PGS', kanaele: '', layout: '', beschreibung: 'PGS German', abgeleitet: false }
const engPgs = { nr: 7, lang: 'eng', sprache: 'English', codec: 'PGS', kanaele: '', layout: '', beschreibung: 'PGS English', abgeleitet: false }
const scan = {
audio: [
{ nr: 1, lang: 'jpn', beschreibung: '', codec: 'dtshd', kanaele: 6 },
{ nr: 2, lang: 'deu', beschreibung: '', codec: 'ac3', kanaele: 6 },
{ nr: 3, lang: 'deu', beschreibung: '', codec: 'ac3', kanaele: 2 },
],
untertitel: [
{ nr: 1, lang: 'deu', beschreibung: '', codec: 'PGS', kanaele: 0 },
{ nr: 2, lang: 'eng', beschreibung: '', codec: 'PGS', kanaele: 0 },
],
dauerS: 3167,
}
it('gewählte Spuren erreichen HandBrake als Nummern — japanischer Ton stellt die deutschen Untertitel vor und wählt sie vor', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
dateien[0].audioSprachen = ['jpn', 'deu']
dateien[0].untertitelSprachen = ['eng', 'deu']
dateien[0].audioSpuren = [jpnHd, deuKommentar]
dateien[0].untertitelSpuren = [engPgs, deuPgs]
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien }),
umgebung(ablage, { spurenScannen: async () => scan }),
() => {},
KEIN_ABBRUCH,
)
expect(ergebnis.status).toBe('fertig')
expect(hb.aufrufe[0].audioNr).toEqual([1, 3])
expect(hb.aufrufe[0].untertitelNr).toEqual([1, 2])
expect(hb.aufrufe[0].untertitelStandardNr).toBe(1)
expect(ergebnis.hinweise.join(' ')).not.toContain('NICHT in der Roh-Datei')
})
it('eine gewählte Spur fehlt in der Roh-Datei → Hinweis, der Rest läuft', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const fra = { ...jpnHd, nr: 9, lang: 'fra', sprache: 'French' }
dateien[0].audioSpuren = [fra, deuKommentar]
dateien[0].untertitelSpuren = []
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien }),
umgebung(ablage, { spurenScannen: async () => scan }),
() => {},
KEIN_ABBRUCH,
)
expect(hb.aufrufe[0].audioNr).toEqual([3])
expect(hb.aufrufe[0].untertitelNr).toEqual([])
expect(ergebnis.hinweise.join(' ')).toContain('Tonspur French DTS-HD MA ist NICHT in der Roh-Datei')
})
it('Scan der Roh-Datei scheitert → Sprachen der gewählten Spuren als Liste (wie bisher)', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
dateien[0].audioSpuren = [jpnHd]
dateien[0].untertitelSpuren = [deuPgs]
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien }),
umgebung(ablage, { spurenScannen: async () => null }),
() => {},
KEIN_ABBRUCH,
)
expect(hb.aufrufe[0].audioNr).toBeUndefined()
expect(hb.aufrufe[0].audioSprachen).toEqual(['jpn'])
expect(hb.aufrufe[0].untertitelSprachen).toEqual(['deu'])
expect(hb.aufrufe[0].untertitelStandard).toBe(true)
expect(ergebnis.hinweise.join(' ')).toContain('ließen sich nicht messen')
})
it('keine gewählte Tonspur findbar → Sprachlisten statt stummer Datei', async () => {
const { ablage, roh, dateien } = aufbau(['title_t00.mkv'])
const fra = { ...jpnHd, nr: 9, lang: 'fra', sprache: 'French' }
dateien[0].audioSpuren = [fra]
dateien[0].untertitelSpuren = [deuPgs]
const ergebnis = await komprimierenUndAblegen(
auftrag({ rohOrdner: roh, dateien }),
umgebung(ablage, { spurenScannen: async () => scan }),
() => {},
KEIN_ABBRUCH,
)
expect(hb.aufrufe[0].audioNr).toBeUndefined()
expect(hb.aufrufe[0].audioSprachen).toEqual(['fra'])
expect(ergebnis.hinweise.join(' ')).toContain('keine der gewählten Tonspuren gefunden')
})
})