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,045–0,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 (P1–P18)

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)
S
Description
videogen
Readme
3.7 MiB
0 Stars 4 Watchers 0 Forks
Languages
HTML 46.8%
JavaScript 31.4%
TypeScript 20.2%
CSS 1.6%