mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 16:58:12 +00:00
Windows File Path Fixes (#39)
* Added system_safe_path to convert paths to Windows * Fix issue where engines would not be reported unless a system engine was installed * Platform independent searching for binaries in engine directory * Add missing package to requirements.txt * Better error handling for ffmpeg.get_all_formats() * Add system_safe_path to more locations in api_server.py * Fix naming issue with Blender on macos * Fix path lookups and add engine_path to workers * Report installed renderers in status * Remove files included by accident
This commit is contained in:
@@ -13,6 +13,8 @@ class BaseRenderEngine(object):
|
||||
|
||||
def __init__(self, custom_path=None):
|
||||
self.custom_renderer_path = custom_path
|
||||
if not self.renderer_path():
|
||||
raise FileNotFoundError(f"Cannot find path to renderer for {self.name()} instance")
|
||||
|
||||
def renderer_path(self):
|
||||
return self.custom_renderer_path or self.default_renderer_path()
|
||||
@@ -24,9 +26,9 @@ class BaseRenderEngine(object):
|
||||
@classmethod
|
||||
def default_renderer_path(cls):
|
||||
path = None
|
||||
try:
|
||||
try: # Linux and macOS
|
||||
path = subprocess.check_output(['which', cls.name()], timeout=SUBPROCESS_TIMEOUT).decode('utf-8').strip()
|
||||
except subprocess.CalledProcessError:
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
for p in cls.install_paths:
|
||||
if os.path.exists(p):
|
||||
path = p
|
||||
|
||||
@@ -13,6 +13,7 @@ class Blender(BaseRenderEngine):
|
||||
|
||||
install_paths = ['/Applications/Blender.app/Contents/MacOS/Blender']
|
||||
supported_extensions = ['.blend']
|
||||
binary_names = {'linux': 'blender', 'windows': 'blender.exe', 'macos': 'Blender'}
|
||||
|
||||
def version(self):
|
||||
version = None
|
||||
@@ -36,18 +37,17 @@ class Blender(BaseRenderEngine):
|
||||
return subprocess.run([self.renderer_path(), '-b', project_path, '--python-expr', python_expression],
|
||||
capture_output=True, timeout=timeout)
|
||||
except Exception as e:
|
||||
logger.warning(f"Error running python expression in blender: {e}")
|
||||
pass
|
||||
logger.error(f"Error running python expression in blender: {e}")
|
||||
else:
|
||||
raise FileNotFoundError(f'Project file not found: {project_path}')
|
||||
|
||||
def run_python_script(self, project_path, script_path, timeout=None):
|
||||
if os.path.exists(project_path) and os.path.exists(script_path):
|
||||
try:
|
||||
return subprocess.run([self.default_renderer_path(), '-b', project_path, '--python', script_path],
|
||||
return subprocess.run([self.renderer_path(), '-b', project_path, '--python', script_path],
|
||||
capture_output=True, timeout=timeout)
|
||||
except Exception as e:
|
||||
logger.warning(f"Error running python expression in blender: {e}")
|
||||
logger.warning(f"Error running python script in blender: {e}")
|
||||
pass
|
||||
elif not os.path.exists(project_path):
|
||||
raise FileNotFoundError(f'Project file not found: {project_path}')
|
||||
@@ -58,8 +58,9 @@ class Blender(BaseRenderEngine):
|
||||
def get_scene_info(self, project_path, timeout=10):
|
||||
scene_info = {}
|
||||
try:
|
||||
results = self.run_python_script(project_path, os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'scripts', 'blender', 'get_file_info.py'), timeout=timeout)
|
||||
script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'scripts', 'blender', 'get_file_info.py')
|
||||
results = self.run_python_script(project_path, system_safe_path(script_path), timeout=timeout)
|
||||
result_text = results.stdout.decode()
|
||||
for line in result_text.splitlines():
|
||||
if line.startswith('SCENE_DATA:'):
|
||||
@@ -76,8 +77,9 @@ class Blender(BaseRenderEngine):
|
||||
# Credit to L0Lock for pack script - https://blender.stackexchange.com/a/243935
|
||||
try:
|
||||
logger.info(f"Starting to pack Blender file: {project_path}")
|
||||
results = self.run_python_script(project_path, os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'scripts', 'blender', 'pack_project.py'), timeout=timeout)
|
||||
script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'scripts', 'blender', 'pack_project.py')
|
||||
results = self.run_python_script(project_path, system_safe_path(script_path), timeout=timeout)
|
||||
|
||||
result_text = results.stdout.decode()
|
||||
dir_name = os.path.dirname(project_path)
|
||||
|
||||
@@ -53,7 +53,7 @@ class BlenderDownloader:
|
||||
versions_data = [x for x in versions_data if x['cpu'] == cpu]
|
||||
|
||||
for v in versions_data:
|
||||
v['url'] = os.path.join(base_url, v['file'])
|
||||
v['url'] = base_url + '/' + v['file']
|
||||
|
||||
versions_data = sorted(versions_data, key=lambda x: x['version'], reverse=True)
|
||||
return versions_data
|
||||
|
||||
@@ -87,7 +87,7 @@ class FFMPEGDownloader:
|
||||
release_dir = 'releases' if version == cls.get_linux_versions()[0] else 'old-releases'
|
||||
remote_url = os.path.join(cls.linux_url, release_dir, f'ffmpeg-{version}-{cpu}-static.tar.xz')
|
||||
elif system_os == 'windows':
|
||||
remote_url = os.path.join(cls.windows_download_url, version, f'ffmpeg-{version}-full_build.zip')
|
||||
remote_url = f"{cls.windows_download_url.strip('/')}/{version}/ffmpeg-{version}-full_build.zip"
|
||||
|
||||
# Download and extract
|
||||
try:
|
||||
|
||||
@@ -4,6 +4,7 @@ import platform
|
||||
import shutil
|
||||
from .downloaders.blender_downloader import BlenderDownloader
|
||||
from .downloaders.ffmpeg_downloader import FFMPEGDownloader
|
||||
from ..utilities.misc_helper import system_safe_path
|
||||
|
||||
try:
|
||||
from .blender_engine import Blender
|
||||
@@ -37,14 +38,25 @@ class EngineManager:
|
||||
# Split the input string by dashes to get segments
|
||||
segments = directory.split('-')
|
||||
|
||||
# Define the keys for each word
|
||||
keys = ["engine", "version", "system_os", "cpu"]
|
||||
|
||||
# Create a dictionary with named keys
|
||||
executable_names = {'linux': 'blender', 'windows': 'blender.exe', 'macos': 'Blender.app'}
|
||||
keys = ["engine", "version", "system_os", "cpu"]
|
||||
result_dict = {keys[i]: segments[i] for i in range(min(len(keys), len(segments)))}
|
||||
result_dict['path'] = os.path.join(cls.engines_path, directory, executable_names.get(result_dict['system_os'], 'unknown'))
|
||||
result_dict['type'] = 'managed'
|
||||
|
||||
# Figure out the binary name for the path
|
||||
binary_name = result_dict['engine'].lower()
|
||||
for eng in cls.supported_engines():
|
||||
if eng.name().lower() == result_dict['engine']:
|
||||
binary_name = eng.binary_names.get(result_dict['system_os'], binary_name)
|
||||
|
||||
# Find path to binary
|
||||
path = None
|
||||
for root, _, files in os.walk(system_safe_path(os.path.join(cls.engines_path, directory))):
|
||||
if binary_name in files:
|
||||
path = os.path.join(root, binary_name)
|
||||
break
|
||||
|
||||
result_dict['path'] = path
|
||||
results.append(result_dict)
|
||||
except FileNotFoundError:
|
||||
logger.warning("Cannot find local engines download directory")
|
||||
@@ -113,11 +125,15 @@ class EngineManager:
|
||||
return
|
||||
|
||||
# Get the appropriate downloader class based on the engine type
|
||||
downloader = downloader_classes[engine]
|
||||
if downloader.download_engine(version, download_location=cls.engines_path, system_os=system_os, cpu=cpu):
|
||||
return cls.has_engine_version(engine, version, system_os, cpu)
|
||||
else:
|
||||
downloader_classes[engine].download_engine(version, download_location=cls.engines_path,
|
||||
system_os=system_os, cpu=cpu)
|
||||
|
||||
# Check that engine was properly downloaded
|
||||
found_engine = cls.has_engine_version(engine, version, system_os, cpu)
|
||||
if not found_engine:
|
||||
logger.error(f"Error downloading {engine}")
|
||||
return found_engine
|
||||
|
||||
|
||||
@classmethod
|
||||
def delete_engine_download(cls, engine, version, system_os=None, cpu=None):
|
||||
|
||||
@@ -7,6 +7,8 @@ import re
|
||||
|
||||
class FFMPEG(BaseRenderEngine):
|
||||
|
||||
binary_names = {'linux': 'ffmpeg', 'windows': 'ffmpeg.exe', 'macos': 'ffmpeg'}
|
||||
|
||||
def version(self):
|
||||
version = None
|
||||
try:
|
||||
@@ -31,11 +33,15 @@ class FFMPEG(BaseRenderEngine):
|
||||
return encoders
|
||||
|
||||
def get_all_formats(self):
|
||||
formats_raw = subprocess.check_output([self.renderer_path(), '-formats'], stderr=subprocess.DEVNULL,
|
||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
||||
pattern = '(?P<type>[DE]{1,2})\s+(?P<id>\S{2,})\s+(?P<name>.*)'
|
||||
all_formats = [m.groupdict() for m in re.finditer(pattern, formats_raw)]
|
||||
return all_formats
|
||||
try:
|
||||
formats_raw = subprocess.check_output([self.renderer_path(), '-formats'], stderr=subprocess.DEVNULL,
|
||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
||||
pattern = '(?P<type>[DE]{1,2})\s+(?P<id>\S{2,})\s+(?P<name>.*)\r'
|
||||
all_formats = [m.groupdict() for m in re.finditer(pattern, formats_raw)]
|
||||
return all_formats
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting all formats: {e}")
|
||||
return []
|
||||
|
||||
def extension_for_format(self, ffmpeg_format):
|
||||
# Extract the common extension using regex
|
||||
@@ -53,7 +59,7 @@ class FFMPEG(BaseRenderEngine):
|
||||
return [x for x in self.get_all_formats() if 'E' in x['type'].upper()]
|
||||
|
||||
def get_frame_count(self, path_to_file):
|
||||
raw_stdout = subprocess.check_output([self.default_renderer_path(), '-i', path_to_file, '-map', '0:v:0', '-c', 'copy',
|
||||
raw_stdout = subprocess.check_output([self.renderer_path(), '-i', path_to_file, '-map', '0:v:0', '-c', 'copy',
|
||||
'-f', 'null', '-'], stderr=subprocess.STDOUT,
|
||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
||||
match = re.findall(r'frame=\s*(\d+)', raw_stdout)
|
||||
|
||||
Reference in New Issue
Block a user