stashing work, added command example page and functional config loading and saving, skeleton for background job processing

This commit is contained in:
thelamer 2019-07-06 05:25:57 +00:00
parent f77d46b66a
commit ceb78ac245
7 changed files with 322 additions and 46 deletions

View File

@ -1,6 +1,7 @@
---
- category: "H.264 Downsampling"
commands:
- name: "720p 4mpbs"
description: "This can be used as a catch all for most H.264 720p encodes, if you want a higher bitrate then modify the 4000k to your suit your needs"
command: |
@ -10,7 +11,35 @@
-c:a copy \
-b:v 4000k \
"${OUTPUTFILE}"
- name: "1080p 8mpbs"
description: "This can be used as a catch all for most H.264 1080p encodes, if you want a higher bitrate then modify the 8000k to your suit your needs"
command: |
ffmpeg -i "${INPUTFILE}" \
-vf scale=-1:1080 \
-c:v h264 \
-c:a copy \
-b:v 8000k \
"${OUTPUTFILE}"
- category: "H.265 Downsampling"
commands:
- name: "720p 2mpbs"
description: "This can be used as a catch all for most H.265 720p encodes, if you want a higher bitrate then modify the 2000k to your suit your needs"
command: |
ffmpeg -i "${INPUTFILE}" \
-vf scale=-1:720 \
-c:v h264 \
-c:a hevc \
-b:v 2000k \
"${OUTPUTFILE}"
- name: "1080p 4mpbs"
description: "This can be used as a catch all for most H.265 1080p encodes, if you want a higher bitrate then modify the 4000k to your suit your needs"
command: |
ffmpeg -i "${INPUTFILE}" \
-vf scale=-1:1080 \
-c:v hevc \
-c:a copy \
-b:v 4000k \
"${OUTPUTFILE}"
- category: "H.264 Downsampling Vaapi"
- category: "H.265 Downsampling Vaapi"
- category: "H.264 Downsampling Nvidia"

View File

@ -5,6 +5,7 @@ interval: 60
commands:
- name: "test1"
extension: ".mkv"
delete: false
command: |
ffmpeg -i "${INPUTFILE}" \
-vf scale=-1:720 \
@ -14,6 +15,7 @@ commands:
"${OUTPUTFILE}"
- name: "test2"
extension: ".mkv"
delete: true
command: |
ffmpeg -i "${INPUTFILE}" \
-vf scale=-1:720 \

View File

