Files
abdulive/docs/decisions/ADR-0007-native-windows.md
ZeasyAlexandClaude Opus 5 5f6301268a Native Windows-Auslieferung statt Container
Docker faellt weg. Das Projekt hat keine npm-Abhaengigkeiten und Node fuehrt
TypeScript direkt aus - der Container schrieb nur noch drei Programme fest,
und dafuer braucht es keine Virtualisierung, sondern einen Ordner.

Der Anlass war, dass ADR-0004 zwar `docker compose pull` versprach, es aber
nie ein veroeffentlichtes Image gab. Wer installieren wollte, musste
whisper.cpp aus dem Quelltext uebersetzen.

Plattformschicht (packages/core/src/platform.ts):
- Programme werden neben der Anwendung gesucht, nicht im PATH
- Daten unter %LOCALAPPDATA%, damit Installation und Update ohne
  Administratorrechte auskommen
- Schriften mitgeliefert: ohne fontsdir setzt libass wortlos eine
  Ersatzschrift ein - gemessen 720 statt 826 Pixel Zeilenbreite

Windows-Eigenheiten, an echtem ffmpeg 8.1 durchprobiert:
- Pfade im Filtergraphen brauchen alle drei Schritte (Vorwaertsschraegstriche,
  escapter Doppelpunkt, Anfuehrungszeichen); je zwei davon reichen nicht
- `nice` gibt es nicht, ersetzt durch os.setPriority

Auslieferung:
- scripts/build-package.ts baut das Paket, laeuft auch unter Linux
- install.ps1 ohne Administratorrechte, Autostart ueber den Aufgabenplaner
- update legt neue Fassungen bereit; eingespielt wird beim naechsten Start,
  nie mitten im Stream
- ZIP lesen und schreiben ohne Abhaengigkeiten (packages/core/src/zip.ts)

Kein `irm ... | iex`-Einzeiler: git.zeasy.dev antwortete instanzweit mit einer
Umleitung auf /user/login, und `iex` fuehrte die Anmeldeseite als PowerShell
aus. Der Updater verfolgt Umleitungen jetzt nicht und hat keine erfundene
Vorgabeadresse mehr.

Ausserdem: backup.ps1 war an zwei Stellen dokumentiert, aber nie geschrieben.

ADR-0007 ersetzt ADR-0004. 84 Tests, Typpruefung sauber.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AhVSfMFdWnXKCv5JfLnArZ
2026-09-09 17:39:26 +02:00

6.8 KiB

ADR-0007: Native Windows-Anwendung statt Container

Status: akzeptiert · Datum: 2026-09-09 · ersetzt: ADR-0004

Kontext

ADR-0004 hat den Betrieb in Docker festgelegt und dabei zwei Dinge versprochen: „Die einzige Voraussetzung auf dem Stream-PC ist Docker Desktop und OBS" und „Updates sind ein docker compose pull && up -d".

Das zweite Versprechen wurde nie eingelöst. Es gab keine CI, kein veröffentlichtes Image, und image: abdulive/toolkit:dev zeigte auf nichts. Wer das Toolkit installieren wollte, musste docker compose build ausführen — und damit whisper.cpp aus dem Quelltext übersetzen, gut zehn Minuten, mit allen Fehlermöglichkeiten eines Compilers. Für Abdu ist das nicht zumutbar.

Beim Nachdenken über die Reparatur fiel auf, dass die Voraussetzung selbst teurer ist als der Nutzen. Die Kosten stehen ehrlich in ADR-0004: die WSL2-VM belegt im Leerlauf ein bis zwei GB RAM, und Intel QSV ist aus WSL2 heraus nicht verlässlich erreichbar, also bleibt CPU-Encoding — beides auf einem Rechner, der gleichzeitig streamt.

Dem steht ein Umstand gegenüber, der die ursprüngliche Begründung untergräbt: Das Projekt hat keine npm-Abhängigkeiten, und Node führt TypeScript direkt aus (ADR-0001). Es gibt nichts zu bauen. Der Container schrieb also nur noch drei Programme fest — Node, ffmpeg, whisper.cpp — und dafür braucht es keine Virtualisierung, sondern einen Ordner.

Entscheidung

Das Toolkit wird als native Windows-Anwendung ausgeliefert. Ein Ordner mit allem darin, ein PowerShell-Installer, ein Updater. Kein Docker, kein WSL2.

abdulive/
  node.exe          portable Node-Laufzeit
  bin/              ffmpeg, ffprobe, whisper-cli und ihre DLLs
  fonts/            DejaVu Sans (im Repo mitgeliefert, siehe unten)
  app/              die TypeScript-Quellen, unverändert
  abdulive.cmd      Kommandozeile
  launcher.ps1      vom Aufgabenplaner gestartet
  version.json

Gebaut wird mit node scripts/build-package.ts — lädt die drei Bausteine bei ihren Herausgebern, packt alles zusammen, schreibt Prüfsummen. Das läuft auch in der CI unter Linux.

