From 96355f689779cb523a6f87e352959be99346250a Mon Sep 17 00:00:00 2001 From: Erik Thiele Date: Fri, 25 Sep 2026 12:45:16 +0200 Subject: [PATCH] feat: Medien im Player mit ?v= und langem Cache ausliefern Bisher lieferte Flask Medien mit no-cache aus; der Player musste jede Datei bei jeder Anzeige neu anfragen. Versionierte URLs duerfen jetzt ein Jahr ohne Rueckfrage aus dem Cache kommen, unversionierte bleiben unveraendert. Co-Authored-By: Claude Opus 5.5 --- app.py | 37 ++++++++++++++++++++++++++++++++----- templates/player.html | 3 ++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/app.py b/app.py index 98258c4..30293ee 100755 --- a/app.py +++ b/app.py @@ -1208,17 +1208,41 @@ def api_add_customer(): # ------------------------------------------------- # Medien ausliefern (statische Dateien) # ------------------------------------------------- +# Traegt die URL ein ?v=, darf der Player die Datei ein Jahr lang ohne +# Rueckfrage aus dem Cache nehmen: eine geaenderte Datei bekommt eine neue URL. +# Ohne ?v bleibt es beim Flask-Standard (no-cache + ETag), sonst saehe ein +# unversionierter Aufruf nach dem Ersetzen einer Datei weiter die alte Fassung. +MEDIA_CACHE_MAX_AGE = 365 * 24 * 3600 + + +def media_version(path): + """Liefert die mtime einer Datei als Versionskennung fuer ?v=, 0 wenn sie fehlt.""" + try: + return int(os.path.getmtime(path)) + except OSError: + return 0 + + +def send_media(directory, filename): + """send_from_directory mit langem Cache, sofern die URL versioniert ist.""" + if not request.args.get("v"): + return send_from_directory(directory, filename) + resp = send_from_directory(directory, filename, max_age=MEDIA_CACHE_MAX_AGE) + resp.cache_control.immutable = True + return resp + + @app.route("/media//priority/") def media_priority(site, filename): """Liefert die Priority-Medien eines Standorts aus media//priority/ aus.""" - return send_from_directory(priority_dir(site), filename) + return send_media(priority_dir(site), filename) @app.route("/media///") def media(site, screen, filename): """Liefert Medien-Dateien für einen bestimmten Screen aus.""" path = os.path.join(MEDIA_DIR, site, screen) - return send_from_directory(path, filename) + return send_media(path, filename) # ------------------------------------------------- @@ -1279,7 +1303,8 @@ def player(site, screen): continue if item_name and os.path.exists(os.path.join(folder, item_name)) and allowed_file(item_name): - normal_files.append({"kind": "file", "name": item_name, "enabled": True}) + normal_files.append({"kind": "file", "name": item_name, "enabled": True, + "v": media_version(os.path.join(folder, item_name))}) existing_file_names = { item["name"] for item in normal_files if item["kind"] == "file" @@ -1291,7 +1316,8 @@ def player(site, screen): if f in existing_file_names or f.startswith("._"): continue if allowed_file(f): - normal_files.append({"kind": "file", "name": f}) + normal_files.append({"kind": "file", "name": f, + "v": media_version(os.path.join(folder, f))}) # ─── Priority-Playlist (global) ─── priority_cfg = get_priority_config(config, site) @@ -1311,7 +1337,8 @@ def player(site, screen): file_path = os.path.join(priority_dir(site), item_name) if os.path.exists(file_path): - prio_files.append({"kind": "file", "name": item_name, "enabled": True}) + prio_files.append({"kind": "file", "name": item_name, "enabled": True, + "v": media_version(file_path)}) else: prio_files = [] diff --git a/templates/player.html b/templates/player.html index 0edb8a8..3e4087e 100755 --- a/templates/player.html +++ b/templates/player.html @@ -472,7 +472,8 @@ function playNext() { } // Datei aus dem Medienverzeichnis - const src = `/media/${site}/${basePath}/${item.name}`; + // ?v= erlaubt dem Browser, die Datei ohne Rueckfrage aus dem Cache zu nehmen + const src = `/media/${site}/${basePath}/${item.name}` + (item.v ? `?v=${item.v}` : ""); if (isVideo(item)) { vid.style.display = "block"; vid.src = src;