@ -4,78 +4,240 @@ var port = window.location.port;
var protocol = window.location.protocol;
var socket = io.connect(protocol + '//' + host + ':' + port, {});
// On connect render the main page
$(document).ready(function(){rendermain()})
//// Main Page rendering ////
function rendermain(){
$('#pagecontent').empty();
pagepurge();
$('#pagecontent').append('<center><div class="spinner-border" style="width: 6rem; height: 6rem;"></div></center>');
socket.emit('getmain');
}
socket.on('sendmain', function(rules){
$('#pagecontent').empty();
socket.on('sendmain', function(data){
pagepurge();
$('#pagecontent').append('<center>Processed jobs here and current status</center>');
});
//// Config Page rendering ////
function renderconfig(){
pagepurge();
$('#pagecontent').append('<center><div class="spinner-border" style="width: 6rem; height: 6rem;"></div></center>');
socket.emit('getconfig');
}
socket.on('sendconfig', function(rules){
pagepurge();
var interval = rules.interval;
$('#headerform').append('\
<select class="custom-select form-control mr-sm-2" id="interval">\
<option selected value="' + interval + '">' + interval + '</option>\
<option value"Never">Never</option>\
<option value="30">30 seconds</option>\
<option value="60">60 seconds</option>\
<option value="300">5 minutes</option>\
</select>\
<button style="cursor:pointer;" onclick="saveall()" class="btn btn-primary my-2 my-sm-0" type="submit">Save Config</button>\
');
var editor = [];
$(rules.commands).each(function(i,data){
var name = data.name;
var extension = data.extension;
var command = data.command;
var cleanup = data.delete;
if (cleanup == true){
var checkbox = '<input class="form-check-input" type="checkbox" id="delete' + i + '" checked>'
}
else{
var checkbox = '<input class="form-check-input" type="checkbox" id="delete' + i + '">'
}
$('#pagecontent').append('\
<div class="card" id="' + name + '">\
<div class="card rule" id="' + i + '">\
<div class="card-body">\
<form>\
<div class="form-row align-items-center">\
<div class="col-auto">\
<input type="text" class="form-control" value="' + name + '" placeholder="File Extension">\
</div>\
<div class="col-auto">\
<input type="text" class="form-control" value="' + extension + '" placeholder="File Extension">\
</div>\
<div class="col-auto">\
<div class="form-check mb-2">\
<input class="form-check-input" type="checkbox" id="deletesource">\
<label class="form-check-label" for="deletesource">\
Delete Source\
</label>\
</div>\
</div>\
<div class="col float-right">\
<button class="btn btn-success mb-2 float-right">Run Single</button>\
<div class="form-row align-items-center">\
<div class="col-auto">\
<input type="text" class="form-control" value="' + name + '" placeholder="Rule Name" id="name' + i + '">\
</div>\
<div class="col-auto">\
<input type="text" class="form-control" value="' + extension + '" placeholder="File Extension" id="extension' + i + '">\
</div>\
<div class="col-auto">\
<div class="form-check mb-2">\
'+ checkbox +'\
<label class="form-check-label" for="delete' + i + '">\
Delete Source\
</label>\
</div>\
</div>\
</form>\
</div>\
<br>\
<div id="editor' + i + '" style="height: 250px; width: 100%"></div>\
</div>\
</div>\
<br>\
').promise().done(function(){
editor[i] = ace.edit("editor" + i);
editor[i].setTheme("ace/theme/chrome");
editor[i].session.setMode("ace/mode/sh");
editor[i] = ace.edit('editor' + i);
editor[i].setTheme('ace/theme/chrome');
editor[i].session.setMode('ace/mode/sh');
editor[i].$blockScrolling = Infinity;
editor[i].setOptions({
readOnly: false,
});
editor[i].setValue(command, -1);
});
}).promise().done(function(){
$('#pagecontent').append('<center><button type="button" class="btn btn-secondary btn-lg">+</button></center>');
});
$('#pagefooter').append('<center><button type="button" class="btn btn-secondary btn-lg" onclick="addconfig()">+</button></center>');
});
// Add config rule slots to the page
function addconfig(){
var rand = Math.random().toString(36).replace('0.', '');
$('#pagecontent').append('\
<div class="card rule" id="' + rand + '">\
<div class="card-body">\
<div class="form-row align-items-center">\
<div class="col-auto">\
<input type="text" class="form-control" placeholder="Rule Name" id="name' + rand + '">\
</div>\
<div class="col-auto">\
<input type="text" class="form-control" placeholder="File Extension" id="extension' + rand + '">\
</div>\
<div class="col-auto">\
<div class="form-check mb-2">\
<input class="form-check-input" type="checkbox" id="delete' + rand + '">\
<label class="form-check-label" for="delete' + rand + '">\
Delete Source\
</label>\
</div>\
</div>\
</div>\
<br>\
<div id="editor' + rand + '" style="height: 250px; width: 100%"></div>\
</div>\
</div>\
<br>\
').promise().done(function(){
editor = ace.edit('editor' + rand);
editor.setTheme('ace/theme/chrome');
editor.session.setMode('ace/mode/sh');
editor.$blockScrolling = Infinity;
editor.setOptions({
readOnly: false,
});
});
}
// Save all config data
function saveall(){
var interval = $('#interval').val();
var dataset = {'interval':interval,'commands':[]};
var ids = $('.rule').map(function(){
return this.id;
}).get();
$(ids).each(function(i,id){
var name = $('#name' + id).val();
var extension = $('#extension' + id).val();
var cleanup = $('#delete' + id).prop('checked');
var editor = ace.edit("editor" + id);
var command = editor.getValue();
var single_command = {'name':name,'extension':extension,'delete':cleanup,'command':command};
dataset.commands.push(single_command);
}).promise().done(function(){
socket.emit('saveconfig',dataset);
});
}
//// Terminal Page rendering ////
function renderterminal(){
pagepurge();
$('#pagecontent').append('<center><div class="spinner-border" style="width: 6rem; height: 6rem;"></div></center>');
socket.emit('getterminal');
}
socket.on('sendterminal', function(data){
pagepurge();
$('#pagecontent').append('<center>Raw bash terminal here for users to test commands</center>');
});
///////////////////////// test function client side //////////////////////////////////////
socket.on('testout', function(out){
console.log(out);
});
///////////////////////// REMOVE FROM RELEASE ////////////////////////////////////////////
//// Command Page rendering ////
function rendercommands(){
$('#pagecontent').empty();
pagepurge();
$('#pagecontent').append('<center><div class="spinner-border" style="width: 6rem; height: 6rem;"></div></center>');
socket.emit('getcommands');
}
socket.on('sendcommands', function(categories){
console.log(categories);
$('#pagecontent').empty();
pagepurge();
var editor = [];
$('#pagecontent').append('<div id="cats"></div>')
$(categories).each(function(i,data){
var commands = data.commands;
var cat = data.category;
var catid = cat.replace(/ |\./g,'');
$('#cats').append('\
<div class="card">\
<div class="card-header" id="' + catid + '" data-toggle="collapse" data-target="#' + catid + '_list" aria-expanded="true" aria-controls="' + catid + '_list">\
' + cat + '\
</div>\
</div>');
$('#cats').append('\
<div id="' + catid + '_list" class="collapse" aria-labelledby="' + catid + '" data-parent="#cats">\
<div class="card-body btn-toolbar" id="' + catid + '_commands">\
</div>\
</div>').promise().done(
function(){
$(commands).each(function(i,data){
var name = data.name;
var nameid = name.replace(/ |\./g,'');
var description = btoa(data.description);
var command = btoa(data.command);
$('#' + catid + '_commands').append('\
<button type="button" \
id="' + nameid + '_button" \
data-toggle="modal" \
data-target="#modal" \
style="cursor:pointer;" \
onclick="commandmodal(\'' + nameid + '\')"\
class="btn btn-secondary btn-lg mx-auto" \
data-name="' + name + '"\
data-description="' + description + '"\
data-command="' + command + '"\
>' + name + '\
</button>');
});
});
});
});
function commandmodal(nameid){
modalpurge();
var name = $('#' + nameid + '_button').data('name')
var description = atob($('#' + nameid + '_button').data('description'));
var command = atob($('#' + nameid + '_button').data('command'));
$('#modaltitle').append(name);
$('#modalbody').append('<p>' + description + '</p>');
$('#modalbody').append('<div id="editor" style="height: 250px; width: 100%"></div>'
).promise().done(function(){
editor = ace.edit("editor");
editor.setTheme("ace/theme/chrome");
editor.session.setMode("ace/mode/sh");
editor.$blockScrolling = Infinity;
editor.setOptions({
readOnly: true,
});
editor.setValue(command, -1);
});
}
function modalpurge(){
$('#modaltitle').empty();
$('#modalbody').empty();
}
function pagepurge(){
$('#headerform').empty();
$('#pagecontent').empty();
$('#pagefooter').empty();
}

View File

@ -23,6 +23,7 @@
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/public/img/favicon/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<link href="public/vendor/css/xterm.min.css" rel="stylesheet">
<link href="public/vendor/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
@ -37,24 +38,37 @@
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<div class="nav-link" style="cursor:pointer;" onclick="renderconfig()" >Config</div>
</li>
<li class="nav-item active">
<div class="nav-link" style="cursor:pointer;" onclick="renderterminal()" >Terminal</div>
</li>
<li class="nav-item active">
<div class="nav-link" style="cursor:pointer;" onclick="rendercommands()" >Example Commands</div>
</li>
</ul>
<form class="form-inline my-2 my-md-0" onsubmit="return false;">
<select class="custom-select form-control mr-sm-2" id="interval">
<option selected>Never</option>
<option value="30">30 seconds</option>
<option value="60">60 seconds</option>
<option value="300">5 minutes</option>
</select>
<button style="cursor:pointer;" onclick="saveall()" class="btn btn-primary my-2 my-sm-0" type="submit">Save Config</button>
<form class="form-inline my-2 my-md-0" onsubmit="return false;" id="headerform">
</form>
</div>
</nav>
<br>
<!-- Body -->
<div class="container" id="pagecontent">
<div class="container" id="pagecontent"></div>
<div class="container" id="pagefooter"></div>
<!--FFmpeg Modal-->
<div id="modal" class="modal fade" tabindex="-1" role="dialog" style="overflow-y:scroll;">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"><div id="modaltitle"></div></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body" style="overflow-x:auto">
<div id="modalbody"></div>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript -->
<script src="/public/vendor/js/jquery.min.js"></script>
@ -63,6 +77,7 @@
<!-- Plugin JavaScript -->
<script src="/public/vendor/js/socket.io.js"></script>
<script src="/public/vendor/js/ace.js"></script>
<script src="/public/vendor/js/xterm.min.js"></script>
<!-- Our JavaScript -->
<script src="/public/ffmpeg-web.js"></script>
</body>

2
public/vendor/css/xterm.min.css vendored Normal file
View File

@ -0,0 +1,2 @@
.xterm{font-feature-settings:"liga" 0;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:0}.xterm .xterm-helpers{position:absolute;top:0;z-index:10}.xterm .xterm-helper-textarea{position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-10;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm{cursor:text}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:100;color:transparent}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline{text-decoration:underline}
/*# sourceMappingURL=xterm.min.css.map */

1
public/vendor/js/xterm.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -2,31 +2,65 @@ from aiohttp import web
import socketio
import yaml
import os
import glob
import re
import time
sio = socketio.AsyncServer()
# Websocket server
sio = socketio.AsyncServer(async_mode='aiohttp')
app = web.Application()
sio.attach(app)
################################
# Job functions #
################################
# Build and return an array of stuff to process
def build_list(extension):
# Sanitize exenstion
extension_regex = re.sub(r'([A-Za-z])', lambda m: '[' + m.group(1).upper() + m.group(1).lower() + ']', extension)
# Build full glob
all_files = glob.glob('/in/**/*' + extension_regex, recursive=True)
# Check if anything in this array has a processed log or is not a file and pull it out
for file in all_files:
if not os.path.isfile(file) or os.path.isfile(file + '.ffmpeg_log'):
all_files.remove(file)
return all_files
# Background job thread loop for file processing
async def processor():
while True:
files = build_list('.mkv')
await sio.emit('testoutt', files)
await sio.sleep(5)
sio.start_background_task(processor)
################################
# Web Server #
################################
# Default returns for static files and index root
async def index(request):
with open('./public/index.html') as f:
return web.Response(text=f.read(), content_type='text/html')
app.router.add_get('/', index)
app.router.add_static('/public/', path=str('./public/'))
# Send the current config to the user to render
@sio.on('getmain')
async def message(sid):
@sio.on('getconfig')
async def config(sid):
with open("./config.yml", 'r') as stream:
try:
config = yaml.safe_load(stream)
await sio.emit('sendmain', config, room=sid)
await sio.emit('sendconfig', config, room=sid)
except yaml.YAMLError as e:
print(e)
# Send the current command examples from github to the user to render
@sio.on('getcommands')
async def message(sid):
async def commands(sid):
with open("./commands.yml", 'r') as stream:
try:
commands = yaml.safe_load(stream)
@ -34,5 +68,36 @@ async def message(sid):
except yaml.YAMLError as e:
print(e)
# Main page for rendering processing history and current
@sio.on('getmain')
async def commands(sid):
data = 'test'
await sio.emit('sendmain', data, room=sid)
# Save user set config
@sio.on('saveconfig')
async def commands(sid, data):
with open("/config/config.yml", 'w') as configfile:
try:
yaml.dump(data, configfile)
except yaml.YAMLError as e:
print(e)
################################
# User Terminal #
################################
# Terminal page rendering
@sio.on('getterminal')
async def commands(sid):
data = 'test'
await sio.emit('sendterminal', data, room=sid)
################################
# App Run #
################################
if __name__ == '__main__':
web.run_app(app, port=8787)