051cab3afa4a897bc371eb471330e922ba9ecbe7
100
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
051cab3afa |
fix(v5): Das Update installierte nie — die eigene Leine erschlug den Installer
Ampel / ampel (push) Successful in 1m41s
Commander: "Rippy wird zwar sauber beendet aber startet NICHT neu in der
neuen version."
Ursache, auf drei Beinen belegt:
1. Bibliothek: electron-updater startet den Installer per
spawn(..., { detached: true }) (BaseUpdater.js, an der installierten
Fassung nachgelesen). `detached` setzt unter Windows KEIN Breakaway.
2. Leine: leine.ts setzt KILL_ON_JOB_CLOSE, und Kinder erben die
Mitgliedschaft. Der Installer war damit Mitglied der Arbeitsgruppe.
3. Platte: pending\RippySetup-5.1.3.exe lag geladen da (18:11:47),
installiert war weiter 5.1.2. Der Download klappte, nur die
Installation nicht.
Der Installer starb also in genau der Sekunde, in der Rippy sich
beendete, um ihm Platz zu machen.
Fix — die Leine bleibt, der Installer klinkt sich aus:
* leine.ts setzt zusaetzlich JOB_OBJECT_LIMIT_BREAKAWAY_OK. Das aendert
fuer sich genommen NICHTS: Kinder erben weiter, ausser sie verlangen
das Ausklinken selbst. Kern, makemkvcon und HandBrakeCLI bleiben
angeleint, die rc10-Waisen-Falle (Paragraph 3.3) ist unveraendert zu.
* Neu: ausserhalbDerLeineStarten() startet EIN Programm per CreateProcessW
mit CREATE_BREAKAWAY_FROM_JOB. Liegt in leine.ts, weil dort die
Arbeitsgruppe verwaltet wird — und weil Waechter R1 koffi nur dort und
in kern/laufwerk/win32.ts erlaubt.
* update.ts installiert jetzt SELBST: autoInstallOnAppQuit ist aus, der
Installerpfad kommt aus downloadUpdate() (laut AppUpdater.d.ts "Paths to
downloaded files"), die Argumente sind die von electron-updater
gemessenen (--updated /S --force-run). Gestartet wird beim Beenden
(will-quit) und ueber den Knopf.
* Neu kommandozeile.ts (pur, ohne koffi/Electron): CreateProcessW nimmt
EINE Zeichenkette. Ohne die Regeln von CommandLineToArgvW zerfiele ein
Pfad wie "C:\Users\Tobi Neu\..." in zwei Argumente.
BEWIESEN, nicht behauptet — neu: beweise/leine-breakaway.js
OHNE Ausklinken (so macht es electron-updater)
lebt nach dem Tod des Elternprozesses: NEIN -> WIE ERWARTET
MIT Ausklinken (so macht es Rippy ab 5.1.4)
lebt nach dem Tod des Elternprozesses: JA -> WIE ERWARTET
Der erste Messlauf zeigte ein falsches Negativ: Der Enkel starb an der
sterbenden KONSOLE des Elternprozesses, nicht an der Arbeitsgruppe. Erst
mit CREATE_NO_WINDOW (eigene Konsole) misst der Versuch, was er messen
soll. Diese Lehre steht als Kommentar im Code und der Flag ist auch im
echten Aufruf gesetzt.
237 Tests gruen (vorher 230), Typpruefung sauber. Version 5.1.4.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
1d53e8526f |
docs: SAVEPOINT — 5.1.3 im Kanal, "zieht nichts hoch" aufgeklaert
Ampel / ampel (push) Successful in 1m33s
Das Update ZOG: RippySetup-5.1.2.exe lag geladen und pruefsummengleich in rippy-updater\pending. Es fehlte nur das echte Beenden ueber das Tray — Fenster zumachen beendet Rippy nicht (Paragraph 4.1 Punkt 3). 5.1.3 anonym von aussen nachgemessen: Content-Length = latest.yml = lokale Datei, releaseNotes durchgereicht, Umlaute intakt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
827910fd63 |
feat(v5): eigenes Fenster ohne Windows-Rahmen + Layout skaliert mit
Ampel / ampel (push) Successful in 1m34s
Zwei Befunde des Commanders (01.09.2026):
1. "Momentan ist alles nach links gerueckt, nichts skaliert mit der
fenstergroesse und es ist alles gequetscht."
Ursache gefunden: <main> hatte WEDER Zentrierung NOCH Maximalbreite,
die Karten darin aber einen harten Deckel von max-w-2xl (672 px). In
einem 1280er Fenster klebte damit alles am linken Rand und rechts
stand nichts. Behoben:
* <main> und Kopfzeile teilen sich mx-auto max-w-[1600px] mit
mitwachsendem Innenabstand — der Inhalt ist mittig und nutzt die
Breite.
* Der 672-px-Deckel der Einstellungs-Spalte ist weg. Drei
Encoder-Felder stehen jetzt nebeneinander statt untereinander.
* Die Sektionswahl der Einstellungen wird im schmalen Fenster zur
Zeile ueber den Karten (lg:flex-row), statt eine 176-px-Spalte vom
ohnehin knappen Platz abzuziehen.
* Info-Kacheln der Laufwerks-Karte: zwei Spalten schmal, vier breit.
* Startgroesse 1000x700 -> 1280x820, Mindestmass 720x480 -> 860x560.
2. "waere sogar richtig cool wenn du den rahmen wegbekommst"
frame:false, und die Kopfzeile IST jetzt die Titelleiste:
Minimieren / Maximieren / Schliessen sitzen oben rechts in
Windows-Massen (46x32), gezogen wird an der Kopfzeile, Doppelklick
maximiert. Groesse aendern bleibt moeglich — Electron setzt fuer
rahmenlose Fenster unter Windows weiter WS_THICKFRAME (thickFrame,
Standard true), die Kanten ziehen also wie gewohnt.
Schliessen nimmt GENAU den Weg des alten Fensterkreuzes
(fenster.close() -> der bestehende close-Horcher versteckt nur):
Rippy lebt im Tray weiter, ein laufender Rip merkt davon nichts
(Paragraph 4.1 Punkt 3).
Die Ziehflaeche ist .ziehbar in stil.css; alles Bedienbare darin
traegt .nicht-ziehbar, sonst verschluckt der Griff den Klick.
fensterMaximiert im HauptStatus entscheidet ueber das Knopf-Symbol
und wird auch bei Doppelklick/Tastenkuerzel nachgefuehrt (maximize-
und unmaximize-Horcher).
Nachgemessen mit dem Smoke-Beweis: beweise/ui-513-uebersicht.png (kein
Rahmen, eigene Knoepfe, Inhalt ueber die volle Breite) und
beweise/ui-513-einstellungen.png (drei Encoder nebeneinander).
230 Tests gruen, Typpruefung sauber. Version 5.1.3.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
0c38754c09 |
docs: SAVEPOINT — 5.1.2 im Update-Kanal, Changelog nachgemessen
Ampel / ampel (push) Successful in 1m36s
releaseNotes stehen anonym abrufbar in latest.yml (1191 B statt 337 B, Umlaute intakt), Content-Length der EXE = Zahl in latest.yml = lokale Datei. Notiert: Die Update-Karte ist erst AB 5.1.2 sichtbar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c0a7618e33 |
feat(v5): Update sichtbar machen — Meldung, Knopf und Changelog
Ampel / ampel (push) Successful in 1m33s
Der Commander: "irgendwie fehlt in rippy selbst der Button 'Auf updates pruefen' oder auch direkt beim start ne meldung 'ey jo update verfuegbar' - waere noch cool wenn das kommt inkl. changelog". Zu Recht. Der Kanal arbeitete korrekt, war aber UNSICHTBAR: Der Update-Stand lag im Haupt und wurde nur beim Laden des Fensters EINMAL mitgeschickt. Fand die Pruefung etwas, aenderte sich am Bildschirm nichts; der einzige Hinweis war ein Satz unter "Update-Adresse", den man kennen musste, um ihn zu finden. Was jetzt da ist: * Eine goldene Karte OBEN in der Uebersicht, sobald etwas gefunden wird — mit neuer Versionsnummer, der laufenden Version, Ladefortschritt und den Aenderungsnotizen. Dazu ein Windows-Toast beim Fund. * Knopf "Jetzt auf Updates pruefen" in den Einstellungen. Waehrend eines Rips ist er AUS, mit Begruendung im Tooltip — Rippy sucht dann bewusst nicht (Paragraph 6.8). * Knopf "Jetzt neu starten und installieren" auf der Karte, sobald das Paket geladen ist. Sagt NEIN mit Grund statt still nichts zu tun (R4). * Der Stand meldet JEDE Aenderung sofort ans Fenster (setzen() ruft immer beiAenderung) — kein Zustand mehr, den niemand erfaehrt. Der Changelog kommt aus bau/release-notes.md. An der installierten Bibliothek geprueft statt aus dem Kopf (Regel D): app-builder-lib out/publish/updateInfoBuilder.js getReleaseInfo() liest genau diesen Dateinamen und legt den Inhalt als releaseNotes in latest.yml; builder-util-runtime deklariert UpdateInfo.releaseNotes als "string | Array<ReleaseNoteInfo> | null". neuerungenText() vertraegt beide Formen, wirft HTML-Marken raus und deckelt die Laenge. UpdateStand ist jetzt EINE Definition in gemeinsam/nachrichten.ts, die sich Haupt und Fenster teilen. 230 Tests gruen (vorher 225), Typpruefung sauber. Version 5.1.2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7d496cf654 |
docs: SAVEPOINT — 5.1.1 im Update-Kanal, Gitea-Muster geklaert
Ampel / ampel (push) Successful in 1m59s
Release aktuell traegt jetzt 5.1.1 (vier Dateien, anonym von aussen nachgemessen: latest.yml 200/version 5.1.1, EXE Content-Length 131526527 = Zahl in latest.yml = lokale Datei). KONZEPT 3.6 beantwortet: releases/latest/download liefert HTTP 404 — Gitea 1.27 bedient das GitHub-Muster NICHT. Das feste Tag aktuell ist damit der einzige Weg, nicht nur die Rueckfallebene. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4f943e2c4f |
docs: SAVEPOINT — Disc-Erkennung repariert, 5.1.1 gebaut
Ampel / ampel (push) Successful in 1m37s
Ampel gruen fuer
|
||
|
|
38a3a87f70 |
fix(v5): Disc-Erkennung — die Disc selbst befragen statt raten
Ampel / ampel (push) Successful in 1m38s
Der Commander legte Disc 2 von „Spartacus: Gods of the Arena" ein, Rippy meldete den FILM „Spartacus" von 1960. An der echten Disc gemessen (Laufwerk G:, Label SPARTACUS_GOTA_D2, UDF, 33.759.100.928 Bytes): Der stark gestutzte Kandidat „Spartacus" stand auf Platz 2 der Suchvarianten und holte dort einen wörtlichen Filmtreffer mit 95 Prozent — die richtige Serie stand auf Platz 6 und wurde nie gefragt. Vier Ursachen, alle behoben: 1. Die Sicherheit maß die ART des Treffers, nie die QUALITÄT der Frage. Neu: abdeckung() + MINDEST_ABDECKUNG — wer mehr als die Hälfte des Titels wegwerfen musste, um zu treffen, bekommt höchstens einen Vorschlag (0,60), und das UI fragt nach, statt sich sicher zu irren. 2. Verpackungs-Zusätze galten als Wortmüll. Neu: discZusatzAbtrennen() trennt "- Disc 2" / "_D2" / "S1" ab UND merkt sie — die Nummer braucht die Ablage später sowieso. Vier Fallen sind abgesichert: "Rocky 2", "Kill Bill Teil 2", "Ocean s 11" (Apostroph-Wortgrenze) und nackte Zahlen am Ende. 3. Der Titelvergleich war zeichengenau. Ein Volume-Label KANN keinen Doppelpunkt tragen, TMDb schreibt ihn — der exakt richtige Treffer fiel an einem Satzzeichen durch. Neu: gleichBedeutend(). 4. Rippy öffnete das Inhaltsverzeichnis der Disc und las nur die erste Zeile daraus. Neu: inhaltAusBdmt() liest auch <di:titleName>. "EP 1" und "EP 2" beweisen die Serie, "DUB GER 1" und "104 GER FSK" nicht. Ist die Serie bewiesen, sind die Film-Zweige der Kaskade AUS. Gemessen an der eingelegten Disc (neue messung.disc-erkennung.test.ts): Titel "Spartacus: Gods Of The Arena - Disc 2" (Quelle: bdmt) Disc 2 | Staffel — | Folgen 2 | Serie bewiesen: true Varianten: "...Arena - Disc 2" -> "...Arena" -> ... -> "Spartacus" 225 Tests grün (vorher 209), Typprüfung sauber. Disc-Nummer, Staffel und Folgenzahl stehen jetzt auch im Fenster (Kachel neben der Erkennung). Version 5.1.1 — zugleich der erste echte Selbst-Update-Beweis (§ 6.8). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
673f7ff3e7 |
docs: SAVEPOINT — Übergabe an die nächste Session
Ampel / ampel (push) Successful in 1m31s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5522a50425 |
docs: SAVEPOINT — deployt: Update-Kanal aktuell live, main bereinigt
Ampel / ampel (push) Successful in 1m28s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d21332e3c7 |
docs: SAVEPOINT — Einstellungen-Karten und Version 5.1.0
Ampel / ampel (push) Successful in 1m44s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b64ddf5f06 |
feat(v5): Einstellungen als erklärende Karten-Seite — Version 5.1.0
Ampel / ampel (push) Successful in 1m35s
Der Einstellungs-Tab im Kino-Mix, statt magerer Feldliste: links eine Sektions-Navigation, rechts Karten mit je einem Satz Einordnung — Ablage (mit Struktur-Vorschau Filme/Musik/ISO/roh), Kompression (Schalter, gemessene Encoder als Badges, Empfehlungs-Erklärung), Metadaten (Key-Quellen im Klartext, hinterlegt/fehlt-Punkte), MakeMKV (eigene Karte: Live-Schlüsselstand, Update- und Prüf-Knopf, Dauerlizenz), Medienserver, Programm (Schalter, Update-Stand, Versionszeile) und Werkzeuge — NEU mit den drei „Eigener Pfad'-Feldern (werkzeug.*, vom Katalog seit je unterstützt, bisher ohne UI). Dazu: #einstellungen/#bibliothek als Startanker (RIPPY_START_BEREICH), RIPPY_SMOKE_WARTE_MS für Bild-Beweise, die auf späte Kern-Antworten warten (Einstellungen+Encoder-Messung kommen nach den 750 ms). Version 5.1.0 — erste Fassung ohne Vorab-Suffix, der Bau erzeugt damit erstmals latest.yml (der Update-Kanal aus § 6.8). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c12b1be448 |
docs: SAVEPOINT — Kino-Mix-Design durchgängig eingebaut
Ampel / ampel (push) Successful in 1m28s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b9de2ba729 |
feat(v5): Kino-Mix-Design durchs ganze Fenster (Commander-Entscheid)
Ampel / ampel (push) Successful in 1m54s
Der auf dem Design-Canvas gewählte Mix aus Kinosaal und Schaltpult, durchgängig über Übersicht, Einstellungen, Bibliothek, Erst-Einrichtung und Dialoge: warmes Bühnen-Schwarz mit Saallicht und Filmkorn, Gold als Akzent, Poster mit Goldkante, Plakat-Titel (Bricolage Grotesque) — dazu die Schaltpult-Präzision: REC-Glühen bei laufendem Rip, Info-Kacheln (Medium/Größe/Erkennung/Preset inkl. der echten Preset-Kette), 20-Segment-Aussteuerungsanzeige mit großer Mono-Prozentzahl, Mono-Protokoll, LED-Systemleiste statt Status-Kacheln (Systemfehler werden zur roten Karte, R4). Schriften gebündelt via fontsource (OFL, bau/lizenzen) — zur Laufzeit lädt Rippy nichts aus dem Netz. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
98ca2855e9 |
docs: SAVEPOINT — MakeMKV-Selbst-Update und Kino-Look
Ampel / ampel (push) Successful in 1m27s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d5ea48cd1e |
feat(v5): MakeMKV-Selbst-Update (§ 9) und Kino-Look fürs Fenster
Ampel / ampel (push) Successful in 1m34s
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 <noreply@anthropic.com> |
||
|
|
5622d8e47d |
docs: SAVEPOINT — erster Commander-Test, drei Funde behoben
Ampel / ampel (push) Successful in 1m28s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c5dd90cc18 |
fix(v5): Die drei Funde aus dem ersten Commander-Test
Ampel / ampel (push) Successful in 1m25s
FUND 1 — 'Key korrekt, aber als ungueltig angezeigt': Stimmt beides. Live gemessen: Der aktuelle Forum-Key ist in Ordnung, aber MakeMKV 1.18.4 ist ZU ALT fuer ihn — makemkvcon meldet 5020 UND 5021 zusammen, und die alte Pruefung brach beim ersten Code ab und beschuldigte den Schluessel. urteilAusAusgabe sammelt jetzt ALLE Codes (5021 schlaegt 5020, MakeMKV-Version aus MSG 1005 wird mitgelesen) und die Kachel sagt den wahren Satz: 'Der Key ist in Ordnung — MakeMKV 1.18.4 ist zu alt. Rippy hat ihn entfernt, damit der Beta-Modus weiterlaeuft (Rips klappen trotzdem). Abhilfe: MakeMKV aktualisieren.' Neues Feld weiterBetrieb: Beta-Modus ist GELB (Warnung), nicht rot. Test mit der echten Ausgabe als Fixture. FUND 2 — 'Komprimieren auf CPU statt GPU': Der Default 'H.265 MKV 1080p30' ist ein CPU-Preset. Neue GEMESSENE Empfehlung (gemeinsam/preset-empfehlung.ts): aus den echten Backends und der echten Preset-Liste des eigenen HandBrake — VCN vor NVENC vor QSV vor CPU, nur Namen die WIRKLICH in der Liste stehen, HandBrake-Presets deckeln die Aufloesung nur (DVD bleibt SD). Kette: eigene Wahl > allgemeines Preset > Empfehlung > CPU-Default. Auf dem Commander-PC: bluray/dvd -> 'H.265 VCN 1080p', uhd -> 'H.265 VCN 2160p 4K'. Die Preset-Auswahl zeigt 'automatisch: <Empfehlung>' als Leer-Option. FUND 3 — 'keine Alternative waehlbar': 'Aendern …'-Dialog an der Laufwerks-Kachel (§ 5 Schritt 4): TMDb-Suche (Film + Serie, mit Postern), Klick uebernimmt — Nutzer-Wahl gilt als 100 % und steuert Ablage-Ordner, NFO und Poster. Neue Nachrichten metadaten-suchen / zuordnung-setzen / metadaten-vorschlaege. Dazu: Poster in der Laufwerks-Kachel (CSP um image.tmdb.org erweitert), Restzeit-Schaetzung am Fortschritt. 192 Tests gruen, Smoke gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0e1c927e7b |
docs: SAVEPOINT — Rippy v5 komplett (W-0 bis W-7), Beweise und offene Hand-am-Geraet-Punkte
Ampel / ampel (push) Successful in 1m21s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9fa3df500d |
feat(v5): Etappe W-6 — Setup, Selbst-Update, Erst-Einrichtung, Lizenzen
Ampel / ampel (push) Successful in 1m22s
electron-builder → NSIS: EIN RippySetup-<version>.exe, pro Benutzer, ohne Adminrechte, eigenes Icon (PNG-basiertes ICO selbst gebaut). extraResources: die MITGELIEFERTEN Werkzeuge (HandBrakeCLI GPL-2, flac.exe MIT libFLAC.dll — die Ohne-DLL-Falle aus § 9) samt Lizenztexten und Quellverweisen (bau/lizenzen, Entscheid 9); vendor/ liegt nicht im Git (74 MB), Befuellung steht in BAUEN.md. Der Kern findet die mitgelieferten Werkzeuge ueber RIPPY_VENDOR (resourcesvendor) VOR allen anderen Suchwegen. haupt/update.ts (§ 6.8): electron-updater gegen das oeffentliche Gitea (festes Tag 'aktuell' als Rueckfallebene, Entscheid 6) — NUR https (http wird ABGELEHNT; Regel-Tests), NIE waehrend eines Rips (der Haupt kennt die aktiven Vorgaenge), Fehlschlaege still aber ablesbar (letzte erfolgreiche Pruefung im Status), installiert beim naechsten Start. haupt/autostart.ts: HKCU-Run ueber app.setLoginItemSettings, Toggle in den Einstellungen. Erst-Einrichtung (§ 6.9) im Programm: 5 Schritte, NUR ein Fehler blockiert (kein Laufwerk = Warnung, MakeMKV-Pflicht mit Klartext-Weg). GEBAUT UND GEMESSEN (30.08.2026): - dist-setup/RippySetup-5.0.0-w0.exe — 130,8 MB, SHA-256 07D39D6BAB74A47C21AA53D847FB5A5DB6990AE9FA1CBC7BE814B51050883115, plus .blockmap. - MESSUNG: Die Update-Auskunft heisst bei Vorab-Versionen nach dem KANAL (w0.yml) — erst ein Release ohne Suffix erzeugt latest.yml (steht jetzt in BAUEN.md). - Das GEPACKTE Programm (win-unpacked, asar + resources) besteht den Smoke: SMOKE OK, und der Screenshot zeigt die Erst-Einrichtung — das Bild eines frischen Rechners. Bewusst offen (SAVEPOINT): die automatische MakeMKV-Beschaffung (beschaffen.ts — Download+Installer-Start braucht einen Live-Test); das Setup NENNT den Weg klar. 185 Tests gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2d9cef22b3 |
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> |
||
|
|
c78ff22ab7 |
feat(v5): Etappe W-5 — Oberflaeche: Tray, Toasts, Taskleiste, Einstellungen, Bibliothek
Ampel / ampel (push) Successful in 1m23s
haupt/: tray.ts (Symbol mit Zustand, Kontextmenue, Fenster zu = weiter- laufen, Beenden nur noch gewollt), Windows-Toasts bei Disc erkannt / fertig / Fehler (Klick holt das Fenster), Taskleisten-Fortschritt (setProgressBar, Fehlermodus), echter Windows-Ordnerdialog per IPC. Eigenes Icon (bau/icon.png + tray.png, Disc-Optik). Kern: Einstellungen lesen/schreiben NUR ueber die eine Quelle (§ 6.7, Whitelist der Schluessel), Werkzeug-Auskunft (Katalog + gemessene HandBrake-Presets/Backends), Bibliothek in db.ts (Schema v2 — fingerprint, Titel, Groesse, Ort, Dauer), Duplikat-Erkennung beim Einlegen (§ 6.6: 'schon einmal gerippt am …' als disc-info + Toast), Pipeline traegt fertige Vorgaenge ein. Fenster: drei Bereiche (Uebersicht / Einstellungen / Bibliothek). Einstellungen: Ablage mit Durchsuchen-Dialog, Kompression je Disc-Typ (Preset-Liste VOM eigenen HandBrake gemessen, 'NICHT komprimieren' waehlbar), Sprachlisten, TMDb/OMDb-Keys, Medienserver, Werkzeug-Stand mit Klartext (gefunden/fehlt + Folgen). Bibliothek als Tabelle. Der Smoke-Screenshot zeigt nebenbei den Ernstfall in schoen: Das Laufwerk ist nach dem rc11-Vorfall inzwischen auch am Storage-Stack in 'unknown' gekippt — und die Kachel zeigt den ZUGRIFFS_GRUENDE-Klartext samt Abhilfe statt eines stummen Zustands. 165 Tests gruen, Smoke gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a9fa8fa2d8 |
fix(v5): discmerkmale auf Plattform-Pfade — dritte Lehre desselben Musters
Ampel / ampel (push) Successful in 1m17s
titelAusBdmt lief auf der Laufzeit-Wurzel (G:\ im Betrieb, tmp-Ordner im Linux-CI-Test) mit win32-join — der BDMV-Pfad wurde auf POSIX zu EINEM Namen mit Backslashes und der Test brach. Dieselbe Regel wie struktur/pipeline: Laufzeit-Wurzeln nehmen plattform-path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5f386e5406 |
feat(v5): Etappe W-4 — Metadaten-Automatik mit Sicherheitsgrad
Ampel / ampel (push) Failing after 1m20s
metadaten/: discmerkmale.ts (BDMT-Studio-Titel, ISO-9660- und UDF-Volume-Label direkt vom Medium — OHNE koffi, ueber fs auf \.\G: —, Label-Normalisierung, Fingerabdruck, progressive Titel-Kandidaten), tmdb.ts (v3-Key UND v4-Token, de-DE, /find fuer deutsche Texte zu OMDb-Treffern), omdb.ts, zuordnung.ts (die Confidence-Kaskade 0,95/0,90/0,80/0,60/0,30 aus prescan.py). ablage/poster.ts. Kern meldet 'disc-info' beim Einlegen; die Pipeline benennt den fertigen Ordner '<Titel> (Jahr)', schreibt movie.nfo/tvshow.nfo mit echten Metadaten und laedt das Poster. Fenster zeigt 'sehr wahrscheinlich: … · 95 %'. ZWEI Kaskaden-Schwaechen LIVE GEFUNDEN und gehaertet (gegen die echten APIs gemessen, Keys aus den Docker-Settings der VM, nicht persistiert): 1. 'Spartacus Gods Of The Arena' wurde zum FILM 'Spartacus' (1960) — der stark gekuerzte Kandidat traf woertlich, bevor die volle Anfrage als spezifische SERIE galt. Kaskade jetzt JE KANDIDAT woertlich→spezifisch (spezifisch nur fuer die volle Anfrage, auch fuer Serien). Nachgemessen: jetzt Serie, 90 %. 2. OMDbs t=-Suche machte aus dem Label 'Bd Evg D2' selbstbewusst 'BD Scream 5' — jetzt Wort-Jaccard-Gate >= 0,5. Nachgemessen: der Fall ist jetzt ein ehrlicher 60-%-Vorschlag, den das UI nachfragt. Messwerte: Akira→95% · Evangelion 2.22→80% (der dokumentierte Fund) · Spartacus GotA→Serie 90% · Bd Evg D2→Vorschlag 60%. Dazu ein neuer Quelltext-Hygiene-Waechter (keine rohen Steuerbytes — zweimal beim Schreiben passiert, einmal haette ein Test dadurch anders gemessen als gelesen). 165 Tests gruen, Smoke gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e840b7f327 |
fix(v5): Laufzeit-Pfade plattformneutral — pipeline/struktur auf node:path
Ampel / ampel (push) Successful in 1m21s
Der NFO-Ablage-Test brach in der Linux-CI: pipeline/struktur bauten mit path.win32 auf POSIX-Testordnern (mkdir legte 'x\Filme' als EINEN Namen an, readdir fand nichts). Regel jetzt dokumentiert: Module ueber LAUFZEIT-Wurzeln (Ablage) nutzen plattform-path — zur echten Laufzeit immer Windows, in der CI physisch korrekt auf POSIX; Module mit FESTER Windows-Semantik (katalog, dateiDaneben) bleiben bei path.win32. Testerwartungen entsprechend ueber join() statt Literale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3dc296f071 |
feat(v5): Etappe W-3 — Komprimieren + Ablegen, mit Vollbeweis
Ampel / ampel (push) Failing after 1m18s
komprimieren/: handbrake.ts traegt ALLE bezahlten Fallen aus dem Docker-Zweig — --aencoder statt --audio-codec (mit Anti-Regressions- Wächter im Test: der alte Test hatte den Fehler FESTGESCHRIEBEN), --format aus der Endung (Container kommt sonst aus dem Preset), Fortschritt NUR aus der Encoding-Zeile (Scan-99%-Falle), letzte 12 Zeilen fuer den Fehlerfall, unknown-option-Erkennung VOR allem anderen (kommt mit Rueckgabewert 0!), Datei-daneben-Suche, CR-Zeilentrennung mit 0x81-Kodierungsfalle. encoder.ts misst statt behauptet (--help, --preset-list, Familien statt Sammelbegriff). presets.ts: je Disc-Typ, PRESET_KEINE, Sprachlisten. ablage/: struktur.ts (sichere Namen, '<Titel> (Jahr)', Season NN, Episoden-Zuordnung NUR bei Eindeutigkeit), nfo.ts (Kodi-Schema), medienserver.ts (Jellyfin/Emby/Kodi-Refresh, wirft nie). ablauf/pipeline.ts ERSETZT rippen.ts: Rip → Kompression → Ablage als EINE Kette; Kompressions-Fehler laesst den verlustfreien Rip STEHEN und nennt den Ort; Lesefehler ueberleben bis in die Fertig-Meldung. Roh wird in dieser Etappe grundsaetzlich behalten (Loeschen erst mit W-5-UI). GEMESSEN am echten System (30.08.2026): - Encoder: 22 Stueck, Backends cpu-x264/x265/av1 + vce + vce-av1 (RX 9070 XT als VCE-Familie — nicht unter Intel-Namen), 107 Presets. - Probe-Encode 5 s Material: success in 45,5 s. - VOLLBEWEIS: Spartacus-Rohschnitt (17,71 GB — die Datei, die in rc11 am falschen Schalter starb) mit Preset 'H.265 VCN 1080p' in 2,1 min auf 1,17 GB komprimiert und nach Jellyfin-Schema abgelegt: E:\Rippy-v5-Beweis\Filme\Spartacus - Gods of the Arena - Disc 2 (2011)\ mit movie.nfo. - Nebenbefund: Der Evangelion-Roh-Rip vom 29.08. traegt 'This file was not properly finalized' im Kopf — 23,5 GB, die wie fertig aussehen und es nicht sind (der abgebrochene rc11-Rip). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ca338ad467 |
fix(v5): Windows-Pfadregeln ausdruecklich — path.win32 statt join
Ampel / ampel (push) Successful in 1m21s
Die Ampel (Linux) brach an den Katalog-Tests: das plattformabhaengige path.join mischte dort Schraegstriche in Windows-Pfade. Rippy v5 baut NUR Windows-Pfade — katalog.ts, handbrake.ts und ablauf/rippen.ts rechnen jetzt ausdruecklich mit path.win32 und damit auf jeder Plattform gleich. Im node:24-Container nachgestellt: vorher 2 failed, mit dem Fix lokal 96 gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
844bca37ff |
feat(v5): Etappe W-2 — Rippen: Parser, makemkvcon-Lauf, Werkzeug-Katalog
Ampel / ampel (push) Failing after 1m22s
rip/parser.ts: die reine Textverarbeitung mit allen bezahlten Fallen aus dem Docker-Zweig — quelle() (dev:G: statt dev:\.\G:, der 404-Verwandte), textVon() (UTF-8 streng, sonst cp1252 — nie wieder 'Öffnen'), parseMsg (Regex statt split — Meldungen mit Komma), PRGV (-1 vs. echtes 0 %), TINFO/SINFO (Sprache aus Attr 3/4, NICHT 28/29), laengsterTitel mit Play-All-Schutz, episodenTitel, KRITISCHE_CODES (sprachfeste NUMMERN). rip/makemkv.ts: Kommandobau + Lauf — ZeilenLeser (binaer, halbe Zeilen, je Zeile dekodiert), RipAuswertung (pur), rippen() mit AbortSignal, Lesefehler-Flag AUCH bei Rueckgabewert 0. werkzeuge/katalog.ts: die Suchreihenfolge samt %VAR%-Gross/Klein-Falle und Registry-Rueckfall. ablauf/rippen.ts: ein Rip je Laufwerk, Status-Push, 'erst nachsehen, dann rippen' (unbekannt heisst trotzdem rippen). Fenster: Rippen-Knopf, Fortschrittsbalken, Abbrechen; Auswurf waehrend des Rips abgelehnt. Gemessen am echten System (30.08.2026): Katalog findet makemkvcon64 (Program Files x86), HandBrakeCLI und flac (beide im rc11-Werkzeugordner LOCALAPPDATA\Rippy\tools). NEUER BEFUND: Nach dem abgebrochenen rc11-Rip vom Mittag sieht MakeMKV GAR KEIN Laufwerk mehr (MSG 5042, leere DRV-Liste, mit und ohne --noscan), waehrend die Storage-IOCTLs sauber antworten — MakeMKV spricht SCSI direkt. 5042 ist jetzt ein KRITISCHER_CODE mit Klartext-Abhilfe. Der volle Rip-Beweis (messung.rip.test.ts liegt bereit) braucht deshalb erst eine Hand am Geraet: Disc neu einlegen oder USB ab-/anstecken. 96 Tests gruen, Smoke gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a45069af5e |
feat(v5): Etappe W-1 — Laufwerk: Wache, Typ, Auswerfen mit Nachsehen
Ampel / ampel (push) Successful in 1m21s
kern/laufwerk in drei Schichten: codes.ts (Steuercodes HERGELEITET wie win_ioctl.py, gegen die Doku-Zahlen getestet), win32.ts (der einzige koffi-Ort im Kern, Muster aus beweise/laufwerk.js), disc.ts (Logik gegen die LaufwerkApi-Schnittstelle — classify mit den cdrom.py-Schwellen, Geraete-Info mit ready/empty/unknown + Klartext-Grund, Auswurf als entriegeln→auswerfen→NACHSEHEN mit injizierbarem Warten). Dazu wache.ts im 3-Sekunden-Takt (§ 5 Plan A): Uebergangslogik pur, gescheiterte Runde behaelt den Stand (R2) und meldet laut (R4). Fenster zeigt Laufwerke live (Modell, Typ, Groesse, Grund) mit Auswerfen-Knopf; Ereigniszeilen fuer Disc rein/raus und Fehler. Bündel-Falle gefunden und behoben: Ein woertliches require überlebt Rollup nicht — win32 wird per dynamischem import() als eigener Chunk gebaut; der Fehler war dank R4 im Fenster sichtbar statt still. Gemessen am echten BU40N (30.08.2026, RIPPY_MESSUNG=1): Laufwerk G: status=ready typ=bluray groesse=33759690752 modell=HL-DT-ST BD-RE BU40N 1.03 serial=0025114C0149 Der Auswurf-Beweis (messung.auswurf) folgt am Ende der Sitzung — der BU40N kann die Schublade nicht selbst einziehen. 52 Tests gruen (Steuercodes, classify, Auswurf-Ablauf, Geraete-Info, Wache, Smoke weiterhin gruen mit Laufwerks-Kachel im Bild). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6ed8035dcc |
fix(tests): Linux-only-Waechter pruefte die Welt vor d3e86d2
Ampel / ampel (push) Successful in 1m17s
Die Ampel war rot, und die Ursache war NICHT Rippy v5:
|
||
|
|
a172a3e2c0 |
fix(ci): setup-node raus — der Runner hat Node 24 nativ
Ampel / ampel (push) Failing after 1m20s
Lauf 221 brach nach 2:14 min ab; der einzige neue Schritt war actions/setup-node@v4. Nachgemessen am 30.08.2026: Das Runner-Image docker.gitea.com/runner-images:ubuntu-latest bringt Node v24.18.0 und npm 11.16 von Haus aus mit, und die kompletten Node-Schritte der Ampel (npm ci + build + test in rippy-windows) laufen in einem frischen node:24-Container auf der VM gruen durch. Statt Beschaffungs-Magie prueft die Ampel jetzt im Klartext, dass Node >= 22 da ist — zu alt heisst ROT mit Ansage, nicht kryptisch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0464933ada |
docs: SAVEPOINT — Rippy v5, Etappe W-0
Ampel / ampel (push) Failing after 2m14s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c539e65cd2 |
feat(v5): Etappe W-0 — das Geruest steht
Electron 44 + TypeScript, drei Prozesse (haupt / kern als utilityProcess / fenster mit React), Nachrichten-Schema mit Pruef-Funktionen, node:sqlite als einzige Datenbank-Stelle (kern/speicher/db.ts, § 6.7), Prozess-Leine im Haupt (Job Object aus beweise/leine.js — Begruendung fuer den Ort im Dateikopf), MessagePort direkt Fenster<->Kern, Einzelinstanz-Sperre, Kern-Neustart-Wache, strikte CSP im gebauten Fenster. Wächter-Tests nach § 4.3: koffi nur an zwei benannten Orten (R1), kein leerer catch und kein catch-mit-Leerwert (R2/R4), kein HTTP-Server (§ 4.1), node:sqlite nur in db.ts (§ 6.7). Dazu Datenbank- und Schema-Tests: 18/18 gruen, Typpruefung in drei Kontexten. Der W-0-Beweis (npm run smoke, echtes Programm): SMOKE: OK — fenster=geladen kern=pid:13676,node:24.18.1 datenbank=ok leine=gesetzt pong=ok version=5.0.0-w0 Nachgemessen ausserdem: taskkill /F auf den Haupt-Prozess (ohne /T, der Absturz-Fall aus rc10) — der Kern stirbt mit. Bau-Fallen dokumentiert (BAUEN.md): npm 11 blockt Electrons Install-Skript (§ 3.5), plugin-react 6 verlangt Vite 8 (deshalb 5.2), TypeScript bewusst auf 5.9.3 gepinnt statt tsgo 7. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
564bd17f84 |
ci: Ampel auf Node 24 verankert
Rippy v5 (rippy-windows/) braucht Node 24: Vite 7 und node:sqlite laufen nicht auf dem Zufalls-Node des Runners. docker/ui unter Node 24 lokal nachgemessen (npm ci + build gruen, 30.08.2026). Die Ampel prueft unveraendert — nur das Werkzeug ist jetzt festgelegt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a0cff205ab |
docs(konzept): Entscheid 4 — Worktree aufgeloest, Branch normal ausgecheckt
Der doppelte Checkout (Worktree + Hauptordner) blockierte den Start neuer Sitzungen auf dem Branch. Der Branch selbst und main bleiben unveraendert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c44bfa6daa |
docs(konzept): Zweite Fassung — geprueft, nachgemessen, drei Entscheide
- Faktenfehler korrigiert: Gitea ist oeffentlich per HTTPS erreichbar (gemessen, § 3.6) — Updates kommen direkt daraus, Drei-Wege-Tabelle weg - node:sqlite in Electron 44 im utilityProcess bewiesen (§ 3.4, beweise/sqlite-main.js + sqlite-kern.js) — better-sqlite3 nicht noetig - Disc-Wache umgedreht: 3-Sekunden-Takt ist Plan A, WM_DEVICECHANGE spaetere Verfeinerung (§ 5) - Entscheid 7 verschaerft: main-Aufraeumen sofort, nicht erst nach W-6 - Entscheid 9 neu: Zielgruppe Bekannte mit Link, Lizenztexte liegen bei - MakeMKV-Lage tagesaktuell bestaetigt (Key bis Ende September 2026, Kaufseite defekt = Dauerzustand seit Sommer 2025) - Electron-Pflege-Regel (§ 6.8), npm-11-Sperre trifft auch Electron selbst (§ 3.5), Release = drei Dateien, 4K-Schluesselkette in § 10, Risikotabelle aktualisiert Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e514e4070b |
docs(konzept): Entscheid 8 — der Remote-Worker bleibt bei Docker
Commander: „Das soll NICHT Teil des neuen Standalone-Produktes sein, da dieser NUR fuer die Docker-Variante verwendet wird." Damit ist bestaetigt, was in Paragraph 11.1 zuvor nur als Auslegung stand. Der Remote-Encode-Worker wird WEDER abgeraeumt (er ist Docker, und Docker bleibt unangetastet — Entscheid 2) NOCH nach v5 uebernommen (v5 ist Einzelplatz, Entscheid 3 — es gibt dort keinen zweiten Knoten, dem man etwas schicken koennte). Dazu die eigentliche Reparatur an diesem Abschnitt: Er war in Entwickler-Begriffen geschrieben (Modulnamen, PyInstaller, os.name-Zweige) und deshalb fuer den Adressaten nicht lesbar — zweimal nachgefragt, zweimal zu Recht. Paragraph 11.1 fuehrt die drei Teile jetzt erst in Klartext ein: A ist Rippy. B ist ein Handlanger fuer die VM. C sind Notizzettel, die wegen A eingeklebt wurden. Und macht den Unterschied A/C an dem fest, worauf es beim Aufraeumen ankommt — nicht am Inhalt, sondern am Ort: A sind ganze Ordner, die Docker nie aufschlaegt (wegwerfen). C sind einzelne Seiten in Ordnern, die Docker taeglich benutzt (einzeln durchgehen). Erst danach kommt die Tabelle mit den Dateinamen. Das ist kein Beiwerk: Genau diese Verwechslung wuerde beim Aufraeumen die laufende VM treffen. Und manche Windows-Zeile braucht sogar B weiter — etwa die Regel, dass eine Laufwerkswurzel ihren abschliessenden Trenner behaelt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fde3a78e97 |
docs(konzept): Entscheide 5-7 — kein Zertifikat, Gitea-Updates, Altlast weg
Die drei offenen Punkte aus KONZEPT-WINDOWS sind entschieden (30.08.2026).
Entscheid 5 — kein Code-Signing-Zertifikat. Die SmartScreen-Warnung wird
hingenommen und in der Anleitung erklaert. Die Folge gehoert dazu und steht
jetzt in 6.8: Ohne Signatur kann electron-updater ein Update nicht auf
Echtheit pruefen, also ist der Transportweg die einzige Absicherung —
Updates nur ueber HTTPS, eine http-Adresse wird abgelehnt.
Entscheid 6 — Updates aus Gitea, auch fuer Externe erreichbar. Rippys
Seite ist einfach: die Adresse steht in der Konfiguration, electron-updater
braucht nur einen statischen HTTPS-Ort. Die Netz-Seite ist es nicht —
Gitea liegt auf 192.168.178.153 im Heimnetz und ist von aussen nicht
erreichbar. Drei Wege sind aufgeschrieben und bewertet (Gitea
veroeffentlichen / oeffentlicher Spiegel / eigene Quelle je Nutzer),
Empfehlung ist der Spiegel. Rippy wird fuer alle drei gebaut, die
Entscheidung kann spaeter fallen ohne Programmaenderung.
Entscheid 7 — der alte Windows-Weg wird entfernt, auch aus main. Dafuer
gibt es einen neuen Abschnitt 11 mit der Arbeitsliste, und die zerfaellt
in DREI Teile statt einem:
A die Standalone-App (PyInstaller, Tray, Fenster, Win32-Treiber,
lokale Queue, Werkzeug-Beschaffung) -> weg
B der Remote-Encode-Worker fuer die Docker-Installation -> BLEIBT
C die verstreuten os.name=="nt"-Zweige im Docker-Code -> einzeln pruefen
Dass B bleibt, ist ausdruecklich als Auslegung markiert, nicht als
Anweisung: Der Remote-Worker ist eine Funktion der Docker-Installation,
und Entscheid 2 haelt die unangetastet. Ihn mit abzuraeumen wuerde der VM
eine Faehigkeit nehmen, die niemand gekuendigt hat.
Teil C ist der heikle: pfade.py bleibt (der Remote-Worker braucht
Laufwerkswurzeln und UNC), nativ_nachsehen() ist zu pruefen, und die
Faehigkeiten-Auskunft /betrieb bleibt sinnvoll — nur ihre Windows-Werte
fallen weg.
Zeitpunkt: erst NACH v5 W-6. Solange v5 nicht installierbar ist, ist die
alte Fassung das einzige Windows-Rippy — sie vorher zu loeschen waere eine
Luecke ohne Gegenwert. Der Entscheid nennt die Richtung, kein Datum.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
cb811bec28 |
docs: KONZEPT-WINDOWS — Rippy v5 als eigenstaendiges Electron-Programm
Die heutige Windows-Fassung ist der Docker-Code in einer EXE: PyInstaller
buendelt docker/api und docker/worker, uvicorn laeuft auf 127.0.0.1:7788,
pywebview stellt ein Fenster davor. Elf Release-Kandidaten in drei Tagen,
und die Funde sind fast alle derselbe Typ — Linux-Annahmen, die unter
Windows etwas anderes bedeuten (timeout ls -d, shutil.which, /app,
/dev/{name}, F: ohne Trenner).
Das Konzept beschreibt den Neubau als Windows-Programm: Electron 44,
TypeScript durchgehend, Einzelplatz, kein Python, kein HTTP-Server.
Vier Entscheide des Commanders (30.08.2026) sind eingearbeitet:
alles TypeScript/Node · Docker bleibt unangetastet · reiner Einzelplatz ·
eigener Branch.
Vor der ersten Zeile Konzept gemessen (Regel D), Belege in beweise/:
* Win32 aus Node an G: (HL-DT-ST BD-RE BU40N) — Laufwerkssuche,
CreateFileW, QUERY_PROPERTY (Modell + Serial), CHECK_VERIFY2,
GET_LENGTH_INFO (33,76 GB -> Blu-ray). IOCTL_CDROM_DISK_TYPE
antwortet mit Fehler 50 — derselbe Befund wie im Python-Treiber.
* Prozess-Leine (Job Object): Kind stirbt mit dem per taskkill /F
ohne /T abgeschossenen Elternprozess. Die Messung aus rc10, in
Node nachgestellt.
* node:sqlite ist in Node 24 eingebaut — in Electron noch ungeprueft,
ausdruecklich als offener Punkt markiert.
Akutestes Risiko im Dokument: MakeMKVs freier Beta-Key laeuft Ende
September 2026 ab, die Kaufseite fuer die Dauerlizenz ist seit Mai defekt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
9050fea3f0 |
docs: SAVEPOINT v4.0-rc11
Der Schalter, den es nicht gibt — und vierzehn weitere Funde aus demselben Rundgang. Jeder mit der Messung, an der er haengt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d3e86d2641 |
fix(windows): Der Schalter, den es nicht gibt, und vierzehn weitere Funde
Commander: „Kompression fehlgeschlagen bei Spartacus … _t01.mkv: HandBrake
endete mit Code 0" — 16,5 GB fertiger Rohschnitt, und die Kompression war in
derselben Sekunde vorbei, in der sie begann.
Nachgestellt mit genau der Befehlszeile, die Rippy baute:
unknown option (--audio-codec)
HandBrake has exited. $? = 0
Den Schalter `--audio-codec` gibt es bei HandBrakeCLI nicht; er heisst
`-E` / `--aencoder`. Ein unbekannter Schalter ist fuer HandBrake kein Fehler,
der Rueckgabewert ist 0. Der Test dazu forderte den falschen Namen sogar ein.
Daraus wurde ein Rundgang durch den Windows-Pfad. Alles unten ist gemessen,
nichts vermutet (Regel D).
## Die Kompression
1. `--aencoder` statt `--audio-codec`. Am mitgelieferten HandBrakeCLI 1.11.2
gemessen, mit einem 5-Sekunden-Encode auf der echten Roh-Datei bestaetigt.
2. HandBrakes letzte Zeilen werden aufgehoben (12 gepuffert, 4 in der
Meldung) und `unknown option (...)` wird als eigener Fall erkannt, VOR
allen anderen. Vorher wurde jede Zeile weggeworfen, die kein Fortschritt
war — bei Rueckgabewert 0 blieb damit keine Auskunft uebrig. Die geratene
Zeile „Meist ist der Zielordner nicht beschreibbar" ist raus; sie war
falsch und hat die Suche in die falsche Richtung geschickt.
3. Ein `ue`-Umlaut im Pfad toetete die Kompression. HandBrake schreibt zwei
Kodierungen in denselben Strom (derselbe Pfad einmal UTF-8, einmal CP850).
In CP850 ist das Byte 0x81, und das ist in cp1252 — was `text=True` auf
deutschem Windows waehlt — undefiniert:
UnicodeDecodeError: charmap codec can't decode byte 0x81
Neu: `rip/handbrake_aufruf.py` mit `HB_LESEN`, benutzt von ripping.py und
caps.py. Bewusst nicht binaer wie bei makemkvcon: HandBrake trennt
Fortschrittszeilen mit CR, im Binaermodus waere der Balken weg.
## Die Rohdaten
4. `rohdaten.kandidaten` machte aus dem Arbeitsordner `F:\` ein `F:` und
verband damit weiter. Das ist unter Windows der aktuelle Ordner auf
Laufwerk F, nicht dessen Wurzel — 16,5 GB waren unsichtbar, und der
Wiederholen-Dialog bot nur „Neu rippen" an. Die Falle steht woertlich im
Kopf von `pfade.verbinden`.
5. Gesucht wurde unter der heutigen Einstellung statt unter der Wahl DIESES
Rips (`meta["work_dir"]`). Genau dafuer wurde rohdaten.py am 26.07.
gebaut; repariert wurde damals die Kandidatenliste, nicht der Aufrufer.
Neu: `_arbeitsverzeichnis_des_jobs`, benutzt an vier Stellen.
6. Zwei Speicher fuer dieselben Ordner: Die Oberflaeche schreibt
`outputDir`/`workDir` in die Datenbank, `betrieb` liest `storage.*` aus
der Konfigurationsdatei, und die schreibt niemand. Gemessen: eingestellt
`E:\Rippy`, angezeigt `C:\Users\...\Videos\Rippy`. Neu:
`betrieb.mit_einstellungen`.
## Das Laufwerk
7. `device_info` fing den OSError ab und lieferte „unknown" ohne den Grund.
Nach einem Rip mit Lesefehlern beantwortete das Laufwerk keine
Medien-Abfragen mehr (Win32-Fehler 1), die Geraete-Auskunft aber schon —
im UI stand eine volle Laufwerkskarte, kein Rip startbar, und im
Protokoll das laengst veraltete „Disc erkannt". Neu: `ZUGRIFFS_GRUENDE`,
ein Feld `grund` im Laufwerks-Eintrag und eine Protokollzeile je Wechsel.
Eine fehlgeschlagene Disc-Erkennung wird ebenfalls protokolliert.
8. Der Linux-Treiber nannte ein unzugaengliches Laufwerk „empty", waehrend
Windows richtig „unknown" sagt. Angeglichen, samt Feld-Paritaet.
9. `CreateFileW`, `DeviceIoControl` und `CloseHandle` hatten weder `restype`
noch `argtypes` — 32-Bit-`c_int` fuer einen 64-Bit-HANDLE, in beide
Richtungen. Mit `restype` aendert sich der Fehlerwert von -1 auf
0xFFFFFFFFFFFFFFFF; die Pruefung deckt jetzt beides ab. Am echten
Laufwerk gegengeprueft, Fehlerpfad eingeschlossen.
10. Der Vor-Scan lief bei JEDER eingelegten Disc ein `makemkvcon info` mit
120 s Zeitgrenze — 20 bis 120 Sekunden „Disc wird gelesen". Frueher war
das schnell, weil der Zweig unter Windows nie lief (`shutil.which`,
repariert am 28.08.). Das Ergebnis landete allein in `toc["tracks"]`,
das niemand liest: Der Rip-Dialog holt seine Liste ueber
`/devices/{id}/scan-tracks`, wenn sie gebraucht wird. Entfernt.
## Notbremsen
11. `_frei_bytes` suchte den naechsten vorhandenen Ordner selbst.
`os.path.dirname("Q:\\")` gibt sich selbst zurueck — ein
Arbeitsverzeichnis auf einer abgezogenen Platte haette den Job vor dem
Rip stumm haengen lassen. Benutzt jetzt
`pfade.naechster_vorhandener`, das den Abbruch seit V2-1 hat.
12. `naechster_vorhandener` haelt Laufwerks- und UNC-Wurzeln jetzt absolut.
13. `aufraeum_skript` baut sein `rmdir /s /q` aus `InstallLocation` in der
Registry. Waere das eine Laufwerks-Wurzel, loeschte die Deinstallation
das Laufwerk. Nicht beobachtet, aber nicht wiedergutzumachen — der
Loeschbefehl bleibt in dem Fall weg.
## Lesefehler
MakeMKV sicherte 1 von 2 Titeln, endete mit 0, und Rippy schrieb „Rip
fertig". Jetzt gibt es eine Warnung, auch wenn der Rip als Erfolg endet, und
die MSG-Nummer steht im Protokoll: MakeMKVs Texte sind uebersetzt, die
Nummern nicht.
## Aus der Gegenprobe am laufenden Rippy
Die erste Fassung dieses Standes war installiert, als der Commander meldete:
„nun oeffnen sich diverse fenster im hintergrund, gehen ganz kurz auf und dann
wieder zu. Das laufwerk hoert auch einfach auf zu lesen." Beides Altlasten,
die erst durch die neue Protokollzeile sichtbar wurden.
14. Prozesserzeugung mitgeschnitten:
14:54:40 timeout.exe timeout 4 ls -d C:\Users\...\d7ee6c06-...
14:54:40 WindowsTerminal.exe
`rohdaten.pruefen` fragt mit `timeout N ls -d`, ob es ein Verzeichnis
gibt. Unter Linux ist das richtig (os.path.isdir kann an einem toten
CIFS-Mount im Kernel haengen, ein Kindprozess laesst sich abbrechen).
Unter Windows ist es dreifach falsch: timeout.exe gibt es dort, kennt
aber weder `ls` noch `-d`; sie braucht eine Konsole, und die reisst
Windows auf; und ihr Rueckgabewert ist nie 0, die Antwort lautete also
„weg" fuer JEDES Verzeichnis. Rohdaten waren unter Windows
grundsaetzlich unsichtbar. Neu: `nativ_nachsehen()`. Die zwei
gleichartigen Aufrufe in mounts.py bekommen dieselbe Absicherung.
15. Der Waechter fragte das Laufwerk alle drei Sekunden ab — auch mitten im
Rip, also drei CreateFileW plus IOCTLs auf ein Geraet, das makemkvcon
gerade liest:
12:49:52 bluray-Rip gestartet
12:50:09 [watcher] Laufwerk G: beantwortet keine Medien-Abfragen
12:50:12 MSG 2003 SCSI-Fehler ILLEGAL REQUEST:INVALID FIELD IN CDB
12:50:12 makemkvcon endete mit Code 11
`_auto_prescan` haelt sich seit dem 29.08.2026 an die Regel „waehrend
eines Rips wird nicht gescannt"; die Laufwerksabfrage tat es nicht.
Jetzt gilt in der Zeit der letzte bekannte Stand.
## Zwei Tests, die gelogen haben
* `assert "--audio-codec" in cmd` schrieb den Fehler fest.
* `lambda: {}` als Doppelgaenger fuer `get_settings(key, bei_fehler_leer)`
brach, sobald ein Aufrufer einen Parameter benutzte — und zeigte dann auf
den Code statt auf sich selbst.
977 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b6a93aa726 |
docs: SAVEPOINT v4.0-rc10
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fed24237c9 |
fix(windows): makemkvcon ueberlebte Rippy und hielt das Laufwerk fest
Commander: „jetzt hast du den makemkvcon kram dir nicht angeguckt".
Stimmt — es stand als Frage im Notizbuch statt als Riegel im Code.
## Gemessen, nicht vermutet
Ein Elternprozess startete makemkvcon, dann wurde er hart beendet
(`taskkill /F` OHNE `/T` — genau das, was beim Dienst-Stopp und beim
Drueber-Installieren passiert):
ohne Leine makemkvcon PID 15368 vor dem Kill: True danach: True
mit Leine makemkvcon PID 43608 vor dem Kill: True danach: False
Windows raeumt Kindprozesse nicht auf. Eine solche Waise HAELT DAS LAUFWERK —
jeder spaetere Rip scheitert dann mit „Das Öffnen der Disk schlug fehl". Zwei
davon standen waehrend der Messungen auf diesem Rechner.
## Zwei Riegel
* **Die Leine** (`winlauf.kinder_an_die_leine`) — eine Arbeitsgruppe (Job
Object) mit KILL_ON_JOB_CLOSE. Stirbt Rippy, sterben makemkvcon, HandBrake
und flac mit. Auch beim Absturz, auch per Taskmanager. Einmal beim
Dienststart gesetzt, deckt sie JEDEN Werkzeugaufruf ab.
* **Der Aufraeumer** (`winlauf.waisen_beenden`) — beendet beim Start
Werkzeug-Prozesse, deren Elternprozess es nicht mehr gibt. Fuer das, was
eine aeltere Fassung oder ein Absturz hinterlassen hat. Nur ELTERNLOSE:
Ein makemkvcon eines laufenden Rippy bleibt unangetastet, `makemkv.exe`
(die Oberflaeche) steht gar nicht erst auf der Liste.
Am echten Fall nachgestellt: Waise erzeugt, Rippy gestartet, Waise weg.
## Drei Prozesse duerfen NICHT mitsterben
Dienst, Fensterprogramm und das Aufraeum-Skript der Deinstallation loesen sich
ueber `eigenstaendig_starten` heraus (CREATE_BREAKAWAY_FROM_JOB). Mit
Rueckfall ohne die Fahne: Steckt Rippy in einer fremden Arbeitsgruppe ohne
Herausloese-Erlaubnis, verweigert Windows den Start rundweg — ein Fenster, das
gar nicht mehr aufgeht, waere schlimmer als eines, das mitstirbt.
## Die Falle beim Bauen
Der erste Anlauf meldete nur „ging nicht". Ursache: Ohne `argtypes` reicht
ctypes einen Griff als 32-Bit-int weiter. `GetCurrentProcess()` liefert aber
(HANDLE)-1 = 0xFFFFFFFFFFFFFFFF, ctypes wirft `ArgumentError: int too long to
convert`, und das breite `except` verschluckte es. `kernel32()` meldet jetzt
jede Signatur an; ein Test wacht darueber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
21c626eb4e |
docs: SAVEPOINT v4.0-rc9
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f0f719ca12 |
fix(windows): Zweiter Rip startete waehrend der Kompression ins leere Laufwerk
Der Commander meldete einen Rip-Fehlschlag „bei der Komprimierung", obwohl
der Rip laengst durch war:
makemkvcon endete mit Code 11 — letzte Meldung:
Das Öffnen der Disk schlug fehl — keine MKV-Datei entstanden
Auffaellig war, was FEHLTE: kein „Ursache:". Waere der Geraetepfad schuld
gewesen, stuende dort MSG 2024. Bleibt: kein Datentraeger im Laufwerk.
Der Ablauf dahinter:
1. Rip fertig → Rippy wirft die Disc aus (Standardeinstellung)
2. Status wird auf `transcoding` gesetzt — ab hier sagte `has_active_job`
NEIN, das Laufwerk sei frei; es kennt nur pending/running
3. Die Disc-Wache sieht beim Auswurf einen Statuswechsel → „eingelegt"
4. Die Vollautomatik startet einen ZWEITEN Rip — auf ein Laufwerk, dessen
Schublade gerade herausfaehrt
Der zweite Rip lief in ein leeres Laufwerk. Auf dem Bildschirm sah das aus,
als sei die Kompression gescheitert — sie lief ungestoert weiter.
Drei Aenderungen:
* `store.job_offen` — der weitere Riegel (pending/running/transcoding/
canceling) fuer die Vollautomatik. `has_active_job` bleibt unveraendert:
Das fragt „haelt gerade jemand das Laufwerk?", und waehrend der Kompression
tut das niemand — ein Auswurf von Hand bleibt erlaubt.
* `ripping.disc_fehlt` — vor dem makemkvcon-Start nachsehen, ob ueberhaupt
eine Disc drin liegt. Statt zwei Minuten Warten und Code 11 gibt es einen
Satz, den man versteht. Ein FEHLGESCHLAGENER Blick verweigert nichts:
„ich weiss es nicht" darf nie zu „es geht nicht" werden.
* MSG 5010 in KRITISCHE_CODES — MakeMKVs Sammelmeldung sagt fuer sich nichts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f6f0a4ccb6 |
feat(windows): MusicBrainz-Tags fuer Audio-CDs
Ampel / ampel (push) Successful in 1m25s
Commander: „go"
Die Dateien hiessen `Track 01.flac`. Eine Musiksammlung ohne Titel ist eine
Sammlung von Nummern.
## Wie MusicBrainz eine CD wiedererkennt
Nicht am Namen (den kennt die CD nicht) und nicht an einer Kennung auf der
Disc (die gibt es nicht), sondern an der LAGE ihrer Spuren. Daraus wird ein
Fingerabdruck gerechnet — SHA-1 ueber die Versaetze, base64 mit eigenem
Alphabet (musicbrainz.org/doc/Disc_ID_Calculation).
## Belegt OHNE Audio-CD
MusicBrainz gibt zu jeder bekannten Kennung die Spurlage heraus, aus der sie
gerechnet wurde. Wer die Lage zurueckbekommt und daraus dieselbe Kennung
rechnet, hat die Rechnung belegt:
Spurlage „Back in Black" (abgefragt) -> 3KVSIWn_ewv9z0cCizmOJzDWtsQ-
MusicBrainz sagt dazu -> 3KVSIWn_ewv9z0cCizmOJzDWtsQ-
Und die ganze Kette am Stueck, mit echtem Encoder:
Ordner ACDC - Back in Black
Dateien 01 - Hells Bells.flac, 02 - Shoot to Thrill.flac, …
Tags TITLE/ARTIST/ALBUM/ALBUMARTIST/DATE/TRACKNUMBER
— mit metaflac aus der FERTIGEN Datei zurueckgelesen
## Zwei Fallen
**Der Vorlauf.** Die Kennung rechnet MIT den 150 Frames, das Lesen OHNE. Wer
das verwechselt, bekommt eine Kennung, die niemand kennt — und zwar ohne
Fehlermeldung, denn die Antwort ist dann schlicht „unbekannte Disc".
**`AC/DC`.** Als Ordnername haette der Schraegstrich unter Windows einen
Unterordner aufgemacht. Aufgefallen NUR, weil der Beweis den Namen ausgegeben
hat. Jetzt saeubert `sauberer_name` beides — Datei und Ordner.
## ⚠️ Mein Fehler dabei
Die Spurlage im Test hatte ich zuerst ERFUNDEN: Beim Beweis hatte ich nur
Spurzahl und Lead-Out ausgegeben und den Rest „passend" ergaenzt. Der Test
wurde prompt rot — zu Recht. Die Zahlen stehen jetzt so drin, wie MusicBrainz
sie herausgibt (erste Spur bei 182, nicht bei 150: diese Pressung hat einen
laengeren Vorlauf).
Ohne Treffer bleibt es bei `Track 01.flac`. Eine unbekannte Disc ist kein
Fehler, und ein Netzausfall darf keinen Rip umwerfen.
932 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
986fcf19b1 |
feat(windows): Audio-CDs rippen — die letzte Luecke ist zu
Ampel / ampel (push) Successful in 1m24s
Commander: „mach es"
Gemeint war die Lücke, die seit Wochen so im SAVEPOINT stand: **Audio-CDs
laufen unter Windows nicht.** `cdparanoia` und `abcde` sind Linux-Werkzeuge.
## Nicht abcde nachbauen — den Windows-Weg gehen
Eine Audio-CD hat kein Dateisystem. Die `Track01.cda`, die Windows zeigt,
sind 44 Byte grosse Platzhalter; die Musik liegt roh in 2352-Byte-Sektoren.
Windows bietet dafuer zwei Steuercodes an:
IOCTL_CDROM_READ_TOC Inhaltsverzeichnis (MSF je Spur, Audio/Daten)
IOCTL_CDROM_RAW_READ die Sektoren selbst
Gegengeprueft, dass die berechneten Codes den dokumentierten entsprechen:
0x24000 und 0x2403e.
Kodiert wird mit dem FLAC-Encoder vom offiziellen Xiph-Spiegel (1.5.0), den
Rippy beim Einrichten holt — wie MakeMKV. KEIN Pflichtwerkzeug: Ohne ihn
laeuft alles ausser Audio-CDs, und ein Fehlschlag darf das Einrichten nicht
truebe machen.
## Zwei Fallen, beide gemessen
**Die Leseadresse zaehlt in 2048er-Einheiten**, obwohl ein Audio-Sektor 2352
Bytes hat. Das ist dokumentiert und sieht falsch aus; mit 2352 liest man an
der falschen Stelle.
**`flac.exe` braucht `libFLAC.dll` daneben.** Mit nur der exe endete jeder
Aufruf mit 0xC0000135 — „DLL nicht gefunden" — und zwar ohne eine einzige
Zeile Ausgabe. Jetzt wird der ganze Win64-Ordner ausgepackt.
## Was geprueft ist
Ein Laufwerk und eine Audio-CD lassen sich in der Ampel nicht herstellen.
Deshalb steht alles Rechenbare in reinen Funktionen — MSF↔LBA, das Zerlegen
der TOC-Bytes, WAV-Kopf, Blockaufteilung, Dateinamen — und der Ablauf
bekommt Laufwerk und Encoder eingespritzt. 28 Tests dafuer.
Zusaetzlich mit dem ECHTEN Encoder gemessen: zwei Spuren erzeugten Tons
gerippt, und **FLAC bestaetigt seine eigenen Dateien** (`flac -t`, Code 0).
Am echten Laufwerk gegengeprueft, dass das Inhaltsverzeichnis gelesen wird —
die eingelegte Blu-ray meldet sich korrekt als DATEN-Track und wird nicht als
Musik behandelt.
Was noch fehlt: MusicBrainz-Tags. Die Dateien heissen `Track 01.flac`. Der
Weg dafuer steht (`tags_je_spur`), die Disc-Kennung fuer die Abfrage nicht.
915 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
1b84ec2a45 |
fix(windows): Vollstaendiger Rundgang durch die Docker-Reste
Ampel / ampel (push) Successful in 1m20s
Commander: „Bro, du musst alles was rippy jetzt im code hat für Windows
Bauen! Jeden pfad, alles wo die tools drauf zugreifen. Diese Rippy version
MUSS 100% Windows Kompatibel sein. Prüfe bitte den kompletten Quellcode nach
Docker Resten."
Systematisch gesucht statt Fundstelle fuer Fundstelle: feste POSIX-Pfade,
Linux-Programme, POSIX-eigene Aufrufe, `shutil.which`, `posixpath` auf echten
Pfaden, Container-Texte. Sechs echte Fehler dabei.
## 1. `/dev/{name}` in drei Endpunkten — der schwerste
Das UI ruft `/devices/{id}/eject`, `/scan-tracks` und `/tracks` mit der
Kennung aus der Geraeteliste auf, unter Windows also `G`. Gebaut wurde daraus
`/dev/G` — steht in keiner Laufwerksliste. **Auswerfen und „Disc scannen"
antworteten unter Windows IMMER mit 404**, ohne dass irgendwo stand, warum.
Hin- und Rueckweg gehoeren zusammen: Beide Treiber haben jetzt `kennung()`
und `pfad_zu_kennung()`. Wer die Kennung vergibt, loest sie auch auf.
## 2. `os.path.isdir("/app")` — zum zweiten Mal
Nach `caps.py` (heute frueh) auch in `ablauf.py`: Der eigenstaendige
Windows-Rippy hielt sich fuer einen FREMDEN Worker und haette sich selbst
vorgeworfen, Container-Pfade nicht zu erreichen — auf einer Maschine ohne
Container. Die Entscheidung ist jetzt einspritzbar; vorher hing der Test
daran, ob es einen Ordner `/app` gibt.
## 3. `shutil.which` in `schluessel.py`
Ausgerechnet im Modul, das es NUR unter Windows gibt: Es suchte makemkvcon im
PATH, wo unter Windows nie ein Programm aus „Programme" steht. Die
Schluessel-Automatik fuer 4K-UHD lief damit nie an.
## 4. `posixpath.join` auf echten Pfaden
`rohdaten.py` baute `C:\Roh/datei.mkv` — gemischte Trenner, die im UI falsch
aussehen und jeden Vergleich brechen.
## 5. Container-Pfad in einer Nutzermeldung
„Roh-Datei bleibt in /app/temp erhalten" nennt jetzt den echten Ordner. Wer
die Datei retten will, sucht sonst am falschen Ort.
## 6. Container-Pfade als UI-Vorbelegung
Rip-Dialog und `useBetrieb` starteten mit `/app/media`, bis die Antwort da
war. Leer ist ehrlicher: Es behauptet nichts.
## Und HandBrakes „Code 0"
Code 0 heisst ERFOLG. Rippy meldete trotzdem „fehlgeschlagen", weil die Datei
nicht am erwarteten Ort lag: **HandBrake bestimmt den Container aus dem
PRESET, nicht aus der Endung** — ein MP4-Preset schreibt `.mp4` neben das
verlangte `.mkv`. Jetzt erzwingt `--format` den Container passend zur Endung
(an HandBrake 1.11.2 gegengeprueft), und falls doch etwas daneben liegt, wird
es gefunden statt weggeworfen.
## Der Waechter
`test_keine_container_reste.py` prueft mechanisch, dass im Windows-Weg kein
Container-Pfad ohne Begruendung steht. Die Ausnahmen stehen namentlich mit
Grund da (Linux-Zweige, benannte Rueckfaelle) — und ein zweiter Test wirft
jede Ausnahme raus, die niemand mehr braucht.
Ueber den Tokenizer, nicht ueber „faengt mit Anfuehrungszeichen an": Der
erste Anlauf blieb prompt an seinem eigenen `r\"\"\"`-Docstring haengen.
887 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
1a529f4e75 |
fix(windows): „Disc wird gelesen" blieb waehrend des ganzen Rips stehen
Ampel / ampel (push) Successful in 1m13s
Commander: „der ‚Disk wird gelesen' panel braucht eeeeewig. Der Rip ist bereits bei 30% und er liest immernoch." Genau so war es — und die Erkennung kam auch nie zum Ende. ## Warum `_auto_prescan` hatte GENAU EINE Absicherung: nicht zweimal gleichzeitig (`_laeuft`). Ob auf dem Laufwerk gerade ein Rip laeuft, hat es nie gefragt. Waehrend eines Rips haelt `makemkvcon` das Laufwerk. Ein zweites `makemkvcon info` daneben wartet, bis es seine Zeitgrenze erreicht (gemessen: eine Laufwerks-Abfrage braucht dann 14 s statt 5, der `info`-Aufruf laeuft in seine 120 s). Solange steht die Marke `_laeuft` — und damit das Panel, das ich heute frueh genau dafuer gebaut habe. Es gibt keinen Grund, waehrend eines Rips zu scannen: Das Laufwerk ist belegt, und **welche Disc drin ist, wissen wir bereits** — der Job laeuft ja auf ihr. ## Zwei Stellen, weil es zwei Wege hinein gibt 1. `_auto_prescan` bricht ab, wenn auf dem Geraet ein Job laeuft. Das verhindert jeden Scan, der NACH dem Rip-Start angestossen wird. 2. Der Job-Start raeumt eine haengende Marke weg. Ein Scan, der KURZ VORHER begann, haelt sie sonst bis zu seinem Ende fest. Verloren geht dabei nichts: Unter `_laeuft` steht nur der Platzhalter, und der laufende Scan traegt sein Ergebnis spaeter selbst nach. 880 Tests gruen, ruff sauber. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
548742e771 |
fix(windows): Restzeit blieb ewig „wird gemessen" — zwei Docker-Annahmen
Ampel / ampel (push) Successful in 1m32s
Commander: „Über den kompletten vorgang steht dort ‚Restzeit wird gemessen'
aber messung wird nicht abgeschlossen. Das heißt man hat kein ETA"
Zwei Ursachen, beide unabhaengig, beide toedlich fuer sich allein.
## 1. Die Messreihe lag nur in Redis
`eta.py` schrieb sie ausschliesslich in den Cache — mit der Begruendung im
Modul-Kopf: „Der Cache (Redis) ist schon da". Im Container stimmt das. Auf
einem Windows-PC gibt es kein Redis: `cache_get` gab bei JEDEM Aufruf None
zurueck, `beobachtung_hinzufuegen` legte also jedes Mal eine frische Reihe mit
EINEM Punkt an — und `restzeit_sekunden` braucht `MINDEST_PUNKTE = 2`.
Jetzt wird in beide Ablagen geschrieben: in den Cache, wo es einen gibt (er
ueberlebt einen API-Neustart), und in ein Woerterbuch im Prozess. Das ist ein
paar Zahlen gross, gilt nur fuer die Dauer eines Jobs, und im eigenstaendigen
Betrieb gibt es ohnehin nur diesen einen Prozess. Alte Reihen werden nach
einem Tag weggeraeumt.
## 2. Der Ereignisstrom rechnete die Restzeit gar nicht
Die Rechnung stand nur in `/jobs`. Der SSE-Schnappschuss baute seine Jobs mit
dem nackten `_job_row_to_model` — also ohne Restzeit. **Seit der Umstellung
auf den Ereignisstrom (V2-3) liest die Oberflaeche aber genau diesen
Schnappschuss und nicht mehr `/jobs`.** Die Restzeit wurde also brav berechnet
und niemandem gezeigt.
Beides jetzt in `jobs_fuer_ui()` — dieselbe Lehre wie bei
`laufwerke_mit_disc` heute frueh: Eine Auskunft in zwei Fassungen ist eine
Fassung zu viel.
Nebenbei: Unlesbare Einstellungen duerfen die Jobliste nicht umwerfen. Seit
sie auch den Schnappschuss baut, haengt daran die ganze Oberflaeche — zwei
Snapshot-Tests wurden davon prompt rot.
## Beweis
Job in der Datenbank, Fortschritt 10 % -> 25 % ueber 130 s:
nach 1. Messpunkt : eta_text='' (richtig, eine Messung reicht nicht)
nach 2. Messpunkt : 674 s, „noch ca. 11 min"
877 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
aeb3128a85 |
fix(windows): Dauerscan der Disc, aufblitzende Fenster, Docker-Einstellung uebernommen
Ampel / ampel (push) Successful in 1m30s
Commander: „liest er die disc NOCHMAL ein das macht aber keinen sinn, wenn er
sie bereits erkannt hat. Außerdem öffnen sich nun immer irgendwelche fenster
ganz kurz im hintergrund."
## Beides war dieselbe Schleife
In der Disc-Wache stand:
except OSError:
continue
Damit blieb `bekannt[pfad]` ungesetzt, und im naechsten Durchlauf war
`vorher is None` — also wieder „Erststart, liegt schon eine Disc drin". **Ein
einziger fehlgeschlagener Lesevorgang loeste einen neuen Vor-Scan aus.**
Und der scheitert regelmaessig: Waehrend der Vor-Scan laeuft, haelt
makemkvcon das Laufwerk (gemessen: /devices braucht dann 14 s statt 5). Also
Scan haelt das Laufwerk -> Statusabfrage scheitert -> naechster Durchlauf
haelt es fuer den ersten -> neuer Scan. Eine Schleife, die sich selbst am
Leben haelt.
Und weil derselbe makemkvcon-Aufruf in `prescan.py` als EINZIGER kein
`creationflags=OHNE_FENSTER` hatte, blitzte bei jeder Runde eine Konsole auf.
Das war meine Zeile von heute Vormittag.
Die Entscheidung steckt jetzt in `disc_entscheidung()` — pure Funktion, also
ohne Laufwerk pruefbar. Zwei getrennte Fragen statt einer: Haben wir dieses
Laufwerk je gelesen, und was war zuletzt drin? Ein Fehlschlag beantwortet die
zweite nicht und darf die erste nicht zuruecksetzen.
Nachgemessen: Ein Tabwechsel loest KEINEN neuen Scan aus (8 Erkennungen
vorher, 8 nachher). Meine erste Zaehlung von „7 vs 8" war ein Messfehler —
zwei verschieden grosse Abfragefenster.
## Aus dem Docker-Betrieb uebernommen
`docker/worker/entrypoint.sh` setzt vor jedem Worker-Start zwei Dinge:
`app_Key` und `app_UpdateEnable`. Der Key war schon uebernommen, die zweite
nicht — auf dem Rechner des Commanders nachgesehen: NICHT gesetzt.
Das ist MakeMKVs Web-Kontakt; darueber holt es die Disc-Schluessel fuer
4K-UHD nach (Meldung 3338, auf Windows gemessen). „Fuer Windows umschreiben"
heisst hier: Registry statt settings.conf, DWORD statt Text — die
Nachbarwerte (`app_BackupDecrypted`) zeigen die Form.
Gesetzt wird NUR, was fehlt: Wer den Web-Kontakt bewusst abgeschaltet hat,
behaelt ihn abgeschaltet. Ein Waechter-Test vergleicht kuenftig die
`app_*`-Namen aus der entrypoint.sh gegen die Windows-Seite — sonst driften
die beiden Betriebe auseinander.
871 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f0d39b4d04 |
docs(savepoint): laufende Disc-Erkennung sichtbar, verwaiste makemkvcon notiert
Ampel / ampel (push) Successful in 1m12s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e67138e9ca |
feat(ui): Laufende Disc-Erkennung ist jetzt sichtbar
Ampel / ampel (push) Successful in 1m26s
Commander: „das die disc erkennung noch läuft muss sichtbar sein"
Er hat recht, und die Wartezeit ist gemessen: `makemkvcon info` laeuft an
einer Blu-ray in seine 120-Sekunden-Grenze (Disc nach 119 s erkannt). Zwei
Minuten, in denen NICHTS zu sehen war — weder die Disc noch ein Hinweis. Das
sah aus wie ein leeres Laufwerk, also wie ein Fehler.
## Der Zustand war da, er wurde nur weggeworfen
`_auto_prescan` legt beim Start `{"_laeuft": True}` in den Vorrat.
`laufwerke_mit_disc` liess so einen Eintrag KOMPLETT weg — damit keine
halbfertige Disc-Karte mit „Wird erkannt…" als Titel und einem „Rippen
starten"-Knopf erscheint. Richtig gedacht, falsch geloest.
Jetzt gibt es ein eigenes Feld `disc_wird_erkannt`. Die Oberflaeche kann
„wird gelesen" zeigen, ohne einen Titel zu behaupten, den es noch nicht gibt:
eine Karte mit Spinner ueber der Laufwerksliste und eine eigene Zeile im
Server-Status.
## Und der Haken dahinter
Genau waehrend der Erkennung ist die Laufwerks-Auskunft am teuersten:
makemkvcon haelt das Laufwerk, und `device_info` wartet mit. Gemessen:
/devices waehrend des Scans 14,0 s
Zeitgrenze des Schnappschusses 5,0 s -> devices = None
`None` heisst „konnte nicht nachsehen", und die Oberflaeche behaelt dann
ihren Stand — nach einem frischen Laden also GAR NICHTS. Die Anzeige waere
ausgerechnet in ihrer eigenen Phase leer geblieben.
`laufwerke_notdurft()` antwortet deshalb ohne ioctl: letzter bekannter Stand
plus die aktuelle Erkennungs-Marke aus dem Vorrat. Beim Kaltstart wenigstens
der Laufwerksname — die LISTE der Laufwerke ist billig, teuer ist erst das
Anfassen. Nichts wird erfunden: kein Typ, kein Modell. Und wer noch nie
erfolgreich gelesen hat, schweigt weiter mit `None`.
Im Browser gegengeprueft: „Disc wird gelesen — Laufwerk G:" mit Spinner,
Server-Status „Disc wird gelesen — das dauert bis zu zwei Minuten", danach
der richtige Titel.
859 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
dbb934d146 |
docs(savepoint): v4.0-rc7 — Drueberinstallieren, Linux-Reste, Ordner-Waehler, Jobzeile
Ampel / ampel (push) Successful in 1m9s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7b0c41ddfe |
fix(ui): Datum 1.1.1970, Disc nicht erkannt, kein Abbrechen — eine Ursache je Fall
Ampel / ampel (push) Successful in 1m16s
Vier Befunde aus einem Bildschirmfoto vom 29.08.2026.
## 1. „das Datum ist falsch" — und der Typ auch
TYP „DISC" STARTZEIT „1.1.1970, 01:00:00" STATUS „running"
⚠️ Das war MEIN Fehler von heute Vormittag. Ich hatte `type`, `device` und
`startTime` in das Job-Ereignis aufgenommen — mit Feldnamen, **die es in der
Datenbankzeile nicht gibt**. Dort heissen sie `disc_type` und `created_at`;
die Uebersetzung macht `_job_row_to_model`, und sie bildet auch `running` auf
`processing` ab.
Die Folge war schlimmer als das Problem davor: Statt eines FEHLENDEN Feldes
kam ein LEERES. `new Date(null)` ist der 1.1.1970, und „DISC" war der
Rueckfall, den ich zwei Stunden vorher fuer genau diesen Fall eingebaut hatte.
Aus „offensichtlich kaputt" war „sieht plausibel aus" geworden.
Und mein Test hat es nicht gefunden, weil ich seine Beispielzeile selbst
erfunden habe — mit meinen falschen Feldnamen. Ein Test, der dieselbe Annahme
macht wie der Code, prueft nichts. Der neue geht von der ECHTEN Zeile aus.
Der Waechter bekommt jetzt dieselbe Uebersetzung eingespritzt, die auch
`/jobs` benutzt.
## 2. „Es ist eine Disk im laufwerk, aber er erkennt sie dort nicht"
Die Laufwerksliste wurde an DREI Stellen gebaut: `/devices` haengte die
erkannte Disc an, der Ereignis-Waechter und der SSE-Schnappschuss nicht. Das
Dashboard liest den Schnappschuss — und schloss aus der fehlenden Disc auf ein
leeres Laufwerk, waehrend ihr Titel eine Zeile weiter oben stand.
Jetzt gibt es `laufwerke_mit_disc()`, und ein Test zaehlt die Aufrufe von
`device_info` — bei zwei ist er rot.
## 3. „man kann einen rip garnicht abbrechen"
Dieselbe Ursache wie 1: Es gab genau EINEN Abbrechen-Knopf, in der Kachel
„laufender Job". Die erscheint nur bei Status `processing`; der Strom lieferte
`running`. Keine Kachel, kein Knopf, kein Weg zurueck — und aus demselben
Grund stand „Aktiv (0)", waehrend der Rip lief.
Zusaetzlich gibt es den Knopf jetzt in der Job-Zeile selbst, wo man ihn sucht.
## 4. „bei ‚Platz für rippy' sollte eher das arbeitsverzeichnis und der
## Ablagepfad sein"
Dort stand eine nackte Zahl fuer den ERSTEN Ort. Welcher Ordner das war, sah
man nicht — und die zweite Zeile fiel ganz weg, wenn beide auf derselben
Platte lagen. Das stimmt fuer die ZAHL und war der falsche Schluss fuer den
ORT. Jetzt stehen beide da, mit Pfad, plus der Hinweis, dass der Platz
geteilt wird.
Im Browser gegengeprueft: „Disc erkannt — wartet auf ‚Rippen starten'",
BLU-RAY mit richtigem Datum, beide Pfade, keine Konsolenfehler.
852 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b2acddbdfa |
fix(windows): Drueberinstallieren, Linux-Reste im Windows-Betrieb, Ordner-Waehler
Ampel / ampel (push) Successful in 1m27s
Vier Fragen des Commanders vom 29.08.2026, drei davon mit Codefolge.
## 1. „Was passiert wenn man die Setup.exe einfach drueber installiert?"
Bis hierher: nicht zuverlaessig. Rippy startet mit Windows, laeuft also fast
immer — dann ist `Rippy.exe` gesperrt, `shutil.copy2` warf PermissionError,
und die neue Fassung landete als `Rippy.exe.neu` daneben. Dazu die Meldung
„wird beim naechsten Start uebernommen".
**Diese Zusage hat niemand eingeloest.** `.neu` kam im ganzen Projekt genau
einmal vor: an der Stelle, die es schrieb. Wer drueberinstallierte, behielt
still die alte Fassung, und das Setup meldete Erfolg.
Jetzt wird der laufende Rippy vorher beendet (`dienst_beenden` gibt es seit
der Deinstallation und wartet auch die zwei Sekunden ab, die Windows fuer die
Dateihandles braucht). Eine von einer aelteren Setup-Fassung liegengelassene
`.neu` wird dabei uebernommen. Bleibt die Datei DANN noch gesperrt, gibt es
einen klaren Fehler statt einer Zusage — Rippy im Infobereich beenden und das
Setup erneut starten.
Der Tausch laeuft bewusst im SETUP und nicht beim Dienststart: Windows sperrt
eine laufende .exe, und `Rippy.exe` waere genau die zu ersetzende Datei.
## 3. Linux-Reste im Windows-Betrieb (Docker/Headless unveraendert)
**`caps.py`: `os.path.isdir("/app")`.** Damit hielt sich der eigenstaendige
Windows-Rippy fuer einen FREMDEN Worker — und das UI warnte vor fehlender
Pfad-Uebersetzung auf einer Maschine ohne Container und ohne Freigabe.
„Extern" heisst jetzt, was es meint: Rippy laeuft woanders als dieser Worker.
**`caps.py`: `shutil.which("makemkvcon")` + `os.path.ismount(daten_dir)`.**
Beide unter Windows immer falsch (Programme liegen nicht im PATH, ein
normaler Ordner ist kein Mount). Die Schluessel-Auskunft blieb dauerhaft
„unbekannt", obwohl MakeMKV samt Datenverzeichnis da war. Der Mount-Test
bleibt fuer den Container, wo er einen Zweck hat.
**`rohdaten.py`: `/app/temp/raw` und `/app/media` fest.** Dieses Modul findet
die Rohdaten eines Jobs wieder — fuer den Wiederholen-Dialog und fuer
„Rohdaten mitloeschen". Unter Windows fand es NIE etwas: Der Dialog meldete
„keine Rohdaten", das Aufraeumen loeschte nichts, und die Bruchstuecke eines
abgebrochenen Rips blieben liegen (bei 4K-UHD bis 100 GB).
Sieben Tests wurden dabei rot, und zwar zu Recht: Sie pruefen Container-Regeln,
liefen aber unter Windows. Die Wurzeln sind jetzt einspritzbar — beide
Betriebsfaelle auf jedem Rechner pruefbar statt vom laufenden abhaengig.
## 4. „Der Durchsuchen button fehlt. Wie es der Installer auch macht"
Neu: `OrdnerWaehler` — Pfadfeld plus „Durchsuchen …", benutzt fuer Ablage und
Arbeitsverzeichnis. Es waere der DRITTE fest eingebaute Ordner-Browser
geworden (RipTargetModal, StorageMounts); dieser hier ist wiederverwendbar.
`/browse` weiss seit dem 28.08. selbst, in welchem Betrieb es laeuft.
⚠️ Beim Einbau fiel der Import unter den Tisch. `vite` pruefte das NICHT — das
Buendel blieb byte-gleich gross, und zur Laufzeit waere es der naechste leere
Bildschirm gewesen. Aufgefallen nur, weil die erwartete Anzahl Ersetzungen
nicht stimmte. Im Browser gegengeprueft: alle sieben Laufwerke, Navigation in
D:\, keine Konsolenfehler.
843 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
21b1757aa4 |
docs(savepoint): v4.0-rc6 — leerer Bildschirm und zwei weitere Container-Wurzeln
Ampel / ampel (push) Successful in 1m43s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
423374e65a |
fix(windows): Roh- und Zielordner landeten in einem Ordner namens „app"
Beim Nachstellen des leeren Bildschirms lief ein echter Test-Rip durch. Die
Rohdaten landeten in:
F:\app\temp\raw\<job>\Evangelion 2.22_t00.mkv (436 MB)
Also in einem Ordner namens `app` auf dem Laufwerk, von dem Rippy gerade lief.
## Zwei Container-Wurzeln im Worker
RAW_DIR = /app/temp/raw
MEDIA_ROOT = /app/media
Unter Windows sind das keine Pfade, sondern Unfaelle. Schlimmer: Die Pruefung
`unter_wurzel(wahl, MEDIA_ROOT)` verwarf auch eine AUSDRUECKLICHE Wahl — ein
Arbeitsordner wie `D:\Roh` liegt nicht unter `/app/media`, also fiel er still
auf den Container-Standard zurueck.
**Damit kam der Arbeitsordner, den der Commander am 28.08.2026 ausdruecklich
bestellt hat, unter Windows nie an.** Der Dialog zeigte ihn, das Setzen ging,
und der Worker ignorierte ihn — ohne ein Wort. Dasselbe galt fuer das Ziel:
Eine UNC-Freigabe liegt unter gar keiner lokalen Wurzel, also waere die
fertige Datei in `X:\app\media\bluray` gelandet.
## Die Wurzeln kommen jetzt aus dem Betrieb
Im Container aendert sich NICHTS: dort ist `/app/media` die Wurzel und `frei`
falsch. Nativ zaehlt die Wahl des Nutzers — dort IST sein Laufwerk die Grenze.
Die drei bestehenden Tests wurden rot, und zwar zu Recht: Sie pruefen die
Container-Regel, liefen aber unter Windows, wo `frei` gilt. Die Wurzeln sind
deshalb einspritzbar — beide Betriebsfaelle sind jetzt auf jedem Rechner
pruefbar, statt vom laufenden abzuhaengen.
837 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
27c9d9a4bb |
fix(ui): Leerer Bildschirm nach „Rippen starten" — ein halber Job riss alles mit
Commander: „wenn man auf rippen starten klickt passiert irgendwas, was das
Programm nicht mag. Der Hintergrund ist einfach leer und er startet nix. Es
gibt auch keine fehlermeldung."
Nachgestellt: Dienst lokal gestartet, Disc-Karte geoeffnet, „Rippen starten"
geklickt. Browser-Konsole:
TypeError: Cannot read properties of undefined (reading 'toUpperCase')
## Er startet sehr wohl — man sieht es nur nie
Waehrend die Seite leer war, lief der Job (per API gemessen: status
`processing`, progress 12). Das „er startet nix" ist also der zweite Teil
desselben Fehlers: Die Oberflaeche war weg, bevor sie ihn zeigen konnte.
## Die Ursache
`_job_kurz` in `bus/waechter.py` trug nur `status`, `progress`, `title` und
`error` — **keinen `type`**. Das UI fuegt aus `job.created` einen halben Job
in seine Liste ein, `LiveLogSection` liest `job.type.toUpperCase()`, und React
baut bei einem Fehler im Zeichnen den GESAMTEN Baum ab. Es gab in diesem
Projekt keine einzige Fehlergrenze — also blieb ein leerer Bildschirm ohne
jede Meldung.
## Drei Reparaturen, weil es drei Fehler waren
1. **Das Ereignis traegt den Job.** `type`, `device` und `startTime` fahren
mit. Sie aendern sich ueber die Lebenszeit eines Jobs nie, kosten also kein
zusaetzliches Ereignis — und ohne `startTime` stand in der Jobliste
sekundenlang „Invalid Date".
2. **Das UI vertraegt sein Fehlen.** `LiveLogSection` und `TypeBadge` nahmen
einen vollstaendigen Job an. Zeile 108 derselben Datei hatte das
Fragezeichen laengst, Zeile 48 nicht.
3. **Eine Fehlergrenze.** Ein Fehler in einer Karte darf nicht den ganzen
Bildschirm mitnehmen — und schon gar nicht schweigend. Jetzt steht da, was
los ist, die Navigation bleibt bedienbar, und der Hinweis sagt das
Wichtigste: laufende Rips gehen weiter.
## Warum es niemand gefunden hat
`unterschiede()` bekommt die Kurzform schon fertig, und alle Tests reichen
ihre eigenen Woerterbuecher herein. **`_job_kurz` selbst war nie geprueft.**
Jetzt bewacht `UI_PFLICHTFELDER` den Vertrag mechanisch — es gibt kein
gemeinsames Typsystem zwischen Python und dem UI.
Gegengeprueft im Browser: derselbe Klick, keine Konsolenfehler, Job erscheint
als „Alle (1)".
833 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
d9780a4951 |
docs(savepoint): v4.0-rc5 — Rippy rippt, drei Fehler im Rip-Pfad behoben
Ampel / ampel (push) Successful in 1m38s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
954f293871 |
fix(windows): Rippen ging nicht — drei Container-Annahmen im Rip-Pfad
Commander: „Das Rippen der disk funktioniert nicht. makemkvcon endete mit
Code 11 — letzte Meldung: Das Öffnen der Disk schlug fehl — keine MKV-Datei
entstanden"
In diesem einen Satz steckten drei Fehler. Alle drei an seinem Laufwerk
gemessen, nicht hergeleitet.
## 1. Die Quellenangabe — das war der Abbruch
dev:\.\G: MSG:2024 „Unknown device" -> MSG:5010 Öffnen schlug fehl
dev:G: 68 TINFO-Zeilen, „Die Aufgabe wurde erfolgreich abgeschlossen"
`f"dev:{device_path}"` stand an VIER Stellen. Unter Linux richtig (/dev/sr0),
unter Windows nicht: `rippy.drives.windows` meldet den Geraete-Namensraum
`\.\G:`, den `CreateFileW` fuer die IOCTLs braucht — MakeMKV kennt ihn
nicht.
## 2. „Ö" statt „Ö"
`Ö` als UTF-8 (C3 96), gelesen als cp1252. `subprocess` stand auf `text=True`
ohne `encoding=`, nahm also die Gebietsschema-Kodierung; Rippy stellt seine
Konsole beim Start aber auf UTF-8 (sonst stirbt der Start an einer
Umlaut-Logzeile). Direkt gemessen kommen die Bytes je nach Umgebung als UTF-8
ODER cp1252 — deshalb wird jetzt bestimmt statt behauptet.
## 3. Die Ursache fehlte im Fehlertext
Gesucht wurde nach ENGLISCHEN Textbausteinen („evaluation period has
expired"). Auf einem deutschen Windows trifft keiner. Jetzt zaehlen die
Meldungs-NUMMERN, die sprachunabhaengig sind.
⚠️ Beim ersten Anlauf hatte ich 5021 aus dem Kopf mit „Volume-Key unbekannt"
beschriftet — falsch. Meine eigene Ausgabe zeigte nur meine Beschriftung
statt MakeMKVs Text, sodass es fast durchgegangen waere. Der echte Wortlaut
steht jetzt im Code.
## Nebenbefund: der Beta-Key lag zweimal am falschen Ort
`makemkv_daten.DATEN_DIR` war fest `/root/.MakeMKV`. Nach der Reparatur des
Ordners blieb MSG:5051 trotzdem stehen — weil MakeMKV unter Windows seine
Einstellungen in der REGISTRY haelt (HKCU\Software\MakeMKV), nicht in einer
settings.conf. Nachgesehen statt vermutet.
Und der wichtigste Teil davon: **Ein abgelehnter Schluessel ist schlimmer als
gar keiner.** Ohne Key las MakeMKV die Disc noch (68 TINFO), mit dem
abgelehnten verweigerte es alles (0 Titel). Deshalb wird jetzt geprueft und
bei Ablehnung zurueckgenommen.
## Beweis
Echter Rip ueber `ripping.run_makemkv`, kuerzester Titel:
Status success, Code 0, „Evangelion 2.22_t03.mkv" 217,9 MB
5036 „Das Kopieren wurde abgeschlossen. 1 Titel wurden gesichert."
828 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
73ed104bbc |
docs(savepoint): Arbeitsordner im Setup, Beta-Key auf Knopfdruck
Ampel / ampel (push) Successful in 1m15s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
678fe276f9 |
feat(windows): Arbeitsordner im Setup, Beta-Key auf Knopfdruck
Ampel / ampel (push) Successful in 1m18s
Zwei Wuensche des Commanders vom 28.08.2026.
## 1. „kannst du noch einbauen das man den arbeitsordner usw. in den
## Einstellungen setzen kann und direkt im setup?"
In den Einstellungen ging es seit
|
||
|
|
d0281f7a1b |
style: Zeilenenden zurueck auf LF (kein Inhalt geaendert)
In
|
||
|
|
6881a63fb7 |
docs(savepoint): v4.0-rc4 — Cover, Arbeitsverzeichnis, aufgeraeumte Temp-Ordner
Ampel / ampel (push) Successful in 55s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
35370fd552 |
fix(windows): Cover, Arbeitsverzeichnis und die zurueckgelassenen _MEI-Ordner
Drei Befunde des Commanders vom 28.08.2026, alle drei gemessen.
## 1. „Was ist mit dem Cover auf Windows Rippy?"
Es gab keins, weil es keinen Treffer gab. `_scan_video` verlangte woertliche
Gleichheit:
if movie.get("title", "").lower() == kandidat.lower():
An seiner Disc gemessen:
'Evangelion 2.22' 1 Treffer Evangelion: 2.0 You Can (Not) Advance
'Evangelion' 20 Treffer irgendein Evangelion
Der EINZIGE Treffer auf den vollen Disc-Titel war der richtige Film, mit
Poster — und wurde verworfen. Jetzt zaehlt die SPEZIFITAET der Anfrage: Wer
auf den vollen Disc-Titel hoechstens drei Treffer bekommt, hat gefragt wie
jemand, der weiss was er sucht. Ergebnis an derselben Disc:
Evangelion: 2.0 You Can (Not) Advance / 2009 / 80 % / Poster + dt. Text
## 2. „Warum heisst das hier noch container platte? … Waere es moeglich das
## Arbeitsverzeichnis zu aendern? momentan geht das nicht."
Beide Haelften gehen auf EINE Zeile zurueck: `MEDIA_ROOT = "/app/media"` war
zugleich Vorgabe UND Pfadgrenze. Auf Windows gibt es den Ordner nicht:
* `/storage-targets` fing den OSError und gab still [] zurueck — die Auswahl
hatte genau einen Eintrag. Das ist „momentan geht das nicht".
* Dessen Text war fest verdrahtet „(Container-Platte)".
* `/browse` antwortete auf jeden Pfad mit 422.
Die Grenze faellt nicht weg, sie wird betriebsabhaengig: Container und
Kopflos-Betrieb bedienen ein Netz, die native App den Menschen davor.
Gemessen auf seinem PC: 7 Laufwerke zur Auswahl, X/Y/Z mit je 2,2 TB frei.
## 3. „Failed to remove temporary directory: …_MEI0000b0882"
GEMESSEN: 20 zurueckgelassene _MEI-Ordner mit 1,1 GB. Ursache: `Popen` ohne
`env=` reicht PyInstallers Auspack-Zeiger an die Kinder weiter (--dienst und
--oeffnen). Beide laufen dann im Ordner des Elternprozesses, der sich zuerst
beendet und ihn loeschen will. Waere das TEILWEISE geglueckt, haetten Dienst
und Fenster mitten im Betrieb ihre Dateien verloren.
Nebenbefund: test_setup_fenster scheiterte in jeder Umgebung ohne pywebview.
794 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
11001a443e |
docs: SAVEPOINT v4.0-rc3 — Windows testbereit
Ampel / ampel (push) Successful in 55s
Stand vor dem Test des Commanders. Zwei Dinge stehen bewusst gross drin: die seit V2-1 tote Metadaten-Suche (betrifft die VM genauso) und die bekannte Luecke bei Audio-CDs unter Windows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
be3fac53af |
fix(prescan): makemkvcon ueber den Katalog suchen, nicht ueber shutil.which
Ampel / ampel (push) Successful in 55s
Auf die Frage des Commanders: "du hast jetzt nur mein laufwerk analysiert
oder? wenn ich rippy weitergebe wie laeuft es dort?"
Berechtigt -- und beim Nachsehen fiel derselbe Fehler auf wie heute Vormittag
im Werkzeug-Katalog:
shutil.which("makemkvcon") -> None, auch bei installiertem MakeMKV
`which` sucht nur im PATH. Windows-Programme liegen in "Programme". Der
Zweig, der die Titelliste von makemkvcon holt, lief unter Windows also NIE --
unabhaengig vom Laufwerk, auf jedem Rechner. Jetzt ueber
`katalog.finden("makemkv")`, das genau dafuer da ist.
Dazu vier Tests fuer Disc-Arten, die ICH NICHT HABE, damit die Zusage nicht
an einer einzigen Disc haengt:
Blu-ray ohne BDMV/META -> None (sehr haeufig; Rueckfall aufs Label)
DVD (nur VIDEO_TS) -> None
bdmt_deu.xml vorhanden -> Titel wird gelesen
deu + eng vorhanden -> eng gewinnt (APIs suchen damit besser)
Und ein Waechter gegen shutil.which in prescan.py. Der erste Anlauf davon
fiel ueber den eigenen Erklaertext, in dem der alte Aufruf zitiert wird --
er prueft jetzt nur Code, keine Kommentare.
Ampel lokal: 774 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
878b24c43b |
fix: die Metadaten-Suche war seit V2-1 tot — auf Windows UND auf der VM
Ampel / ampel (push) Successful in 54s
Der Commander: "TMDB und OMDB Key sind hinterlegt, das laufwerk wird aber
auch nicht korrekt ausgelesen. Eigentlich sollte er direkt bei TMDB oder OMDB
oder JIKAN anfragen nach metadaten."
Er hat das Symptom gemeldet. Darunter lagen VIER Fehler.
## 1. Ein Phantom-Modul (der schwerste)
clients/tmdb.py:27 from db import get_settings
clients/omdb.py:36 from db import get_settings
clients/thetvdb.py:16 from db import get_settings
`docker/api/db.py` gibt es seit der Zusammenlegung in V2-1 (
|
||
|
|
ea8a043643 |
feat(setup): Fortschrittsbalken, MakeMKV-Installer mit Rechteabfrage, Updates
Ampel / ampel (push) Successful in 55s
Drei Punkte des Commanders vom 28.08.2026.
## 1. "bei der installation eine progress bar waere gut"
Die Daten dafuer waren schon da -- der Fortschritts-Rueckruf traegt seit
jeher einen Anteil, die Bruecke warf ihn nur weg.
Jetzt rechnet `Fortschritt` Abschnitt + Teil-Anteil in einen Gesamtstand um.
Die Gewichte sind gemessen: Dateien ablegen 0-8 % (rund drei Sekunden),
Ablage 8-10 %, Werkzeuge 10-97 % (sechs Sekunden bis mehrere Minuten). Ein
Balken, der bei 90 % minutenlang steht, ist schlimmer als gar keiner.
Zwei Regeln, beide mit Test:
* Er laeuft NIE zurueck. Der Werkzeug-Abschnitt kann mehrere Downloads
enthalten, jeder mit eigenem 0-bis-1 -- ohne die Regel spraenge der Balken
bei jedem neuen Werkzeug an den Abschnittsanfang. Zusaetzlich rechnet
`sicherstellen` den Anteil eines Werkzeugs auf die ganze Strecke um.
* Eine Textmeldung ohne Zahl bewegt ihn nicht. Sie ist keine Aussage ueber
den Fortschritt.
Am laufenden Fenster nachgewiesen: "Werkzeuge werden geprueft -- 36 %",
Balken bernsteinfarben, am Ende gruen bei 100 %.
## 2. "make MKV laesst sich nicht installieren weil der vorgang erhoehte
## rechte benoetigt"
Der Kern, und der Fehler lag bei uns: `subprocess.Popen` benutzt
`CreateProcess`, und das ZEIGT KEINE UAC-Abfrage -- es bricht mit
ERROR_ELEVATION_REQUIRED (740) ab. MakeMKV installiert nach Program Files und
fordert im Manifest Administratorrechte an.
`ShellExecuteW` ist der dokumentierte Weg, der die Abfrage ausloest. Damit
braucht NUR dieser eine Aufruf die Erhoehung.
Und deshalb laeuft das Setup NICHT dauerhaft erhoeht (dazu die Antwort im
Chat): Ein erhoehter Prozess sieht die eingebundenen Netzlaufwerke nicht --
auf diesem Rechner gemessen, X:, Y:, Z: ohne Erhoehung sichtbar,
EnableLinkedConnections nicht gesetzt. Genau das hatte der Commander vorher
selbst als Fehler gemeldet. Wer trotzdem erhoeht starten will, bekommt einen
Knopf -- aber nur, wenn das Schreibrecht am gewaehlten Ziel wirklich fehlt.
## 3. "wenn MakeMKV oder Handbrake schon installiert sind ein update
## verfuegbar ist. Das kann ja direkt im setup abgehandelt werden."
`veraltete()` vergleicht nach ZAHLEN (ein Zeichenvergleich hielte 1.9.2 fuer
neuer als 1.11.2 -- und HandBrake ist genau dort). `sicherstellen` holt sie
auf Wunsch mit, aber IMMER nach den fehlenden: Ein Update ist Komfort, ein
fehlendes Pflichtwerkzeug ist ein Hindernis.
Der Assistent zeigt es als eigenen Schalter mit Klartext ("MakeMKV 1.18.2 ->
1.18.4"). Der Update-Check laeuft als GETRENNTER Aufruf nach der
Voraussetzungs-Pruefung: Er kostet Netz, und makemkv.com braucht dafuer im
schlimmsten Fall gut zwanzig Sekunden -- in der Pruefung bliebe das Fenster
so lange leer. Sind die Quellen nicht erreichbar, schaltet sich der Haken ab
und sagt warum; ein Haken, der nichts tun kann, ist eine Falle.
Ampel lokal: 758 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
95beb91364 |
feat(tools): Ausweichquellen fuer MakeMKV — recherchiert und gemessen
Ampel / ampel (push) Successful in 1m1s
Commander: "bitte baue fuer MakeMKV fallback seiten ein."
Anlass war ein echter Ausfall. Am 28.08.2026 gemessen:
https://www.makemkv.com/download/ HTTP 525 (dauerhaft)
https://makemkv.com/download/ HTTP 525
http://www.makemkv.com/download/ HTTP 403
https://forum.makemkv.com/ 200 / 522 / Zeitablauf (wechselnd)
web.archive.org/web/2025id_/...exe 200, 16.432.607 Bytes
525/522 heissen: Cloudflare erreicht den Ursprungsserver nicht. Eine Stoerung
beim Hersteller -- dieselbe, die im Projekt schon einmal jeden Worker-Build
lahmgelegt hat.
## Die Kette
Version: Hersteller-Seite (massgeblich) -> Forum-Ankuendigungen -> Archiv
Datei: eigene Quelle -> Hersteller -> Internet Archive
Der Hersteller zuerst, immer. Das Archiv ist Rueckfallebene, und Rippy nennt
hinterher die Quelle, aus der die Datei kam.
Zwei Feinheiten, beide aus Messungen statt aus dem Kopf:
* Die HOECHSTE Nummer gewinnt, nicht die erste. Antwortet das Forum nicht,
meldet das Archiv einen aelteren Stand (1.18.2 statt 1.18.4) -- "neueste
Fassung" waere dann eine falsche Aussage.
* Ein zweiter Anlauf, mit knapper Zeitgrenze. Drei Abrufe am Forum:
TimeoutError, HTTP 522, dann Erfolg. Ohne Wiederholung fiel die Quelle in
zwei von drei Faellen aus; mit 20-Sekunden-Grenzen dauerte der schlimmste
Fall 46 Sekunden, in denen das Setup eingefroren aussah. Jetzt acht
Sekunden je Versuch, schlimmstenfalls gut zwanzig.
## Warum eine fremde Quelle trotzdem sicher ist
Der naheliegende Schutz geht NICHT: MakeMKV signiert seinen Installer nicht
(Get-AuthenticodeSignature -> NotSigned, an der echten Datei gemessen). Eine
Signaturpruefung waere eine, die immer fehlschlaegt -- schlimmer als keine,
weil sie Sicherheit vortaeuscht.
Geprueft wird die Versions-Ressource, ebenfalls gemessen:
CompanyName GuinpinSoft inc
FileDescription MakeMKV installer
FileVersion v1.18.4
Das ersetzt keine Signatur, faengt aber ab, was hier wirklich droht: eine
Fehlerseite mit .exe-Namen, ein abgebrochener Download, eine falsche Fassung.
Ende zu Ende nachgewiesen, ohne etwas zu installieren:
Version laut Kette: 1.18.4
Hersteller: HTTP 525 -> uebersprungen
Internet Archive: 15,7 MB in 6,1s
Pruefung: BESTANDEN (MakeMKV v1.18.4)
Die Grenze aus KONZEPT.md 6 bleibt: Rippy liefert MakeMKV weiterhin NICHT
mit. Es holt die Datei des Herstellers -- im Rueckfall aus einem Archiv, das
genau diese Datei aufbewahrt.
Ampel lokal: 733 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
480b658294 |
fix: die Laufwerks-Pruefung muss selbst einspritzbar sein
Ampel / ampel (push) Successful in 54s
Ampel-Lauf zu
|
||
|
|
16df61ea3a |
fix: pruefe_schreibrecht nahm os.path statt pfade — zum vierten Mal dieselbe Falle
Ampel / ampel (push) Failing after 54s
Ampel-Lauf zu
|
||
|
|
3d5f82b786 |
fix(windows): der Deinstallierer beendet jetzt auch den laufenden Dienst
Ampel / ampel (push) Failing after 54s
Beim Deinstallieren im laufenden Betrieb blieben liegen:
rippy.db, rippy.db-shm, rippy.db-wal
(Der Prozess kann nicht auf die Datei zugreifen, da sie von einem
anderen Prozess verwendet wird)
Der Dienst lief weiter und hielt die Datenbank offen. Damit waere der Fehler
von vorhin durch eine andere Tuer zurueckgekommen: Die naechste Installation
haette wieder den alten Zustand samt setup.done geerbt, und der
Ersteinrichtungs-Assistent waere wieder verschwunden.
Aufgefallen ist es nur, weil der Deinstallierer seit heute NENNT, was er
nicht wegbekommen hat. Vorher stand dort ein `except OSError: pass` und die
Meldung "Rippy wurde entfernt".
Gefiltert wird ueber den Installationsordner, nicht ueber den Namen:
`Rippy.exe` heisst auch die Datei, die den Auftrag gerade ausfuehrt. Die
eigene Prozesskennung und die des PyInstaller-Starters sind ausgenommen.
Nachgewiesen am laufenden Prozess: beendet, Datenbankdateien danach
loeschbar. In der Entwicklungsumgebung schlaegt der Pfadvergleich uebrigens
immer fehl -- die Werkzeuge laufen in einem App-Container, der
%LOCALAPPDATA% umleitet, waehrend die Registry den echten Pfad meldet. Das
steht jetzt als Hinweis im Docstring, damit es niemand ein zweites Mal sucht.
Ampel lokal: 713 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8e65411189 |
fix(windows): die Datenbank lag AUSSERHALB der Installation — daher fehlte
Ampel / ampel (push) Failing after 54s
der Ersteinrichtungs-Assistent
Der Befund des Commanders: "Ausserdem fehlt der 1st run wizzard wenn man es
installiert." Meine erste Reparatur (App.tsx las einen gescheiterten Abruf
als "erledigt") war richtig, aber nicht die Ursache. Nachgemessen:
Installation %LOCALAPPDATA%\Rippy wird deinstalliert
Datenbank %ProgramData%\Rippy BLEIBT LIEGEN
Eine "frische" Installation erbte damit die alte Datenbank samt
`setup.done = true` -- der Assistent erschien nie wieder. Gegengeprueft:
nach dem Aufraeumen meldet /api/setup wieder {"done": false}, und das
Fenster zeigt "Willkommen bei Rippy".
Der Grund fuer den alten Ort stand im Docstring: "Ein Programmordner ist
unter Windows fuer einen DIENST nicht zuverlaessig beschreibbar." Das stimmte
-- fuer den Windows-Dienst, den es nie gab. Rippy laeuft als der angemeldete
Benutzer (Autostart unter HKCU). Jetzt liegt alles, was Rippy gehoert, in
EINEM Ordner: Programm, UI, Werkzeuge, Protokoll, WebView2-Zwischenspeicher
und die Datenbank.
Dazu:
* `datenbank_umziehen()` holt eine vorhandene Datenbank vom alten Ort ab --
wer Rippy schon benutzt hat, behaelt Jobs und Einstellungen. Gibt es am
neuen Ort schon eine, bleibt sie unangetastet: Die BENUTZTE gewinnt.
* `deinstallieren()` raeumt den alten Ort mit weg, und `alte_orte()` fasst
nur einen Ordner an, der wirklich "Rippy" heisst.
* Der Ersteinrichtungs-Assistent spricht nicht mehr von "Encoding-Worker",
wenn es keine gibt -- dort steht jetzt "Kompression". Gegengeprueft: kein
"docker", kein "Container", kein "Worker" mehr im Text.
Ampel lokal: 709 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
082e8b8b55 |
fix(windows): der Knopf "Rippy oeffnen" oeffnete Rippy nicht
Ampel / ampel (push) Failing after 55s
Nach einer erfolgreichen Einrichtung schloss er nur das Fenster, und danach
passierte nichts mehr: Der Dienst lief nicht, kein Fenster ging auf. Der
Commander hat Rippy anschliessend selbst gestartet ("das tool selbst laesst
sich starten") -- was den Fehler verdeckt hat.
Ein Knopf, der etwas anderes tut als seine Beschriftung sagt, ist schlimmer
als keiner. Jetzt startet main() nach einem geglueckten Assistenten das
installierte Programm.
Dazu der zweite Teil des Netzlaufwerk-Befunds: Auch Netz-ANMELDUNGEN haengen
am Token der Sitzung, nicht nur die Laufwerksbuchstaben. Ein erhoehter Prozess
kennt die Zugangsdaten zur Freigabe nicht und bekommt "Zugriff verweigert" --
also dieselbe Meldung wie bei einem echten Rechteproblem. Ein UNC-Pfad mit
Administratorrechten wird jetzt als solcher erkannt und erklaert.
Ampel lokal: 705 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
6dfff5c99d |
fix(windows): vier Befunde aus dem ersten echten Installationslauf
Alle vom Commander gemeldet, alle nachgestellt.
## 1. Der Installer brach ab: No module named 'db'
Fehlgeschlagen: ModuleNotFoundError: No module named 'db'
Zu Recht: **docker/api/db.py gibt es nicht.** Seit der Zusammenlegung in V2-1
ist der Store `rippy.store`; main.py schreibt seitdem
`from rippy import store as db`. Ich habe mir aus einem ALIAS ein Modul
zusammengereimt und importiert -- genau die erfundene Schnittstelle, vor der
AGENTS.md Regel D warnt, nur diesmal im eigenen Code. Der Store wird jetzt
direkt benutzt; der Umweg ueber den API-Suchpfad war ohnehin ueberfluessig.
## 2. Der Ersteinrichtungs-Assistent fehlte nach der Installation
In App.tsx stand:
.catch(() => setSetupDone(true)) // API/Setup nicht erreichbar -> nicht blockieren
Das Fenster geht unmittelbar nach dem Setup auf, der Dienst braucht ein bis
zwei Sekunden bis zur ersten Antwort -- der erste Abruf scheitert also fast
immer. Aus "ich konnte nicht nachsehen" wurde "ist schon erledigt", und der
Assistent war fuer immer weg.
Dasselbe Prinzip steht nebenan in useEventStream.tsx: Ein Verbindungsabriss
ist KEINE Aussage ueber die Welt. Jetzt wird nachgefragt, bis der Server
ANTWORTET (zwei Minuten lang alle zwei Sekunden), und solange steht
"Rippy startet ..." im Fenster statt einer leeren Flaeche.
## 3. + 4. Als Administrator keine Netzlaufwerke, Netzlaufwerk = "keine Rechte"
Beides Windows-Verhalten, kein Rippy-Fehler -- aber Rippy hat ihn
hineinlaufen lassen. Auf dem Rechner nachgemessen:
X:, Y:, Z: Netzlaufwerke
EnableLinkedConnections nicht gesetzt (Standard)
C:\Program Files (x86) ohne Administrator nicht beschreibbar
Ein erhoehter Prozess bekommt ein anderes Zugriffstoken; eingebundene
Netzlaufwerke haengen am Token der Sitzung und existieren dort schlicht
nicht. Daraus wird ein Teufelskreis:
Program Files verlangt Administrator
Administrator versteckt die Netzlaufwerke
Netzlaufwerk wirkt dann wie "keine Schreibrechte"
Rippy braucht ueberhaupt keine Administratorrechte (Vorgabeordner unter
%LOCALAPPDATA%, Autostart unter HKCU). Der Assistent sagt das jetzt:
* eine eigene Pruefung "Rechte", die bei erhoehtem Start warnt und die
unsichtbaren Laufwerke beim Namen nennt
* ein fehlender Laufwerksbuchstabe wird als solcher gemeldet, nicht als
fehlendes Schreibrecht ("Laufwerk Q: ist hier nicht vorhanden")
* ein Ziel unter "Programme" erklaert den Kreis und verweist auf den
Vorgabeordner
## Dazu: zwei Schreibfallen in AGENTS.md
Deutsche Anfuehrungszeichen in doppelt gequoteten Zeichenketten (vier Mal an
einem Tag) und Bash-Heredocs, die Backslashes halbieren (fuenf Mal, zuletzt
beim Schreiben genau dieses Absatzes). Ein Test dafuer waere Rauschen gewesen
-- Python faengt beides bereits beim Import, nur mit verwirrender Meldung.
Die Regel gehoert in die Konventionen, nicht in eine Pruefung.
Ampel lokal: 703 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8eca982e68 |
docs: SAVEPOINT v4.0-rc2 — Windows ist ein eigenes Produkt
Ampel / ampel (push) Successful in 55s
Die drei Befunde des Commanders und was dahinter steckte, plus der teuerste Fund des Tages: %ProgramFiles(x86)% wurde auf einer echten Maschine NIE aufgeloest, weil der Test genau die Schreibweise einspritzte, die im Muster stand. Er war gruener als die Wirklichkeit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
151376aee3 |
fix: Pfad-Regeln an EINE Stelle — dreimal dieselbe Falle war zweimal zu viel
Ampel / ampel (push) Successful in 54s
Ampel-Lauf zu
|
||
|
|
b47d21510d |
docs: Entscheid 6 — Faehigkeiten statt Modus-Name, und ein Setup das fragt
Ampel / ampel (push) Failing after 54s
Beide Befunde des Commanders vom 28.08.2026 festgehalten, mitsamt der Begruendung, warum ein 'if (windows)' an dreissig Stellen die falsche Reparatur gewesen waere: Die Oberflaeche haette weiterhin nichts ueber ihren Betrieb gewusst, nur eine zweite Sorte Vermutung gehabt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a4a045f303 |
feat(windows): ein echtes Setup — Voraussetzungs-Pruefung, Zielordner, Ablage
Commander: "Dann haette ich beim 'Setup' auch erwartet das nen echtes setup
passiert - wo will ich das hinspeichern, nen pre requirement check usw. Da
kommt garnichts Rippy geht einfach auf."
Er hatte recht. Ein Doppelklick installierte stillschweigend nach
%LOCALAPPDATA%\Rippy und oeffnete das Fenster. Der Nutzer wurde nichts
gefragt und erfuhr nichts -- auch nicht, ob sein Rechner ueberhaupt kann, was
Rippy braucht.
Jetzt: ein Assistent mit acht Pruefungen, zwei Ordner-Wahlen und drei
Schaltern. Auf diesem Rechner nachgemessen:
OK Betriebssystem Windows 11 (Build 26200)
OK Fenster (WebView2) 151.0.4129.107
OK Optisches Laufwerk \.\G:
OK HandBrakeCLI 1.11.2
! MakeMKV nicht gefunden
-> wird von makemkv.com geholt
OK Speicherplatz 59.1 GB frei
OK Schreibrecht C:\Users\...\AppData\Local\Rippy
OK Netzwerk-Port 7788 ist frei
Drei Entwurfsentscheidungen, die dazugehoeren:
* Nur ein FEHLER blockiert, eine Warnung nicht. Eine Warnung, die den Knopf
sperrt, ist eine Bevormundung; ein Fehler, der nur warnt, ist eine Falle.
Kein Laufwerk = Warnung (reine Komprimier-Maschine ist ein vorgesehener
Betriebsfall). Kein Schreibrecht = Fehler.
* Jeder Befund sagt, was zu tun ist. Ein Befund ohne Abhilfe laesst den
Nutzer ratlos zurueck -- ein Test haelt das fuer jede Pruefung fest.
* Die Pruefungen stehen in einrichtung.py, komplett ohne Fenster pruefbar.
Im Fenstermodul steht nur Aufbau und Bruecke.
WebView2 statt Win32-Dialog: Es ist ohnehin da (Entscheid 4), der Assistent
kostet damit NULL zusaetzliche Bytes. Die Seite laedt nichts aus dem Netz --
sie muss auf einem Rechner ohne Internet aufgehen, dort wird sie am
dringendsten gebraucht. Ein Test haelt das fest.
--still und --ohne-assistent gehen weiterhin den stummen Weg. Geht das
Fenster nicht auf, wird mit den Vorgaben installiert und gesagt warum -- ein
Setup, das gar nichts tut, weil sein Fenster nicht aufging, waere schlimmer
als eines, das nicht fragt.
fix(tools): %ProgramFiles(x86)% wurde auf echten Rechnern NIE aufgeloest
Beim Bauen der Pruefung aufgefallen, und es ist ein Lehrstueck:
Testumgebung {"ProgramFiles(x86)": ...} -> C:\Program Files (x86)\...
echtes Windows {"PROGRAMFILES(X86)": ...} -> ''
Windows behandelt Umgebungsvariablen ohne Ruecksicht auf die Schreibweise und
legt sie in os.environ GROSS ab. Der Vergleich in _entfalten war exakt, fand
nie eine Uebereinstimmung, und die bekannten Installationsorte fielen aus der
Kandidatenliste. MakeMKV wurde also NUR ueber die Registry-Rueckfallebene
gefunden -- wo die fehlt oder anders aussieht, fand Rippy nichts.
Aufgefallen ist es nur, weil hier zum ersten Mal eine ECHTE Umgebung benutzt
wurde. Der Test spritzte genau die Schreibweise ein, die im Muster stand: Er
war gruener als die Wirklichkeit. Beide Tests nehmen jetzt die echte
Schreibweise, und einer prueft direkt gegen os.environ. Beim Zurueckdrehen
des Fehlers rot gesehen (5 Fehlschlaege).
Ampel lokal: 691 gruen, ruff sauber. Assistent im Bildschirmfoto belegt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
66642b8d93 |
feat(ui): die Oberflaeche weiss jetzt, worauf sie laeuft
Commander-Befund 28.08.2026, zum Windows-Fenster:
Worker erreichbar: 0 von 1
Kein Worker antwortet — Pruefen: docker compose ps
Container-Platte: unbekannt
Freigaben: keine eingehaengt
Kein Satz davon ergibt auf einem Windows-PC einen Sinn. Sein Urteil: "Du hast
ja quasi nur rippy genommen und die docker installation fuer Windows gebaut.
Das gilt fuer die ganze standalone version fuer Windows, auch fuer die
settings und die Anleitung usw."
## Die Ursache war nicht die Anzeige
Die naheliegende Reparatur waere ein `if (windows)` an dreissig Stellen
gewesen -- dieselbe Falle noch einmal, nur mit einer zweiten Sorte Vermutung.
Es fehlte etwas anderes: Das UI hat nie erfahren, worauf es laeuft. config.py
kennt das Profil seit V2-2, weitergegeben wurde es nie. Also hat das UI
angenommen.
Neu: GET /betrieb meldet FAEHIGKEITEN, keinen Modus-Namen.
externe_worker Gibt es andere Maschinen, die Jobs uebernehmen?
freigaben_einhaengen Kann Rippy Netzwerk-Freigaben selbst einhaengen?
container_pfade Sind Pfade wie /app/media ueberhaupt gemeint?
werkzeuge_verwalten Kann Rippy MakeMKV/HandBrake selbst beschaffen?
Ein Modus-Name wuerde das UI zwingen, aus einem Namen auf Verhalten zu
schliessen -- und das bricht beim naechsten Betriebsfall: Ein
Docker-All-in-One hat Container-Pfade, aber keinen zweiten Worker.
## Was sich sichtbar aendert (auf Windows nachgemessen)
Server-Status "Rippy arbeitet: auf diesem Rechner" statt Worker-Zaehler
"Platz fuer Rippy: 59.2 von 232 GB frei" statt "unbekannt"
Freigaben-Block faellt weg
Einstellungen kein Reiter "Worker"; Speicherziele BLEIBT (dort steht die
Ablage -- "wo will ich das hinspeichern" war die Frage),
aber mit Pfadfeld statt Container-Auswahl und ohne die
Maske zum Einhaengen
Anleitung kein docker, kein Encoding-Worker-Abschnitt, stattdessen
der echte Ordner (C:\Users\...\Videos\Rippy)
## Zwei Fehler, die dabei aufgefallen sind
* MEDIA_ROOT = "/app/media" war in main.py fest verdrahtet. shutil.disk_usage
warf unter Windows, die Liste blieb leer -- daher "unbekannt", obwohl auf
dem Laufwerk 59 GB frei waren. Eine Nichtauskunft, die wie eine Auskunft
aussieht. platz_orte() liefert die Orte jetzt je Betrieb, und ein noch
nicht angelegter Ordner faellt auf das naechste vorhandene Elternteil
zurueck.
* Der SSE-Schnappschuss enthielt den Server-Zustand NICHT, und der Waechter
schickt ihn nur alle 15 Sekunden. Nach jedem Neuladen stand deshalb bis zu
eine Viertelminute "unbekannt" da. Jetzt ist er im Schnappschuss, und das
UI uebernimmt ihn auch von dort.
Dazu: der Windows-Skip in test_api_smoke.py ist weg. Er stammte aus der Zeit
vor V2-4, als main.py fcntl brauchte; seit der Treiberwahl ueber den Port
laedt es auf beiden Plattformen (57 Routen, gemessen). Damit laufen 20 Tests
mehr auch lokal statt nur auf der Ampel.
Ampel lokal: 654 gruen, ruff sauber. Docker-Zweig durch Unit-Tests gedeckt,
am echten Container noch nicht gegengeprueft -- das kommt beim Deploy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
a5e64f8e56 |
fix(windows): der Deinstallierer liess 148 Dateien liegen und meldete Erfolg
Ampel / ampel (push) Successful in 54s
Beim ersten echten Deinstallieren auf dem Rechner des Commanders gemessen:
Uninstall-Eintrag weg
Autostart weg
Verknuepfungen weg
Dateien 149 NOCH DA (davon 148 in fenster\EBWebView\)
Gemeldet wurde: "Rippy wurde entfernt."
Zwei Ursachen, beide behoben:
## 1. WebView2 ueberlebt Rippy
WebView2 startet eigene Prozesse (msedgewebview2.exe: Renderer, GPU, Netz,
Crashpad). Stirbt Rippy, laufen sie als Waisen weiter und halten den
Zwischenspeicher offen -- acht davon liefen noch, als ich nachsah. shutil.rmtree
scheiterte an jeder gesperrten Datei.
fenster.helfer_beenden() beendet sie jetzt VOR dem Loeschen. Gefiltert wird
ueber den --user-data-dir in der Befehlszeile: msedgewebview2.exe benutzen
auch andere Programme, und die alle zu beenden waere ein Uebergriff -- der
Nutzer verloere die Fenster fremder Anwendungen. Ein Test haelt fest, dass
erst gefiltert und dann beendet wird.
## 2. Der Fehler wurde verschluckt
`except OSError: pass` in der Loeschschleife. Ein "Rippy wurde entfernt" ueber
einem halb geleerten Ordner ist eine Falschaussage -- genau die Sorte stiller
Fehlschlag, vor der AGENTS.md warnt. Jetzt wird gesammelt, was nicht ging, und
mit dem Grund genannt.
Dazu im Aufraeum-Skript: `rmdir /s /q` statt `rmdir`. Ein blosses rmdir
scheitert an JEDER verbliebenen Datei und laesst den ganzen Ordner stehen --
selbst wenn spaeter nur noch eine Sperrdatei uebrig ist.
Ampel lokal: 612 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ce4e7e6b8a |
feat(windows): das Setup richtet MakeMKV und HandBrake wirklich ein
Ampel / ampel (push) Successful in 54s
Commander: "handbrake und makemkv MUESSEN mitgeliefert werden oder waehrend
des Setups separat installiert werden! Ohne das ist das tool NICHT
einsatzfaehig."
Er hat recht, und die Luecke war real: katalog.py konnte die Werkzeuge
finden, beschaffen.py konnte sie holen -- das Setup rief beides nie auf. Wer
Rippy auf einem frischen Rechner installierte, bekam eine Oberflaeche, die
ihm sagte was fehlt, und keinen Weg, es zu aendern.
Die beiden gehen unterschiedliche Wege, und der Grund ist die LIZENZ:
* HandBrakeCLI ist GPL-2 -> mitgeliefert. Weitergabe ausdruecklich erlaubt,
solange Lizenztext und Quellverweis dabei sind (LIZENZ-HandBrake.txt liegt
daneben). Damit komprimiert Rippy auch ohne Internet.
* MakeMKV ist proprietaer -> beim Einrichten vom Hersteller geholt und mit
DESSEN Installer installiert. Weitergabe durch Dritte ist nicht erlaubt.
Das ist dieselbe Black-Box-Trennung wie in KONZEPT.md 6 und deckt sich mit
KONZEPT-V2.md 4.1 ("MakeMKV wird NICHT mitgeliefert").
Drei Regeln, die dazugehoeren:
1. Ein Setup meldet NIE Erfolg, waehrend ein Pflichtwerkzeug fehlt.
sicherstellen() liest den Bestand VOR und NACH dem Versuch und gibt
zurueck, was danach wirklich da ist -- nicht, dass es versucht wurde.
2. Ein nicht erreichbarer Download bricht die Installation nicht ab. Am
28.08.2026 antwortete makemkv.com mit HTTP 525. Rippy ist dann trotzdem
installiert und sagt im Klartext, was fehlt und wie man es beschafft.
3. Ohne HandBrake ist Rippy einsatzbereit (Rip laeuft, nur ohne Kompression),
ohne MakeMKV nicht. Nur Pflichtwerkzeuge entscheiden ueber "bereit".
Nachgemessen am fertigen Setup: HandBrake geloescht, installiert, nach DREI
Sekunden wieder da (74 MB, also aus dem Paket und nicht geladen), Meldung
"Rippy ist einsatzbereit. HandBrakeCLI 1.11.2 / MakeMKV 1.18.4".
RippySetup.exe waechst von 32,0 auf 56,4 MB.
fix(tools): der Fortschritts-Rueckruf bekommt immer einen Text
_datei_laden schickte `None` als Text, wenn sich nur der Fortschritt geaendert
hatte. Der erste echte Aufrufer starb daran:
can only concatenate str (not "NoneType") to str
Ein Rueckruf, den man nur mit einer nirgends dokumentierten Sonderbehandlung
benutzen kann, ist eine Falle. Jetzt kommt immer ein Satz, gedrosselt auf
jedes zehnte Prozent -- 24 MB in 256-KB-Haeppchen waeren sonst hundert Zeilen
im Protokoll. Zwei Tests halten beides fest.
Nicht im Repo: Die 70,9 MB HandBrakeCLI holt der BAU nach
dist/windows/vendor/ (nicht versioniert) -- dasselbe Muster wie der
vendor/-Ordner fuer MakeMKV auf der VM.
Ampel lokal: 607 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
60c0f93237 |
fix(windows): leere Symbole und der verschwundene Eintrag in "Installierte Apps"
Ampel / ampel (push) Successful in 54s
Beides vom Commander gemeldet, beides gemessen statt vermutet.
## 1. Weisses leeres Blatt statt Symbol
In deploy/worker-windows/rippy.ico steckte GENAU EIN Bild:
Bilder = 1
256x256 32 bit 5657 Bytes PNG
Windows holt sich fuer jede Stelle die passende Groesse -- 16x16 fuer die
Taskleiste, 32/48 fuer Desktop und Startmenue. Fehlt sie, skaliert die Shell
nicht zuverlaessig selbst, sondern zeigt das leere Blatt. Bei einer Datei mit
nur einem PNG-komprimierten 256er passiert das regelmaessig.
packaging/windows/icon.py baut die Datei jetzt mit neun Groessen
(16/20/24/32/40/48/64/128/256, LANCZOS beim Verkleinern). Nachgemessen an
der neuen EXE: 16x16 mit 138 Farben, 32x32 mit 264 -- kein leeres Blatt mehr.
build.py laesst ein unvollstaendiges Symbol gar nicht mehr durch, und
test_icon.py haelt die Pflichtgroessen fest (mit dem alten Stand rot gesehen).
## 2. Rippy stand nicht in "Installierte Apps"
Die Ursache war MEINE Testsuite. Gemessen:
Rippy.exe --nicht-starten -> Eintrag: DA (Rippy 2.0.0)
pytest -q -> Eintrag: WEG
test_installation_legt_die_dateien_an_... schrieb in den ECHTEN
Uninstall-Schluessel und loeschte ihn im finally wieder -- also auch den des
Commanders. Der Autostart-Eintrag ging denselben Weg.
Der Schluesselname stand als VORGABEWERT in jeder Signatur, und Vorgabewerte
wertet Python zur Definitionszeit aus: Ein Test konnte ihn gar nicht umbiegen.
Jetzt steht dort None, aufgeloest zur Laufzeit -- damit wirkt ein
monkeypatch.setattr(reg, "SCHLUESSEL", ...) ueberall.
Dazu ein Waechter (test_die_testsuite_ruehrt_den_ECHTEN_eintrag_nicht_an):
Er merkt sich den echten Zustand vorher, laesst eine vollstaendige
Test-Installation samt Autostart laufen und prueft danach, dass sich am
echten Eintrag nichts geaendert hat.
Das ist derselbe Fehler wie vorhin bei den Verknuepfungen, nur an anderer
Stelle. Die Regel gehoert in den Code, nicht in meinen Kopf: **Ein Test
raeumt nur weg, was er selbst angelegt hat.**
Ampel lokal: 590 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e1984dc543 |
docs: SAVEPOINT — Ampel wieder gruen, und warum sie fuenf Laeufe rot war
Ampel / ampel (push) Successful in 54s
Nachgetragen: die 28 Linux-Fehler, der schwerste davon (der API-Container waere auf der VM gar nicht hochgekommen), und die Lehre daraus — ein Test, der nur auf einer Plattform greift, ist eine halbe Zusage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ca212ff835 |
fix(test): die detection-Attrappe muss auch das PAKET-Attribut ersetzen
Ampel / ampel (push) Successful in 54s
Ampel-Lauf 186: Die 28 echten Linux-Fehler waren weg, uebrig blieben fuenf
— alle in meinem neuen Test:
TypeError: drive_status() missing 1 required positional argument
`from rippy.drives import detection` sieht zuerst im PAKET nach
(`_handle_fromlist` prueft `hasattr`) und greift erst dann auf sys.modules
zurueck. Auf Linux ist das echte Modul dort laengst gesetzt, weil es
importierbar ist — die Attrappe blieb wirkungslos, und der Test lief in die
echten Funktionen.
Unter Windows fiel das nicht auf: Dort gibt es das Attribut mangels fcntl
gar nicht, also griff sys.modules. Derselbe Test, zwei Plattformen, zwei
Ergebnisse — genau die Sorte Luecke, die dieser Test eigentlich schliessen
soll.
Jetzt werden beide ersetzt. Gegengeprueft: mit kaputter Durchreiche fuenf
Fehlschlaege, danach wieder gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
bfc0cada62 |
fix(windows): kein CMD-Fenster mehr — die EXE ist ein Fenster-Programm
Ampel / ampel (push) Failing after 55s
Commander: "Unterbinde das CMD Fenster was sich beim oeffnen mitoeffnet."
Mein erster Anlauf wollte die Konsole VERSTECKEN und war falsch, aus zwei
Gruenden:
* Eine Onefile-EXE ist ZWEI Prozesse (gemessen: PID 33172 mit 8 MB, PID
33580 mit 103 MB, beide Rippy.exe). Die Pruefung "haengt genau EIN Prozess
an der Konsole" war damit nie wahr, das Fenster blieb einfach stehen.
* Selbst wenn sie ginge, waere das Fenster erst nach ein bis zwei Sekunden
weg. Ein Aufblitzen ist kein "unterbunden".
Jetzt wird die Konsole gar nicht erst erzeugt: --windowed statt --console.
Nachgemessen am fertigen Bau -- PE-Subsystem 2 (GUI) statt 3 (Konsole), und
beim Klick auf die Desktop-Verknuepfung ist genau EIN sichtbares Fenster da:
die WinForms-Oberflaeche. Kein ConsoleWindowClass-Fenster von Rippy, weder
sichtbar noch unsichtbar.
Was dazugehoert, damit daraus kein stiller Fehlschlag wird:
* Ohne Konsole ist sys.stdout None. uvicorn schreibt auf stderr und waere
mitten im Start gestorben, ohne dass irgendwo etwas stuende. einstieg.py
haengt sich deshalb an die Konsole des Aufrufers (--status in PowerShell
schreibt weiter dorthin) oder leitet nach %LOCALAPPDATA%\Rippy\rippy.log um.
* Der Installer meldet sein Ergebnis in einem Fenster, wenn keine Konsole da
ist -- sonst saehe ein gescheiterter Doppelklick aus wie einer, der nichts
tut. Bei --still und mit Konsole bleibt es bei der Textzeile.
Gegengeprueft am laufenden Programm: /api/health, / und /api/capabilities
antworten mit 200, und rippy.log faengt auf, was vorher ins Leere ging.
fix(drives): linux.py reicht die detection-Funktionen wieder durch
Die Ampel war seit Lauf 181 rot -- vier Commits lang, und ich hatte es
uebersehen. Ursache:
docker/api/main.py:54
AttributeError: module 'rippy.drives.linux' has no attribute 'drive_status'
main.py holt sich beim Import device_discovery.drive_status. Der
Windows-Treiber hat die Funktion; linux.py hatte sie nicht mehr, seit
detection (und damit fcntl) bewusst nicht mehr oben importiert wird -- sonst
waere main.py unter Windows nicht ladbar. Unter Windows lief also alles, auf
Linux waere der API-Container gar nicht hochgekommen.
Behoben mit PEP 562 (__getattr__), demselben Kniff wie in store/__init__.py:
Der Import bleibt ohne fcntl moeglich, wer die Funktionen BENUTZT bekommt sie.
Dazu drei weitere Fehler, die nur auf Linux auftraten:
* katalog.py baute Windows-Pfade mit os.path.join. Auf dem Linux-Runner wurde
daraus C:\Program Files (x86)\MakeMKV/makemkvcon64.exe. Jetzt entscheidet
der Pfad selbst ueber den Trenner, nicht die rechnende Maschine.
* verknuepfungen.py nahm os.path.dirname fuer den Arbeitsordner einer .lnk.
Eine .lnk zeigt IMMER auf einen Windows-Pfad -- also ntpath.
* waechter.py las 0.0 als Zeitpunkt statt als "noch nie geprueft". Auf einer
frisch gestarteten Maschine ist time.monotonic() klein, also fiel die erste
Laufwerks-Abfrage aus. None statt 0.0.
Alle vier haben jetzt Tests, die auf JEDER Plattform laufen -- der
Treiber-Test schiebt eine detection-Attrappe unter, der Waechter-Test stellt
eine Uhr bei 0,5 s nach, die Pfad-Tests pruefen beide Zweige. Beim
Wegnehmen der Durchreiche rot gesehen (5 Fehlschlaege), danach wieder gruen.
Damit faengt die Ampel so etwas kuenftig, ohne dass es eine Linux-Umgebung
auf dem Entwicklungsrechner braucht.
Ampel lokal: 582 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e1577a4f47 |
docs: SAVEPOINT v4.0-rc — Windows ist ein echtes Programm
Ampel / ampel (push) Failing after 55s
Der SAVEPOINT stand seit V2-3 still, waehrend elf Commits dazukamen. Nachgetragen mit den gemessenen Zahlen: 562 gruen, RippySetup.exe 31,7 MB, WebView2 151.0.4129.107, 5 Encoder auf diesem PC. Zwei Dinge stehen bewusst gross drin, weil sie sonst verloren gehen: * Der Dienst, der /api/health mit 200 beantwortete und / mit 404 -- und warum ein %TEMP%-Verzeichnis kein Ort fuer eine Oberflaeche ist. * Der Test, der den PC des Commanders getroffen hat. Ein Test, der Spuren ausserhalb von tmp_path hinterlaesst, ist kein Test, sondern ein Eingriff. Und was NICHT bewiesen ist, steht als solches da: Ein echter Rip unter Windows ist nie gelaufen, die VM haengt elf Commits zurueck. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c01ef1081c |
feat(windows): echtes Fenster statt Browser-Tab (WebView2 statt Electron)
Auf die Frage des Commanders: "Warum nutzen wir fuer Windows weiterhin
einen Browser? Warum nutzen wir kein Electron oder sowas und machen daraus
einen echten Client."
Die erste Haelfte trifft zu und ist umgesetzt. Die zweite ist abgelehnt --
gemessen, nicht geschaetzt:
pywebview + WebView2 8,0 MB in der Bau-Umgebung, 2,7 MB in der EXE
Electron 150-210 MB, dazu eine zweite Laufzeitumgebung
Windows 11 bringt die WebView2-Laufzeit mit (hier 151.0.4129.107).
Nachgemessen statt angenommen, was sie rendert:
navigator.userAgent ...Chrome/151.0.0.0 Safari/537.36 Edg/151.0.0.0
fetch ja EventSource ja CSS Grid ja Pfeilfunktionen ja
Dasselbe Chromium, das auch in Electron steckt -- nur ohne es ein zweites
Mal mitzuschleppen. RippySetup.exe waechst von 29,0 auf 31,7 MB.
Drei Dinge gehoeren dazu, nicht als Beiwerk:
* Kein stiller Rueckfall auf MSHTML. Der alte IE-Renderer stellt die
React-Oberflaeche nicht dar. Fehlt WebView2, oeffnet Rippy den Browser
und SAGT warum -- ein leeres Fenster waere schlimmer als ein Tab.
* Fenster und Tray sind getrennte Prozesse. pystray belegt unter Windows
den Haupt-Thread, das Fenster braucht ihn genauso.
* Die eigene Konsole wird versteckt, eine geerbte nie. GetConsoleProcessList
unterscheidet beides: genau ein Prozess an der Konsole heisst, sie
gehoert uns. Ohne diese Unterscheidung haette "Rippy.exe --status" im
Terminal das Terminal des Nutzers verschwinden lassen.
fix(windows): Oberflaeche neben das Programm legen statt aus %TEMP% bedienen
Beim Nachsehen im laufenden Betrieb gefunden: Ein Dienst lief eine Stunde,
/api/health gab HTTP 200, / gab 404. Im Fenster stand {"detail":"Not Found"}.
Der Server war in Ordnung. Sein Entpack-Verzeichnis war es nicht:
_MEI000074b02 31 Eintraege, 5 Ordner -- kein ui, kein api
_MEI000082e82 44 Eintraege, 16 Ordner -- vollstaendig
Eine PyInstaller-Onefile-EXE liest bei JEDER Anfrage aus %TEMP%\_MEIxxxxx.
Ein Temp-Verzeichnis ist kein Ort fuer etwas, das eine Woche liegen bleibt.
Und der Ausfall ist der schlimmstmoegliche: Die API antwortet weiter, der
Dienst gilt als gesund, nur die Oberflaeche ist weg.
Genau davor warnte ROADMAP.md beim Bau-Verfahren ("one-dir statt one-file").
Die Abweichung bleibt, die Luecke wird geschlossen: Der Installer legt die
Oberflaeche neben das Programm, daemon._ui_pfad() nimmt diese Kopie zuerst.
Zeigt sich derselbe Ausfall an den API-Modulen, ist one-dir die Antwort.
Ampel: 562 gruen, ruff sauber. Fenster mit der echten Oberflaeche im
Bildschirmfoto nachgewiesen, nicht nur der Titel geprueft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
069fc46f44 |
fix(test): der Installations-Test hat die ECHTEN Verknuepfungen ueberschrieben
Ampel / ampel (push) Failing after 47s
WAS: `test_installation_legt_die_dateien_an_...` installiert in ein
tmp_path — legt aber seit dem Verknuepfungs-Einbau auch Symbole auf dem
ECHTEN Desktop und im ECHTEN Startmenue an. Die kennen kein tmp_path.
DIE FOLGE, vom Commander gemeldet: Er klickte auf das Desktop-Symbol und
bekam von Windows "Diese App kann auf dem PC nicht ausgefuehrt werden".
Nachgemessen zeigte die Verknuepfung auf
...\Temp\pytest-of-TobisPC\pytest-142\test_installation_..._0\installiert\Rippy.exe
— und dort liegt die 2-KB-ATTRAPPE aus dem Test, kein Programm. Windows
hatte voellig recht.
Der `finally`-Block raeumte nur Registry und Autostart auf; die
Verknuepfungen nicht, weil es sie beim Schreiben des Tests noch nicht gab.
Als sie dazukamen, wuchs der Test stillschweigend ueber sein tmp_path
hinaus. Ein Test, der Spuren ausserhalb seines Temp-Ordners hinterlaesst,
ist kein Test, sondern ein Eingriff.
BEHOBEN mit ZWEI Sicherungen statt einer:
1. verknuepfen=False — die Funktion legt gar keine an.
2. vk.alle_anlegen ist trotzdem ersetzt. Wer den Schalter spaeter
umdreht oder die Vorgabe aendert, kann damit trotzdem nichts am
echten System anrichten.
Dazu ein Waechter: test_installation_ruehrt_die_echten_verknuepfungen_
nicht_an prueft, dass die Anlege-Funktionen bei verknuepfen=False GAR
NICHT gerufen werden. Waere er frueher dagewesen, haette der Commander
keine kaputte Verknuepfung bekommen.
DER RECHNER IST REPARIERT: neu installiert, beide Verknuepfungen zeigen
wieder auf C:\Users\...\AppData\Local\Rippy\Rippy.exe (29 MB, existiert),
Programme+Features-Eintrag ist zurueck. Start ueber die Verknuepfung:
HTTP 200 nach 2 s, Worker TobisNicerPC mit cpu-x264/x265/av1 + vce/vce-av1,
MakeMKV 1.18.4 und HandBrake 1.11.2 gefunden.
Nebenbei ausgeschlossen, bevor die Ursache klar war: Die EXE selbst ist in
Ordnung (gueltiges x64-PE, MZ/PE-Kopf, 29 MB, Hash identisch mit dem
Installat), kein Mark-of-the-Web, kein Defender-Fund, Smart App Control
aus. Der Fehler lag nicht an der Datei, sondern daran, WORAUF gezeigt wurde.
GEMESSEN: ruff sauber, 522 Tests gruen + 15 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
99c05865af |
feat(windows): Desktop-Symbol und Startmenue-Eintrag — plus --oeffnen
Ampel / ampel (push) Failing after 47s
WAS: Das Setup legt jetzt eine Verknuepfung auf dem Desktop UND im
Startmenue an, die Deinstallation nimmt beide zurueck, und `--status`
zeigt, ob sie da sind. Abwaehlbar mit --keine-verknuepfungen.
WARUM (Commander-Meldung): "es gibt kein icon aufm desktop und keinen
startmenü eintrag". Zu Recht — ein Hintergrundprogramm ohne Symbol ist
praktisch unauffindbar: Es laeuft, aber es gibt keinen Weg hinein ausser
der Adresse im Kopf.
DIE VERKNUEPFUNG ZEIGT AUF --oeffnen, NICHT AUF --dienst. Ein Doppelklick
soll Rippy OEFFNEN; ob der Dienst laeuft, ist dem Nutzer egal und er kann
es auch nicht wissen. Zeigte das Symbol auf --dienst, startete jeder Klick
einen ZWEITEN Server, der am belegten Port scheitert — das Fenster blitzt
auf, im Browser passiert nichts. Ein Symbol, das beim zweiten Klick nichts
tut, ist schlimmer als keins.
`--oeffnen` sieht deshalb erst NACH, ob jemand antwortet, startet nur wenn
noetig, und wartet dann, bis der Dienst wirklich da ist — statt sofort
einen Browser auf eine tote Adresse zu schicken. Gemessen: erster Aufruf
2 s bis HTTP 200, zweiter Aufruf keine zusaetzlichen Prozesse.
DIE ORDNER KOMMEN AUS DER REGISTRY, nicht aus %USERPROFILE%. Wer OneDrive
benutzt oder seine Ordner verschoben hat, dessen Desktop liegt woanders —
die Verknuepfung landete dann in einem Ordner, den niemand sieht, und der
Nutzer suchte ein Symbol, das es nirgends gibt.
UEBER POWERSHELL statt pywin32: Eine .lnk ist ein binaeres Shell-Objekt.
pywin32 koennte es, waere aber 20 MB fuer vier Zeilen. Denselben Weg geht
der alte Worker-Installer schon ("nativ via PowerShell").
ZWEI FEHLER, DIE DIE TESTS GEFUNDEN HABEN:
- os.makedirs stand AUSSERHALB des try. Auf einem Laufwerk, das es nicht
gibt, wirft es — aus "Verknuepfung konnte nicht angelegt werden" waere
ein Abbruch der ganzen Installation geworden.
- Der Test dafuer nahm fest Z: als "gibt es nicht". Auf dem Commander-PC
ist Z: ein NETZLAUFWERK (X, Y, Z sind belegt). Ein fest verdrahteter
Laufwerksbuchstabe ist eine Annahme ueber eine fremde Maschine; jetzt
wird zur Laufzeit ein freier gesucht.
Und der Apostroph: In PowerShell wird er innerhalb einfacher
Anfuehrungszeichen durch VERDOPPELN maskiert. Ohne das braeche ein Pfad wie
C:\Users\O'Brien\... das Skript entzwei, und der Fehler saehe aus wie ein
Syntaxfehler in Rippy.
GEMESSEN, nach echter Installation nach %LOCALAPPDATA%\Rippy:
Desktop-Symbol -> Rippy.exe --oeffnen, mit Symbol und Beschreibung
Startmenue -> ebenso
Programme+Feat. -> Rippy 2.0.0, 100 MB, Deinstallieren funktioniert
Autostart -> "...\Rippy.exe" --dienst
Start ueber das Symbol: HTTP 200 nach 2 s
Worker: TobisNicerPC, Encoder cpu-x264/x265/av1, vce, vce-av1
GEMESSEN: ruff sauber, 521 Tests gruen + 15 uebersprungen (vorher 512).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f4a8d77598 |
feat(windows): Rippy arbeitet eigenstaendig — Rippen, Werkzeuge, Fähigkeiten
Ampel / ampel (push) Failing after 48s
WAS: Der Ablauf ist aus tasks.py heraus (ablauf.py, ohne Celery), die
LocalQueue wird bedient, ein Laeufer arbeitet Auftraege im selben Prozess
ab, und Rippy meldet sich mit gemessenen Faehigkeiten selbst als Arbeiter.
WARUM: "Rippy fuer Windows soll standalone funktionieren" (Commander). Bis
hierher konnte die Windows-App alles ANZEIGEN und nichts TUN — ein Rip waere
eingereiht worden und fuer immer liegengeblieben, weil niemand ihn holt.
DIE TRENNUNG: rip_disc hing an GENAU DREI Celery-Stellen in 234 Zeilen —
self.update_state, _transcode_queue, transcode_files.apply_async. Alle drei
sind Fragen der ZUSTELLUNG, nicht des Ablaufs. Sie sind jetzt Rueckrufe:
tasks.py reicht die Celery-Fassung herein, standalone.py die lokale. OHNE
Rueckruf komprimiert derselbe Prozess weiter — genau das, was ein
Ein-Prozess-Rippy braucht. Der Ablauf selbst ist Zeile fuer Zeile derselbe;
der Docker-Betrieb merkt vom Umbau nichts (Task-Namen, Argumente, Queues
unveraendert).
EINE ZUSTELL-STELLE statt drei: celery_client.abschicken() bedient alle
Auftragsarten. Vorher rief jede Stelle send_task selbst auf — der
Standalone-Betrieb haette an drei Stellen umgebogen werden muessen, beim
naechsten Auftragstyp an einer vierten.
WEITERER BLOCKER GEFUNDEN: ablauf.py holte detect_disc_type fest aus dem
LINUX-Treiber, in einem try/except. Unter Windows waere es damit IMMER None
gewesen und Rippen "hart verriegelt" — Rippy haette alles angezeigt und
nichts gerippt, ohne dass irgendwo ein Fehler stuende. Jetzt fragt es den
Treiber-Port.
WERKZEUGE: ripping.py und caps.py suchten nur im PATH. Auf dem Commander-PC
gemessen, vorher/nachher:
vorher check_makemkv_installed() -> False (obwohl installiert)
erkenne_encoder() -> nur CPU
nachher MakeMKV 1.18.4 C:\Program Files (x86)\MakeMKV\makemkvcon64.exe
HandBrake 1.11.2 ueber die API geholt, in 2,7 s
Encoder cpu-x264, cpu-x265, cpu-av1, VCE, VCE-AV1
107 Presets, Ryzen 7 9700X, 16 Kerne, avx512f
VCE ist die Hardwarebeschleunigung der Radeon — die hat Rippy auf diesem
Rechner vorher nie gesehen, weil es HandBrake gar nicht fand.
NEUE ROUTEN: GET /system/werkzeuge (was liegt wo, in welcher Fassung, gibt
es Neueres) und POST /system/werkzeuge/{name}/holen. HandBrake kommt
vollautomatisch von GitHub. MakeMKV wird NICHT mitgeliefert — Rippy laedt
die offizielle Datei und startet sie (Black-Box-Trennung, KONZEPT.md § 6).
makemkv.com antwortete beim Bauen mit HTTP 525; das wird im Klartext
gemeldet, und eine selbst geholte Datei bleibt moeglich.
HERZSCHLAG: /capabilities las die workers-Tabelle, die bisher nur der
Celery-Herzschlag fuellte. Im Standalone-Betrieb stand dort "0 Worker" und
die Encoder-Auswahl im UI blieb LEER — auf einem Rechner, der alles kann.
Jetzt meldet sich der Prozess selbst, mit dem, was caps.py MISST.
GEMESSEN, aus der fertigen EXE (29,0 MB):
bereit nach 1 s, keine Fehler im Log
Werkzeuge: beide gefunden, mit Version und Pfad
Worker: 1 (TobisNicerPC), 5 Encoder, 107 Presets
Die ganze Kette ist als Test festgehalten (test_kette.py): zustellen ->
einreihen -> Laeufer -> ablauf -> Job endet in einem EHRLICHEN Zustand.
Ohne Laufwerk geprueft, und das ist der wichtigere Fall: Ein Rip auf ein
totes Geraet muss zuegig scheitern, nicht auf "pending" haengenbleiben.
GEMESSEN: ruff sauber, 512 Tests gruen + 15 uebersprungen (vorher 489).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
288f9eeb8d |
feat(tools): Werkzeuge finden, holen und aktuell halten — Grundlage fuer Windows
Ampel / ampel (push) Failing after 47s
WAS: rippy/tools/ mit katalog.py (finden + Version) und beschaffen.py
(herunterladen, installieren, Update-Stand). ripping.py und caps.py fragen
den Katalog statt shutil.which.
DER BEFUND, DER DAS NOETIG MACHT: Der Bestand suchte AUSSCHLIESSLICH mit
shutil.which(). Im Container stimmt das — dort liegen beide Werkzeuge in
/usr/local/bin. Unter Windows findet es NICHTS, auch wenn alles installiert
ist: Windows-Programme liegen in Program Files, nicht im PATH. Am
28.08.2026 auf dem Commander-PC nachgesehen — MakeMKV lag unter
"C:\Program Files (x86)\MakeMKV\makemkvcon64.exe", which sah es nicht.
Fuer einen Rippy, der unter Windows eigenstaendig arbeiten soll, ist das der
Unterschied zwischen "laeuft" und "laeuft nicht". Vorher/nachher gemessen:
vorher check_makemkv_installed() -> False (obwohl installiert)
nachher check_makemkv_installed() -> True
build_makemkv_cmd()[0] -> C:\Program Files (x86)\MakeMKV\makemkvcon64.exe
SUCHREIHENFOLGE, mit Begruendung im Modul: eingestellt > Rippys eigener
Werkzeug-Ordner > PATH > bekannte Installationsorte > Uninstall-Zweig der
Registry. Rippys eigener Ordner steht VOR dem System, damit eine selbst
gepflegte Fassung eine alte Systeminstallation schlaegt.
VERSION: HandBrakeCLI kann --version. makemkvcon KANN DAS NICHT (usage.txt
kennt keinen solchen Schalter) — die Version kommt aus dem Uninstall-Zweig.
Dort steht bei MakeMKV zwar eine Version, aber InstallLocation ist LEER
(nachgesehen); deshalb wird der Ordner ersatzweise aus DisplayIcon bzw.
UninstallString abgeleitet. Der Test dafuer hat sofort einen Fehler
gefunden: '"C:\...\uninstall.exe" /S' liess sich mit einem blossen
strip('"') nicht aufloesen.
BESCHAFFUNG, an echter Hardware gemessen:
HandBrake GitHub-Release-API -> 1.11.2, HandBrakeCLI-1.11.2-win-x86_64.zip
geladen, entpackt, gestartet: meldet sich als 1.11.2. 2,8 s.
MakeMKV makemkv.com antwortete mit HTTP 525 (Cloudflare) — auch mit
Browser-Kennung. Dieselbe Sperre, die im Projekt schon den
Docker-Bau lahmlegt. Deshalb: Abruf wird versucht, ein
Fehlschlag wird im Klartext gemeldet, und eine selbst geholte
Datei laesst sich weiterhin verwenden. Ein Update-Server, der
heute antwortet, darf keine Startbedingung fuer morgen sein.
DREI FALLEN, GEGEN DIE ES TESTS GIBT:
- Die .sig-Datei liegt im Release direkt neben dem ZIP und ist 566 Bytes
gross. Wer nur nach "win" filtert, laedt sie.
- "1.9.2" ist als Zeichenkette GROESSER als "1.11.2". Ein Update-Hinweis, der
ab Version zehn dauerhaft in die Irre fuehrt — und genau dort ist
HandBrake gerade.
- Eine Fehlerseite kommt oft mit HTTP 200. Sie darf keine funktionierende
Installation ersetzen: erst laden, pruefen, dann tauschen.
Und: Ist die neueste Version unbekannt (Quelle tot), steht NICHT "Update
verfuegbar" da. Eine Nichtauskunft ist keine Aussage.
GEMESSEN: ruff sauber, 489 Tests gruen + 15 uebersprungen (vorher 456).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
059183c651 |
feat(windows): V2-4 fertig — RippySetup.exe, Taskmanager-Eintrag, Deinstallation
Ampel / ampel (push) Failing after 46s
WAS: Eine 28,6-MB-Datei, drei Betriebsarten. Doppelklick installiert,
`--dienst` laeuft im Hintergrund, `--deinstallieren` raeumt sich weg.
Dazu packaging/windows/build.py, das sie baut.
DIE ZWEI COMMANDER-WUENSCHE, GEMESSEN:
1. EINTRAG IM TASKMANAGER
ProcessName Rippy
Beschreibung Rippy — automatisches Ripping
Produkt Rippy
Version 2.0.0
Der Prozessname kommt vom Dateinamen (bei der Installation kopiert sich
die Datei als Rippy.exe), die Beschreibung aus einer eingebetteten
Versions-Ressource. Ohne die steht dort nichts, und Windows SmartScreen
sieht eine EXE ohne Herausgeberangabe genauso misstrauisch wie ein Mensch.
2. EINTRAG IN PROGRAMME UND FEATURES
Aus der Registry zurueckgelesen, nicht behauptet:
DisplayName Rippy
DisplayVersion 2.0.0
Publisher Rippy
InstallLocation <Zielordner>
UninstallString "<pfad>\Rippy.exe" --deinstallieren
EstimatedSize 29241 (KiB)
NoModify/NoRepair 1
HKCU statt HKLM: Der Eintrag erscheint genauso in "Apps & Features", die
Installation laeuft aber ohne UAC-Abfrage durch.
VOLLER KREISLAUF GEMESSEN: installieren -> Rippy.exe + rippy.ico liegen da,
Registry-Eintrag da, Autostart da; Dienst starten -> nach 1 s bereit, alle
sieben Endpunkte HTTP 200, Oberflaeche kommt; deinstallieren -> nach ~4 s
sind Datei, Ordner, Registry-Eintrag, Autostart UND das Aufraeum-Skript weg.
VIER FEHLER, DIE NUR DIE FERTIGE EXE ZEIGT:
1. rippy/store baute die Engine BEIM IMPORT, mit PostgreSQL als Vorgabe.
Im Windows-Paket gibt es psycopg2 bewusst nicht -> die EXE starb sofort.
Die Engine entsteht jetzt beim ersten Zugriff. Und der Fehler war
grundsaetzlicher als der fehlende Treiber: Ein Modul, das beim Import
schon eine Verbindung aufbaut, laesst sich gar nicht mehr umstellen —
store.verbinden() kaeme immer zu spaet.
2. celery_client baute den Broker-Client beim Import. Dasselbe Muster, und
`from celery_client import celery_client` loeste es sogar dann aus, wenn
nie ein Rip angestossen wird. Jetzt traege, mit KeinBroker als klarer
Ansage statt eines Importfehlers.
3. Die Selbstloeschung beim Deinstallieren ging als EIN Argument an cmd.
Python maskiert dabei die inneren Anfuehrungszeichen — cmd suchte einen
Dateinamen mit Backslashes davor, fand nichts und meldete nichts (>nul).
Registry und Autostart waren weg, die 28-MB-Datei blieb liegen: ein still
scheiternder Hintergrundprozess, wie ihn AGENTS.md beschreibt. Jetzt ein
Aufraeum-Skript, das 15-mal versucht (die Onefile-EXE laeuft als ZWEI
Prozesse; der Starter haelt die Datei nach dem Ende noch offen).
4. deinstallieren() raeumte den STANDARD-Ordner auf statt des tatsaechlichen.
Wer nach --ziel D:\Rippy installiert hatte, dessen Dateien waeren
geblieben, waehrend ein fremder Ordner angefasst worden waere. Der Pfad
steht in der Registry (InstallLocation) und wird jetzt dort gelesen.
WAS NOCH NICHT GEHT, ausdruecklich: Ein RIP laesst sich unter Windows noch
nicht anstossen — die Zustellung laeuft weiter ueber Celery. Die Umstellung
auf die LocalQueue ist V2-5. Oberflaeche, Laufwerks-Erkennung, Auswurf und
alles Lesende laufen.
GEMESSEN: ruff sauber, 456 Tests gruen + 15 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e5254c23f0 |
feat(daemon): V2-4 (Teil 3) — rippyd laeuft unter Windows, ohne Docker
Ampel / ampel (push) Failing after 46s
WAS: `rippyd` startet Rippy als EINEN Prozess — SQLite statt Postgres, LocalQueue statt Celery/Redis, In-Process-Bus statt Redis Pub/Sub, UI von FastAPI statt von nginx. Dieselbe API, dasselbe UI, derselbe Rip-Code. GEMESSEN (Windows 11, frische Python-3.12-Umgebung, ohne Docker/Postgres/Redis): bereit nach 1 s /api/health 200 /api/settings 200 /api/devices 200 /api/logs 200 /api/jobs 200 /api/capabilities 200 /api/health/vorraete 200 / (Weboberflaeche) 200 Ereignis-Waechter gesund: true, alter_sekunden: 0.5 SSE-Strom liefert `event: snapshot` mit vollem Zustand SQLite angelegt, WAL aktiv, alle Tabellen da ZWEI FEHLER, DIE NUR DER ECHTE LAUF ZEIGT: 1. STARLETTE REICHT LIFESPAN NICHT AN MOUNT-UNTERANWENDUNGEN WEITER. Das UI ruft /api/..., im Docker-Betrieb entfernt der nginx das Praefix. Ohne nginx haengt die API als Unteranwendung unter /api — und ihr startup_event lief nie. Folge: db.init_db() nicht ausgefuehrt, "no such table: settings", HTTP 500 auf /jobs, /settings, /logs, /capabilities, und der SSE-Strom blieb stumm. /api/health antwortete trotzdem mit 200, weil die Route keine Datenbank braucht — der Fehlschlag sah also aus wie "laeuft". Behoben ueber einen eigenen lifespan, der den der Unteranwendung mit ausloest. 2. EIN print() HAT DEN GANZEN START UMGEBRACHT. UnicodeEncodeError: 'charmap' codec can't encode characters -> ERROR: Application startup failed. Exiting. Windows-Konsolen arbeiten mit cp1252; Rippys Meldungen sind deutsch und voller Umlaute und Warnzeichen. Das Bittere: Es war nicht die WARNUNG, die den Start verhindert hat, sondern der VERSUCH, sie auszugeben. Behoben mit encoding="utf-8" UND errors="replace" — eine Ausgabe darf unter keinen Umstaenden etwas abbrechen koennen. BEFUND ZUR HARDWARE (kein Codefehler): Nach dem Auswurf-Test hat sich das USB-Laufwerk vom Bus getrennt. Windows kennt es nur noch als Karteileiche (Get-PnpDevice -Class CDROM -> Status "Unknown", Win32_CDROMDrive leer), weder ein Laufwerksbuchstabe noch \.\CdRom0..4 sind da. Die leere Geraeteliste ist damit die RICHTIGE Antwort. Bei bus-versorgten USB-Laufwerken ist das bekannt — der Auswurf zieht kurz mehr Strom. GEMESSEN: ruff sauber, 440 Tests gruen + 15 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9d95c95d79 |
feat(drives): Windows-Treiber an echter Disc BEWIESEN
Ampel / ampel (push) Failing after 46s
WAS: Die Hardware-Schicht des Windows-Treibers ist nicht mehr nur gebaut,
sondern gemessen. Messwerte stehen im Modul-Docstring, der gemessene
BD-50-Wert als Testfall in test_cdrom.py.
GEMESSEN (28.08.2026, LG BU40N extern, Laufwerk G:, echte BD-50):
drive_status() -> 4 (CDS_DISC_OK), sofort
IOCTL_CDROM_DISK_TYPE -> DATA_TRACK
IOCTL_DISK_GET_LENGTH_INFO -> 48 149 364 736 Bytes = 44,84 GiB
disc_status() -> 101 (CDS_DATA_1)
detect_disc_type() -> 'bluray'
device_info()['status'] -> 'ready'
device_info()['type'] -> 'bluray'
auswerfen_versuchen() -> True, 2,41 s
drive_status() davor / danach -> 4 / 1
DIE LETZTE ZEILE IST DER EIGENTLICHE BEWEIS. Der Auswurf haengt nicht am
Rueckgabewert des Steuercodes, sondern am ZUSTAND davor und danach: Die
Disc war drin (4) und ist danach draussen (1). Genau das fehlte in v1
monatelang — dort quittierte das ioctl Erfolg, die Schublade blieb zu, und
im Log stand "Disc ausgeworfen".
Die 44,84 GiB pruefen die Schwelle gleich mit: Eine BD-50 liegt knapp unter
den 55 GiB, ab denen classify() auf 'uhd' geht, und wird korrekt als
'bluray' eingeordnet. Wer die Schwelle senkt, macht aus jeder BD-50 eine
UHD — und Rippy waehlte dann das falsche Kompressions-Preset. Der Testfall
haelt den gemessenen Wert fest.
Offen bleibt allein die Ereignis-Erkennung (WM_DEVICECHANGE) — die wird
erst gebaut.
Nebenbei: Beim Eintragen der Messwerte sind drei echte NUL-Bytes in die
Datei geraten (eine Escape-Ebene im Schreib-Skript verschluckt). Python
lehnt so eine Datei rundheraus ab ("source code string cannot contain null
bytes") — im Binaermodus repariert und gegengeprueft.
GEMESSEN: ruff sauber, 430 Tests gruen + 14 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3d7f3b1680 |
feat(drives): V2-4 (Teil 2) — main.py laedt unter Windows, Treiber gemessen
Ampel / ampel (push) Failing after 46s
WAS: rippy/drives/__init__.py waehlt den Treiber fuer DIESE Maschine.
main.py und prescan.py fragen ihn, statt fest den Linux-Treiber zu
importieren. Damit ist der Blocker fuer den nativen Windows-Betrieb weg.
GEMESSEN, vorher (frische Python-3.12-Umgebung unter Windows):
>>> import main
ModuleNotFoundError: No module named 'fcntl'
GEMESSEN, nachher:
main.py laedt unter Windows: JA
Treiber: rippy.drives.windows
Routen: 61
Laufwerke: ['\\.\G:']
Der Import stand in rippy/drives/linux.py -> detection.py -> fcntl. Genau
dafuer gibt es den Port: Der Aufrufer sagt WAS, nicht WIE. test_treiberwahl.py
haelt ausserdem fest, dass BEIDE Treiber denselben Satz Funktionen anbieten —
sonst faellt eine fehlende erst im Betrieb als AttributeError auf, und zwar
auf der anderen Plattform.
DER TREIBER AN ECHTER HARDWARE (LG BU40N extern, Laufwerk G:, LEER):
GetDriveTypeW("G:\\") -> 5 (DRIVE_CDROM)
list_optical_devices() -> ['\\.\G:']
CreateFileW(r"\.\G:") -> Handle 380
CHECK_VERIFY2 bei leerem Laufwerk -> ERROR_NOT_READY (21)
drive_status() -> 1 (CDS_NO_DISC)
detect_disc_type() -> 'no_disc'
device_info()['status'] -> 'empty'
MEDIA_REMOVAL (entriegeln) -> Erfolg, 0,1 ms
EJECT_MEDIA -> Erfolg, 664,8 ms
Die 664,8 ms sind der Beleg, dass wirklich etwas passiert ist: Ein
abgelehnter Steuercode kommt in rund 2 ms zurueck.
BEFUND, DER EIN PLACEBO VERHINDERT HAT: IOCTL_STORAGE_LOAD_MEDIA (Schublade
einziehen) antwortet auf diesem Laufwerk mit ERROR_INVALID_FUNCTION. Bei
flachen und externen Laufwerken ist das die Regel. Deshalb gibt es bewusst
KEINE einziehen()-Funktion — ein Knopf "Schublade schliessen", der auf der
Haelfte aller Laufwerke stillschweigend nichts tut, waere genau die Sorte
Placebo, die in Etappe 19 aufgeraeumt wurde.
Dazu eine DeprecationWarning behoben: '\G' im Docstring ist keine gueltige
Escape-Sequenz und waere in einer kuenftigen Python-Version ein harter Fehler.
NOCH NICHT gemessen: das Verhalten MIT eingelegter Disc (Typ-Erkennung ueber
die Groesse, und ein Auswurf, bei dem wirklich etwas drin ist).
GEMESSEN: ruff sauber, 429 Tests gruen + 13 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b723fee3a2 |
feat(drives): V2-4 (Teil 1) — Windows-Laufwerkstreiber, ohne Hardware geprueft
Ampel / ampel (push) Successful in 45s
WAS: rippy/drives/windows.py mit denselben zwei Vertraegen wie die Linux-Fassung (auswerfen_versuchen gibt False zurueck, eject wirft). Dazu win_ioctl.py: die Win32-Steuercodes, HERGELEITET statt abgeschrieben. WARUM ctypes und nicht pywin32: 20 MB zusaetzliche Abhaengigkeit, die in jedes PyInstaller-Paket muss und bei jedem Python-Wechsel neu passen will. Gebraucht werden fuenf Funktionen aus kernel32 — die spricht ctypes direkt an. DIE STEUERCODES WERDEN AUSGERECHNET, NICHT ABGETIPPT: win_ioctl.py enthaelt das CTL_CODE-Makro aus winioctl.h als Funktion; jede Konstante wird daraus gebildet. test_win_ioctl.py rechnet das Ergebnis gegen die in der Microsoft-Dokumentation stehenden Zahlen. Grund: Ein falscher IOCTL-Code meldet kein "unbekannter Befehl", sondern ERROR_INVALID_FUNCTION — und das liest sich wie "dieses Laufwerk kann das nicht". Man sucht dann am Geraet statt an einer Zahl. AGENTS Regel D, ernst genommen. DIE ZWEI PORT-REGELN GELTEN AUCH HIER: - Auswerfen heisst entriegeln, auswerfen, NACHSEHEN. Unter Linux wurde am 26.07.2026 gemessen, dass ein verriegeltes Laufwerk den Auswurf mit ERFOLG quittiert und nichts tut. IOCTL_STORAGE_EJECT_MEDIA meldet ebenfalls nur die Annahme des Befehls — also wird auch hier nachgesehen (CHECK_VERIFY2 muss ERROR_NOT_READY liefern). - Ein unzugaengliches Laufwerk ist UNBEKANNT, nicht leer. device_info kennt deshalb drei Zustaende, nicht zwei. EIN FEHLER, DEN DIE TESTS SOFORT GEFANGEN HABEN: Win32Fehler setzte self.winerror VOR super().__init__(). Nachgemessen: Ein zweiargumentiges OSError.__init__(code, text) setzt winerror wieder auf None. Damit erkannte _medium_da ERROR_NOT_READY nicht mehr, hielt jedes leere Laufwerk fuer einen echten Fehler und meldete "unbekannt" statt "leer". Vier Tests rot, Ursache in einer Minute gemessen statt geraten. ⚠️ WAS HIER NICHT BEWIESEN IST: dass echte Hardware sich so verhaelt. Das Laufwerk ist derzeit nirgends angeschlossen. Der Ablauf, die Fehlerunterscheidung und die Typ-Zuordnung sind gegen ein nachgebautes Laufwerk geprueft (51 Tests, laufen auf jeder Plattform) — die Hardware- Schicht gilt bis zu einer Messung als GEBAUT, nicht als BEWIESEN. GEMESSEN: ruff sauber, 400 Tests gruen + 4 uebersprungen (vorher 378). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4337253797 |
docs: SAVEPOINT v4.0-beta — V2-0 bis V2-3 stehen, Laufwerk fehlt an der VM
Ampel / ampel (push) Successful in 41s
Vier Etappen gebaut und deployt, Polling ist weg (121 Anfragen/min je Tab -> ~2 einmalige Abrufe), SSE-Strom live belegt. Wichtig fuer die naechste Sitzung: Das optische Laufwerk haengt NICHT mehr an der VM. Rippy laeuft als reine Komprimier-Maschine. Und der Vorfall, der das aufgedeckt hat, ist dokumentiert — ein devices:-Eintrag in compose ist eine Startbedingung, und die hat api, worker und ui stillgelegt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |