JS overhaul
This commit is contained in:
parent
a6d6072ac1
commit
3dedb45f09
|
@ -83,10 +83,9 @@ sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
||||||
|
|
||||||
|
|
||||||
# TODO :
|
# TODO :
|
||||||
|
|
||||||
* webgui: remove file from timeline (drag&drop to bin?)
|
* webgui: remove file from timeline (drag&drop to bin?)
|
||||||
* webgui: file sync UI freeze/status/progress bar
|
* webgui: file sync UI freeze/status/progress bar
|
||||||
*
|
* webgui : js l10n
|
||||||
* ~ Test with several rpis
|
* ~ Test with several rpis
|
||||||
* ? Scripts hotspot linux : nmcli
|
* ? Scripts hotspot linux : nmcli
|
||||||
win : https://github.com/JamesCullum/Windows-Hotspot
|
win : https://github.com/JamesCullum/Windows-Hotspot
|
||||||
|
|
634
static/script.js
634
static/script.js
|
@ -1,424 +1,453 @@
|
||||||
|
// Config
|
||||||
|
const DEBUG = 0;
|
||||||
|
const CMD_PORT = "8888";
|
||||||
|
const TIMELINE_COLOR_CURSOR = "#FF8839";
|
||||||
|
const TIMELINE_COLOR_BG = "#2EB8E600";
|
||||||
// Timeline drag and drop elements default attributes
|
// Timeline drag and drop elements default attributes
|
||||||
const tl_cont_attr = {id:"tl_cont", ondrop: "drop(event, this)", ondragover:"allow_drop(event)"};
|
const tl_cont_attr = {id:"tl_cont", ondrop: "drop(event, this)", ondragover:"allow_drop(event)"};
|
||||||
const tl_drag_attr = {id:"tl_drag", draggable:"true", ondragstart:"drag(event, this)"};
|
const tl_drag_attr = {id:"tl_drag", draggable:"true", ondragstart:"drag(event, this)"};
|
||||||
|
// Global object
|
||||||
|
window.currentUser = {
|
||||||
|
scan_interval : 3000,
|
||||||
|
status_all : "Searching network for live hosts...",
|
||||||
|
medias_status : {},
|
||||||
|
freeze_timeline_update : 0,
|
||||||
|
};
|
||||||
|
|
||||||
// Config
|
|
||||||
var DEBUG = 0;
|
|
||||||
var cmd_port = "8888";
|
|
||||||
var timeline_color_cursor = "#FF8839";
|
|
||||||
var timeline_color_bg = "#2EB8E600";
|
|
||||||
var scan_interval = 3000;
|
|
||||||
var status_all = "Searching network for live hosts...";
|
|
||||||
// Global vars
|
|
||||||
var src_elem = "";
|
|
||||||
var medias_status = {};
|
|
||||||
var fileButtons = [];
|
|
||||||
var freeze_timeline_update = 0;
|
|
||||||
|
|
||||||
function sleep(ms) {
|
function sleep(ms) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
let delay = new Promise(function(resolve) {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
return delay ;
|
||||||
|
// arrow notation equivalent
|
||||||
|
// return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update_playlist(host){
|
|
||||||
var media_count = document.getElementById("timeline_" + host).children.length;
|
async function update_VLC_playlist(host) {
|
||||||
|
// Update host's VLC playlist according to web UI timeline
|
||||||
|
let media_count = document.getElementById(`timeline_${host}`).children.length;
|
||||||
// Reversed loop
|
// Reversed loop
|
||||||
for (i=media_count,l=0;i>l;i--) {
|
for (let i=media_count, l=0; i>l; i--) {
|
||||||
//~ for (i=0,l=media_count;i<l;i++) {
|
|
||||||
// Find current's timeline element children's 'media_id' value
|
// Find current's timeline element children's 'media_id' value
|
||||||
toShift = document.getElementById("timeline_" + host).children[i-1].children[0].getAttribute("media_id");
|
let to_shift = document.getElementById(`timeline_${host}`).children[i-1].children[0].getAttribute("media_id");
|
||||||
console.log(toShift + " : " + document.getElementById("timeline_" + host).children[i-1].children[0].innerText);
|
console.log(to_shift + " : " + document.getElementById(`timeline_${host}`).children[i-1].children[0].innerText);
|
||||||
// Move 'toShift' after element with id '1'
|
// Move 'to_shift' after element with id '1' :
|
||||||
// In VLC's playlist XML representation, playlist gets id '1', so moving to that id
|
// In VLC's playlist XML representation, the playlist node always gets id '1', so moving to that id
|
||||||
// really means moving to the the very start of the playlist.
|
// really means moving to the the very start of the playlist.
|
||||||
send_ajax_cmd("/" + host + "/move/" + toShift + "/1");
|
send_ajax_cmd("/" + host + "/move/" + to_shift + "/1");
|
||||||
await sleep(80);
|
await sleep(90);
|
||||||
}
|
}
|
||||||
// Un-freeze timeline update flag
|
// Un-freeze timeline update flag
|
||||||
freeze_timeline_update = 0;
|
currentUser.freeze_timeline_update = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function find_target_index(element, index, array, thisArg){
|
|
||||||
|
function find_target_index(element, index) {
|
||||||
if (element == this) {
|
if (element == this) {
|
||||||
return index + 1;
|
return index + 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shift_elements(target_elem) {
|
|
||||||
|
function shift_elements(source_element, target_element) {
|
||||||
// Shift elements in the timeline UI
|
// Shift elements in the timeline UI
|
||||||
//
|
//
|
||||||
// Get a list of current element siblings
|
// Get a list of current element siblings
|
||||||
var elem_list = Array.from(src_elem.parentElement.children);
|
let siblings_list = Array.from(source_element.parentElement.children);
|
||||||
// Initiate slice
|
let siblings_list_slice;
|
||||||
var elem_list_slice = elem_list;
|
|
||||||
// Find indexes of source and target elements in the timeline
|
// Find indexes of source and target elements in the timeline
|
||||||
var source_idx = elem_list.findIndex(find_target_index, src_elem);
|
let source_index = siblings_list.findIndex(find_target_index, source_element);
|
||||||
var target_idx = elem_list.findIndex(find_target_index, target_elem);
|
let target_index = siblings_list.findIndex(find_target_index, target_element);
|
||||||
var idx;
|
let insert_at = 0;
|
||||||
// idx is the index where the elements should be inserted back
|
|
||||||
// Target element is on the left of the source element
|
// Target element is on the left of the source element
|
||||||
if (source_idx < target_idx){
|
if (source_index < target_index){
|
||||||
elem_list_slice = elem_list.slice(source_idx + 1, target_idx + 1);
|
siblings_list_slice = siblings_list.slice(source_index + 1, target_index + 1);
|
||||||
idx = source_idx;
|
insert_at = source_index;
|
||||||
} else {
|
} else {
|
||||||
// Target element is on the right of the source element
|
// Target element is on the right of the source element
|
||||||
elem_list_slice = elem_list.slice(target_idx, source_idx );
|
siblings_list_slice = siblings_list.slice(target_index, source_index );
|
||||||
idx = target_idx + 1;
|
insert_at = target_index + 1;
|
||||||
}
|
}
|
||||||
// Shift elements according to idx
|
// Shift elements according to insert_at
|
||||||
for (i=0, l=elem_list_slice.length; i<l;i++){
|
for (let i=0, l=siblings_list_slice.length; i<l;i++){
|
||||||
elem_list[idx + i].appendChild(elem_list_slice[i].children[0]);
|
siblings_list[insert_at + i].appendChild(siblings_list_slice[i].children[0]);
|
||||||
}
|
}
|
||||||
return target_elem;
|
return insert_at;
|
||||||
}
|
}
|
||||||
|
|
||||||
function allow_drop(ev) {
|
|
||||||
ev.preventDefault();
|
function allow_drop(event) {
|
||||||
drop_id = ev.dataTransfer.getData("text");
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag(ev, source) {
|
|
||||||
|
function drag(event, source) {
|
||||||
// Freeze timeline update flag
|
// Freeze timeline update flag
|
||||||
freeze_timeline_update = 1;
|
currentUser.freeze_timeline_update = 1;
|
||||||
src_elem = ev.target.parentElement;
|
event.dataTransfer.setData("text", event.target.id);
|
||||||
if (DEBUG) {
|
|
||||||
console.log(src_elem.children[0].getAttribute("media_id") + " : " + src_elem.children[0].innerText);
|
|
||||||
}
|
|
||||||
ev.dataTransfer.setData("text", ev.target.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function drop(ev, target_elem) {
|
|
||||||
// If the currently dragged element is dropped on another element, shift HTML elements in the timeline to the left or right from the target element.
|
function drop(event, target_element) {
|
||||||
//
|
// If the currently dragged element is dropped on another element,
|
||||||
ev.preventDefault();
|
// shift HTML elements in the timeline to the left or right from the target element.
|
||||||
|
event.preventDefault();
|
||||||
// Get dragged element id
|
// Get dragged element id
|
||||||
var data = ev.dataTransfer.getData("text");
|
let dropped_id = event.dataTransfer.getData("text");
|
||||||
|
let source_element = document.getElementById(dropped_id);
|
||||||
|
let current_host = target_element.parentElement.id.split("_")[1];
|
||||||
// Only shift if not dropping on self
|
// Only shift if not dropping on self
|
||||||
if (src_elem.id != target_elem.id) {
|
if (source_element.id != target_element.id) {
|
||||||
dropTarget = shift_elements(target_elem);
|
let dropTarget = shift_elements(source_element.parentElement, target_element);
|
||||||
|
//~ let dropTarget = shift_elements(currentUser.source_element, target_element);
|
||||||
if (dropTarget) {
|
if (dropTarget) {
|
||||||
//
|
// Append dropped element to drop target.
|
||||||
dropTarget.appendChild(document.getElementById(data));
|
target_element.appendChild(source_element);
|
||||||
// Update VLC playlist according to UI timeline
|
update_VLC_playlist(current_host);
|
||||||
// TODO : This currently sends the move cmd to all instances. Fix it to specify which host to send the cmd to.
|
|
||||||
update_playlist("10.42.0.10");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
send_ajax_cmd("/all/list");
|
send_ajax_cmd("/" + current_host + "/list");
|
||||||
//~ setTimeout(function(){ freeze_timeline_update = 0; console.log("shwing!"); }, 2000);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function adjust_timeline() {
|
function adjust_timeline() {
|
||||||
// Adapt timeline's UI elements to fit the width of their parent container.
|
// Adapt timeline's UI elements to fit the width of their parent container.
|
||||||
//
|
let timeline_div = document.querySelector('[id^="timeline_"]').children;
|
||||||
//~ var child = document.getElementById('timeline').children;
|
let div_width = 100 / timeline_div.length;
|
||||||
var child = document.querySelector('[id^="timeline_"]').children;
|
for (let i=0, l=timeline_div.length; i<l; i++) {
|
||||||
var divWidth = 100 / child.length;
|
timeline_div[i].style.width= div_width + "%";
|
||||||
for (i=0, l=child.length;i<l;i++) {
|
|
||||||
child[i].style.width= divWidth + "%";
|
|
||||||
}
|
}
|
||||||
|
return timeline_div.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_HTML_attr(id, attr, val , child=-1) {
|
|
||||||
|
function add_HTML_attr(id, attribute, val , child=-1) {
|
||||||
// Add attribute 'attr' with value 'val' to the HTML element with id 'id'.
|
// Add attribute 'attr' with value 'val' to the HTML element with id 'id'.
|
||||||
// If child is other than -1, the attribute is applied to the HTML element's child at position 'child'
|
// If child is other than -1, the attribute is applied to the HTML element's child at position 'child'
|
||||||
var elem = document.getElementById(id);
|
let HTML_element = document.getElementById(id);
|
||||||
if (child>-1){
|
if (child > -1){
|
||||||
elem = elem.children[child];
|
HTML_element = HTML_element.children[child];
|
||||||
}
|
}
|
||||||
var att = document.createAttribute(attr);
|
let HTML_attr = document.createAttribute(attribute);
|
||||||
att.value = val;
|
HTML_attr.value = val;
|
||||||
elem.setAttributeNode(att);
|
HTML_element.setAttributeNode(HTML_attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_HTML_element(type, attr, meta=0, j=0){
|
|
||||||
// Add HTML element with type 'type' and attributes 'attr'.
|
function add_HTML_element(type, attribute, meta=0, uid=0){
|
||||||
// 'attr' should be a javascript object
|
// Add HTML element with type 'type' and attributes 'attribute'.
|
||||||
|
// 'attribute' should be a javascript object containing HTML attributes keys/values
|
||||||
// 'meta' should be an array
|
// 'meta' should be an array
|
||||||
// 'j' is used to pass an increment value when used in a loop
|
// 'uid' is used to make the HTML id attribute unique
|
||||||
//
|
//
|
||||||
var elem = document.createElement(type);
|
let HTML_element = document.createElement(type);
|
||||||
var keys_array = Object.keys(attr);
|
let HTML_attributes = Object.keys(attribute);
|
||||||
for (i=0, l=keys_array.length;i<l;i++) {
|
for (let i=0, l=HTML_attributes.length; i<l; i++) {
|
||||||
var att = document.createAttribute(keys_array[i]);
|
let new_attribute = document.createAttribute(HTML_attributes[i]);
|
||||||
// First iteration
|
if(HTML_attributes[i] == "id") {
|
||||||
if(!i){
|
// HTML id needs to be unique
|
||||||
att.value = Object.values(attr)[i]+j;
|
new_attribute.value = Object.values(attribute)[i] + uid;
|
||||||
} else {
|
} else {
|
||||||
att.value = Object.values(attr)[i];
|
new_attribute.value = Object.values(attribute)[i];
|
||||||
}
|
}
|
||||||
elem.setAttributeNode(att);
|
HTML_element.setAttributeNode(new_attribute);
|
||||||
}
|
}
|
||||||
// Set media_id attribute if metadata is provided
|
// Set media_id attribute if metadata is provided
|
||||||
if (meta) {
|
if (meta) {
|
||||||
attribute = document.createAttribute("media_id");
|
let media_attribute = document.createAttribute("media_id");
|
||||||
attribute.value = meta[0];
|
media_attribute.value = meta[0];
|
||||||
elem.setAttributeNode(attribute);
|
HTML_element.setAttributeNode(media_attribute);
|
||||||
}
|
}
|
||||||
// Get filename
|
// Set filename as inner text
|
||||||
elem.innerText = meta[1];
|
HTML_element.innerText = meta[1];
|
||||||
return elem;
|
return HTML_element;
|
||||||
}
|
}
|
||||||
// Bouttons de commande
|
|
||||||
addEventListener("DOMContentLoaded", function() {
|
|
||||||
//~ send_ajax_cmd("/scan");
|
|
||||||
//~ send_ajax_cmd("/browse_local");
|
|
||||||
//~ setTimeout(send_ajax_cmd, 3000, "/all/browse");
|
|
||||||
//~ setTimeout(send_ajax_cmd, 4000, "/all/rssi");
|
|
||||||
//~ setTimeout(send_ajax_cmd, 1000, "/all/list");
|
|
||||||
adjust_timeline();
|
|
||||||
|
|
||||||
// Get all elements with class 'command'
|
|
||||||
var commandButtons = document.querySelectorAll(".command");
|
|
||||||
for (var i=0, l=commandButtons.length; i<l; i++) {
|
|
||||||
var button = commandButtons[i];
|
|
||||||
// Listen for clicks
|
|
||||||
button.addEventListener("click", function(event) {
|
|
||||||
// Discard signal
|
|
||||||
event.preventDefault();
|
|
||||||
// Get value=""
|
|
||||||
var clickedButton = event.currentTarget;
|
|
||||||
var command = clickedButton.value;
|
|
||||||
// Proceed accordingly
|
|
||||||
if ( command.indexOf("/reboot" ) > -1 || command.indexOf("/poweroff") > -1 ) {
|
|
||||||
if ( !confirm("Êtes vous certain de vouloir effectuer cette action ?") ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ( command == "/scan" ) {
|
function toggle_indicator(infos_array_element, property) {
|
||||||
document.getElementById("status_all").innerHTML = status_all;
|
// Toggle element bg color
|
||||||
|
toggle_state = 0;
|
||||||
|
if (infos_array_element[property] == "true") {
|
||||||
|
document.getElementById(`${property}_ind_${infos_array_element.host}`).style.backgroundColor = "#78E738";
|
||||||
|
toggle_state = 1;
|
||||||
|
} else {
|
||||||
|
document.getElementById(`${property}_ind_${infos_array_element.host}`).style.backgroundColor = "#A42000";
|
||||||
|
}
|
||||||
|
return toggle_state;
|
||||||
|
}
|
||||||
|
|
||||||
} else if ( command.indexOf("/sort") > -1 ){
|
|
||||||
if (command.indexOf('/1/') > -1 ) {
|
|
||||||
clickedButton.value = clickedButton.value.replace('/1/','/0/');
|
|
||||||
} else {
|
|
||||||
clickedButton.value = clickedButton.value.replace('/0/','/1/');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function update_status(infos_array_element) {
|
||||||
|
if (infos_array_element.status) {
|
||||||
|
document.getElementById("status_" + infos_array_element.host).innerHTML = infos_array_element.file + " <br/> " + infos_array_element.time + " / " + infos_array_element.leng;
|
||||||
|
currentUser.medias_status[infos_array_element.id] = infos_array_element.pos;
|
||||||
|
// Highlight currently playing element
|
||||||
|
let timeline_medias_array = Array.from(document.querySelectorAll('[media_id]'));
|
||||||
|
timeline_medias_array.forEach(function(media_element){
|
||||||
|
if ( media_element.getAttribute("media_id") == infos_array_element.id ) {
|
||||||
|
let first_CSS_gradient_stop = infos_array_element.pos * 100;
|
||||||
|
let second_CSS_gradient_stop = first_CSS_gradient_stop - 1 + "%";
|
||||||
|
first_CSS_gradient_stop = first_CSS_gradient_stop + "%";
|
||||||
|
let media_url_str = "url(https://" + infos_array_element.host + ":" + CMD_PORT + "/thumb/" + infos_array_element.file + ")";
|
||||||
|
let media_cssgrad_rule = "linear-gradient(90deg," + TIMELINE_COLOR_BG + " " + second_CSS_gradient_stop + ", " + TIMELINE_COLOR_CURSOR + " " + first_CSS_gradient_stop + ", " + TIMELINE_COLOR_BG + " " + first_CSS_gradient_stop + ")," + media_url_str;
|
||||||
|
media_element.style.backgroundImage = media_cssgrad_rule;
|
||||||
|
media_element.style.borderBottom = "4px solid " + TIMELINE_COLOR_CURSOR;
|
||||||
|
} else {
|
||||||
|
let media_url_str = "url(https://" + infos_array_element.host + ":" + CMD_PORT + "/thumb/" + media_element.innerText + ")"
|
||||||
|
media_element.style.backgroundImage = media_url_str;
|
||||||
|
media_element.style.borderBottom = "None";
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.getElementById("status_" + infos_array_element.host).innerHTML = "<br><br>";
|
||||||
|
}
|
||||||
|
toggle_indicator(infos_array_element, "loop");
|
||||||
|
toggle_indicator(infos_array_element, "repeat");
|
||||||
|
return infos_array_element.status;
|
||||||
|
}
|
||||||
|
|
||||||
// AJAX request
|
|
||||||
var request = new XMLHttpRequest();
|
function update_list(infos_array_element){
|
||||||
if ( command == "/scan") {
|
// Playlist infos are displayed in a div ; number of items in list and total duration.
|
||||||
request.onload = send_ajax_cmd(command);
|
//~ document.getElementById("playlist_"+infos_array[i].host).innerHTML = infos_array[i].leng + " item(s) in playlist - " + infos_array[i].duration;
|
||||||
} else if ( command.indexOf("/clear") > -1 || command.indexOf("/sort") > -1) {
|
// Populate timeline according to the content of the playlist
|
||||||
request.onload = send_ajax_cmd(command);
|
// Get returned items as an array
|
||||||
} else if ( command == "/sync/all" ) {
|
let items_array = Array.from(infos_array_element.items);
|
||||||
status_all = "Syncing files...";
|
let host = infos_array_element.host;
|
||||||
request.onload = send_ajax_cmd("/sync/status");
|
// If playlist is empty, remove all media divs in the timeline UI.
|
||||||
|
if (items_array.length == 0){
|
||||||
|
let child_list = Array.from(document.getElementById("timeline_" + host).children);
|
||||||
|
child_list.forEach(function(child){
|
||||||
|
document.getElementById("timeline_" + host).removeChild(child);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items_array.forEach(function(item, j){
|
||||||
|
item_meta = item.split(';');
|
||||||
|
let child_node = add_HTML_element("div", tl_drag_attr, item_meta, j);
|
||||||
|
let len = document.getElementById("timeline_" + host).children.length;
|
||||||
|
add_HTML_attr("timeline_" + host, "length", len);
|
||||||
|
if ( len < items_array.length ) {
|
||||||
|
document.getElementById("timeline_" + host).appendChild( add_HTML_element("div", tl_cont_attr, 0, len) );
|
||||||
}
|
}
|
||||||
|
document.getElementById(tl_cont_attr.id + j).replaceChildren(child_node);
|
||||||
// build request
|
let media_name = document.getElementById(tl_cont_attr.id + j).children[0].innerText;
|
||||||
request.open("GET", command, true);
|
let media_url_str = "url(https://" + host + ":" + CMD_PORT + "/thumb/" + media_name + ")"
|
||||||
// send request
|
document.getElementById(tl_cont_attr.id + j).children[0].style.backgroundImage = media_url_str;
|
||||||
request.send();
|
// Adjust elements width
|
||||||
|
adjust_timeline();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, true);
|
return items_array.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function populate_HTML_table(inner_text, host="all", CSS_class="file_selection") {
|
||||||
|
tr = document.createElement("tr");
|
||||||
|
td = document.createElement("td");
|
||||||
|
tr.appendChild(td);
|
||||||
|
tr.setAttribute("class", CSS_class);
|
||||||
|
tr.host = host;
|
||||||
|
td.innerText = inner_text;
|
||||||
|
// Add an event to the created element to send enqueue command when a file is clicked in the list
|
||||||
|
tr.addEventListener("click", enqueue_file, false);
|
||||||
|
document.getElementById("file_sel_" + host).appendChild(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_remote_filelist(infos_array_element) {
|
||||||
|
hosts = Object.keys(infos_array_element);
|
||||||
|
for (let j=0, m=hosts.length; j<m; j++) {
|
||||||
|
let media_item = Object.keys(infos_array_element[hosts[j]]);
|
||||||
|
for (let k=0, n=media_item.length; k<n; k++) {
|
||||||
|
media_item_meta = infos_array_element[hosts[j]][media_item[k]];
|
||||||
|
populate_HTML_table(media_item_meta.name, hosts[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function update_rssi_indicator(infos_array_element){
|
||||||
|
let signal_color = 40;
|
||||||
|
let best_rssi = 30;
|
||||||
|
let worst_rssi = 70;
|
||||||
|
let rssi_norm = Math.ceil( (worst_rssi - parseInt(infos_array_element.rssi) ) / ( worst_rssi - best_rssi ) * 4 );
|
||||||
|
signal_color = (rssi_norm-1) * signal_color;
|
||||||
|
// Reset to grey
|
||||||
|
for (let i=0, l=4; i<l; i++) {
|
||||||
|
document.getElementById("wl_"+i).style.backgroundColor = "hsl(0, 0%, 65%)";
|
||||||
|
}
|
||||||
|
// Color it
|
||||||
|
for (let i=0, l=rssi_norm > 4 ? 4 : rssi_norm; i<l; i++) {
|
||||||
|
document.getElementById("wl_"+i).style.backgroundColor = "hsl(" + signal_color + ", 100%, 50%)";
|
||||||
|
}
|
||||||
|
return rssi_norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function update_local_filelist(infos_array) {
|
||||||
|
// TODO : Is this useful ?
|
||||||
|
// TODO : Get real media length
|
||||||
|
let html_table = "<table>" +
|
||||||
|
"<tr>" +
|
||||||
|
"<th>Filename</th>" +
|
||||||
|
"<th>Duration</th>" +
|
||||||
|
"</tr>";
|
||||||
|
infos_array.forEach(function(element){
|
||||||
|
html_table += "<tr>" +
|
||||||
|
"<td>" + element + "</td>" +
|
||||||
|
"<td>" + "00:00" + "</td>" +
|
||||||
|
"</tr>" ;
|
||||||
|
});
|
||||||
|
html_table += "</table>";
|
||||||
|
return html_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function update_host_list(infos_array){
|
||||||
|
let host_up = infos_array[0];
|
||||||
|
let host_down = infos_array[1];
|
||||||
|
if (host_up.length) {
|
||||||
|
host_up.forEach(function(host){
|
||||||
|
document.getElementById(host).style.display = 'block';
|
||||||
|
send_ajax_cmd("/" + host + "/list");
|
||||||
|
send_ajax_cmd("/" + host + "/rssi");
|
||||||
|
});
|
||||||
|
currentUser.scan_interval = 10000;
|
||||||
|
}
|
||||||
|
host_down.forEach(function(element){
|
||||||
|
document.getElementById(element).style.display = 'none';
|
||||||
|
});
|
||||||
|
currentUser.status_all = host_up.length + " client(s).";
|
||||||
|
return host_up.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Metadata display
|
// Metadata display
|
||||||
function parse_result(command, infos_array) {
|
function parse_result(command, infos_array) {
|
||||||
if (command == "/all/status") {
|
if (command == "/all/status") {
|
||||||
// Requests current status of every instances, especially current media file's name, position and length.
|
// Requests current status of every instances, especially current media file's name, position and length.
|
||||||
// Also retrieves loop and repeat status.
|
// Also retrieves loop and repeat status.
|
||||||
//
|
infos_array.forEach(update_status);
|
||||||
// Iterate over array
|
|
||||||
for (var i = 0, l=infos_array.length; i<l; i++) {
|
|
||||||
// Get filename, time/length
|
|
||||||
if (infos_array[i].status) {
|
|
||||||
document.getElementById("status_"+infos_array[i].host).innerHTML = infos_array[i].file + " <br/> " + infos_array[i].time + " / " + infos_array[i].leng;
|
|
||||||
medias_status[infos_array[i].id] = infos_array[i].pos;
|
|
||||||
|
|
||||||
// Highlight currently playing element
|
|
||||||
timeline_medias_array = Array.from(document.querySelectorAll('[media_id]'));
|
|
||||||
for (var z=0, x=timeline_medias_array.length; z<x;z++){
|
|
||||||
if ( timeline_medias_array[z].getAttribute("media_id") == infos_array[i].id ) {
|
|
||||||
var pos = infos_array[i].pos * 100;
|
|
||||||
var pos1 = pos-1 + "%";
|
|
||||||
pos = pos + "%";
|
|
||||||
var media_url_str = "url(https://" + infos_array[i].host + ":" + cmd_port + "/thumb/" + infos_array[i].file + ")"
|
|
||||||
var media_cssgrad_rule = "linear-gradient(90deg," + timeline_color_bg + " " + pos1 + ", " + timeline_color_cursor + " " + pos + ", " + timeline_color_bg + " " + pos + ")," + media_url_str;
|
|
||||||
timeline_medias_array[z].style.backgroundImage = media_cssgrad_rule;
|
|
||||||
timeline_medias_array[z].style.borderBottom = "4px solid " + timeline_color_cursor;
|
|
||||||
} else {
|
|
||||||
var media_url_str = "url(https://" + infos_array[i].host + ":" + cmd_port + "/thumb/" + timeline_medias_array[z].innerText + ")"
|
|
||||||
timeline_medias_array[z].style.backgroundImage = media_url_str;
|
|
||||||
timeline_medias_array[z].style.borderBottom = "None";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
document.getElementById("status_"+infos_array[i].host).innerHTML = "<br><br>";
|
|
||||||
}
|
|
||||||
// Toggle loop indicator
|
|
||||||
if (infos_array[i].loop == "true") {
|
|
||||||
document.getElementById("loop_ind_" + infos_array[i].host).style.backgroundColor = "#78E738";
|
|
||||||
} else {
|
|
||||||
document.getElementById("loop_ind_" + infos_array[i].host).style.backgroundColor = "#A42000";
|
|
||||||
}
|
|
||||||
// Toggle repeat indicator
|
|
||||||
if (infos_array[i].repeat == "true") {
|
|
||||||
document.getElementById("repeat_ind_" + infos_array[i].host).style.backgroundColor = "#78E738";
|
|
||||||
} else {
|
|
||||||
document.getElementById("repeat_ind_" + infos_array[i].host).style.backgroundColor = "#A42000";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ( command.indexOf("/list") > -1 ) {
|
} else if ( command.indexOf("/list") > -1 ) {
|
||||||
// Requests playlist of every instances
|
// Requests playlist of every instances
|
||||||
//
|
if (!currentUser.freeze_timeline_update){
|
||||||
if (!freeze_timeline_update){
|
infos_array.forEach(update_list);
|
||||||
console.log("Timeline freeze : " + freeze_timeline_update);
|
|
||||||
console.log("Infos array : " + infos_array);
|
|
||||||
for (var i = 0, l=infos_array.length; i<l; i++) {
|
|
||||||
// Playlist infos are displayed in a div ; number of items in list and total duration.
|
|
||||||
//~ document.getElementById("playlist_"+infos_array[i].host).innerHTML = infos_array[i].leng + " item(s) in playlist - " + infos_array[i].duration;
|
|
||||||
// Populate timeline according to the content of the playlist
|
|
||||||
// Get returned items as an array
|
|
||||||
var items_array = Array.from(infos_array[i].items);
|
|
||||||
//console.log(items_array.length);
|
|
||||||
// If playlist is empty, remove all media divs in the timeline UI.
|
|
||||||
if (items_array.length == 0){
|
|
||||||
var child_list = Array.from(document.getElementById("timeline_" + infos_array[i].host).children);
|
|
||||||
for(x=0,y=child_list.length;x<y;x++){
|
|
||||||
document.getElementById("timeline_" + infos_array[i].host).removeChild(child_list[x]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (var j = 0, k=items_array.length; j<k; j++) {
|
|
||||||
// Timeline
|
|
||||||
item_meta = items_array[j].split(';');
|
|
||||||
var child_node = add_HTML_element("div", tl_drag_attr, item_meta, j);
|
|
||||||
var len = document.getElementById("timeline_" + infos_array[i].host).children.length;
|
|
||||||
add_HTML_attr("timeline_" + infos_array[i].host, "length", len);
|
|
||||||
if ( len < items_array.length ) {
|
|
||||||
document.getElementById("timeline_" + infos_array[i].host).appendChild( add_HTML_element("div", tl_cont_attr, 0, len) );
|
|
||||||
}
|
|
||||||
document.getElementById(tl_cont_attr.id + j).replaceChildren(child_node);
|
|
||||||
var media_name = document.getElementById(tl_cont_attr.id + j).children[0].innerText;
|
|
||||||
var media_url_str = "url(https://" + infos_array[i].host + ":" + cmd_port + "/thumb/" + media_name + ")"
|
|
||||||
document.getElementById(tl_cont_attr.id + j).children[0].style.backgroundImage = media_url_str;
|
|
||||||
// Adjust elements width
|
|
||||||
adjust_timeline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( command == "/scan") {
|
} else if ( command == "/scan") {
|
||||||
var host_up = infos_array[0];
|
// Scan for live hosts
|
||||||
var host_down = infos_array[1];
|
update_host_list(infos_array);
|
||||||
for ( var i=0, l=host_up.length; i<l; i++){
|
|
||||||
document.getElementById(host_up[i]).style.display = 'block';
|
|
||||||
}
|
|
||||||
for ( var i=0, l=host_down.length; i<l; i++){
|
|
||||||
document.getElementById(host_down[i]).style.display = 'none';
|
|
||||||
}
|
|
||||||
if (host_up.length) {
|
|
||||||
scan_interval = 10000;
|
|
||||||
//~ document.getElementById("status_all").innerHTML = "Scan interval set to " + scan_interval;
|
|
||||||
status_all = "Scan interval set to " + scan_interval;
|
|
||||||
}
|
|
||||||
//~ document.getElementById("status_all").innerHTML = host_up.length + " client(s).";
|
|
||||||
status_all = host_up.length + " client(s).";
|
|
||||||
} else if ( command == "/browse_local") {
|
} else if ( command == "/browse_local") {
|
||||||
// Display local media files in a table
|
// Display local media files in a table
|
||||||
var html_table = "<table>" +
|
document.getElementById("filelist").innerHTML = update_local_filelist(infos_array);
|
||||||
"<tr>" +
|
|
||||||
"<th>Filename</th>" +
|
|
||||||
"<th>Duration</th>" +
|
|
||||||
"</tr>";
|
|
||||||
for (var j = 0, k=infos_array.length; j<k; j++) {
|
|
||||||
html_table += "<tr>" +
|
|
||||||
"<td>" + infos_array[j] + "</td>" +
|
|
||||||
"<td>" + "00:00" + "</td>" +
|
|
||||||
"</tr>" ;
|
|
||||||
}
|
|
||||||
html_table += "</table>";
|
|
||||||
document.getElementById("filelist").innerHTML = html_table;
|
|
||||||
} else if ( command == "/all/rssi") {
|
} else if ( command == "/all/rssi") {
|
||||||
// RSSI strength indicator
|
// RSSI strength indicator
|
||||||
var signal_color = 40;
|
infos_array.forEach(update_rssi_indicator);
|
||||||
var best_rssi = 30;
|
|
||||||
var worst_rssi = 70;
|
|
||||||
for (var j = 0, k=infos_array.length; j<k; j++) {
|
|
||||||
var rssi_norm = Math.ceil( (worst_rssi - parseInt(infos_array[j].rssi) ) / ( worst_rssi - best_rssi ) * 4 );
|
|
||||||
signal_color = (rssi_norm-1) * signal_color;
|
|
||||||
// Reset to grey
|
|
||||||
for (i=0, l=4; i<l;i++) {
|
|
||||||
document.getElementById("wl_"+i).style.backgroundColor = "hsl(0, 0%, 65%)";
|
|
||||||
}
|
|
||||||
// Color it
|
|
||||||
for (i=0, l=rssi_norm>4?4:rssi_norm; i<l;i++) {
|
|
||||||
document.getElementById("wl_"+i).style.backgroundColor = "hsl(" + signal_color + ", 100%, 50%)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ( command == "/all/browse") {
|
} else if ( command == "/all/browse") {
|
||||||
// Display remote media files in a table
|
// Display remote media files in a table
|
||||||
for (var i=0, l=infos_array.length; i<l; i++) {
|
infos_array.forEach(update_remote_filelist);
|
||||||
hosts = Object.keys(infos_array[i]);
|
|
||||||
//~ console.log(keys);
|
|
||||||
for ( var j=0, k=hosts.length;j<k;j++ ) {
|
|
||||||
keys_ = Object.keys(infos_array[i][hosts[j]]);
|
|
||||||
//~ console.log(infos_array[i][hosts[j]]);
|
|
||||||
for ( var m=0, n=keys_.length;m<n;m++ ) {
|
|
||||||
item_meta = infos_array[i][hosts[j]][keys_[m]];
|
|
||||||
//~ console.log(item_meta);
|
|
||||||
tr = document.createElement("tr");
|
|
||||||
td = document.createElement("td");
|
|
||||||
tr.appendChild(td);
|
|
||||||
tr.setAttribute("class", "file_selection");
|
|
||||||
tr.host = hosts[j];
|
|
||||||
td.innerText = item_meta.name;
|
|
||||||
// Add an event to the created element to send enqueue command when a file is clicked in the list
|
|
||||||
tr.addEventListener("click", enqueue_file, false);
|
|
||||||
document.getElementById("file_sel_"+hosts).appendChild(tr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ( command == "/sync/status") {
|
} else if ( command == "/sync/status") {
|
||||||
status_all = infos_array;
|
// TODO : File sync UI status
|
||||||
|
currentUser.status_all = infos_array;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
setTimeout(send_ajax_cmd, 80, "/all/list");
|
setTimeout(send_ajax_cmd, 80, "/all/list");
|
||||||
setTimeout(send_ajax_cmd, 120, "/all/status");
|
setTimeout(send_ajax_cmd, 120, "/all/status");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function send_ajax_cmd(command) {
|
function send_ajax_cmd(command) {
|
||||||
var request = new XMLHttpRequest();
|
let request = new XMLHttpRequest();
|
||||||
request.onload = function() {
|
request.onload = function() {
|
||||||
if (request.readyState === request.DONE) {
|
if (request.readyState === request.DONE) {
|
||||||
if (request.status === 200) {
|
if (request.status === 200) {
|
||||||
// responseText is a string, use parse to get an array.
|
// responseText is a string, use parse to get an array.
|
||||||
if (!freeze_timeline_update) {
|
if (!currentUser.freeze_timeline_update) {
|
||||||
var infos_array = JSON.parse(request.responseText);
|
let infos_array = JSON.parse(request.responseText);
|
||||||
//console.log(infos_array.length);
|
|
||||||
parse_result(command, infos_array);
|
parse_result(command, infos_array);
|
||||||
//return infos_array;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// On construit la commande
|
|
||||||
request.open("GET", command, true);
|
request.open("GET", command, true);
|
||||||
// et on l'envoie
|
|
||||||
request.send();
|
request.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function enqueue_file(evt) {
|
function enqueue_file(evt) {
|
||||||
//console.log(evt.currentTarget.innerText + evt.currentTarget.host);
|
|
||||||
send_ajax_cmd("/" + evt.currentTarget.host + "/enqueue/" + evt.currentTarget.innerText);
|
send_ajax_cmd("/" + evt.currentTarget.host + "/enqueue/" + evt.currentTarget.innerText);
|
||||||
//~ send_ajax_cmd("/all/list");
|
|
||||||
setTimeout(send_ajax_cmd, 40, "/" + evt.currentTarget.host + "/list");
|
setTimeout(send_ajax_cmd, 40, "/" + evt.currentTarget.host + "/list");
|
||||||
//~ send_ajax_cmd("/" + evt.currentTarget.host + "/list");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function update_statusall_content() {
|
function update_statusall_content() {
|
||||||
document.getElementById("status_all").innerHTML = status_all;
|
document.getElementById("status_all").innerHTML = currentUser.status_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
var scan_hosts = function() {
|
|
||||||
|
function scan_hosts() {
|
||||||
send_ajax_cmd("/scan");
|
send_ajax_cmd("/scan");
|
||||||
update_statusall_content();
|
update_statusall_content();
|
||||||
setTimeout(scan_hosts, scan_interval);
|
setTimeout(scan_hosts, currentUser.scan_interval);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// UI
|
||||||
|
addEventListener("DOMContentLoaded", function() {
|
||||||
|
adjust_timeline();
|
||||||
|
let commandButtons = document.querySelectorAll(".command");
|
||||||
|
for (let i=0, l=commandButtons.length; i<l; i++) {
|
||||||
|
let button = commandButtons[i];
|
||||||
|
button.addEventListener("click", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let clickedButton = event.currentTarget;
|
||||||
|
let command = clickedButton.value;
|
||||||
|
|
||||||
|
if ( command.indexOf("/reboot" ) > -1 || command.indexOf("/poweroff") > -1 ) {
|
||||||
|
// TODO : l10n
|
||||||
|
if ( !confirm("Êtes vous certain de vouloir effectuer cette action ?") ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ( command == "/scan" ) {
|
||||||
|
document.getElementById("status_all").innerHTML = currentUser.status_all;
|
||||||
|
|
||||||
|
} else if ( command.indexOf("/sort") > -1 ){
|
||||||
|
if (command.indexOf('/1/') > -1 ) {
|
||||||
|
clickedButton.value = clickedButton.value.replace('/1/','/0/');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
clickedButton.value = clickedButton.value.replace('/0/','/1/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AJAX request
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
if ( command == "/scan") {
|
||||||
|
request.onload = send_ajax_cmd(command);
|
||||||
|
|
||||||
|
} else if ( command.indexOf("/clear") > -1 || command.indexOf("/sort") > -1) {
|
||||||
|
request.onload = send_ajax_cmd(command);
|
||||||
|
|
||||||
|
} else if ( command == "/sync/all" ) {
|
||||||
|
currentUser.status_all = "Syncing files...";
|
||||||
|
request.onload = send_ajax_cmd("/sync/status");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
request.open("GET", command, true);
|
||||||
|
request.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
send_ajax_cmd("/scan");
|
send_ajax_cmd("/scan");
|
||||||
send_ajax_cmd("/browse_local");
|
send_ajax_cmd("/browse_local");
|
||||||
|
@ -426,7 +455,6 @@ setInterval(send_ajax_cmd, 500, "/all/status");
|
||||||
setTimeout(send_ajax_cmd, 1000, "/all/list");
|
setTimeout(send_ajax_cmd, 1000, "/all/list");
|
||||||
setTimeout(send_ajax_cmd, 3000, "/all/browse");
|
setTimeout(send_ajax_cmd, 3000, "/all/browse");
|
||||||
setInterval(send_ajax_cmd, 20000, "/all/rssi");
|
setInterval(send_ajax_cmd, 20000, "/all/rssi");
|
||||||
//~ setInterval(update_statusall_content, 1000);
|
setTimeout(scan_hosts, currentUser.scan_interval);
|
||||||
setTimeout(scan_hosts, scan_interval);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue