#!/usr/bin/env bash
# Idempotent setup for the video studio. Safe to re-run any time.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"
export PATH="/opt/homebrew/bin:$PATH"
say() { printf "\033[1;36m==>\033[0m %s\n" "$*"; }
warn() { printf "\033[1;33m[!]\033[0m %s\n" "$*"; }
die() { printf "\033[1;31m[x]\033[0m %s\n" "$*" >&2; exit 1; }
# --- 1. verify host tools ----------------------------------------------------
say "Checking host tools"
command -v git >/dev/null || die "git missing"
command -v node >/dev/null || die "node missing (need 22+)"
command -v python3>/dev/null || die "python3 missing"
command -v ffmpeg >/dev/null || die "ffmpeg missing (brew install ffmpeg-full)"
# Subtitle burn-in needs libass โ only ffmpeg-full (keg-only) has it on this setup
FFFULL="/opt/homebrew/opt/ffmpeg-full/bin"
if ! "$FFFULL/ffmpeg" -hide_banner -filters 2>/dev/null | grep -qw subtitles; then
warn "ffmpeg-full not present or lacks libass โ installing (keg-only, won't touch default ffmpeg)"
brew install ffmpeg-full
fi
if "$FFFULL/ffmpeg" -hide_banner -filters 2>/dev/null | grep -qw subtitles; then
say "ffmpeg-full has libass/subtitles โ (renders must prepend $FFFULL to PATH)"
else
warn "ffmpeg-full still missing the subtitles filter โ caption burn-in will fail"
fi
command -v uv >/dev/null || die "uv missing (curl -LsSf https://astral.sh/uv/install.sh | sh)"
NODE_MAJOR="$(node -v | sed 's/v//' | cut -d. -f1)"
[ "$NODE_MAJOR" -ge 22 ] || die "node $NODE_MAJOR < 22; upgrade node"
command -v bun >/dev/null || { warn "bun missing โ installing via brew"; brew install bun; }
command -v yt-dlp >/dev/null || warn "yt-dlp missing (optional, for online sources)"
say "Host tools OK: node $(node -v), bun $(bun -v), python $(python3 --version | cut -d' ' -f2), ffmpeg present"
# --- 2. vendor the two tools -------------------------------------------------
mkdir -p tools projects
clone_or_update() {
local url="$1" dir="tools/$2"
if [ -d "$dir/.git" ]; then
say "Updating $dir"; git -C "$dir" pull --ff-only 2>/dev/null || warn "$dir: pull skipped (local fork or shallow)"
elif [ -d "$dir" ]; then
say "$dir present (non-git vendored copy) โ leaving as is"
else
say "Cloning $url -> $dir"; git clone "$url" "$dir"
fi
}
clone_or_update https://github.com/browser-use/video-use.git video-use
clone_or_update https://github.com/heygen-com/hyperframes.git hyperframes
# --- 3. install deps ---------------------------------------------------------
say "Installing video-use Python deps (uv sync)"
( cd tools/video-use && uv sync )
say "Installing hyperframes deps (bun install)"
( cd tools/hyperframes && bun install )
say "Building hyperframes (bun run build) โ this can take a few minutes"
( cd tools/hyperframes && bun run build ) || warn "hyperframes build had issues; CLI may still work via 'bunx hyperframes'"
# --- 4. env / key ------------------------------------------------------------
if [ ! -f .env ]; then
cp .env.example .env 2>/dev/null || echo "ELEVENLABS_API_KEY=" > .env
warn ".env created โ add your ElevenLabs API key (https://elevenlabs.io/app/settings/api-keys)"
else
if grep -q '^ELEVENLABS_API_KEY=.\+' .env; then
say "ELEVENLABS_API_KEY present in .env"
else
warn ".env exists but ELEVENLABS_API_KEY looks empty โ fill it in"
fi
fi
# Mirror the key into video-use's own .env (it reads from its repo root)
if [ -f .env ] && grep -q '^ELEVENLABS_API_KEY=.\+' .env; then
cp .env tools/video-use/.env
say "Mirrored key into tools/video-use/.env"
fi
say "Done. Drop footage in projects/<YYYY-MM-DD-slug>/raw/ and say: edit this"