Files
videogen/README.md
2026-08-17 23:41:49 +02:00

6.1 KiB
Raw Blame History

BrandLoop (videogen)

KI-Video-Generierung mit lernender Brand-Knowledge-Base (Elo-Scoring über Attribute).

  • Prototyp: prototyp-app.html statische Preview unter videogen.project.webklar.com
  • Desktop-Ansicht: Design-Handoff für die Desktop-Adaption in design/desktop-ansicht/ (Spezifikation: README.md)
  • Backend: selbst gehostetes Appwrite (https://appwrite.webklar.com/v1), Projekt BrandLoop 6a5cee34002bb8360c34, Datenbank brandloop

App (client/)

Expo / React Native, eine Codebase für Web und nativ. Stand: Gerüst aus E1 drei Tabs, sonst leer.

npm --prefix client run web

Bildgenerierung

Zwei Job-Typen arbeitet der Dispatcher ab serverseitig, weil der Anbieter-Schlüssel nicht ins Client-Bundle darf:

Job Entsteht bei Ergebnis
modell_gen Modell anlegen (Person, Produkt, Kulisse) 4 Varianten als Entwurfs-Versionen, eine wird freigegeben
bild_gen Post erstellen Bilderkette in post_images

Logo-Sonderfall: Wer eine Datei hochlädt, bekommt sie unverändert als freigegebene Version ab da wird sie nie neu generiert. Wer keine hat, bekommt Entwürfe. Sobald etwas freigegeben ist, weist der Dispatcher jede weitere Generierung auf dieses Asset ab. So bleibt die Brand-Regel gewahrt, ohne Marken ohne Logo auszusperren.

node scripts/job-dispatcher.mjs

Ohne Angabe läuft er auf auto und probiert der Reihe nach Ark → OpenRouter → Platzhalter. Ark steht vorn, weil Seedream mit 0,0450,090 $ je Bild günstiger ist als Gemini mit 0,14 $ und in eu-west läuft. Ist Ark nicht freigeschaltet, fällt die Kette still auf OpenRouter zurück; jede Zeile im Log nennt den Anbieter, der geliefert hat.

Unterschieden wird zwischen „Anbieter nicht verfügbar" (Konto zu, kein Guthaben) und einem echten Fehler nur beim ersten Fall wird weitergereicht, damit ein inhaltlicher Fehler nicht stillschweigend überdeckt wird.

Fest wählen geht weiterhin: BILD_ANBIETER=stub|ark|openrouter.

Voraussetzungen je Anbieter: OpenRouter braucht gekauftes Guthaben (eine hinterlegte Karte genügt nicht es ist ein Prepaid-Konto). BytePlus Ark braucht die Aktivierung je Modell in der Konsole unter Model activation; das ist keine Zahlungsfrage, verlangt aber die Zustimmung zu mehreren Verträgen.

Demo-Daten

Demo-Daten zum Anschauen (Marke, zwei Ordner, vier Modelle mit echten Bildern aus uploads/) legt alles als Client an, belegt damit nebenbei die Rechte, und ist idempotent:

node scripts/seed-demo.mjs

Die Mandantentrennung lässt sich jederzeit nachprüfen legt zwei echte Konten an, prüft über die API und räumt sich selbst auf:

node scripts/test-mandanten.mjs

Die Appwrite-SDKs sind exakt gepinnt (appwrite@23.0.0, react-native-appwrite@0.25.0): der Server läuft 1.8.1, neuere SDKs sprechen Response-Format 1.9.x. Beim Server-Upgrade beide zusammen anheben.

Konfiguration (Endpoint, Projekt-ID, DB-ID) steht in client/app.json unter expo.extra.appwrite. Der Appwrite-Server-Key gehört dort nicht hinein er landet sonst im Client-Bundle; er steht in der .env im Repo-Root und wird nur von scripts/ und später von Appwrite-Functions benutzt.

Datenbank-Setup

Das komplette Schema (22 Tabellen, Indizes, 5 Storage-Buckets, internes Dev-Team) legt scripts/setup-appwrite.mjs an idempotent, kann nach Plan-Erweiterungen jederzeit erneut laufen:

# API-Key liegt als BRANDLOOP_APPWRITE_API_KEY in /home/webklar/apps/.env, dann einfach:
node scripts/setup-appwrite.mjs

# oder explizit:
APPWRITE_API_KEY=... node scripts/setup-appwrite.mjs

Der API-Key braucht die Scopes Databases/Tables (read+write), Storage/Buckets (read+write), Teams (read+write).

Prompt-Templates (P1P18)

Alle 18 Prompts des Prompt-Inventars liegen versioniert in prompts/ (Konventionen und Slot-Registry: prompts/README.md) und werden mit scripts/seed-prompts.mjs in die Tabelle prompt_templates geseedet — idempotent: Inhaltsänderung ⇒ neue Zeile version+1, alte Zeilen aktiv=false. P8 ist eine Familie (1× Regie-Kern + 4 Modell-Adapter seedance/veo/sora/wan) = 22 Zeilen. Das Skript seedet außerdem die 4 globalen Brand-Regeln in rules.

node scripts/seed-prompts.mjs

Bewusste Abweichungen vom DB-Plan

  • Referenzen als indizierte String-Spalten (size 64) statt Relationship-Spalten. Appwrite-Relationships sind nicht filter-/indizierbar der Top-N-Index auf attribute_scores (brand_id, folder_id, category_id, score DESC) und alle Listen-Queries brauchen aber genau das. Many-to-many (videos.attribute_ids) ist ein String-Array (Query.contains). IDs mit 64 Zeichen, weil uuid4 = 36 Zeichen.
  • created_at-Spalten entfallen Appwrite pflegt $createdAt automatisch (Indizes nutzen $createdAt direkt, z. B. score_events, jobs).
  • Enum-Werte ohne Umlaute (laeuft statt läuft).
  • attributes.tags: Key-Index statt Volltext-Index Appwrite verbietet Fulltext auf Array-Spalten; Tag-Suche läuft über Query.contains.
  • S3/Hetzner-Backend für Storage ist eine instanzweite Appwrite-Einstellung (_APP_STORAGE_DEVICE) und betrifft alle Projekte auf dem Server wird daher nicht vom Skript gesetzt, sondern muss bewusst am Server konfiguriert werden. Aktuell: lokales Storage.

Rechte-Modell

  • rules + prompt_templates: Tabellen-Rechte nur für Team internal (Dev-Modus) 🔒
  • Alle anderen Tabellen: rowSecurity an, keine Tabellen-Rechte Zeilen bekommen beim Anlegen die Team-Permission der jeweiligen Brand (Multi-Tenant).
  • consents-Bucket: zusätzlich Lese-Recht für Team internal.
  • Append-only per Konvention (Server-Key schreibt): score_events, video_metrics, jobs.

Noch offen (laut Plan)

  • Appwrite Functions: elo-update, favorit-berechnen, ads-metriken-holen, kategorie-durchschnitt, archivierung, job-dispatcher
  • Stripe-Anbindung (metered billing über usage_records)