#!/usr/bin/env bash # # End-to-end re-render of the dashboard demo videos. # # All per-storyboard knobs (aspect, fps, bitrate, prompt text, voice persona, # poster timestamp, brand strings…) live on the Storyboard objects in # src/storyboard.ts. To add a vertical cut or change the voice, edit that # file — this script only handles target/auth/transport concerns. # # Two targets: # local (default) — assumes the docker-compose stack on host.docker.internal, # bootstraps a recorder admin user automatically. # prod — points at https://perfect-postcode.co.uk and skips the # bootstrap step; you supply real account credentials. # # Usage: # ./render.sh # local stack # ./render.sh --prod # prod (requires LOGIN_EMAIL/LOGIN_PASSWORD) # ./render.sh --target prod # same as --prod # ./render.sh --fresh-auth # force re-auth even if cache is fresh # ./render.sh --no-encode # stop at WebM, skip MP4 encode # ./render.sh --no-audio # skip Qwen3-TTS narration; silent MP4 # FORCE_AUTH=1 ./render.sh # same as --fresh-auth # APP_URL=http://localhost:3001 ./render.sh # override frontend URL # # Cred env vars (read for both targets, but prod has no fallback defaults): # LOGIN_EMAIL, LOGIN_PASSWORD — the dashboard account to record as # (same email/password you'd type into # the login modal) # PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD — PocketBase superuser, only used by # --target local to bootstrap the # recorder user; ignored on --prod set -euo pipefail # -- target ------------------------------------------------------------------- TARGET="${TARGET:-local}" parsed_args=() while [ $# -gt 0 ]; do case "$1" in --prod) TARGET=prod; shift ;; --local) TARGET=local; shift ;; --target) TARGET="$2"; shift 2 ;; --target=*) TARGET="${1#--target=}"; shift ;; *) parsed_args+=("$1"); shift ;; esac done set -- "${parsed_args[@]+"${parsed_args[@]}"}" case "$TARGET" in local|prod) ;; *) echo "Unknown --target: $TARGET (expected: local, prod)" >&2; exit 2 ;; esac # -- environment (target-specific URLs and credentials) ---------------------- if [ "$TARGET" = "prod" ]; then # Prod serves frontend, /api/*, and /pb/* off the same domain. export APP_URL="${APP_URL:-https://perfect-postcode.co.uk}" export PB_URL="${PB_URL:-https://perfect-postcode.co.uk/pb}" export API_URL="${API_URL:-https://perfect-postcode.co.uk}" # Prod has no recorder-bootstrap path: the user supplies a real account. PB_BOOTSTRAP_ADMIN="${PB_BOOTSTRAP_ADMIN:-0}" if [ -z "${LOGIN_EMAIL:-}" ] || [ -z "${LOGIN_PASSWORD:-}" ]; then echo "FAIL: --prod requires LOGIN_EMAIL and LOGIN_PASSWORD (your dashboard login)." >&2 echo " Example: LOGIN_EMAIL=you@example.com LOGIN_PASSWORD='...' $0 --prod" >&2 exit 2 fi else export APP_URL="${APP_URL:-http://host.docker.internal:3001}" export PB_URL="${PB_URL:-http://host.docker.internal:8090}" export API_URL="${API_URL:-http://host.docker.internal:8001}" PB_ADMIN_EMAIL="${PB_ADMIN_EMAIL:-admin@propertymap.local}" PB_ADMIN_PASSWORD="${PB_ADMIN_PASSWORD:-propertymap-dev-2024}" LOGIN_EMAIL="${LOGIN_EMAIL:-demo-video@local.test}" LOGIN_PASSWORD="${LOGIN_PASSWORD:-DemoVideoPass123!}" PB_BOOTSTRAP_ADMIN="${PB_BOOTSTRAP_ADMIN:-1}" fi export PB_BOOTSTRAP_ADMIN export LOGIN_EMAIL LOGIN_PASSWORD # Per-target storage state — switching targets must not reuse a stale token. # config.ts reads AUTH_STATE_FILE for AUTH_STATE_PATH. export AUTH_STATE_FILE="${AUTH_STATE_FILE:-auth.${TARGET}.json}" AUTH_TTL_HOURS="${AUTH_TTL_HOURS:-24}" # re-auth if cache older than this # Where the homepage