Uebergabe auf den aktuellen Stand

Version 0.5.4, 93 Tests, acht ADRs. Neu darin:

- Auslieferung: was build-package.ts tut, wie die Veroeffentlichung
  entsteht, und dass die CI-Pipeline noch nie gelaufen ist
- Vier Windows-Fallen, die je einen Fehler verursacht haben und im
  Fehlerfall nicht nach ihrer Ursache aussehen: stderr als
  NativeCommandError, Rueckgabewert von & plus $LASTEXITCODE, localhost
  gegen 127.0.0.1, ffmpeg-Filterpfade
- Untertitel: die gemessenen Zahlen statt der alten Schaetzung
- Hinweis, dass das Repo oeffentlich ist

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 20:54:58 +02:00
1 parent 7775c689be
commit d231e1c722
1 file changed
+142 -59
+142 -59
View File
@@ -1,121 +1,204 @@
# Übergabe # Übergabe
Stand **2026-09-09**. Wer hier weitermacht, sollte diese Seite zuerst lesen und Stand **2026-09-09**, Version 0.5.4. Wer hier weitermacht, liest zuerst diese
danach [`07-mvp-plan.md`](07-mvp-plan.md). Seite, dann [`07-mvp-plan.md`](07-mvp-plan.md). Für Abdu selbst gibt es das
[Handbuch](handbuch/handbuch.pdf).
## Wo das Projekt steht ## Wo das Projekt steht
Vier von sieben Scheiben des MVP „Clip-Knopf" sind gebaut. Die Kette vom Vier von sieben Scheiben des MVP „Clip-Knopf" sind gebaut. Die Kette vom
Auslöser bis zum fertigen Short steht **im Code vollständig** — geprüft ist sie Auslöser bis zum fertigen Short steht im Code vollständig. Geprüft ist sie
überall dort, wo es ohne Abdus Stream-PC ging. überall dort, wo es ohne Abdus Stream-PC ging.
| Scheibe | Stand | Nachweis | | Scheibe | Stand | Nachweis |
|---|---|---| |---|---|---|
| S0 Toolchain + Auslieferung | fertig | `check-env` nativ ohne harte Fehler, Paket gebaut und installiert | | S0 Toolchain + Auslieferung | fertig | Paket gebaut und installiert, `check-env` ohne harte Fehler, Updater einmal komplett durchgelaufen |
| S1 Einrichtung, Auth, DB, Chat | fertig | Daemon läuft, Chat verbunden, Secrets verschlüsselt | | S1 Einrichtung, Auth, DB, Chat | fertig | Daemon läuft, Chat verbunden, Secrets verschlüsselt |
| S2 OBS + `!clip` | Code fertig, **nicht abgenommen** | braucht laufendes OBS | | S2 OBS + `!clip` | Code fertig, **nicht abgenommen** | braucht laufendes OBS |
| S3 9:16-Render | fertig | Durchlauf mit künstlicher Aufnahme | | S3 9:16-Render | fertig | Durchlauf mit künstlicher Aufnahme |
| S4 Untertitel | fertig | Durchlauf mit echtem deutschen Clip | | S4 Untertitel | fertig | Durchlauf mit echtem deutschen Clip, Ränder nachgemessen |
| S5 Onboarding + Sichtung | offen | — | | S5 Onboarding + Sichtung | offen | |
| S6 Erstchatter-Begrüßung | offen | — | | S6 Erstchatter-Begrüßung | offen | |
79 Tests, alle grün. Vollständige Liste dessen, was noch fehlt: 93 Tests, alle grün. Was noch fehlt, steht vollständig in
[`offene-punkte.md`](offene-punkte.md). [`offene-punkte.md`](offene-punkte.md).
## Was jemand wissen muss, bevor er etwas anfasst ## Was jemand wissen muss, bevor er etwas anfasst
**Es gibt keine Abhängigkeiten und keinen Build.** Node 24 bringt SQLite, **Keine Abhängigkeiten, kein Build.** Node 24 bringt SQLite, WebSocket,
WebSocket, `fetch` und die Ausführung von TypeScript mit. `node apps/cli/main.ts` `fetch` und die Ausführung von TypeScript mit. `node apps/cli/main.ts` läuft
läuft direkt. Deshalb gilt: nur *löschbare* TypeScript-Syntax — keine `enum`, direkt. Deshalb gilt: nur *löschbare* TypeScript-Syntax, also keine `enum`,
keine Parameter-Properties im Konstruktor, keine `namespace`. `erasableSyntaxOnly` keine Parameter-Properties im Konstruktor, keine `namespace`.
in der `tsconfig.json` erzwingt das und hat schon einen echten Fehler gefangen. `erasableSyntaxOnly` in der `tsconfig.json` erzwingt das und hat schon einen
echten Fehler gefangen.
**Alles läuft unter einem Dach**, in einem Prozess. Schwere **Alles läuft in einem Prozess.** Schwere Arbeit (ffmpeg, whisper) läuft als
Arbeit (ffmpeg, whisper) läuft als Kindprozess und darf abstürzen. Der Ingest Kindprozess und darf abstürzen. Der Ingest darf nie sterben. Was er nicht
darf nie sterben — was er nicht mitschreibt, ist für immer weg. mitschreibt, ist für immer weg.
**Zugangsdaten kommen aus der Weboberfläche**, nicht aus einer Datei. Sie liegen **Zugangsdaten kommen aus der Weboberfläche**, nicht aus einer Datei. Sie
verschlüsselt neben der Datenbank, aber bewusst nicht *in* ihr — die Datenbank liegen verschlüsselt neben der Datenbank, aber bewusst nicht *in* ihr. Die
wird gesichert und weitergereicht, die Geheimnisse nicht. Datenbank wird gesichert und weitergereicht, die Geheimnisse nicht.
**Rohdateien gehören in den Arbeitsordner**, nicht neben den OBS-Puffer. Das **Das Repo ist seit 2026-09-09 öffentlich.** Damit funktioniert der Einzeiler
war unter Docker ein Zeitproblem (Encodieren über die WSL2-Brücke kostete ein zur Installation, und der Updater braucht keinen Token. Es heißt aber auch:
Vielfaches) und ist nativ nur noch Ordnung — der Wiedergabepuffer-Ordner nichts Vertrauliches mehr hineinschreiben, auch nicht in die Doku.
gehört OBS.
Die Begründungen stehen in [`decisions/`](decisions/), sieben ADRs. Wer eine Die Begründungen stehen in [`decisions/`](decisions/), acht ADRs. Wer eine
davon umwerfen will, sollte sie vorher lesen; jede hat einen Grund, der beim davon umwerfen will, liest sie vorher. ADR-0004 (Docker) ist durch ADR-0007
Bauen aufgetaucht ist. ADR-0004 (Docker) ist durch ADR-0007 ersetzt — die ersetzt; die Analyse darin bleibt lesenswert, weil zwei der vier
Analyse darin bleibt trotzdem lesenswert. beschriebenen Fallen mit dem Container weggefallen sind statt gelöst worden
zu sein.
## Loslegen ## Loslegen
**Auf Abdus Rechner** läuft eine native Windows-Anwendung — kein Docker, kein Auf Abdus Rechner, eine native Windows-Anwendung:
WSL2, nichts zu übersetzen ([ADR-0007](decisions/ADR-0007-native-windows.md)):
```powershell ```powershell
irm https://git.zeasy.dev/ZeasyAlex/abdulive/raw/branch/main/scripts/install.ps1 | iex irm https://git.zeasy.dev/ZeasyAlex/abdulive/raw/branch/main/scripts/install.ps1 | iex
``` ```
Der Installer laedt das Sprachmodell, laeuft den Selbsttest, startet den Der Installer lädt das Paket, prüft die Prüfsumme, holt das Sprachmodell,
Daemon und oeffnet die Einrichtungsseite. läuft den Selbsttest, startet den Dienst und öffnet die Einrichtungsseite.
Ohne Administratorrechte. Ausführlich in [`05-setup.md`](05-setup.md).
Ausführlich in [`05-setup.md`](05-setup.md). Zum Entwickeln reicht ein Checkout und Node 24 oder neuer:
**Zum Entwickeln** reicht ein Checkout und Node 24 oder neuer:
```powershell ```powershell
node apps/cli/main.ts check-env node apps/cli/main.ts check-env
node --test "packages/**/*.test.ts" "apps/**/*.test.ts" node --test "packages/**/*.test.ts" "apps/**/*.test.ts"
node scripts/build-package.ts --version 0.5.0 # baut dist/*.zip node scripts/build-package.ts --version 0.5.5 # baut dist/
``` ```
Typprüfung (das Einzige, wofür npm gebraucht wird): Typprüfung, das Einzige wofür npm gebraucht wird:
```powershell ```powershell
npm i typescript@5.9 @types/node npm i typescript@5.9 @types/node
npx tsc --noEmit npx tsc --noEmit
``` ```
## Ausliefern
`build-package.ts` lädt Node, ffmpeg und whisper.cpp bei ihren Herausgebern,
packt alles nach `dist/` und schreibt `abdulive.json`. Dieses Manifest fragt
`abdulive update` auf Abdus Rechner ab.
Die Veröffentlichung `latest` trägt beide Dateien und wird bei jedem Bau
ersetzt, damit die Adresse gleich bleibt.
`.gitea/workflows/release.yml` macht das beim Tag automatisch. **Die Pipeline
ist noch nie gelaufen**, bisher wurde von Hand veröffentlicht. Der Runner
(`ubuntu-latest`) steht.
## Windows-Fallen, die schon zugeschlagen haben
Alle vier haben Zeit gekostet, und keine sieht im Fehlerfall nach ihrer
Ursache aus. Wer an den PowerShell-Skripten arbeitet, sollte sie kennen.
**stderr beendet das Skript.** Windows PowerShell 5.1 verpackt jede
stderr-Zeile eines nativen Programms in einen `ErrorRecord`. Zusammen mit
`$ErrorActionPreference = 'Stop'` bricht das sofort alles ab. Der Daemon
schreibt im Normalbetrieb Warnungen auf stderr, der Starter hat ihn also bei
seiner ersten Logzeile umgebracht: Aufgabe meldete Code 1, das Protokoll war
leer. Deshalb läuft der Start über `Start-Process` mit getrennten
Umleitungen, und `install.ps1` ruft native Programme über eine Hilfsfunktion
auf, die `ErrorActionPreference` kurz zurückstellt.
**`& $programm; return $LASTEXITCODE` gibt auch die Ausgabe zurück.** Die
Funktion liefert dann ein Array aus allen Ausgabezeilen plus Exit-Code, und
jeder Vergleich damit ist falsch, ohne dass es auffällt. Ein erfolgreiches
`fetch-model` galt so als fehlgeschlagen. Ausgabe mit `| Out-Host`
durchreichen.
**`localhost` kostet zwei Sekunden.** Der Dienst lauscht nur auf IPv4,
Windows probiert bei `localhost` zuerst `::1`. Gemessen: 2066 ms bis zum
Rückfall gegenüber 15 ms bei `127.0.0.1`. Eine Bereitschaftsprüfung mit zwei
Sekunden Zeitlimit läuft dadurch immer ab, obwohl der Dienst längst läuft.
**Windows-Pfade im ffmpeg-Filtergraphen brauchen drei Schritte.** 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 |
Vorwärtsschrägstriche, escapter Doppelpunkt **und** Anführungszeichen. Je
zwei davon reichen nicht. `filterPath()` in `packages/media/src/ffmpeg.ts`
macht das, mit Tests.
## Untertitel: was gemessen ist
**Die Schrift muss mitgeliefert werden.** Im Container kam DejaVu Sans aus
dem Betriebssystem, Windows hat sie nicht. Fehlt `fontsdir`, setzt libass
wortlos eine Ersatzschrift ein. Gemessen: dieselbe Zeile wird 720 statt 826
Pixel breit, ohne jede Fehlermeldung. Die Dateien liegen unter
`assets/fonts/`.
**Die Zeilenlänge ist eingemessen, nicht geschätzt.** Vorher stand dort fest
`maxChars: 30`. Das sind bei Schriftgröße 76 rund 1085 px, nutzbar sind 940.
Zusammen mit `WrapStyle: 2`, bei dem libass gar nicht umbricht, lief der Text
links und rechts aus dem Bild. Jetzt wird die Grenze aus Breite und
Schriftgröße abgeleitet, Versalien zählen breiter, und einzelne zu lange
Wörter bekommen eine kleinere Schrift, weil libass innerhalb eines Wortes
nicht umbricht. Deutsche Komposita sind regelmäßig breiter als das Bild.
Wer die Schrift wechselt, muss `CHAR_WIDTH_RATIO` in `subtitles.ts` neu
einmessen. Sonst bricht die Zeile wieder an der falschen Stelle.
## Die drei Dinge, die Abdu tun muss ## Die drei Dinge, die Abdu tun muss
1. **Broadcaster-Konto verbinden.** Die beiden Twitch-Apps 1. **Broadcaster-Konto verbinden.** Die beiden Twitch-Apps
(dev.twitch.tv/console/apps, Redirect-URI `http://localhost:4792/callback`) (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 auch jemand anders anlegen, sie hängen nicht am Kanal. Was nur Abdu
kann: auf der Einrichtungsseite eingeloggt als er selbst auf „Mit Twitch kann: auf der Einrichtungsseite eingeloggt als er selbst auf „Mit Twitch
verbinden" klicken. Dafür muss die Anwendung auf seinem Rechner schon verbinden" klicken. Dafür muss die Anwendung auf seinem Rechner laufen,
laufen; die Seite hört nur auf `127.0.0.1`. die Seite hört nur auf `127.0.0.1`.
2. **OBS einrichten**: obs-websocket an (Port 4455), Wiedergabepuffer 90 s, 2. **OBS einrichten**: obs-websocket an (Port 4455), Wiedergabepuffer 90 s,
Hotkey auf „Wiedergabepuffer speichern", getrennte Audiospuren. Hotkey auf „Wiedergabepuffer speichern", getrennte Audiospuren.
3. **Layout umstellen** nach [`obs-layout.md`](obs-layout.md). Die 3. **Layout umstellen** nach [`obs-layout.md`](obs-layout.md), danach
Ausrichtungsvorlage in OBS über die Szene legen, Quellen einpassen, wieder `abdulive calibrate` zur Kontrolle.
ausblenden. Danach `calibrate` zur Kontrolle.
Ohne Punkt 1 liest das Toolkit den Chat anonym mit und kann nichts senden. Ohne Punkt 1 liest das Toolkit den Chat anonym mit und kann nichts senden.
Ohne Punkt 2 gibt es keine Clips. Ohne Punkt 3 sitzt der Ausschnitt schief. Ohne Punkt 2 gibt es keine Clips. Ohne Punkt 3 sitzt der Ausschnitt schief.
Dazu eine Kleinigkeit mit Wirkung: das Bot-Konto sollte Moderator sein
(`/mod <botname>`). Ohne das gilt ein Limit von 20 Chat-Nachrichten je 30
Sekunden statt 100, und im Follower-only-Modus kommt der Bot gar nicht durch.
## Wo ich weitermachen würde ## Wo ich weitermachen würde
**S5.** Nicht wegen des Codes, sondern weil das Werkzeug ohne Einrichtungs- **S2 an einem laufenden OBS abnehmen.** Das ist die einzige ungeprüfte Stelle
Assistenten und Sichtungsseite für Abdu nicht bedienbar ist. Die Kette in der ganzen Kette, und alles Weitere hängt daran. Seit es `abdulive sources`
funktioniert, aber sie hat noch keine Oberfläche, die jemand ohne Erklärung gibt, ist auch die Frage beantwortbar, welche OBS-Quelle fremden Text ins Bild
benutzt. Abnahmekriterium steht im Plan: *Ein Fremder kommt von „nichts malt. Das war vorher nicht zu erraten.
eingerichtet" bis zum ersten eigenen Clip, ohne die Dokumentation zu lesen.*
Davor, wenn Gelegenheit ist: **S2 an einem laufenden OBS abnehmen**. Das ist **Danach S5.** Nicht wegen des Codes, sondern weil das Werkzeug ohne
die einzige ungeprüfte Stelle in der ganzen Kette, und alles Weitere hängt Sichtungsseite für Abdu nicht bedienbar ist. Die Kette funktioniert, aber sie
daran. hat keine Oberfläche, auf der man Clips durchgeht und aussortiert.
Abnahmekriterium steht im Plan: *Ein Fremder kommt von „nichts eingerichtet"
bis zum ersten eigenen Clip, ohne die Dokumentation zu lesen.* Installer und
Handbuch decken davon inzwischen ein gutes Stück ab.
## Womit ich nicht zufrieden bin ## Womit ich nicht zufrieden bin
Die Untertitel sind brauchbar, nicht gut. `ggml-small` verhört sich hörbar **Das Aufräumen im Arbeitsordner fehlt komplett.** Rohdateien sammeln sich
(„Danke" wurde „Dank es"), die Schrift ist unauffällig, und das fortlaufende unbegrenzt an, mehrere hundert MB je Clip. Das ist die Lücke, die als Erstes
Einfärben ist nicht der Stil, der auf Shorts trägt. Sechs konkrete weh tun wird.
Verbesserungen stehen in [`offene-punkte.md`](offene-punkte.md).
Und: Das Aufräumen im Arbeitsordner fehlt komplett. Rohdateien sammeln sich **Die Untertitel sind brauchbar, nicht gut.** `ggml-small` verhört sich
unbegrenzt an. Das ist die Lücke, die als Erstes weh tun wird. hörbar („Danke" wurde „Dank es"), DejaVu Sans ist unauffällig, und das
fortlaufende Einfärben ist nicht der Stil, der auf Shorts trägt. Sechs
konkrete Verbesserungen stehen in [`offene-punkte.md`](offene-punkte.md).
Der Updater ist einmal komplett durchgelaufen: 0.4.0 erkannt, 119 MB von **Die CI ist geschrieben, aber ungetestet.** Beim ersten Tag zusehen.
`git.zeasy.dev` geladen, Prüfsumme geprüft, bereitgelegt, von `launcher.ps1`
getauscht, `pending` aufgeräumt. Was noch nie lief, ist die CI-Pipeline. **Das Paket ist nicht signiert.** SmartScreen warnt bei jeder Installation.
Solange nur Abdu installiert, reicht ein Klick. Sobald jemand Drittes es
bekommt, nicht mehr.
**Es gibt keine Einstellung für den OBS-Wiedergabepuffer-Ordner.** Im
Container kam der Pfad über `OBS_REPLAY_DIR`, nativ fehlt der Weg. Für die
Clip-Kette ist das egal, weil OBS den Pfad über WebSocket meldet. Aber
`calibrate` findet so keine Aufnahmen, und `check-env` warnt zu Recht.