operator manual / legacy edition

Documentation

A self-hostable AI faceless-video auto-poster. You bring API keys; it generates a daily AI video per account and posts it on schedule.

Overview

Each scheduled post runs one resumable pipeline:

AI character → AI video (Kling) → AI music (Stable Audio) → viral text overlay → + your app demo → caption → publish (Zernio).

You describe your app once; a minute-by-minute cron fires due slots; a detached worker runs each job and is crash-resumable. Cost ≈ $0.70/video (fal) + cents (OpenAI), capped by a daily budget.

Requirements

  • PHP 8.3+ with pdo_sqlite, curl, mbstring
  • ffmpeg + ffprobe built with libfreetype (the overlay uses drawtext) + fonts-dejavu
  • A web server with PHP-FPM (Caddy / nginx / Apache), docroot public/
  • Accounts: fal.ai, OpenAI, Cloudflare R2, Google OAuth, Zernio

Quick start

php bin/setup.php                 # checks deps, creates .env, inits the SQLite DB
$EDITOR .env                      # fill in FAL_KEY, OPENAI_API_KEY, R2_*, GOOGLE_CLIENT_ID, secrets
php -S 127.0.0.1:8090 public/index.php
# open http://127.0.0.1:8090 → sign in

Then in the app: connect your Zernio key → create a workspace → add accounts → set up an AI character per account → upload your app-demo clips → set a schedule.

Environment

Everything is in .env (gitignored, chmod 600). See .env.example for every key with notes. Generate secrets with php -r "echo bin2hex(random_bytes(32)).PHP_EOL;".

KeyWhat
FAL_KEYfal.ai — AI image / video / music
FAL_DAILY_BUDGET_USDkill-switch: stop generating past this daily spend
OPENAI_API_KEY / OPENAI_MODELcaption + on-video hook text
R2_*Cloudflare R2 (S3-compatible) storage
GOOGLE_CLIENT_IDsign-in (Authorized JS origin = APP_URL)
APP_NAMEyour brand (navbar + titles)
DRY_RUN1 = generate but don't post

Operator access

Single-operator by design. ENTITLEMENT_ALLOWLIST empty = open (anyone who signs in); set it to your email to lock the instance to yourself. ADMIN_EMAILS controls admin-only features.

The pipeline

Per post: a fresh variation of the account's locked character image → a 5s Kling reaction clip → a Stable Audio bed (vibe per character) → the OpenAI hook is burned on TikTok-style (white + black stroke, centered) → your app-demo clip is appended → muxed with the music → an OpenAI caption → published via Zernio. State persists per stage so a crashed worker resumes.

First real post

  1. Set up an AI character on an account (Schedule tab → describe → save).
  2. Upload at least one app-demo clip (Library tab).
  3. Enable a schedule hour, or watch the cron fire the due slot.
  4. Watch the Overview dashboard light up IMAGE → VIDEO → MUSIC → OVERLAY → PUBLISH.

Deploy

Serve public/ with PHP-FPM behind Caddy/nginx. Add the cron (every minute):

* * * * * php /path/to/app/bin/cron.php >> /path/to/app/logs/cron.log 2>&1

Set APP_ENV=production, DEV_LOGIN=0, and a real SESSION_COOKIE_DOMAIN.

Customize

  • Brand: APP_NAME in .env; logo SVG in public/views/_brand.php.
  • Theme: CSS variables at the top of public/assets/style.css (light/dark/blue).
  • Prompts & models: character/video/music prompts in src/gen_pipeline.php; caption/hook in src/openai_caption.php; model IDs + costs in src/fal_client.php.
  • Overlay style: font/size/stroke in Mux::hookFilters() (src/ffmpeg_mux.php).

Code layout

public/        index.php (front controller) + views + assets
src/           gen_pipeline, fal_client, openai_caption, ffmpeg_mux,
               zernio_client, r2_client, media_select, auth, gate, db, crypto
bin/           cron.php (scheduler), gen_worker.php (one job), setup.php
db/schema.sql  SQLite schema (IF NOT EXISTS; safe to re-apply)

Support

Questions? Email support@goviraldev.com. License terms are in LICENSE.