Performance improvements / API refactoring

This commit is contained in:
2026-01-08 13:42:00 -06:00
parent d2a30a887f
commit c83037ee8a
8 changed files with 40 additions and 31 deletions

View File

@@ -380,6 +380,16 @@ def delete_job(job_id):
# Engine Info and Management:
# --------------------------------------------
@server.get('/api/engine_for_filename')
def get_engine_for_filename():
filename = request.args.get("filename")
if not filename:
return "Error: filename is required", 400
found_engine = EngineManager.engine_class_for_project_path(filename)
if not found_engine:
return f"Error: cannot find a suitable engine for '{filename}'", 400
return found_engine.name()
@server.get('/api/installed_engines')
def get_installed_engines():
result = {}
@@ -464,7 +474,8 @@ def get_engine_info(engine_name):
future_results = {
'supported_extensions': executor.submit(en.supported_extensions),
'supported_export_formats': executor.submit(en.get_output_formats),
'system_info': executor.submit(en.system_info)
'system_info': executor.submit(en.system_info),
'options': executor.submit(en.ui_options)
}
for key, future in future_results.items():
@@ -620,6 +631,15 @@ def handle_detached_instance(_):
return "Unavailable", 503
@server.errorhandler(404)
def handle_404(error):
url = request.url
err_msg = f"404 Not Found: {url}"
if 'favicon' not in url:
logger.warning(err_msg)
return err_msg, 404
@server.errorhandler(Exception)
def handle_general_error(general_error):
err_msg = f"Server error: {general_error}"

View File

@@ -247,6 +247,10 @@ class RenderServerProxy:
# Engines:
# --------------------------------------------
def get_engine_for_filename(self, filename, timeout=5):
response = self.request(f'engine_for_filename?filename={os.path.basename(filename)}', timeout)
return response.text
def get_installed_engines(self, timeout=5):
return self.request_data(f'installed_engines', timeout)

View File

@@ -24,10 +24,12 @@ class Blender(BaseRenderEngine):
from src.engines.blender.blender_worker import BlenderRenderWorker
return BlenderRenderWorker
@staticmethod
def ui_options(system_info):
from src.engines.blender.blender_ui import BlenderUI
return BlenderUI.get_options(system_info)
def ui_options(self):
options = [
{'name': 'engine', 'options': self.supported_render_engines()},
{'name': 'render_device', 'options': ['Any', 'GPU', 'CPU']},
]
return options
def supported_extensions(self):
return ['blend']
@@ -180,7 +182,7 @@ class Blender(BaseRenderEngine):
logger.error("GPU data not found in the output.")
def supported_render_engines(self):
engine_output = subprocess.run([self.engine_path(), '-E', 'help'], timeout=SUBPROCESS_TIMEOUT,
engine_output = subprocess.run([self.engine_path(), '-b', '-E', 'help'], timeout=SUBPROCESS_TIMEOUT,
capture_output=True, creationflags=_creationflags).stdout.decode('utf-8').strip()
render_engines = [x.strip() for x in engine_output.split('Blender Engine Listing:')[-1].strip().splitlines()]
return render_engines

View File

@@ -1,9 +0,0 @@
class BlenderUI:
@staticmethod
def get_options(system_info):
options = [
{'name': 'engine', 'options': system_info.get('engines', [])},
{'name': 'render_device', 'options': ['Any', 'GPU', 'CPU']},
]
return options

View File

@@ -133,8 +133,7 @@ class BaseRenderEngine(object):
def downloader(): # override when subclassing if using a downloader class
return None
@staticmethod
def ui_options(system_info): # override to return options for ui
def ui_options(self): # override to return options for ui
return {}
# --------------------------------------------

View File

@@ -20,10 +20,8 @@ class FFMPEG(BaseRenderEngine):
from src.engines.ffmpeg.ffmpeg_worker import FFMPEGRenderWorker
return FFMPEGRenderWorker
@staticmethod
def ui_options(system_info):
from src.engines.ffmpeg.ffmpeg_ui import FFMPEGUI
return FFMPEGUI.get_options(system_info)
def ui_options(self):
return []
def supported_extensions(self):
help_text = (subprocess.check_output([self.engine_path(), '-h', 'full'], stderr=subprocess.STDOUT,

View File

@@ -1,5 +0,0 @@
class FFMPEGUI:
@staticmethod
def get_options(system_info):
options = []
return options

View File

@@ -68,7 +68,6 @@ class NewRenderJobForm(QWidget):
# Setup
self.setWindowTitle("New Job")
self.setup_ui()
self.update_engine_info()
self.setup_project()
# get renderer info in bg thread
@@ -325,11 +324,11 @@ class NewRenderJobForm(QWidget):
def update_engine_info(self):
# get the engine info and add them all to the ui
engine = EngineManager.engine_class_for_project_path(self.project_path)
installed_engines = self.server_proxy.get_installed_engines()
self.engine_type.addItems(installed_engines.keys())
# select the best engine for the file type
self.engine_type.setCurrentText(engine.name().lower())
engine_name = self.server_proxy.get_engine_for_filename(self.project_path)
self.engine_type.setCurrentText(engine_name)
# refresh ui
self.engine_changed()
@@ -341,6 +340,7 @@ class NewRenderJobForm(QWidget):
self.file_format_combo.clear()
if current_engine:
engine_info = self.server_proxy.get_engine_info(current_engine, 'full', timeout=10)
self.current_engine_options = engine_info.get('options', [])
if not engine_info:
raise FileNotFoundError(f"Cannot get information about engine '{current_engine}'")
engine_vers = [v['version'] for v in engine_info['versions']]
@@ -442,7 +442,6 @@ class NewRenderJobForm(QWidget):
# Dynamic Engine Options
clear_layout(self.engine_options_layout) # clear old options
# dynamically populate option list
self.current_engine_options = {} #todo: fix this
for option in self.current_engine_options:
h_layout = QHBoxLayout()
label = QLabel(option['name'].replace('_', ' ').capitalize() + ':')
@@ -592,7 +591,7 @@ class SubmitWorker(QThread):
children_jobs.append(child_job_data)
job_json['child_jobs'] = children_jobs
# presubmission tasks
# presubmission tasks - use local installs
engine_class = EngineManager.engine_class_with_name(self.window.engine_type.currentText().lower())
latest_engine = EngineManager.get_latest_engine_instance(engine_class)
input_path = latest_engine.perform_presubmission_tasks(input_path)
@@ -623,6 +622,7 @@ class GetProjectInfoWorker(QThread):
def run(self):
try:
self.window.update_engine_info()
# this should be the only time we use a local engine instead of using the proxy besides submitting
engine_class = EngineManager.engine_class_for_project_path(self.project_path)
engine = EngineManager.get_latest_engine_instance(engine_class)
self.window.project_info = engine.get_project_info(self.project_path)