Add Job Window Redesign (#128)

* Initial refactor of add_job_window

* Improved project naming and fixed Blender engine issue

* Improve time representation in main window

* Cleanup Blender job creation

* Send resolution / fps data in job submission

* More window improvements

* EngineManager renaming and refactoring

* FFMPEG path fixes for ffprobe

* More backend refactoring / improvements

* Performance improvements / API refactoring

* Show current job count in add window UI before submission

* Move some UI update code out of background thread

* Move some main window UI update code out of background thread
This commit is contained in:
2026-01-12 09:06:53 -06:00
committed by GitHub
parent d8af7c878e
commit 8b3fdd14b5
18 changed files with 657 additions and 372 deletions
+93 -21
View File
@@ -309,6 +309,12 @@ def add_job_handler():
new_job = DistributedJobManager.create_render_job(processed_job_data, loaded_project_local_path)
created_jobs.append(new_job)
# Save notes to .txt
if processed_job_data.get("notes"):
parent_dir = os.path.dirname(os.path.dirname(loaded_project_local_path))
notes_name = processed_job_data['name'] + "-notes.txt"
with open(os.path.join(parent_dir, notes_name), "w") as f:
f.write(processed_job_data["notes"])
return [x.json() for x in created_jobs]
except Exception as e:
logger.exception(f"Error creating render job: {e}")
@@ -374,6 +380,26 @@ 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 = {}
for engine_class in EngineManager.supported_engines():
data = EngineManager.all_version_data_for_engine(engine_class.name())
if data:
result[engine_class.name()] = data
return result
@server.get('/api/engine_info')
def engine_info():
response_type = request.args.get('response_type', 'standard')
@@ -383,7 +409,7 @@ def engine_info():
def process_engine(engine):
try:
# Get all installed versions of the engine
installed_versions = EngineManager.all_versions_for_engine(engine.name())
installed_versions = EngineManager.all_version_data_for_engine(engine.name())
if not installed_versions:
return None
@@ -414,7 +440,7 @@ def engine_info():
except Exception as e:
logger.error(f"Error fetching details for engine '{engine.name()}': {e}")
raise e
return {}
engine_data = {}
with concurrent.futures.ThreadPoolExecutor() as executor:
@@ -428,14 +454,69 @@ def engine_info():
return engine_data
@server.get('/api/<engine_name>/info')
def get_engine_info(engine_name):
try:
response_type = request.args.get('response_type', 'standard')
# Get all installed versions of the engine
installed_versions = EngineManager.all_version_data_for_engine(engine_name)
if not installed_versions:
return {}
result = { 'is_available': RenderQueue.is_available_for_job(engine_name),
'versions': installed_versions
}
if response_type == 'full':
with concurrent.futures.ThreadPoolExecutor() as executor:
engine_class = EngineManager.engine_class_with_name(engine_name)
en = EngineManager.get_latest_engine_instance(engine_class)
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),
'options': executor.submit(en.ui_options)
}
for key, future in future_results.items():
result[key] = future.result()
return result
except Exception as e:
logger.error(f"Error fetching details for engine '{engine_name}': {e}")
return {}
@server.get('/api/<engine_name>/is_available')
def is_engine_available(engine_name):
return {'engine': engine_name, 'available': RenderQueue.is_available_for_job(engine_name),
'cpu_count': int(psutil.cpu_count(logical=False)),
'versions': EngineManager.all_versions_for_engine(engine_name),
'versions': EngineManager.all_version_data_for_engine(engine_name),
'hostname': server.config['HOSTNAME']}
@server.get('/api/engine/<engine_name>/args')
def get_engine_args(engine_name):
try:
engine_class = EngineManager.engine_class_with_name(engine_name)
return engine_class().get_arguments()
except LookupError:
return f"Cannot find engine '{engine_name}'", 400
@server.get('/api/engine/<engine_name>/help')
def get_engine_help(engine_name):
try:
engine_class = EngineManager.engine_class_with_name(engine_name)
return engine_class().get_help()
except LookupError:
return f"Cannot find engine '{engine_name}'", 400
# --------------------------------------------
# Engine Downloads and Updates:
# --------------------------------------------
@server.get('/api/is_engine_available_to_download')
def is_engine_available_to_download():
available_result = EngineManager.version_is_available_to_download(request.args.get('engine'),
@@ -476,24 +557,6 @@ def delete_engine_download():
(f"Error deleting {json_data.get('engine')} {json_data.get('version')}", 500)
@server.get('/api/engine/<engine_name>/args')
def get_engine_args(engine_name):
try:
engine_class = EngineManager.engine_with_name(engine_name)
return engine_class().get_arguments()
except LookupError:
return f"Cannot find engine '{engine_name}'", 400
@server.get('/api/engine/<engine_name>/help')
def get_engine_help(engine_name):
try:
engine_class = EngineManager.engine_with_name(engine_name)
return engine_class().get_help()
except LookupError:
return f"Cannot find engine '{engine_name}'", 400
# --------------------------------------------
# Miscellaneous:
# --------------------------------------------
@@ -568,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}"