From d5ea48cd1e464263ee743c55fa5310e77de1dd17 Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Mon, 31 Aug 2026 20:18:08 +0200 Subject: [PATCH] =?UTF-8?q?feat(v5):=20MakeMKV-Selbst-Update=20(=C2=A7=209?= =?UTF-8?q?)=20und=20Kino-Look=20f=C3=BCrs=20Fenster?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update-Strecke: Quellenkette (Hersteller 525 → Forum → Archiv, höchste gewinnt, 31.08. nachgemessen), MZ- + Versions-Ressourcen-Prüfung (GuinpinSoft inc, gemessen), Download in .neu, Start übers Haupt (shell/UAC — Installer hängt bewusst NICHT an der Leine), Warte-Poll bis die EXE-Version wechselt, dann Schlüssel neu prüfen. Ehrlicher Zweig: installierte == neueste Fassung → kein sinnloser Download. Fenster: Laufwerks-Kachel als Bühne (TMDb-Backdrop, Poster, Beschreibung, Phasen-Label, Restzeit, aufklappbares Protokoll), Bibliothek als Poster-Regal (DB-Schema 3: poster_pfad, Migration), Schlüssel-Karte mit Update-Knopf und Beschaffungs-Balken, MakeMKV-Version als Badge. Co-Authored-By: Claude Fable 5 --- beweise/ui-kino-look.png | Bin 0 -> 42940 bytes rippy-windows/.gitignore | 3 +- rippy-windows/BAUEN.md | 12 + rippy-windows/src/fenster/App.tsx | 588 ++++++++++++------ rippy-windows/src/fenster/kernverbindung.ts | 41 +- rippy-windows/src/gemeinsam/nachrichten.ts | 56 +- rippy-windows/src/haupt/index.ts | 26 +- rippy-windows/src/haupt/kernstart.ts | 5 + rippy-windows/src/kern/ablauf/pipeline.ts | 3 + rippy-windows/src/kern/index.ts | 123 ++++ rippy-windows/src/kern/metadaten/zuordnung.ts | 6 + rippy-windows/src/kern/speicher/db.ts | 17 +- .../src/kern/werkzeuge/beschaffen.ts | 330 ++++++++++ .../src/kern/werkzeuge/schluessel.ts | 5 + rippy-windows/test/beschaffung.test.ts | 185 ++++++ 15 files changed, 1199 insertions(+), 201 deletions(-) create mode 100644 beweise/ui-kino-look.png create mode 100644 rippy-windows/src/kern/werkzeuge/beschaffen.ts create mode 100644 rippy-windows/test/beschaffung.test.ts diff --git a/beweise/ui-kino-look.png b/beweise/ui-kino-look.png new file mode 100644 index 0000000000000000000000000000000000000000..294111456b046f92d0a268f5f19582a5de43bb36 GIT binary patch literal 42940 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 literal 0 HcmV?d00001 diff --git a/rippy-windows/.gitignore b/rippy-windows/.gitignore index ef99b70..a541bc9 100644 --- a/rippy-windows/.gitignore +++ b/rippy-windows/.gitignore @@ -5,6 +5,7 @@ out/ # Setup-Bau (electron-builder) und die MITGELIEFERTEN Binaerdateien — # vendor/ wird beim Paketieren aus LOCALAPPDATA\Rippy\tools befuellt # (BAUEN.md), 74 MB gehoeren nicht ins Git. -dist-setup/ +# Auch die Ausweich-Ordner der win-unpacked-Sperre (BAUEN.md) +dist-setup*/ vendor/ .electron-cache/ diff --git a/rippy-windows/BAUEN.md b/rippy-windows/BAUEN.md index 500209a..a5fca40 100644 --- a/rippy-windows/BAUEN.md +++ b/rippy-windows/BAUEN.md @@ -59,6 +59,18 @@ Adminrechte) plus `latest.yml` und `.blockmap` — **alle drei Dateien gehören zum Release** (§ 6.8: `latest.yml` ist die Update-Auskunft, die `.blockmap` macht aus dem Voll-Download einen Differenz-Download). +⚠ **Die `win-unpacked`-Sperre (30./31.08.2026 gemessen):** Nachdem der +Paket-Smoke (`win-unpacked\Rippy.exe --smoke`) gelaufen ist, hält ein +Filter-Treiber (Virenscanner/Indexer — KEIN Prozess mit sichtbarem Pfad) +`resources\app.asar` mitunter STUNDENLANG fest. Der nächste Bau in +denselben Ordner scheitert dann mit `EBUSY … unlink app.asar`, und zwar +mit **Exit-Code 0** (electron-builder!) — nicht vom Log-Ende täuschen +lassen, die ⨯-Zeile zählt. Ausweg: in ein frisches Verzeichnis bauen +(`npx electron-builder --win --config electron-builder.yml +-c.directories.output=dist-setup-neu`), die fertige Setup-EXE nach +`dist-setup/` KOPIEREN (eine Kopie ist eine neue Datei ohne Sperre) und +den gesperrten Ordner löschen, sobald er wieder frei ist. + Vorher einmalig `vendor/` befüllen (die MITGELIEFERTEN Werkzeuge, § 9 — liegen nicht im Git, 74 MB): diff --git a/rippy-windows/src/fenster/App.tsx b/rippy-windows/src/fenster/App.tsx index d5cd2b1..da879b0 100644 --- a/rippy-windows/src/fenster/App.tsx +++ b/rippy-windows/src/fenster/App.tsx @@ -1,6 +1,8 @@ -// Die Oberfläche (W-5): Übersicht (Laufwerke + Gerüst-Status), -// Einstellungen (EINE Quelle — alles läuft über den Kern, § 6.7) und die -// Bibliothek (§ 6.6). Design-Sprache wie das heutige Rippy: dunkel, Kacheln. +// Die Oberfläche — seit dieser Runde im Kino-Look: Die Laufwerks-Kachel +// wird zur Bühne (TMDb-Backdrop als Panorama, Poster, Beschreibung), die +// Bibliothek zum Poster-Regal, der MakeMKV-Schlüssel bekommt seinen +// Update-Knopf (§ 9). Die Regeln bleiben: Übersicht/Einstellungen/ +// Bibliothek, EINE Einstellungs-Quelle über den Kern (§ 6.7), R2/R4. import { useEffect, useState } from 'react' import type { DiscInfoStand, GeraeteInfo, HauptStatus, RipStatus } from '../gemeinsam/nachrichten' import { presetEmpfehlung } from '../gemeinsam/preset-empfehlung' @@ -8,9 +10,11 @@ import { aufVerbindung, auswerfen, einstellungSetzen, + makemkvUpdate, metadatenSuchen, ripAbbrechen, ripStarten, + schluesselPruefen, verbindungStarten, zuordnungSetzen, type Verbindung, @@ -23,12 +27,24 @@ function posterUrlKlein(pfad: string): string { return `https://image.tmdb.org/t/p/w154${pfad}` } +function posterUrlGross(pfad: string): string { + if (pfad.length === 0) return '' + if (pfad.startsWith('http')) return pfad + return `https://image.tmdb.org/t/p/w342${pfad}` +} + +function backdropUrl(pfad: string): string { + if (pfad.length === 0) return '' + if (pfad.startsWith('http')) return pfad + return `https://image.tmdb.org/t/p/w780${pfad}` +} + function restzeitText(prozent: number, startMs: number | undefined): string { if (startMs === undefined || prozent <= 2 || prozent >= 100) return '' const verstrichen = (Date.now() - startMs) / 1000 const restS = Math.round((verstrichen * (100 - prozent)) / prozent) - if (restS < 90) return `· ~${restS} s` - return `· ~${Math.round(restS / 60)} min` + if (restS < 90) return `noch ~${restS} s` + return `noch ~${Math.round(restS / 60)} min` } function istHauptStatus(wert: unknown): wert is HauptStatus { @@ -51,6 +67,24 @@ const TYP_NAMEN: Record = { unknown: 'unbekannt', } +const TYP_SYMBOL: Record = { + cd: '💿', + dvd: '📀', + bluray: '📀', + uhd: '📀', + iso: '💽', +} + +const PHASEN_NAMEN: Record = { + 'liest-info': 'liest Titel-Info', + rippt: 'rippt (verlustfrei)', + komprimiert: 'komprimiert', + 'legt-ab': 'legt ab', + fertig: 'fertig', + fehler: 'Fehler', + abgebrochen: 'abgebrochen', +} + const LAUFENDE_PHASEN = ['liest-info', 'rippt', 'komprimiert', 'legt-ab'] function sicherheitsWort(confidence: number): string { @@ -60,6 +94,20 @@ function sicherheitsWort(confidence: number): string { return 'unsicher' } +function Badge({ text, ton }: { text: string; ton: 'gruen' | 'gelb' | 'blau' | 'grau' }) { + const farben = { + gruen: 'border-emerald-500/40 bg-emerald-500/10 text-emerald-300', + gelb: 'border-amber-500/40 bg-amber-500/10 text-amber-300', + blau: 'border-sky-500/40 bg-sky-500/10 text-sky-300', + grau: 'border-slate-600/60 bg-slate-800/60 text-slate-300', + } + return ( + + {text} + + ) +} + interface KachelProps { titel: string zustand: 'ok' | 'fehler' | 'wartet' @@ -70,7 +118,7 @@ function Kachel({ titel, zustand, zeilen }: KachelProps) { const punktFarbe = zustand === 'ok' ? 'bg-emerald-400' : zustand === 'fehler' ? 'bg-red-400' : 'bg-amber-400' return ( -
+

