REST API endpoint streamlining and cleanup (#133)

* Consolidate engine_info api calls

* Change api methods to use POST when possible

* Delete engine API cleanup

* Remove redundant installed_engines endpoint

* Update proxy to use similar named methods to new API calls

* More API cleanup

* Jobs API cleanup

* More jobs API cleanup

* Fix add jobs error due to null queue

* Remove unnecessary full_status and snapshot API endpoints

* Streamline add job POST endpoint

* Fix test after method name change
This commit is contained in:
2026-06-06 12:04:52 -05:00
committed by GitHub
parent 24eb7b5616
commit f0be78adcc
15 changed files with 1255 additions and 203 deletions
+94 -125
View File
@@ -17,6 +17,7 @@ import psutil
import yaml
from flask import Flask, request, send_file, after_this_request, Response, redirect, url_for
from sqlalchemy.orm.exc import DetachedInstanceError
from werkzeug.exceptions import HTTPException
from src.api.job_import_handler import JobImportHandler
from src.api.preview_manager import PreviewManager
@@ -83,7 +84,7 @@ def jobs_json() -> Dict[str, Any]:
return {'jobs': all_jobs, 'token': job_cache_token}
@server.get('/api/jobs_long_poll')
@server.get('/api/jobs/long_poll')
def long_polling_jobs():
hash_token = request.args.get('token', None)
start_time = time.time()
@@ -97,7 +98,7 @@ def long_polling_jobs():
time.sleep(1)
@server.get('/api/jobs/<status_val>')
@server.get('/api/jobs/status/<status_val>')
def filtered_jobs_json(status_val):
state = string_to_status(status_val)
jobs = [x.json() for x in RenderQueue.jobs_with_status(state)]
@@ -111,7 +112,7 @@ def filtered_jobs_json(status_val):
# Job Details / File Handling
# --------------------------------------------
@server.get('/api/job/<job_id>')
@server.get('/api/jobs/<job_id>')
def get_job_details(job_id):
"""Retrieves the details of a requested job in JSON format
@@ -124,7 +125,7 @@ def get_job_details(job_id):
return RenderQueue.job_with_id(job_id).json()
@server.get('/api/job/<job_id>/logs')
@server.get('/api/jobs/<job_id>/logs')
def get_job_logs(job_id):
"""Retrieves the log file for a specific render job.
@@ -143,12 +144,12 @@ def get_job_logs(job_id):
return Response(log_data, mimetype='text/plain')
@server.get('/api/job/<job_id>/file_list')
def get_file_list(job_id):
return [Path(p).name for p in RenderQueue.job_with_id(job_id).file_list()]
@server.get('/api/jobs/<job_id>/files')
def get_job_files(job_id):
return [Path(p).name for p in RenderQueue.job_with_id(job_id).file_list()]
@server.route('/api/job/<job_id>/download')
@server.route('/api/jobs/<job_id>/download')
def download_requested_file(job_id):
requested_filename = request.args.get("filename")
if not requested_filename:
@@ -164,7 +165,7 @@ def download_requested_file(job_id):
return f"File '{requested_filename}' not found", 404
@server.route('/api/job/<job_id>/download_all')
@server.route('/api/jobs/<job_id>/download_all')
def download_all_files(job_id):
zip_filename = None
@@ -205,29 +206,6 @@ def presets() -> Dict[str, Any]:
return loaded_presets
@server.get('/api/full_status')
def full_status():
full_results = {'timestamp': datetime.now().isoformat(), 'servers': {}}
try:
snapshot_results = snapshot()
server_data = {'status': snapshot_results.get('status', {}), 'jobs': snapshot_results.get('jobs', {}),
'is_online': True}
full_results['servers'][server.config['HOSTNAME']] = server_data
except Exception as e:
logger.error(f"Exception fetching full status: {e}")
return full_results
@server.get('/api/snapshot')
def snapshot():
server_status = status()
server_jobs = [x.json() for x in RenderQueue.all_jobs()]
server_data = {'status': server_status, 'jobs': server_jobs, 'timestamp': datetime.now().isoformat()}
return server_data
@server.route('/api/status')
def status():
return {"timestamp": datetime.now().isoformat(),
@@ -253,10 +231,10 @@ def status():
# Job Lifecyle (Create, Cancel, Delete)
# --------------------------------------------
@server.post('/api/add_job')
def add_job_handler():
@server.post('/api/jobs')
def create_jobs_handler():
"""
POST /api/add_job
POST /api/jobs
Add a render job to the queue.
**Request Formats**
@@ -306,7 +284,7 @@ def add_job_handler():
return 'unknown error', 500
@server.get('/api/job/<job_id>/cancel')
@server.post('/api/jobs/<job_id>/cancel')
def cancel_job(job_id):
if not request.args.get('confirm', False):
return 'Confirmation required to cancel job', 400
@@ -320,7 +298,7 @@ def cancel_job(job_id):
return "Unknown error", 500
@server.route('/api/job/<job_id>/delete', methods=['POST', 'GET'])
@server.post('/api/jobs/<job_id>/delete')
def delete_job(job_id):
try:
if not request.args.get("confirm", False):
@@ -369,7 +347,7 @@ def delete_job(job_id):
# Engine Info and Management:
# --------------------------------------------
@server.get('/api/engine_for_filename')
@server.get('/api/engines/for_filename')
def get_engine_for_filename():
filename = request.args.get("filename")
if not filename:
@@ -379,93 +357,38 @@ def get_engine_for_filename():
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():
def _validated_engine_response_type():
response_type = request.args.get('response_type', 'standard')
if response_type not in ['full', 'standard']:
raise ValueError(f"Invalid response_type: {response_type}")
def process_engine(engine):
try:
# Get all installed versions of the engine
installed_versions = EngineManager.all_version_data_for_engine(engine.name())
if not installed_versions:
return None
system_installed_versions = [v for v in installed_versions if v['type'] == 'system']
install_path = system_installed_versions[0]['path'] if system_installed_versions else installed_versions[0]['path']
en = engine(install_path)
engine_name = en.name()
result = {
engine_name: {
'is_available': RenderQueue.is_available_for_job(engine_name),
'versions': installed_versions
}
}
if response_type == 'full':
with concurrent.futures.ThreadPoolExecutor() as executor:
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)
}
for key, future in future_results.items():
result[engine_name][key] = future.result()
return result
except Exception as e:
traceback.print_exc(e)
logger.error(f"Error fetching details for engine '{engine.name()}': {e}")
return {}
engine_data = {}
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = {executor.submit(process_engine, engine): engine.name() for engine in EngineManager.supported_engines()}
for future in concurrent.futures.as_completed(futures):
result = future.result()
if result:
engine_data.update(result)
return engine_data
return response_type
@server.get('/api/<engine_name>/info')
def get_engine_info(engine_name):
def _engine_info_for_engine(engine_class, response_type='standard'):
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)
installed_versions = EngineManager.all_version_data_for_engine(engine_class.name())
if not installed_versions:
return {}
return None
result = { 'is_available': RenderQueue.is_available_for_job(engine_name),
'versions': installed_versions
}
system_installed_versions = [v for v in installed_versions if v['type'] == 'system']
install_path = (
system_installed_versions[0]['path'] if system_installed_versions else installed_versions[0]['path']
)
engine = engine_class(install_path)
engine_name = engine.name()
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)
'supported_extensions': executor.submit(engine.supported_extensions),
'supported_export_formats': executor.submit(engine.get_output_formats),
'system_info': executor.submit(engine.system_info),
'options': executor.submit(engine.ui_options)
}
for key, future in future_results.items():
@@ -473,32 +396,75 @@ def get_engine_info(engine_name):
return result
except Exception as e:
logger.error(f"Error fetching details for engine '{engine_class.name()}': {e}")
return {}
@server.get('/api/engines')
def get_engines_info():
response_type = _validated_engine_response_type()
engine_data = {}
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = {
executor.submit(_engine_info_for_engine, engine, response_type): engine.name()
for engine in EngineManager.supported_engines()
}
for future in concurrent.futures.as_completed(futures):
result = future.result()
if result:
engine_data[futures[future]] = result
return engine_data
@server.get('/api/engines/names')
def get_engine_names():
result = []
for engine_class in EngineManager.supported_engines():
data = EngineManager.all_version_data_for_engine(engine_class.name())
if data:
result.append(engine_class.name())
return result
@server.get('/api/engines/<engine_name>')
def get_engine(engine_name):
try:
response_type = _validated_engine_response_type()
engine_class = EngineManager.engine_class_with_name(engine_name)
return _engine_info_for_engine(engine_class, response_type) or {}
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):
@server.get('/api/engines/<engine_name>/availability')
def get_engine_availability(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_version_data_for_engine(engine_name),
'hostname': server.config['HOSTNAME']}
'hostname': server.config.get('HOSTNAME', socket.gethostname())}
@server.get('/api/engine/<engine_name>/args')
@server.get('/api/engines/<engine_name>/args')
def get_engine_args(engine_name):
try:
engine_class = EngineManager.engine_class_with_name(engine_name)
if not engine_class:
return f"Cannot find engine '{engine_name}'", 400
return engine_class().get_arguments()
except LookupError:
return f"Cannot find engine '{engine_name}'", 400
@server.get('/api/engine/<engine_name>/help')
@server.get('/api/engines/<engine_name>/help')
def get_engine_help(engine_name):
try:
engine_class = EngineManager.engine_class_with_name(engine_name)
if not engine_class:
return f"Cannot find engine '{engine_name}'", 400
return engine_class().get_help()
except LookupError:
return f"Cannot find engine '{engine_name}'", 400
@@ -507,7 +473,7 @@ def get_engine_help(engine_name):
# Engine Downloads and Updates:
# --------------------------------------------
@server.get('/api/is_engine_available_to_download')
@server.get('/api/engines/download_available')
def is_engine_available_to_download():
available_result = EngineManager.version_is_available_to_download(request.args.get('engine'),
request.args.get('version'),
@@ -517,7 +483,7 @@ def is_engine_available_to_download():
(f"Cannot find available download for {request.args.get('engine')} {request.args.get('version')}", 500)
@server.get('/api/find_most_recent_version')
@server.get('/api/engines/most_recent_version')
def find_most_recent_version():
most_recent = EngineManager.find_most_recent_version(request.args.get('engine'),
request.args.get('system_os'),
@@ -526,7 +492,7 @@ def find_most_recent_version():
(f"Error finding most recent version of {request.args.get('engine')}", 500)
@server.post('/api/download_engine')
@server.post('/api/engines/download')
def download_engine():
download_result = EngineManager.download_engine(request.args.get('engine'),
request.args.get('version'),
@@ -536,7 +502,7 @@ def download_engine():
(f"Error downloading {request.args.get('engine')} {request.args.get('version')}", 500)
@server.post('/api/delete_engine')
@server.post('/api/engines/delete')
def delete_engine_download():
json_data = request.json
delete_result = EngineManager.delete_engine_download(json_data.get('engine'),
@@ -554,14 +520,14 @@ def delete_engine_download():
def heartbeat():
return datetime.now().isoformat(), 200
@server.post('/api/job/<job_id>/send_subjob_update_notification')
@server.post('/api/jobs/<job_id>/subjob_update')
def subjob_update_notification(job_id):
subjob_details = request.json
DistributedJobManager.handle_subjob_update_notification(RenderQueue.job_with_id(job_id), subjob_data=subjob_details)
return Response(status=200)
@server.route('/api/job/<job_id>/thumbnail')
@server.route('/api/jobs/<job_id>/thumbnail')
def job_thumbnail(job_id):
try:
@@ -632,6 +598,9 @@ def handle_404(error):
@server.errorhandler(Exception)
def handle_general_error(general_error):
if isinstance(general_error, HTTPException):
return general_error.description, general_error.code
traceback.print_exception(type(general_error), general_error, general_error.__traceback__)
err_msg = f"Server error: {general_error}"
logger.error(err_msg)
@@ -649,7 +618,7 @@ def detected_clients():
return ZeroconfServer.found_hostnames()
@server.get('/api/_debug/clear_history')
@server.post('/api/_debug/clear_history')
def clear_history():
RenderQueue.clear_history()
return 'success'
+35 -32
View File
@@ -109,6 +109,11 @@ class RenderServerProxy:
return requests.get(f'http://{self.hostname}:{self.port}/api/{payload}', timeout=timeout,
headers={"X-API-Version": str(API_VERSION)})
def _post(self, payload, timeout=5, **kwargs):
from src.api.api_server import API_VERSION
return requests.post(f'http://{self.hostname}:{self.port}/api/{payload}', timeout=timeout,
headers={"X-API-Version": str(API_VERSION)}, **kwargs)
# --------------------------------------------
# Background Updates:
# --------------------------------------------
@@ -134,7 +139,7 @@ class RenderServerProxy:
if self.__offline_flags: # if we're offline, don't bother with the long poll
ignore_token = True
url = f'jobs_long_poll?token={self.__jobs_cache_token}' if (self.__jobs_cache_token and
url = f'jobs/long_poll?token={self.__jobs_cache_token}' if (self.__jobs_cache_token and
not ignore_token) else 'jobs'
status_result = self.request_data(url, timeout=timeout)
if status_result is not None:
@@ -153,14 +158,11 @@ class RenderServerProxy:
# Get System Info:
# --------------------------------------------
def get_all_jobs(self, timeout=5, ignore_token=False):
def get_jobs(self, timeout=5, ignore_token=False):
if not self.__update_in_background or ignore_token:
self.__update_job_cache(timeout, ignore_token)
return self.__jobs_cache.copy() if self.__jobs_cache else None
def get_data(self, timeout=5):
return self.request_data('full_status', timeout=timeout)
def get_status(self):
status = self.request_data('status')
if status and not self.system_cpu:
@@ -175,17 +177,17 @@ class RenderServerProxy:
# Get Job Info:
# --------------------------------------------
def get_job_info(self, job_id, timeout=5):
return self.request_data(f'job/{job_id}', timeout=timeout)
def get_job(self, job_id, timeout=5):
return self.request_data(f'jobs/{job_id}', timeout=timeout)
def get_job_files_list(self, job_id):
return self.request_data(f"job/{job_id}/file_list")
def get_job_files(self, job_id):
return self.request_data(f'jobs/{job_id}/files')
# --------------------------------------------
# Job Lifecycle:
# --------------------------------------------
def post_job_to_server(self, file_path: Path, job_data, callback=None):
def create_job(self, file_path: Path, job_data, callback=None):
"""
Posts a job to the server.
@@ -204,7 +206,7 @@ class RenderServerProxy:
# Bypass uploading file if posting to localhost
if self.is_localhost:
job_data['local_path'] = str(file_path)
url = urljoin(f'http://{self.hostname}:{self.port}', '/api/add_job')
url = urljoin(f'http://{self.hostname}:{self.port}', '/api/jobs')
headers = {'Content-Type': 'application/json'}
return requests.post(url, data=json.dumps(job_data), headers=headers)
@@ -218,17 +220,17 @@ class RenderServerProxy:
# Create a monitor that will track the upload progress
monitor = MultipartEncoderMonitor(encoder, callback) if callback else MultipartEncoderMonitor(encoder)
headers = {'Content-Type': monitor.content_type}
url = urljoin(f'http://{self.hostname}:{self.port}', '/api/add_job')
url = urljoin(f'http://{self.hostname}:{self.port}', '/api/jobs')
# Send the request with proper resource management
with requests.post(url, data=monitor, headers=headers) as response:
return response
def cancel_job(self, job_id, confirm=False):
return self.request_data(f'job/{job_id}/cancel?confirm={confirm}')
return self._post(f'jobs/{job_id}/cancel', params={'confirm': confirm})
def delete_job(self, job_id, confirm=False):
return self.request_data(f'job/{job_id}/delete?confirm={confirm}')
return self._post(f'jobs/{job_id}/delete', params={'confirm': confirm})
def send_subjob_update_notification(self, parent_id, subjob):
"""
@@ -241,7 +243,7 @@ class RenderServerProxy:
Returns:
Response: The response from the server.
"""
return requests.post(f'http://{self.hostname}:{self.port}/api/job/{parent_id}/send_subjob_update_notification',
return requests.post(f'http://{self.hostname}:{self.port}/api/jobs/{parent_id}/subjob_update',
json=subjob.json())
# --------------------------------------------
@@ -249,18 +251,18 @@ class RenderServerProxy:
# --------------------------------------------
def get_engine_for_filename(self, filename:str, timeout=5):
response = self.request(f'engine_for_filename?filename={os.path.basename(filename)}', timeout)
response = self.request(f'engines/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)
def get_engine_availability(self, engine_name:str, timeout=5):
return self.request_data(f'engines/{engine_name}/availability', timeout)
def is_engine_available(self, engine_name:str, timeout=5):
return self.request_data(f'{engine_name}/is_available', timeout)
def get_engine_names(self, timeout=5):
return self.request_data('engines/names', timeout=timeout)
def get_all_engine_info(self, response_type='standard', timeout=5):
def get_engines(self, response_type='standard', timeout=5):
"""
Fetches all engine information from the server.
Fetches engine information from the server.
Args:
response_type (str, optional): Returns standard or full version of engine info
@@ -269,10 +271,10 @@ class RenderServerProxy:
Returns:
dict: A dictionary containing the engine information.
"""
all_data = self.request_data(f"engine_info?response_type={response_type}", timeout=timeout)
all_data = self.request_data(f'engines?response_type={response_type}', timeout=timeout)
return all_data
def get_engine_info(self, engine_name:str, response_type='standard', timeout=5):
def get_engine(self, engine_name:str, response_type='standard', timeout=5):
"""
Fetches specific engine information from the server.
@@ -284,33 +286,34 @@ class RenderServerProxy:
Returns:
dict: A dictionary containing the engine information.
"""
return self.request_data(f'{engine_name}/info?response_type={response_type}', timeout)
return self.request_data(f'engines/{engine_name}?response_type={response_type}', timeout)
def delete_engine(self, engine_name:str, version:str, system_cpu=None):
def delete_engine_download(self, engine_name:str, version:str, system_os=None, cpu=None):
"""
Sends a request to the server to delete a specific engine.
Sends a request to the server to delete a specific engine download.
Args:
engine_name (str): The name of the engine to delete.
version (str): The version of the engine to delete.
system_cpu (str, optional): The system CPU type. Defaults to None.
system_os (str, optional): The system OS. Defaults to None.
cpu (str, optional): The system CPU type. Defaults to None.
Returns:
Response: The response from the server.
"""
form_data = {'engine': engine_name, 'version': version, 'system_cpu': system_cpu}
return requests.post(f'http://{self.hostname}:{self.port}/api/delete_engine', json=form_data)
form_data = {'engine': engine_name, 'version': version, 'system_os': system_os, 'cpu': cpu}
return self._post('engines/delete', json=form_data)
# --------------------------------------------
# Download Files:
# --------------------------------------------
def download_all_job_files(self, job_id, save_path):
url = f"http://{self.hostname}:{self.port}/api/job/{job_id}/download_all"
url = f'http://{self.hostname}:{self.port}/api/jobs/{job_id}/download_all'
return self.__download_file_from_url(url, output_filepath=save_path)
def download_job_file(self, job_id, job_filename, save_path):
url = f"http://{self.hostname}:{self.port}/api/job/{job_id}/download?filename={job_filename}"
url = f'http://{self.hostname}:{self.port}/api/jobs/{job_id}/download?filename={job_filename}'
return self.__download_file_from_url(url, output_filepath=save_path)
@staticmethod
+3 -3
View File
@@ -172,7 +172,7 @@ class DistributedJobManager:
subjob_id = child_key.split('@')[0]
subjob_hostname = child_key.split('@')[-1]
subjob_data = RenderServerProxy(subjob_hostname).get_job_info(subjob_id)
subjob_data = RenderServerProxy(subjob_hostname).get_job(subjob_id)
if not subjob_data:
logger.warning(f"No response from {subjob_hostname}")
parent_job.children[child_key]['download_status'] = f'error: No response from {subjob_hostname}'
@@ -260,7 +260,7 @@ class DistributedJobManager:
subjob['engine_version'] = parent_worker.engine_version
logger.debug(f"Posting subjob with frames {subjob['start_frame']}-"
f"{subjob['end_frame']} to {server_hostname}")
post_results = RenderServerProxy(server_hostname).post_job_to_server(
post_results = RenderServerProxy(server_hostname).create_job(
file_path=project_path, job_data=subjob)
return post_results
@@ -276,7 +276,7 @@ class DistributedJobManager:
host_properties = ZeroconfServer.get_hostname_properties(hostname)
if host_properties.get('api_version') == API_VERSION:
if not system_os or (system_os and system_os == host_properties.get('system_os')):
response = RenderServerProxy(hostname).is_engine_available(engine_name)
response = RenderServerProxy(hostname).get_engine_availability(engine_name)
if response and response.get('available', False):
found_available_servers.append(response)
+7 -7
View File
@@ -66,7 +66,7 @@ class NewRenderJobForm(QWidget):
# Job / Server Data
self.server_proxy = RenderServerProxy(socket.gethostname())
self.project_info = None
self.installed_engines = {}
self.installed_engines = []
self.preferred_engine = None
# Setup
@@ -345,7 +345,7 @@ class NewRenderJobForm(QWidget):
self.engine_version_combo.addItem('latest')
self.file_format_combo.clear()
if current_engine:
engine_info = self.server_proxy.get_engine_info(current_engine, 'full', timeout=10)
engine_info = self.server_proxy.get_engine(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}'")
@@ -386,7 +386,7 @@ class NewRenderJobForm(QWidget):
self.job_name_input.setText(directory)
def args_help_button_clicked(self):
url = (f'http://{self.server_proxy.hostname}:{self.server_proxy.port}/api/engine/'
url = (f'http://{self.server_proxy.hostname}:{self.server_proxy.port}/api/engines/'
f'{self.engine_type.currentText()}/help')
self.engine_help_viewer = EngineHelpViewer(url)
self.engine_help_viewer.show()
@@ -404,7 +404,7 @@ class NewRenderJobForm(QWidget):
"""Called by the GetProjectInfoWorker - Do not call directly."""
try:
self.engine_type.addItems(self.installed_engines.keys())
self.engine_type.addItems(self.installed_engines)
self.engine_type.setCurrentText(self.preferred_engine)
self.engine_changed()
@@ -608,8 +608,8 @@ class SubmitWorker(QThread):
input_path = Path(latest_engine.perform_presubmission_tasks(input_path))
# submit
err_msg = ""
result = self.window.server_proxy.post_job_to_server(file_path=input_path, job_data=job_json,
callback=create_callback)
result = self.window.server_proxy.create_job(file_path=input_path, job_data=job_json,
callback=create_callback)
if not (result and result.ok):
err_msg = f"Error posting job to server: {result.text}"
@@ -633,7 +633,7 @@ class GetProjectInfoWorker(QThread):
def run(self):
try:
# get the engine info and add them all to the ui
self.window.installed_engines = self.window.server_proxy.get_installed_engines()
self.window.installed_engines = self.window.server_proxy.get_engine_names()
# select the best engine for the file type
self.window.preferred_engine = self.window.server_proxy.get_engine_for_filename(self.project_path)
+4 -2
View File
@@ -93,7 +93,7 @@ class EngineBrowserWindow(QMainWindow):
def update_table(self):
def update_table_worker():
raw_server_data = RenderServerProxy(self.hostname).get_all_engine_info()
raw_server_data = RenderServerProxy(self.hostname).get_engines()
if not raw_server_data:
return
@@ -158,7 +158,9 @@ class EngineBrowserWindow(QMainWindow):
if reply is not QMessageBox.StandardButton.Yes:
return
result = RenderServerProxy(self.hostname).delete_engine(engine_info['engine'], engine_info['version'])
result = RenderServerProxy(self.hostname).delete_engine_download(
engine_info['engine'], engine_info['version'], engine_info.get('system_os'), engine_info.get('cpu'),
)
if result.ok:
self.update_table()
else:
+9 -8
View File
@@ -334,7 +334,7 @@ class MainWindow(QMainWindow):
default_image_path = "error.png"
before_fetch_hostname = self.current_server_proxy.hostname
response = self.current_server_proxy.request(f'job/{job_id}/thumbnail?size=big')
response = self.current_server_proxy.request(f'jobs/{job_id}/thumbnail?size=big')
if response.ok:
try:
with io.BytesIO(response.content) as image_data_stream:
@@ -547,7 +547,8 @@ class MainWindow(QMainWindow):
"""
selected_job_ids = self.selected_job_ids()
if selected_job_ids:
url = f'http://{self.current_server_proxy.hostname}:{self.current_server_proxy.port}/api/job/{selected_job_ids[0]}/logs'
url = (f'http://{self.current_server_proxy.hostname}:{self.current_server_proxy.port}'
f'/api/jobs/{selected_job_ids[0]}/logs')
self.log_viewer_window = LogViewer(url)
self.log_viewer_window.show()
@@ -562,7 +563,7 @@ class MainWindow(QMainWindow):
return
if len(job_ids) == 1:
job = next((job for job in self.current_server_proxy.get_all_jobs() if job.get('id') == job_ids[0]), None)
job = next((job for job in self.current_server_proxy.get_jobs() if job.get('id') == job_ids[0]), None)
if job:
display_name = job.get('name', os.path.basename(job.get('input_path', '')))
message = f"Are you sure you want to stop job: {display_name}?"
@@ -591,7 +592,7 @@ class MainWindow(QMainWindow):
return
if len(job_ids) == 1:
job = next((job for job in self.current_server_proxy.get_all_jobs() if job.get('id') == job_ids[0]), None)
job = next((job for job in self.current_server_proxy.get_jobs() if job.get('id') == job_ids[0]), None)
if job:
display_name = job.get('name', os.path.basename(job.get('input_path', '')))
message = f"Are you sure you want to delete the job:\n{display_name}?"
@@ -616,8 +617,8 @@ class MainWindow(QMainWindow):
return
import webbrowser
download_url = (f"http://{self.current_server_proxy.hostname}:{self.current_server_proxy.port}"
f"/api/job/{job_ids[0]}/download_all")
download_url = (f'http://{self.current_server_proxy.hostname}:{self.current_server_proxy.port}'
f'/api/jobs/{job_ids[0]}/download_all')
webbrowser.open(download_url)
def open_files(self, event):
@@ -626,7 +627,7 @@ class MainWindow(QMainWindow):
return
for job_id in job_ids:
job_info = self.current_server_proxy.get_job_info(job_id)
job_info = self.current_server_proxy.get_job(job_id)
path = os.path.dirname(job_info['output_path'])
launch_url(path)
@@ -665,7 +666,7 @@ class BackgroundUpdater(QThread):
ZeroconfServer.get_hostname_properties(x)['api_version'] == API_VERSION]
if self.window.current_server_proxy:
self.window.job_data[self.window.current_server_proxy.hostname] = \
self.window.current_server_proxy.get_all_jobs(ignore_token=False)
self.window.current_server_proxy.get_jobs(ignore_token=False)
self.needs_update = False
self.updated_signal.emit()
time.sleep(0.05)
+2 -2
View File
@@ -37,7 +37,7 @@ class GetEngineInfoWorker(QThread):
self.parent = parent
def run(self):
data = RenderServerProxy(socket.gethostname()).get_all_engine_info()
data = RenderServerProxy(socket.gethostname()).get_engines()
self.done.emit(data)
class SettingsWindow(QMainWindow):
@@ -549,4 +549,4 @@ if __name__ == "__main__":
app = QApplication([])
window = SettingsWindow()
window.show()
app.exec()
app.exec()
+1 -1
View File
@@ -17,7 +17,7 @@ def download_missing_frames_from_subjob(local_job, subjob_id, subjob_hostname):
try:
local_files = [os.path.basename(x) for x in local_job.file_list()]
subjob_proxy = RenderServerProxy(subjob_hostname)
subjob_files = subjob_proxy.get_job_files_list(job_id=subjob_id) or []
subjob_files = subjob_proxy.get_job_files(job_id=subjob_id) or []
for subjob_filename in subjob_files:
if subjob_filename not in local_files: