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
This commit is contained in:
1 parent
71dd660d01
commit
5f6301268a
41 files changed
+2254
-312
No files matched your search
@@ -37,7 +37,8 @@ Einrichtungs-Assistenten unter `http://localhost:4792/setup`, **nicht** in einer
|
||||
| `bits:read` | Bits-Events | Broadcaster |
|
||||
|
||||
Der Autorisierungs-Flow läuft über den Einrichtungs-Assistenten:
|
||||
`docker compose up -d`, dann `http://localhost:4792/setup` öffnen und je einmal
|
||||
`Start-ScheduledTask -TaskName abdulive`, dann `http://localhost:4792/setup`
|
||||
öffnen und je einmal
|
||||
für Broadcaster- und Bot-Konto auf „Mit Twitch verbinden" klicken. Die
|
||||
Redirect-URI, die in beiden Twitch-Apps hinterlegt sein muss, lautet
|
||||
`http://localhost:4792/callback`.
|
||||
|
||||
+82
-52
@@ -1,34 +1,51 @@
|
||||
# Setup
|
||||
|
||||
Alles läuft in Containern. Auf dem Rechner selbst wird nur Docker Desktop und
|
||||
OBS gebraucht — ffmpeg, whisper.cpp, Node und pnpm stecken im Image.
|
||||
Begründung: [ADR-0004](decisions/ADR-0004-docker.md).
|
||||
Eine native Windows-Anwendung. Node, ffmpeg, whisper.cpp und die Schriften
|
||||
sind mitgeliefert — vorausgesetzt wird nur OBS.
|
||||
Begruendung: [ADR-0007](decisions/ADR-0007-native-windows.md).
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
| Was | Version | Wo |
|
||||
| Was | Version | Anmerkung |
|
||||
|---|---|---|
|
||||
| Docker Desktop | aktuell, WSL2-Backend | Host |
|
||||
| OBS Studio | 30+ mit obs-websocket v5 | Host |
|
||||
| Git | beliebig | Host |
|
||||
| Windows | 10 oder 11, 64 Bit | |
|
||||
| OBS Studio | 30+ mit obs-websocket v5 | derselbe Rechner |
|
||||
|
||||
Alles Weitere (Node 22, pnpm, ffmpeg mit libx264/libass, whisper.cpp,
|
||||
Sprachmodell) liefert das Image.
|
||||
Mehr nicht. Kein Docker, kein WSL2, kein Compiler, kein Git.
|
||||
|
||||
## WSL2 begrenzen
|
||||
## Installieren
|
||||
|
||||
Docker Desktop startet eine VM, die sich ohne Grenze so viel RAM nimmt, wie sie
|
||||
kriegen kann. Auf einem Stream-PC ist das nicht egal. `%USERPROFILE%\.wslconfig`
|
||||
anlegen:
|
||||
Das Paket herunterladen, entpacken, darin `install.ps1` ausfuehren:
|
||||
|
||||
```ini
|
||||
[wsl2]
|
||||
memory=4GB
|
||||
processors=4
|
||||
swap=2GB
|
||||
```powershell
|
||||
Expand-Archive .\abdulive-0.5.0-win-x64.zip -DestinationPath .\abdulive
|
||||
.\abdulive\install.ps1
|
||||
```
|
||||
|
||||
Danach `wsl --shutdown` und Docker Desktop neu starten.
|
||||
Liegt das Paket an einer Adresse **ohne** Anmeldung, holt der Installer es
|
||||
selbst:
|
||||
|
||||
```powershell
|
||||
.\install.ps1 -Quelle https://.../abdulive
|
||||
```
|
||||
|
||||
> Ein `irm … | iex`-Einzeiler funktioniert mit `git.zeasy.dev` nicht. Die
|
||||
> Instanz verlangt fuer alles eine Anmeldung und antwortet mit einer Umleitung
|
||||
> auf `/user/login`; `iex` fuehrt dann die Anmeldeseite als PowerShell aus.
|
||||
> Wo die Pakete liegen sollen, ist noch zu entscheiden.
|
||||
|
||||
Der Installer laeuft **ohne Administratorrechte**:
|
||||
|
||||
| Was | Wohin |
|
||||
|---|---|
|
||||
| Programm | `%LOCALAPPDATA%\Programs\abdulive` |
|
||||
| Daten, Datenbank, Clips | `%LOCALAPPDATA%\abdulive` |
|
||||
| Autostart | Aufgabe `abdulive`, bei der Anmeldung |
|
||||
| Update-Pruefung | Aufgabe `abdulive-update`, taeglich 05:00 |
|
||||
|
||||
Beim ersten Start warnt Windows SmartScreen vor einem unbekannten Herausgeber —
|
||||
das Paket ist nicht signiert. Einmal auf *Weitere Informationen* → *Trotzdem
|
||||
ausfuehren*.
|
||||
|
||||
## Twitch-Apps anlegen
|
||||
|
||||
@@ -64,60 +81,73 @@ im Einrichtungs-Assistenten einfügen (siehe unten). Begründung:
|
||||
## Erste Einrichtung
|
||||
|
||||
```powershell
|
||||
Copy-Item config\config.example.yaml config\config.yaml
|
||||
|
||||
docker compose build
|
||||
docker compose run --rm toolkit check-env # ffmpeg, whisper, Modell, Volumes, Layout
|
||||
docker compose run --rm toolkit fetch-model # Sprachmodell, ca. 465 MB
|
||||
docker compose up -d
|
||||
abdulive check-env # ffmpeg, whisper, Schriften, Verzeichnisse, Layout
|
||||
abdulive fetch-model # Sprachmodell, ca. 465 MB
|
||||
Start-ScheduledTask -TaskName abdulive
|
||||
```
|
||||
|
||||
Dann **http://localhost:4792/setup** öffnen. Dort werden nacheinander
|
||||
eingetragen:
|
||||
Dann **http://localhost:4792/setup** oeffnen: Client-ID und Secret der beiden
|
||||
Twitch-Apps eintragen und je einmal auf „Mit Twitch verbinden" klicken.
|
||||
|
||||
1. Client-ID und Client-Secret der beiden Twitch-Apps
|
||||
2. „Mit Twitch verbinden" für Broadcaster- und Bot-Konto (OAuth)
|
||||
3. obs-websocket-Passwort
|
||||
4. später: Discord-Webhook, Bluesky-Zugang
|
||||
|
||||
Alles landet verschlüsselt im Docker-Volume, nicht in einer Datei im Projekt.
|
||||
|
||||
> **Diese Seite zeigt Zugangsdaten. Nicht öffnen, während gesendet wird.**
|
||||
|
||||
Eine `.env` ist optional und enthält nur noch den Pfad zum Wiedergabepuffer
|
||||
und das Log-Level (Vorlage: `.env.example`).
|
||||
|
||||
## Facecam einmessen
|
||||
|
||||
Einmalig, sobald eine Replay-Datei vorliegt:
|
||||
## Layout einmessen
|
||||
|
||||
```powershell
|
||||
docker compose run --rm toolkit calibrate
|
||||
abdulive calibrate
|
||||
```
|
||||
|
||||
Legt einen Frame als PNG in `data/clips/_calibration/` ab, fragt die vier
|
||||
Crop-Werte ab und schreibt sie nach `config/layout.yaml`.
|
||||
Legt einen Frame als PNG unter `%LOCALAPPDATA%\abdulive\clips\_calibration`
|
||||
ab und rendert die 9:16-Vorschau dazu. Anleitung: [`obs-layout.md`](obs-layout.md).
|
||||
|
||||
Welche OBS-Quelle im Ausschnitt landet — etwa fremde Untertitel:
|
||||
|
||||
```powershell
|
||||
abdulive sources
|
||||
```
|
||||
|
||||
## Betrieb
|
||||
|
||||
```powershell
|
||||
docker compose up -d # startet
|
||||
docker compose logs -f # Logs
|
||||
docker compose down # stoppt
|
||||
Start-ScheduledTask -TaskName abdulive # startet
|
||||
Stop-ScheduledTask -TaskName abdulive # stoppt
|
||||
Get-Content "$env:LOCALAPPDATA\abdulive\logs\abdulive.log" -Wait -Tail 40
|
||||
```
|
||||
|
||||
Sichtungsseite: http://localhost:4792
|
||||
|
||||
## Aktualisieren
|
||||
|
||||
```powershell
|
||||
abdulive update
|
||||
```
|
||||
|
||||
Prueft das Manifest, laedt das Paket, vergleicht die SHA256 und legt es
|
||||
bereit. Die Adresse steht in `config.yaml` unter `update.url` und hat
|
||||
**keine Vorgabe** — ohne Eintrag sagt der Befehl das auch. Liegt die Ablage
|
||||
hinter einer Anmeldung, gehoert ein Lese-Token in den verschluesselten
|
||||
Speicher (Einrichtungsseite), nicht in die `config.yaml`. **Eingespielt wird es beim naechsten Start** — waehrend eines Streams
|
||||
wird nichts ausgetauscht. Sofort uebernehmen:
|
||||
|
||||
```powershell
|
||||
Stop-ScheduledTask -TaskName abdulive; Start-ScheduledTask -TaskName abdulive
|
||||
```
|
||||
|
||||
Die Aufgabe `abdulive-update` macht die Pruefung taeglich um 05:00 von selbst.
|
||||
|
||||
## Backup
|
||||
|
||||
```powershell
|
||||
.\scripts\backup.ps1
|
||||
```
|
||||
|
||||
Kopiert die Datenbank per `VACUUM INTO` aus dem Volume `abdulive-db` nach
|
||||
`data/db/backup/` und hält 30 Generationen. Die DB liegt bewusst in einem
|
||||
Docker-Volume und nicht in einem Windows-Ordner — Dateisperren über die
|
||||
WSL2-Brücke sind für SQLite nicht verlässlich.
|
||||
Kopiert die Datenbank per `VACUUM INTO` aus `%LOCALAPPDATA%\abdulive\db`
|
||||
nach `backup\` und haelt 30 Generationen. `VACUUM INTO` und nicht einfach
|
||||
kopieren: die Datei wird im laufenden Betrieb gesichert, und ein Kopieren
|
||||
waehrend eines Schreibvorgangs ergaebe eine unbrauchbare Sicherung.
|
||||
|
||||
Die Datenbank liegt seit [ADR-0007](decisions/ADR-0007-native-windows.md)
|
||||
in einem gewoehnlichen Windows-Ordner. Der Grund fuer das fruehere
|
||||
Docker-Volume - unzuverlaessige Dateisperren ueber die WSL2-Bruecke - ist
|
||||
mit dem Container weggefallen.
|
||||
|
||||
## Was nicht ins Repo gehört
|
||||
|
||||
|
||||
+5
-5
@@ -113,10 +113,10 @@ Scheibe gibt es etwas, das man anschauen kann.
|
||||
### S0 — Container und Toolchain · ~1 Tag
|
||||
|
||||
`docker/Dockerfile` (Node 22, ffmpeg mit libx264 + libass, whisper.cpp),
|
||||
`docker-compose.yml` mit Volumes und Ports, Modell-Download beim ersten Start
|
||||
Paketbau, Installer und Updater, Modell-Download bei der Einrichtung
|
||||
in ein eigenes Volume, `scripts/check-env` als Selbsttest im Container.
|
||||
|
||||
**Fertig, wenn:** `docker compose run --rm toolkit check-env` meldet ffmpeg,
|
||||
**Fertig, wenn:** `abdulive check-env` meldet ffmpeg,
|
||||
whisper.cpp, Modell und die Schreibrechte auf allen Volumes als OK.
|
||||
|
||||
### S1 — Einrichtung, Auth und Datenaufnahme · ~3 Tage
|
||||
@@ -184,7 +184,7 @@ kopiert und dort verarbeitet — genau das, was [ADR-0004](decisions/ADR-0004-do
|
||||
Punkt 2 verbietet. Jetzt liegt die Rohdatei im Arbeits-Volume, und auf die
|
||||
Windows-Seite kommt nur das fertige, kleine Ergebnis.
|
||||
|
||||
Neu dazu: `docker compose run --rm toolkit render <datei>` rendert eine Rohdatei
|
||||
Neu dazu: `abdulive render <datei>` rendert eine Rohdatei
|
||||
von Hand — gedacht zum Nachrendern, wenn sich das Layout ändert.
|
||||
|
||||
### S4 — Untertitel · ~1,5 Tage
|
||||
@@ -301,11 +301,11 @@ Ein echter Stream, ohne Eingriff von außen:
|
||||
| whisper.cpp versteht Spielgeräusche statt Sprache | ~~mittel~~ **gelöst** | OBS nimmt getrennte Spuren auf; transkribiert wird nur das Mikrofon (Spur 2). |
|
||||
| Twitch ändert die Ableitung der Clip-MP4-URL | niedrig | Betrifft nur den Zweitweg; die Rohdatei kommt aus OBS. Gekapselt in einer Datei. |
|
||||
| Replay-Buffer-Pfad-Abbildung Windows ↔ Container | mittel | In S2 als Erstes lösen, mit einem Testlauf vor allem anderen |
|
||||
| Docker Desktop belastet den Stream-PC | mittel | `.wslconfig` mit Speicher- und CPU-Grenze; wenn es stört, ist ein Betrieb ohne Container jederzeit möglich, weil nur ffmpeg und whisper.cpp fehlen |
|
||||
| ~~Docker Desktop belastet den Stream-PC~~ | — | **eingetreten und geloest**: seit [ADR-0007](decisions/ADR-0007-native-windows.md) laeuft alles nativ, die WSL2-VM und ihre 1-2 GB entfallen |
|
||||
|
||||
## Was Abdu vorher tun muss
|
||||
|
||||
1. Docker Desktop installieren, WSL2-Backend aktivieren
|
||||
1. `install.ps1` ausfuehren (kein Docker, keine Administratorrechte)
|
||||
2. Zwei Twitch-Apps anlegen, Redirect-URI `http://localhost:4792/callback`
|
||||
(siehe [`05-setup.md`](05-setup.md)). Client-ID und Secret **nicht**
|
||||
irgendwo eintragen — die kommen in den Einrichtungs-Assistenten.
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
# ADR-0004: Betrieb in Docker
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-09-09
|
||||
**Status:** ~~akzeptiert~~ **ersetzt durch [ADR-0007](ADR-0007-native-windows.md)** · **Datum:** 2026-09-09
|
||||
|
||||
> Diese Entscheidung gilt nicht mehr. Das Toolkit laeuft nativ unter Windows.
|
||||
> Die Analyse unten bleibt lesenswert - die vier Fallen, die sie beschreibt,
|
||||
> waren real, und zwei davon (Pfaduebersetzung, Kopieren ueber die
|
||||
> WSL2-Bruecke) sind mit dem Container weggefallen statt geloest worden zu
|
||||
> sein. Warum gewechselt wurde, steht in ADR-0007.
|
||||
|
||||
## Kontext
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
# ADR-0007: Native Windows-Anwendung statt Container
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-09-09 · **ersetzt:** [ADR-0004](ADR-0004-docker.md)
|
||||
|
||||
## 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.
|
||||
@@ -5,9 +5,10 @@
|
||||
| [0001](ADR-0001-monorepo-typescript.md) | TypeScript-Monorepo statt gemischter Sprachen | akzeptiert |
|
||||
| [0002](ADR-0002-sqlite.md) | SQLite statt Postgres | akzeptiert |
|
||||
| [0003](ADR-0003-eventsub-websocket.md) | EventSub ueber WebSocket, Chat ueber IRC | akzeptiert |
|
||||
| [0004](ADR-0004-docker.md) | Betrieb in Docker, whisper.cpp statt Python | akzeptiert |
|
||||
| [0004](ADR-0004-docker.md) | Betrieb in Docker, whisper.cpp statt Python | ersetzt durch 0007 |
|
||||
| [0005](ADR-0005-zugangsdaten.md) | Zugangsdaten ueber Einrichtungs-Assistent statt .env | akzeptiert |
|
||||
| [0006](ADR-0006-ein-dach.md) | Ein Dach: lokale Weboberflaeche, lokale Arbeitsprozesse | akzeptiert |
|
||||
| [0007](ADR-0007-native-windows.md) | Native Windows-Anwendung statt Container | akzeptiert |
|
||||
|
||||
Neue Entscheidung: Datei `ADR-<nummer>-<kurzname>.md`, Aufbau Kontext /
|
||||
Entscheidung / Begruendung / Konsequenzen.
|
||||
+43
-30
@@ -11,7 +11,7 @@ Auslöser bis zum fertigen Short steht **im Code vollständig** — geprüft ist
|
||||
|
||||
| Scheibe | Stand | Nachweis |
|
||||
|---|---|---|
|
||||
| S0 Container + Toolchain | fertig | `check-env` ohne harte Fehler |
|
||||
| S0 Toolchain + Auslieferung | fertig | `check-env` nativ ohne harte Fehler, Paket gebaut und installiert |
|
||||
| S1 Einrichtung, Auth, DB, Chat | fertig | Daemon läuft, Chat verbunden, Secrets verschlüsselt |
|
||||
| S2 OBS + `!clip` | Code fertig, **nicht abgenommen** | braucht laufendes OBS |
|
||||
| S3 9:16-Render | fertig | Durchlauf mit künstlicher Aufnahme |
|
||||
@@ -19,68 +19,76 @@ Auslöser bis zum fertigen Short steht **im Code vollständig** — geprüft ist
|
||||
| S5 Onboarding + Sichtung | offen | — |
|
||||
| S6 Erstchatter-Begrüßung | offen | — |
|
||||
|
||||
53 Tests, alle grün. Vollständige Liste dessen, was noch fehlt:
|
||||
79 Tests, alle grün. Vollständige Liste dessen, was noch fehlt:
|
||||
[`offene-punkte.md`](offene-punkte.md).
|
||||
|
||||
## Was jemand wissen muss, bevor er etwas anfasst
|
||||
|
||||
**Es gibt keine Abhängigkeiten und keinen Build.** Node 24 bringt SQLite,
|
||||
WebSocket, `fetch` und die Ausführung von TypeScript mit. `node docker/entrypoint.ts`
|
||||
WebSocket, `fetch` und die Ausführung von TypeScript mit. `node apps/cli/main.ts`
|
||||
läuft direkt. Deshalb gilt: nur *löschbare* TypeScript-Syntax — keine `enum`,
|
||||
keine Parameter-Properties im Konstruktor, keine `namespace`. `erasableSyntaxOnly`
|
||||
in der `tsconfig.json` erzwingt das und hat schon einen echten Fehler gefangen.
|
||||
|
||||
**Alles läuft unter einem Dach**, einem Prozess in einem Container. Schwere
|
||||
**Alles läuft unter einem Dach**, in einem Prozess. Schwere
|
||||
Arbeit (ffmpeg, whisper) läuft als Kindprozess und darf abstürzen. Der Ingest
|
||||
darf nie sterben — was er nicht mitschreibt, ist für immer weg.
|
||||
|
||||
**Zugangsdaten kommen aus der Weboberfläche**, nicht aus einer Datei. Sie liegen
|
||||
verschlüsselt in einem Docker-Volume, bewusst außerhalb der Datenbank, weil die
|
||||
gesichert und dabei auf die Windows-Seite kopiert wird.
|
||||
verschlüsselt neben der Datenbank, aber bewusst nicht *in* ihr — die Datenbank
|
||||
wird gesichert und weitergereicht, die Geheimnisse nicht.
|
||||
|
||||
**Rohdateien gehören ins Arbeits-Volume**, nicht in den Windows-Bind-Mount. Über
|
||||
die WSL2-Brücke zu encodieren kostet ein Vielfaches. Dagegen habe ich einmal
|
||||
selbst verstoßen — der Fehler fiel erst in S3 auf.
|
||||
**Rohdateien gehören in den Arbeitsordner**, nicht neben den OBS-Puffer. Das
|
||||
war unter Docker ein Zeitproblem (Encodieren über die WSL2-Brücke kostete ein
|
||||
Vielfaches) und ist nativ nur noch Ordnung — der Wiedergabepuffer-Ordner
|
||||
gehört OBS.
|
||||
|
||||
Die Begründungen stehen in [`decisions/`](decisions/), sechs ADRs. Wer eine
|
||||
Die Begründungen stehen in [`decisions/`](decisions/), sieben ADRs. Wer eine
|
||||
davon umwerfen will, sollte sie vorher lesen; jede hat einen Grund, der beim
|
||||
Bauen aufgetaucht ist.
|
||||
Bauen aufgetaucht ist. ADR-0004 (Docker) ist durch ADR-0007 ersetzt — die
|
||||
Analyse darin bleibt trotzdem lesenswert.
|
||||
|
||||
## Loslegen
|
||||
|
||||
```powershell
|
||||
git clone https://git.zeasy.dev/ZeasyAlex/abdulive.git
|
||||
cd abdulive
|
||||
Copy-Item config\config.example.yaml config\config.yaml
|
||||
**Auf Abdus Rechner** läuft eine native Windows-Anwendung — kein Docker, kein
|
||||
WSL2, nichts zu übersetzen ([ADR-0007](decisions/ADR-0007-native-windows.md)):
|
||||
|
||||
docker compose build
|
||||
docker compose run --rm toolkit check-env
|
||||
docker compose run --rm toolkit fetch-model # ca. 465 MB
|
||||
docker compose up -d
|
||||
```powershell
|
||||
Expand-Archive .\abdulive-0.5.0-win-x64.zip -DestinationPath .\abdulive
|
||||
.\abdulive\install.ps1
|
||||
abdulive fetch-model
|
||||
abdulive check-env
|
||||
```
|
||||
|
||||
Dann `http://localhost:4792/setup` öffnen. Ausführlich in
|
||||
[`05-setup.md`](05-setup.md).
|
||||
Einen Einzeiler zum Nachladen gibt es nicht — `git.zeasy.dev` ist komplett
|
||||
angemeldet-only. **Wo die Pakete liegen sollen, ist die naechste offene
|
||||
Entscheidung**, und ohne sie funktioniert auch `abdulive update` nicht.
|
||||
|
||||
Tests:
|
||||
Ausführlich in [`05-setup.md`](05-setup.md).
|
||||
|
||||
**Zum Entwickeln** reicht ein Checkout und Node 24 oder neuer:
|
||||
|
||||
```powershell
|
||||
docker run --rm -v "${PWD}:/app" -w /app node:24-bookworm-slim `
|
||||
node --test "packages/**/*.test.ts" "apps/**/*.test.ts"
|
||||
node apps/cli/main.ts check-env
|
||||
node --test "packages/**/*.test.ts" "apps/**/*.test.ts"
|
||||
node scripts/build-package.ts --version 0.5.0 # baut dist/*.zip
|
||||
```
|
||||
|
||||
Typprüfung (das Einzige, wofür npm gebraucht wird):
|
||||
|
||||
```powershell
|
||||
docker run --rm -v "${PWD}:/app" -w /tmp node:24-bookworm-slim sh -c `
|
||||
"npm i -s typescript@5.9 @types/node && cd /app && /tmp/node_modules/.bin/tsc --noEmit --typeRoots /tmp/node_modules/@types"
|
||||
npm i typescript@5.9 @types/node
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
## Die drei Dinge, die Abdu tun muss
|
||||
|
||||
1. **Zwei Twitch-Apps anlegen** (dev.twitch.tv/console/apps), Redirect-URI
|
||||
`http://localhost:4792/callback`. Client-ID und Secret danach auf der
|
||||
Einrichtungsseite eintragen — nicht in eine Datei.
|
||||
1. **Broadcaster-Konto verbinden.** Die beiden Twitch-Apps
|
||||
(dev.twitch.tv/console/apps, Redirect-URI `http://localhost:4792/callback`)
|
||||
kann auch jemand anders anlegen — sie hängen nicht am Kanal. Was **nur Abdu**
|
||||
kann: auf der Einrichtungsseite eingeloggt als er selbst auf „Mit Twitch
|
||||
verbinden" klicken. Dafür muss die Anwendung auf seinem Rechner schon
|
||||
laufen; die Seite hört nur auf `127.0.0.1`.
|
||||
2. **OBS einrichten**: obs-websocket an (Port 4455), Wiedergabepuffer 90 s,
|
||||
Hotkey auf „Wiedergabepuffer speichern", getrennte Audiospuren.
|
||||
3. **Layout umstellen** nach [`obs-layout.md`](obs-layout.md). Die
|
||||
@@ -109,5 +117,10 @@ Die Untertitel sind brauchbar, nicht gut. `ggml-small` verhört sich hörbar
|
||||
Einfärben ist nicht der Stil, der auf Shorts trägt. Sechs konkrete
|
||||
Verbesserungen stehen in [`offene-punkte.md`](offene-punkte.md).
|
||||
|
||||
Und: Das Aufräumen im Arbeits-Volume fehlt komplett. Rohdateien sammeln sich
|
||||
Und: Das Aufräumen im Arbeitsordner fehlt komplett. Rohdateien sammeln sich
|
||||
unbegrenzt an. Das ist die Lücke, die als Erstes weh tun wird.
|
||||
|
||||
Der Updater ist gebaut und in Teilen geprüft — Manifest, Download, Prüfsumme
|
||||
und Bereitlegen laufen. Der eigentliche Tausch durch `launcher.ps1` ist noch
|
||||
nie im Feld passiert. Vor dem ersten echten Update einmal künstlich
|
||||
durchspielen.
|
||||
+20
-4
@@ -52,16 +52,32 @@ schiebt dann nicht sofort einen Streifen Gesicht in das Gameplay-Bild.
|
||||
## Prüfen
|
||||
|
||||
```powershell
|
||||
docker compose run --rm toolkit calibrate
|
||||
abdulive calibrate
|
||||
```
|
||||
|
||||
Nimmt das neueste Video aus dem Wiedergabepuffer-Ordner und legt drei Bilder in
|
||||
`data/clips/_calibration/` ab: das Einzelbild, dasselbe mit eingezeichneten
|
||||
Nimmt das neueste Video aus dem Wiedergabepuffer-Ordner und legt drei Bilder
|
||||
unter `%LOCALAPPDATA%\abdulive\clips\_calibration` ab: das Einzelbild, dasselbe mit eingezeichneten
|
||||
Ausschnitten, und die fertige 9:16-Vorschau. Sitzt in der Vorschau der Kopf
|
||||
mittig und ist im Gameplay-Teil kein Chat zu sehen, stimmt das Layout.
|
||||
|
||||
## Text im Bild — hier wird es eng
|
||||
|
||||
**Wer malt den Text?** Das ist nicht zu erraten — die Overlays sind ins
|
||||
Streambild eingebrannt, man muss nachsehen, welche OBS-Quelle sie zeichnet:
|
||||
|
||||
```powershell
|
||||
abdulive sources
|
||||
```
|
||||
|
||||
Der Befehl fragt das laufende OBS und listet jede Quelle der aktuellen Szene
|
||||
mit Position, Größe und dem Urteil, ob sie im 9:16-Ausschnitt landet. Bei
|
||||
Browser-Quellen steht die Adresse dabei — daran erkennt man den Dienst
|
||||
dahinter. Was nach Untertiteln aussieht, wird am Ende noch einmal
|
||||
zusammengefasst.
|
||||
|
||||
Dafür muss in OBS **Werkzeuge → WebSocket-Server-Einstellungen → Server
|
||||
aktivieren** gesetzt sein (Port 4455) und das Passwort unter `/setup` liegen.
|
||||
|
||||
Bei der Prüfung eines echten Clips lagen **drei Textebenen übereinander**:
|
||||
|
||||
1. die Untertitel des Spiels („Genau, ich mach da … sag zu dem Typen")
|
||||
@@ -115,5 +131,5 @@ fest, wie es vor der Vorgabe aussah — damit lassen sich alte Aufnahmen noch
|
||||
korrekt rendern:
|
||||
|
||||
```powershell
|
||||
docker compose run --rm toolkit render <datei> bestand-2026-09
|
||||
abdulive render <datei> bestand-2026-09
|
||||
```
|
||||
+43
-9
@@ -16,7 +16,7 @@ Erledigtes wandert raus, nicht ins Archiv — die Historie steht in Git.
|
||||
|
||||
## Bekannte Lücke im Betrieb
|
||||
|
||||
- [ ] **Aufräumen im Arbeits-Volume.** Jede Rohdatei landet unter `/work/<clipId>/`
|
||||
- [ ] **Aufräumen im Arbeitsordner.** Jede Rohdatei landet unter `work/<clipId>/`
|
||||
und bleibt dort liegen. Bei fünf Clips pro Stream und mehreren hundert MB
|
||||
je Datei läuft das Volume in wenigen Wochen voll. Gebraucht wird eine
|
||||
Aufbewahrungsregel — Vorschlag: Rohdateien der letzten 30 Clips behalten,
|
||||
@@ -47,6 +47,10 @@ Der Durchlauf am echten Clip war brauchbar, aber nicht gut. Was auffiel:
|
||||
- [ ] **Position prüfen.** 340 px Abstand nach unten ist geschätzt, nicht
|
||||
gemessen. An einem echten Handy gegen die TikTok-, Reels- und
|
||||
Shorts-Oberfläche halten.
|
||||
- [ ] **Seitliche Ränder gegengeprüft**, aber nur rechnerisch gegen eine
|
||||
gemessene mittlere Zeichenbreite. Bei einer anderen Schrift (siehe
|
||||
Punkt oben) muss `CHAR_WIDTH_RATIO` in `subtitles.ts` neu eingemessen
|
||||
werden — sonst bricht die Zeile wieder an der falschen Stelle.
|
||||
- [ ] **Nur transkribieren, wenn Abdu spricht.** Bei getrennten Tonspuren
|
||||
reicht ein Pegelfilter auf der Mikrofonspur, damit aus Stille kein
|
||||
erfundener Text wird.
|
||||
@@ -55,11 +59,47 @@ Der Durchlauf am echten Clip war brauchbar, aber nicht gut. Was auffiel:
|
||||
|
||||
- [ ] **Live-Captions verschieben.** Sie liegen im Gameplay-Band und sind fest
|
||||
eingebrannt. Zwei Vorschläge stehen in [`obs-layout.md`](obs-layout.md).
|
||||
Welche OBS-Quelle sie überhaupt zeichnet, sagt
|
||||
`abdulive sources` — das war vorher unbekannt.
|
||||
- [ ] **Spieluntertitel abschalten.**
|
||||
- [ ] **Bessere Bildquelle für die Facecam** — Plugin `Source Record` oder
|
||||
`Aitum Vertical`. Damit entfällt die 2,25-fache Vergrößerung. Lohnt sich
|
||||
erst, wenn regelmäßig Shorts entstehen.
|
||||
|
||||
## Auslieferung
|
||||
|
||||
- [ ] **Wo liegen die Pakete?** Das ist die Entscheidung, die als Naechstes
|
||||
ansteht — ohne sie funktioniert `abdulive update` gar nicht.
|
||||
`git.zeasy.dev` verlangt **instanzweit** eine Anmeldung: `/explore`,
|
||||
die API und jede Rohdatei antworten mit 303 auf `/user/login`. Ein
|
||||
`irm … | iex`-Einzeiler bekommt dadurch die Anmeldeseite und versucht,
|
||||
sie als PowerShell auszufuehren. Drei Wege:
|
||||
*(a)* eine Ablage ohne Anmeldung, etwa `dl.zeasy.software` — am
|
||||
einfachsten, und das Paket enthaelt ohnehin den vollstaendigen Quelltext
|
||||
als lesbare `.ts`-Dateien;
|
||||
*(b)* Gitea-Release plus Lese-Token auf Abdus Rechner — der Token darf
|
||||
dann aber das ganze private Repo lesen;
|
||||
*(c)* kein Updater, Alexander spielt neue Fassungen per Fernwartung ein.
|
||||
Code und Installer sind auf *(a)* und *(b)* vorbereitet: `update.url` in
|
||||
der `config.yaml`, Token im verschluesselten Speicher.
|
||||
|
||||
- [ ] **Code-Signing.** Das Paket ist nicht signiert, SmartScreen warnt bei
|
||||
jeder Installation vor einem unbekannten Herausgeber. Ein
|
||||
OV-Zertifikat kostet rund 200 EUR im Jahr, ein EV-Zertifikat mehr und
|
||||
braucht Hardware. Solange nur Abdu installiert, reicht ein Klick auf
|
||||
"Trotzdem ausfuehren" - sobald jemand Drittes das Paket bekommt, nicht
|
||||
mehr.
|
||||
- [ ] **CI auf git.zeasy.dev.** `scripts/build-package.ts` laeuft bisher von
|
||||
Hand. Als Forgejo-Action beim Tag gebaut und als Veroeffentlichung
|
||||
hochgeladen, findet `abdulive update` sie von allein.
|
||||
- [ ] **Update noch nie im Feld gelaufen.** Bereitlegen und Pruefsumme sind
|
||||
getestet, der Tausch durch `launcher.ps1` nur gelesen. Vor dem ersten
|
||||
echten Update einmal kuenstlich durchspielen.
|
||||
- [ ] **Hardware-Encoding.** Der mitgelieferte ffmpeg kann QSV, AMF und NVENC
|
||||
(`--enable-libvpl`, `--enable-amf`, `--enable-ffnvcodec`) - aus WSL2
|
||||
heraus ging das nicht. `clipper.ffmpeg.encoder` steht weiter auf
|
||||
`libx264`. Auf Abdus Rechner messen, ob `h264_qsv` etwas bringt.
|
||||
|
||||
## Nach dem MVP
|
||||
|
||||
- [ ] **Spitzen-Erkennung scharf schalten.** Sie läuft mit und schreibt jeden
|
||||
@@ -72,11 +112,5 @@ Der Durchlauf am echten Clip war brauchbar, aber nicht gut. Was auffiel:
|
||||
|
||||
## Kleinigkeiten
|
||||
|
||||
- [ ] `docker compose run` reicht ein TTY durch, dadurch schreibt die
|
||||
Fortschrittsanzeige `\r`-Zeilen ins Protokoll. Mit `-T` sauber, aber
|
||||
unschön als Standard.
|
||||
- [ ] `ldconfig` meldet beim Bauen `libwhisper.so.1 is not a symbolic link`.
|
||||
Funktioniert, ist aber unsauber.
|
||||
- [ ] `docs/05-setup.md` nennt ffmpeg 6+; im Image steckt 5.1.9 aus Debian
|
||||
bookworm. Für unseren Filtergraph irrelevant, die Angabe stimmt trotzdem
|
||||
nicht.
|
||||
- [ ] Die Fortschrittsanzeige schreibt `\r`-Zeilen; im Protokoll der Aufgabe
|
||||
sieht das unschoen aus. Beim Umleiten in eine Datei besser unterdruecken.
|
||||
@@ -95,7 +95,7 @@ ffmpeg -i in.mp4 -filter_complex "
|
||||
Nachmessen und Vorschau rendern:
|
||||
|
||||
```powershell
|
||||
docker compose run --rm toolkit calibrate
|
||||
abdulive calibrate
|
||||
```
|
||||
|
||||
Das legt drei Bilder in `data/clips/_calibration/` ab: das Einzelbild, dasselbe
|
||||
|
||||
Reference in new issue
Block a user