Begründung

  • Abdu baut nichts. Herunterladen, install.ps1, fertig. Kein Docker Desktop, kein .wslconfig, kein Compiler.
  • Ein bis zwei GB RAM stehen wieder dem Stream zur Verfügung.
  • Hardware-Encoding wird erreichbar. Der mitgelieferte ffmpeg ist mit --enable-libvpl, --enable-amf und --enable-ffnvcodec gebaut; QSV und AMF sind nativ ansprechbar. Genutzt wird das noch nicht — aber es ist jetzt möglich, und aus WSL2 heraus war es das nicht.
  • Die Pfadübersetzung entfällt. mapReplayPath musste OBS' Windows-Pfade auf /replay abbilden; laut Übergabe die Stelle, an der die Clip-Pipeline am ehesten scheitert. Nativ sieht die Anwendung denselben Pfad wie OBS.
  • Kein Kopieren über die WSL2-Brücke mehr. ADR-0004 Punkt 2 verbot das Verarbeiten auf dem Bind-Mount, weil es „ein Vielfaches der Zeit" kostet. Diese Klasse von Fehlern gibt es nicht mehr.

Konsequenzen — was dabei zu beachten ist

1. Programme werden neben der Anwendung gesucht, nicht im PATH. packages/core/src/platform.ts löst auf: erst die Umgebungsvariable (FFMPEG_BIN …), dann bin/, dann der PATH. Die Umgebungsvariable steht bewusst vorn — eine kaputte Mitlieferung lässt sich so ohne Neuinstallation überschreiben.

2. Schriften müssen mitgeliefert werden. Im Container kam DejaVu Sans aus fonts-dejavu-core und libass fand sie über fontconfig. Windows hat DejaVu nicht. Fehlt der Ordner, setzt libass wortlos eine Ersatzschrift ein — gemessen: dieselbe Zeile wird 720 statt 826 Pixel breit, ohne jede Fehlermeldung. Deshalb liegen die Schriftdateien unter assets/fonts/ im Repo, und assFilter setzt fontsdir.

3. Windows-Pfade im ffmpeg-Filtergraphen brauchen drei Schritte. C:\Users\… enthält einen Doppelpunkt (trennt Filteroptionen) und Rückwärtsschrägstriche (ffmpeg-Escapes). An echtem ffmpeg 8.1 durchprobiert:

Form Ergebnis
ass=filename=C:/x/s.ass No option name near '/x/s.ass'
ass=filename=C\:/x/s.ass ebenso
ass=filename='C:/x/s.ass' ebenso
ass=filename='C\:/x/s.ass' läuft

Anführungszeichen allein schützen den Doppelpunkt nicht, das Escape allein auch nicht. filterPath() macht alle drei Schritte, mit Tests.

4. nice gibt es unter Windows nicht. Ersetzt durch os.setPriority auf die Kind-PID. Das kann beides, braucht keinen Hilfsprozess davor, und das Zurückstufen darf jeder Benutzer.

5. Daten liegen unter %LOCALAPPDATA%\abdulive, nicht neben dem Programm. Ein Update tauscht den Programmordner komplett aus; die Datenbank darf dabei nicht mitgehen. LOCALAPPDATA und nicht ProgramData, weil die Anwendung als angemeldeter Benutzer läuft — dieselbe Sitzung wie OBS. Damit braucht weder Installation noch Update Administratorrechte. Genau daran scheitern Aktualisierungen sonst auf einem Rechner, an dem gerade gestreamt wird.

6. Autostart über den Aufgabenplaner, nicht als Windows-Dienst. Ein Dienst liefe unter SYSTEM in einer anderen Sitzung als OBS, bräuchte Administratorrechte und ein zusätzliches Wrapper-Programm (nssm, WinSW). Die Aufgabe abdulive startet bei der Anmeldung, versteckt, mit Neustart nach zwei Minuten bei Absturz.

7. Updates werden bereitgelegt, nicht sofort eingespielt. Windows sperrt die Dateien laufender Programme — node.exe lässt sich nicht ersetzen, solange es läuft. abdulive update prüft das Manifest, lädt das Paket, prüft die SHA256 und legt es unter pending/ ab. launcher.ps1 tauscht beim nächsten Start aus, bevor irgendetwas startet. Damit gibt es nie einen halb ausgetauschten Ordner, und mitten im Stream wird nichts angefasst. Schlägt der Tausch fehl, läuft die alte Fassung weiter.

Kosten, ehrlich benannt:

  • Die Umgebung ist nicht mehr festgeschrieben. Der Container garantierte „kein läuft bei mir". Jetzt garantiert das nur noch der Paketinhalt — Node, ffmpeg und whisper sind mitgeliefert, aber Windows selbst ist es nicht.
  • Kein Code-Signing. SmartScreen warnt beim ersten Start vor einem unbekannten Herausgeber. Ein Zertifikat kostet Geld; bis dahin muss man einmal auf „Trotzdem ausführen" klicken. Steht in offene-punkte.md.
  • Windows-only. Ein Linux-Paket gibt es nicht mehr. Für ein Werkzeug, das neben OBS auf einem Windows-Stream-PC läuft, ist das kein Verlust — die Pfade in platform.ts kennen den Linux-Fall aber weiterhin, und die Tests laufen dort.
  • Drei Bausteine wollen gepflegt werden. Node, ffmpeg und whisper.cpp aktualisieren sich nicht von selbst. build-package.ts holt jeweils die aktuelle Ausgabe und schreibt Name und Prüfsumme nach version.json — was drin ist, lässt sich also nachlesen.