File size: 4,798 Bytes
f0e2e50 5535d80 f0e2e50 50544a2 f0e2e50 46f75eb f0e2e50 46f75eb f0e2e50 46f75eb f0e2e50 46f75eb f0e2e50 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
"""Video rendering utilities."""
import os
import time
import logging
import subprocess
from pathlib import Path
from typing import Tuple, Optional, Any
from config.settings import QUALITY_SETTINGS, APP_SETTINGS
from config.constants import (
ErrorType,
FILE_CONSTANTS,
VideoQuality
)
from utils.error_handler import ErrorHandler
logger = logging.getLogger(__name__)
class VideoRenderer:
"""Handles Manim video rendering."""
def __init__(
self,
code_file: str,
temp_dir: str,
quality: str = VideoQuality.MEDIUM.value,
metrics: Optional[Any] = None
):
"""Initialize the video renderer.
Args:
code_file: Path to the Python file containing the Manim scene
temp_dir: Directory to store temporary files
quality: Video quality setting
metrics: Optional metrics collector instance
"""
self.code_file = code_file
self.temp_dir = temp_dir
self.quality = quality
self.metrics = metrics
self.start_time = None
def _get_quality_settings(self) -> dict:
"""Get quality settings for the current quality level."""
return QUALITY_SETTINGS.get(
self.quality,
QUALITY_SETTINGS[VideoQuality.MEDIUM.value]
)
def _build_manim_command(self) -> list:
"""Build the Manim command with appropriate flags."""
quality_settings = self._get_quality_settings()
return [
"manim",
"render",
quality_settings["flag"],
self.code_file,
"GeneratedScene",
"--output_file", FILE_CONSTANTS["OUTPUT_FILE_NAME"],
"--media_dir", self.temp_dir
]
def _find_output_video(self) -> Optional[str]:
"""Find the generated video file."""
video_files = list(Path(self.temp_dir).rglob(f"*{FILE_CONSTANTS['VIDEO_EXTENSION']}"))
return str(video_files[0]) if video_files else None
def _record_metrics(self, success: bool, error_type: Optional[str] = None) -> None:
"""Record rendering metrics if metrics collector is available."""
if self.metrics and self.start_time:
render_time = time.time() - self.start_time
if success:
self.metrics.record_successful_render(
render_time,
"unknown", # TODO: Add scene type detection
self.quality
)
else:
self.metrics.record_failed_render(error_type or "UnknownError")
def render(self) -> Tuple[bool, str, str]:
"""Render the Manim video.
Returns:
Tuple containing:
- Success flag
- Result message or error message
- Log output
"""
self.start_time = time.time()
try:
# Build and run command
cmd = self._build_manim_command()
logger.info(f"Running command: {' '.join(cmd)}")
process = subprocess.run(
cmd,
cwd=self.temp_dir,
capture_output=True,
text=True,
timeout=APP_SETTINGS["max_render_time"]
)
if process.returncode == 0:
# Find and verify video file
video_file = self._find_output_video()
if video_file:
self._record_metrics(True)
return True, video_file, process.stdout
else:
self._record_metrics(False, "NoVideoFile")
return False, "Rendering completed but no video file was found", ""
else:
self._record_metrics(False, "RenderingFailed")
return False, "Rendering failed. Please check your scene code.", ""
except subprocess.TimeoutExpired:
self._record_metrics(False, "TimeoutError")
return False, "Rendering took too long and was stopped", ""
except Exception as e:
logger.error(f"Error rendering video: {e}")
self._record_metrics(False, "RenderingError")
ErrorHandler.handle_error(ErrorType.RENDERING_ERROR, str(e))
return False, "An error occurred during rendering", ""
@property
def estimated_render_time(self) -> int:
"""Get estimated render time in seconds."""
return self._get_quality_settings()["estimated_time"]
@property
def resolution(self) -> str:
"""Get video resolution for current quality setting."""
return self._get_quality_settings()["resolution"] |