mirror of
https://github.com/linuxserver/docker-ffmpeg.git
synced 2026-02-20 04:56:23 +08:00
stashing work, added command example page and functional config loading and saving, skeleton for background job processing
This commit is contained in:
parent
f77d46b66a
commit
ceb78ac245
29
commands.yml
29
commands.yml
@ -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"
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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">×</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
2
public/vendor/css/xterm.min.css
vendored
Normal 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
1
public/vendor/js/xterm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
77
server.py
77
server.py
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user