mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
Add submit job to HTML page
This commit is contained in:
@@ -10,32 +10,67 @@ from datetime import datetime
|
|||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from flask import Flask, request, render_template, send_file, after_this_request, Response, redirect, url_for
|
from flask import Flask, request, render_template, send_file, after_this_request, Response, redirect, url_for, abort
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
|
||||||
from lib.render_job import RenderJob
|
from lib.render_job import RenderJob
|
||||||
from lib.render_queue import RenderQueue
|
from lib.render_queue import RenderQueue
|
||||||
from utilities.render_worker import RenderWorkerFactory, string_to_status
|
from lib.server_helper import post_job_to_server
|
||||||
|
from utilities.render_worker import RenderWorkerFactory, string_to_status, RenderStatus
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
server = Flask(__name__, template_folder='../templates')
|
server = Flask(__name__, template_folder='../templates')
|
||||||
|
|
||||||
|
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
|
||||||
|
RenderStatus.COMPLETED, RenderStatus.CANCELLED]
|
||||||
|
|
||||||
|
|
||||||
|
def sorted_jobs(all_jobs, sort_by_date=True):
|
||||||
|
if not sort_by_date:
|
||||||
|
sorted_job_list = []
|
||||||
|
if all_jobs:
|
||||||
|
for status_category in categories:
|
||||||
|
found_jobs = [x for x in all_jobs if x.render_status() == status_category.value]
|
||||||
|
if found_jobs:
|
||||||
|
sorted_found_jobs = sorted(found_jobs, key=lambda d: d.date_created, reverse=True)
|
||||||
|
sorted_job_list.extend(sorted_found_jobs)
|
||||||
|
else:
|
||||||
|
sorted_job_list = sorted(all_jobs, key=lambda d: d.date_created, reverse=True)
|
||||||
|
return sorted_job_list
|
||||||
|
|
||||||
|
|
||||||
@server.route('/')
|
@server.route('/')
|
||||||
@server.route('/index')
|
@server.route('/index')
|
||||||
def index():
|
def index():
|
||||||
return render_template('index.html', all_jobs=RenderQueue.job_queue, hostname=RenderQueue.host_name)
|
return render_template('index.html', all_jobs=sorted_jobs(RenderQueue.job_queue), hostname=RenderQueue.host_name,
|
||||||
|
renderer_info=renderer_info())
|
||||||
|
|
||||||
|
|
||||||
@server.route('/ui/job/<job_id>/full_details')
|
@server.route('/ui/job/<job_id>/full_details')
|
||||||
def job_detail(job_id):
|
def job_detail(job_id):
|
||||||
found_job = RenderQueue.job_with_id(job_id)
|
found_job = RenderQueue.job_with_id(job_id)
|
||||||
if found_job:
|
if found_job:
|
||||||
table_html = json2html.json2html.convert(json=found_job.json())
|
table_html = json2html.json2html.convert(json=found_job.json(), table_attributes='class="table is-narrow is-striped"')
|
||||||
return render_template('details.html', detail_table=table_html)
|
media_url = None
|
||||||
|
if found_job.file_list():
|
||||||
|
media_basename = os.path.basename(found_job.file_list()[0])
|
||||||
|
media_url = f"/api/job/{job_id}/file/{media_basename}"
|
||||||
|
return render_template('details.html', detail_table=table_html, media_url=media_url)
|
||||||
return f'Cannot find job with ID {job_id}', 400
|
return f'Cannot find job with ID {job_id}', 400
|
||||||
|
|
||||||
|
|
||||||
|
# Get job file routing
|
||||||
|
@server.route('/api/job/<job_id>/file/<filename>', methods=['GET'])
|
||||||
|
def get_job_file(job_id, filename):
|
||||||
|
found_job = RenderQueue.job_with_id(job_id)
|
||||||
|
try:
|
||||||
|
for full_path in found_job.file_list():
|
||||||
|
if filename in full_path:
|
||||||
|
return send_file(path_or_file=full_path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@server.get('/api/jobs')
|
@server.get('/api/jobs')
|
||||||
def jobs_json():
|
def jobs_json():
|
||||||
return [x.json() for x in RenderQueue.job_queue]
|
return [x.json() for x in RenderQueue.job_queue]
|
||||||
@@ -169,8 +204,22 @@ def add_job_handler():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
"""Create new job and add to server render queue"""
|
"""Create new job and add to server render queue"""
|
||||||
if not request.form.get('json', None) and not request.is_json:
|
if request.is_json:
|
||||||
return 'missing json data', 400
|
jobs_list = [request.json]
|
||||||
|
elif request.form.get('json', None):
|
||||||
|
jobs_list = json.loads(request.form['json'])
|
||||||
|
else:
|
||||||
|
form_dict = dict(request.form)
|
||||||
|
args = {}
|
||||||
|
arg_keys = [k for k in form_dict.keys() if '-arg_' in k]
|
||||||
|
for key in arg_keys:
|
||||||
|
if form_dict['renderer'] in key:
|
||||||
|
cleaned_key = key.split('-arg_')[-1]
|
||||||
|
args[cleaned_key] = form_dict[key]
|
||||||
|
form_dict.pop(key)
|
||||||
|
args['raw'] = form_dict.get('raw_args', None)
|
||||||
|
form_dict['args'] = args
|
||||||
|
jobs_list = [form_dict]
|
||||||
|
|
||||||
# handle uploaded files
|
# handle uploaded files
|
||||||
uploaded_file = request.files.get('file', None)
|
uploaded_file = request.files.get('file', None)
|
||||||
@@ -185,8 +234,6 @@ def add_job_handler():
|
|||||||
uploaded_file.save(uploaded_file_local_path)
|
uploaded_file.save(uploaded_file_local_path)
|
||||||
|
|
||||||
# convert job input paths for uploaded files and add jobs
|
# convert job input paths for uploaded files and add jobs
|
||||||
json_string = request.form.get('json', None)
|
|
||||||
jobs_list = json.loads(json_string) if json_string else [request.json]
|
|
||||||
results = []
|
results = []
|
||||||
for job in jobs_list:
|
for job in jobs_list:
|
||||||
if uploaded_file_local_path:
|
if uploaded_file_local_path:
|
||||||
@@ -205,6 +252,10 @@ def add_job_handler():
|
|||||||
return results, response.get('code', 500)
|
return results, response.get('code', 500)
|
||||||
else:
|
else:
|
||||||
return results, 400
|
return results, 400
|
||||||
|
|
||||||
|
if request.args.get('redirect', False):
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
else:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -296,7 +347,10 @@ def cancel_job(job_id):
|
|||||||
return 'Confirmation required to cancel job', 400
|
return 'Confirmation required to cancel job', 400
|
||||||
|
|
||||||
if RenderQueue.cancel_job(found_job):
|
if RenderQueue.cancel_job(found_job):
|
||||||
|
if request.args.get('redirect', False):
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
else:
|
||||||
|
return "Job cancelled"
|
||||||
else:
|
else:
|
||||||
return "Unknown error", 500
|
return "Unknown error", 500
|
||||||
|
|
||||||
@@ -328,10 +382,13 @@ def delete_job(job_id):
|
|||||||
shutil.rmtree(input_dir)
|
shutil.rmtree(input_dir)
|
||||||
|
|
||||||
RenderQueue.delete_job(found_job)
|
RenderQueue.delete_job(found_job)
|
||||||
|
if request.args.get('redirect', False):
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
else:
|
||||||
|
return "Job deleted", 200
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return "Unknown error", 500
|
return f"Unknown error: {e}", 500
|
||||||
|
|
||||||
|
|
||||||
@server.get('/api/clear_history')
|
@server.get('/api/clear_history')
|
||||||
@@ -362,12 +419,3 @@ def upload_file_page():
|
|||||||
return render_template('upload.html', render_clients=RenderQueue.render_clients,
|
return render_template('upload.html', render_clients=RenderQueue.render_clients,
|
||||||
supported_renderers=RenderWorkerFactory.supported_renderers())
|
supported_renderers=RenderWorkerFactory.supported_renderers())
|
||||||
|
|
||||||
|
|
||||||
#todo: move this to a helper file
|
|
||||||
def post_job_to_server(input_path, job_list, client, server_port=8080):
|
|
||||||
# Pack job data and submit to server
|
|
||||||
job_files = {'file': (os.path.basename(input_path), open(input_path, 'rb'), 'application/octet-stream'),
|
|
||||||
'json': (None, json.dumps(job_list), 'application/json')}
|
|
||||||
|
|
||||||
req = requests.post(f'http://{client}:{server_port}/api/add_job', files=job_files)
|
|
||||||
return req
|
|
||||||
|
|||||||
12
lib/server_helper.py
Normal file
12
lib/server_helper.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import requests
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def post_job_to_server(input_path, job_list, client, server_port=8080):
|
||||||
|
# Pack job data and submit to server
|
||||||
|
job_files = {'file': (os.path.basename(input_path), open(input_path, 'rb'), 'application/octet-stream'),
|
||||||
|
'json': (None, json.dumps(job_list), 'application/json')}
|
||||||
|
|
||||||
|
req = requests.post(f'http://{client}:{server_port}/api/add_job', files=job_files)
|
||||||
|
return req
|
||||||
44
lib/static/modals.js
Normal file
44
lib/static/modals.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Functions to open and close a modal
|
||||||
|
function openModal($el) {
|
||||||
|
$el.classList.add('is-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal($el) {
|
||||||
|
$el.classList.remove('is-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAllModals() {
|
||||||
|
(document.querySelectorAll('.modal') || []).forEach(($modal) => {
|
||||||
|
closeModal($modal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a click event on buttons to open a specific modal
|
||||||
|
(document.querySelectorAll('.js-modal-trigger') || []).forEach(($trigger) => {
|
||||||
|
const modal = $trigger.dataset.target;
|
||||||
|
const $target = document.getElementById(modal);
|
||||||
|
|
||||||
|
$trigger.addEventListener('click', () => {
|
||||||
|
openModal($target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a click event on various child elements to close the parent modal
|
||||||
|
(document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
|
||||||
|
const $target = $close.closest('.modal');
|
||||||
|
|
||||||
|
$close.addEventListener('click', () => {
|
||||||
|
closeModal($target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a keyboard event to close all modals
|
||||||
|
document.addEventListener('keydown', (event) => {
|
||||||
|
const e = event || window.event;
|
||||||
|
|
||||||
|
if (e.keyCode === 27) { // Escape key
|
||||||
|
closeAllModals();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<script src="https://kit.fontawesome.com/698705d14d.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/698705d14d.js" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename = 'modals.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename = 'modals.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body onload="configureDropDownLists(document.getElementById('renderer'), document.getElementById('export_format'))">
|
||||||
|
|
||||||
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
|
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
@@ -24,52 +24,11 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div id="navbarBasicExample" class="navbar-menu">-->
|
<div class="navbar-end">
|
||||||
<!-- <div class="navbar-start">-->
|
<div class="navbar-item">
|
||||||
<!-- <a class="navbar-item">-->
|
<button class="button is-primary is-outlined js-modal-trigger" data-target="add-job-modal">Submit Job</button>
|
||||||
<!-- Home-->
|
</div>
|
||||||
<!-- </a>-->
|
</div>
|
||||||
|
|
||||||
<!-- <a class="navbar-item">-->
|
|
||||||
<!-- Documentation-->
|
|
||||||
<!-- </a>-->
|
|
||||||
|
|
||||||
<!-- <div class="navbar-item has-dropdown is-hoverable">-->
|
|
||||||
<!-- <a class="navbar-link">-->
|
|
||||||
<!-- More-->
|
|
||||||
<!-- </a>-->
|
|
||||||
|
|
||||||
<!-- <div class="navbar-dropdown">-->
|
|
||||||
<!-- <a class="navbar-item">-->
|
|
||||||
<!-- About-->
|
|
||||||
<!-- </a>-->
|
|
||||||
<!-- <a class="navbar-item">-->
|
|
||||||
<!-- Jobs-->
|
|
||||||
<!-- </a>-->
|
|
||||||
<!-- <a class="navbar-item">-->
|
|
||||||
<!-- Contact-->
|
|
||||||
<!-- </a>-->
|
|
||||||
<!-- <hr class="navbar-divider">-->
|
|
||||||
<!-- <a class="navbar-item">-->
|
|
||||||
<!-- Report an issue-->
|
|
||||||
<!-- </a>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
|
|
||||||
<!-- <div class="navbar-end">-->
|
|
||||||
<!-- <div class="navbar-item">-->
|
|
||||||
<!-- <div class="buttons">-->
|
|
||||||
<!-- <a class="button is-primary">-->
|
|
||||||
<!-- <strong>Sign up</strong>-->
|
|
||||||
<!-- </a>-->
|
|
||||||
<!-- <a class="button is-light">-->
|
|
||||||
<!-- Log in-->
|
|
||||||
<!-- </a>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
@@ -103,7 +62,7 @@
|
|||||||
<td>{{job.priority}}</td>
|
<td>{{job.priority}}</td>
|
||||||
<td>{{job.render_status().value}}</td>
|
<td>{{job.render_status().value}}</td>
|
||||||
<td>{{job.time_elapsed()}}</td>
|
<td>{{job.time_elapsed()}}</td>
|
||||||
<td>{{job.percent_complete()}}</td>
|
<td>{{ '{0:0.0f}'.format(job.percent_complete() * 100) }}%</td>
|
||||||
<td>{{job.frame_count()}}</td>
|
<td>{{job.frame_count()}}</td>
|
||||||
<td>{{job.client}}</td>
|
<td>{{job.client}}</td>
|
||||||
<td>{{job.worker.last_output}}</td>
|
<td>{{job.worker.last_output}}</td>
|
||||||
@@ -116,7 +75,7 @@
|
|||||||
<span class="icon"><i class="fa-regular fa-file-lines"></i></span>
|
<span class="icon"><i class="fa-regular fa-file-lines"></i></span>
|
||||||
</button>
|
</button>
|
||||||
{% if job.render_status().value in ['running', 'scheduled', 'not_started']: %}
|
{% if job.render_status().value in ['running', 'scheduled', 'not_started']: %}
|
||||||
<button class="button is-warning is-active" onclick="window.location.href='/api/job/{{job.id}}/cancel?confirm=True';">
|
<button class="button is-warning is-active" onclick="window.location.href='/api/job/{{job.id}}/cancel?confirm=True&redirect=True';">
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
{% elif job.render_status().value == 'completed': %}
|
{% elif job.render_status().value == 'completed': %}
|
||||||
@@ -125,7 +84,7 @@
|
|||||||
<span>{{job.file_list() | length}}</span>
|
<span>{{job.file_list() | length}}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="button is-danger" onclick="window.location.href='/api/job/{{job.id}}/delete?confirm=True'">
|
<button class="button is-danger" onclick="window.location.href='/api/job/{{job.id}}/delete?confirm=True&redirect=True'">
|
||||||
<span class="icon"><i class="fa-regular fa-trash-can"></i></span>
|
<span class="icon"><i class="fa-regular fa-trash-can"></i></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,5 +95,130 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="add-job-modal" class="modal">
|
||||||
|
<!-- Start Add Form -->
|
||||||
|
<form id="submit_job" action="/api/add_job?redirect=True" method="POST" enctype="multipart/form-data">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Submit New Job</p>
|
||||||
|
<button class="delete" aria-label="close" type="button"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<!-- File Uploader -->
|
||||||
|
|
||||||
|
<div id="file-uploader" class="file has-name is-fullwidth">
|
||||||
|
<label class="file-label">
|
||||||
|
<input class="file-input" type="file" name="file">
|
||||||
|
<span class="file-cta">
|
||||||
|
<span class="file-icon">
|
||||||
|
<i class="fas fa-upload"></i>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">
|
||||||
|
Choose a file…
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="file-name">
|
||||||
|
No File Uploaded
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<script>
|
||||||
|
const fileInput = document.querySelector('#file-uploader input[type=file]');
|
||||||
|
fileInput.onchange = () => {
|
||||||
|
if (fileInput.files.length > 0) {
|
||||||
|
const fileName = document.querySelector('#file-uploader .file-name');
|
||||||
|
fileName.textContent = fileInput.files[0].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function configureDropDownLists(ddl1, ddl2) {
|
||||||
|
var renderers = {
|
||||||
|
{% for renderer in renderer_info: %}
|
||||||
|
{% if renderer_info[renderer]['supported_export_formats']: %}
|
||||||
|
{{renderer}}: [
|
||||||
|
{% for format in renderer_info[renderer]['supported_export_formats']: %}
|
||||||
|
'{{format}}',
|
||||||
|
{% endfor %}
|
||||||
|
],
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
ddl2.options.length = 0;
|
||||||
|
var options = renderers[ddl1.value]
|
||||||
|
for (i = 0; i < options.length; i++) {
|
||||||
|
createOption(ddl2, options[i], options[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOption(ddl, text, value) {
|
||||||
|
var opt = document.createElement('option');
|
||||||
|
opt.value = value;
|
||||||
|
opt.text = text;
|
||||||
|
ddl.options.add(opt);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Renderer -->
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Renderer</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select">
|
||||||
|
<select id="renderer" name="renderer" onchange="configureDropDownLists(this, document.getElementById('export_format'))">
|
||||||
|
{% for renderer in renderer_info: %}
|
||||||
|
<option name="renderer" value="{{renderer}}">{{renderer}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Priority -->
|
||||||
|
<div class="control">
|
||||||
|
<label class="label">Priority</label>
|
||||||
|
<label class="radio"><input type="radio" name="priority" value="1">1</label>
|
||||||
|
<label class="radio"><input type="radio" name="priority" value="2" checked>2</label>
|
||||||
|
<label class="radio"><input type="radio" name="priority" value="3">3</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<!-- Output Path -->
|
||||||
|
<label class="label">Output</label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<input class="input is-success" type="text" placeholder="Output Name" name="output_path">
|
||||||
|
</div>
|
||||||
|
<p class="control">
|
||||||
|
<span class="select">
|
||||||
|
<select id="export_format" name="export_format">
|
||||||
|
<option value="ar">option</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Checkboxes -->
|
||||||
|
<!-- <div class="field">-->
|
||||||
|
<!-- <div class="control">-->
|
||||||
|
<!-- <label class="checkbox"><input type="checkbox" name="blender-arg_render_all_frames"> Render All Frames</label>-->
|
||||||
|
<!-- <label class="checkbox"><input type="checkbox" name="blender-arg_multiple_cameras"> Multiple Cameras</label>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="Custom Args" name="raw_args">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- End Add Form -->
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<input class="button is-link" type="submit"/>
|
||||||
|
<button class="button" type="button">Cancel</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -10,8 +10,8 @@ class BlenderRenderWorker(BaseRenderWorker):
|
|||||||
render_engine = 'blender'
|
render_engine = 'blender'
|
||||||
supported_extensions = ['.blend']
|
supported_extensions = ['.blend']
|
||||||
install_paths = ['/Applications/Blender.app/Contents/MacOS/Blender']
|
install_paths = ['/Applications/Blender.app/Contents/MacOS/Blender']
|
||||||
supported_export_formats = ['TGA', 'RAWTGA', 'JPEG', 'IRIS', 'IRIZ', 'AVIRAW', 'AVIJPEG', 'PNG', 'BMP', 'HDR', 'TIFF',
|
supported_export_formats = ['TGA', 'RAWTGA', 'JPEG', 'IRIS', 'IRIZ', 'AVIRAW', 'AVIJPEG', 'PNG', 'BMP', 'HDR',
|
||||||
'OPEN_EXR', 'OPEN_EXR_MULTILAYER', 'MPEG', 'CINEON', 'DPX', 'DDS', 'JP2']
|
'TIFF', 'OPEN_EXR', 'OPEN_EXR_MULTILAYER', 'MPEG', 'CINEON', 'DPX', 'DDS', 'JP2']
|
||||||
|
|
||||||
def __init__(self, input_path, output_path, args=None):
|
def __init__(self, input_path, output_path, args=None):
|
||||||
super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path,
|
super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path,
|
||||||
@@ -29,10 +29,9 @@ class BlenderRenderWorker(BaseRenderWorker):
|
|||||||
|
|
||||||
# Scene Info
|
# Scene Info
|
||||||
self.scene_info = get_scene_info(input_path)
|
self.scene_info = get_scene_info(input_path)
|
||||||
self.total_frames = int(self.scene_info.get('frame_end', 0))
|
self.total_frames = (int(self.scene_info.get('frame_end', 0)) - int(self.scene_info.get('frame_start', 0)) + 1) \
|
||||||
|
if self.render_all_frames else 1
|
||||||
self.current_frame = int(self.scene_info.get('frame_start', 0))
|
self.current_frame = int(self.scene_info.get('frame_start', 0))
|
||||||
self.resolution = {'x': int(self.scene_info.get('resolution_x', 0)),
|
|
||||||
'y': int(self.scene_info.get('resolution_y', 0))}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def version(cls):
|
def version(cls):
|
||||||
@@ -61,6 +60,11 @@ class BlenderRenderWorker(BaseRenderWorker):
|
|||||||
# all frames or single
|
# all frames or single
|
||||||
cmd.extend(['-a'] if self.render_all_frames else ['-f', str(self.frame_to_render)])
|
cmd.extend(['-a'] if self.render_all_frames else ['-f', str(self.frame_to_render)])
|
||||||
|
|
||||||
|
# Convert raw args from string if available
|
||||||
|
raw_args = self.args.get('raw', None)
|
||||||
|
if raw_args:
|
||||||
|
cmd.extend(raw_args.split(' '))
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
def _parse_stdout(self, line):
|
def _parse_stdout(self, line):
|
||||||
|
|||||||
@@ -43,9 +43,14 @@ class FFMPEGRenderWorker(BaseRenderWorker):
|
|||||||
|
|
||||||
cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input_path]
|
cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input_path]
|
||||||
if self.args:
|
if self.args:
|
||||||
cmd.extend(self.args)
|
cmd.extend([x for x in self.args if x != 'raw'])
|
||||||
cmd.append(self.output_path)
|
|
||||||
|
|
||||||
|
# Convert raw args from string if available
|
||||||
|
raw_args = self.args.get('raw', None)
|
||||||
|
print(raw_args)
|
||||||
|
if raw_args:
|
||||||
|
cmd.extend(raw_args.split(' '))
|
||||||
|
cmd.append(self.output_path)
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
def percent_complete(self):
|
def percent_complete(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user