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('')
})
})