150 lines
5.1 KiB
Bash
Executable file
150 lines
5.1 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# End-to-end re-render of the dashboard demo video.
|
|
#
|
|
# Defaults assume you run from inside this repo's vscode-server container
|
|
# (where host.docker.internal reaches the docker-compose stack). Override
|
|
# any URL/credential via env vars at the top.
|
|
#
|
|
# Usage:
|
|
# ./render.sh # full pipeline (uses cached auth.json if fresh)
|
|
# ./render.sh --fresh-auth # force re-auth even if auth.json exists
|
|
# ./render.sh --no-encode # stop at WebM, skip MP4 encode
|
|
# FORCE_AUTH=1 ./render.sh # same as --fresh-auth
|
|
# APP_URL=http://localhost:3001 ./render.sh # override frontend URL
|
|
|
|
set -euo pipefail
|
|
|
|
# -- config (override via env) -------------------------------------------------
|
|
APP_URL="${APP_URL:-http://host.docker.internal:3001}"
|
|
PB_URL="${PB_URL:-http://host.docker.internal:8090}"
|
|
API_URL="${API_URL:-http://host.docker.internal:8001}"
|
|
PB_EMAIL="${PB_EMAIL:-demo-video@local.test}"
|
|
PB_PASSWORD="${PB_PASSWORD:-DemoVideoPass123!}"
|
|
MAX_DURATION_S="${MAX_DURATION_S:-15}"
|
|
AUTH_TTL_HOURS="${AUTH_TTL_HOURS:-24}" # re-auth if auth.json older than this
|
|
|
|
FRESH_AUTH="${FORCE_AUTH:-0}"
|
|
DO_ENCODE=1
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--fresh-auth) FRESH_AUTH=1 ;;
|
|
--no-encode) DO_ENCODE=0 ;;
|
|
-h|--help)
|
|
sed -n '3,18p' "$0"
|
|
exit 0 ;;
|
|
*) echo "Unknown arg: $arg" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
# -- helpers ------------------------------------------------------------------
|
|
say() { printf '\n[render] %s\n' "$*"; }
|
|
fail() { printf '\n[render] FAIL: %s\n' "$*" >&2; exit 1; }
|
|
|
|
http_code() {
|
|
curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 5 "$1" || echo "000"
|
|
}
|
|
|
|
wait_for() {
|
|
local url="$1" desc="$2" timeout="${3:-90}"
|
|
say "Waiting for $desc ($url)"
|
|
for i in $(seq 1 "$timeout"); do
|
|
if [ "$(http_code "$url")" = "200" ]; then
|
|
say " ready after ${i}s"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
fail "$desc not reachable after ${timeout}s"
|
|
}
|
|
|
|
# -- stack health -------------------------------------------------------------
|
|
say "Checking stack health"
|
|
fe_code="$(http_code "$APP_URL/")"
|
|
api_code="$(http_code "$API_URL/api/features")"
|
|
pb_code="$(http_code "$PB_URL/api/health")"
|
|
echo " frontend=$fe_code api=$api_code pocketbase=$pb_code"
|
|
|
|
if [ "$fe_code" != "200" ] || [ "$pb_code" != "200" ]; then
|
|
fail "Stack down. From the repo root run: docker compose up -d"
|
|
fi
|
|
if [ "$api_code" != "200" ]; then
|
|
wait_for "$API_URL/api/features" "Rust API" 120
|
|
fi
|
|
|
|
# -- node deps ----------------------------------------------------------------
|
|
if [ ! -d node_modules ]; then
|
|
say "Installing npm deps"
|
|
npm install --no-audit --no-fund
|
|
fi
|
|
|
|
# Chromium binary lives in Playwright's cache; install if missing.
|
|
if ! npx --no-install playwright --version >/dev/null 2>&1 \
|
|
|| [ ! -d "$HOME/.cache/ms-playwright" ] \
|
|
|| ! find "$HOME/.cache/ms-playwright" -maxdepth 1 -name "chromium-*" -print -quit | grep -q .; then
|
|
say "Installing Playwright Chromium"
|
|
npx playwright install chromium
|
|
fi
|
|
|
|
# -- build --------------------------------------------------------------------
|
|
say "Compiling TypeScript"
|
|
./node_modules/.bin/tsc
|
|
|
|
# -- auth ---------------------------------------------------------------------
|
|
need_auth=0
|
|
if [ "$FRESH_AUTH" = "1" ] || [ ! -f auth.json ]; then
|
|
need_auth=1
|
|
else
|
|
# File mtime check, portable: if older than TTL, refresh.
|
|
if [ "$(find auth.json -mmin "+$((AUTH_TTL_HOURS * 60))" -print 2>/dev/null)" ]; then
|
|
say "auth.json is older than ${AUTH_TTL_HOURS}h, will refresh"
|
|
need_auth=1
|
|
fi
|
|
fi
|
|
|
|
if [ "$need_auth" = "1" ]; then
|
|
say "Minting fresh auth.json (user: $PB_EMAIL)"
|
|
PB_URL="$PB_URL" PB_EMAIL="$PB_EMAIL" PB_PASSWORD="$PB_PASSWORD" \
|
|
APP_URL="$APP_URL" \
|
|
node dist/auth.js
|
|
else
|
|
say "Reusing existing auth.json"
|
|
fi
|
|
|
|
# -- record -------------------------------------------------------------------
|
|
say "Recording"
|
|
mkdir -p output
|
|
# Wipe last run's leaking artifacts so the rename step picks up *this* run.
|
|
rm -f output/recording.webm output/recording.mp4 output/page@*.webm output/page@*.webm.untrimmed
|
|
|
|
APP_URL="$APP_URL" MAX_DURATION_S="$MAX_DURATION_S" node dist/record.js
|
|
|
|
if [ ! -s output/recording.webm ]; then
|
|
fail "recording.webm missing or empty"
|
|
fi
|
|
|
|
# -- encode -------------------------------------------------------------------
|
|
if [ "$DO_ENCODE" = "1" ]; then
|
|
if ! command -v ffmpeg >/dev/null 2>&1; then
|
|
fail "ffmpeg not on PATH; rerun with --no-encode if you only need the WebM"
|
|
fi
|
|
say "Encoding to MP4"
|
|
ffmpeg -y -loglevel warning -i output/recording.webm \
|
|
-c:v libx264 -pix_fmt yuv420p -crf 18 -movflags +faststart \
|
|
output/recording.mp4
|
|
fi
|
|
|
|
# -- report -------------------------------------------------------------------
|
|
say "Done"
|
|
if command -v ffprobe >/dev/null 2>&1; then
|
|
for f in output/recording.webm output/recording.mp4; do
|
|
[ -f "$f" ] || continue
|
|
dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$f")
|
|
size=$(stat -c '%s' "$f" 2>/dev/null || stat -f '%z' "$f")
|
|
printf ' %s %ss %s bytes\n' "$f" "$(printf '%.2f' "$dur")" "$size"
|
|
done
|
|
else
|
|
ls -la output/recording.* 2>/dev/null || true
|
|
fi
|