Add video thumbnails, pep8, timeline overhaul
This commit is contained in:
parent
d8d8793807
commit
a6d6072ac1
66
app.py
66
app.py
|
@ -7,13 +7,10 @@ from flask import Flask, render_template, request, make_response, jsonify
|
||||||
import gettext
|
import gettext
|
||||||
import http.client
|
import http.client
|
||||||
import os
|
import os
|
||||||
# ~ import requests
|
|
||||||
# ~ import socket
|
|
||||||
import ssl
|
import ssl
|
||||||
import subprocess
|
import subprocess
|
||||||
from shutil import which
|
from shutil import which
|
||||||
import sys
|
import sys
|
||||||
# ~ import threading
|
|
||||||
import toml
|
import toml
|
||||||
from urllib.parse import quote, unquote
|
from urllib.parse import quote, unquote
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
@ -41,6 +38,7 @@ gui_l10n = { "str_pilpil_title" : _("Pilpil-server"),
|
||||||
"str_reboot": _("Reboot"),
|
"str_reboot": _("Reboot"),
|
||||||
"str_blink": _("Blink"),
|
"str_blink": _("Blink"),
|
||||||
"str_link": _("Link"),
|
"str_link": _("Link"),
|
||||||
|
"str_refresh": _("Refresh"),
|
||||||
}
|
}
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
@ -122,32 +120,6 @@ else:
|
||||||
|
|
||||||
current_upload = 0
|
current_upload = 0
|
||||||
|
|
||||||
class PilpilClient:
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __init_connection(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __send_request(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def isup(self):
|
|
||||||
pass
|
|
||||||
def rssi(self):
|
|
||||||
pass
|
|
||||||
def status(self):
|
|
||||||
pass
|
|
||||||
def playlist(self):
|
|
||||||
pass
|
|
||||||
def command(self, cmd):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Network/link utilities
|
# Network/link utilities
|
||||||
# https://www.metageek.com/training/resources/understanding-rssi/
|
# https://www.metageek.com/training/resources/understanding-rssi/
|
||||||
|
@ -176,6 +148,7 @@ def send_HTTP_request(listed_host, port, time_out=3, request_="/"):
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def check_hosts(host_list):
|
def check_hosts(host_list):
|
||||||
'''
|
'''
|
||||||
Check hosts in a host list are up and build then return two lists with up/down hosts.
|
Check hosts in a host list are up and build then return two lists with up/down hosts.
|
||||||
|
@ -191,6 +164,7 @@ def check_hosts(host_list):
|
||||||
print(_("{} of {} hosts found.").format(str(len(hosts_up)), hosts_number))
|
print(_("{} of {} hosts found.").format(str(len(hosts_up)), hosts_number))
|
||||||
return hosts_up, hosts_down
|
return hosts_up, hosts_down
|
||||||
|
|
||||||
|
|
||||||
def HTTP_upload(filename, host_local, port, trailing_slash=1):
|
def HTTP_upload(filename, host_local, port, trailing_slash=1):
|
||||||
'''
|
'''
|
||||||
Build HTTP file upload request and send it.
|
Build HTTP file upload request and send it.
|
||||||
|
@ -209,6 +183,7 @@ def HTTP_upload(filename, host_local, port, trailing_slash=1):
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def sync_media_folder(media_folder_local, media_folder_remote, host_local, port, sync_facility=sync_facility):
|
def sync_media_folder(media_folder_local, media_folder_remote, host_local, port, sync_facility=sync_facility):
|
||||||
'''
|
'''
|
||||||
Sync media_folder_local with media_folder_remote using sync_facility
|
Sync media_folder_local with media_folder_remote using sync_facility
|
||||||
|
@ -261,6 +236,7 @@ def sync_media_folder(media_folder_local, media_folder_remote, host_local, port,
|
||||||
print("Transferred size: " + str(total_size))
|
print("Transferred size: " + str(total_size))
|
||||||
return total_size
|
return total_size
|
||||||
|
|
||||||
|
|
||||||
def get_meta_data(host, xml_data, request_="status", m3u_=0):
|
def get_meta_data(host, xml_data, request_="status", m3u_=0):
|
||||||
'''
|
'''
|
||||||
Parse XML response from pilpil-client instance and return a dict of metadata according to request type.
|
Parse XML response from pilpil-client instance and return a dict of metadata according to request type.
|
||||||
|
@ -278,7 +254,10 @@ def get_meta_data(host, xml_data, request_="status", m3u_=0):
|
||||||
if xml_data.findall("./information/category/"):
|
if xml_data.findall("./information/category/"):
|
||||||
for leaf in xml_data.findall("./information/category/"):
|
for leaf in xml_data.findall("./information/category/"):
|
||||||
if leaf.get("name") == "filename":
|
if leaf.get("name") == "filename":
|
||||||
|
if debug:
|
||||||
|
print(leaf.text)
|
||||||
filename = leaf.text
|
filename = leaf.text
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
filename = "N/A"
|
filename = "N/A"
|
||||||
cur_length = int(xml_data.find('length').text)
|
cur_length = int(xml_data.find('length').text)
|
||||||
|
@ -323,17 +302,15 @@ def get_meta_data(host, xml_data, request_="status", m3u_=0):
|
||||||
print(media_infos)
|
print(media_infos)
|
||||||
return media_infos
|
return media_infos
|
||||||
|
|
||||||
|
|
||||||
def get_playlist(host, xml_data, m3u=0):
|
def get_playlist(host, xml_data, m3u=0):
|
||||||
playlist = []
|
playlist = []
|
||||||
item_list = []
|
item_list = []
|
||||||
playlist_duration = 0
|
playlist_duration = 0
|
||||||
# VLC's playlist node name can change according to the locale on the client, so check for this too.
|
# VLC's playlist node name can change according to the locale on the client, so check for this too.
|
||||||
if xml_data.find("./node") and (xml_data.find("./node").get('name') == "Playlist" or xml_data.find("./node").get('name') == _("Playlist")):
|
if xml_data.find("./node") and (xml_data.find("./node").get('name') == "Playlist" or xml_data.find("./node").get('name') == _("Playlist")):
|
||||||
print("HAHAHAH")
|
|
||||||
playlist = xml_data.findall("./node/leaf")
|
playlist = xml_data.findall("./node/leaf")
|
||||||
|
|
||||||
content_format = "{};{};{};"
|
content_format = "{};{};{};"
|
||||||
|
|
||||||
if m3u:
|
if m3u:
|
||||||
m3u_hdr = "#EXTM3U\n"
|
m3u_hdr = "#EXTM3U\n"
|
||||||
m3u_prefix = "#EXTINF:"
|
m3u_prefix = "#EXTINF:"
|
||||||
|
@ -367,6 +344,7 @@ def get_playlist(host, xml_data, m3u=0):
|
||||||
print(playlist_overview)
|
print(playlist_overview)
|
||||||
return playlist_overview
|
return playlist_overview
|
||||||
|
|
||||||
|
|
||||||
def send_pilpil_command(host, arg0, arg1, arg2):
|
def send_pilpil_command(host, arg0, arg1, arg2):
|
||||||
'''
|
'''
|
||||||
Builds a pilpil request according to args, send it and return parsed result.
|
Builds a pilpil request according to args, send it and return parsed result.
|
||||||
|
@ -399,9 +377,10 @@ def send_pilpil_command(host, arg0, arg1, arg2):
|
||||||
# Add 'id' url parameter
|
# Add 'id' url parameter
|
||||||
HTTP_request = HTTP_request + "&id=" + arg1
|
HTTP_request = HTTP_request + "&id=" + arg1
|
||||||
if (arg0 == "sort") or (arg0 == "move"):
|
if (arg0 == "sort") or (arg0 == "move"):
|
||||||
# Add 'val' url parameter
|
# Add 'val' url parameter for "sort"
|
||||||
# val possible values: id, title, title nodes first, artist, genre, random, duration, title numeric, album
|
# val possible values: id, title, title nodes first, artist, genre, random, duration, title numeric, album
|
||||||
# https://github.com/videolan/vlc/blob/3.0.17.4/modules/lua/libs/playlist.c#L353-L362
|
# https://github.com/videolan/vlc/blob/3.0.17.4/modules/lua/libs/playlist.c#L353-L362
|
||||||
|
# For "move", 'val' should be the id of the playlist item we want to move arg1 after.
|
||||||
HTTP_request = HTTP_request + "&val=" + escape_str(arg2)
|
HTTP_request = HTTP_request + "&val=" + escape_str(arg2)
|
||||||
elif arg0 == "seek":
|
elif arg0 == "seek":
|
||||||
HTTP_request = HTTP_request + "&val=" + arg1
|
HTTP_request = HTTP_request + "&val=" + arg1
|
||||||
|
@ -412,6 +391,7 @@ def send_pilpil_command(host, arg0, arg1, arg2):
|
||||||
# Send request and get data response
|
# Send request and get data response
|
||||||
data = send_HTTP_request(host, port_, time_out=3, request_=HTTP_request)
|
data = send_HTTP_request(host, port_, time_out=3, request_=HTTP_request)
|
||||||
if debug:
|
if debug:
|
||||||
|
if data:
|
||||||
print(str(host) + " - data length:" + str(len(data)))
|
print(str(host) + " - data length:" + str(len(data)))
|
||||||
if not data:
|
if not data:
|
||||||
print("No data was received.")
|
print("No data was received.")
|
||||||
|
@ -425,13 +405,14 @@ def send_pilpil_command(host, arg0, arg1, arg2):
|
||||||
print("Metadata:" + str(metadata))
|
print("Metadata:" + str(metadata))
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
# Utilities
|
# Utilities
|
||||||
def list_local_media_files(folder):
|
def list_local_media_files(folder):
|
||||||
'''
|
'''
|
||||||
List files in folder which extension is allowed (exists in media_exts).
|
List files in folder which extension is allowed (exists in media_exts).
|
||||||
'''
|
'''
|
||||||
if os.path.exists(folder):
|
if os.path.exists(folder):
|
||||||
files = os.listdir(folder);
|
files = os.listdir(folder)
|
||||||
medias = []
|
medias = []
|
||||||
for fd in files:
|
for fd in files:
|
||||||
if len(fd.split('.')) > 1:
|
if len(fd.split('.')) > 1:
|
||||||
|
@ -440,6 +421,8 @@ def list_local_media_files(folder):
|
||||||
return medias
|
return medias
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
# /requests/status.xml?command=in_enqueue&input=file:///home/pi/tst1.mp4
|
# /requests/status.xml?command=in_enqueue&input=file:///home/pi/tst1.mp4
|
||||||
def write_M3U(m3u_content: str, host: str):
|
def write_M3U(m3u_content: str, host: str):
|
||||||
'''
|
'''
|
||||||
|
@ -451,20 +434,24 @@ def write_M3U(m3u_content : str, host : str):
|
||||||
fd.close()
|
fd.close()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def escape_str(uri):
|
def escape_str(uri):
|
||||||
'''
|
'''
|
||||||
Replace spaces with %20 for http urls
|
Replace spaces with %20 for http urls
|
||||||
'''
|
'''
|
||||||
return uri.replace(" ", "%20")
|
return uri.replace(" ", "%20")
|
||||||
|
|
||||||
|
|
||||||
def sec2min(duration):
|
def sec2min(duration):
|
||||||
'''
|
'''
|
||||||
Convert seconds to min:sec format.
|
Convert seconds to min:sec format.
|
||||||
'''
|
'''
|
||||||
return ('%02d:%02d' % (duration / 60, duration % 60))
|
return ('%02d:%02d' % (duration / 60, duration % 60))
|
||||||
|
|
||||||
|
|
||||||
status_message = _("Idle")
|
status_message = _("Idle")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def main():
|
def main():
|
||||||
status_message = _("Searching network for live hosts...")
|
status_message = _("Searching network for live hosts...")
|
||||||
|
@ -476,16 +463,19 @@ def main():
|
||||||
}
|
}
|
||||||
return render_template('main.html', **templateData)
|
return render_template('main.html', **templateData)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/scan")
|
@app.route("/scan")
|
||||||
def scan():
|
def scan():
|
||||||
global hosts_available, hosts_unavailable
|
global hosts_available, hosts_unavailable
|
||||||
hosts_available, hosts_unavailable = check_hosts(hosts)
|
hosts_available, hosts_unavailable = check_hosts(hosts)
|
||||||
return [hosts_available, hosts_unavailable]
|
return [hosts_available, hosts_unavailable]
|
||||||
|
|
||||||
|
|
||||||
@app.route("/browse_local")
|
@app.route("/browse_local")
|
||||||
def browse():
|
def browse():
|
||||||
return list_local_media_files(media_folder_local)
|
return list_local_media_files(media_folder_local)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/sync/<host>")
|
@app.route("/sync/<host>")
|
||||||
def sync(host):
|
def sync(host):
|
||||||
# TODO: Add feedback for transfer in GUI
|
# TODO: Add feedback for transfer in GUI
|
||||||
|
@ -495,12 +485,11 @@ def sync(host):
|
||||||
elif host == "all":
|
elif host == "all":
|
||||||
for hostl in hosts_available:
|
for hostl in hosts_available:
|
||||||
size += sync_media_folder(media_folder_local, media_folder_remote_expanded, hostl, cmd_port)
|
size += sync_media_folder(media_folder_local, media_folder_remote_expanded, hostl, cmd_port)
|
||||||
# ~ th = threading.Thread(target=blink_pi, args=(16,))
|
|
||||||
# ~ th.start()
|
|
||||||
else:
|
else:
|
||||||
size = sync_media_folder(media_folder_local, media_folder_remote_expanded, host, cmd_port)
|
size = sync_media_folder(media_folder_local, media_folder_remote_expanded, host, cmd_port)
|
||||||
return str(size)
|
return str(size)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<host>/<arg0>/", defaults={"arg1": "null", "arg2": "null"})
|
@app.route("/<host>/<arg0>/", defaults={"arg1": "null", "arg2": "null"})
|
||||||
@app.route("/<host>/<arg0>/<arg1>/", defaults={"arg2": "null"})
|
@app.route("/<host>/<arg0>/<arg1>/", defaults={"arg2": "null"})
|
||||||
@app.route("/<host>/<arg0>/<arg1>/<arg2>")
|
@app.route("/<host>/<arg0>/<arg1>/<arg2>")
|
||||||
|
@ -508,7 +497,7 @@ def action(host, arg0, arg1, arg2):
|
||||||
status_message = "Idle"
|
status_message = "Idle"
|
||||||
|
|
||||||
if (arg0 not in cmd_player) and (arg0 not in cmd_server):
|
if (arg0 not in cmd_player) and (arg0 not in cmd_server):
|
||||||
status_message = "<p>{}</p>".format(_("Wrong command"))
|
status_message = "E:{}".format(_("Wrong command"))
|
||||||
return status_message
|
return status_message
|
||||||
|
|
||||||
if host == "all":
|
if host == "all":
|
||||||
|
@ -521,13 +510,14 @@ def action(host, arg0, arg1, arg2):
|
||||||
status_message = "<p>{}</p>".format("Host is not reachable")
|
status_message = "<p>{}</p>".format("Host is not reachable")
|
||||||
else:
|
else:
|
||||||
# Send request to specified host
|
# Send request to specified host
|
||||||
status_message = send_pilpil_command(host, arg0, arg1, arg2)
|
status_message = [send_pilpil_command(host, arg0, arg1, arg2)]
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
print(status_message)
|
print(status_message)
|
||||||
|
|
||||||
return status_message
|
return status_message
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# ~ app.run()
|
# ~ app.run()
|
||||||
serve(app, host='127.0.0.1', port=8080)
|
serve(app, host='127.0.0.1', port=8080)
|
|
@ -38,9 +38,22 @@ sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
||||||
* Add media folder sync (scp, rsync, http upload)
|
* 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
|
* Install script ; Wifi setup, generate/install SSH keys/ nginx SSL cert/key fore each host, change hostname, static IPs
|
||||||
|
|
||||||
## 0.5 : 2022-10-XX-videopi.img.xz
|
## 0.5 : 2022-11-17-videopi.img.xz
|
||||||
md5 :
|
md5 : d1dd7404ec05d16c8a4179370a214821
|
||||||
sha256 :
|
sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
||||||
|
|
||||||
|
* webgui : Increase live host scan when host first found
|
||||||
|
* webgui : Btn hover/press fixed
|
||||||
|
* webgui : File selection/enqueuing
|
||||||
|
* server : Disable ssl verif for isup()
|
||||||
|
* server : Blink command
|
||||||
|
* client : rt8821cu driver is back
|
||||||
|
* client : Use safe overclocking settings for rpi1, 3, 4, better memory split
|
||||||
|
* setup : Split git repo from pilpil-server
|
||||||
|
* setup : Generate/install random http auth secret at setup
|
||||||
|
* setup : Add dry, help flags
|
||||||
|
* FR localisation for server, client and install script
|
||||||
|
* pep8ification
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,30 +71,28 @@ sha256 :
|
||||||
|
|
||||||
# DOING NEXT :
|
# DOING NEXT :
|
||||||
|
|
||||||
|
|
||||||
# DONE :
|
# DONE :
|
||||||
* webgui : Increase live host scan when host first found
|
|
||||||
* webgui : Btn hover/press fixed
|
|
||||||
* webgui : file selection/enqueuing
|
|
||||||
* server : Disable ssl verif for isup()
|
|
||||||
* server : Add blink command
|
|
||||||
* client : rt8821cu driver back
|
|
||||||
* client : Use safe overclocking settings for rpi1, 3, 4, better memory split
|
|
||||||
* setup : Split git repo from pilpil-server
|
|
||||||
* setup : Generate/install http auth secret at setup
|
|
||||||
* setup : Add dry, help flags
|
|
||||||
* FR localisation for server, client and install script
|
|
||||||
* pep8ification
|
|
||||||
|
|
||||||
|
* webgui: l10n
|
||||||
|
* 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)
|
||||||
|
|
||||||
# OTHER:
|
|
||||||
* get_client_rssi.sh on server
|
|
||||||
|
|
||||||
# TODO :
|
# TODO :
|
||||||
|
|
||||||
* ~ python sanitization
|
* webgui: remove file from timeline (drag&drop to bin?)
|
||||||
|
* webgui: file sync UI freeze/status/progress bar
|
||||||
|
*
|
||||||
* ~ Test with several rpis
|
* ~ Test with several rpis
|
||||||
* ? Scripts hotspot linux/win/mac
|
* ? Scripts hotspot linux : nmcli
|
||||||
* ? Config sync
|
win : https://github.com/JamesCullum/Windows-Hotspot
|
||||||
|
mac : https://apple.stackexchange.com/questions/2488/start-stop-internet-sharing-from-a-script
|
||||||
* ! Remove git personal details/resolv.conf, remove authorized_keys, ssh config, clean home, re-enable ssh pw login
|
* ! Remove git personal details/resolv.conf, remove authorized_keys, ssh config, clean home, re-enable ssh pw login
|
||||||
* ~ Doc
|
* ~ Doc
|
||||||
|
|
||||||
|
# OTHER:
|
||||||
|
* get_client_rssi.sh on server
|
||||||
|
|
|
@ -83,6 +83,10 @@ msgstr ""
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../app.py:43
|
||||||
|
msgid "Refresh"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: ../app.py:52
|
#: ../app.py:52
|
||||||
msgid "Found configuration file in {}"
|
msgid "Found configuration file in {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
Binary file not shown.
|
@ -90,6 +90,10 @@ msgstr "Clignoter"
|
||||||
msgid "Link"
|
msgid "Link"
|
||||||
msgstr "Lien"
|
msgstr "Lien"
|
||||||
|
|
||||||
|
#: ../app.py:43
|
||||||
|
msgid "Refresh"
|
||||||
|
msgstr "Rafraîchir"
|
||||||
|
|
||||||
#: app.py:22
|
#: app.py:22
|
||||||
msgid "Found configuration file in {}"
|
msgid "Found configuration file in {}"
|
||||||
msgstr "Fichier de configuration trouvé dans {}"
|
msgstr "Fichier de configuration trouvé dans {}"
|
||||||
|
|
405
static/script.js
405
static/script.js
|
@ -1,84 +1,128 @@
|
||||||
// Timeline drag and drop
|
// Timeline drag and drop elements default attributes
|
||||||
const tl_cont_attr = {id:"tl_cont", ondrop: "drop(event, this)", ondragover:"allowDrop(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)"};
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
|
var DEBUG = 0;
|
||||||
|
var cmd_port = "8888";
|
||||||
var timeline_color_cursor = "#FF8839";
|
var timeline_color_cursor = "#FF8839";
|
||||||
var timeline_color_bg = "#2EB8E6";
|
var timeline_color_bg = "#2EB8E600";
|
||||||
var scanInterval = 3000;
|
var scan_interval = 3000;
|
||||||
var status_all = "Searching network for live hosts..."
|
var status_all = "Searching network for live hosts...";
|
||||||
// Global vars
|
// Global vars
|
||||||
var src_id = "";
|
var src_elem = "";
|
||||||
var medias_status = {};
|
var medias_status = {};
|
||||||
var fileButtons = [];
|
var fileButtons = [];
|
||||||
|
var freeze_timeline_update = 0;
|
||||||
|
|
||||||
function updatePlaylist(){
|
function sleep(ms) {
|
||||||
var new_list = [];
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
var media_count = document.getElementById("timeline").children.length;
|
}
|
||||||
|
|
||||||
|
async function update_playlist(host){
|
||||||
|
var media_count = document.getElementById("timeline_" + host).children.length;
|
||||||
|
// Reversed loop
|
||||||
for (i=media_count,l=0;i>l;i--) {
|
for (i=media_count,l=0;i>l;i--) {
|
||||||
toMove = document.getElementById("timeline").children[i-1].children[0].getAttribute("media_id");
|
//~ for (i=0,l=media_count;i<l;i++) {
|
||||||
console.log(toMove);
|
// Find current's timeline element children's 'media_id' value
|
||||||
sendCmd("/all/move/" + toMove + "/1");
|
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
|
||||||
|
// really means moving to the the very start of the playlist.
|
||||||
|
send_ajax_cmd("/" + host + "/move/" + toShift + "/1");
|
||||||
|
await sleep(80);
|
||||||
}
|
}
|
||||||
|
// Un-freeze timeline update flag
|
||||||
|
freeze_timeline_update = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findTargetIndex(element, index, array, thisArg){
|
function find_target_index(element, index, array, thisArg){
|
||||||
if (element == this) {
|
if (element == this) {
|
||||||
return index + 1;
|
return index + 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
function moveElements(target) {
|
function shift_elements(target_elem) {
|
||||||
var elem_list = Array.from(src_id.parentElement.children);
|
// 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;
|
var elem_list_slice = elem_list;
|
||||||
var source_idx = elem_list.findIndex(findTargetIndex, src_id);
|
// Find indexes of source and target elements in the timeline
|
||||||
var target_idx = elem_list.findIndex(findTargetIndex, target);
|
var source_idx = elem_list.findIndex(find_target_index, src_elem);
|
||||||
|
var target_idx = elem_list.findIndex(find_target_index, target_elem);
|
||||||
var idx;
|
var idx;
|
||||||
|
// idx is the index where the elements should be inserted back
|
||||||
|
// Target element is on the left of the source element
|
||||||
if (source_idx < target_idx){
|
if (source_idx < target_idx){
|
||||||
elem_list_slice = elem_list.slice(source_idx + 1, target_idx + 1);
|
elem_list_slice = elem_list.slice(source_idx + 1, target_idx + 1);
|
||||||
idx = source_idx;
|
idx = source_idx;
|
||||||
} else {
|
} else {
|
||||||
|
// Target element is on the right of the source element
|
||||||
elem_list_slice = elem_list.slice(target_idx, source_idx );
|
elem_list_slice = elem_list.slice(target_idx, source_idx );
|
||||||
idx = target_idx + 1;
|
idx = target_idx + 1;
|
||||||
}
|
}
|
||||||
|
// Shift elements according to idx
|
||||||
for (i=0, l=elem_list_slice.length; i<l;i++){
|
for (i=0, l=elem_list_slice.length; i<l;i++){
|
||||||
elem_list[idx + i].appendChild(elem_list_slice[i].children[0]);
|
elem_list[idx + i].appendChild(elem_list_slice[i].children[0]);
|
||||||
}
|
}
|
||||||
return target;
|
return target_elem;
|
||||||
};
|
}
|
||||||
|
|
||||||
function allowDrop(ev) {
|
function allow_drop(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
drop_id = ev.dataTransfer.getData("text");
|
drop_id = ev.dataTransfer.getData("text");
|
||||||
};
|
}
|
||||||
|
|
||||||
function drag(ev, source) {
|
function drag(ev, source) {
|
||||||
src_id = ev.target.parentElement;
|
// Freeze timeline update flag
|
||||||
|
freeze_timeline_update = 1;
|
||||||
|
src_elem = ev.target.parentElement;
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log(src_elem.children[0].getAttribute("media_id") + " : " + src_elem.children[0].innerText);
|
||||||
|
}
|
||||||
ev.dataTransfer.setData("text", ev.target.id);
|
ev.dataTransfer.setData("text", ev.target.id);
|
||||||
};
|
}
|
||||||
|
|
||||||
function drop(ev, target) {
|
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.
|
||||||
|
//
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
// Get dragged element id
|
||||||
var data = ev.dataTransfer.getData("text");
|
var data = ev.dataTransfer.getData("text");
|
||||||
if (src_id.id != target.id) {
|
// Only shift if not dropping on self
|
||||||
dropTarget = moveElements(target);
|
if (src_elem.id != target_elem.id) {
|
||||||
|
dropTarget = shift_elements(target_elem);
|
||||||
if (dropTarget) {
|
if (dropTarget) {
|
||||||
|
//
|
||||||
dropTarget.appendChild(document.getElementById(data));
|
dropTarget.appendChild(document.getElementById(data));
|
||||||
updatePlaylist();
|
// Update VLC playlist according to UI timeline
|
||||||
|
// 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");
|
||||||
|
//~ setTimeout(function(){ freeze_timeline_update = 0; console.log("shwing!"); }, 2000);
|
||||||
|
|
||||||
function adjustTl() {
|
}
|
||||||
var child = document.getElementById('timeline').children;
|
|
||||||
|
function adjust_timeline() {
|
||||||
|
// Adapt timeline's UI elements to fit the width of their parent container.
|
||||||
|
//
|
||||||
|
//~ var child = document.getElementById('timeline').children;
|
||||||
|
var child = document.querySelector('[id^="timeline_"]').children;
|
||||||
var divWidth = 100 / child.length;
|
var divWidth = 100 / child.length;
|
||||||
for (i=0, l=child.length;i<l;i++) {
|
for (i=0, l=child.length;i<l;i++) {
|
||||||
child[i].style.width= divWidth + "%";
|
child[i].style.width= divWidth + "%";
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function addAttr(id, attr, val , child=-1) {
|
function add_HTML_attr(id, attr, val , child=-1) {
|
||||||
|
// 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'
|
||||||
var elem = document.getElementById(id);
|
var elem = document.getElementById(id);
|
||||||
if (child>-1){
|
if (child>-1){
|
||||||
elem = elem.children[child];
|
elem = elem.children[child];
|
||||||
|
@ -86,13 +130,19 @@ function addAttr(id, attr, val , child=-1) {
|
||||||
var att = document.createAttribute(attr);
|
var att = document.createAttribute(attr);
|
||||||
att.value = val;
|
att.value = val;
|
||||||
elem.setAttributeNode(att);
|
elem.setAttributeNode(att);
|
||||||
};
|
}
|
||||||
|
|
||||||
function addElement(type, attr, meta = 0, j = 0){
|
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
|
||||||
|
// 'meta' should be an array
|
||||||
|
// 'j' is used to pass an increment value when used in a loop
|
||||||
|
//
|
||||||
var elem = document.createElement(type);
|
var elem = document.createElement(type);
|
||||||
var keys_array = Object.keys(attr);
|
var keys_array = Object.keys(attr);
|
||||||
for (i=0, l=keys_array.length;i<l;i++) {
|
for (i=0, l=keys_array.length;i<l;i++) {
|
||||||
var att = document.createAttribute(keys_array[i]);
|
var att = document.createAttribute(keys_array[i]);
|
||||||
|
// First iteration
|
||||||
if(!i){
|
if(!i){
|
||||||
att.value = Object.values(attr)[i]+j;
|
att.value = Object.values(attr)[i]+j;
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,45 +150,37 @@ function addElement(type, attr, meta = 0, j = 0){
|
||||||
}
|
}
|
||||||
elem.setAttributeNode(att);
|
elem.setAttributeNode(att);
|
||||||
}
|
}
|
||||||
// Set playlist id attribute
|
// Set media_id attribute if metadata is provided
|
||||||
if (meta) {
|
if (meta) {
|
||||||
att = document.createAttribute("media_id");
|
attribute = document.createAttribute("media_id");
|
||||||
att.value = meta[0];
|
attribute.value = meta[0];
|
||||||
elem.setAttributeNode(att);
|
elem.setAttributeNode(attribute);
|
||||||
}
|
}
|
||||||
// Get filename
|
// Get filename
|
||||||
elem.innerText = meta[1];
|
elem.innerText = meta[1];
|
||||||
return elem;
|
return elem;
|
||||||
};
|
}
|
||||||
// Bouttons de commande
|
// Bouttons de commande
|
||||||
addEventListener("DOMContentLoaded", function() {
|
addEventListener("DOMContentLoaded", function() {
|
||||||
sendCmd("/scan");
|
//~ send_ajax_cmd("/scan");
|
||||||
sendCmd("/all/list");
|
//~ send_ajax_cmd("/browse_local");
|
||||||
sendCmd("/browse_local");
|
//~ setTimeout(send_ajax_cmd, 3000, "/all/browse");
|
||||||
setTimeout(sendCmd, 3000, "/all/browse");
|
//~ setTimeout(send_ajax_cmd, 4000, "/all/rssi");
|
||||||
setTimeout(sendCmd, 4000, "/all/rssi");
|
//~ setTimeout(send_ajax_cmd, 1000, "/all/list");
|
||||||
adjustTl();
|
adjust_timeline();
|
||||||
|
|
||||||
// Get filename when selected in table
|
// Get all elements with class 'command'
|
||||||
//~ for (var i=0, l=fileButtons.length; i<l; i++) {
|
|
||||||
//~ var selected_file = fileButtons[i];
|
|
||||||
//~ // Sur un click
|
|
||||||
//~ selected_file.addEventListener("click", function(event) {
|
|
||||||
//~ // On intercepte le signal
|
|
||||||
//~ event.preventDefault();
|
|
||||||
//~ }
|
|
||||||
//~ }
|
|
||||||
// Tous les elements avec la classe ".command"
|
|
||||||
var commandButtons = document.querySelectorAll(".command");
|
var commandButtons = document.querySelectorAll(".command");
|
||||||
for (var i=0, l=commandButtons.length; i<l; i++) {
|
for (var i=0, l=commandButtons.length; i<l; i++) {
|
||||||
var button = commandButtons[i];
|
var button = commandButtons[i];
|
||||||
// Sur un click
|
// Listen for clicks
|
||||||
button.addEventListener("click", function(event) {
|
button.addEventListener("click", function(event) {
|
||||||
// On intercepte le signal
|
// Discard signal
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// On recupere la valeur de value="" sur le bouton
|
// Get value=""
|
||||||
var clickedButton = event.currentTarget;
|
var clickedButton = event.currentTarget;
|
||||||
var command = clickedButton.value;
|
var command = clickedButton.value;
|
||||||
|
// Proceed accordingly
|
||||||
if ( command.indexOf("/reboot" ) > -1 || command.indexOf("/poweroff") > -1 ) {
|
if ( command.indexOf("/reboot" ) > -1 || command.indexOf("/poweroff") > -1 ) {
|
||||||
if ( !confirm("Êtes vous certain de vouloir effectuer cette action ?") ) {
|
if ( !confirm("Êtes vous certain de vouloir effectuer cette action ?") ) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -149,120 +191,118 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
|
|
||||||
} else if ( command.indexOf("/sort") > -1 ){
|
} else if ( command.indexOf("/sort") > -1 ){
|
||||||
if (command.indexOf('/1/') > -1 ) {
|
if (command.indexOf('/1/') > -1 ) {
|
||||||
clickedButton.value = clickedButton.value.replace('/1/','/0/')
|
clickedButton.value = clickedButton.value.replace('/1/','/0/');
|
||||||
} else {
|
} else {
|
||||||
clickedButton.value = clickedButton.value.replace('/0/','/1/')
|
clickedButton.value = clickedButton.value.replace('/0/','/1/');
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( command.indexOf("/move") > -1 ) {
|
}
|
||||||
const test_array = [21,19,20];
|
|
||||||
for (i=test_array.length, l=0;i>l;i--){
|
|
||||||
console.log(test_array[i-1]);
|
|
||||||
sendCmd("/all/move/" + test_array[i-1] + "/1");
|
|
||||||
};
|
|
||||||
sendCmd("/all/list");
|
|
||||||
//setInterval( sendCmd, scanInterval, "/all/move/16/1");
|
|
||||||
};
|
|
||||||
|
|
||||||
// On envoie la commande en AJAX
|
// AJAX request
|
||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
if ( command == "/scan") {
|
if ( command == "/scan") {
|
||||||
request.onload = sendCmd(command);
|
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" ) {
|
} else if ( command == "/sync/all" ) {
|
||||||
status_all = "Syncing files..."
|
status_all = "Syncing files...";
|
||||||
request.onload = sendCmd("/sync/status");
|
request.onload = send_ajax_cmd("/sync/status");
|
||||||
};
|
}
|
||||||
|
|
||||||
// On construit la commande
|
// build request
|
||||||
request.open("GET", command, true);
|
request.open("GET", command, true);
|
||||||
// et on l'envoie
|
// send request
|
||||||
request.send();
|
request.send();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
// Affichage des infos
|
// Metadata display
|
||||||
function parseResult(command, infos_array) {
|
function parse_result(command, infos_array) {
|
||||||
switch (command) {
|
if (command == "/all/status") {
|
||||||
case "/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
|
// Iterate over array
|
||||||
for (var i = 0, l=infos_array.length; i<l; i++) {
|
for (var i = 0, l=infos_array.length; i<l; i++) {
|
||||||
// Get filename, time/length
|
// Get filename, time/length
|
||||||
if (infos_array[i].status) {
|
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;
|
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;
|
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 {
|
} else {
|
||||||
document.getElementById("status_"+infos_array[i].host).innerHTML = "<br><br>";
|
document.getElementById("status_"+infos_array[i].host).innerHTML = "<br><br>";
|
||||||
}
|
}
|
||||||
|
// Toggle loop indicator
|
||||||
if (infos_array[i].loop == "true") {
|
if (infos_array[i].loop == "true") {
|
||||||
document.getElementById("loop_ind_" + infos_array[i].host).style.backgroundColor = "#78E738"
|
document.getElementById("loop_ind_" + infos_array[i].host).style.backgroundColor = "#78E738";
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("loop_ind_" + infos_array[i].host).style.backgroundColor = "#A42000"
|
document.getElementById("loop_ind_" + infos_array[i].host).style.backgroundColor = "#A42000";
|
||||||
};
|
}
|
||||||
// Toggle repeat indicator
|
// Toggle repeat indicator
|
||||||
if (infos_array[i].repeat == "true") {
|
if (infos_array[i].repeat == "true") {
|
||||||
document.getElementById("repeat_ind_" + infos_array[i].host).style.backgroundColor = "#78E738"
|
document.getElementById("repeat_ind_" + infos_array[i].host).style.backgroundColor = "#78E738";
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("repeat_ind_" + infos_array[i].host).style.backgroundColor = "#A42000"
|
document.getElementById("repeat_ind_" + infos_array[i].host).style.backgroundColor = "#A42000";
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
break;
|
} else if ( command.indexOf("/list") > -1 ) {
|
||||||
case "/all/list":
|
// 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<l; i++) {
|
for (var i = 0, l=infos_array.length; i<l; i++) {
|
||||||
// Fill Playlist infos
|
// 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;
|
//~ document.getElementById("playlist_"+infos_array[i].host).innerHTML = infos_array[i].leng + " item(s) in playlist - " + infos_array[i].duration;
|
||||||
// Build html table and timeline
|
// Populate timeline according to the content of the playlist
|
||||||
|
// Get returned items as an array
|
||||||
var items_array = Array.from(infos_array[i].items);
|
var items_array = Array.from(infos_array[i].items);
|
||||||
//console.log(items_array.length);
|
//console.log(items_array.length);
|
||||||
|
// If playlist is empty, remove all media divs in the timeline UI.
|
||||||
if (items_array.length == 0){
|
if (items_array.length == 0){
|
||||||
var child_list = Array.from(document.getElementById("timeline").children);
|
var child_list = Array.from(document.getElementById("timeline_" + infos_array[i].host).children);
|
||||||
for(i=0,l=child_list.length;i<l;i++){
|
for(x=0,y=child_list.length;x<y;x++){
|
||||||
document.getElementById("timeline").removeChild(child_list[i]);
|
document.getElementById("timeline_" + infos_array[i].host).removeChild(child_list[x]);
|
||||||
};
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//~ var html_table = "<table>" +
|
|
||||||
//~ "<tr>" +
|
|
||||||
//~ "<th>Id</th>" +
|
|
||||||
//~ "<th>Filename</th>" +
|
|
||||||
//~ "<th>Duration</th>" +
|
|
||||||
//~ "</tr>";
|
|
||||||
for (var j = 0, k=items_array.length; j<k; j++) {
|
for (var j = 0, k=items_array.length; j<k; j++) {
|
||||||
// Table
|
|
||||||
item_meta = items_array[j].split(';');
|
|
||||||
//~ html_table += "<tr>" +
|
|
||||||
//~ "<td>" + item_meta[0] + "</td>" +
|
|
||||||
//~ "<td>" + item_meta[1] + "</td>" +
|
|
||||||
//~ "<td>" + item_meta[2] + "</td>" +
|
|
||||||
//~ "</tr>" ;
|
|
||||||
// Timeline
|
// Timeline
|
||||||
var child_node = addElement("div", tl_drag_attr, item_meta, j);
|
item_meta = items_array[j].split(';');
|
||||||
var len = document.getElementById("timeline").children.length;
|
var child_node = add_HTML_element("div", tl_drag_attr, item_meta, j);
|
||||||
addAttr("timeline", "length", len);
|
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 ) {
|
if ( len < items_array.length ) {
|
||||||
document.getElementById("timeline").appendChild( addElement("div", tl_cont_attr, 0, len) );
|
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);
|
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 elements width
|
||||||
adjustTl();
|
adjust_timeline();
|
||||||
// Highlight currently playing element
|
|
||||||
if (item_meta[3] != ""){
|
|
||||||
document.getElementById(tl_cont_attr.id + j).children[0].style.borderBottom = "4px solid " + timeline_color_cursor;
|
|
||||||
document.getElementById(tl_cont_attr.id + j).children[0].style.fontWeight = "bold";
|
|
||||||
var pos = medias_status[item_meta[0]] * 100;
|
|
||||||
//~ pos = pos.toPrecision(2);
|
|
||||||
var pos1 = pos-1 + "%";
|
|
||||||
pos = pos + "%";
|
|
||||||
//console.log( "linear-gradient(90deg," + timeline_color2 + " " + pos1 + ", " + timeline_color1 + " " + pos + ", " + timeline_color2 + " " + pos + ")" );
|
|
||||||
document.getElementById(tl_cont_attr.id + j).children[0].style.background = "linear-gradient(90deg," + timeline_color_bg + " " + pos1 + ", " + timeline_color_cursor + " " + pos + ", " + timeline_color_bg + " " + pos + ")";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//~ html_table += "</table>";
|
}
|
||||||
//~ document.getElementById("playlist_"+infos_array[i].host).innerHTML += html_table;
|
} else if ( command == "/scan") {
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "/scan":
|
|
||||||
var host_up = infos_array[0];
|
var host_up = infos_array[0];
|
||||||
var host_down = infos_array[1];
|
var host_down = infos_array[1];
|
||||||
for ( var i=0, l=host_up.length; i<l; i++){
|
for ( var i=0, l=host_up.length; i<l; i++){
|
||||||
|
@ -272,14 +312,14 @@ function parseResult(command, infos_array) {
|
||||||
document.getElementById(host_down[i]).style.display = 'none';
|
document.getElementById(host_down[i]).style.display = 'none';
|
||||||
}
|
}
|
||||||
if (host_up.length) {
|
if (host_up.length) {
|
||||||
scanInterval = 10000;
|
scan_interval = 10000;
|
||||||
//~ document.getElementById("status_all").innerHTML = "Scan interval set to " + scanInterval;
|
//~ document.getElementById("status_all").innerHTML = "Scan interval set to " + scan_interval;
|
||||||
status_all = "Scan interval set to " + scanInterval;
|
status_all = "Scan interval set to " + scan_interval;
|
||||||
}
|
}
|
||||||
//~ document.getElementById("status_all").innerHTML = host_up.length + " client(s).";
|
//~ document.getElementById("status_all").innerHTML = host_up.length + " client(s).";
|
||||||
status_all = host_up.length + " client(s).";
|
status_all = host_up.length + " client(s).";
|
||||||
break;
|
} else if ( command == "/browse_local") {
|
||||||
case "/browse_local":
|
// Display local media files in a table
|
||||||
var html_table = "<table>" +
|
var html_table = "<table>" +
|
||||||
"<tr>" +
|
"<tr>" +
|
||||||
"<th>Filename</th>" +
|
"<th>Filename</th>" +
|
||||||
|
@ -293,8 +333,8 @@ function parseResult(command, infos_array) {
|
||||||
}
|
}
|
||||||
html_table += "</table>";
|
html_table += "</table>";
|
||||||
document.getElementById("filelist").innerHTML = html_table;
|
document.getElementById("filelist").innerHTML = html_table;
|
||||||
break;
|
} else if ( command == "/all/rssi") {
|
||||||
case "/all/rssi":
|
// RSSI strength indicator
|
||||||
var signal_color = 40;
|
var signal_color = 40;
|
||||||
var best_rssi = 30;
|
var best_rssi = 30;
|
||||||
var worst_rssi = 70;
|
var worst_rssi = 70;
|
||||||
|
@ -304,90 +344,89 @@ function parseResult(command, infos_array) {
|
||||||
// Reset to grey
|
// Reset to grey
|
||||||
for (i=0, l=4; i<l;i++) {
|
for (i=0, l=4; i<l;i++) {
|
||||||
document.getElementById("wl_"+i).style.backgroundColor = "hsl(0, 0%, 65%)";
|
document.getElementById("wl_"+i).style.backgroundColor = "hsl(0, 0%, 65%)";
|
||||||
};
|
}
|
||||||
// Color it
|
// Color it
|
||||||
for (i=0, l=rssi_norm>4?4:rssi_norm; i<l;i++) {
|
for (i=0, l=rssi_norm>4?4:rssi_norm; i<l;i++) {
|
||||||
document.getElementById("wl_"+i).style.backgroundColor = "hsl(" + signal_color + ", 100%, 50%)";
|
document.getElementById("wl_"+i).style.backgroundColor = "hsl(" + signal_color + ", 100%, 50%)";
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
break;
|
} else if ( command == "/all/browse") {
|
||||||
case "/all/browse":
|
// Display remote media files in a table
|
||||||
for (var i=0, l=infos_array.length; i<l; i++) {
|
for (var i=0, l=infos_array.length; i<l; i++) {
|
||||||
hosts = Object.keys(infos_array[i]);
|
hosts = Object.keys(infos_array[i]);
|
||||||
//~ console.log(keys);
|
//~ console.log(keys);
|
||||||
//~ var html_table = "<table id='file_sel_" + hosts + "'>" +
|
|
||||||
//~ "<tr>" +
|
|
||||||
//~ "<th>Filename</th>" +
|
|
||||||
//"<th>Size</th>" +
|
|
||||||
//~ "</tr>";
|
|
||||||
for ( var j=0, k=hosts.length;j<k;j++ ) {
|
for ( var j=0, k=hosts.length;j<k;j++ ) {
|
||||||
keys_ = Object.keys(infos_array[i][hosts[j]]);
|
keys_ = Object.keys(infos_array[i][hosts[j]]);
|
||||||
//~ console.log(infos_array[i][hosts[j]]);
|
//~ console.log(infos_array[i][hosts[j]]);
|
||||||
for ( var m=0, n=keys_.length;m<n;m++ ) {
|
for ( var m=0, n=keys_.length;m<n;m++ ) {
|
||||||
item_meta = infos_array[i][hosts[j]][keys_[m]];
|
item_meta = infos_array[i][hosts[j]][keys_[m]];
|
||||||
//~ console.log(item_meta);
|
//~ console.log(item_meta);
|
||||||
//~ html_table += "<tr>" +
|
|
||||||
//~ "<td class='.file_selection'>" + item_meta["name"] + "</td>" +
|
|
||||||
//"<td>" + item_meta["size"] + "</td>" +
|
|
||||||
//~ "</tr>" ;
|
|
||||||
tr = document.createElement("tr");
|
tr = document.createElement("tr");
|
||||||
td = document.createElement("td");
|
td = document.createElement("td");
|
||||||
tr.appendChild(td);
|
tr.appendChild(td);
|
||||||
tr.setAttribute("class", "file_selection");
|
tr.setAttribute("class", "file_selection");
|
||||||
tr.host = hosts[j];
|
tr.host = hosts[j];
|
||||||
td.innerText = item_meta["name"];
|
td.innerText = item_meta.name;
|
||||||
tr.addEventListener("click", enqueueFile, false)
|
// 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);
|
document.getElementById("file_sel_"+hosts).appendChild(tr);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
//~ html_table += "</table>";
|
|
||||||
//~ document.getElementById("playlist_"+hosts).innerHTML += html_table;
|
|
||||||
//~ fileButtons = document.querySelectorAll('.file_selection');
|
|
||||||
//~ console.log(fileButtons);
|
|
||||||
//~ for (var i=fileButtons.length; i>0; i--) {
|
|
||||||
//~ fileButtons[i].addEventListener('click', selectFile, false);
|
|
||||||
//~ }
|
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case "/sync/status":
|
} else if ( command == "/sync/status") {
|
||||||
status_all = infos_array;
|
status_all = infos_array;
|
||||||
break;
|
} else {
|
||||||
}; // End switch case
|
setTimeout(send_ajax_cmd, 80, "/all/list");
|
||||||
};
|
setTimeout(send_ajax_cmd, 120, "/all/status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function sendCmd(command) {
|
function send_ajax_cmd(command) {
|
||||||
var request = new XMLHttpRequest();
|
var 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) {
|
||||||
var infos_array = JSON.parse(request.responseText);
|
var infos_array = JSON.parse(request.responseText);
|
||||||
//console.log(infos_array);
|
//console.log(infos_array.length);
|
||||||
parseResult(command, infos_array);
|
parse_result(command, infos_array);
|
||||||
//return infos_array;
|
//return infos_array;
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// On construit la commande
|
// On construit la commande
|
||||||
request.open("GET", command, true);
|
request.open("GET", command, true);
|
||||||
// et on l'envoie
|
// et on l'envoie
|
||||||
request.send();
|
request.send();
|
||||||
};
|
}
|
||||||
|
|
||||||
function enqueueFile(evt) {
|
function enqueue_file(evt) {
|
||||||
//console.log(evt.currentTarget.innerText + evt.currentTarget.host);
|
//console.log(evt.currentTarget.innerText + evt.currentTarget.host);
|
||||||
sendCmd("/" + 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");
|
||||||
|
//~ send_ajax_cmd("/" + evt.currentTarget.host + "/list");
|
||||||
|
}
|
||||||
|
|
||||||
function updateStatusAll() {
|
function update_statusall_content() {
|
||||||
document.getElementById("status_all").innerHTML = status_all;
|
document.getElementById("status_all").innerHTML = status_all;
|
||||||
};
|
}
|
||||||
|
|
||||||
setInterval(sendCmd, 500, "/all/status");
|
var scan_hosts = function() {
|
||||||
setInterval(sendCmd, 1000, "/all/list");
|
send_ajax_cmd("/scan");
|
||||||
//~ setInterval(sendCmd, 3000, "/all/browse");
|
update_statusall_content();
|
||||||
setInterval(sendCmd, scanInterval, "/scan");
|
setTimeout(scan_hosts, scan_interval);
|
||||||
setInterval(sendCmd, 20000, "/all/rssi");
|
}
|
||||||
setInterval(updateStatusAll, 1000);
|
|
||||||
|
send_ajax_cmd("/scan");
|
||||||
|
send_ajax_cmd("/browse_local");
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,14 +23,18 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
display:none;
|
display:none;
|
||||||
background: linear-gradient(0deg, #999 10%, #666 80%);
|
background: linear-gradient(0deg, #999 10%, #666 80%);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
.client_container .left_col{border-right: #a8a8a8 2px solid;}
|
||||||
|
*/
|
||||||
.client_container .button{}
|
.client_container .button{}
|
||||||
/*
|
/*
|
||||||
.timeline {height: 3em;background-color: #0f0;margin: 2em 0;}
|
.timeline {height: 3em;background-color: #0f0;margin: 2em 0;}
|
||||||
*/
|
*/
|
||||||
#timeline {
|
.timeline {
|
||||||
height: 75px;
|
height: 75px;
|
||||||
width:100%;
|
width:100%;
|
||||||
margin-top:1em;
|
margin-top:1em;
|
||||||
|
background-color: #33333361;
|
||||||
}
|
}
|
||||||
[id^="tl_cont"] {
|
[id^="tl_cont"] {
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -47,6 +51,8 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
line-height: 75px;
|
line-height: 75px;
|
||||||
width:100%;
|
width:100%;
|
||||||
background-color:#1F7B99;
|
background-color:#1F7B99;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
box-shadow: inset 0 0 20px #0008;
|
||||||
}
|
}
|
||||||
[id^="tl_cont"]:nth-child(2n+1) [id^="tl_drag"] {
|
[id^="tl_cont"]:nth-child(2n+1) [id^="tl_drag"] {
|
||||||
background-color:#255E70;
|
background-color:#255E70;
|
||||||
|
@ -117,17 +123,27 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
width: 59%;
|
width: 59%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
clear: right;
|
clear: right;
|
||||||
height: 170px;
|
max-height: 170px;
|
||||||
|
padding:.5em;
|
||||||
}
|
}
|
||||||
.col_2 div {
|
.col_2 div {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
height: 170px;
|
max-height: 170px;
|
||||||
|
}
|
||||||
|
.col_2 button {
|
||||||
|
margin:.3em !important;
|
||||||
|
background: #bbb;
|
||||||
|
color: #444;
|
||||||
}
|
}
|
||||||
.indicator {
|
.indicator {
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
background-color: #C32600;
|
background-color: #C32600;
|
||||||
margin: 0 0 0 5%;
|
margin: 0 0 0 5%;
|
||||||
padding: 0.3em;
|
padding: 0.3em;
|
||||||
|
}
|
||||||
|
.table_cont table {
|
||||||
|
margin:0;
|
||||||
|
width:100%;
|
||||||
}
|
}
|
||||||
.file_sel {
|
.file_sel {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -61,11 +61,12 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<button value="/{{host}}/browse" class="command btn btn-block btn-lg btn-default" role="button">↺<span class="btn_txt">{{gui_l10n['str_refresh']}}</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right_col">
|
<div class="right_col">
|
||||||
<div id="timeline">
|
<div class="timeline" id="timeline_{{host}}">
|
||||||
<!--
|
<!--
|
||||||
<div id="tl_contX"></div>
|
<div id="tl_contX"></div>
|
||||||
-->
|
-->
|
||||||
|
|
Loading…
Reference in New Issue