diff --git a/.DS_Store b/.DS_Store index 9ce5624..0e8361f 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/app.py b/app.py old mode 100644 new mode 100755 index 8d837eb..ab565f3 --- a/app.py +++ b/app.py @@ -23,11 +23,11 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MEDIA_DIR = os.path.join(BASE_DIR, "media") CONFIG_FILE = os.path.join(BASE_DIR, "config.json") -APP_VERSION = "2.0.0" +APP_VERSION = "2.1.0" UPLOAD_EXTENSIONS = {".jpg", ".jpeg", ".png", ".mp4"} app = Flask(__name__) -app.secret_key = "CHANGE_THIS_SECRET" +app.secret_key = "CHANGE_THIS_SECRET!!!" login_manager = LoginManager(app) login_manager.login_view = "login" @@ -91,6 +91,7 @@ def media(screen, filename): # ------------------------------------------------- # Player # ------------------------------------------------- + @app.route("/player/") def player(screen): config = load_config() @@ -116,81 +117,109 @@ def player(screen): return show_videos return False - ordered = [] + # ------------------------------ + # Normale Screen-Playlist + # ------------------------------ + normal_files = [] # 1. Playlist-Reihenfolge for name in playlist: if os.path.exists(os.path.join(folder, name)) and allowed_file(name): - ordered.append(name) + normal_files.append(name) # 2. Neue Dateien hintendran for f in sorted(os.listdir(folder)): - if f not in ordered and allowed_file(f): - ordered.append(f) + if f not in normal_files and allowed_file(f): + normal_files.append(f) + + # ------------------------------ + # Priority-Playlist (global) + # WICHTIG: IMMER definieren! + # ------------------------------ + priority_cfg = config.get("priority", {}) + + if priority_cfg.get("enabled", False): + prio_files = priority_cfg.get("playlist", []) + else: + prio_files = [] return render_template( "player.html", screen=screen, - files=ordered, + normal_files=normal_files, # ✅ explizit + prio_files=prio_files, # ✅ IMMER Liste interval=screen_cfg.get("interval", 10) ) + # ------------------------------------------------- # Hash für automatisches Player-Reload # ------------------------------------------------- @app.route("/playlist//hash") def playlist_hash(screen): - screen_dir = os.path.join(MEDIA_DIR, screen) - if not os.path.isdir(screen_dir): - return "" + config = load_config() - entries = [] - for f in os.listdir(screen_dir): - if f.startswith("._"): - continue - p = os.path.join(screen_dir, f) - try: - entries.append(f"{f}:{int(os.path.getmtime(p))}") - except OSError: - pass + # ✅ Globaler Hash über ALLE relevanten Daten + relevant = { + "screens": { + screen: config["screens"].get(screen, {}) + }, + "priority": config.get("priority", {}) + } + + blob = json.dumps(relevant, sort_keys=True).encode() + return hashlib.md5(blob).hexdigest() - h = hashlib.md5("|".join(sorted(entries)).encode()).hexdigest() - return h # ------------------------------------------------- # Admin Dashboard # ------------------------------------------------- + @app.route("/admin") @login_required def admin(): - config = load_config() - screens_cfg = config.setdefault("screens", {}) + cfg = load_config() screens = {} media_files = {} screen_status = {} - for screen in sorted(os.listdir(MEDIA_DIR)): - screen_dir = os.path.join(MEDIA_DIR, screen) - if not os.path.isdir(screen_dir): + for screen in os.listdir(MEDIA_DIR): + if screen == "priority": + continue # ✅ kein echter Screen + + path = os.path.join(MEDIA_DIR, screen) + if not os.path.isdir(path): continue - screens_cfg.setdefault(screen, { - "interval": 10, - "show_images": True, - "show_videos": True, - "playlist": [] - }) - - screens[screen] = screens_cfg[screen] - files = [] - for f in sorted(os.listdir(screen_dir)): - if f.startswith("._"): + playlist = cfg["screens"][screen].get("playlist", []) + + # 1️⃣ Playlist-Reihenfolge + for name in playlist: + file_path = os.path.join(path, name) + if not os.path.exists(file_path): continue + + ext = os.path.splitext(name)[1].lower() + ftype = "video" if ext == ".mp4" else "image" + size = os.path.getsize(file_path) // 1024 + + files.append({ + "name": name, + "type": ftype, + "size": size + }) + + # 2️⃣ Neue Dateien anhängen (nicht in Playlist) + for f in sorted(os.listdir(path)): + if f in playlist or f.startswith("._"): + continue + + file_path = os.path.join(path, f) ext = os.path.splitext(f)[1].lower() ftype = "video" if ext == ".mp4" else "image" - size = os.path.getsize(os.path.join(screen_dir, f)) // 1024 + size = os.path.getsize(file_path) // 1024 files.append({ "name": f, @@ -198,21 +227,63 @@ def admin(): "size": size }) + screens[screen] = cfg["screens"][screen] media_files[screen] = files screen_status[screen] = "active" if files else "empty" - save_config(config) + + # --- PRIORITY EXTENSION: Admin --- + prio_dir = os.path.join(MEDIA_DIR, "priority") + os.makedirs(prio_dir, exist_ok=True) + priority_files = [] + prio_playlist = cfg["priority"].get("playlist", []) + + # 1️⃣ Playlist-Reihenfolge + for name in prio_playlist: + file_path = os.path.join(MEDIA_DIR, "priority") + if not os.path.exists(file_path): + continue + + ext = os.path.splitext(name)[1].lower() + ftype = "video" if ext == ".mp4" else "image" + size = os.path.getsize(file_path) // 1024 + + priority_files.append({ + "name": name, + "type": ftype, + "size": size + }) + + # 2️⃣ Neue Dateien anhängen (nicht in Playlist) + for f in sorted(os.listdir(prio_dir)): + if f in prio_playlist or f.startswith("._"): + continue + + file_path = os.path.join(prio_dir, f) + ext = os.path.splitext(f)[1].lower() + ftype = "video" if ext == ".mp4" else "image" + size = os.path.getsize(file_path) // 1024 + + priority_files.append({ + "name": f, + "type": ftype, + "size": size + }) + + return render_template( "admin.html", screens=screens, media_files=media_files, screen_status=screen_status, - hostname=socket.gethostname(), version=APP_VERSION, - year=datetime.now().year + year=datetime.now().year, + hostname=os.uname().nodename, + priority_files=priority_files ) + # ------------------------------------------------- # Admin: Screen-Einstellungen speichern # ------------------------------------------------- @@ -251,29 +322,50 @@ def upload(screen): # ------------------------------------------------- # Admin: Datei löschen # ------------------------------------------------- + @app.route("/admin/delete//", methods=["POST"]) @login_required def delete_file(screen, filename): + cfg = load_config() + + # Datei löschen path = os.path.join(MEDIA_DIR, screen, filename) if os.path.exists(path): os.remove(path) + + # --- Playlist bereinigen --- + if screen == "priority": + playlist = cfg.get("priority", {}).get("playlist", []) + if filename in playlist: + playlist.remove(filename) + else: + playlist = cfg["screens"].get(screen, {}).get("playlist", []) + if filename in playlist: + playlist.remove(filename) + + save_config(cfg) return redirect("/admin") + # ------------------------------------------------- # Admin: Playlist speichern # ------------------------------------------------- + @app.route("/admin/playlist/", methods=["POST"]) @login_required -def update_playlist(screen): - config = load_config() - cfg = config["screens"][screen] - +def save_playlist(screen): + cfg = load_config() data = request.get_json() - cfg["playlist"] = data.get("playlist", []) - save_config(config) + if screen == "priority": + cfg["priority"]["playlist"] = data["playlist"] + else: + cfg["screens"][screen]["playlist"] = data["playlist"] + + save_config(cfg) return "", 204 + # ------------------------------------------------- # Main # ------------------------------------------------- diff --git a/config.json b/config.json old mode 100644 new mode 100755 index 4cbce4c..70acafc --- a/config.json +++ b/config.json @@ -1,11 +1,18 @@ { + "priority": { + "enabled": true, + "playlist": [] + }, "screens": { "lobby": { "interval": 10, "show_images": true, "show_videos": true, "playlist": [ - "QRCode.png" + "HilfeKI.jpg", + "QRCode.png", + "Will_AI_Replace_Developers_1.jpg", + "Video CANCOM Jahresr\u00fcckblick 2025.MP4" ] }, "casino": { @@ -13,20 +20,12 @@ "show_images": true, "show_videos": true, "playlist": [ - "HilfeKI.jpg", - "IMG_6730.jpeg", "JET-Design.JPG" ] - }, - "Erik": { - "interval": 10, - "show_images": true, - "show_videos": true, - "playlist": [] } }, "admin": { "username": "admin", - "password": "wooper-01" + "password": "cancom@2026!" } } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 diff --git a/static/cancom.svg b/static/cancom.svg new file mode 100755 index 0000000..a7db991 --- /dev/null +++ b/static/cancom.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100755 index 0000000..39dc672 Binary files /dev/null and b/static/favicon.ico differ diff --git a/templates/admin.html b/templates/admin.html old mode 100644 new mode 100755 index c8c9c29..d4967de --- a/templates/admin.html +++ b/templates/admin.html @@ -3,6 +3,7 @@ CANCOM Simple Signage Admin + @@ -112,6 +128,88 @@
+ + + +
+
+
+ + ⚠ PRIORITY (global) + +
+ wirkt auf alle Player +
+ +
+

+ Inhalte dieser Playlist werden auf allen Playern + zusätzlich zur normalen Playlist angezeigt. +
+ Die Wiedergabe erfolgt abwechselnd + (Normal → Priority → Normal → …). +

+ + +

Medien hochladen

+
+
+ + +
+
+ + +

Playlist Reihenfolge

+ +
    + {% for file in priority_files %} +
  • + + {{ file.name }} + + {% if file.type == "image" %} + Bild + {% else %} + Video + {% endif %} + + {{ file.size }} KB + + + +
    + +
    + + + + +
  • + {% endfor %} +
+ + + +
+
+ + {% for screen, cfg in screens.items() %}
@@ -157,7 +255,7 @@
-
Medien hochladen
+

Medien hochladen

@@ -167,7 +265,7 @@ -
Playlist Reihenfolge
+

Playlist Reihenfolge

    {% for file in media_files[screen] %} @@ -215,7 +313,7 @@
    - © {{ year }} CANCOM · Version {{ version }} · {{ hostname }} + © {{ year }} CANCOM GmbH · Version {{ version }} · {{ hostname }}
    @@ -224,8 +322,8 @@