PLAN.md: Produktions-DB Schema, Verzeichnisstruktur, Statusmaschine, Orchestrator-Logik, CLI
This commit is contained in:
parent
d4c8e49f51
commit
642d53a927
1 changed files with 216 additions and 7 deletions
223
ki-video/PLAN.md
223
ki-video/PLAN.md
|
|
@ -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.
|
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.
|
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
|
## Worker-Architektur
|
||||||
|
|
||||||
```
|
```
|
||||||
ki-tower (3090, Chef) gpu-worker (4x 3080, Worker) gpu-reserve (RX 6600 XT)
|
ki-tower (3090, Chef) gpu-worker (4x 3080, Worker) gpu-reserve (RX 6600 XT)
|
||||||
┌─────────────────────────┐ ┌──────────────────────────────┐ ┌──────────────────┐
|
┌─────────────────────────┐ ┌──────────────────────────────┐ ┌──────────────────┐
|
||||||
│ Orchestrator (Python) │ │ Debian 12 + Docker + CUDA │ │ Reserve/Whisper │
|
│ 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/submit-job │◄────────────►│ GPU #0: xtts+sdxl :8501 │ │ Whisper :8601 │
|
||||||
│ ├── /api/job-status │ │ GPU #1: sdxl-worker :8502 │ │ CPU-Batch :8602 │
|
│ ├── /api/job-status │ │ GPU #1: sdxl-worker :8502 │ │ CPU-Batch :8602 │
|
||||||
│ └── /api/get-result │ Tailscale │ GPU #2: sadtalker :8503 │ │ (nur bei Bedarf)│
|
│ └── /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)
|
## VRAM-Budget (ki-tower, sequentiell)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue