From 56632f21e6e92600d3c935b0d7999eaa73e1d0d0 Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Fri, 14 Oct 2022 13:27:01 +0200 Subject: [PATCH] Add media sync, docker conf --- Dockerfile | 2 ++ app.py | 84 +++++++++++++++++++++++++++++++++++++++++---- defaults.toml | 4 ++- docker-compose.yml | 12 +++++++ requirements.txt | 3 ++ templates/main.html | 1 + videopi.toml | 7 ++-- 7 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..39792b9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,2 @@ +FROM bitnami/python:latest +#RUN apt-get update && apt-get install -y openssh rsync diff --git a/app.py b/app.py index 35d8402..c318dbc 100755 --- a/app.py +++ b/app.py @@ -64,14 +64,17 @@ cmd = { DEBUG = app.config['DEFAULT']['DEBUG'] media_folder_remote = app.config['DEFAULT']['media_folder_remote'] -media_folder_local = app.config['DEFAULT']['media_folder_local'] -media_ext = app.config['DEFAULT']['media_ext'] +media_folder_local = os.path.expanduser(app.config['DEFAULT']['media_folder_local']) +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'] useSSL = app.config['DEFAULT']['useSSL'] +sync_facility = app.config['DEFAULT']['sync_facility'] + +headers = {"Authorization":"Basic " + auth} # Network/link utilities # https://www.metageek.com/training/resources/understanding-rssi/ @@ -115,10 +118,65 @@ def listMediaFiles(folder): medias = [] for fd in files: if len(fd.split('.')) > 1: - if fd.split('.')[1] in media_ext: + if fd.split('.')[1] in media_exts: medias.append(fd) return medias +def httpUpload(filename, hostl, trailing_slash=1): + import requests + url = "https://" + hostl + ":" + str(cmd_port) + "/upload" + if not trailing_slash: + filename = "/" + filename + files = { "file":( filename, open( media_folder_local + filename, "rb"), "multipart/form-data") } + print(files) + # ~ resp = requests.post(url, files=files, auth=('', auth ), verify=hostl + ".crt") + resp = requests.post(url, files=files, headers=headers, verify=hostl + ".crt") + print(resp.text) + if resp.ok: + return 1 + else: + return 0 + +def syncMediaFolder(media_folder_local, media_folder_remote, hostl, sync_facility=sync_facility): + trailing_slash = 1 + # Check for trailing / and add it if missing + if media_folder_local[-1:] != "/": + media_folder_local += "/" + trailing_slash = 0 + if media_folder_remote[-1:] != "/": + media_folder_remote += "/" + if sync_facility == "http": + media_list = listMediaFiles(media_folder_local) + transfer_ok = 0 + for media in media_list: + transfer_ok += httpUpload(media, hostl, trailing_slash) + return str(transfer_ok) + " files uploaded." + + # Check sync utility exists + elif which(sync_facility): + from shutil import whichdb + # Build subprocess arg list accroding to facility + 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]) + if sync_facility == "scp": + media_list = listMediaFiles(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_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) + total_size = str(sync_proc.stdout)[ scrape_index: ].split(" ")[0][:-1] + else: + total_size = "N/A"; + return total_size + # /requests/status.xml?command=in_enqueue&input=file:///home/pi/tst1.mp4 def writeM3U(m3u_content : str, host : str): filename = host.replace(".", "_") + ".m3u" @@ -173,7 +231,8 @@ def sendCommand(host, arg0, arg1, arg2): else: conn = http.client.HTTPConnection( host + ":" + str(portl), timeout=3 ) try: - conn.request( "GET", req, headers={"Authorization":"Basic " + auth} ) + # ~ conn.request( "GET", req, headers={"Authorization":"Basic " + auth} ) + conn.request( "GET", req, headers = headers ) except: return "Connection to " + host + " was refused on port " + str(portl) resp = conn.getresponse() @@ -272,9 +331,19 @@ def scan(): @app.route("/browse") def browse(): - files = listMediaFiles(media_folder_local); + files = listMediaFiles(media_folder_local) return files; + +@app.route("/sync/") +def sync(host): + if host == "all": + for hostl in hosts_available: + size = syncMediaFolder(media_folder_local, media_folder_remote, hostl) + else: + size = syncMediaFolder(media_folder_local, media_folder_remote, host) + return size; + @app.route("///", defaults = { "arg1": "null", "arg2": "null" }) @app.route("////", defaults = { "arg2": "null" }) @@ -298,5 +367,6 @@ def action(host, arg0, arg1, arg2): return status_message if __name__ == '__main__': - app.run() - # ~ serve(app, host='127.0.0.1', port=8080) \ No newline at end of file + # ~ app.run() + #serve(app, host='127.0.0.1', port=8080) + serve(app, host='0.0.0.0', port=8080) \ No newline at end of file diff --git a/defaults.toml b/defaults.toml index 12c0b95..50460db 100644 --- a/defaults.toml +++ b/defaults.toml @@ -1,9 +1,11 @@ [DEFAULT] DEBUG = 0 useSSL = false +# Could be scp, sftp ? +sync_facility = "rsync" media_folder_local = "~/Videos" media_folder_remote = "~/Videos" -media_ext = [] +media_exts = [] auth = "" cmd_auth = "" hosts = [] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..201f1ce --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' # or '2.4' # add +services: # add + videopi: + build: + context: . + dockerfile: Dockerfile + ports: + - "8080:8080" + command: "sh -c 'pip install -r requirements.txt && python app.py'" + volumes: + - .:/app + - ../medias:/medias diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f55289f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask +waitress +toml diff --git a/templates/main.html b/templates/main.html index d645720..84310d5 100644 --- a/templates/main.html +++ b/templates/main.html @@ -32,6 +32,7 @@ +

diff --git a/videopi.toml b/videopi.toml index b6e58d3..1628684 100644 --- a/videopi.toml +++ b/videopi.toml @@ -1,10 +1,13 @@ [DEFAULT] DEBUG = 0 useSSL = true +# Can be rsync, scp, http +sync_facility = "http" media_folder_local = "../medias" media_folder_remote = "/home/pi/Videos/" -media_ext = ["mp4", "avi", "mkv"] -auth = "secret" +media_exts = ["mp4", "avi", "mkv"] +auth = "secret" +# OnNlY3JldA== cmd_auth = "secret" hosts = ["10.42.0.135", "10.42.0.156"] # VLC http LUA port