From 12b972038663e5ab0585c0aabb8c1ba46ef611f1 Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Sat, 12 Sep 2026 21:17:43 +0200 Subject: [PATCH] =?UTF-8?q?fix(v5):=20Beta-Key=20mit=20@=20kommt=20ganz=20?= =?UTF-8?q?an;=20Schl=C3=BCssel-Stand=20ehrlich=20(rot=20statt=20=E2=80=9E?= =?UTF-8?q?Rips=20laufen",=20nicht=20pr=C3=BCfbar=20ohne=20Laufwerk);=20Ze?= =?UTF-8?q?iten=20in=20Ortszeit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WAS: KEY_MUSTER nimmt alles bis Leerraum/HTML-Zeichen (der September-Key „T-…4BI@N5rPp" hat 68 Zeichen mit @; das alte Muster lieferte 62 und MakeMKV lief danach ohne Schlüssel). extractGueltigBis versteht „end of September 2026". Die Kette prüft zuerst den vorhandenen Schlüssel und lässt ihn stehen, wenn MakeMKV ihn annimmt (kein Überschreiben gekaufter Keys mehr); reg add wird auf Erfolg geprüft; 5042 ohne Lizenzzeile heißt „nicht prüfbar" statt „angenommen". Fenster: Ablehnung ist ROT mit dem Satz, dass Blu-rays dann nur noch in MakeMKVs Gnadenfrist laufen; die MakeMKV-Karte sagt, was Rippy in MakeMKVs Einstellungen schreibt. Ein neuer Werkzeug-Pfad wird sofort gesucht, ein neuer eigener Schlüssel sofort geprüft. datumKurz zeigt Ortszeit statt UTC. WARUM: Durchsicht 12.09.2026, Funde F1, F5, F12, F13 und A2 — am echten Forum-HTML und an makemkvcon ohne Laufwerk gemessen. Co-Authored-By: Claude Opus 5 --- rippy-windows/src/fenster/App.tsx | 4 +- rippy-windows/src/fenster/bausteine.tsx | 7 +- .../src/fenster/dialoge/TitelWahlDialog.tsx | 4 +- .../src/fenster/einstellungen/Karten.tsx | 18 +- .../src/fenster/uebersicht/LaufwerkKachel.tsx | 3 +- rippy-windows/src/kern/index.ts | 6 + .../src/kern/werkzeuge/schluessel.ts | 180 ++++++++++++++---- rippy-windows/test/audiocd.test.ts | 15 ++ rippy-windows/test/commander-funde.test.ts | 34 ++++ 9 files changed, 224 insertions(+), 47 deletions(-) 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('') }) })