← back to Voice Clone

clone.sh

68 lines

#!/bin/bash
# clone.sh — upload sample.wav to ElevenLabs as Instant Voice Clone "Steve",
# capture the voice_id, route it through the secrets manager so every skill
# (app-demo-video, reviewed-demo-video, use-voice) can grab it from env.
#
# Free tier: 10 voice slots, IVC enabled, ~90K chars/mo TTS budget.

set -euo pipefail
SAMPLE=${1:-$HOME/Projects/voice-clone/sample.wav}
NAME=${2:-Steve}

[ -f "$SAMPLE" ] || { echo "[abort] sample not found: $SAMPLE  (run ./record.sh first)"; exit 1; }
KEY=$(grep '^ELEVENLABS_API_KEY=' ~/Projects/secrets-manager/.env | cut -d= -f2)
[ -z "$KEY" ] && { echo "[abort] ELEVENLABS_API_KEY missing in secrets-manager .env"; exit 1; }

echo "Uploading $(basename $SAMPLE) ($(du -h $SAMPLE | awk '{print $1}')) as voice '$NAME'..."

RESP=$(curl -s -X POST https://api.elevenlabs.io/v1/voices/add \
  -H "xi-api-key: $KEY" \
  -F "name=$NAME" \
  -F "description=Steve Abrams narrator voice — used for project walkthroughs and reviewed-demo-videos" \
  -F "files=@$SAMPLE" \
  -F 'labels={"use_case":"narration","style":"editorial"}')

VOICE_ID=$(echo "$RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('voice_id',''))" 2>/dev/null || echo "")

if [ -z "$VOICE_ID" ]; then
  echo "[fail] no voice_id in response:"
  echo "$RESP"
  exit 2
fi

echo "✓ Voice created — id: $VOICE_ID"
echo ""

# Persist to .env (master + desktop copy)
for ENVFILE in ~/Projects/secrets-manager/.env ~/Desktop/site-factory.env; do
  if [ -f "$ENVFILE" ]; then
    grep -v '^ELEVENLABS_VOICE_STEVE=' "$ENVFILE" > "$ENVFILE.tmp" || true
    echo "ELEVENLABS_VOICE_STEVE=$VOICE_ID" >> "$ENVFILE.tmp"
    mv "$ENVFILE.tmp" "$ENVFILE"
    echo "  wrote ELEVENLABS_VOICE_STEVE → $ENVFILE"
  fi
done

# Run a quick sanity TTS to verify the clone
SAMPLE_TEXT="This is Steve, narrating a walkthrough. Open the archive for every house."
echo ""
echo "Test render (8 words, ~3 sec audio):"
curl -s -X POST "https://api.elevenlabs.io/v1/text-to-speech/$VOICE_ID" \
  -H "xi-api-key: $KEY" -H "Content-Type: application/json" \
  -d "$(python3 -c "import json,sys; print(json.dumps({'text':'$SAMPLE_TEXT','model_id':'eleven_multilingual_v2','voice_settings':{'stability':0.5,'similarity_boost':0.85}}))")" \
  -o ~/Projects/voice-clone/test.mp3

if [ -s ~/Projects/voice-clone/test.mp3 ]; then
  echo "✓ Test MP3: ~/Projects/voice-clone/test.mp3"
  echo "  Listen:   afplay ~/Projects/voice-clone/test.mp3"
else
  echo "✗ Test render failed"
fi

echo ""
echo "═══════════════════════════════════════════════════════════"
echo "  Voice ID saved as ELEVENLABS_VOICE_STEVE in .env files"
echo "  Use in any script:  export VOICE_ID=\$ELEVENLABS_VOICE_STEVE"
echo "  Generate narration: ./say.sh \"some text\" out.mp3"
echo "═══════════════════════════════════════════════════════════"