From 5c03ebe2cfa29a112b130966503baa52f8662700 Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Sat, 5 Nov 2022 20:04:27 +0100 Subject: [PATCH] pep8ify code --- app.py | 254 +++++++++++++++++++++++++++------------------ changelog_todo.md | 26 +++-- defaults.toml | 2 +- pilpil-server.toml | 2 +- 4 files changed, 173 insertions(+), 111 deletions(-) diff --git a/app.py b/app.py index a25f70b..c1faec4 100755 --- a/app.py +++ b/app.py @@ -1,41 +1,45 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -# -import sys, os, base64, toml -import http.client, ssl -import xml.etree.ElementTree as ET +# pilpil-client 0.1 +# abelliqueux + +import base64 from flask import Flask, render_template, request, make_response, jsonify +import gettext +import http.client +import os +import socket +import ssl +import subprocess +import requests +from shutil import which +import sys +import toml +import xml.etree.ElementTree as ET from waitress import serve - -app = Flask(__name__) - # l10n -import gettext LOCALE = os.getenv('LANG', 'en') _ = gettext.translation('template', localedir='locales', languages=[LOCALE]).gettext + +app = Flask(__name__) -queue_msgs = [ _("No items"), - _("No files queued."), - ] - -# Load config defaults, then look for other config files app.config.from_file("defaults.toml", load=toml.load) config_locations = ["./", "~/.", "~/.config/"] for location in config_locations: # Optional config files, ~ is expanded to $HOME on *nix, %USERPROFILE% on windows - # ~ app.config.from_file("videopi.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) ) ) - # ~ app.config.from_file(os.path.expanduser("~/.config/videopi.toml"), load=toml.load, silent=True) ### hosts_available, hosts_unavailable = [],[] - -# Map vlc cmds -# See https://github.com/videolan/vlc/blob/1336447566c0190c42a1926464fa1ad2e59adc4f/share/lua/http/requests/README.txt +queue_msgs = [ + _("No items"), + _("No files queued.") + ] cmd_player = { + # Map vlc http url parameters to pilpil-server urls + # See https://github.com/videolan/vlc/blob/1336447566c0190c42a1926464fa1ad2e59adc4f/share/lua/http/requests/README.txt "play" : "pl_play", "resume" : "pl_forceresume", "pause" : "pl_forcepause", @@ -62,77 +66,115 @@ cmd_player = { #"command" : "?command=", #"key" : "key=", #"browse" : "browse.xml?uri=file://~" -# System commands : - # ~ "rssi" : "rssi", - # ~ "blink" : "blink", - # ~ "poweroff" : "poweroff", - # ~ "reboot" : "reboot", - -} + } +cmd_server = [ + # Map pilpil-client http url parameters to pilpil-server urls + "blink", + "reboot", + "poweroff", + "rssi" + ] -cmd_server = ["blink", "reboot", "poweroff", "rssi"] - -# Set configuration - -DEBUG = app.config['DEFAULT']['DEBUG'] +# Configuration +debug = app.config['DEFAULT']['debug'] media_folder_remote = app.config['DEFAULT']['media_folder_remote'] 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']['auth']).encode('utf-8')), 'utf-8') cmd_auth = str(base64.b64encode(str(":" + app.config['DEFAULT']['cmd_auth']).encode('utf-8')), 'utf-8') -hosts = app.config['DEFAULT']['hosts'] -port = app.config['DEFAULT']['port'] -cmd_port = app.config['DEFAULT']['cmd_port'] +hosts = app.config['DEFAULT']['hosts'] +port = app.config['DEFAULT']['port'] +cmd_port = app.config['DEFAULT']['cmd_port'] useSSL = app.config['DEFAULT']['useSSL'] CAfile = app.config['DEFAULT']['CAfile'] sync_facility = app.config['DEFAULT']['sync_facility'] - headers = {"Authorization":"Basic " + auth} +# SSl context creation should be out of class +sslcontext = ssl.create_default_context() +if os.path.exists(CAfile): + sslcontext.load_verify_locations(cafile=CAfile) +else: + sslcontext.check_hostname = False + sslcontext.verify_mode = ssl.CERT_NONE + + +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 # https://www.metageek.com/training/resources/understanding-rssi/ - -def isup(host_l, port): - global DEBUG - import socket +# Rewrite using HTTPConnection +def isup(listed_host, port): + ''' + Check listed_host is up and listening on port by opening a socket + Return 1 if successfull. + ''' s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if useSSL: - sslcontext = ssl.create_default_context() - # ~ if os.path.exists(CAfile): - # ~ sslcontext.load_verify_locations(cafile=CAfile) - # ~ else: - # Dont validate cert, we juts want to see if host is live + # ~ sslcontext = ssl.create_default_context() + # Disable cert validation, we just want to know if host is live sslcontext.check_hostname = False sslcontext.verify_mode = ssl.CERT_NONE - s = sslcontext.wrap_socket(s, server_hostname=host_l) + s = sslcontext.wrap_socket(s, server_hostname=listed_host) try: s.settimeout(3.0) - s.connect((host_l, port)) - if DEBUG: - print( _("Port {} reachable").format(str(port)) ) + s.connect((listed_host, port)) + if debug: + print(_("Port {} reachable on {}").format(str(port), str(listed_host))) return 1 except (socket.error, socket.timeout) as e: - if DEBUG: - print( _("Error on connection to {} : {} : {} ").format(host_l, str(port), e) ) + if debug: + print(_("Error on connection to {} : {} : {} ").format(listed_host, str(port), e)) return 0 finally: s.close() -def checkHosts(host_l): - hostdown, hostup = [], [] - hosts_number = str(len(host_l)) - for lhost in host_l: - if not isup(lhost, port): - hostdown.append(lhost) +def check_hosts(host_list): + ''' + Check hosts in a host list are up and build then return two lists with up/down hosts. + ''' + hosts_up, hosts_down = [], [] + hosts_number = str(len(host_list)) + for local_host in host_list: + if not isup(local_host, port): + hosts_down.append(local_host) else: - hostup.append(lhost) - if DEBUG: - print( _("{} of {} hosts found.").format(str(len(hostup)), hosts_number)) - return hostup, hostdown + hosts_up.append(local_host) + if debug: + print( _("{} of {} hosts found.").format(str(len(hosts_up)), hosts_number)) + return hosts_up, hosts_down # File utilities -def listMediaFiles(folder): +def list_media_files(folder): + ''' + List files in folder which extension is allowed (exists in media_exts). + ''' if os.path.exists(folder): files = os.listdir(folder); medias = [] @@ -144,23 +186,28 @@ def listMediaFiles(folder): else: return [] -def httpUpload(filename, hostl, trailing_slash=1): - import requests - url = "https://" + hostl + ":" + str(cmd_port) + "/upload" +def HTTP_upload(filename, host_local, port, trailing_slash=1): + ''' + Build HTTP file upload request and send it. + ''' + url = "https://" + host_local + ":" + str(port) + "/upload" if not trailing_slash: filename = "/" + filename files = { "file":( filename, open( media_folder_local + filename, "rb"), "multipart/form-data") } - if DEBUG: + if debug: print(files) resp = requests.post(url, files=files, headers=headers, verify=CAfile) - if DEBUG: + if debug: print(resp.text) if resp.ok: return 1 else: return 0 -def syncMediaFolder(media_folder_local, media_folder_remote, hostl, 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 + ''' trailing_slash = 1 # Check for trailing / and add it if missing if media_folder_local[-1:] != "/": @@ -168,30 +215,29 @@ def syncMediaFolder(media_folder_local, media_folder_remote, hostl, sync_facilit trailing_slash = 0 if media_folder_remote[-1:] != "/": media_folder_remote += "/" + #Using http_upload if sync_facility == "http": - media_list = listMediaFiles(media_folder_local) + media_list = list_media_files(media_folder_local) transfer_ok = 0 for media in media_list: - transfer_ok += httpUpload(media, hostl, trailing_slash) + transfer_ok += HTTP_upload(media, host_local, port, trailing_slash) return _("{} files uploaded.").format(str(transfer_ok)) - - # Check sync utility exists elif which(sync_facility): - from shutil import whichdb - # Build subprocess arg list accroding to facility + # Build subprocess arg list accroding to sync_facility + # Using Rsync if sync_facility == "rsync": scrape_str = "total size is " sync_args = [sync_facility, "-zharm", "--include='*/'"] for media_type in media_exts: sync_args.append( "--include='*." + media_type + "'" ) - sync_args.extend(["--exclude='*'", media_folder_local, hostl + ":" + media_folder_remote]) + sync_args.extend(["--exclude='*'", media_folder_local, host_local + ":" + media_folder_remote]) + # Using scp if sync_facility == "scp": - media_list = listMediaFiles(media_folder_local) + media_list = list_media_files(media_folder_local) sync_args = [sync_facility, "-Crp", "-o IdentitiesOnly=yes"] for media in media_list: sync_args.append( media_folder_local + media ) - sync_args.append( hostl + ":" + media_folder_remote ) - + sync_args.append( host_local + ":" + media_folder_remote ) sync_proc = subprocess.run( sync_args , capture_output=True) if len(sync_proc.stdout): scrape_index = str(sync_proc.stdout).index(scrape_str)+len(scrape_str) @@ -201,7 +247,10 @@ def syncMediaFolder(media_folder_local, media_folder_remote, hostl, sync_facilit return total_size # /requests/status.xml?command=in_enqueue&input=file:///home/pi/tst1.mp4 -def writeM3U(m3u_content : str, host : str): +def write_M3U(m3u_content : str, host : str): + ''' + Write a M3U file named host.m3u from m3u_content + ''' filename = host.replace(".", "_") + ".m3u" fd = open(filename, "w") fd.write(m3u_content) @@ -210,15 +259,24 @@ def writeM3U(m3u_content : str, host : str): # String utilities -def escapeStr(uri): +def escape_str(uri): + ''' + Replace spaces with %20 for http urls + ''' return uri.replace(" ", "%20") def sec2min(duration): + ''' + Convert seconds to min:sec format. + ''' return('%02d:%02d' % (duration / 60, duration % 60)) # VLC lua utilities - -def sendCommand(host, arg0, arg1, arg2): +# TODO : make that a class +def send_command(host, arg0, arg1, arg2): + ''' + Build a http request according to args, send it and return parsed result. + ''' portl = port # Build request req = "/requests/status.xml" @@ -235,19 +293,19 @@ def sendCommand(host, arg0, arg1, arg2): if (arg0 == "sort") or (arg0 == "move") : # 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 - req = req + "&val=" + escapeStr(arg2) + req = req + "&val=" + escape_str(arg2) elif arg0 == "seek" : req = req + "&val=" + arg1 elif (arg0 == "enqueue") or (arg0 == "add") : req = req + "&input=file://" + media_folder_remote + "/" + arg1 # Send request if useSSL: - sslcontext = ssl.create_default_context() - if os.path.exists(CAfile): - sslcontext.load_verify_locations(cafile=CAfile) - else: - sslcontext.check_hostname = False - sslcontext.verify_mode = ssl.CERT_NONE + # ~ sslcontext = ssl.create_default_context() + # ~ if os.path.exists(CAfile): + # ~ sslcontext.load_verify_locations(cafile=CAfile) + # ~ else: + # ~ sslcontext.check_hostname = False + # ~ sslcontext.verify_mode = ssl.CERT_NONE conn = http.client.HTTPSConnection( host + ":" + str(portl), timeout=3, context = sslcontext ) else: conn = http.client.HTTPConnection( host + ":" + str(portl), timeout=3 ) @@ -266,7 +324,7 @@ def sendCommand(host, arg0, arg1, arg2): # ~ data = resp.read() if arg0 == "rssi": - if DEBUG: + if debug: print(data) response_dict = { 'host': host, @@ -339,9 +397,7 @@ status_message = _("Idle") @app.route("/") def main(): - global hosts status_message = _("Searching network for live hosts...") - # ~ hosts_available, hosts_unavailable = checkHosts(hosts) templateData = { 'hosts' : hosts, 'status_message' : status_message, @@ -352,13 +408,13 @@ def main(): @app.route("/scan") def scan(): global hosts_available, hosts_unavailable - hosts_available, hosts_unavailable = checkHosts(hosts) + hosts_available, hosts_unavailable = check_hosts(hosts) hosts_status = [hosts_available, hosts_unavailable] return hosts_status @app.route("/browse") def browse(): - files = listMediaFiles(media_folder_local) + files = list_media_files(media_folder_local) return files; @@ -366,9 +422,9 @@ def browse(): def sync(host): if host == "all": for hostl in hosts_available: - size = syncMediaFolder(media_folder_local, media_folder_remote, hostl) + size = sync_media_folder(media_folder_local, media_folder_remote, hostl, cmd_port) else: - size = syncMediaFolder(media_folder_local, media_folder_remote, host) + size = sync_media_folder(media_folder_local, media_folder_remote, host, cmd_port) return size; @@ -382,13 +438,13 @@ def action(host, arg0, arg1, arg2): elif host == "all": resp = [] for hostl in hosts_available: - resp.append( sendCommand(hostl, arg0, arg1, arg2) ) + resp.append( send_command(hostl, arg0, arg1, arg2) ) status_message = resp elif host not in hosts_available: status_message = "

