Spaces:
Runtime error
Runtime error
| """Blender post-processing for game-ready assets.""" | |
| import os | |
| import shutil | |
| import subprocess | |
| from pathlib import Path | |
| from typing import Optional, Tuple | |
| class BlenderProcessor: | |
| """Processes GLB files using Blender for game optimization.""" | |
| def __init__(self): | |
| self.blender_path = self._find_blender() | |
| def _find_blender(self) -> Optional[Path]: | |
| """Find Blender executable across platforms.""" | |
| # Check environment variable | |
| if blender_path := os.getenv("BLENDER_PATH"): | |
| if os.path.exists(blender_path): | |
| print(f"[Blender] Found via BLENDER_PATH: {blender_path}") | |
| return Path(blender_path) | |
| # Check common locations | |
| common_paths = [ | |
| "/usr/bin/blender", # Linux (HF Space) | |
| "/usr/local/bin/blender", | |
| "/app/blender/blender", | |
| "D:/KIRO/Projects/XStudios/Blender/blender.exe", # Local dev | |
| ] | |
| for path in common_paths: | |
| if os.path.exists(path): | |
| print(f"[Blender] Found at: {path}") | |
| return Path(path) | |
| # Try system PATH | |
| if blender_path := shutil.which("blender"): | |
| print(f"[Blender] Found in PATH: {blender_path}") | |
| return Path(blender_path) | |
| print("[Blender] WARNING: Blender not found") | |
| return None | |
| def is_available(self) -> bool: | |
| """Check if Blender is available.""" | |
| return self.blender_path is not None | |
| def optimize( | |
| self, | |
| input_path: Path, | |
| output_path: Path, | |
| script_path: Path | |
| ) -> Tuple[bool, str]: | |
| """Optimize GLB using external Blender script.""" | |
| if not self.is_available(): | |
| return False, "Blender not available" | |
| try: | |
| print(f"[Blender] Optimizing: {input_path.name}") | |
| # Run Blender in background mode | |
| cmd = [ | |
| str(self.blender_path), | |
| "--background", | |
| "--python", str(script_path), | |
| "--", | |
| str(input_path), | |
| str(output_path) | |
| ] | |
| result = subprocess.run( | |
| cmd, | |
| capture_output=True, | |
| text=True, | |
| timeout=120 # 2 minute timeout | |
| ) | |
| if result.returncode != 0: | |
| error_msg = result.stderr[-500:] if result.stderr else "Unknown error" | |
| return False, f"Blender failed: {error_msg}" | |
| if not output_path.exists(): | |
| return False, "Output file not created" | |
| # Get file sizes | |
| input_size = input_path.stat().st_size / 1e6 | |
| output_size = output_path.stat().st_size / 1e6 | |
| reduction = ((input_size - output_size) / input_size) * 100 | |
| message = f"Optimized: {input_size:.2f}MB → {output_size:.2f}MB ({reduction:.1f}% reduction)" | |
| print(f"[Blender] {message}") | |
| return True, message | |
| except subprocess.TimeoutExpired: | |
| return False, "Blender timeout (>2 minutes)" | |
| except Exception as e: | |
| return False, f"Blender error: {str(e)}" | |