Spaces:
Configuration error
Configuration error
Fix visual clutter and audio sync: enhanced layout zones, audio concatenation, proper timing
ee6d55e | import os | |
| import subprocess | |
| import uuid | |
| import shutil | |
| import glob | |
| from pathlib import Path | |
| # Get the directory of the current script (backend/) | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| MEDIA_DIR = os.path.join(BASE_DIR, "media") | |
| def cleanup_old_files(): | |
| """Clean up old scene files and media to free memory""" | |
| try: | |
| # Remove old scene files (keep only last 2) | |
| scene_files = sorted(glob.glob(os.path.join(BASE_DIR, "scene_*.py")), key=os.path.getmtime) | |
| for old_file in scene_files[:-2]: | |
| try: | |
| os.remove(old_file) | |
| print(f"๐๏ธ Cleaned up old scene file: {old_file}") | |
| except Exception as e: | |
| print(f"โ ๏ธ Could not remove {old_file}: {e}") | |
| # Remove old video folders (keep only last 3) | |
| videos_dir = os.path.join(MEDIA_DIR, "videos") | |
| if os.path.exists(videos_dir): | |
| video_folders = sorted( | |
| [f for f in Path(videos_dir).iterdir() if f.is_dir()], | |
| key=os.path.getmtime | |
| ) | |
| for old_folder in video_folders[:-3]: | |
| try: | |
| shutil.rmtree(old_folder) | |
| print(f"๐๏ธ Cleaned up old video folder: {old_folder}") | |
| except Exception as e: | |
| print(f"โ ๏ธ Could not remove {old_folder}: {e}") | |
| # Remove old step audio files (keep only last 5) | |
| audio_files = sorted(glob.glob(os.path.join(BASE_DIR, "step_*_narration.mp3")), key=os.path.getmtime) | |
| for old_audio in audio_files[:-5]: | |
| try: | |
| os.remove(old_audio) | |
| print(f"๐๏ธ Cleaned up old audio file: {old_audio}") | |
| except Exception as e: | |
| print(f"โ ๏ธ Could not remove {old_audio}: {e}") | |
| # Remove old combined audio files (keep only last 2) | |
| combined_files = sorted(glob.glob(os.path.join(BASE_DIR, "combined_narration*.mp3")), key=os.path.getmtime) | |
| for old_combined in combined_files[:-2]: | |
| try: | |
| os.remove(old_combined) | |
| print(f"๐๏ธ Cleaned up old combined audio: {old_combined}") | |
| except Exception as e: | |
| print(f"โ ๏ธ Could not remove {old_combined}: {e}") | |
| except Exception as e: | |
| print(f"โ ๏ธ Cleanup error: {e}") | |
| async def render_scene(code: str): | |
| # Clean up old files before rendering | |
| cleanup_old_files() | |
| # Create a unique ID for this run | |
| run_id = str(uuid.uuid4()) | |
| filename = f"scene_{run_id}.py" | |
| # Save code to file in the backend directory | |
| filepath = os.path.join(BASE_DIR, filename) | |
| with open(filepath, "w", encoding='utf-8') as f: | |
| f.write(code) | |
| # Run Manim with LOW quality (-ql) for better memory usage | |
| # -ql = 480p15, much lighter than -qm (720p30) | |
| cmd = ["manim", "-ql", "--media_dir", MEDIA_DIR, filepath, "GenScene"] | |
| try: | |
| result = subprocess.run(cmd, capture_output=True, text=True, check=True) | |
| print("โ Manim render completed") | |
| except subprocess.CalledProcessError as e: | |
| print("โ Manim Error:", e.stderr) | |
| # Clean up failed scene file | |
| try: | |
| os.remove(filepath) | |
| except: | |
| pass | |
| raise Exception(f"Manim failed: {e.stderr}") | |
| # Construct expected output path | |
| # Low quality: {MEDIA_DIR}/videos/{filename_without_extension}/480p15/GenScene.mp4 | |
| video_folder = filename.replace(".py", "") | |
| video_path = os.path.join(MEDIA_DIR, "videos", video_folder, "480p15", "GenScene.mp4") | |
| if not os.path.exists(video_path): | |
| # Clean up scene file | |
| try: | |
| os.remove(filepath) | |
| except: | |
| pass | |
| raise Exception(f"Video file not found at {video_path}") | |
| print(f"โ Video created: {video_path}") | |
| # Clean up the scene file immediately after successful render | |
| try: | |
| os.remove(filepath) | |
| print(f"๐๏ธ Cleaned up scene file: {filepath}") | |
| except Exception as e: | |
| print(f"โ ๏ธ Could not remove scene file: {e}") | |
| return video_path | |