Add video thumbnail serving

This commit is contained in:
ABelliqueux 2022-11-24 20:15:22 +01:00
parent 4b95a8130e
commit 4af1885757
1 changed files with 41 additions and 8 deletions

45
app.py
View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# pilpil-client 0.1 # pilpil-client 0.1
# abelliqueux <contact@arthus.net> # abelliqueux <contact@arthus.net>
from flask import Flask, flash, request, redirect, url_for from flask import Flask, flash, request, redirect, url_for, send_file
from flask_httpauth import HTTPBasicAuth from flask_httpauth import HTTPBasicAuth
import gettext import gettext
import os import os
@ -23,8 +23,8 @@ app.config.from_file("defaults.toml", load=toml.load)
config_locations = ["./", "~/.", "~/.config/"] config_locations = ["./", "~/.", "~/.config/"]
for location in config_locations: for location in config_locations:
# Optional config files, ~ is expanded to $HOME on *nix, %USERPROFILE% on windows # Optional config files, ~ is expanded to $HOME on *nix, %USERPROFILE% on windows
if app.config.from_file(os.path.expanduser( location + "pilpil-client.toml"), load=toml.load, silent=True): if app.config.from_file(os.path.expanduser(location + "pilpil-client.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)))
upload_folder = os.path.expanduser(app.config['DEFAULT']['media_folder_local']) upload_folder = os.path.expanduser(app.config['DEFAULT']['media_folder_local'])
media_exts = app.config['DEFAULT']['media_exts'] media_exts = app.config['DEFAULT']['media_exts']
@ -32,6 +32,7 @@ HTTP_secret = str(app.config['DEFAULT']['auth'])
debug = app.config['DEFAULT']['debug'] debug = app.config['DEFAULT']['debug']
useSSL = app.config['DEFAULT']['useSSL'] useSSL = app.config['DEFAULT']['useSSL']
rssi_signal = 0 rssi_signal = 0
thumbnails_folder = "thumb"
# HTTP upload settings # HTTP upload settings
app.config['UPLOAD_FOLDER'] = upload_folder app.config['UPLOAD_FOLDER'] = upload_folder
@ -51,6 +52,7 @@ if useSSL:
if debug: if debug:
print(HTTP_url_scheme) print(HTTP_url_scheme)
@auth.verify_password @auth.verify_password
def verify_password(username, password): def verify_password(username, password):
''' '''
@ -59,6 +61,7 @@ def verify_password(username, password):
if username in users and check_password_hash(users.get(username), password): if username in users and check_password_hash(users.get(username), password):
return username return username
def allowed_ext(filename): def allowed_ext(filename):
''' '''
Check if filename has an allowed extension. Check if filename has an allowed extension.
@ -66,9 +69,10 @@ def allowed_ext(filename):
# Check for dot in filename # Check for dot in filename
if "." in filename: if "." in filename:
# Split from right at first dot to find ext and allow files with "." in name # Split from right at first dot to find ext and allow files with "." in name
if filename.rsplit(".",1)[-1] in media_exts: if filename.rsplit(".", 1)[-1] in media_exts:
return True return True
def XMLify(string, child_node_name="child"): def XMLify(string, child_node_name="child"):
''' '''
''' '''
@ -77,6 +81,7 @@ def XMLify(string, child_node_name="child"):
print("<{0}><{1}>{2}</{1}></{0}>".format(root_element, child_node_name, string)) print("<{0}><{1}>{2}</{1}></{0}>".format(root_element, child_node_name, string))
return "<{0}><{1}>{2}</{1}></{0}>".format(root_element, child_node_name, string) return "<{0}><{1}>{2}</{1}></{0}>".format(root_element, child_node_name, string)
def get_RSSI(): def get_RSSI():
''' '''
Get wireless signal level to AP Get wireless signal level to AP
@ -90,13 +95,15 @@ def get_RSSI():
print(rssi_signal) print(rssi_signal)
return XMLify(rssi_signal, child_node_name="rssi") return XMLify(rssi_signal, child_node_name="rssi")
def running_on_rpi(): def running_on_rpi():
with open("/proc/cpuinfo", "r") as wl: with open("/proc/cpuinfo", "r") as wl:
for line in wl: for line in wl:
if line.lower().find("raspberry") > 0 : if line.lower().find("raspberry") > 0:
return True return True
return False return False
def led_init(): def led_init():
''' '''
Set ACT and PWR leds trigger and turn them off Set ACT and PWR leds trigger and turn them off
@ -106,6 +113,7 @@ def led_init():
led_set(0, 0) led_set(0, 0)
led_set(1, 0) led_set(1, 0)
def led_set(led_id, state): def led_set(led_id, state):
''' '''
Set led with id led_id to state. Set led with id led_id to state.
@ -114,6 +122,7 @@ def led_set(led_id, state):
# state : 0 = off, 1 = on # state : 0 = off, 1 = on
os.system("echo {} | sudo tee /sys/class/leds/led{}/brightness".format(str(state), str(led_id))) os.system("echo {} | sudo tee /sys/class/leds/led{}/brightness".format(str(state), str(led_id)))
def blink_pi(n): def blink_pi(n):
''' '''
Blink ACT and PWR leds altenatively n times to allow physical identification. Blink ACT and PWR leds altenatively n times to allow physical identification.
@ -130,6 +139,7 @@ def blink_pi(n):
led_set(1, 0) led_set(1, 0)
return "OK" return "OK"
def thread_blink(): def thread_blink():
''' '''
Blink leds as a thread to avoid blocking. Blink leds as a thread to avoid blocking.
@ -137,12 +147,13 @@ def thread_blink():
th = threading.Thread(target=blink_pi, args=(16,)) th = threading.Thread(target=blink_pi, args=(16,))
th.start() th.start()
def list_media_files(folder): def list_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:
@ -152,16 +163,25 @@ def list_media_files(folder):
else: else:
return [] return []
def generate_thumbnails():
media_files = list_media_files(upload_folder)
for media in media_files:
subprocess.call(['ffmpeg', '-i', upload_folder + "/" + media, '-q:v', '30', '-s' , '160x120' , '-vf' , 'boxblur=2' , '-ss', '00:00:01.000', '-vframes', '1', thumbnails_folder + "/" + media + ".jpg", "-y"])
@app.route("/") @app.route("/")
@auth.login_required @auth.login_required
def main(): def main():
return _("Nothing to see here !") return _("Nothing to see here !")
@app.route("/rssi") @app.route("/rssi")
@auth.login_required @auth.login_required
def rssi(): def rssi():
return get_RSSI() return get_RSSI()
@app.route("/blink") @app.route("/blink")
@auth.login_required @auth.login_required
def blink(): def blink():
@ -169,6 +189,7 @@ def blink():
thread_blink() thread_blink()
return _("Blinkin") return _("Blinkin")
@app.route("/reboot") @app.route("/reboot")
@auth.login_required @auth.login_required
def reboot(): def reboot():
@ -176,6 +197,7 @@ def reboot():
print(stdout) print(stdout)
return _("Rebooting...") return _("Rebooting...")
@app.route("/poweroff") @app.route("/poweroff")
@auth.login_required @auth.login_required
def shutdown(): def shutdown():
@ -185,6 +207,7 @@ def shutdown():
# File upload # File upload
@app.route('/upload', methods=['GET', 'POST']) @app.route('/upload', methods=['GET', 'POST'])
@auth.login_required @auth.login_required
def upload_file(over_write=1): def upload_file(over_write=1):
@ -211,7 +234,17 @@ def upload_file(over_write=1):
return "File exists, skipping..." return "File exists, skipping..."
return "OK" return "OK"
@app.route("/thumb/<media>")
@auth.login_required
def get_thumbnail(media):
if media:
return send_file( thumbnails_folder + os.sep + media + ".jpg", mimetype='image/jpg')
if __name__ == '__main__': if __name__ == '__main__':
generate_thumbnails()
# Turn ACT and POW leds off on start # Turn ACT and POW leds off on start
if running_on_rpi(): if running_on_rpi():
led_init() led_init()