chore(main): Der Windows-Anteil ist raus — Entscheid 7, § 11 umgesetzt
Ampel / ampel (push) Failing after 41s

Teil A (ganze Dateien): windows_app, fenster, setup_fenster,
einrichtung, standalone, daemon, drives/windows + win_ioctl,
platform/verknuepfungen + win_registry, tools/einrichten,
rip/audio_cd + musicbrainz_cd, queue/ (lokal + laeufer),
packaging/windows/, test_keine_container_reste — samt Tests.
Bilanz: +51 / -10.039 Zeilen in 48 Dateien.

Bleibt TROTZ § 11.2-Listung (Importe gemessen, nicht geraten):
winlauf (OHNE_FENSTER nutzt der Worker; Teil B laeuft nativ Windows),
dateiangaben/katalog/beschaffen (ripping + api importieren sie),
makemkv_aufruf KOMPLETT (ablauf.py ruft key_setzen_und_pruefen;
Registry-Zweige braucht der Remote-Worker), bus/waechter (bedient
Jobs/Logs/Laufwerke der API), die nativ-Pfadzweige (Teil B).

Teil C: drives/treiber() liefert unter Windows den Klartext-Platzhalter
kein_windows.py (Importe/Testsammlung auf Entwicklungs-PCs bleiben
heil, BENUTZUNG wirft mit Verweis auf v5); ripping.rip_cd ohne
Windows-Zweig; betrieb.windows_laufwerke + Aufrufer raus;
/worker-setup/windows* bleibt (Teil B, Entscheid 8).

