feat(v5): Etappe W-7 — Audio-CD, ISO-Backup, MakeMKV-Schluesselkette
Ampel / ampel (push) Successful in 1m20s

rip/audiocd.ts: die Rechnungen PUR (MSF→LBA mit den 150 Vorlauf-Frames,
TOC-Zerlegung mit Lead-Out-Pflicht und Daten-Track-Bit, 27-Sektoren-
Bloecke unter der 64-KB-Treibergrenze, RAW_READ_INFO mit DiskOffset in
2048ern OBWOHL der Sektor 2352 hat, WAV-Kopf, AC/DC-sichere Namen).
metadaten/musicbrainz.ts: DiscID nach musicbrainz.org-Rechnung — gegen
die ECHTE AC/DC-Kennung getestet (3KVSIWn_…), Vorlauf wird beim Melden
wieder HINZUgerechnet; Tags je Spur mit Bindewort-Kuenstlern.
laufwerk/disc.ts: audioToc + audioSpurLesen ueber die LaufwerkApi.

rip/iso.ts (§ 6.4): 1:1-Abbild fuer unbekannte Discs — async (ein
33-GB-Abbild darf den Kern nicht blockieren), sektor-ausgerichtet,
unvollstaendig heisst FEHLER mit Byte-Zahlen, nie Schoenfaerberei.

werkzeuge/schluessel.ts (§ 9.1): Dauerlizenz schlaegt Forum-Beta-Key
(t=1053, T-Muster aus makemkv_key.py), Ablage in HKCU ueber reg.exe
(kein zweiter koffi-Ort), GEFRAGT statt gerechnet (makemkvcon info
disc:9999, MSG 5020/5021/5051), abgelehnter Key wird ZURUECKGENOMMEN
(schlimmer als keiner: 0 Titel, kein Laufwerk), app_UpdateEnable nur-
wenn-fehlt (der 4K-Schluesselkanal). Kern prueft beim Start und
taeglich; Dashboard-Kachel zeigt Stand + letzten Pruefzeitpunkt;
Dauerlizenz-Feld in den Einstellungen.

Pipeline verzweigt nach Disc-Typ: cd → Sektoren→WAV→FLAC mit
MusicBrainz-Tags (Ordner 'Kuenstler - Album', ohne Treffer ehrlich
'Track NN'), unknown → ISO. Bibliothek-Eintraege fuer beide.

181 Tests gruen, Smoke gruen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-30 18:56:07 +02:00
co-authored by Claude Fable 5
parent c78ff22ab7
commit 2d9cef22b3
14 changed files with 1045 additions and 2 deletions
+26
View File
@@ -322,6 +322,13 @@ function Einstellungen({ verbindung }: { verbindung: Verbindung }) {
<Feld name="TMDb-API-Key" geheim wert={werte['tmdbApiKey'] ?? ''} aufAendern={(w) => setzen('tmdbApiKey', w)} />
<Feld name="OMDb-API-Key" geheim wert={werte['omdbApiKey'] ?? ''} aufAendern={(w) => setzen('omdbApiKey', w)} />
</div>
<Feld
name="MakeMKV-Schlüssel (Dauerlizenz)"
hinweis="schlägt den automatischen Beta-Key — leer lassen, wenn keiner gekauft ist"
geheim
wert={werte['makemkvAppKey'] ?? ''}
aufAendern={(w) => setzen('makemkvAppKey', w)}
/>
</section>
<section className="space-y-3">
@@ -426,6 +433,7 @@ export default function App() {
einstellungen: {},
werkzeuge: null,
bibliothek: [],
schluessel: null,
meldungen: [],
})
@@ -535,6 +543,24 @@ export default function App() {
: ['Ping unterwegs …']
}
/>
<Kachel
titel="MakeMKV-Schlüssel (§ 9.1)"
zustand={
verbindung.schluessel === null
? 'wartet'
: verbindung.schluessel.angenommen === true
? 'ok'
: 'fehler'
}
zeilen={
verbindung.schluessel === null
? ['wird geprüft …']
: [
verbindung.schluessel.text,
`zuletzt geprüft: ${verbindung.schluessel.geholtAm.slice(0, 16).replace('T', ' ')}`,
]
}
/>
</div>
</section>
@@ -29,6 +29,7 @@ export interface Verbindung {
einstellungen: Record<string, string>
werkzeuge: WerkzeugAuskunft | null
bibliothek: BibliothekEintrag[]
schluessel: { quelle: string; angenommen: boolean | null; gueltigBis: string; geholtAm: string; text: string } | null
/** Kurze Ereigniszeilen (neueste zuerst): Disc rein/raus, Auswurf, Fehler. */
meldungen: string[]
}
@@ -47,6 +48,7 @@ const stand: Verbindung = {
einstellungen: {},
werkzeuge: null,
bibliothek: [],
schluessel: null,
meldungen: [],
}
const horcher = new Set<Horcher>()
@@ -122,6 +124,10 @@ function kernNachricht(nachricht: KernNachricht): void {
case 'bibliothek':
stand.bibliothek = nachricht.eintraege
break
case 'schluessel-status':
stand.schluessel = nachricht.stand
if (nachricht.stand.angenommen === false) meldung(`⚠ MakeMKV-Schlüssel: ${nachricht.stand.text}`)
break
case 'rip-status': {
stand.rips = { ...stand.rips, [nachricht.status.id]: nachricht.status }
if (nachricht.status.phase === 'fertig') {