Ampel / ampel (push) Successful in 1m38s
Der Commander legte Disc 2 von „Spartacus: Gods of the Arena" ein, Rippy meldete den FILM „Spartacus" von 1960. An der echten Disc gemessen (Laufwerk G:, Label SPARTACUS_GOTA_D2, UDF, 33.759.100.928 Bytes): Der stark gestutzte Kandidat „Spartacus" stand auf Platz 2 der Suchvarianten und holte dort einen wörtlichen Filmtreffer mit 95 Prozent — die richtige Serie stand auf Platz 6 und wurde nie gefragt. Vier Ursachen, alle behoben: 1. Die Sicherheit maß die ART des Treffers, nie die QUALITÄT der Frage. Neu: abdeckung() + MINDEST_ABDECKUNG — wer mehr als die Hälfte des Titels wegwerfen musste, um zu treffen, bekommt höchstens einen Vorschlag (0,60), und das UI fragt nach, statt sich sicher zu irren. 2. Verpackungs-Zusätze galten als Wortmüll. Neu: discZusatzAbtrennen() trennt "- Disc 2" / "_D2" / "S1" ab UND merkt sie — die Nummer braucht die Ablage später sowieso. Vier Fallen sind abgesichert: "Rocky 2", "Kill Bill Teil 2", "Ocean s 11" (Apostroph-Wortgrenze) und nackte Zahlen am Ende. 3. Der Titelvergleich war zeichengenau. Ein Volume-Label KANN keinen Doppelpunkt tragen, TMDb schreibt ihn — der exakt richtige Treffer fiel an einem Satzzeichen durch. Neu: gleichBedeutend(). 4. Rippy öffnete das Inhaltsverzeichnis der Disc und las nur die erste Zeile daraus. Neu: inhaltAusBdmt() liest auch <di:titleName>. "EP 1" und "EP 2" beweisen die Serie, "DUB GER 1" und "104 GER FSK" nicht. Ist die Serie bewiesen, sind die Film-Zweige der Kaskade AUS. Gemessen an der eingelegten Disc (neue messung.disc-erkennung.test.ts): Titel "Spartacus: Gods Of The Arena - Disc 2" (Quelle: bdmt) Disc 2 | Staffel — | Folgen 2 | Serie bewiesen: true Varianten: "...Arena - Disc 2" -> "...Arena" -> ... -> "Spartacus" 225 Tests grün (vorher 209), Typprüfung sauber. Disc-Nummer, Staffel und Folgenzahl stehen jetzt auch im Fenster (Kachel neben der Erkennung). Version 5.1.1 — zugleich der erste echte Selbst-Update-Beweis (§ 6.8). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
421 lines
16 KiB
TypeScript
421 lines
16 KiB
TypeScript
// Tests der Metadaten-Schicht: Disc-Merkmale (pur/Dateisystem) und die
|
|
// Confidence-Kaskade mit nachgebildeten Clients — jede Stufe hat ihren
|
|
// gemessenen Grund (prescan.py).
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
import {
|
|
discZusatzAbtrennen,
|
|
folgenZahl,
|
|
inhaltAusBdmt,
|
|
istFolgenName,
|
|
normalizeDiscLabel,
|
|
parseUdfDstring,
|
|
titelAusBdmt,
|
|
titelKandidaten,
|
|
} from '../src/kern/metadaten/discmerkmale'
|
|
import {
|
|
abdeckung,
|
|
gleichBedeutend,
|
|
istSicherGenug,
|
|
wortUeberlappung,
|
|
zuordnen,
|
|
WENIGE_TREFFER,
|
|
} from '../src/kern/metadaten/zuordnung'
|
|
import { posterUrl } from '../src/kern/ablage/poster'
|
|
|
|
let wegwerf: string[] = []
|
|
afterEach(() => {
|
|
for (const o of wegwerf.splice(0)) rmSync(o, { recursive: true, force: true })
|
|
})
|
|
|
|
describe('normalizeDiscLabel', () => {
|
|
it('GROSS_MIT_UNTERSTRICHEN → Titel-Schreibung', () => {
|
|
expect(normalizeDiscLabel('PULP_FICTION_DE')).toBe('Pulp Fiction De')
|
|
expect(normalizeDiscLabel('BD_EVG_D2')).toBe('Bd Evg D2')
|
|
})
|
|
it('Gemischtes bleibt, wie es ist', () => {
|
|
expect(normalizeDiscLabel('Akira 4K')).toBe('Akira 4K')
|
|
expect(normalizeDiscLabel('')).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('parseUdfDstring', () => {
|
|
it('liest Latin-1 (Kompressions-ID 8)', () => {
|
|
const roh = Buffer.concat([Buffer.from([8]), Buffer.from('SPARTACUS', 'latin1'), Buffer.alloc(21)])
|
|
roh[roh.length - 1] = 10 // genutzte Länge: ID + 9 Zeichen
|
|
expect(parseUdfDstring(roh)).toBe('SPARTACUS')
|
|
})
|
|
it('liest UTF-16BE (Kompressions-ID 16)', () => {
|
|
const text = Buffer.from([0x00, 0x41, 0x00, 0x42])
|
|
const roh = Buffer.concat([Buffer.from([16]), text, Buffer.alloc(27)])
|
|
roh[roh.length - 1] = 1 + text.length
|
|
expect(parseUdfDstring(roh)).toBe('AB')
|
|
})
|
|
it('Unsinnige Längen sind leer, kein Absturz', () => {
|
|
expect(parseUdfDstring(Buffer.alloc(0))).toBe('')
|
|
expect(parseUdfDstring(Buffer.from([8, 65, 99]))).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('titelAusBdmt', () => {
|
|
it('liest den Studio-Titel, bdmt_eng bevorzugt', () => {
|
|
const wurzel = mkdtempSync(join(tmpdir(), 'rippy-bdmt-'))
|
|
wegwerf.push(wurzel)
|
|
const meta = join(wurzel, 'BDMV', 'META', 'DL')
|
|
mkdirSync(meta, { recursive: true })
|
|
writeFileSync(join(meta, 'bdmt_deu.xml'), '<x><di:name>Deutscher Titel</di:name></x>')
|
|
writeFileSync(join(meta, 'bdmt_eng.xml'), '<x><di:name>English Title</di:name></x>')
|
|
expect(titelAusBdmt(wurzel)).toBe('English Title')
|
|
})
|
|
it('ohne BDMV-Metadaten leer', () => {
|
|
const wurzel = mkdtempSync(join(tmpdir(), 'rippy-bdmt-'))
|
|
wegwerf.push(wurzel)
|
|
expect(titelAusBdmt(wurzel)).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('titelKandidaten — progressive Degradation (ARM-Lehre)', () => {
|
|
it('voller Titel zuerst, dann Varianten, dann gestutzt', () => {
|
|
const kandidaten = titelKandidaten('Evangelion: 2.22 You Can (Not) Advance.')
|
|
expect(kandidaten[0]).toBe('Evangelion: 2.22 You Can (Not) Advance')
|
|
expect(kandidaten).toContain('Evangelion')
|
|
expect(kandidaten.length).toBeLessThanOrEqual(8)
|
|
})
|
|
})
|
|
|
|
// ── Die Kaskade mit Fake-Clients ────────────────────────────────────────
|
|
interface FilmEintrag {
|
|
id: number
|
|
title: string
|
|
details?: Record<string, unknown>
|
|
}
|
|
|
|
function fakeTmdb(filme: Record<string, FilmEintrag[]>, serien: Record<string, FilmEintrag[]> = {}) {
|
|
return {
|
|
verfuegbar: true,
|
|
searchMovie: async (t: string) => filme[t] ?? [],
|
|
searchTv: async (t: string) => (serien[t] ?? []).map((s) => ({ id: s.id, name: s.title })),
|
|
movieDetails: async (id: number) => {
|
|
for (const liste of Object.values(filme)) {
|
|
const treffer = liste.find((f) => f.id === id)
|
|
if (treffer !== undefined) {
|
|
return { id, title: treffer.title, release_date: '1988-07-16', overview: 'Ü', ...treffer.details }
|
|
}
|
|
}
|
|
return null
|
|
},
|
|
tvDetails: async (id: number) => {
|
|
for (const liste of Object.values(serien)) {
|
|
const treffer = liste.find((f) => f.id === id)
|
|
if (treffer !== undefined) return { id, name: treffer.title, first_air_date: '1993-09-10' }
|
|
}
|
|
return null
|
|
},
|
|
findByImdb: async () => null,
|
|
}
|
|
}
|
|
|
|
const keinOmdb = { verfuegbar: false, lookup: async () => null }
|
|
|
|
describe('zuordnen — die Confidence-Kaskade', () => {
|
|
it('wörtlicher Filmtreffer: 0,95', async () => {
|
|
const z = await zuordnen('Akira', fakeTmdb({ Akira: [{ id: 1, title: 'Akira' }] }), keinOmdb)
|
|
expect(z.confidence).toBe(0.95)
|
|
expect(z.titel).toBe('Akira')
|
|
expect(z.jahr).toBe(1988)
|
|
expect(istSicherGenug(z)).toBe(true)
|
|
})
|
|
|
|
it('wörtlicher Serientreffer: 0,90', async () => {
|
|
const z = await zuordnen('Akte X', fakeTmdb({}, { 'Akte X': [{ id: 7, title: 'Akte X' }] }), keinOmdb)
|
|
expect(z.confidence).toBe(0.9)
|
|
expect(z.meta.typ).toBe('tv')
|
|
})
|
|
|
|
it('der Evangelion-Fund: voller Titel + wenige Treffer = 0,80', async () => {
|
|
// 'Evangelion 2.22' → 1 Treffer, dessen Titel NICHT wörtlich gleich
|
|
// ist — vor dem Fix wurde genau der richtige Film verworfen.
|
|
const z = await zuordnen(
|
|
'Evangelion 2.22',
|
|
fakeTmdb({ 'Evangelion 2.22': [{ id: 9, title: 'Evangelion: 2.0 You Can (Not) Advance' }] }),
|
|
keinOmdb,
|
|
)
|
|
expect(z.confidence).toBe(0.8)
|
|
expect(z.titel).toBe('Evangelion: 2.0 You Can (Not) Advance')
|
|
})
|
|
|
|
it('viele Treffer auf den vollen Titel sind KEIN spezifischer Fund', async () => {
|
|
const viele = Array.from({ length: WENIGE_TREFFER + 1 }, (_, i) => ({
|
|
id: 100 + i,
|
|
title: `Irgendwas ${i}`,
|
|
}))
|
|
const z = await zuordnen('Evangelion', fakeTmdb({ Evangelion: viele }), keinOmdb)
|
|
// Kaskade fällt bis zum Vorschlag durch (0,6) — das UI fragt nach.
|
|
expect(z.confidence).toBe(0.6)
|
|
expect(istSicherGenug(z)).toBe(false)
|
|
})
|
|
|
|
it('OMDb greift, wenn TMDb nichts Spezifisches hat: 0,80', async () => {
|
|
const omdb = {
|
|
verfuegbar: true,
|
|
lookup: async (t: string) =>
|
|
t === 'Seltener Film'
|
|
? {
|
|
type: 'movie' as const,
|
|
id: 'tt0123',
|
|
title: 'Seltener Film',
|
|
year: 1999,
|
|
overview: 'en',
|
|
poster_path: '',
|
|
runtime: 100,
|
|
genres: [],
|
|
source: 'omdb' as const,
|
|
}
|
|
: null,
|
|
}
|
|
const z = await zuordnen('Seltener Film', fakeTmdb({}), omdb)
|
|
expect(z.confidence).toBe(0.8)
|
|
expect(z.meta.quelle).toBe('omdb')
|
|
})
|
|
|
|
it('der Spartacus-Fund: die Serie schlägt den wörtlichen Treffer eines STARK gekürzten Kandidaten', async () => {
|
|
// Live gemessen (30.08.2026): 'Spartacus Gods Of The Arena' wurde zum
|
|
// Film „Spartacus" (1960), weil der gekürzte Kandidat wörtlich traf,
|
|
// BEVOR die volle Anfrage als spezifische Serie gewertet wurde.
|
|
// Seit dem 01.09.2026 ist es sogar ein WÖRTLICHER Serientreffer (0,90
|
|
// statt 0,80): Der Vergleich stolpert nicht mehr über den Doppelpunkt,
|
|
// den TMDb schreibt und ein Volume-Label nicht tragen kann.
|
|
const z = await zuordnen(
|
|
'Spartacus Gods Of The Arena',
|
|
fakeTmdb(
|
|
{ Spartacus: [{ id: 5, title: 'Spartacus' }] },
|
|
{ 'Spartacus Gods Of The Arena': [{ id: 6, title: 'Spartacus: Gods of the Arena' }] },
|
|
),
|
|
keinOmdb,
|
|
)
|
|
expect(z.confidence).toBe(0.9)
|
|
expect(z.meta.typ).toBe('tv')
|
|
expect(z.titel).toBe('Spartacus: Gods of the Arena')
|
|
})
|
|
|
|
it('OMDb-Unsinn fällt am Ähnlichkeits-Gate (der Bd-Evg-D2-Fund)', async () => {
|
|
const omdb = {
|
|
verfuegbar: true,
|
|
lookup: async () => ({
|
|
type: 'movie' as const,
|
|
id: 'tt9',
|
|
title: 'BD Scream 5',
|
|
year: 2013,
|
|
overview: '',
|
|
poster_path: '',
|
|
runtime: 90,
|
|
genres: [],
|
|
source: 'omdb' as const,
|
|
}),
|
|
}
|
|
const z = await zuordnen('Bd Evg D2', fakeTmdb({}), omdb)
|
|
expect(z.confidence).toBe(0.3)
|
|
expect(z.meta.typ).toBe('unknown')
|
|
})
|
|
|
|
it('wortUeberlappung rechnet Jaccard über Wörter', () => {
|
|
expect(wortUeberlappung('Pulp Fiction', 'Pulp Fiction')).toBe(1)
|
|
expect(wortUeberlappung('Bd Evg D2', 'BD Scream 5')).toBeLessThan(0.5)
|
|
expect(wortUeberlappung('', 'x')).toBe(0)
|
|
})
|
|
|
|
it('gar nichts gefunden: 0,30 mit dem Disc-Titel', async () => {
|
|
const z = await zuordnen('Logical Volume Id', fakeTmdb({}), keinOmdb)
|
|
expect(z.confidence).toBe(0.3)
|
|
expect(z.titel).toBe('Logical Volume Id')
|
|
})
|
|
|
|
it('leerer Titel bleibt unbekannt, ohne API-Anfragen', async () => {
|
|
const z = await zuordnen('', fakeTmdb({}), keinOmdb)
|
|
expect(z.confidence).toBe(0.3)
|
|
})
|
|
})
|
|
|
|
// ── Der Spartacus-Fund des Commanders (01.09.2026) ──────────────────────
|
|
//
|
|
// Gemessen an der Disc, die im Laufwerk lag: Volume-Label
|
|
// SPARTACUS_GOTA_D2 (UDF, 33 759 100 928 Bytes), und in
|
|
// G:\BDMV\META\DL\bdmt_eng.xml stand alles, was Rippy brauchte — es las
|
|
// nur die erste Zeile davon. Rippy meldete „Spartacus" (Film, 1960).
|
|
|
|
/** Das echte bdmt_eng.xml der Disc, gekürzt auf das Wesentliche. */
|
|
const ECHTES_BDMT =
|
|
'<?xml version="1.0" encoding="UTF-8"?><disclib xmlns="urn:BDA:bdmv;disclib">' +
|
|
'<di:discinfo xmlns:di="urn:BDA:bdmv;discinfo"><di:title>' +
|
|
'<di:name>Spartacus: Gods Of The Arena - Disc 2</di:name>' +
|
|
'<di:numSets>1</di:numSets><di:setNumber>1</di:setNumber></di:title>' +
|
|
'<di:description><di:tableOfContents>' +
|
|
'<di:titleName titleNumber="1">EP 1</di:titleName>' +
|
|
'<di:titleName titleNumber="2">EP 2</di:titleName>' +
|
|
'<di:titleName titleNumber="20">MM ITA</di:titleName>' +
|
|
'<di:titleName titleNumber="50">ILSM</di:titleName>' +
|
|
'<di:titleName titleNumber="51">DUB GER 1</di:titleName>' +
|
|
'<di:titleName titleNumber="52">DUB GER 2</di:titleName>' +
|
|
'<di:titleName titleNumber="101">101 LOGO</di:titleName>' +
|
|
'<di:titleName titleNumber="104">104 GER FSK</di:titleName>' +
|
|
'<di:titleName titleNumber="105">105 WARNINGS</di:titleName>' +
|
|
'</di:tableOfContents></di:description></di:discinfo></disclib>'
|
|
|
|
describe('inhaltAusBdmt — die Disc sagt selbst, was sie ist', () => {
|
|
it('liest Titel UND Inhaltsverzeichnis der echten Disc', () => {
|
|
const wurzel = mkdtempSync(join(tmpdir(), 'rippy-toc-'))
|
|
wegwerf.push(wurzel)
|
|
const meta = join(wurzel, 'BDMV', 'META', 'DL')
|
|
mkdirSync(meta, { recursive: true })
|
|
writeFileSync(join(meta, 'bdmt_eng.xml'), ECHTES_BDMT)
|
|
const inhalt = inhaltAusBdmt(wurzel)
|
|
expect(inhalt.titel).toBe('Spartacus: Gods Of The Arena - Disc 2')
|
|
expect(inhalt.eintraege).toContain('EP 1')
|
|
expect(inhalt.eintraege).toContain('DUB GER 1')
|
|
// Zwei Folgen — das deckt sich mit der Disc-Struktur: genau zwei große
|
|
// Ströme (15,76 GB und 14,89 GB), alles andere unter 200 MB.
|
|
expect(folgenZahl(inhalt.eintraege)).toBe(2)
|
|
})
|
|
|
|
it('Tonspuren, Trailer und Warnhinweise sind KEINE Folgen', () => {
|
|
expect(istFolgenName('EP 1')).toBe(true)
|
|
expect(istFolgenName('Episode 12')).toBe(true)
|
|
expect(istFolgenName('Folge 3')).toBe(true)
|
|
expect(istFolgenName('DUB GER 1')).toBe(false)
|
|
expect(istFolgenName('101 LOGO')).toBe(false)
|
|
expect(istFolgenName('104 GER FSK')).toBe(false)
|
|
expect(istFolgenName('MM ITA')).toBe(false)
|
|
})
|
|
|
|
it('ohne bdmt-Datei bleibt alles leer, ohne Absturz', () => {
|
|
const wurzel = mkdtempSync(join(tmpdir(), 'rippy-toc-'))
|
|
wegwerf.push(wurzel)
|
|
expect(inhaltAusBdmt(wurzel)).toEqual({ titel: '', eintraege: [] })
|
|
})
|
|
})
|
|
|
|
describe('discZusatzAbtrennen — abtrennen UND merken', () => {
|
|
it('trennt den Disc-Zusatz vom echten Titel', () => {
|
|
expect(discZusatzAbtrennen('Spartacus: Gods Of The Arena - Disc 2')).toEqual({
|
|
titel: 'Spartacus: Gods Of The Arena',
|
|
discNr: 2,
|
|
staffel: null,
|
|
})
|
|
})
|
|
|
|
it('auch aus dem Volume-Label (SPARTACUS_GOTA_D2)', () => {
|
|
expect(discZusatzAbtrennen(normalizeDiscLabel('SPARTACUS_GOTA_D2'))).toEqual({
|
|
titel: 'Spartacus Gota',
|
|
discNr: 2,
|
|
staffel: null,
|
|
})
|
|
})
|
|
|
|
it('Staffel und Disc hintereinander', () => {
|
|
expect(discZusatzAbtrennen('Akte X S1 D2')).toEqual({ titel: 'Akte X', discNr: 2, staffel: 1 })
|
|
})
|
|
|
|
it('lässt echte Titel in Ruhe — die vier Fallen', () => {
|
|
// Nackte Zahl am Ende: sonst würde aus 'Rocky 2' die 'Rocky', Disc 2.
|
|
expect(discZusatzAbtrennen('Rocky 2').titel).toBe('Rocky 2')
|
|
// 'Teil'/'Part' sind Titel-Wörter, keine Verpackung.
|
|
expect(discZusatzAbtrennen('Kill Bill Teil 2').titel).toBe('Kill Bill Teil 2')
|
|
// Die Apostroph-Wortgrenze: ohne den Trenner-Zwang würde hieraus
|
|
// "Ocean'" mit Staffel 11.
|
|
expect(discZusatzAbtrennen("Ocean's 11").titel).toBe("Ocean's 11")
|
|
expect(discZusatzAbtrennen("Ocean's 11").staffel).toBe(null)
|
|
expect(discZusatzAbtrennen('Pulp Fiction')).toEqual({ titel: 'Pulp Fiction', discNr: null, staffel: null })
|
|
})
|
|
})
|
|
|
|
describe('titelKandidaten — der Doppelpunkt-Vorspann steht GANZ hinten', () => {
|
|
it('die Reihenfolge der echten Disc', () => {
|
|
const k = titelKandidaten('Spartacus: Gods Of The Arena - Disc 2')
|
|
// Platz 1 war bis zum 01.09.2026 'Spartacus' — und erschlug damit alles.
|
|
expect(k[0]).toBe('Spartacus: Gods Of The Arena - Disc 2')
|
|
expect(k[1]).toBe('Spartacus: Gods Of The Arena')
|
|
expect(k).toContain('Spartacus Gods Of The Arena')
|
|
expect(k[k.length - 1]).toBe('Spartacus')
|
|
expect(k.indexOf('Spartacus')).toBeGreaterThan(k.indexOf('Spartacus: Gods Of The Arena'))
|
|
// Kein Müll-Zwischenschritt mehr, der auf einem Trennzeichen endet.
|
|
expect(k.some((t) => /[\s\-–—_:,.]$/.test(t))).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('gleichBedeutend und abdeckung', () => {
|
|
it('der Doppelpunkt kostet keinen Treffer mehr', () => {
|
|
expect(gleichBedeutend('Spartacus: Gods of the Arena', 'Spartacus Gods Of The Arena')).toBe(true)
|
|
})
|
|
|
|
it('aber Wortdopplungen bleiben unterscheidbar', () => {
|
|
expect(gleichBedeutend('New York, New York', 'New York')).toBe(false)
|
|
expect(gleichBedeutend('', 'x')).toBe(false)
|
|
})
|
|
|
|
it('abdeckung misst, wie viel von der Frage übrig ist', () => {
|
|
expect(abdeckung('Spartacus: Gods Of The Arena', 'Spartacus')).toBeCloseTo(0.2)
|
|
expect(abdeckung('Spartacus: Gods Of The Arena', 'Spartacus: Gods Of The Arena')).toBe(1)
|
|
expect(abdeckung('', 'x')).toBe(0)
|
|
})
|
|
})
|
|
|
|
describe('zuordnen — der Fund des Commanders, an der echten Disc', () => {
|
|
const echteWelt = () =>
|
|
fakeTmdb(
|
|
// TMDb hat einen Film, der WÖRTLICH 'Spartacus' heißt — das ist die Falle.
|
|
{ Spartacus: [{ id: 5, title: 'Spartacus' }] },
|
|
{ 'Spartacus: Gods Of The Arena': [{ id: 6, title: 'Spartacus: Gods of the Arena' }] },
|
|
)
|
|
|
|
it('der echte Disc-Titel führt zur Serie, nicht zum Film von 1960', async () => {
|
|
const z = await zuordnen('Spartacus: Gods Of The Arena - Disc 2', echteWelt(), keinOmdb)
|
|
expect(z.meta.typ).toBe('tv')
|
|
expect(z.titel).toBe('Spartacus: Gods of the Arena')
|
|
expect(z.confidence).toBe(0.9)
|
|
})
|
|
|
|
it('mit dem Serien-Hinweis der Disc kann ein Film gar nicht mehr gewinnen', async () => {
|
|
const z = await zuordnen('Spartacus: Gods Of The Arena - Disc 2', echteWelt(), keinOmdb, { istSerie: true })
|
|
expect(z.meta.typ).toBe('tv')
|
|
expect(z.titel).toBe('Spartacus: Gods of the Arena')
|
|
})
|
|
|
|
it('DER REGRESSIONSTEST: kennt TMDb nur den Film, wird daraus ein VORSCHLAG', async () => {
|
|
// Genau der Fehler, den der Commander sah: Die Restfrage 'Spartacus'
|
|
// trifft wörtlich — vorher 95 % Sicherheit auf den falschen Film.
|
|
// Jetzt deckelt die Abdeckung (1 von 5 Wörtern übrig) auf 0,60: Rippy
|
|
// zeigt es als Vorschlag und fragt nach, statt sich sicher zu irren.
|
|
const z = await zuordnen(
|
|
'Spartacus: Gods Of The Arena - Disc 2',
|
|
fakeTmdb({ Spartacus: [{ id: 5, title: 'Spartacus' }] }),
|
|
keinOmdb,
|
|
)
|
|
expect(z.titel).toBe('Spartacus')
|
|
expect(z.confidence).toBe(0.6)
|
|
expect(istSicherGenug(z)).toBe(false)
|
|
})
|
|
|
|
it('eine Serien-Disc nimmt auch aus dem Label keinen Filmtreffer an', async () => {
|
|
// Ohne bdmt bliebe nur 'Spartacus Gota D2' — die Folgen im
|
|
// Inhaltsverzeichnis bezeugen trotzdem, dass hier kein Film liegt.
|
|
const z = await zuordnen(
|
|
normalizeDiscLabel('SPARTACUS_GOTA_D2'),
|
|
fakeTmdb({ Spartacus: [{ id: 5, title: 'Spartacus' }] }),
|
|
keinOmdb,
|
|
{ istSerie: true },
|
|
)
|
|
expect(z.meta.typ).not.toBe('movie')
|
|
expect(z.confidence).toBe(0.3)
|
|
})
|
|
})
|
|
|
|
describe('posterUrl', () => {
|
|
it('relative TMDb-Pfade bekommen das w500-Präfix', () => {
|
|
expect(posterUrl('/abc.jpg')).toBe('https://image.tmdb.org/t/p/w500/abc.jpg')
|
|
expect(posterUrl('https://x/y.jpg')).toBe('https://x/y.jpg')
|
|
expect(posterUrl('')).toBe('')
|
|
})
|
|
})
|