Sync queue management UI, sync/reboot/poweroff all btns
This commit is contained in:
parent
a029b2e902
commit
e44ff71a58
10
app.py
10
app.py
|
@ -38,6 +38,9 @@ gui_l10n = {"locale": LOCALE[:2],
|
|||
"str_sync": _("Sync"),
|
||||
"str_poweroff": _("Poweroff"),
|
||||
"str_reboot": _("Reboot"),
|
||||
# TODO : add to l10n
|
||||
"str_poweroff_all": _("Poweroff all"),
|
||||
"str_reboot_all": _("Reboot all"),
|
||||
"str_blink": _("Blink"),
|
||||
"str_link": _("Link"),
|
||||
"str_refresh": _("Refresh"),
|
||||
|
@ -631,11 +634,8 @@ def sync(host, arg0):
|
|||
if arg0 == "stop":
|
||||
return stop_upload()
|
||||
elif host == "all":
|
||||
reset_current_upload()
|
||||
return sync_media_folder(media_folder_local, media_folder_remote_expanded, host, cmd_port)
|
||||
# TODO : figure it out for multiple hosts
|
||||
# ~ for hostl in hosts_available:
|
||||
# ~ sync_media_folder(media_folder_local, media_folder_remote_expanded, hostl, cmd_port)
|
||||
# This is done in js for now.
|
||||
pass
|
||||
else:
|
||||
reset_current_upload()
|
||||
return sync_media_folder(media_folder_local, media_folder_remote_expanded, host, cmd_port)
|
||||
|
|
|
@ -28,7 +28,7 @@ sha256 : 0fe3fe76d0e56e445124fa20646fa8b3d8c59568786b3ebc8a96d83d92f203e3
|
|||
* Use nginx reverse proxy + SSL between server and clients ( https://medium.com/@antelle/how-to-generate-a-self-signed-ssl-certificate-for-an-ip-address-f0dd8dddf754 )
|
||||
* Webgui beautifying
|
||||
|
||||
## 0.4 : 2022-10-21-videopi.img.xz
|
||||
## 0.4 : 2022-10-21-pilpil.img.xz
|
||||
md5 : dee7af70135994169cab4f073ee51905
|
||||
sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
||||
|
||||
|
@ -38,7 +38,7 @@ sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
|||
* Add media folder sync (scp, rsync, http upload)
|
||||
* Install script ; Wifi setup, generate/install SSH keys/ nginx SSL cert/key fore each host, change hostname, static IPs
|
||||
|
||||
## 0.5 : 2022-11-17-videopi.img.xz
|
||||
## 0.5 : 2022-11-17-pilpil.img.xz
|
||||
md5 : d1dd7404ec05d16c8a4179370a214821
|
||||
sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
||||
|
||||
|
@ -55,6 +55,31 @@ sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
|||
* FR localisation for server, client and install script
|
||||
* pep8ification
|
||||
|
||||
## 0.6 : 2023-01-06-pilpil.img.xz
|
||||
md5 :
|
||||
sha256 :
|
||||
|
||||
* os update, cleanup
|
||||
* server: upload in chunks
|
||||
* server: file upload : only send new files, get upload status (/sync/status)
|
||||
* server: pep8fied (except for line length)
|
||||
* server: better overclock values for RPIs
|
||||
* server: VLC file caching set to 5000ms for seamless playback
|
||||
* server: BCM4345 workaround to solve wifi connectivity (https://wiki.arthus.net/?Rpi_wifi_-_BCM4345_and_CTRL-EVENT-ASSOC-REJECT)
|
||||
* webgui: remove file from timeline (drag&drop to bin)
|
||||
* webgui: upload progress dialog
|
||||
* webgui: l10n html + js
|
||||
* webgui: Add remote file enqueuing
|
||||
* webgui: Fix timeline UI
|
||||
* webgui: Add video thumbnails to timeline UI
|
||||
* webgui: fix timeline/file sync with multiple clients
|
||||
* webgui: file sync queuing UI
|
||||
* webgui: sync all live hosts button
|
||||
* webgui: Add remove from sync queue button
|
||||
* webgui: Poweroff/Reboot all btn
|
||||
* client: Only blink when running on rpi
|
||||
* client: Generate video thumbnails on startup/upload with ffmpeg, serve SVG when file not found, create folder if needed
|
||||
|
||||
|
||||
|
||||
# FS checklist
|
||||
|
@ -70,32 +95,27 @@ sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
|||
|
||||
|
||||
# DOING NEXT :
|
||||
* server : remove incomplete files before upload
|
||||
* webgui/server : interrupt current download ?
|
||||
* webgui : upload status CSS transition ?
|
||||
* FIXME : playhead stuck after removing item from playlist
|
||||
|
||||
# DONE :
|
||||
|
||||
* server : upload in chunks
|
||||
* webgui: remove file from timeline (drag&drop to bin?) (check bugs)
|
||||
* webgui: upload progress dialog
|
||||
* server : file upload : only send new files, get upload status (/sync/status)
|
||||
* webgui: l10n html + js
|
||||
* webgui: Add remote file enqueuing
|
||||
* webgui: Fix timeline UI
|
||||
* webgui: Add video thumbnails to timeline UI
|
||||
* client: Only blink when running on rpi
|
||||
* client: Generate video thumbnail on start with ffmpegthumbnailer, serve SVG when file not found
|
||||
* server: pep8fied (except for line length)
|
||||
|
||||
|
||||
# TODO :
|
||||
* ~ Test with several rpis
|
||||
* ? Scripts hotspot linux : nmcli
|
||||
win : https://github.com/JamesCullum/Windows-Hotspot
|
||||
mac : https://apple.stackexchange.com/questions/2488/start-stop-internet-sharing-from-a-script
|
||||
* check control from wifi client ( smartphone )
|
||||
* playlist save/load ?
|
||||
* webgui/server : interrupt current download ?
|
||||
* server : remove incomplete uploaded files before new upload ?
|
||||
* webgui : upload status CSS transition ?
|
||||
* ! Remove git personal details/resolv.conf, remove authorized_keys, ssh config, clean home, re-enable ssh pw login
|
||||
* ~ Doc
|
||||
|
||||
# OTHER:
|
||||
* ? Scripts hotspot
|
||||
linux : nmcli
|
||||
win : https://github.com/JamesCullum/Windows-Hotspot
|
||||
mac : https://apple.stackexchange.com/questions/2488/start-stop-internet-sharing-from-a-script
|
||||
* get_client_rssi.sh on server
|
||||
|
||||
# À FINANCER
|
||||
* Shutters/motor control
|
||||
* XLR support
|
||||
* UDEV rules : keyboard/mouse = standalone GUI
|
100
static/script.js
100
static/script.js
|
@ -435,6 +435,8 @@ function update_host_list(infos_array){
|
|||
host_down.forEach(function(element){
|
||||
document.getElementById(element).style.display = 'none';
|
||||
});
|
||||
currentUser.hosts_up = host_up;
|
||||
currentUser.hosts_down = host_down;
|
||||
currentUser.status_all = host_up.length + " client(s).";
|
||||
return host_up.length;
|
||||
}
|
||||
|
@ -449,10 +451,10 @@ function freeze_ui_host(host, html_attributes, inner_html=0, inner_text=0){
|
|||
let upload_dialog_cont_element = add_HTML_element("div", html_attributes, 0, host);
|
||||
container_element.insertBefore(upload_dialog_cont_element, siblings[0]);
|
||||
if (inner_html){
|
||||
let child_node = add_HTML_element("div", inner_html, 0, host)
|
||||
let child_node = add_HTML_element("div", inner_html, 0, host);
|
||||
let new_node = container_element.children.item(html_attributes.id + host).appendChild(child_node);
|
||||
if (inner_text){
|
||||
new_node.innerText = inner_text;
|
||||
new_node.innerHTML = inner_text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -461,17 +463,43 @@ function freeze_queued_container(command){
|
|||
freeze_attributes = {"id":"ul_queued_freeze_", "class": "ul_queued_freeze"};
|
||||
inner_html = {"id":"ul_queued_msg_", "class": "ul_queued_msg"};
|
||||
inner_text = t9n[LOCALE].item_queued;
|
||||
freeze_ui_host(command.split("/")[2], freeze_attributes, inner_html, inner_text);
|
||||
let host = command.split("/")[2];
|
||||
inner_text += '<buttton id="sync_unqueue_' + host + '" onclick="cancel_sync(this)" class="command btn btn-block btn-lg btn-default btn-unqueue" role="button">X</button';
|
||||
freeze_ui_host(host, freeze_attributes, inner_html, inner_text);
|
||||
}
|
||||
|
||||
function unfreeze_queued_container(host){
|
||||
let container = document.getElementById(host);
|
||||
let exists = container.querySelector("div.ul_queued_freeze");
|
||||
exists? exists.remove(): false;
|
||||
if (exists){
|
||||
exists.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function unfreeze_all_containers(){
|
||||
if (currentUser.hosts_up){
|
||||
currentUser.hosts_up.forEach(function(host){
|
||||
unfreeze_queued_container(host);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function exec_all_hosts(command){
|
||||
let authorized_commands = ['reboot', 'poweroff'];
|
||||
// Only send authorized commands
|
||||
if (authorized_commands.indexOf(command) == -1 ){
|
||||
return 0;
|
||||
}
|
||||
if ( confirm(t9n[LOCALE].confirmMessage) ) {
|
||||
if (currentUser.hosts_up){
|
||||
currentUser.hosts_up.forEach(function(host){
|
||||
send_ajax_cmd("/" + host + "/" + command);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function display_upload_status(command) {
|
||||
// TODO : First this should set the HTMl with empty values, then call update_upload...
|
||||
let host = command.split("/")[2];
|
||||
let container_element = document.getElementById(host);
|
||||
let siblings = container_element.children;
|
||||
|
@ -534,6 +562,7 @@ async function update_upload_status(current_upload) {
|
|||
// Upload finished
|
||||
console.log("Upload finished - Destroying dialog for " + currentUser.last_ul_host);
|
||||
destroy_upload_status(currentUser.last_ul_host);
|
||||
|
||||
// Remove first item from command queue
|
||||
currentUser.ul_queue.shift();
|
||||
// Upload done, un-freeze
|
||||
|
@ -553,6 +582,7 @@ async function update_upload_status(current_upload) {
|
|||
document.getElementById("ul_status_filename_" + currentUser.last_ul_host).innerHTML = t9n[LOCALE].filename + ": " + current_upload.filename;
|
||||
document.getElementById("ul_status_filesize_" + currentUser.last_ul_host).innerHTML = t9n[LOCALE].size + ": " + current_upload.size + t9n[LOCALE].size_unit;
|
||||
// Progress bar CSS
|
||||
// FIXME : cursor stops updating after upload ?
|
||||
if (current_upload.transferred_percent) {
|
||||
document.getElementById("ul_progress_" + currentUser.last_ul_host).innerText = current_upload.transferred_percent + "%";
|
||||
document.getElementById("ul_progress_" + currentUser.last_ul_host).style.background = "linear-gradient(90deg, " + TIMELINE_COLOR_CURSOR + " " + current_upload.transferred_percent + "%, #fff " + current_upload.transferred_percent + "%)";
|
||||
|
@ -613,12 +643,7 @@ function parse_result(command, infos_array) {
|
|||
//~ console.log("stopping upload...");
|
||||
//~ console.log(infos_array);
|
||||
//~ destroy_upload_status();
|
||||
} //else {
|
||||
//~ console.log("displaying status");
|
||||
//~ console.log(infos_array);
|
||||
//~ if (infos_array.total_count) {
|
||||
//~ display_upload_status(infos_array)
|
||||
//}
|
||||
}
|
||||
} else {
|
||||
setTimeout(send_ajax_cmd, 180, "/all/list");
|
||||
setTimeout(send_ajax_cmd, 180, "/all/status");
|
||||
|
@ -685,7 +710,6 @@ function sync_host() {
|
|||
// Update status
|
||||
currentUser.status_all = t9n[LOCALE].sync;
|
||||
// Create dialog
|
||||
// TODO : display grayed out dialog when host is queued for upload
|
||||
display_upload_status(command_q);
|
||||
// Set current host
|
||||
currentUser.last_ul_host = host;
|
||||
|
@ -695,6 +719,7 @@ function sync_host() {
|
|||
} else {
|
||||
console.log("Nothing else in the queue !");
|
||||
// Make sure sane values are set back
|
||||
unfreeze_all_containers();
|
||||
currentUser.last_ul_host = 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -704,6 +729,20 @@ function sync_host() {
|
|||
}
|
||||
|
||||
|
||||
function cancel_sync(host_btn_id){
|
||||
let host = host_btn_id.id.split("_")[2];
|
||||
let command = "/sync/" + host;
|
||||
if (currentUser.ul_queue){
|
||||
let item_index = currentUser.ul_queue.indexOf(command);
|
||||
// Skip [0]
|
||||
if (item_index > 0){
|
||||
console.log(host + " unqueuing");
|
||||
currentUser.ul_queue.splice(item_index, 1);
|
||||
unfreeze_queued_container(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UI
|
||||
addEventListener("DOMContentLoaded", function() {
|
||||
//~ adjust_timeline();
|
||||
|
@ -716,11 +755,11 @@ addEventListener("DOMContentLoaded", function() {
|
|||
let command = clickedButton.value;
|
||||
|
||||
if ( command.indexOf("/reboot" ) > -1 || command.indexOf("/poweroff") > -1 ) {
|
||||
// TODO : l10n
|
||||
if (command.indexOf("/all" ) == -1){
|
||||
if ( !confirm(t9n[LOCALE].confirmMessage) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
} else if ( command == "/scan" ) {
|
||||
document.getElementById("status_all").innerHTML = currentUser.status_all;
|
||||
|
||||
|
@ -741,27 +780,36 @@ addEventListener("DOMContentLoaded", function() {
|
|||
request.onload = send_ajax_cmd(command);
|
||||
|
||||
} else if ( command.indexOf("/sync/") > -1 ) {
|
||||
console.log("Sync command detected");
|
||||
if ( command.indexOf("/all") > -1 ) {
|
||||
console.log("Sync all request");
|
||||
// If command is /sync/all, add live hosts to sync queue
|
||||
if (currentUser.hosts_up){
|
||||
currentUser.hosts_up.forEach(function(host){
|
||||
let command = "/sync/" + host;
|
||||
if (currentUser.ul_queue.indexOf(command) == -1){
|
||||
console.log("Adding " + host + " to sync queue...");
|
||||
currentUser.ul_queue.push(command);
|
||||
freeze_queued_container(command);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (currentUser.ul_queue.length > 1){
|
||||
sync_host();
|
||||
}
|
||||
// Prevent request
|
||||
return 0;
|
||||
}
|
||||
// If command not already in queue, add it
|
||||
if (!currentUser.ul_queue.includes(command)){
|
||||
console.log("Queuing command...");
|
||||
currentUser.ul_queue.push(command);
|
||||
freeze_queued_container(command);
|
||||
} else {
|
||||
console.log("Command already in queue...");
|
||||
return 0;
|
||||
}
|
||||
sync_host();
|
||||
//~ if (currentUser.ul_queue.length > 1){
|
||||
//~ if (currentUser.freeze_ul){
|
||||
//~ console.log("UL freezed, wait for queue to complete.")
|
||||
// Offload command sending to sync_host()
|
||||
// Prevent request
|
||||
return 1;
|
||||
//~ }
|
||||
//~ currentUser.status_all = t9n[LOCALE].sync;
|
||||
//~ // Display dialog
|
||||
//~ display_upload_status(command);
|
||||
//~ // Request values to fill dialog
|
||||
//~ setTimeout(send_ajax_cmd, 1000, command + "/status");
|
||||
}
|
||||
else if ( command.indexOf("/browse") > -1 ) {
|
||||
request.onload = send_ajax_cmd("/all/browse");
|
||||
|
|
|
@ -63,7 +63,12 @@ tr:nth-child(2n+1) {background-color: #888;}
|
|||
/*
|
||||
.client_container .left_col{border-right: #a8a8a8 2px solid;}
|
||||
*/
|
||||
.client_container .button{}
|
||||
.client_container .btn {
|
||||
min-width:1em;
|
||||
}
|
||||
.client_container .right_col .btn {
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
.client_container .upload_dialog_cont {
|
||||
position: absolute;
|
||||
|
@ -122,6 +127,7 @@ tr:nth-child(2n+1) {background-color: #888;}
|
|||
margin-top:1em;
|
||||
background-color: #33333361;
|
||||
}
|
||||
|
||||
[id^="tl_cont"] {
|
||||
float: left;
|
||||
width: 10%;
|
||||
|
@ -152,7 +158,7 @@ tr:nth-child(2n+1) {background-color: #888;}
|
|||
.buttons {width:75%;margin:auto;text-align: center;padding: 2em;}
|
||||
.btn {
|
||||
margin:auto;
|
||||
width:3em;
|
||||
min-width:2em;
|
||||
height: 4em;
|
||||
display:inline-block;
|
||||
padding: 0;
|
||||
|
@ -182,6 +188,11 @@ tr:nth-child(2n+1) {background-color: #888;}
|
|||
}
|
||||
.btn_txt {display: block;font-size: small;}
|
||||
|
||||
.btn-unqueue {background: #bbb;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
#delete_btn {
|
||||
width: 5em;
|
||||
height: 3em;
|
||||
|
@ -199,6 +210,7 @@ tr:nth-child(2n+1) {background-color: #888;}
|
|||
|
||||
/*Right column*/
|
||||
.right_col {width: 71%;display: inline-block;}
|
||||
|
||||
/*Left column*/
|
||||
.left_col {width: 28%;display: inline-block;float: left;clear: left;}
|
||||
.left_col button {
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
<button value="/all/clear" class="command btn btn-block btn-lg btn-default" role="button">X<span class="btn_txt">{{gui_l10n['str_clear']}}</span></button>
|
||||
<button value="/all/move/0/1" class="command btn btn-block btn-lg btn-default" role="button">β<span class="btn_txt">{{gui_l10n['str_sort']}}</span></button>
|
||||
<button value="/sync/all" class="command btn btn-block btn-lg btn-default" role="button">↭<span class="btn_txt">{{gui_l10n['str_sync']}}</span></button>
|
||||
<button value="/poweroff/all" onclick="exec_all_hosts('poweroff')" class="command btn btn-block btn-lg btn-default" role="button">⏻<span class="btn_txt">{{gui_l10n['str_poweroff_all']}}</span></button>
|
||||
<button value="/reboot/all" onclick="exec_all_hosts('reboot')" class="command btn btn-block btn-lg btn-default" role="button">↺<span class="btn_txt">{{gui_l10n['str_reboot_all']}}</span></button>
|
||||
<button id="delete_btn" ondrop="drop(event, this)", ondragover="drag_over_bin(event)" ondragleave="drag_leave_bin(event)" class="delete_btn" role="button">🗑<span class="btn_txt">{{gui_l10n['str_delete']}}</span></button>
|
||||
</p>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue