← back to Crcp Pitch Video
narrate.py
42 lines
#!/usr/bin/env python3
# Generate per-scene cloned-voice narration (ElevenLabs, Steve Abrams voice) timed to the video.
import os, sys, json, subprocess, urllib.request
VOICE = "Xa9qV4wNbvSkdUWsYLzq" # Steve Abrams (cloned)
KEY = None
for p in [os.path.expanduser("~/Projects/secrets-manager/.env"), os.path.expanduser("~/.claude/skills/clone-voice/.env")]:
try:
for line in open(p):
if "ELEVENLABS_API_KEY" in line: KEY = line.strip().split("=", 1)[1].strip().strip('"'); break
except FileNotFoundError: pass
if KEY: break
assert KEY, "no ElevenLabs key"
# (start_sec, text) — each scene starts at its Sequence boundary in the 180s video
SCENES = [
(0, "This is C-R-C-P. Every licensed real estate agent, in America's most expensive markets — the prospecting platform built for loan officers, escrow, and title."),
(13, "Your business runs on one thing: relationships with the agents who close the deals. But which agents? In which markets? And how do you reach them, before every other rep in town beats you to it?"),
(36, "Meet C-R-C-P. Three hundred fifty-five thousand licensed agents. Twenty-one elite markets. The twenty priciest cities in the country — Beverly Hills, Manhattan, Aspen, Greenwich, Naples, and more. Every record is backed by real government license data. Verified, not guessed. Search any market, and see every agent, their brokerage, their license, and their listings."),
(79, "Here's why that matters to you. If you're a loan officer, agents send you borrowers, so target the top producers in luxury markets, where a single deal is a jumbo loan. If you run escrow, agents choose you, so get in front of the highest-volume agents first. And if you're in title, it's all relationships. Build yours with the agents who control the priciest inventory in America. One platform. Every deal-maker. Before anyone else finds them."),
(127, "So how do you approach them? Four channels. Calls are warm and personal, so reference a recent listing, and save them for your highest-value agents. Email scales, so lead with value, keep it short, one ask. Postcards cut through the digital noise, because luxury agents still notice great physical mail. And LinkedIn is relationship-first: connect, engage, and comment on their wins before you ever pitch."),
(169, "Every record gives you the name, brokerage, license, and city. Enrich it, and reach out. Start with the agents closing the biggest deals, at C-R-C-P dot agent abrams dot com."),
]
os.makedirs("audio", exist_ok=True)
total_chars = 0
for i, (start, text) in enumerate(SCENES):
total_chars += len(text)
body = json.dumps({"text": text, "model_id": "eleven_multilingual_v2",
"voice_settings": {"stability": 0.5, "similarity_boost": 0.8, "style": 0.15}}).encode()
req = urllib.request.Request(f"https://api.elevenlabs.io/v1/text-to-speech/{VOICE}",
data=body, headers={"xi-api-key": KEY, "Content-Type": "application/json", "Accept": "audio/mpeg"})
mp3 = f"audio/scene{i}.mp3"
with urllib.request.urlopen(req) as r, open(mp3, "wb") as f:
f.write(r.read())
# duration
dur = subprocess.check_output(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", mp3]).decode().strip()
print(f" scene{i}: start {start}s, {len(text)} chars, {float(dur):.1f}s audio -> {mp3}")
json.dump([{"i": i, "start": s} for i, (s, t) in enumerate(SCENES)], open("audio/timing.json", "w"))
print(f"TOTAL {total_chars} chars (~${total_chars/1000*0.30:.2f} ElevenLabs)")