{titel}

@@ -105,9 +153,12 @@ function AendernDialog({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return ( -
+
e.stopPropagation()} >

Was ist das wirklich?

@@ -167,109 +218,224 @@ function AendernDialog({ ) } +// Die Bühne: Backdrop als Panorama, Poster, Titel — und darunter der +// laufende Vorgang mit Phase, Restzeit und aufklappbarem Protokoll. function LaufwerkKachel({ geraet, rip, info, vorschlaege, ripStartMs, + protokoll, }: { geraet: GeraeteInfo rip: RipStatus | undefined info: DiscInfoStand | undefined vorschlaege: MetaVorschlag[] ripStartMs: number | undefined + protokoll: string[] }) { const [aendernOffen, setAendernOffen] = useState(false) + const [protokollOffen, setProtokollOffen] = useState(false) const ripLaeuft = rip !== undefined && LAUFENDE_PHASEN.includes(rip.phase) - const zustand = geraet.status === 'ready' ? 'ok' : geraet.status === 'empty' ? 'wartet' : 'fehler' - const punktFarbe = ripLaeuft - ? 'animate-pulse bg-sky-400' - : zustand === 'ok' - ? 'bg-emerald-400' - : zustand === 'fehler' - ? 'bg-red-400' - : 'bg-slate-500' + const discDa = geraet.status === 'ready' + const backdrop = discDa && info !== undefined ? backdropUrl(info.backdropPfad) : '' + const poster = discDa && info !== undefined ? posterUrlGross(info.posterPfad) : '' + const erkannt = discDa && info !== undefined && info.titel.length > 0 + return ( -
-
-
- -

{geraet.name}

-
-
- {ripLaeuft ? ( +
+ {backdrop.length > 0 && ( + <> + +
+
+ + )} + +
+
+
+ +

+ Laufwerk {geraet.id}: + {geraet.modell} +

+
+
+ {ripLaeuft ? ( + + ) : ( + + )} - ) : ( - - )} - +
-
-
- {geraet.status === 'ready' && info !== undefined && info.posterPfad.length > 0 && ( - + + {!discDa && ( +
+ 💿 +

+ {geraet.status === 'empty' ? 'Keine Disc eingelegt — Disc rein genügt.' : geraet.grund} +

+
+ )} + + {discDa && ( +
+ {poster.length > 0 ? ( + + ) : ( +
+ {TYP_SYMBOL[geraet.typ] ?? '💿'} +
+ )} +
+ {erkannt ? ( + <> +

+ {info.titel} + {info.jahr !== null && ({info.jahr})} +

+
+ + {geraet.groesseBytes > 0 && } + 0 ? ` · ${info.quelle}` : ''}`} + ton={info.confidence >= 0.8 ? 'gruen' : 'gelb'} + /> +
+ {info.beschreibung.length > 0 && ( +

+ {info.beschreibung} +

+ )} + + ) : ( + <> +

+ {TYP_NAMEN[geraet.typ]} + {geraet.groesseBytes > 0 && ( + {gb(geraet.groesseBytes)} + )} +

+

+ {geraet.typ === 'cd' + ? 'Titel kommen beim Rippen von MusicBrainz.' + : 'Noch keine Zuordnung — „Ändern …" hilft nach.'} +

+ + )} +
+ + {info !== undefined && info.schonGerippt !== null && ( + + )} +
+ {geraet.grund.length > 0 &&

{geraet.grund}

} +
+
+ )} + + {rip !== undefined && ( +
+
+

+ {PHASEN_NAMEN[rip.phase]} + {rip.lesefehler && rip.phase === 'fertig' ? ' — mit Lesefehlern' : ''} +

+ {ripLaeuft && rip.prozent >= 0 && ( +

+ {rip.prozent} % + {restzeitText(rip.prozent, ripStartMs)} +

+ )} +
+ {ripLaeuft && ( +
+
+
+ )} +

{rip.text}

+ {rip.dateien.map((datei) => ( +

+ {datei} +

+ ))} + {protokoll.length > 1 && ( + + )} + {protokollOffen && ( +
+ {protokoll.map((zeile, i) => ( +

+ {zeile} +

+ ))} +
+ )} +
)} -
-

{geraet.modell.length > 0 ? geraet.modell : 'Modell unbekannt'}

- {geraet.status === 'ready' && ( -

- {TYP_NAMEN[geraet.typ]} - {geraet.groesseBytes > 0 ? ` · ${gb(geraet.groesseBytes)}` : ''} -

- )} - {geraet.status === 'ready' && info !== undefined && info.titel.length > 0 && ( -

= 0.8 ? 'text-emerald-300' : 'text-amber-300'}> - {sicherheitsWort(info.confidence)}: {info.titel} - {info.jahr !== null ? ` (${info.jahr})` : ''} · {Math.round(info.confidence * 100)} % - {info.quelle.length > 0 ? ` · ${info.quelle}` : ''} -

- )} - {geraet.status === 'ready' && ( - - )} - {geraet.status === 'ready' && info !== undefined && info.schonGerippt !== null && ( -

- ⚠ Diese Disc wurde schon einmal gerippt ({info.schonGerippt.slice(0, 10)}) — siehe Bibliothek. -

- )} - {geraet.status === 'empty' &&

keine Disc eingelegt

} - {geraet.status === 'unknown' &&

{geraet.grund}

} - {geraet.status !== 'unknown' && geraet.grund.length > 0 && ( -

{geraet.grund}

- )} -
+ {aendernOffen && ( setAendernOffen(false)} /> )} - {rip !== undefined && ( -
- {ripLaeuft && ( -
-
-
- )} -

- {ripLaeuft && rip.prozent >= 0 - ? `${rip.prozent} % ${restzeitText(rip.prozent, ripStartMs)} — ` - : ''} - {rip.text} -

- {rip.dateien.map((datei) => ( -

- {datei} -

- ))} +
+ ) +} + +// Die MakeMKV-Schlüssel-Karte (§ 9.1) — mit dem Update-Weg aus § 9: +// „MakeMKV aktualisieren" holt den Installer über die Quellenkette, +// prüft ihn und lässt Windows fragen (UAC). Danach prüft Rippy neu. +function SchluesselKarte({ verbindung }: { verbindung: Verbindung }) { + const s = verbindung.schluessel + const b = verbindung.beschaffung + const zustand: 'ok' | 'fehler' | 'wartet' = + s === null ? 'wartet' : s.angenommen === true ? 'ok' : s.weiterBetrieb ? 'wartet' : 'fehler' + const punktFarbe = + zustand === 'ok' ? 'bg-emerald-400' : zustand === 'fehler' ? 'bg-red-400' : 'bg-amber-400' + return ( +
+
+
+ +

MakeMKV-Schlüssel

+ {s !== null && s.version.length > 0 && }
- )} +
+ {s !== null && s.angenommen === false && ( + + )} + +
+
+
+ {s === null ? ( +

wird geprüft …

+ ) : ( + <> +

{s.text}

+

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

+ + )} + {b !== null && ( +
+

+ {b.text} +

+ {b.laeuft && b.anteil !== null && ( +
+
+
+ )} +
+ )} +
) } @@ -517,7 +721,7 @@ function Einstellungen({ verbindung, haupt }: { verbindung: Verbindung; haupt: H

Werkzeuge

-
+

0 ? 'text-emerald-300' : 'text-red-300'}> makemkvcon: {verbindung.werkzeuge === null ? 'wird gesucht …' : verbindung.werkzeuge.makemkvcon || 'NICHT GEFUNDEN — MakeMKV installieren'}

@@ -536,7 +740,7 @@ function Einstellungen({ verbindung, haupt }: { verbindung: Verbindung; haupt: H ) } -// ── Bibliothek ────────────────────────────────────────────────────────── +// ── Bibliothek: das Poster-Regal (§ 6.6) ──────────────────────────────── function Bibliothek({ verbindung }: { verbindung: Verbindung }) { if (verbindung.bibliothek.length === 0) { return ( @@ -545,37 +749,41 @@ function Bibliothek({ verbindung }: { verbindung: Verbindung }) {

) } + const gesamt = verbindung.bibliothek.reduce((summe, e) => summe + e.groesseBytes, 0) return ( -
- - - - - - - - - - - - - {verbindung.bibliothek.map((eintrag) => ( - - - - - - - - - ))} - -
TitelTypGrößeDatumDauerAblageort
- {eintrag.titel} - {eintrag.jahr !== null ? ` (${eintrag.jahr})` : ''} - {eintrag.typ}{gb(eintrag.groesseBytes)}{eintrag.datum.slice(0, 16).replace('T', ' ')}{Math.round(eintrag.dauerS / 60)} min - {eintrag.ablageort} -
+
+

+ {verbindung.bibliothek.length} {verbindung.bibliothek.length === 1 ? 'Eintrag' : 'Einträge'} ·{' '} + {gb(gesamt)} gesamt +

+
+ {verbindung.bibliothek.map((eintrag) => ( +
+ {eintrag.posterPfad.length > 0 ? ( + + ) : ( +
+ {TYP_SYMBOL[eintrag.typ] ?? '🎬'} +
+ )} +

+ {eintrag.titel} + {eintrag.jahr !== null ? ` (${eintrag.jahr})` : ''} +

+

+ {TYP_NAMEN[eintrag.typ as GeraeteInfo['typ']] ?? eintrag.typ} · {gb(eintrag.groesseBytes)} +

+
+ ))} +
) } @@ -757,10 +965,12 @@ export default function App() { discInfos: {}, vorschlaege: {}, ripStart: {}, + ripProtokoll: {}, einstellungen: {}, werkzeuge: null, bibliothek: [], schluessel: null, + beschaffung: null, meldungen: [], }) @@ -773,6 +983,7 @@ export default function App() { }, []) const db = verbindung.kern?.datenbank ?? null + const einRipLaeuft = Object.values(verbindung.rips).some((r) => LAUFENDE_PHASEN.includes(r.phase)) // § 6.9: Die Erst-Einrichtung läuft NACH der Installation, im Programm — // solange sie nicht abgeschlossen ist, ist sie die ganze Ansicht. @@ -793,12 +1004,23 @@ export default function App() { ) return ( -
-
-
-
-

Rippy

- v{haupt?.version ?? '…'} +
+
+
+
+ + 💿 + +
+

Rippy

+ v{haupt?.version ?? '…'} + {einRipLaeuft && ( + + + arbeitet + + )} +