diff --git a/config.yaml b/config.yaml index 95067b1..564f5ed 100644 --- a/config.yaml +++ b/config.yaml @@ -1,5 +1,6 @@ upload_folder: "/Users/brett/Desktop/zordon-uploads/" max_content_path: 100000000 +server_log_level: info flask_log_level: error flask_debug_enable: false queue_eval_seconds: 1 \ No newline at end of file diff --git a/dashboard.py b/dashboard.py index 33354bb..901e8ce 100755 --- a/dashboard.py +++ b/dashboard.py @@ -18,7 +18,7 @@ from rich.text import Text from rich.tree import Tree from utilities.render_worker import RenderStatus, string_to_status -from zordon_server import start_server +from server import start_server """ The RenderDashboard is designed to be run on a remote machine or on the local server diff --git a/server.py b/server.py index 89994c6..0986dd8 100755 --- a/server.py +++ b/server.py @@ -97,74 +97,113 @@ def snapshot(): @app.post('/add_job') def add_job(): - """Create new job and add to server render queue""" - renderer = request.json.get("renderer", None) - input_path = request.json.get("input", None) - output_path = request.json.get("output", None) - priority = request.json.get('priority', 2) - args = request.json.get('args', None) - client = request.json.get('client', RenderManager.host_name) - force_start = request.json.get('force_start', False) - if None in [renderer, input_path, output_path]: - return {'error': 'missing required parameters'}, 400 + try: + """Create new job and add to server render queue""" + if request.is_json: + request_params = request.get_json() + else: + # handle request values from HTML pages - may deprecate if HTML is not used or can pass JSON from HTML + request_params = request.values - if client == RenderManager.host_name: - logger.info(f"Creating job locally - {input_path}") - # Local Renders - try: - render_job = RenderWorkerFactory.create_worker(renderer, input_path, output_path, args) - except ValueError as e: - logger.exception(e) - return {'error': str(e)}, 400 + renderer = request_params.get("renderer", None) + input_path = request_params.get("input", None) + output_path = request_params.get("output", "test-output.mp4") + priority = int(request_params.get('priority', 2)) + args = request_params.get('args', {}) + client = request_params.get('client', RenderManager.host_name) + force_start = request_params.get('force_start', False) + uploaded_file = request.files.get('file', None) + html_origin = request_params.get('origin', None) == 'html' + custom_id = None - new_job = RenderJob(render_job, priority=priority) - RenderManager.add_to_render_queue(new_job, force_start=force_start) + if None in [renderer, input_path or (uploaded_file and uploaded_file.filename), output_path]: + err_msg = 'Cannot add job: Missing required parameters' + logger.error(err_msg) + return {'error': err_msg} - return new_job.json() + # cleanup args from html form and convert them into an args dict + for key, val in request_params.items(): + if key.startswith(renderer): + cleaned_key = key.split('+')[-1] + args[cleaned_key] = val - elif client in RenderManager.render_clients: + # handle uploaded files + if uploaded_file and uploaded_file.filename: + logger.info(f"Receiving uploaded file {uploaded_file.filename}") + new_id = RenderJob.generate_id() + job_dir = os.path.join(app.config['UPLOAD_FOLDER'], new_id + "-" + uploaded_file.filename) + if not os.path.exists(job_dir): + os.makedirs(job_dir) - # see if host is available - if RenderManager.is_client_available(client): + local_path = os.path.join(job_dir, secure_filename(uploaded_file.filename)) + uploaded_file.save(local_path) + input_path = local_path - if args and renderer == 'blender' and args.get('pack_files', False): - from utilities.blender_worker import pack_blender_files - new_path = pack_blender_files(path=input_path) - if new_path: - logger.info(f'Packed Blender file successfully: {new_path}') - input_path = new_path - else: - err_msg = f'Failed to pack Blender file: {input_path}' - logger.error(err_msg) - return {'error': err_msg}, 400 + # todo: finish output_path - currently placeholder data + output_path = os.path.join(job_dir, uploaded_file.filename + "-output.mp4") - # call uploader on remote client + # local renders + if client == RenderManager.host_name: + logger.info(f"Creating job locally - {input_path}") try: - job_files = {'file': open(input_path, 'rb')} - job_data = request.json - job_data['input'] = input_path - logger.info(f"Uploading file {input_path} to client {client}") - response = requests.post(f"http://{client}:8080/uploader", files=job_files, data=job_data) - if response.ok: - logger.info("Job submitted successfully!") - return response.json() if response.json() else "Job ok" - else: - return {'error', 'Job rejected by client'}, 400 - except requests.ConnectionError as e: - err_msg = f"Error submitting job to client: {client}" + render_job = RenderWorkerFactory.create_worker(renderer, input_path, output_path, args) + except ValueError as e: + logger.exception(e) + return {'error': str(e)}, 400 + + new_job = RenderJob(render_job, priority=priority, custom_id=custom_id) + RenderManager.add_to_render_queue(new_job, force_start=force_start) + + return new_job.json() + + # client renders + elif client in RenderManager.render_clients: + + # see if host is available + if RenderManager.is_client_available(client): + + if args and renderer == 'blender' and args.get('pack_files', False): + from utilities.blender_worker import pack_blender_files + new_path = pack_blender_files(path=input_path) + if new_path: + logger.info(f'Packed Blender file successfully: {new_path}') + input_path = new_path + else: + err_msg = f'Failed to pack Blender file: {input_path}' + logger.error(err_msg) + return {'error': err_msg}, 400 + + # call uploader on remote client + try: + job_files = {'file': open(input_path, 'rb')} + job_data = request.json + job_data['input'] = input_path + logger.info(f"Uploading file {input_path} to client {client}") + response = requests.post(f"http://{client}:8080/add_job", files=job_files, data=job_data) + if response.ok: + logger.info("Job submitted successfully!") + return response.json() if response.json() else "Job ok" + else: + return {'error', 'Job rejected by client'}, 400 + except requests.ConnectionError as e: + err_msg = f"Error submitting job to client: {client}" + logger.error(err_msg) + return {'error', err_msg}, 400 + else: + # client is not available + err_msg = f"Render client '{client}' is unreachable" logger.error(err_msg) return {'error', err_msg}, 400 + else: - # client is not available - err_msg = f"Render client '{client}' is unreachable" + err_msg = f"Unknown render client: '{client}'" logger.error(err_msg) return {'error', err_msg}, 400 - else: - err_msg = f"Unknown render client: '{client}'" - logger.error(err_msg) - return {'error', err_msg}, 400 + except Exception as e: + logger.exception(f"Unknown error adding job: {e}") + return {'error', 'cannot add job'} @app.get('/cancel_job') @@ -204,46 +243,7 @@ def default(): @app.route('/upload') def upload_file_page(): - return render_template('upload.html') - - -@app.route('/uploader', methods=['POST']) -def upload_file(): - if request.method == 'POST': - try: - - uploaded_file = request.files['file'] - if not uploaded_file.filename: - return {'error': 'no file uploaded'} - - # generate directory to use - logger.debug(f"Receiving uploaded file {uploaded_file.filename}") - new_id = RenderJob.generate_id() - job_dir = os.path.join(app.config['UPLOAD_FOLDER'], new_id + "-" + uploaded_file.filename) - if not os.path.exists(job_dir): - os.makedirs(job_dir) - - local_path = os.path.join(job_dir, secure_filename(uploaded_file.filename)) - uploaded_file.save(local_path) - renderer = request.values['renderer'] - - # todo: finish output_path - currently placeholder data - output_path = os.path.join(job_dir, uploaded_file.filename + "-output.mp4") - - try: - render_job = RenderWorkerFactory.create_worker(renderer, local_path, output_path, - args=request.values.get('args', None)) - new_job = RenderJob(render_job, custom_id=new_id) - RenderManager.add_to_render_queue(new_job) - return new_job.json() - except ValueError as e: - logger.exception(e) - return {'error': str(e)}, 400 - - except Exception as e: - logger.exception(e) - - return {'error': 'unknown error'} + return render_template('upload.html', render_clients=RenderManager.render_clients) def start_server(background_thread=False): @@ -253,12 +253,12 @@ def start_server(background_thread=False): RenderManager.evaluate_queue() time.sleep(delay_sec) - logging.basicConfig(format='%(asctime)s: %(levelname)s: %(module)s: %(message)s', datefmt='%d-%b-%y %H:%M:%S', - level=logging.INFO) - with open('config.yaml') as f: config = yaml.load(f, Loader=yaml.FullLoader) + logging.basicConfig(format='%(asctime)s: %(levelname)s: %(module)s: %(message)s', datefmt='%d-%b-%y %H:%M:%S', + level=config.get('server_log_level', 'INFO').upper()) + app.config['UPLOAD_FOLDER'] = config['upload_folder'] app.config['MAX_CONTENT_PATH'] = config['max_content_path'] # app.config['RESULT_STATIC_PATH'] = 'static' diff --git a/templates/upload.html b/templates/upload.html index edea94a..a1885e4 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -1,9 +1,52 @@ - -
- - -
- + + +

Upload a file

+ +
+
+
+
+ + + +
+ Render Client: + +
+ +
+ + +
+
+
+ Priority: + +
+
+ Engine: + +
+
+ + +
+ \ No newline at end of file diff --git a/utilities/aerender_worker.py b/utilities/aerender_worker.py index 6b30083..cc2bc37 100644 --- a/utilities/aerender_worker.py +++ b/utilities/aerender_worker.py @@ -35,12 +35,13 @@ class AERenderWorker(BaseRenderWorker): render_engine = 'aerender' supported_extensions = ['.aep'] - def __init__(self, project, comp, render_settings, omsettings, output): - super(AERenderWorker, self).__init__(input=project, output=output) + def __init__(self, input_path, output_path, args=None): + super(AERenderWorker, self).__init__(input_path=input_path, output_path=output_path, ignore_extensions=False, + args=args) - self.comp = comp - self.render_settings = render_settings - self.omsettings = omsettings + self.comp = args.get('comp', None) + self.render_settings = args.get('render_settings', None) + self.omsettings = args.get('omsettings', None) self.progress = 0 self.progress_history = [] diff --git a/utilities/blender_worker.py b/utilities/blender_worker.py index 75d335a..1bad831 100644 --- a/utilities/blender_worker.py +++ b/utilities/blender_worker.py @@ -25,7 +25,8 @@ class BlenderRenderWorker(BaseRenderWorker): install_paths = ['/Applications/Blender.app/Contents/MacOS/Blender'] def __init__(self, input_path, output_path, args=None, render_all_frames=False, engine='BLENDER_EEVEE'): - super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, args=args) + super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, + ignore_extensions=False, args=args) self.engine = engine # or 'CYCLES' self.format = 'JPEG' diff --git a/utilities/ffmpeg_worker.py b/utilities/ffmpeg_worker.py index 833a622..17f3c21 100644 --- a/utilities/ffmpeg_worker.py +++ b/utilities/ffmpeg_worker.py @@ -21,7 +21,8 @@ class FFMPEGRenderWorker(BaseRenderWorker): render_engine = 'ffmpeg' def __init__(self, input_path, output_path, args=None): - super(FFMPEGRenderWorker, self).__init__(input_path=input_path, output_path=output_path, ignore_extensions=True, args=args) + super(FFMPEGRenderWorker, self).__init__(input_path=input_path, output_path=output_path, ignore_extensions=True, + args=args) self.total_frames = -1 if os.path.exists(input_path): diff --git a/utilities/render_worker.py b/utilities/render_worker.py index e740af7..d677695 100644 --- a/utilities/render_worker.py +++ b/utilities/render_worker.py @@ -38,7 +38,7 @@ class BaseRenderWorker(object): def version(): return 'Unknown' - def __init__(self, input_path, output_path, args=None, ignore_extensions=False): + def __init__(self, input_path, output_path, args=None, ignore_extensions=True): if not ignore_extensions: if not any(ext in input_path for ext in self.supported_extensions):