HTML / JS / CSS work
@@ -61,7 +61,8 @@ def job_detail(job_id):
|
||||
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,
|
||||
job_status=found_job.render_status().value.title(), job=found_job)
|
||||
hostname=RenderQueue.host_name, job_status=found_job.render_status().value.title(),
|
||||
job=found_job, renderer_info=renderer_info())
|
||||
return f'Cannot find job with ID {job_id}', 400
|
||||
|
||||
|
||||
@@ -111,7 +112,7 @@ def filtered_jobs_json(status_val):
|
||||
return f'Cannot find jobs with status {status_val}', 400
|
||||
|
||||
|
||||
@server.get('/api/job_status/<job_id>')
|
||||
@server.get('/api/job/<job_id>')
|
||||
def get_job_status(job_id):
|
||||
found_job = RenderQueue.job_with_id(job_id)
|
||||
if found_job:
|
||||
|
||||
@@ -47,6 +47,7 @@ class RenderJob:
|
||||
job_dict['status'] = self.render_status().value
|
||||
job_dict['time_elapsed'] = self.time_elapsed() if type(self.time_elapsed) != str else self.time_elapsed
|
||||
job_dict['file_hash'] = self.file_hash if self.file_hash and isinstance(self.file_hash, str) else self.file_hash()
|
||||
job_dict['percent_complete'] = self.percent_complete()
|
||||
job_dict['file_list'] = self.file_list()
|
||||
job_dict['worker'] = self.worker.__dict__.copy()
|
||||
job_dict['worker']['status'] = job_dict['status']
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
lib/static/images/desktop.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
BIN
lib/static/images/logo.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
71
lib/static/js/job_table.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const grid = new gridjs.Grid({
|
||||
columns: [
|
||||
{ data: (row) => row.id,
|
||||
name: 'Thumbnail',
|
||||
formatter: (cell) => gridjs.html(`<img src="/ui/job/${cell}/thumbnail" style='width: 320px; min-width: 240px;'>`),
|
||||
sort: {enabled: false}
|
||||
},
|
||||
{ id: 'name',
|
||||
name: 'Name',
|
||||
data: (row) => row.name,
|
||||
formatter: (name, row) => gridjs.html(`<a href="/ui/job/${row.cells[0].data}/full_details">${name}</a>`)
|
||||
},
|
||||
{ id: 'renderer', data: (row) => `${row.renderer}-${row.worker.renderer_version}`, name: 'Renderer' },
|
||||
{ id: 'priority', name: 'Priority' },
|
||||
{ id: 'status',
|
||||
name: 'Status',
|
||||
data: (row) => row,
|
||||
formatter: (cell, row) => gridjs.html(`
|
||||
<span class="tag is-primary is-light ${(cell.status == 'running') ? 'is-hidden': ''}">${cell.status}</span>
|
||||
<progress class="progress is-primary ${(cell.status != 'running') ? 'is-hidden': ''}"
|
||||
value="${(parseFloat(cell.percent_complete) * 100.0)}" max="100">${cell.status}</progress>
|
||||
`)},
|
||||
{ id: 'time_elapsed', name: 'Time Elapsed' },
|
||||
{ data: (row) => row.worker.total_frames, name: 'Frame Count' },
|
||||
{ id: 'client', name: 'Client'},
|
||||
{ data: (row) => row.worker.last_output,
|
||||
name: 'Last Output',
|
||||
formatter: (output, row) => gridjs.html(`<a href="/api/job/${row.cells[0].data}/logs">${output}</a>`)
|
||||
},
|
||||
{ data: (row) => row,
|
||||
name: 'Commands',
|
||||
formatter: (cell, row) => gridjs.html(`
|
||||
<div class="field has-addons" style='white-space: nowrap; display: inline-block;'>
|
||||
<button class="button is-info" onclick="window.location.href='/ui/job/${row.cells[0].data}/full_details';">
|
||||
<span class="icon"><i class="fa-solid fa-info"></i></span>
|
||||
</button>
|
||||
<button class="button is-link" onclick="window.location.href='/api/job/${row.cells[0].data}/logs';">
|
||||
<span class="icon"><i class="fa-regular fa-file-lines"></i></span>
|
||||
</button>
|
||||
<button class="button is-warning is-active ${(cell.status != 'running') ? 'is-hidden': ''}" onclick="window.location.href='/api/job/${row.cells[0].data}/cancel?confirm=True&redirect=True';">
|
||||
<span class="icon"><i class="fa-solid fa-x"></i></span>
|
||||
</button>
|
||||
<button class="button is-success ${(cell.status != 'completed') ? 'is-hidden': ''}" onclick="window.location.href='/api/job/${row.cells[0].data}/download_all';">
|
||||
<span class="icon"><i class="fa-solid fa-download"></i></span>
|
||||
<span>${cell.file_list.length}</span>
|
||||
</button>
|
||||
<button class="button is-danger" onclick="window.location.href='/api/job/${row.cells[0].data}/delete?confirm=True&redirect=True'">
|
||||
<span class="icon"><i class="fa-regular fa-trash-can"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
`),
|
||||
sort: false
|
||||
}
|
||||
],
|
||||
style: {
|
||||
table: {
|
||||
'white-space': 'nowrap'
|
||||
},
|
||||
th: {
|
||||
'vertical-align': 'middle',
|
||||
},
|
||||
td: {
|
||||
'vertical-align': 'middle',
|
||||
}
|
||||
},
|
||||
server: {
|
||||
url: '/api/jobs',
|
||||
then: results => results,
|
||||
},
|
||||
sort: true,
|
||||
}).render(document.getElementById('table'));
|
||||
@@ -1,52 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Zordon Dashboard</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
</head>
|
||||
<body>
|
||||
{% extends 'layout.html' %}
|
||||
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="#">
|
||||
Zordon Render Server - {{hostname}}
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
<div class="container table-container" style="text-align:center; width: 100%">
|
||||
{% block body %}
|
||||
<div class="container" style="text-align:center; width: 100%">
|
||||
<br>
|
||||
{% if media_url: %}
|
||||
<video width="1280" height="720" controls>
|
||||
<source src="{{media_url}}" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
{% elif job_status == 'Running': %}
|
||||
<div style="width: 100%; height: 720px; position: relative; background: black;">
|
||||
<img src="/static/gears.png" style="vertical-align: middle; width: auto; height: auto; position:absolute; margin: auto; top: 0;bottom: 0; left: 0; right: 0;">
|
||||
<span style="height: auto; position:absolute; margin: auto; top: 60%; left: 0; right: 0; color: white;">
|
||||
Rendering - {{ '{0:0.0f}'.format(job.percent_complete() * 100) }}%
|
||||
</span>
|
||||
<div style="width: 100%; height: 720px; position: relative; background: black; text-align: center; color: white;">
|
||||
<img src="/static/images/gears.png" style="vertical-align: middle; width: auto; height: auto; position:absolute; margin: auto; top: 0; bottom: 0; left: 0; right: 0;">
|
||||
<span style="height: auto; position:absolute; margin: auto; top: 58%; left: 0; right: 0; color: white; width: 60%">
|
||||
<progress class="progress is-primary" value="{{job.percent_complete() * 100}}" max="100" style="margin-top: 6px;" id="progress-bar">Rendering</progress>
|
||||
Rendering in Progress - <span id="percent-complete">{{(job.percent_complete() * 100) | int}}%</span>
|
||||
<br>Time Elapsed: <span id="time-elapsed">{{job.time_elapsed()}}</span>
|
||||
</span>
|
||||
<script>
|
||||
var startingStatus = '{{job.render_status().value}}';
|
||||
function update_job() {
|
||||
$.getJSON('/api/job/{{job.id}}', function(data) {
|
||||
document.getElementById('progress-bar').value = (data.percent_complete * 100);
|
||||
document.getElementById('percent-complete').innerHTML = (data.percent_complete * 100).toFixed(0) + '%';
|
||||
document.getElementById('time-elapsed').innerHTML = data.time_elapsed;
|
||||
if (data.status != startingStatus){
|
||||
clearInterval(renderingTimer);
|
||||
window.location.reload(true);
|
||||
};
|
||||
});
|
||||
}
|
||||
if (startingStatus == 'running'){
|
||||
var renderingTimer = setInterval(update_job, 2000);
|
||||
};
|
||||
</script>
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="width: 100%; height: 720px; position: relative; background: black;">
|
||||
<img src="/static/{{job_status}}.png" style="vertical-align: middle; width: auto; height: auto; position:absolute; margin: auto; top: 0;bottom: 0; left: 0; right: 0;">
|
||||
<span style="height: auto; position:absolute; margin: auto; top: 60%; left: 0; right: 0; color: white;">
|
||||
<img src="/static/images/{{job_status}}.png" style="vertical-align: middle; width: auto; height: auto; position:absolute; margin: auto; top: 0; bottom: 0; left: 0; right: 0;">
|
||||
<span style="height: auto; position:absolute; margin: auto; top: 58%; left: 0; right: 0; color: white;">
|
||||
{{job_status}}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<br>
|
||||
{{detail_table|safe}}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
@@ -1,296 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Zordon Dashboard</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<script src="https://kit.fontawesome.com/698705d14d.js" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename = 'modals.js') }}"></script>
|
||||
</head>
|
||||
<body onload="rendererChanged(document.getElementById('renderer'))">
|
||||
{% extends 'layout.html' %}
|
||||
|
||||
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<span class="navbar-item h1">
|
||||
Zordon Render Server - {{hostname}}
|
||||
</span>
|
||||
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<button class="button is-primary is-outlined js-modal-trigger" data-target="add-job-modal">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</span>
|
||||
<span>Submit Job</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="table-container">
|
||||
<table class="table is-bordered is-striped is-hoverable is-fullwidth is-narrow" style="text-align: center; vertical-align: middle;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: center;">Preview</th>
|
||||
<th style="text-align: center;">Name</th>
|
||||
<th style="text-align: center;">Renderer</th>
|
||||
<th style="text-align: center;">Priority</th>
|
||||
<th style="text-align: center;">Status</th>
|
||||
<th style="text-align: center;">Time Elapsed</th>
|
||||
<th style="text-align: center;">%</th>
|
||||
<th style="text-align: center;">Frame Count</th>
|
||||
<th style="text-align: center;">Client</th>
|
||||
<th style="text-align: center;">Last Output</th>
|
||||
<th style="text-align: center;">Commands</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<!-- <tfoot>-->
|
||||
<!-- <tr>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </tfoot>-->
|
||||
{% for job in all_jobs %}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding: 0; margin: 0;">
|
||||
<a href="/ui/job/{{job.id}}/full_details">
|
||||
<img src="/ui/job/{{job.id}}/thumbnail" width="300">
|
||||
</a>
|
||||
</td>
|
||||
<td><a href="/ui/job/{{job.id}}/full_details">{{job.name}}</a></td>
|
||||
<td>{{job.renderer}}-{{job.worker.renderer_version}}</td>
|
||||
<td>{{job.priority}}</td>
|
||||
<td>{{job.render_status().value}}</td>
|
||||
<td>{{job.time_elapsed()}}</td>
|
||||
<td>{{ '{0:0.0f}'.format(job.percent_complete() * 100) }}%</td>
|
||||
<td>{{job.frame_count()}}</td>
|
||||
<td>{{job.client}}</td>
|
||||
<td><a href="/api/job/{{job.id}}/logs">{{job.worker.last_output}}</a></td>
|
||||
<td>
|
||||
<div class="buttons are-small">
|
||||
<button class="button is-info" onclick="window.location.href='/ui/job/{{job.id}}/full_details';">
|
||||
<span class="icon"><i class="fa-solid fa-info"></i></span>
|
||||
</button>
|
||||
<button class="button is-link" onclick="window.location.href='/api/job/{{job.id}}/logs';">
|
||||
<span class="icon"><i class="fa-regular fa-file-lines"></i></span>
|
||||
</button>
|
||||
{% 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&redirect=True';">
|
||||
Cancel
|
||||
</button>
|
||||
{% elif job.render_status().value == 'completed': %}
|
||||
<button class="button is-success" onclick="window.location.href='/api/job/{{job.id}}/download_all';">
|
||||
<span class="icon"><i class="fa-solid fa-download"></i></span>
|
||||
<span>{{job.file_list() | length}}</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% block body %}
|
||||
<div class="container is-fluid" style="padding-top: 20px;">
|
||||
<div id="table" class="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 -->
|
||||
|
||||
<label class="label">Upload File</label>
|
||||
<div id="file-uploader" class="file has-name is-fullwidth">
|
||||
<label class="file-label">
|
||||
<input class="file-input is-small" 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;
|
||||
}
|
||||
}
|
||||
|
||||
const presets = {
|
||||
{% for preset in preset_list: %}
|
||||
{{preset}}: {
|
||||
name: '{{preset_list[preset]['name']}}',
|
||||
renderer: '{{preset_list[preset]['renderer']}}',
|
||||
args: '{{preset_list[preset]['args']}}',
|
||||
},
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
function rendererChanged(ddl1) {
|
||||
|
||||
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 %}
|
||||
};
|
||||
|
||||
var selectedRenderer = ddl1.value;
|
||||
|
||||
var ddl3 = document.getElementById('preset_list');
|
||||
ddl3.options.length = 0;
|
||||
createOption(ddl3, '-Presets-', '');
|
||||
for (var preset_name in presets) {
|
||||
if (presets[preset_name]['renderer'] == selectedRenderer) {
|
||||
createOption(ddl3, presets[preset_name]['name'], preset_name);
|
||||
};
|
||||
};
|
||||
document.getElementById('raw_args').value = "";
|
||||
|
||||
var ddl2 = document.getElementById('export_format');
|
||||
ddl2.options.length = 0;
|
||||
var options = renderers[selectedRenderer];
|
||||
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);
|
||||
}
|
||||
|
||||
function addPresetTextToInput(presetfield, textfield) {
|
||||
var p = presets[presetfield.value];
|
||||
textfield.value = p['args'];
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Renderer & Priority -->
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<label class="label">Renderer</label>
|
||||
<span class="select">
|
||||
<select id="renderer" name="renderer" onchange="rendererChanged(this)">
|
||||
{% for renderer in renderer_info: %}
|
||||
<option name="renderer" value="{{renderer}}">{{renderer}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p class="control">
|
||||
<label class="label">Client</label>
|
||||
<span class="select">
|
||||
<select name="client">
|
||||
<option name="client" value="">First Available</option>
|
||||
{% for client in render_clients: %}
|
||||
<option name="client" value="{{client}}">{{client}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p class="control">
|
||||
<label class="label">Priority</label>
|
||||
<span class="select">
|
||||
<select name="priority">
|
||||
<option name="priority" value="1">1</option>
|
||||
<option name="priority" value="2" selected="selected">2</option>
|
||||
<option name="priority" value="3">3</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Output Path -->
|
||||
<label class="label">Output</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<input class="input is-small" type="text" placeholder="Output Name" name="output_path">
|
||||
</div>
|
||||
<p class="control">
|
||||
<span class="select is-small">
|
||||
<select id="export_format" name="export_format">
|
||||
<option value="ar">option</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Resolution -->
|
||||
<!-- <label class="label">Resolution</label>-->
|
||||
<!-- <div class="field is-grouped">-->
|
||||
<!-- <p class="control">-->
|
||||
<!-- <input class="input" type="text" placeholder="auto" maxlength="5" size="8" name="AnyRenderer-arg_x_resolution">-->
|
||||
<!-- </p>-->
|
||||
<!-- <label class="label"> x </label>-->
|
||||
<!-- <p class="control">-->
|
||||
<!-- <input class="input" type="text" placeholder="auto" maxlength="5" size="8" name="AnyRenderer-arg_y_resolution">-->
|
||||
<!-- </p>-->
|
||||
<!-- <label class="label"> @ </label>-->
|
||||
<!-- <p class="control">-->
|
||||
<!-- <input class="input" type="text" placeholder="auto" maxlength="3" size="5" name="AnyRenderer-arg_frame_rate">-->
|
||||
<!-- </p>-->
|
||||
<!-- <label class="label"> fps </label>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<label class="label">Command Line Arguments</label>
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<span class="select is-small">
|
||||
<select id="preset_list" onchange="addPresetTextToInput(this, document.getElementById('raw_args'))">
|
||||
<option value="preset-placeholder">presets</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p class="control is-expanded">
|
||||
<input class="input is-small" type="text" placeholder="Args" id="raw_args" name="raw_args">
|
||||
</p>
|
||||
</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>
|
||||
</html>
|
||||
<script src="/static/js/job_table.js"></script>
|
||||
{% endblock %}
|
||||
236
templates/layout.html
Normal file
@@ -0,0 +1,236 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Zordon Dashboard</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gridjs/dist/gridjs.umd.js"></script>
|
||||
<link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet" />
|
||||
<script src="https://kit.fontawesome.com/698705d14d.js" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="/static/js/modals.js"></script>
|
||||
</head>
|
||||
<body onload="rendererChanged(document.getElementById('renderer'))">
|
||||
|
||||
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/">
|
||||
<img src="/static/images/logo.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/">
|
||||
Home
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<button class="button is-primary js-modal-trigger" data-target="add-job-modal">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-upload"></i>
|
||||
</span>
|
||||
<span>Submit Job</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
|
||||
<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 -->
|
||||
|
||||
<label class="label">Upload File</label>
|
||||
<div id="file-uploader" class="file has-name is-fullwidth">
|
||||
<label class="file-label">
|
||||
<input class="file-input is-small" 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;
|
||||
}
|
||||
}
|
||||
|
||||
const presets = {
|
||||
{% for preset in preset_list: %}
|
||||
{{preset}}: {
|
||||
name: '{{preset_list[preset]['name']}}',
|
||||
renderer: '{{preset_list[preset]['renderer']}}',
|
||||
args: '{{preset_list[preset]['args']}}',
|
||||
},
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
function rendererChanged(ddl1) {
|
||||
|
||||
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 %}
|
||||
};
|
||||
|
||||
var selectedRenderer = ddl1.value;
|
||||
|
||||
var ddl3 = document.getElementById('preset_list');
|
||||
ddl3.options.length = 0;
|
||||
createOption(ddl3, '-Presets-', '');
|
||||
for (var preset_name in presets) {
|
||||
if (presets[preset_name]['renderer'] == selectedRenderer) {
|
||||
createOption(ddl3, presets[preset_name]['name'], preset_name);
|
||||
};
|
||||
};
|
||||
document.getElementById('raw_args').value = "";
|
||||
|
||||
var ddl2 = document.getElementById('export_format');
|
||||
ddl2.options.length = 0;
|
||||
var options = renderers[selectedRenderer];
|
||||
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);
|
||||
}
|
||||
|
||||
function addPresetTextToInput(presetfield, textfield) {
|
||||
var p = presets[presetfield.value];
|
||||
textfield.value = p['args'];
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Renderer & Priority -->
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<label class="label">Renderer</label>
|
||||
<span class="select">
|
||||
<select id="renderer" name="renderer" onchange="rendererChanged(this)">
|
||||
{% for renderer in renderer_info: %}
|
||||
<option name="renderer" value="{{renderer}}">{{renderer}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p class="control">
|
||||
<label class="label">Client</label>
|
||||
<span class="select">
|
||||
<select name="client">
|
||||
<option name="client" value="">First Available</option>
|
||||
{% for client in render_clients: %}
|
||||
<option name="client" value="{{client}}">{{client}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p class="control">
|
||||
<label class="label">Priority</label>
|
||||
<span class="select">
|
||||
<select name="priority">
|
||||
<option name="priority" value="1">1</option>
|
||||
<option name="priority" value="2" selected="selected">2</option>
|
||||
<option name="priority" value="3">3</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Output Path -->
|
||||
<label class="label">Output</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<input class="input is-small" type="text" placeholder="Output Name" name="output_path" value="output.mp4">
|
||||
</div>
|
||||
<p class="control">
|
||||
<span class="select is-small">
|
||||
<select id="export_format" name="export_format">
|
||||
<option value="ar">option</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Resolution -->
|
||||
<!-- <label class="label">Resolution</label>-->
|
||||
<!-- <div class="field is-grouped">-->
|
||||
<!-- <p class="control">-->
|
||||
<!-- <input class="input" type="text" placeholder="auto" maxlength="5" size="8" name="AnyRenderer-arg_x_resolution">-->
|
||||
<!-- </p>-->
|
||||
<!-- <label class="label"> x </label>-->
|
||||
<!-- <p class="control">-->
|
||||
<!-- <input class="input" type="text" placeholder="auto" maxlength="5" size="8" name="AnyRenderer-arg_y_resolution">-->
|
||||
<!-- </p>-->
|
||||
<!-- <label class="label"> @ </label>-->
|
||||
<!-- <p class="control">-->
|
||||
<!-- <input class="input" type="text" placeholder="auto" maxlength="3" size="5" name="AnyRenderer-arg_frame_rate">-->
|
||||
<!-- </p>-->
|
||||
<!-- <label class="label"> fps </label>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<label class="label">Command Line Arguments</label>
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<span class="select is-small">
|
||||
<select id="preset_list" onchange="addPresetTextToInput(this, document.getElementById('raw_args'))">
|
||||
<option value="preset-placeholder">presets</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p class="control is-expanded">
|
||||
<input class="input is-small" type="text" placeholder="Args" id="raw_args" name="raw_args">
|
||||
</p>
|
||||
</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>
|
||||
</html>
|
||||