Spaces:
Running
Running
| # src/rendering/video_assembler.py | |
| import re | |
| from pathlib import Path | |
| from datetime import datetime | |
| from moviepy import VideoFileClip, concatenate_videoclips | |
| from src.utils.helpers import get_hash | |
| from src.utils.smart_logger import logger | |
| def assemble_final_video(project_config: dict, output_dir: Path, temp_dir): | |
| """Workflow Step 5: Combine all rendered scenes into a final video.""" | |
| logger.start_operation("assemble_video", "Assembling final video") | |
| code_file_path = next((s["code_file"] for s in project_config["scenes"].values() if s.get("code_file")), None) | |
| if not code_file_path: | |
| logger.end_operation("assemble_video", "Can't find code file for scene order") | |
| return | |
| code_content = Path(code_file_path).read_text() | |
| match = re.search(r"SCENE_ORDER\s*=\s*(\[.*?\])", code_content, re.DOTALL) | |
| if not match: | |
| logger.end_operation("assemble_video", "SCENE_ORDER not found in code") | |
| return | |
| try: | |
| scene_order = eval(match.group(1)) | |
| logger.quick_log("info", f"Scene order: {scene_order}") | |
| except: | |
| logger.end_operation("assemble_video", "Could not parse SCENE_ORDER") | |
| return | |
| video_clips = [] | |
| for scene_name in scene_order: | |
| scene_data = project_config["scenes"].get(scene_name, {}) | |
| video_path = scene_data.get("final_video_path") | |
| if video_path and Path(video_path).exists(): | |
| clip = VideoFileClip(video_path) | |
| video_clips.append(clip) | |
| else: | |
| logger.quick_log("warn", f"Skipping {scene_name}: video not found") | |
| if not video_clips: | |
| logger.end_operation("assemble_video", "No video clips found to assemble") | |
| return | |
| try: | |
| final_video = concatenate_videoclips(video_clips) | |
| prompt_hash = get_hash(project_config.get("prompt", "no_prompt")) | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| output_file = output_dir / f"{prompt_hash}_{timestamp}.mp4" | |
| final_video.write_videofile(str(output_file), codec='libx264', audio_codec='aac', logger=None) | |
| for clip in video_clips: | |
| clip.close() | |
| file_size = output_file.stat().st_size / (1024*1024) | |
| logger.end_operation("assemble_video", f"Final video created: {output_file.name}", f"{file_size:.1f}MB") | |
| except Exception as e: | |
| logger.error_with_context("Final video creation failed", str(e)) | |
| logger.end_operation("assemble_video", "Assembly failed") | |
| # Clean up temporary directory | |
| temp_dir.cleanup() | |
| logger.quick_log("info", "Temporary files cleaned up") | |