""" Hero reel v3 — 7 shots with corrected prompts. """ import os, sys, time, subprocess try: from google import genai from google.genai import types except ImportError: os.system(f"{sys.executable} -m pip install google-genai --quiet") from google import genai from google.genai import types API_KEY = os.environ.get("GEMINI_API_KEY", "AIzaSyB_1p8KvaT_rdNJGPs8HKk8bKsvUlcL6Kg") BASE_DIR = os.path.dirname(os.path.dirname(__file__)) VID_DIR = os.path.join(BASE_DIR, "assets", "videos", "hero", "clips") REEL_OUT = os.path.join(BASE_DIR, "assets", "videos", "hero", "hero-reel.mp4") os.makedirs(VID_DIR, exist_ok=True) client = genai.Client(api_key=API_KEY) SHOTS = [ { "name": "v3-shot-01", "prompt": ( "Medium shot. A family of four — two adults and two children — walks through the front door " "of a warm residential home. Camera is inside the home facing them as they enter. " "After they walk in, the camera slowly pans down to the carpet, revealing dirty footprints " "and mud tracked onto the beige carpet. Warm natural light. Photorealistic." ), }, { "name": "v3-shot-02", "prompt": ( "Medium shot, slow zoom in. A glass of red wine has spilled onto a light grey upholstered sofa. " "The camera starts wide on the sofa then slowly zooms in on the dark wine stain spreading " "across the fabric. The stain is clearly visible, soaked into the cushion. " "Warm living room light. No people. Photorealistic." ), }, { "name": "v3-shot-03", "prompt": ( "Medium shot at low angle, camera near floor level. Inside a bright commercial office entryway. " "A person wearing work boots walks through the entry door toward the camera and past it. " "The camera is low, showing the boots and lower legs as they walk across the carpet past the lens. " "Office carpet visible, natural light from glass doors behind. Photorealistic." ), }, { "name": "v3-shot-04", "prompt": ( "Close-up cinematic shot. A technician's gloved hand holds a small upholstery extraction wand — " "a flat rectangular handheld tool. The technician presses the wand firmly onto a wine-stained " "sofa cushion and draws it slowly across the fabric. The stain visibly lifts as the wand moves. " "Suction only — no water sprays out. Slow motion. Natural light. Photorealistic." ), }, { "name": "v3-shot-05", "prompt": ( "Wide cinematic shot. Camera slowly pans across the entrance of a modern commercial office building. " "Clean grey commercial carpet stretches across the lobby floor. Large windows, glass doors, " "professional lighting. The carpet looks freshly cleaned and spotless. " "No people. No machines. Photorealistic." ), }, { "name": "v3-shot-06", "prompt": ( "Wide cinematic shot. Camera slowly pans across a bright, clean residential living room. " "Plush clean beige carpet throughout. Comfortable furniture — sofa, armchairs, coffee table. " "Warm natural afternoon light through large windows. The room looks fresh and inviting. " "No people. No cleaning equipment. Photorealistic." ), }, { "name": "v3-shot-07", "prompt": ( "Wide cinematic shot. Camera moves slowly forward through an upscale restaurant dining room. " "Rich carpet covers the floor. White tablecloths, warm ambient lighting, wood accents. " "The carpet looks clean, deep, and well-maintained as the camera glides through the space. " "No people. Photorealistic, luxurious atmosphere." ), }, ] MODEL = "veo-3.1-generate-preview" def poll(op, timeout=600): elapsed = 0 while not op.done: if elapsed >= timeout: return None print(f" Waiting... ({elapsed}s)") time.sleep(15) elapsed += 15 op = client.operations.get(op) return op saved = [] for i, item in enumerate(SHOTS): out_path = os.path.join(VID_DIR, f"{item['name']}.mp4") print(f"\n[{i+1}/{len(SHOTS)}] {item['name']}...") try: op = client.models.generate_videos( model=MODEL, prompt=item["prompt"], config=types.GenerateVideosConfig( aspect_ratio="16:9", resolution="720p", duration_seconds=6, number_of_videos=1, ), ) op = poll(op) if op and op.response and op.response.generated_videos: vid = op.response.generated_videos[0].video video_bytes = client.files.download(file=vid) if video_bytes: with open(out_path, "wb") as f: f.write(video_bytes) print(f" Saved ({os.path.getsize(out_path)//1024}KB)") saved.append(out_path) else: try: vid.save(out_path) print(f" Saved via .save()") saved.append(out_path) except Exception as e2: print(f" Download failed: {e2}") else: print(f" No video returned") except Exception as e: print(f" Error: {e}") print(f"\n{len(saved)}/{len(SHOTS)} shots saved") if len(saved) >= 2: concat_file = os.path.join(VID_DIR, "concat-v3.txt") clips = [os.path.join(VID_DIR, f"{s['name']}.mp4") for s in SHOTS if os.path.exists(os.path.join(VID_DIR, f"{s['name']}.mp4"))] with open(concat_file, "w") as f: for p in clips: f.write(f"file '{p}'\n") result = subprocess.run( ["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", concat_file, "-c:v", "libx264", "-crf", "22", "-preset", "fast", "-movflags", "+faststart", REEL_OUT], capture_output=True, text=True ) if result.returncode == 0: print(f" Reel saved ({os.path.getsize(REEL_OUT)//1024}KB)") else: print(f" ffmpeg error: {result.stderr[-300:]}") print("\nDone.")