← back to Contact Mailer Ideas
render-pitch-video.sh
59 lines
#!/bin/bash
# render-pitch-video.sh — generate a 30-sec scalability-focused VC pitch video for ONE idea
# Usage: render-pitch-video.sh <name> <score> <headline> <pitch_script>
# Output: ~/Videos/shipped-apps/Pitch-<Name>-2026-05-11.mp4
set -euo pipefail
NAME="${1:?idea name required}"
SCORE="${2:?score required}"
HEADLINE="${3:?headline required}"
SCRIPT="${4:?pitch script required}"
DATE=$(date +%Y-%m-%d)
OUT_DIR="$HOME/Videos/shipped-apps"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TMP=$(mktemp -d)
trap "rm -rf $TMP" EXIT
mkdir -p "$OUT_DIR"
SLUG=$(echo "$NAME" | tr -cd '[:alnum:]')
OUT="$OUT_DIR/Pitch-$SLUG-$DATE.mp4"
# ───── 1. Slide via PIL
python3 "$SCRIPT_DIR/render-slide.py" "$NAME" "$HEADLINE" "$SCORE" "$TMP/slide.png" > /dev/null
# ───── 2. TTS via ElevenLabs (Adam = stock male voice)
ELEVENLABS_API_KEY=$(grep '^ELEVENLABS_API_KEY=' ~/Projects/secrets-manager/.env | cut -d= -f2)
VOICE_ID="pNInz6obpgDQGcFmaJgB" # Adam
curl -sS -X POST "https://api.elevenlabs.io/v1/text-to-speech/$VOICE_ID" \
-H "xi-api-key: $ELEVENLABS_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg text "$SCRIPT" '{
text: $text,
model_id: "eleven_turbo_v2_5",
voice_settings: {stability: 0.5, similarity_boost: 0.75, style: 0.3, use_speaker_boost: true}
}')" -o "$TMP/voice.mp3"
# Check audio actually downloaded (not an error JSON)
if [ "$(stat -f %z "$TMP/voice.mp3")" -lt 5000 ]; then
echo "ERROR: ElevenLabs returned tiny file (likely error). Raw response:" >&2
cat "$TMP/voice.mp3" >&2
exit 1
fi
# Pad/trim audio to exactly 30s
ffmpeg -y -loglevel error -i "$TMP/voice.mp3" -af "apad=whole_dur=30" -t 30 "$TMP/voice30.mp3"
# ───── 3. Combine static PNG slide + audio → 30s MP4
ffmpeg -y -loglevel error \
-loop 1 -framerate 30 -i "$TMP/slide.png" \
-i "$TMP/voice30.mp3" \
-c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 -tune stillimage \
-c:a aac -b:a 128k \
-shortest -movflags +faststart \
"$OUT" 2>&1 | tail -3
echo "OK: $OUT ($(stat -f %z "$OUT") bytes)"