{}

".format("Host is not reachable") else: - status_message = sendCommand(host, arg0, arg1, arg2) - if DEBUG: + status_message = send_command(host, arg0, arg1, arg2) + if debug: print(status_message) return status_message diff --git a/changelog_todo.md b/changelog_todo.md index 8048944..50c16aa 100644 --- a/changelog_todo.md +++ b/changelog_todo.md @@ -42,9 +42,7 @@ sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde md5 : sha256 : - * Add rt8821cu driver back - * Use safe overclocking settings for rpi1, 3, 4, better memory split - * Add blink function to pilpil + # FS checklist * /etc/dhcpcd.conf @@ -59,20 +57,28 @@ sha256 : # DOING NEXT : - * ~ Test with several rpis - * Define http auth secret at setup - + # DONE : - * Increase live host scan when host first found + * webgui : Increase live host scan when host first found + * webgui : Btn hover/press fixed + * 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 + # OTHER: * get_client_rssi.sh on server # TODO : - * FR localisation - * GUI : Btn hover/press ? - + + * ? python sanitization + * ~ Test with several rpis * ? Scripts hotspot linux/win/mac * ? Config sync * ! Remove git personal details/resolv.conf, remove authorized_keys, ssh config, clean home, re-enable ssh pw login diff --git a/defaults.toml b/defaults.toml index 01093bf..5fcf2ee 100644 --- a/defaults.toml +++ b/defaults.toml @@ -1,5 +1,5 @@ [DEFAULT] -DEBUG = 0 +debug = 0 useSSL = false CAfile = "selfCA.crt" # scp, rsync, http diff --git a/pilpil-server.toml b/pilpil-server.toml index 27f233a..bc7f047 100644 --- a/pilpil-server.toml +++ b/pilpil-server.toml @@ -1,5 +1,5 @@ [DEFAULT] -DEBUG = 0 +debug = 0 useSSL = true CAfile = "selfCA.crt" # scp, rsync, http