diff --git a/rippy-windows/src/fenster/App.tsx b/rippy-windows/src/fenster/App.tsx index 5a4293c..61bec6c 100644 --- a/rippy-windows/src/fenster/App.tsx +++ b/rippy-windows/src/fenster/App.tsx @@ -112,7 +112,9 @@ export default function App() { const w = verbindung.werkzeuge const werkzeugTon = w === null ? 'grau' : w.makemkvcon.length > 0 && w.handbrake.length > 0 ? 'gruen' : w.makemkvcon.length > 0 ? 'warn' : 'rot' const s = verbindung.schluessel - const schluesselTon = s === null ? 'grau' : s.angenommen === true ? 'gruen' : s.weiterBetrieb ? 'warn' : 'rot' + // 5.7.0 (F1/F5): abgelehnt oder fehlend = ROT (Blu-rays laufen nicht), + // nicht prüfbar (kein Laufwerk) = grau — nie mehr ein gelbes „läuft". + const schluesselTon = s === null ? 'grau' : s.angenommen === true ? 'gruen' : s.angenommen === null ? 'grau' : 'rot' const arbeit = verbindung.speicher?.arbeit ?? null const speicherTon = arbeit === null ? 'grau' : !arbeit.ok ? 'rot' : arbeit.freiBytes < SEHR_WENIG_FREI_BYTES ? 'rot' : arbeit.freiBytes < WENIG_FREI_BYTES ? 'warn' : 'gruen' const updateTon = haupt === null ? 'grau' : haupt.update.neueVersion.length > 0 ? 'warn' : haupt.update.letzteErfolgreichePruefung.length > 0 ? 'gruen' : 'grau' diff --git a/rippy-windows/src/fenster/bausteine.tsx b/rippy-windows/src/fenster/bausteine.tsx index 057e6e7..3a37bf5 100644 --- a/rippy-windows/src/fenster/bausteine.tsx +++ b/rippy-windows/src/fenster/bausteine.tsx @@ -32,9 +32,14 @@ export function dauerGrob(s: number): string { return `${Math.floor(m / 60)} h ${String(m % 60).padStart(2, '0')} min` } +/** ISO-Zeit → „2026-09-12 21:30" in DEINER Uhrzeit (5.7.0, Durchsicht F12): + * vorher wurde der UTC-Text abgeschnitten — 21:30 Uhr stand als 19:30 da. */ export function datumKurz(iso: string | null): string { if (iso === null || iso.length < 16) return '—' - return iso.slice(0, 16).replace('T', ' ') + const d = new Date(iso) + if (Number.isNaN(d.getTime())) return iso.slice(0, 16).replace('T', ' ') + const z = (n: number): string => String(n).padStart(2, '0') + return `${d.getFullYear()}-${z(d.getMonth() + 1)}-${z(d.getDate())} ${z(d.getHours())}:${z(d.getMinutes())}` } /** Restzeit in Sekunden als Klartext (5.6.0, HandBrakes ETA): -1/fehlend = diff --git a/rippy-windows/src/fenster/dialoge/TitelWahlDialog.tsx b/rippy-windows/src/fenster/dialoge/TitelWahlDialog.tsx index ad116ba..1df051b 100644 --- a/rippy-windows/src/fenster/dialoge/TitelWahlDialog.tsx +++ b/rippy-windows/src/fenster/dialoge/TitelWahlDialog.tsx @@ -22,7 +22,7 @@ import type { TitelRolle, TitelStand, } from '../../gemeinsam/nachrichten' -import { DialogRahmen, IconDisc, KNOPF_GOLD, dauerText, gb, gbKurz, posterUrlKlein, sprachenVorbelegt } from '../bausteine' +import { DialogRahmen, IconDisc, KNOPF_GOLD, datumKurz, dauerText, gb, gbKurz, posterUrlKlein, sprachenVorbelegt } from '../bausteine' import { metadatenSuchen, ripStarten } from '../kernverbindung' /** Sprach-Chips eines Titels — anklickbar, außerhalb des Auswahl-Labels @@ -410,7 +410,7 @@ export function TitelWahlDialog({

{merkerGilt && merker !== null - ? `Serien-Merker: Die vorige Disc dieser Serie endete mit Folge ${merker.naechsteFolge - 1} (${merker.wann.slice(0, 10)}) — Rippy zählt ab ${merker.naechsteFolge} weiter. Stimmt das nicht, ändere die Zahl.` + ? `Serien-Merker: Die vorige Disc dieser Serie endete mit Folge ${merker.naechsteFolge - 1} (${datumKurz(merker.wann).slice(0, 10)}) — Rippy zählt ab ${merker.naechsteFolge} weiter. Stimmt das nicht, ändere die Zahl.` : 'Leer heißt: Rippy ordnet die Folgen über die TMDb-Laufzeiten zu, und nur, wenn es eindeutig ist. Weißt du es besser („auf Disc 2 sind die Folgen 3 und 4"), trag es hier ein — dann zählt Rippy fortlaufend in Disc-Reihenfolge.'} {info !== undefined && info.discNr !== null && ` Die Disc nennt sich selbst Disc ${info.discNr}.`}

diff --git a/rippy-windows/src/fenster/einstellungen/Karten.tsx b/rippy-windows/src/fenster/einstellungen/Karten.tsx index ec3755f..a70a57c 100644 --- a/rippy-windows/src/fenster/einstellungen/Karten.tsx +++ b/rippy-windows/src/fenster/einstellungen/Karten.tsx @@ -406,16 +406,19 @@ export function MakemkvKarte({ verbindung }: { verbindung: Verbindung }) {

Schlüssel wird geprüft …

) : ( <> + {/* 5.7.0 (F1/F5): Ein abgelehnter oder fehlender Schlüssel ist + ROT — Blu-rays laufen dann nur noch in MakeMKVs Gnadenfrist. + „Nicht prüfbar" (kein Laufwerk) ist grau, kein Erfolg. */}

- - - {s.angenommen === true ? 'SCHLÜSSEL ANGENOMMEN' : s.weiterBetrieb ? 'BETA-MODUS — RIPS LAUFEN' : 'AUSFALL'} + + + {s.angenommen === true ? 'SCHLÜSSEL ANGENOMMEN' : s.angenommen === null ? 'NICHT PRÜFBAR — KEIN LAUFWERK' : 'KEIN GÜLTIGER SCHLÜSSEL — BLU-RAY-RIPS LAUFEN NICHT'} {s.version.length > 0 && MakeMKV {s.version}}

{s.text}

- zuletzt geprüft: {s.geholtAm.slice(0, 16).replace('T', ' ')} + zuletzt geprüft: {datumKurz(s.geholtAm)} {s.gueltigBis.length > 0 && ` · Key gültig bis ${s.gueltigBis}`}

@@ -448,11 +451,14 @@ export function MakemkvKarte({ verbindung }: { verbindung: Verbindung }) { )} setzen('makemkvAppKey', w)} /> +

