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:
ZeasyAlexandClaude Opus 5 committed 2026-09-09 17:39:26 +02:00
1 parent 71dd660d01
commit 5f6301268a
41 files changed
+2254 -312

No files matched your search

+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+7 -1
View File
@@ -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
+134
View File
@@ -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.
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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