Buckets:
| """ | |
| run-full-pipeline.py — End-to-End Video Pipeline | |
| Script → Scene Planning → B-roll Generation → Video Assembly → Quality Check | |
| Usage: | |
| python run-full-pipeline.py --script script.txt --output final.mp4 | |
| python run-full-pipeline.py --script script.txt --gemini-key KEY --generate-images --output final.mp4 | |
| """ | |
| import argparse, json, os, subprocess, sys, time, shutil | |
| SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| PIPELINE_DIR = os.path.dirname(SCRIPT_DIR) | |
| def run_scene_planner(script_path, images_dir, output_json, gemini_key=None): | |
| """Step 1: Plan scenes with transitions.""" | |
| print("\n" + "="*60) | |
| print("STEP 1: Scene Planning") | |
| print("="*60) | |
| cmd = [sys.executable, os.path.join(SCRIPT_DIR, "scene-planner.py"), | |
| script_path, "--images-dir", images_dir, "--output", output_json] | |
| if gemini_key: | |
| cmd.extend(["--gemini-key", gemini_key]) | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| print(result.stdout) | |
| if result.returncode != 0: | |
| print(f"ERROR: {result.stderr}") | |
| return False | |
| return True | |
| def generate_broll_images(scenes_json, images_dir): | |
| """Step 2: Generate B-roll images via ComfyUI API.""" | |
| print("\n" + "="*60) | |
| print("STEP 2: B-roll Image Generation") | |
| print("="*60) | |
| # Check if ComfyUI is available | |
| try: | |
| import urllib.request | |
| req = urllib.request.Request("http://127.0.0.1:8188/system_stats") | |
| urllib.request.urlopen(req, timeout=3) | |
| print(" ComfyUI detected!") | |
| except: | |
| print(" ComfyUI not running - skipping image generation") | |
| print(" Using existing images from", images_dir) | |
| return True | |
| # Generate images from scene prompts | |
| cmd = [sys.executable, os.path.join(SCRIPT_DIR, "comfyui-client.py"), | |
| "--prompt-file", scenes_json, "--output-dir", images_dir] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| print(result.stdout) | |
| if result.returncode != 0: | |
| print(f" Warning: {result.stderr[:200]}") | |
| return True | |
| def assemble_video(scenes_json, images_dir, output_path, fps=24, overlap=0.5): | |
| """Step 3: Assemble video with Ken Burns + transitions.""" | |
| print("\n" + "="*60) | |
| print("STEP 3: Video Assembly") | |
| print("="*60) | |
| cmd = [sys.executable, os.path.join(SCRIPT_DIR, "make-video-v3.py"), | |
| "--scenes", scenes_json, "--images-dir", images_dir, | |
| "--output", output_path, "--fps", str(fps), "--overlap", str(overlap)] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| print(result.stdout) | |
| if result.returncode != 0: | |
| print(f"ERROR: {result.stderr[-500:]}") | |
| return False | |
| return True | |
| def check_quality(video_path): | |
| """Step 4: Quality check.""" | |
| print("\n" + "="*60) | |
| print("STEP 4: Quality Check") | |
| print("="*60) | |
| # File info | |
| size_mb = os.path.getsize(video_path) / (1024*1024) | |
| print(f" File: {video_path}") | |
| print(f" Size: {size_mb:.1f} MB") | |
| # ffprobe | |
| try: | |
| cmd = ["ffprobe", "-v", "error", "-show_entries", | |
| "format=duration,bit_rate:stream=codec_name,width,height,r_frame_rate", | |
| "-of", "json", video_path] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| info = json.loads(result.stdout) | |
| fmt = info.get("format", {}) | |
| streams = info.get("streams", [{}]) | |
| video_stream = next((s for s in streams if s.get("codec_name") == "h264"), streams[0] if streams else {}) | |
| duration = float(fmt.get("duration", 0)) | |
| bitrate = int(fmt.get("bit_rate", 0)) | |
| width = video_stream.get("width", 0) | |
| height = video_stream.get("height", 0) | |
| fps = video_stream.get("r_frame_rate", "0/1") | |
| print(f" Duration: {duration:.1f}s") | |
| print(f" Resolution: {width}x{height}") | |
| print(f" Bitrate: {bitrate/1000:.0f} kbps") | |
| print(f" FPS: {fps}") | |
| # Quality score | |
| score = 0 | |
| if width >= 1920: score += 2 | |
| elif width >= 1280: score += 1 | |
| if bitrate > 5000000: score += 2 | |
| elif bitrate > 2000000: score += 1 | |
| if duration > 10: score += 1 | |
| score += 2 # Base score for having transitions | |
| print(f"\n Quality Score: {score}/7") | |
| if score >= 5: | |
| print(" Rating: GOOD") | |
| elif score >= 3: | |
| print(" Rating: ACCEPTABLE") | |
| else: | |
| print(" Rating: NEEDS IMPROVEMENT") | |
| return {"duration": duration, "bitrate": bitrate, "width": width, "height": height, "score": score} | |
| except Exception as e: | |
| print(f" Quality check failed: {e}") | |
| return None | |
| def main(): | |
| parser = argparse.ArgumentParser(description="End-to-End Video Pipeline") | |
| parser.add_argument("--script", required=True, help="Script file (.txt)") | |
| parser.add_argument("--images-dir", default=os.path.join(PIPELINE_DIR, "images"), help="Images directory") | |
| parser.add_argument("--output", default=os.path.join(PIPELINE_DIR, "video", "final.mp4"), help="Output video") | |
| parser.add_argument("--gemini-key", help="Gemini API key (optional)") | |
| parser.add_argument("--generate-images", action="store_true", help="Generate B-roll via ComfyUI") | |
| parser.add_argument("--fps", type=int, default=24) | |
| parser.add_argument("--overlap", type=float, default=0.5) | |
| parser.add_argument("--skip-planning", action="store_true", help="Skip scene planning (use existing scenes.json)") | |
| args = parser.parse_args() | |
| start_time = time.time() | |
| scenes_json = os.path.join(PIPELINE_DIR, "scripts", "scenes.json") | |
| print("\n" + "#"*60) | |
| print("# VIDEO PIPELINE — START") | |
| print("#"*60) | |
| print(f" Script: {args.script}") | |
| print(f" Images: {args.images_dir}") | |
| print(f" Output: {args.output}") | |
| # Step 1: Scene planning | |
| if not args.skip_planning: | |
| if not run_scene_planner(args.script, args.images_dir, scenes_json, args.gemini_key): | |
| sys.exit(1) | |
| # Step 2: Generate images (optional) | |
| if args.generate_images: | |
| generate_broll_images(scenes_json, args.images_dir) | |
| # Step 3: Assemble video | |
| if not assemble_video(scenes_json, args.images_dir, args.output, args.fps, args.overlap): | |
| sys.exit(1) | |
| # Step 4: Quality check | |
| quality = check_quality(args.output) | |
| # Summary | |
| elapsed = time.time() - start_time | |
| print("\n" + "#"*60) | |
| print("# PIPELINE COMPLETE") | |
| print("#"*60) | |
| print(f" Output: {args.output}") | |
| print(f" Time: {elapsed:.0f}s") | |
| if quality: | |
| print(f" Score: {quality['score']}/7") | |
| print() | |
| if __name__ == "__main__": | |
| main() | |
Xet Storage Details
- Size:
- 6.67 kB
- Xet hash:
- a6f07dc3854d297475f2a7149c7d28a29d5d2eaf9926c760e45be103210a1fdb
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.