Lokal: Ruff gruen, 658 Tests gruen (vorher 977 — die Differenz sind
die Windows-Tests, deren Gegenstand mitgegangen ist). Das Wissen liegt
vollstaendig in Rippy v5 (rippy-windows/), mit denselben Testfaellen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-30 19:18:10 +02:00
co-authored by Claude Fable 5
parent 6f230cbd7f
commit cedab61ddf
49 changed files with 105 additions and 10040 deletions
+4 -24
View File
@@ -1507,19 +1507,9 @@ async def storage_targets():
"free_gb": frei_gb,
})
# Die Laufwerke — nur nativ. Im Container gäbe es nichts zu holen.
if frei_blaettern():
from rippy import betrieb
bekannt = {z["path"].rstrip("\\/").lower() for z in ziele}
for laufwerk in betrieb.windows_laufwerke():
if laufwerk.rstrip("\\/").lower() in bekannt:
continue
ziele.append({
"name": "Laufwerk %s" % laufwerk.rstrip("\\/"),
"path": laufwerk,
"is_mount": False,
"free_gb": platz(laufwerk),
})
# Der Laufwerksbuchstaben-Block stand hier für die Windows-
# Standalone-App — sie ist mit v5 ein eigenes Produkt (KONZEPT-
# WINDOWS.md Entscheid 7); im Container gab es hier nie etwas.
return ziele
return await asyncio.to_thread(sammle)
@@ -2145,7 +2135,7 @@ async def browse(path: str = ""):
einem anderen Laufwerk — und genau dort ist Platz für 100 GB Rohdaten.
"""
if not path.strip():
laufwerke = betrieb_laufwerke() if frei_blaettern() else []
laufwerke = []
if laufwerke:
return {"path": "", "parent": None, "dirs": laufwerke, "files": []}
path = medien_wurzel()
@@ -2203,16 +2193,6 @@ def eltern_von(pfad: str, wurzel: str = None, frei: bool = None) -> Optional[str
return "" if oben == pfad else oben
def betrieb_laufwerke() -> list:
"""Die Laufwerke als Ordner-Einträge für die oberste Ebene des Browsers."""
from rippy import betrieb
try:
return [{"name": "Laufwerk " + laufwerk.rstrip("\\/"), "path": laufwerk}
for laufwerk in betrieb.windows_laufwerke()]
except Exception: # noqa: BLE001
return []
class MkdirRequest(BaseModel):
path: str
name: str
-3
View File
@@ -77,6 +77,3 @@ def test_die_wurzel_kommt_aus_dem_betrieb_und_ist_nie_leer():
assert main.medien_wurzel()
def test_laufwerke_ohne_windows_sind_eine_leere_liste():
"""Auf dem Linux-Runner der Ampel darf das kein Fehler sein."""
assert isinstance(main.betrieb_laufwerke(), list)
+4 -56
View File
@@ -1114,66 +1114,14 @@ def rip_video(device_path: str, disc_id: str, disc_type: str = "dvd", progress_c
log_cb=log_cb)
def _rip_cd_windows(device_path: str, disc_id: str, progress_cb=None,
output_dir: str = None, auswerfen: bool = True) -> dict:
"""Audio-CD unter Windows: selbst lesen, mit FLAC kodieren.
Die Rechnung steht in `rippy.rip.audio_cd` (ohne Laufwerk pruefbar), das
Lesen im Windows-Treiber (der einzige Ort mit Win32-Aufrufen).
"""
from rippy.drives import windows as win
from rippy.rip import audio_cd
class Leser:
toc = staticmethod(win.audio_toc)
@staticmethod
def spur(geraet, start, sektoren, schreiben, fortschritt=None):
return win.audio_spur_lesen(geraet, start, sektoren, schreiben,
fortschritt)
# Erst die Disc bei MusicBrainz nachschlagen — daraus kommen sowohl der
# Ordnername als auch die Titel. Ohne Treffer heissen die Dateien
# `Track 01.flac`; das ist keine Ausrede, sondern die Wahrheit ueber eine
# Disc, die niemand kennt.
from rippy.rip import musicbrainz_cd
try:
spuren = win.audio_toc(device_path)
antwort = musicbrainz_cd.abfragen(
musicbrainz_cd.kennung_aus_spuren(spuren))
except Exception: # noqa: BLE001
antwort = None
tags = musicbrainz_cd.tags_je_spur(antwort)
ordner = musicbrainz_cd.album_ordner(antwort)
ziel = output_dir or os.path.join(RIP_OUTPUT_DIR, "cd", ordner or disc_id)
ergebnis = audio_cd.rippen(device_path, ziel, Leser(),
werkzeuge.finden("flac"),
fortschritt=progress_cb, tags_je_spur=tags)
if auswerfen and ergebnis.get("status") == "success":
wirf_disc_aus(device_path)
return ergebnis
def rip_cd(device_path: str, disc_id: str, progress_cb=None, output_dir: str = None,
auswerfen: bool = True) -> dict:
"""Rippt eine CD mit abcde (FLAC). `auswerfen` = Einstellung „Automatischer
Auswurf" (abcde macht das selbst per -x)."""
# ── Windows geht einen eigenen Weg (Befund 29.08.2026) ─────────────
#
# `abcde` und `cdparanoia` sind Linux-Werkzeuge; es gibt sie fuer Windows
# nicht, und ein Nachbau ihrer Shell-Logik waere ein zweites Projekt.
# Windows bietet stattdessen zwei Steuercodes an, mit denen sich eine
# Audio-CD direkt lesen laesst — `rippy.rip.audio_cd`, kodiert wird mit
# dem FLAC-Encoder aus dem Werkzeug-Katalog.
#
# Das war die letzte offene Luecke des Windows-Betriebs; sie stand seit
# Wochen so im SAVEPOINT.
if os.name == "nt":
return _rip_cd_windows(device_path, disc_id, progress_cb, output_dir,
auswerfen)
Auswurf" (abcde macht das selbst per -x).
Der Windows-Weg (Roh-Sektoren + FLAC) ist mit der Standalone-App nach
Rippy v5 gewandert (KONZEPT-WINDOWS.md Entscheid 7, dort § 6.3) —
diese Fassung rippt CDs nur noch im Container, mit abcde."""
if not check_abcde_installed():
return {
"status": "error",