Media enqueuing UI
This commit is contained in:
parent
9bcb04890b
commit
82a67286ea
103
app.py
103
app.py
|
@ -7,15 +7,18 @@ 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 socket
|
# ~ import requests
|
||||||
|
# ~ import socket
|
||||||
import ssl
|
import ssl
|
||||||
import subprocess
|
import subprocess
|
||||||
import requests
|
|
||||||
from shutil import which
|
from shutil import which
|
||||||
import sys
|
import sys
|
||||||
|
# ~ import threading
|
||||||
import toml
|
import toml
|
||||||
|
from urllib.parse import quote, unquote
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from waitress import serve
|
from waitress import serve
|
||||||
|
|
||||||
|
|
||||||
# l10n
|
# l10n
|
||||||
LOCALE = os.getenv('LANG', 'en')
|
LOCALE = os.getenv('LANG', 'en')
|
||||||
|
@ -30,8 +33,6 @@ for location in config_locations:
|
||||||
if app.config.from_file(os.path.expanduser( location + "pilpil-server.toml"), load=toml.load, silent=True):
|
if app.config.from_file(os.path.expanduser( location + "pilpil-server.toml"), load=toml.load, silent=True):
|
||||||
print( _("Found configuration file in {}").format( os.path.expanduser(location) ) )
|
print( _("Found configuration file in {}").format( os.path.expanduser(location) ) )
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
hosts_available, hosts_unavailable = [],[]
|
hosts_available, hosts_unavailable = [],[]
|
||||||
queue_msgs = [
|
queue_msgs = [
|
||||||
_("No items"),
|
_("No items"),
|
||||||
|
@ -57,7 +58,6 @@ cmd_player = {
|
||||||
"move" : "pl_move",
|
"move" : "pl_move",
|
||||||
"sort" : "pl_sort",
|
"sort" : "pl_sort",
|
||||||
"seek" : "seek",
|
"seek" : "seek",
|
||||||
"sync" : "sync",
|
|
||||||
"status" : "status.xml",
|
"status" : "status.xml",
|
||||||
"list" : "playlist.xml",
|
"list" : "playlist.xml",
|
||||||
#"volume" : "volume",
|
#"volume" : "volume",
|
||||||
|
@ -65,19 +65,20 @@ cmd_player = {
|
||||||
#"dir" : "?dir=<uri>",
|
#"dir" : "?dir=<uri>",
|
||||||
#"command" : "?command=<cmd>",
|
#"command" : "?command=<cmd>",
|
||||||
#"key" : "key=",
|
#"key" : "key=",
|
||||||
#"browse" : "browse.xml?uri=file://~"
|
"browse" : "browse.xml?uri=file://~"
|
||||||
}
|
}
|
||||||
cmd_server = [
|
cmd_server = [
|
||||||
# Map pilpil-client http url parameters to pilpil-server urls
|
# Map pilpil-client http url parameters to pilpil-server urls
|
||||||
"blink",
|
"blink",
|
||||||
"reboot",
|
"reboot",
|
||||||
"poweroff",
|
"poweroff",
|
||||||
"rssi"
|
"rssi",
|
||||||
|
"sync"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
debug = app.config['DEFAULT']['debug']
|
debug = app.config['DEFAULT']['debug']
|
||||||
media_folder_remote = app.config['DEFAULT']['media_folder_remote']
|
media_folder_remote = os.path.expanduser(app.config['DEFAULT']['media_folder_remote'])
|
||||||
media_folder_local = os.path.expanduser(app.config['DEFAULT']['media_folder_local'])
|
media_folder_local = os.path.expanduser(app.config['DEFAULT']['media_folder_local'])
|
||||||
media_exts = app.config['DEFAULT']['media_exts']
|
media_exts = app.config['DEFAULT']['media_exts']
|
||||||
auth = str(base64.b64encode(str(":" + app.config['DEFAULT']['vlc_auth']).encode('utf-8')), 'utf-8')
|
auth = str(base64.b64encode(str(":" + app.config['DEFAULT']['vlc_auth']).encode('utf-8')), 'utf-8')
|
||||||
|
@ -98,6 +99,7 @@ else:
|
||||||
sslcontext.check_hostname = False
|
sslcontext.check_hostname = False
|
||||||
sslcontext.verify_mode = ssl.CERT_NONE
|
sslcontext.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
current_upload = 0
|
||||||
|
|
||||||
class PilpilClient:
|
class PilpilClient:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -190,6 +192,8 @@ def sync_media_folder(media_folder_local, media_folder_remote, host_local, port,
|
||||||
'''
|
'''
|
||||||
Sync media_folder_local with media_folder_remote using sync_facility
|
Sync media_folder_local with media_folder_remote using sync_facility
|
||||||
'''
|
'''
|
||||||
|
total_size = 0
|
||||||
|
transfer_ok = 0
|
||||||
trailing_slash = 1
|
trailing_slash = 1
|
||||||
# Check for trailing / and add it if missing
|
# Check for trailing / and add it if missing
|
||||||
if media_folder_local[-1:] != "/":
|
if media_folder_local[-1:] != "/":
|
||||||
|
@ -200,10 +204,18 @@ def sync_media_folder(media_folder_local, media_folder_remote, host_local, port,
|
||||||
#Using http_upload
|
#Using http_upload
|
||||||
if sync_facility == "http":
|
if sync_facility == "http":
|
||||||
media_list = list_media_files(media_folder_local)
|
media_list = list_media_files(media_folder_local)
|
||||||
transfer_ok = 0
|
|
||||||
for media in media_list:
|
for media in media_list:
|
||||||
transfer_ok += HTTP_upload(media, host_local, port, trailing_slash)
|
current_upload = media.strip("/")
|
||||||
return _("{} files uploaded.").format(str(transfer_ok))
|
if debug:
|
||||||
|
print("Uploading " + str(media))
|
||||||
|
if HTTP_upload(media, host_local, port, trailing_slash):
|
||||||
|
if debug:
|
||||||
|
print("File size : " + str(os.stat(media_folder_local + media).st_size))
|
||||||
|
total_size += os.stat(media_folder_local + media).st_size
|
||||||
|
transfer_ok += 1
|
||||||
|
# ~ return _("{} files uploaded.").format(str(transfer_ok))
|
||||||
|
# ~ return _("{} files uploaded.").format(str(transfer_ok))
|
||||||
|
# Using system cmd
|
||||||
elif which(sync_facility):
|
elif which(sync_facility):
|
||||||
# Build subprocess arg list accroding to sync_facility
|
# Build subprocess arg list accroding to sync_facility
|
||||||
# Using Rsync
|
# Using Rsync
|
||||||
|
@ -224,9 +236,9 @@ def sync_media_folder(media_folder_local, media_folder_remote, host_local, port,
|
||||||
if len(sync_proc.stdout):
|
if len(sync_proc.stdout):
|
||||||
scrape_index = str(sync_proc.stdout).index(scrape_str)+len(scrape_str)
|
scrape_index = str(sync_proc.stdout).index(scrape_str)+len(scrape_str)
|
||||||
total_size = str(sync_proc.stdout)[ scrape_index: ].split(" ")[0][:-1]
|
total_size = str(sync_proc.stdout)[ scrape_index: ].split(" ")[0][:-1]
|
||||||
else:
|
if debug:
|
||||||
total_size = "N/A";
|
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):
|
||||||
'''
|
'''
|
||||||
|
@ -239,7 +251,7 @@ def get_meta_data(host, xml_data, request_="status", m3u_=0):
|
||||||
}
|
}
|
||||||
if request_ == "list":
|
if request_ == "list":
|
||||||
# Return current instance's playlist
|
# Return current instance's playlist
|
||||||
return get_playlist(host, xml_data, m3u_)
|
return get_playlist(host, xml_data, m3u_)
|
||||||
elif request_ == "status":
|
elif request_ == "status":
|
||||||
# Return current instance's status ( currently playing, state, time length, etc. )
|
# Return current instance's status ( currently playing, state, time length, etc. )
|
||||||
if xml_data.findall("./information/category/"):
|
if xml_data.findall("./information/category/"):
|
||||||
|
@ -267,22 +279,37 @@ def get_meta_data(host, xml_data, request_="status", m3u_=0):
|
||||||
'id': cur_id,
|
'id': cur_id,
|
||||||
})
|
})
|
||||||
elif request_ == "rssi":
|
elif request_ == "rssi":
|
||||||
# # Return current instance's wifi signal quality
|
# Return current instance's wifi signal quality
|
||||||
print(xml_data)
|
if debug:
|
||||||
|
print(xml_data)
|
||||||
if xml_data.findall("rssi"):
|
if xml_data.findall("rssi"):
|
||||||
media_infos.update({
|
media_infos.update({
|
||||||
'status': 1,
|
'status': 1,
|
||||||
'rssi' : xml_data.find('rssi').text
|
'rssi' : xml_data.find('rssi').text
|
||||||
})
|
})
|
||||||
|
elif request_ == "browse":
|
||||||
|
host_medias = {}
|
||||||
|
for media in xml_data:
|
||||||
|
media_name = media.attrib["name"]
|
||||||
|
if len(media_name.split('.')) > 1:
|
||||||
|
if media_name.split('.')[1] in media_exts:
|
||||||
|
host_medias[media_name] = {
|
||||||
|
"name": media_name,
|
||||||
|
"uri": media.attrib["uri"],
|
||||||
|
"size": round(int(media.attrib["size"])/1000000, 2)}
|
||||||
|
media_infos = {host : host_medias}
|
||||||
|
if debug:
|
||||||
|
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
|
||||||
|
|
||||||
if xml_data.find("./node") and xml_data.find("./node").get('name') == "Playlist":
|
if xml_data.find("./node") and 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 = "{};{};{};"
|
||||||
|
@ -329,14 +356,22 @@ def send_pilpil_command(host, arg0, arg1, arg2):
|
||||||
#
|
#
|
||||||
# Default request
|
# Default request
|
||||||
HTTP_request = "/requests/status.xml"
|
HTTP_request = "/requests/status.xml"
|
||||||
if arg0 == "list" :
|
if arg0 == "list":
|
||||||
# Get playlist
|
# Get playlist
|
||||||
HTTP_request = "/requests/playlist.xml"
|
HTTP_request = "/requests/playlist.xml"
|
||||||
|
elif arg0 == "browse":
|
||||||
|
if debug:
|
||||||
|
print("Brosing {}".format(media_folder_remote))
|
||||||
|
# Browse remote media folder
|
||||||
|
media_folder_remote_URL = quote(media_folder_remote)
|
||||||
|
HTTP_request = "/requests/browse.xml?uri=file://{}".format(media_folder_remote_URL)
|
||||||
|
if debug:
|
||||||
|
print("requesting url {} to {}:{}".format(HTTP_request, host, port_))
|
||||||
elif arg0 in cmd_server:
|
elif arg0 in cmd_server:
|
||||||
# Switching to cmd server
|
# Switching to cmd server
|
||||||
HTTP_request = "/" + str(arg0)
|
HTTP_request = "/" + str(arg0)
|
||||||
port_ = cmd_port
|
port_ = cmd_port
|
||||||
elif arg0 != "status" :
|
elif arg0 != "status":
|
||||||
# Build request for VLC command
|
# Build request for VLC command
|
||||||
HTTP_request = HTTP_request + "?command=" + cmd_player[arg0]
|
HTTP_request = HTTP_request + "?command=" + cmd_player[arg0]
|
||||||
if arg1 != "null" :
|
if arg1 != "null" :
|
||||||
|
@ -352,7 +387,7 @@ def send_pilpil_command(host, arg0, arg1, arg2):
|
||||||
HTTP_request = HTTP_request + "&val=" + arg1
|
HTTP_request = HTTP_request + "&val=" + arg1
|
||||||
elif (arg0 == "enqueue") or (arg0 == "add") :
|
elif (arg0 == "enqueue") or (arg0 == "add") :
|
||||||
# Add 'input' url parameter
|
# Add 'input' url parameter
|
||||||
HTTP_request = HTTP_request + "&input=file://" + media_folder_remote + "/" + arg1
|
HTTP_request = HTTP_request + "&input=file://" + quote(media_folder_remote) + "/" + arg1
|
||||||
|
|
||||||
# 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)
|
||||||
|
@ -371,7 +406,7 @@ def send_pilpil_command(host, arg0, arg1, arg2):
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
# Utilities
|
# Utilities
|
||||||
def list_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).
|
||||||
'''
|
'''
|
||||||
|
@ -426,20 +461,24 @@ def scan():
|
||||||
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")
|
@app.route("/browse_local")
|
||||||
def browse():
|
def browse():
|
||||||
return list_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
|
||||||
size = 0
|
size = 0
|
||||||
if host == "all":
|
if host == "status":
|
||||||
|
return str(current_upload)
|
||||||
|
elif host == "all":
|
||||||
for hostl in hosts_available:
|
for hostl in hosts_available:
|
||||||
size += sync_media_folder(media_folder_local, media_folder_remote, hostl, cmd_port)
|
size += sync_media_folder(media_folder_local, media_folder_remote, 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, host, cmd_port)
|
size = sync_media_folder(media_folder_local, media_folder_remote, host, cmd_port)
|
||||||
return 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" })
|
||||||
|
@ -453,10 +492,10 @@ def action(host, arg0, arg1, arg2):
|
||||||
|
|
||||||
if host == "all":
|
if host == "all":
|
||||||
# Send request to all available hosts
|
# Send request to all available hosts
|
||||||
resp = []
|
responses = []
|
||||||
for hostl in hosts_available:
|
for hostl in hosts_available:
|
||||||
resp.append(send_pilpil_command(hostl, arg0, arg1, arg2))
|
responses.append(send_pilpil_command(hostl, arg0, arg1, arg2))
|
||||||
status_message = resp
|
status_message = responses
|
||||||
elif host not in hosts_available:
|
elif host not in hosts_available:
|
||||||
status_message = "<p>{}</p>".format("Host is not reachable")
|
status_message = "<p>{}</p>".format("Host is not reachable")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -32,7 +32,7 @@ sha256 : 0fe3fe76d0e56e445124fa20646fa8b3d8c59568786b3ebc8a96d83d92f203e3
|
||||||
md5 : dee7af70135994169cab4f073ee51905
|
md5 : dee7af70135994169cab4f073ee51905
|
||||||
sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
||||||
|
|
||||||
* Switch to rpi os Bullseye
|
* Switch to rpi os Bullseye arm64
|
||||||
* Switch to user 'pil', pw 'pilpoil'
|
* Switch to user 'pil', pw 'pilpoil'
|
||||||
* client config file parsing ( look for 'pilpil-client.toml' in ./, ~/., ~/.config/)
|
* client config file parsing ( look for 'pilpil-client.toml' in ./, ~/., ~/.config/)
|
||||||
* Add media folder sync (scp, rsync, http upload)
|
* Add media folder sync (scp, rsync, http upload)
|
||||||
|
@ -70,6 +70,7 @@ sha256 :
|
||||||
* setup : Generate/install http auth secret at setup
|
* setup : Generate/install http auth secret at setup
|
||||||
* setup : Add dry, help flags
|
* setup : Add dry, help flags
|
||||||
* FR localisation for server, client and install script
|
* FR localisation for server, client and install script
|
||||||
|
* pep8ification
|
||||||
|
|
||||||
|
|
||||||
# OTHER:
|
# OTHER:
|
||||||
|
@ -77,7 +78,7 @@ sha256 :
|
||||||
|
|
||||||
# TODO :
|
# TODO :
|
||||||
|
|
||||||
* ? python sanitization
|
* ~ python sanitization
|
||||||
* ~ Test with several rpis
|
* ~ Test with several rpis
|
||||||
* ? Scripts hotspot linux/win/mac
|
* ? Scripts hotspot linux/win/mac
|
||||||
* ? Config sync
|
* ? Config sync
|
||||||
|
|
Binary file not shown.
|
@ -54,6 +54,10 @@ msgstr "Erreur pendant la connexion à {}:{}"
|
||||||
msgid "{} reachable on {}"
|
msgid "{} reachable on {}"
|
||||||
msgstr "{} accessible sur {}"
|
msgstr "{} accessible sur {}"
|
||||||
|
|
||||||
|
#: app.py:311
|
||||||
|
msgid "Playlist"
|
||||||
|
msgstr "Liste de lecture"
|
||||||
|
|
||||||
#: app.py:331
|
#: app.py:331
|
||||||
msgid "Idle"
|
msgid "Idle"
|
||||||
msgstr "Innocupé"
|
msgstr "Innocupé"
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
debug = 1
|
debug = 1
|
||||||
useSSL = true
|
useSSL = false
|
||||||
CAfile = "selfCA.crt"
|
CAfile = "selfCA.crt"
|
||||||
# scp, rsync, http
|
# scp, rsync, http
|
||||||
sync_facility = "http"
|
sync_facility = "http"
|
||||||
media_folder_local = "../medias"
|
media_folder_local = "../medias"
|
||||||
media_folder_remote = "/home/pil/Videos"
|
media_folder_remote = "~/Vidéos"
|
||||||
media_exts = ["mp4", "avi", "mkv"]
|
media_exts = ["mp4", "avi", "mkv"]
|
||||||
vlc_auth = "secret"
|
vlc_auth = "secret"
|
||||||
# OnNlY3JldA==
|
# OnNlY3JldA==
|
||||||
cmd_auth = "secret"
|
cmd_auth = "secret"
|
||||||
hosts = ["10.42.0.10", "10.42.0.11"]
|
#hosts = ["10.42.0.10", "10.42.0.11"]
|
||||||
|
hosts = ["127.0.0.1"]
|
||||||
# VLC http LUA port
|
# VLC http LUA port
|
||||||
vlc_port = 8887
|
vlc_port = 8887
|
||||||
# Clients cmd port
|
# Clients cmd port
|
||||||
|
|
203
static/script.js
203
static/script.js
|
@ -5,15 +5,17 @@ const tl_drag_attr = {id:"tl_drag", draggable:"true", ondragstart:"drag(event, t
|
||||||
// Config
|
// Config
|
||||||
var timeline_color_cursor = "#FF8839";
|
var timeline_color_cursor = "#FF8839";
|
||||||
var timeline_color_bg = "#2EB8E6";
|
var timeline_color_bg = "#2EB8E6";
|
||||||
|
var scanInterval = 3000;
|
||||||
|
var status_all = "Searching network for live hosts..."
|
||||||
|
// Global vars
|
||||||
var src_id = "";
|
var src_id = "";
|
||||||
var medias_status = {};
|
var medias_status = {};
|
||||||
|
var fileButtons = [];
|
||||||
|
|
||||||
function updatePlaylist(){
|
function updatePlaylist(){
|
||||||
var new_list = [];
|
var new_list = [];
|
||||||
var media_count = document.getElementById("timeline").children.length;
|
var media_count = document.getElementById("timeline").children.length;
|
||||||
for (i=media_count,l=0;i>l;i--) {
|
for (i=media_count,l=0;i>l;i--) {
|
||||||
//~ new_list.push(document.getElementById("timeline").children[i].children[0].getAttribute("media_id"));
|
|
||||||
toMove = document.getElementById("timeline").children[i-1].children[0].getAttribute("media_id");
|
toMove = document.getElementById("timeline").children[i-1].children[0].getAttribute("media_id");
|
||||||
console.log(toMove);
|
console.log(toMove);
|
||||||
sendCmd("/all/move/" + toMove + "/1");
|
sendCmd("/all/move/" + toMove + "/1");
|
||||||
|
@ -21,10 +23,10 @@ function updatePlaylist(){
|
||||||
}
|
}
|
||||||
|
|
||||||
function findTargetIndex(element, index, array, thisArg){
|
function findTargetIndex(element, index, array, thisArg){
|
||||||
if (element == this) {
|
if (element == this) {
|
||||||
return index+1;
|
return index+1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
function moveElements(target) {
|
function moveElements(target) {
|
||||||
|
@ -47,26 +49,25 @@ function moveElements(target) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function allowDrop(ev) {
|
function allowDrop(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;
|
src_id = ev.target.parentElement;
|
||||||
ev.dataTransfer.setData("text", ev.target.id);
|
ev.dataTransfer.setData("text", ev.target.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
function drop(ev, target) {
|
function drop(ev, target) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
var data = ev.dataTransfer.getData("text");
|
var data = ev.dataTransfer.getData("text");
|
||||||
if (src_id.id != target.id) {
|
if (src_id.id != target.id) {
|
||||||
dropTarget = moveElements(target);
|
dropTarget = moveElements(target);
|
||||||
if (dropTarget) {
|
if (dropTarget) {
|
||||||
dropTarget.appendChild(document.getElementById(data));
|
dropTarget.appendChild(document.getElementById(data));
|
||||||
updatePlaylist();
|
updatePlaylist();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function adjustTl() {
|
function adjustTl() {
|
||||||
|
@ -88,36 +89,45 @@ function addAttr(id, attr, val , child=-1) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function addElement(type, attr, meta = 0, j = 0){
|
function addElement(type, attr, meta = 0, j = 0){
|
||||||
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]);
|
||||||
if(!i){
|
if(!i){
|
||||||
att.value = Object.values(attr)[i]+j;
|
att.value = Object.values(attr)[i]+j;
|
||||||
} else {
|
} else {
|
||||||
att.value = Object.values(attr)[i];
|
att.value = Object.values(attr)[i];
|
||||||
|
}
|
||||||
|
elem.setAttributeNode(att);
|
||||||
|
}
|
||||||
|
// Set playlist id attribute
|
||||||
|
if (meta) {
|
||||||
|
att = document.createAttribute("media_id");
|
||||||
|
att.value = meta[0];
|
||||||
|
elem.setAttributeNode(att);
|
||||||
}
|
}
|
||||||
elem.setAttributeNode(att);
|
// Get filename
|
||||||
}
|
elem.innerText = meta[1];
|
||||||
// Set playlist id attribute
|
return elem;
|
||||||
if (meta) {
|
|
||||||
att = document.createAttribute("media_id");
|
|
||||||
att.value = meta[0];
|
|
||||||
elem.setAttributeNode(att);
|
|
||||||
}
|
|
||||||
// Get filename
|
|
||||||
elem.innerText = meta[1];
|
|
||||||
return elem;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var scanInterval = 3000;
|
|
||||||
// Bouttons de commande
|
// Bouttons de commande
|
||||||
addEventListener("DOMContentLoaded", function() {
|
addEventListener("DOMContentLoaded", function() {
|
||||||
sendCmd("/scan");
|
sendCmd("/scan");
|
||||||
sendCmd("/browse");
|
sendCmd("/browse_local");
|
||||||
sendCmd("/all/rssi");
|
sendCmd("/all/rssi");
|
||||||
sendCmd("/all/list");
|
sendCmd("/all/list");
|
||||||
|
sendCmd("/all/browse");
|
||||||
adjustTl();
|
adjustTl();
|
||||||
|
|
||||||
|
// Get filename when selected in table
|
||||||
|
//~ 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"
|
// 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++) {
|
||||||
|
@ -133,14 +143,17 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
if ( !confirm("Êtes vous certain de vouloir effectuer cette action ?") ) {
|
if ( !confirm("Êtes vous certain de vouloir effectuer cette action ?") ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( command == "/scan" ) {
|
} else if ( command == "/scan" ) {
|
||||||
document.getElementById("status_all").innerHTML = "Searching network for live hosts...";
|
document.getElementById("status_all").innerHTML = status_all;
|
||||||
|
|
||||||
} 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 ) {
|
} else if ( command.indexOf("/move") > -1 ) {
|
||||||
const test_array = [21,19,20];
|
const test_array = [21,19,20];
|
||||||
for (i=test_array.length, l=0;i>l;i--){
|
for (i=test_array.length, l=0;i>l;i--){
|
||||||
|
@ -155,7 +168,11 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
if ( command == "/scan" ) {
|
if ( command == "/scan" ) {
|
||||||
request.onload = sendCmd(command);
|
request.onload = sendCmd(command);
|
||||||
}
|
} else if ( command == "/sync/all" ) {
|
||||||
|
status_all = "Syncing files..."
|
||||||
|
request.onload = sendCmd("/sync/status");
|
||||||
|
};
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -175,15 +192,6 @@ function parseResult(command, infos_array) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
// Find currently playing element
|
|
||||||
//~ var pl_length = document.getElementById("timeline").getAttribute("length");
|
|
||||||
//~ for (j=0,k=pl_length;j<k;j++){
|
|
||||||
//~ if (document.getElementById("timeline").children[j].children[0].getAttribute('media_id') == infos_array[i].id ) {
|
|
||||||
//~ addAttr(document.getElementById("timeline").children[j].children[0].id, "pos", infos_array[i].pos);
|
|
||||||
//~ }
|
|
||||||
//~ };
|
|
||||||
//~ document.getElementById("timeline").children[0].children[0].hasAttribute('media_id')
|
|
||||||
// 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 {
|
||||||
|
@ -201,7 +209,7 @@ function parseResult(command, 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
|
// Fill Playlist infos
|
||||||
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
|
// Build html table and timeline
|
||||||
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);
|
||||||
|
@ -212,20 +220,20 @@ function parseResult(command, infos_array) {
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var html_table = "<table>" +
|
//~ var html_table = "<table>" +
|
||||||
"<tr>" +
|
//~ "<tr>" +
|
||||||
"<th>Id</th>" +
|
//~ "<th>Id</th>" +
|
||||||
"<th>Filename</th>" +
|
//~ "<th>Filename</th>" +
|
||||||
"<th>Duration</th>" +
|
//~ "<th>Duration</th>" +
|
||||||
"</tr>";
|
//~ "</tr>";
|
||||||
for (var j = 0, k=items_array.length; j<k; j++) {
|
for (var j = 0, k=items_array.length; j<k; j++) {
|
||||||
// Table
|
// Table
|
||||||
item_meta = items_array[j].split(';');
|
item_meta = items_array[j].split(';');
|
||||||
html_table += "<tr>" +
|
//~ html_table += "<tr>" +
|
||||||
"<td>" + item_meta[0] + "</td>" +
|
//~ "<td>" + item_meta[0] + "</td>" +
|
||||||
"<td>" + item_meta[1] + "</td>" +
|
//~ "<td>" + item_meta[1] + "</td>" +
|
||||||
"<td>" + item_meta[2] + "</td>" +
|
//~ "<td>" + item_meta[2] + "</td>" +
|
||||||
"</tr>" ;
|
//~ "</tr>" ;
|
||||||
// Timeline
|
// Timeline
|
||||||
var child_node = addElement("div", tl_drag_attr, item_meta, j);
|
var child_node = addElement("div", tl_drag_attr, item_meta, j);
|
||||||
var len = document.getElementById("timeline").children.length;
|
var len = document.getElementById("timeline").children.length;
|
||||||
|
@ -248,8 +256,8 @@ function parseResult(command, infos_array) {
|
||||||
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 + ")";
|
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>";
|
//~ html_table += "</table>";
|
||||||
document.getElementById("playlist_"+infos_array[i].host).innerHTML += html_table;
|
//~ document.getElementById("playlist_"+infos_array[i].host).innerHTML += html_table;
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "/scan":
|
case "/scan":
|
||||||
|
@ -263,11 +271,13 @@ function parseResult(command, infos_array) {
|
||||||
}
|
}
|
||||||
if (host_up.length) {
|
if (host_up.length) {
|
||||||
scanInterval = 10000;
|
scanInterval = 10000;
|
||||||
document.getElementById("status_all").innerHTML = "Scan intarvel set to " + scanInterval;
|
//~ document.getElementById("status_all").innerHTML = "Scan interval set to " + scanInterval;
|
||||||
|
status_all = "Scan interval set to " + scanInterval;
|
||||||
}
|
}
|
||||||
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).";
|
||||||
break;
|
break;
|
||||||
case "/browse":
|
case "/browse_local":
|
||||||
var html_table = "<table>" +
|
var html_table = "<table>" +
|
||||||
"<tr>" +
|
"<tr>" +
|
||||||
"<th>Filename</th>" +
|
"<th>Filename</th>" +
|
||||||
|
@ -299,6 +309,47 @@ function parseResult(command, infos_array) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
case "/all/browse":
|
||||||
|
for (var i=0, l=infos_array.length; i<l; i++) {
|
||||||
|
hosts = Object.keys(infos_array[i]);
|
||||||
|
//~ 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++ ) {
|
||||||
|
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);
|
||||||
|
//~ html_table += "<tr>" +
|
||||||
|
//~ "<td class='.file_selection'>" + item_meta["name"] + "</td>" +
|
||||||
|
//"<td>" + item_meta["size"] + "</td>" +
|
||||||
|
//~ "</tr>" ;
|
||||||
|
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"];
|
||||||
|
tr.addEventListener("click", enqueueFile, false)
|
||||||
|
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":
|
||||||
|
status_all = infos_array;
|
||||||
|
break;
|
||||||
}; // End switch case
|
}; // End switch case
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -321,8 +372,20 @@ function sendCmd(command) {
|
||||||
request.send();
|
request.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
setInterval( sendCmd, 500, "/all/status");
|
function enqueueFile(evt) {
|
||||||
setInterval( sendCmd, 1000, "/all/list");
|
//console.log(evt.currentTarget.innerText + evt.currentTarget.host);
|
||||||
setInterval( sendCmd, scanInterval, "/scan");
|
sendCmd("/" + evt.currentTarget.host + "/enqueue/" + evt.currentTarget.innerText);
|
||||||
setInterval( sendCmd, 20000, "/all/rssi");
|
};
|
||||||
|
|
||||||
|
function updateStatusAll() {
|
||||||
|
document.getElementById("status_all").innerHTML = status_all;
|
||||||
|
};
|
||||||
|
|
||||||
|
setInterval(sendCmd, 500, "/all/status");
|
||||||
|
setInterval(sendCmd, 1000, "/all/list");
|
||||||
|
//~ setInterval(sendCmd, 3000, "/all/browse");
|
||||||
|
setInterval(sendCmd, scanInterval, "/scan");
|
||||||
|
setInterval(sendCmd, 20000, "/all/rssi");
|
||||||
|
setInterval(updateStatusAll, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
body {color:#fff;background-color:#666;margin:0;}
|
body {color:#fff;background-color:#666;margin:0;}
|
||||||
div {box-sizing: border-box;}
|
div {box-sizing: border-box;}
|
||||||
h2 {margin:0;}
|
h2 {margin:0;}
|
||||||
table {background-color: #555;margin:.5em;max-width:100%;font-size:.9em}
|
table {background-color: #555;margin-left:.5em;max-width:100%;font-size:.9em}
|
||||||
td {padding: 0;}
|
td {padding: 0;}
|
||||||
th {background-color: #aaa;}
|
th {background-color: #aaa;}
|
||||||
tr:nth-child(2n+1) {background-color: #888;}
|
tr:nth-child(2n+1) {background-color: #888;}
|
||||||
|
@ -87,9 +87,9 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
}
|
}
|
||||||
.btn_txt {display: block;font-size: small;}
|
.btn_txt {display: block;font-size: small;}
|
||||||
/*Right column*/
|
/*Right column*/
|
||||||
.right_col {width: 79.9%;display: inline-block;}
|
.right_col {width: 71%;display: inline-block;}
|
||||||
/*Left column*/
|
/*Left column*/
|
||||||
.left_col {width: 20%;display: inline-block;float: left;clear: left;}
|
.left_col {width: 28%;display: inline-block;float: left;clear: left;}
|
||||||
.left_col button {
|
.left_col button {
|
||||||
width: 2em;
|
width: 2em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
|
@ -109,30 +109,38 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
}
|
}
|
||||||
.left_col button:hover .btn_txt {display:block;}
|
.left_col button:hover .btn_txt {display:block;}
|
||||||
.col_1 {
|
.col_1 {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
float: left;
|
float: left;
|
||||||
padding: 3% 0 0 5%;
|
padding: 3% 0 0 5%;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.col_2 {
|
.col_2 {
|
||||||
width: 60%;
|
width: 59%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
clear: right;
|
clear: right;
|
||||||
|
height: 170px;
|
||||||
|
}
|
||||||
|
.col_2 div {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 170px;
|
||||||
}
|
}
|
||||||
.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;
|
||||||
}
|
}
|
||||||
|
.file_sel {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
.wl_indicator {
|
.wl_indicator {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: #bbb;
|
background-color: #bbb;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
margin: 0 1px;
|
margin: 0 1px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: .5em;
|
height: .5em;
|
||||||
width: 5%;
|
width: 5%;
|
||||||
}
|
}
|
||||||
#wl_0 {height:.5em;}
|
#wl_0 {height:.5em;}
|
||||||
#wl_1 {height:.65em;}
|
#wl_1 {height:.65em;}
|
||||||
|
|
|
@ -56,7 +56,15 @@
|
||||||
<p id="repeat_ind_{{ host }}" class="indicator">Repeat</p>
|
<p id="repeat_ind_{{ host }}" class="indicator">Repeat</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col_2">
|
<div class="col_2">
|
||||||
<p id="playlist_{{ host }}">{{queue_msgs[1]}}</p>
|
<div>{{queue_msgs[1]}}
|
||||||
|
<div id="playlist_{{ host }}" class="table_cont">
|
||||||
|
<table id="file_sel_{{ host }}">
|
||||||
|
<tr>
|
||||||
|
<th>Filename</th>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right_col">
|
<div class="right_col">
|
||||||
|
|
Loading…
Reference in New Issue