+ Rippy schreibt in MakeMKVs eigene Einstellungen: den Schlüssel, die Auswahlregel „alle Spuren behalten" und den Kanal für 4K-Disc-Schlüssel. Deshalb nimmt auch das MakeMKV-Programm seitdem alle Tonspuren und Untertitel mit — die Dateien dort werden größer, verloren geht nichts. +

) } @@ -571,7 +577,7 @@ export function UpdateKarte({ verbindung, haupt }: { verbindung: Verbindung; hau

Stand: {haupt.update.text}

- {haupt.update.letzteErfolgreichePruefung.length > 0 &&

letzte erfolgreiche Prüfung: {haupt.update.letzteErfolgreichePruefung.slice(0, 16).replace('T', ' ')}

} + {haupt.update.letzteErfolgreichePruefung.length > 0 &&

letzte erfolgreiche Prüfung: {datumKurz(haupt.update.letzteErfolgreichePruefung)}

} {haupt.update.neueVersion.length > 0 &&

Version {haupt.update.neueVersion} gefunden — die Karte in der Übersicht zeigt, was neu ist.

} )} diff --git a/rippy-windows/src/fenster/uebersicht/LaufwerkKachel.tsx b/rippy-windows/src/fenster/uebersicht/LaufwerkKachel.tsx index f266813..20d2639 100644 --- a/rippy-windows/src/fenster/uebersicht/LaufwerkKachel.tsx +++ b/rippy-windows/src/fenster/uebersicht/LaufwerkKachel.tsx @@ -26,6 +26,7 @@ import { PHASEN_MONO, TYP_NAMEN, backdropUrl, + datumKurz, discHerkunft, gb, logoUrl, @@ -294,7 +295,7 @@ export function LaufwerkKachel({ )} {erkannt && = 0.8 ? 'gruen' : 'gelb'} />} {info !== undefined && discHerkunft(info).length > 0 && } - {info !== undefined && info.schonGerippt !== null && } + {info !== undefined && info.schonGerippt !== null && } {/* Was Rippy NACH dem Info-Lauf dazugelernt hat (Erkennung v2). */} {erkannt && info.hinweise.length > 0 && ( diff --git a/rippy-windows/src/kern/index.ts b/rippy-windows/src/kern/index.ts index 201ac1a..5d4fe66 100644 --- a/rippy-windows/src/kern/index.ts +++ b/rippy-windows/src/kern/index.ts @@ -250,6 +250,12 @@ function fensterNachricht(frage: unknown): void { vorgaenge.senden() speicher.senden() } + // 5.7.0 (Durchsicht F13): Ein neuer Werkzeug-Pfad wird sofort neu + // gesucht und gemessen — vorher stand „NICHT GEFUNDEN" so lange, bis + // das Fenster neu geladen wurde (in der Erst-Einrichtung besonders + // irreführend). Ein neuer eigener Schlüssel wird sofort geprüft. + if (frage.schluessel.startsWith('werkzeug.')) void werkzeuge.senden() + if (frage.schluessel === 'makemkvAppKey') void werkzeuge.schluesselLauf().then(() => werkzeuge.senden()) } return case 'einstellungen-laden': diff --git a/rippy-windows/src/kern/werkzeuge/schluessel.ts b/rippy-windows/src/kern/werkzeuge/schluessel.ts index 6883a2d..dd437e0 100644 --- a/rippy-windows/src/kern/werkzeuge/schluessel.ts +++ b/rippy-windows/src/kern/werkzeuge/schluessel.ts @@ -3,20 +3,32 @@ // am Commander-PC gemessen): // // 1. Ein eingetragener Dauerlizenz-Schlüssel schlägt alles. -// 2. Sonst der Beta-Key aus dem Forum-Thread des Herstellers (t=1053). -// 3. Abgelegt wird in HKCU\Software\MakeMKV\app_Key — die settings.conf +// 2. Sonst: Ein Schlüssel, der schon im Register steht und angenommen +// wird, bleibt (5.7.0 — vorher überschrieb Rippy jeden vorhandenen +// Schlüssel mit dem Forum-Key, auch einen gekauften aus dem GUI). +// 3. Sonst der Beta-Key aus dem Forum-Thread des Herstellers (t=1053). +// 4. Abgelegt wird in HKCU\Software\MakeMKV\app_Key — die settings.conf // ist der Linux-Weg; unter Windows liest MakeMKV die Registry. -// 4. GEFRAGT statt gerechnet: makemkvcon selbst sagt per MSG 5020/5021/ +// 5. GEFRAGT statt gerechnet: makemkvcon selbst sagt per MSG 5020/5021/ // 5051, ob der Key taugt. Ein ABGELEHNTER Key ist schlimmer als // keiner (0 Titel, kein Laufwerk) — er wird dann ZURÜCKGENOMMEN. -// 5. app_UpdateEnable=1 nur-wenn-fehlt: Über diesen Kanal holt MakeMKV +// Ohne angeschlossenes Laufwerk sagt makemkvcon NICHTS zur Lizenz +// (12.09.2026 gemessen: nur 5042/5010) — dann heißt das Urteil +// „nicht prüfbar", nicht „angenommen". +// 6. app_UpdateEnable=1 nur-wenn-fehlt: Über diesen Kanal holt MakeMKV // die 4K-UHD-Disc-Schlüssel nach (MSG 3338; unter Windows gemessen). import { spawn, spawnSync } from 'node:child_process' import { textVon } from '../rip/parser' import { KEY_ABGELEHNT, KRITISCHE_CODES } from '../rip/parser' export const FORUM_URL = 'https://forum.makemkv.com/forum/viewtopic.php?t=1053' -export const KEY_MUSTER = /T-[A-Za-z0-9_]{50,}/ +// 5.7.0 (Durchsicht 12.09.2026, F1): Der September-Key lautet +// „T-vwvn…6Ox4BI@N5rPp" — 68 Zeichen MIT einem @. Das alte Muster +// /T-[A-Za-z0-9_]{50,}/ endete am @ und lieferte 62 Zeichen; MakeMKV lehnte +// den Stumpf ab (5020), Rippy nahm ihn zurück, und MakeMKV lief ohne +// Schlüssel. Jetzt zählt alles bis zum nächsten Leerraum oder HTML-Zeichen — +// Rippy rät nicht, welche Zeichen der Hersteller in seine Schlüssel setzt. +export const KEY_MUSTER = /T-[^\s<>"'&;]{50,}/ const REGISTRY_PFAD = 'HKCU\\Software\\MakeMKV' const KEY_NAME = 'app_Key' @@ -28,12 +40,15 @@ export function extractKey(html: string): string { /** „valid until/through " aus dem Thread — best effort, Rohtext. * § 9.1 will das Ablaufdatum sichtbar; die verlässliche Instanz bleibt - * aber makemkvcon selbst (MSG 5051 = abgelaufen). */ + * aber makemkvcon selbst (MSG 5051 = abgelaufen). Der Thread schreibt + * mal „September 30th, 2026", mal „end of September 2026" (so am + * 12.09.2026 gemessen) — beide Formen kommen an. */ export function extractGueltigBis(html: string): string { - const treffer = /valid\s+(?:until|through)\s+([A-Za-zäöüÄÖÜ]+\s+\d{1,2}(?:st|nd|rd|th)?,?\s+\d{4})/i.exec( - html ?? '', - ) - return treffer === null ? '' : treffer[1].replace(/(\d)(st|nd|rd|th)/, '$1') + const treffer = + /valid\s+(?:until|through)\s+((?:(?:the\s+)?end\s+of\s+|(?:the\s+)?beginning\s+of\s+|mid-?\s*)?[A-Za-zäöüÄÖÜ]+(?:\s+\d{1,2}(?:st|nd|rd|th)?,?)?\s+\d{4})/i.exec( + html ?? '', + ) + return treffer === null ? '' : treffer[1].replace(/(\d)(st|nd|rd|th)/, '$1').replace(/\s+/g, ' ') } export interface ForumKey { @@ -79,6 +94,19 @@ export function keyZuruecknehmen(): boolean { return reg(['delete', REGISTRY_PFAD, '/v', KEY_NAME, '/f']).ok } +/** Den Wert aus einer `reg query`-Ausgabe lesen (pur, testbar): + * „ app_Key REG_SZ T-…" — '' wenn keiner dasteht. */ +export function keyAusQuery(ausgabe: string): string { + const treffer = new RegExp(`${KEY_NAME}\\s+REG_SZ\\s+(\\S+)`).exec(ausgabe ?? '') + return treffer === null ? '' : treffer[1].trim() +} + +/** Der Schlüssel, der gerade im Register steht — '' wenn keiner. */ +export function keyLesen(): string { + const vorhanden = reg(['query', REGISTRY_PFAD, '/v', KEY_NAME]) + return vorhanden.ok ? keyAusQuery(vorhanden.ausgabe) : '' +} + /** app_UpdateEnable=1 setzen, NUR wenn es fehlt — wer den Web-Kontakt * bewusst abgeschaltet hat, behält seine Entscheidung. */ export function updateKanalSicherstellen(): boolean { @@ -89,6 +117,9 @@ export function updateKanalSicherstellen(): boolean { export interface KeyUrteil { ok: boolean + /** false: makemkvcon hat die Lizenz gar nicht angesehen — es sah kein + * Laufwerk (5042). Dann ist `ok` keine Aussage über den Schlüssel. */ + pruefbar: boolean code: number | null grund: string /** Die MakeMKV-Version aus MSG 1005 („MakeMKV v1.18.4 …") — '' wenn @@ -98,7 +129,9 @@ export interface KeyUrteil { /** Nimmt MakeMKV den hinterlegten Schlüssel an? — es selbst gefragt: * `info disc:9999` braucht keinen Datenträger, die Freischaltungs-Meldung - * kommt trotzdem (makemkv_aufruf.py, 29.08.2026). + * kommt trotzdem (makemkv_aufruf.py, 29.08.2026) — ABER nur mit einem + * angeschlossenen Laufwerk (12.09.2026 ohne Laufwerk gemessen: 5042 und + * 5010, keine Lizenzzeile). * * ⚠ ALLE Codes einsammeln, dann urteilen: Beim Commander kamen 5020 UND * 5021 zusammen (30.08.2026 live gemessen) — 5020 allein klingt nach @@ -108,7 +141,7 @@ export interface KeyUrteil { export function keyPruefen(programm: string, timeoutMs = 120_000): Promise { return new Promise((fertig) => { if (programm.length === 0) { - fertig({ ok: false, code: null, grund: 'MakeMKV nicht gefunden', version: '' }) + fertig({ ok: false, pruefbar: false, code: null, grund: 'MakeMKV nicht gefunden', version: '' }) return } const kind = spawn(programm, ['-r', '--noscan', 'info', 'disc:9999'], { @@ -121,7 +154,7 @@ export function keyPruefen(programm: string, timeoutMs = 120_000): Promise kind.kill(), timeoutMs) kind.on('error', (fehler) => { clearTimeout(zeiger) - fertig({ ok: false, code: null, grund: `MakeMKV nicht befragbar (${String(fehler)})`, version: '' }) + fertig({ ok: false, pruefbar: false, code: null, grund: `MakeMKV nicht befragbar (${String(fehler)})`, version: '' }) }) kind.on('close', () => { clearTimeout(zeiger) @@ -130,10 +163,14 @@ export function keyPruefen(programm: string, timeoutMs = 120_000): Promise string = keyLesen, ): Promise { updateKanalSicherstellen() + const versionsText = (urteil: KeyUrteil): string => (urteil.version.length > 0 ? ` ${urteil.version}` : '') + const nichtPruefbar = (quelle: string, gueltigBis: string, urteil: KeyUrteil, wer: string): SchluesselStand => ({ + quelle, + angenommen: null, + weiterBetrieb: true, + gueltigBis, + geholtAm: jetztIso, + text: `${wer} hinterlegt — ob MakeMKV${versionsText(urteil)} ihn annimmt, lässt sich erst mit angeschlossenem Laufwerk prüfen (${urteil.grund}).`, + version: urteil.version, + }) + if (dauerlizenz.trim().length > 0) { - keyAblegen(dauerlizenz) + // 5.7.0: Das Ergebnis von reg add zählt — „hinterlegt" stand vorher + // auch da, wenn nichts geschrieben wurde. + if (!keyAblegen(dauerlizenz)) { + return { + quelle: 'dauerlizenz', + angenommen: false, + weiterBetrieb: false, + gueltigBis: '', + geholtAm: jetztIso, + text: 'Eigener Schlüssel konnte nicht ins Register geschrieben werden (reg add scheiterte). ' + OHNE_SCHLUESSEL_FOLGE, + version: '', + } + } const urteil = await keyPruefen(makemkvcon) + if (!urteil.pruefbar) return nichtPruefbar('dauerlizenz', '', urteil, 'Eigener Schlüssel') return { quelle: 'dauerlizenz', angenommen: urteil.ok, @@ -185,54 +262,85 @@ export async function schluesselKette( gueltigBis: '', geholtAm: jetztIso, text: urteil.ok - ? `Eigener Schlüssel hinterlegt und von MakeMKV${urteil.version.length > 0 ? ` ${urteil.version}` : ''} angenommen.` - : `Eigener Schlüssel abgelehnt: ${urteil.grund}`, + ? `Eigener Schlüssel hinterlegt und von MakeMKV${versionsText(urteil)} angenommen.` + : `Eigener Schlüssel abgelehnt: ${urteil.grund}. ${OHNE_SCHLUESSEL_FOLGE}`, version: urteil.version, } } + // 5.7.0: Ein Schlüssel, der schon steht und angenommen wird, bleibt — + // egal ob gekauft (im GUI eingetragen) oder ein älterer Forum-Key. + const vorhanden = registerLesen() + if (vorhanden.length > 0) { + const urteil = await keyPruefen(makemkvcon) + if (!urteil.pruefbar) return nichtPruefbar('vorhanden', '', urteil, 'Ein Schlüssel ist') + if (urteil.ok) { + return { + quelle: 'vorhanden', + angenommen: true, + weiterBetrieb: true, + gueltigBis: '', + geholtAm: jetztIso, + text: `Der hinterlegte Schlüssel wird von MakeMKV${versionsText(urteil)} angenommen — Rippy lässt ihn stehen.`, + version: urteil.version, + } + } + // Abgelehnt: dann darf der Forum-Key ihn ersetzen (unten). + } + const forum = await betaKeyHolen(holen) if (forum === null) { // Kein neuer Key erreichbar — der VORHANDENE bleibt stehen (R2) und // wird geprüft, damit der Stand ehrlich ist. const urteil = await keyPruefen(makemkvcon) + if (!urteil.pruefbar) return nichtPruefbar('keiner', '', urteil, 'Forum nicht erreichbar; der vorhandene Stand ist') return { quelle: 'keiner', angenommen: urteil.ok, - weiterBetrieb: true, + weiterBetrieb: urteil.ok, gueltigBis: '', geholtAm: jetztIso, text: urteil.ok ? 'Forum nicht erreichbar — der hinterlegte Schlüssel funktioniert weiter.' - : `Forum nicht erreichbar und der hinterlegte Schlüssel taugt nicht: ${urteil.grund}`, + : `Forum nicht erreichbar und der hinterlegte Schlüssel taugt nicht: ${urteil.grund}. ${OHNE_SCHLUESSEL_FOLGE}`, version: urteil.version, } } - keyAblegen(forum.key) + if (!keyAblegen(forum.key)) { + return { + quelle: 'forum', + angenommen: false, + weiterBetrieb: false, + gueltigBis: forum.gueltigBis, + geholtAm: jetztIso, + text: 'Beta-Key geholt, aber nicht ins Register schreibbar (reg add scheiterte). ' + OHNE_SCHLUESSEL_FOLGE, + version: '', + } + } const urteil = await keyPruefen(makemkvcon) + if (!urteil.pruefbar) return nichtPruefbar('forum', forum.gueltigBis, urteil, 'Beta-Key aus dem Forum') if (!urteil.ok) { // Ein abgelehnter Schlüssel ist schlimmer als keiner — zurücknehmen. // (Live gemessen 30.08.2026: MIT abgelehntem Key verweigert MakeMKV - // alles; OHNE läuft es im Beta-Modus weiter und rippt.) + // alles; OHNE läuft es im Beta-Modus weiter und rippt — DVDs immer, + // Blu-rays nur bis zum Ende des Testzeitraums.) keyZuruecknehmen() } - const versionsText = urteil.version.length > 0 ? ` ${urteil.version}` : '' return { quelle: 'forum', angenommen: urteil.ok, - // Der Zurücknahme-Weg lässt MakeMKV im Beta-Modus weiterlaufen — - // Rips gehen, nur eben ohne Freischaltungs-Komfort. - weiterBetrieb: true, + weiterBetrieb: urteil.ok, gueltigBis: forum.gueltigBis, geholtAm: jetztIso, text: urteil.ok - ? `Beta-Key aus dem Forum hinterlegt und von MakeMKV${versionsText} angenommen${forum.gueltigBis.length > 0 ? ` — gültig bis ${forum.gueltigBis}` : ''}.` + ? `Beta-Key aus dem Forum hinterlegt und von MakeMKV${versionsText(urteil)} angenommen${forum.gueltigBis.length > 0 ? ` — gültig bis ${forum.gueltigBis}` : ''}.` : urteil.code === 5021 - ? `Der aktuelle Beta-Key ist in Ordnung — aber MakeMKV${versionsText} ist ZU ALT für ihn. ` + - 'Rippy hat ihn wieder entfernt, damit MakeMKV im Beta-Modus weiterläuft (so klappen Rips trotzdem). ' + - 'Abhilfe: MakeMKV von makemkv.com aktualisieren — danach hinterlegt Rippy den Key automatisch neu.' - : `Beta-Key abgelehnt (${urteil.grund}) — wieder entfernt, damit MakeMKV im Beta-Modus weiterläuft.`, + ? `Der aktuelle Beta-Key ist in Ordnung — aber MakeMKV${versionsText(urteil)} ist ZU ALT für ihn. ` + + 'Rippy hat ihn wieder entfernt. ' + + OHNE_SCHLUESSEL_FOLGE + + ' Abhilfe: MakeMKV von makemkv.com aktualisieren — danach hinterlegt Rippy den Key automatisch neu.' + : `Beta-Key abgelehnt (${urteil.grund}) — wieder entfernt. ${OHNE_SCHLUESSEL_FOLGE}`, version: urteil.version, } } diff --git a/rippy-windows/test/audiocd.test.ts b/rippy-windows/test/audiocd.test.ts index 36141fa..da4dee5 100644 --- a/rippy-windows/test/audiocd.test.ts +++ b/rippy-windows/test/audiocd.test.ts @@ -160,4 +160,19 @@ describe('Schlüsselkette (§ 9.1)', () => { expect(extractGueltigBis('The key is valid until September 30th, 2026 or so')).toBe('September 30, 2026') expect(extractGueltigBis('nichts')).toBe('') }) + // 5.7.0 (Durchsicht 12.09.2026, F1): So steht es WIRKLICH auf der Forum- + // Seite — der September-Key trägt ein @, und das Datum heißt „end of". + // Das alte Muster endete am @ und lieferte 62 von 68 Zeichen. + it('der echte September-2026-Key mit @ kommt ganz an', () => { + const key = 'T-vwvnrmF87JuRPLYYO8HnIrZR_LCAF7JXs9iJEm10moGpjEgkvmPTsf6Ox4BI@N5rPp' + const html = `
The current beta key is:
${key}
The key is valid until end of September 2026. Please check back for updated key on this page.
` + expect(extractKey(html)).toBe(key) + expect(extractKey(html)).toHaveLength(68) + expect(extractGueltigBis(html)).toBe('end of September 2026') + }) + it('der Key endet am HTML-Zeichen, nicht mitten im Wort', () => { + const key = 'T-' + 'a'.repeat(66) + expect(extractKey(`${key}

valid

`)).toBe(key) + expect(extractKey(`key: ${key} (valid until end of October 2026)`)).toBe(key) + }) }) diff --git a/rippy-windows/test/commander-funde.test.ts b/rippy-windows/test/commander-funde.test.ts index d79ea60..e1765a2 100644 --- a/rippy-windows/test/commander-funde.test.ts +++ b/rippy-windows/test/commander-funde.test.ts @@ -28,12 +28,46 @@ describe('Fund 1 — „Key korrekt, aber als ungültig angezeigt"', () => { 'MSG:1005,0,1,"MakeMKV v1.18.4 win(x64-release) gestartet","x"\nMSG:5010,0,0,"Das Öffnen der Disk schlug fehl","x"', ) expect(urteil.ok).toBe(true) + expect(urteil.pruefbar).toBe(true) expect(urteil.version).toBe('1.18.4') }) it('5020 allein bleibt „Schlüssel wird nicht angenommen"', () => { const urteil = urteilAusAusgabe('MSG:5020,516,0,"…","…"') expect(urteil.code).toBe(5020) + expect(urteil.pruefbar).toBe(true) + }) + + // 5.7.0 (Durchsicht 12.09.2026, F5): OHNE angeschlossenes Laufwerk sagt + // makemkvcon zur Lizenz gar nichts — gemessen an diesem PC: nur 5042 und + // 5010. Das ist kein „angenommen", sondern „nicht angesehen". + it('5042 (kein Laufwerk) ohne Lizenzzeile heißt: nicht prüfbar', () => { + const urteil = urteilAusAusgabe( + [ + 'MSG:1005,0,1,"MakeMKV v1.18.4 win(x64-release) gestartet","%1 gestartet","MakeMKV v1.18.4 win(x64-release)"', + 'MSG:5042,0,0,"Das Programm konnte keine verwendbaren optischen Laufwerke finden.","…"', + 'MSG:5010,0,0,"Das Öffnen der Disk schlug fehl ","…"', + ].join('\n'), + ) + expect(urteil.pruefbar).toBe(false) + expect(urteil.code).toBe(5042) + expect(urteil.version).toBe('1.18.4') + }) + + it('5051 (Testzeitraum abgelaufen) ist prüfbar und abgelehnt — auch neben 5042', () => { + const urteil = urteilAusAusgabe('MSG:5042,0,0,"x","x"\nMSG:5054,0,1,"Evaluierungsversion. Der Testzeitraum lief vor 10 Tagen ab.","x","10"\nMSG:5051,1028,0,"Der Testzeitraum ist abgelaufen.","x"') + expect(urteil.ok).toBe(false) + expect(urteil.pruefbar).toBe(true) + expect(urteil.code).toBe(5051) + }) +}) + +describe('Schlüssel aus dem Register lesen (5.7.0)', () => { + it('keyAusQuery liest den Wert aus reg query', async () => { + const { keyAusQuery } = await import('../src/kern/werkzeuge/schluessel') + const ausgabe = '\r\nHKEY_CURRENT_USER\\Software\\MakeMKV\r\n app_Key REG_SZ T-vwvnrmF87JuRPLYYO8HnIrZR_LCAF7JXs9iJEm10moGpjEgkvmPTsf6Ox4BI@N5rPp\r\n\r\n' + expect(keyAusQuery(ausgabe)).toBe('T-vwvnrmF87JuRPLYYO8HnIrZR_LCAF7JXs9iJEm10moGpjEgkvmPTsf6Ox4BI@N5rPp') + expect(keyAusQuery('FEHLER: Der Registrierungsschlüssel oder -wert wurde nicht gefunden.')).toBe('') }) })