mirror of
https://github.com/blw1138/Zordon.git
synced 2026-06-09 13:39:24 -05:00
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:
+94
-125
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user