diff --git a/changelog_todo.md b/changelog_todo.md index 6950b2f..1f55e92 100644 --- a/changelog_todo.md +++ b/changelog_todo.md @@ -83,10 +83,9 @@ sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6 # TODO : - * webgui: remove file from timeline (drag&drop to bin?) * webgui: file sync UI freeze/status/progress bar - * + * webgui : js l10n * ~ Test with several rpis * ? Scripts hotspot linux : nmcli win : https://github.com/JamesCullum/Windows-Hotspot diff --git a/static/script.js b/static/script.js index ff522dd..0026933 100644 --- a/static/script.js +++ b/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 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)"}; +// 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) { - 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 - for (i=media_count,l=0;i>l;i--) { - //~ for (i=0,l=media_count;il; i--) { // Find current's timeline element children's 'media_id' value - toShift = 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); - // Move 'toShift' after element with id '1' - // In VLC's playlist XML representation, playlist gets id '1', so moving to that id + let to_shift = document.getElementById(`timeline_${host}`).children[i-1].children[0].getAttribute("media_id"); + console.log(to_shift + " : " + document.getElementById(`timeline_${host}`).children[i-1].children[0].innerText); + // Move 'to_shift' after element with id '1' : + // 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. - send_ajax_cmd("/" + host + "/move/" + toShift + "/1"); - await sleep(80); + send_ajax_cmd("/" + host + "/move/" + to_shift + "/1"); + await sleep(90); } // 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) { return index + 1; } return 0; } -function shift_elements(target_elem) { + +function shift_elements(source_element, target_element) { // Shift elements in the timeline UI // // Get a list of current element siblings - var elem_list = Array.from(src_elem.parentElement.children); - // Initiate slice - var elem_list_slice = elem_list; + let siblings_list = Array.from(source_element.parentElement.children); + let siblings_list_slice; // Find indexes of source and target elements in the timeline - var source_idx = elem_list.findIndex(find_target_index, src_elem); - var target_idx = elem_list.findIndex(find_target_index, target_elem); - var idx; - // idx is the index where the elements should be inserted back + let source_index = siblings_list.findIndex(find_target_index, source_element); + let target_index = siblings_list.findIndex(find_target_index, target_element); + let insert_at = 0; // Target element is on the left of the source element - if (source_idx < target_idx){ - elem_list_slice = elem_list.slice(source_idx + 1, target_idx + 1); - idx = source_idx; + if (source_index < target_index){ + siblings_list_slice = siblings_list.slice(source_index + 1, target_index + 1); + insert_at = source_index; } else { // Target element is on the right of the source element - elem_list_slice = elem_list.slice(target_idx, source_idx ); - idx = target_idx + 1; + siblings_list_slice = siblings_list.slice(target_index, source_index ); + insert_at = target_index + 1; } - // Shift elements according to idx - for (i=0, l=elem_list_slice.length; i-1){ - elem = elem.children[child]; + let HTML_element = document.getElementById(id); + if (child > -1){ + HTML_element = HTML_element.children[child]; } - var att = document.createAttribute(attr); - att.value = val; - elem.setAttributeNode(att); + let HTML_attr = document.createAttribute(attribute); + HTML_attr.value = val; + HTML_element.setAttributeNode(HTML_attr); } -function add_HTML_element(type, attr, meta=0, j=0){ - // Add HTML element with type 'type' and attributes 'attr'. - // 'attr' should be a javascript object + +function add_HTML_element(type, attribute, meta=0, uid=0){ + // 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 - // '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); - var keys_array = Object.keys(attr); - for (i=0, l=keys_array.length;i -1 || command.indexOf("/poweroff") > -1 ) { - if ( !confirm("Êtes vous certain de vouloir effectuer cette action ?") ) { - return 0; - } - } else if ( command == "/scan" ) { - document.getElementById("status_all").innerHTML = 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/'); - } - +function toggle_indicator(infos_array_element, property) { + // 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; +} + + +function update_status(infos_array_element) { + if (infos_array_element.status) { + document.getElementById("status_" + infos_array_element.host).innerHTML = infos_array_element.file + "
" + 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 = "

"; + } + toggle_indicator(infos_array_element, "loop"); + toggle_indicator(infos_array_element, "repeat"); + return infos_array_element.status; +} - // AJAX request - var 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" ) { - status_all = "Syncing files..."; - request.onload = send_ajax_cmd("/sync/status"); - } - // build request - request.open("GET", command, true); - // send request - request.send(); +function update_list(infos_array_element){ + // 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 + let items_array = Array.from(infos_array_element.items); + let host = infos_array_element.host; + // 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); + let media_name = document.getElementById(tl_cont_attr.id + j).children[0].innerText; + let media_url_str = "url(https://" + 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(); }); } -}, 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 4 ? 4 : rssi_norm; i" + + "Filename" + + "Duration" + + ""; + infos_array.forEach(function(element){ + html_table += "" + + "" + element + "" + + "" + "00:00" + "" + + "" ; + }); + html_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 function parse_result(command, infos_array) { if (command == "/all/status") { // Requests current status of every instances, especially current media file's name, position and length. // Also retrieves loop and repeat status. - // - // Iterate over array - for (var i = 0, l=infos_array.length; i " + 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 -1 ) { // Requests playlist of every instances - // - if (!freeze_timeline_update){ - console.log("Timeline freeze : " + freeze_timeline_update); - console.log("Infos array : " + infos_array); - for (var i = 0, l=infos_array.length; i" + - "Filename" + - "Duration" + - ""; - for (var j = 0, k=infos_array.length; j" + infos_array[j] + "" + - "" + "00:00" + "" + - "" ; - } - html_table += ""; - document.getElementById("filelist").innerHTML = html_table; + document.getElementById("filelist").innerHTML = update_local_filelist(infos_array); + } else if ( command == "/all/rssi") { // RSSI strength indicator - var signal_color = 40; - var best_rssi = 30; - var worst_rssi = 70; - for (var j = 0, k=infos_array.length; j4?4:rssi_norm; i -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("/browse_local"); @@ -426,7 +455,6 @@ setInterval(send_ajax_cmd, 500, "/all/status"); setTimeout(send_ajax_cmd, 1000, "/all/list"); setTimeout(send_ajax_cmd, 3000, "/all/browse"); setInterval(send_ajax_cmd, 20000, "/all/rssi"); -//~ setInterval(update_statusall_content, 1000); -setTimeout(scan_hosts, scan_interval); +setTimeout(scan_hosts, currentUser.scan_interval);