PLAN.md: Produktions-DB Schema, Verzeichnisstruktur, Statusmaschine, Orchestrator-Logik, CLI

This commit is contained in:
root 2026-03-16 12:11:04 +07:00
parent d4c8e49f51
commit 642d53a927

View file

@ -362,13 +362,228 @@ Empfehlung: Erst aufbauen wenn 3080-Rig voll ausgelastet. Alternative: verkaufen
Nicht aktiv aufbauen. Falls 3080-Rig irgendwann an Kapazitaetsgrenzen stoesst: 1-Karten-Test mit Whisper.
Wenn selbst das nicht lohnt: verkaufen und in NVMe-Storage oder RAM investieren.
## Produktions-Datenbank + Orchestrierung
Zentrale Steuerung: **eine SQLite-Datei** (`production.db`) auf ki-tower.
Alles passiert hier: Kanaele, Videos, Szenen, Jobs, Assets, Status.
### Datenbank-Schema
```sql
-- Kanaele (je einer pro YouTube-Kanal)
CREATE TABLE channels (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL, -- "Geld & Imperien Stil"
slug TEXT UNIQUE NOT NULL, -- "kanal-a"
avatar_path TEXT NOT NULL, -- "/data/channels/kanal-a/avatar.png"
voice_path TEXT NOT NULL, -- "/data/channels/kanal-a/voice-sample.wav"
style TEXT NOT NULL, -- "pip_20" oder "fullscreen"
prompt_template TEXT, -- System-Prompt fuer GPT-5.4 Skripte (Ton, Stil, Persoenlichkeit)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Videos (ein Eintrag pro Video)
CREATE TABLE videos (
id INTEGER PRIMARY KEY,
channel_id INTEGER REFERENCES channels(id),
title TEXT NOT NULL, -- "Irans Hyperschall-Schlag"
topic TEXT, -- Thema + Recherche-Notizen
status TEXT DEFAULT 'draft', -- draft → script → scenes → producing → assembly → review → published
script TEXT, -- Fertiges Skript (aus GPT-5.4 + Review)
scene_plan TEXT, -- JSON: Szenenplan (aus Qwen 14B)
voiceover_path TEXT, -- Pfad zur fertigen WAV
avatar_path TEXT, -- Pfad zum fertigen Avatar-Video
subtitle_path TEXT, -- Pfad zur SRT-Datei
final_path TEXT, -- Pfad zum fertigen MP4
thumbnail_path TEXT, -- Pfad zum Thumbnail
yt_title TEXT, -- YouTube-Titel
yt_description TEXT, -- YouTube-Description
yt_tags TEXT, -- YouTube-Tags (JSON array)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
published_at DATETIME
);
-- Szenen (aus dem Szenenplan, je eine Zeile pro Szene)
CREATE TABLE scenes (
id INTEGER PRIMARY KEY,
video_id INTEGER REFERENCES videos(id),
scene_nr INTEGER NOT NULL,
scene_type TEXT NOT NULL, -- "hero", "standard", "karte", "infografik"
prompt TEXT NOT NULL, -- Bildprompt (EN)
overlay TEXT, -- Text-Overlay (oder NULL)
duration_s REAL NOT NULL, -- Geschaetzte Dauer in Sekunden
image_path TEXT, -- Pfad zum generierten Bild (nach Produktion)
upscaled BOOLEAN DEFAULT 0,
status TEXT DEFAULT 'pending' -- pending → generating → done → failed
);
-- GPU-Jobs (jede Aufgabe die ein Worker erledigt)
CREATE TABLE jobs (
id INTEGER PRIMARY KEY,
video_id INTEGER REFERENCES videos(id),
job_type TEXT NOT NULL, -- "tts", "sdxl", "flux", "sadtalker", "whisper", "esrgan", "assembly"
gpu TEXT, -- "3090", "3080_0", "3080_1", "3080_2", "3080_3"
status TEXT DEFAULT 'queued', -- queued → running → done → failed → retry
input_data TEXT, -- JSON: was der Worker braucht
output_path TEXT, -- Pfad zum Ergebnis
error TEXT, -- Fehlermeldung falls failed
queued_at DATETIME DEFAULT CURRENT_TIMESTAMP,
started_at DATETIME,
finished_at DATETIME
);
```
### Verzeichnisstruktur
```
/data/ki-video/
├── production.db # SQLite — zentrale Steuerung
├── channels/ # Pro Kanal
│ ├── kanal-a/
│ │ ├── avatar.png # Referenz-Portraet (FLUX-generiert)
│ │ ├── voice-sample.wav # XTTS Voice-Cloning Referenz (~30s)
│ │ ├── style.json # { "avatar": "pip_20", "position": "bottom_right" }
│ │ └── prompt.md # GPT-5.4 System-Prompt (Persoenlichkeit, Ton, Stil)
│ └── kanal-b/
│ ├── avatar.png # Anderes Gesicht
│ ├── voice-sample.wav # Andere Stimme
│ ├── style.json # { "avatar": "fullscreen" }
│ └── prompt.md # Anderer Stil/Persoenlichkeit
├── videos/ # Pro Video ein Ordner
│ ├── 2026-03-16-iran-hyperschall/
│ │ ├── script.md # Fertiges Skript
│ │ ├── scenes.json # Szenenplan
│ │ ├── images/
│ │ │ ├── hero-001.png
│ │ │ ├── scene-002.png
│ │ │ ├── scene-003.png
│ │ │ └── ... # 80-120 Bilder
│ │ ├── audio/
│ │ │ ├── voiceover.wav # XTTS v2 Output
│ │ │ └── music.mp3 # Hintergrundmusik
│ │ ├── avatar/
│ │ │ ├── clip-001.mp4 # SadTalker 30s-Clips
│ │ │ ├── clip-002.mp4
│ │ │ └── full.mp4 # Zusammengefuegt
│ │ ├── subtitles.srt # Whisper → SRT
│ │ ├── thumbnail.png # FLUX hero + Text
│ │ └── final.mp4 # FERTIGES VIDEO
│ └── 2026-03-18-oelpreis-analyse/
│ └── ...
└── templates/
├── ffmpeg-pip20.sh # FFmpeg-Preset: 20% Avatar unten rechts
├── ffmpeg-fullscreen.sh # FFmpeg-Preset: 100% Avatar
├── ffmpeg-kenburns.sh # Ken-Burns-Effekt Presets
└── music/ # Lizenzfreie Hintergrundmusik
├── ambient-01.mp3
└── ambient-02.mp3
```
### Produktions-Ablauf (Statusmaschine)
```
┌──────────────────────────────────────────────────┐
│ │
┌─────────┐ │ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ draft │──────┼─►│ script │───►│ scenes │───►│ producing │ │
│ │ │ │ │ │ │ │ │ │
│ Thema │ │ │ GPT-5.4 │ │ Qwen 14B │ │ 5 GPUs │ │
│ anlegen │ │ │ + Review │ │ Szenen- │ │ parallel │ │
└─────────┘ │ └──────────┘ │ plan │ └─────┬─────┘ │
│ └──────────┘ │ │
│ ▼ │
│ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │ published │◄───│ review │◄──│ assembly │ │
│ │ │ │ │ │ │ │
│ │ YouTube │ │ Mensch │ │ FFmpeg │ │
│ │ Upload │ │ schaut │ │ PiP/Full │ │
│ └───────────┘ └──────────┘ └──────────┘ │
│ │
└──────────────────────────────────────────────────┘
Status-Uebergaenge:
draft → script : Mensch gibt Thema ein, startet GPT-5.4 Skript
script → scenes : Mensch reviewt Skript, bestaetigt, Qwen generiert Szenenplan
scenes → producing : Orchestrator erstellt Jobs fuer alle 5 GPUs
producing → assembly : Alle GPU-Jobs fertig (TTS + Avatar + Bilder + Untertitel)
assembly → review : FFmpeg Assembly fertig, Video liegt als MP4 vor
review → published : Mensch schaut Video, gibt frei, Upload
```
### Orchestrator-Logik (Python, laeuft auf ki-tower)
```
Alle 10 Sekunden:
1. Checke videos WHERE status = 'scenes'
→ Erstelle GPU-Jobs:
- 1x TTS-Job (3080 #0)
- N x SDXL-Jobs (3080 #0 nach TTS, #1)
- M x FLUX-Jobs (3090, nur hero-Szenen)
- 1x Whisper-Job (3080 #3, nach TTS)
- 1x ESRGAN-Job (3080 #3, nach Bilder)
- 1x SadTalker-Job (3080 #2, nach TTS)
2. Checke jobs WHERE status = 'queued'
→ Pruefe ob Abhaengigkeiten erfuellt:
- SadTalker wartet auf TTS (voiceover.wav muss existieren)
- Whisper wartet auf TTS
- ESRGAN wartet auf Bilder
→ Sende an Worker via HTTP REST
3. Checke jobs WHERE status = 'running'
→ Polle Worker-Status
→ Bei done: speichere output_path, update status
→ Bei failed: retry (max 3x), dann menschliche Aufmerksamkeit
4. Checke videos WHERE status = 'producing'
AND alle zugehoerigen jobs.status = 'done'
→ Starte Assembly-Job (FFmpeg)
→ Status → 'assembly'
5. Checke videos WHERE status = 'assembly'
AND assembly-job.status = 'done'
→ Status → 'review'
→ Benachrichtigung an Telegram (Hausmeister-Bot!)
```
### CLI fuer den Alltag
```bash
# Neues Video anlegen
./produce.py new --channel kanal-a --topic "Irans Hyperschall-Schlag"
# Status aller Videos
./produce.py status
# ID | Kanal | Thema | Status | Fortschritt
# 42 | kanal-a | Irans Hyperschall-Schlag | producing | 3/5 Jobs done
# 43 | kanal-b | Oelpreis-Analyse 2026 | script | Review pending
# Skript bestaetigen und Produktion starten
./produce.py approve-script 42
# Fertiges Video reviewen und freigeben
./produce.py publish 42 --yt-upload
# GPU-Auslastung
./produce.py gpus
# GPU | Status | Job | Video | Progress
# 3090 | busy | flux-hero | #42 | 8/15 images
# 3080 #0 | busy | sdxl-batch | #42 | 23/50 images
# 3080 #1 | busy | sdxl-batch | #42 | 31/50 images
# 3080 #2 | busy | sadtalker | #42 | 12:30/24:00
# 3080 #3 | idle | — | — | waiting for images
```
## Worker-Architektur
```
ki-tower (3090, Chef) gpu-worker (4x 3080, Worker) gpu-reserve (RX 6600 XT)
┌─────────────────────────┐ ┌──────────────────────────────┐ ┌──────────────────┐
│ Orchestrator (Python) │ │ Debian 12 + Docker + CUDA │ │ Reserve/Whisper │
│ ├── Job Queue (SQLite) │ Tailscale │ │
│ ├── production.db │ Tailscale │ │
│ ├── /api/submit-job │◄────────────►│ GPU #0: xtts+sdxl :8501 │ │ Whisper :8601 │
│ ├── /api/job-status │ │ GPU #1: sdxl-worker :8502 │ │ CPU-Batch :8602 │
│ └── /api/get-result │ Tailscale │ GPU #2: sadtalker :8503 │ │ (nur bei Bedarf)│
@ -379,12 +594,6 @@ ki-tower (3090, Chef) gpu-worker (4x 3080, Worker) gpu-
└─────────────────────────┘ └──────────────────────────────┘
```
Prinzipien:
- 1 Container = 1 GPU = 1 Aufgabe. Feste Zuordnung, kein dynamisches Scheduling.
- SQLite als Job-Queue. Ein User, nicht tausend. Eine Datei reicht.
- HTTP-APIs pro Worker. Orchestrator ruft per REST auf, pollt Status.
- Kein Service-Mesh, kein Kubernetes. Tailscale verbindet die zwei Maschinen.
## VRAM-Budget (ki-tower, sequentiell)
```