From 5ac9dee8fcdd1eeb8e9009f71bc47e539688b920 Mon Sep 17 00:00:00 2001 From: Erik Thiele Date: Wed, 22 Apr 2026 20:26:00 +0200 Subject: [PATCH] Version 2.0.0 --- .DS_Store | Bin 10244 -> 12292 bytes README.md | 0 app.py | 186 +++++++++++++++++++++++++++++++----------- config.json | 19 ++--- requirements.txt | 0 static/cancom.svg | 4 + static/favicon.ico | Bin 0 -> 1150 bytes templates/admin.html | 126 ++++++++++++++++++++++++++-- templates/login.html | 0 templates/player.html | 182 +++++++++++++++++++++++++++++------------ 10 files changed, 398 insertions(+), 119 deletions(-) mode change 100644 => 100755 README.md mode change 100644 => 100755 app.py mode change 100644 => 100755 config.json mode change 100644 => 100755 requirements.txt create mode 100755 static/cancom.svg create mode 100755 static/favicon.ico mode change 100644 => 100755 templates/admin.html mode change 100644 => 100755 templates/login.html mode change 100644 => 100755 templates/player.html diff --git a/.DS_Store b/.DS_Store index 9ce562472bc2cdfb6743625a14839d2d60a7edec..0e8361f3b6e573e1d47254d93689441173f15636 100644 GIT binary patch delta 250 zcmZn(Xi1P~U|?W$DortDV9)?EIe-{M3-ADifgFX2@&=4D8v~{>PQEB1w^@+s8|%ad z-p%YBEF6r2n|TCe*e5G6%1$YiXlf-0Gfa+L*7zi}DfrKlPYq^0ge9t_YU&WV&5$Ku8DJnB3Zxm>r RJVzvb^Ep{gCWsM?DFBtIIz#{f delta 198 zcmZokXbF&KU|?W$DortDU{C-uIe-{M3vdI4fgFj6@&=6J8v~{>%B3=-Fk~_$rW7aV zB<1JlFalMvGUPHqg*OW_ePf;2z`L29gN1{UcQcQG3_Ax*?PNi5@ySXOYLn;6rBAFe zXXKo$sPK}JaWl8#L$=AP@&=NuK*NAQ0cb0OE0XQpKx5xCPv+P0") 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 0000000000000000000000000000000000000000..39dc6726724f39f8ef3a0992db129addf9c24a67 GIT binary patch literal 1150 zcmZ|Nzb`{k6u|K#>PIxvrlOm)69x&3M64zegGBsV85#)-7Sj{|gi(Tt+1!PNM8ts9 zP!o}ehDeH1-}8FU%hNvO^X_@)-gEAK=@n_>KbaJsS;@sj(jw9iQe89xUS!7r4P0b}@?t{Pp1;E;q(_iVCU`-pTDFgP>ev zj6nCQultF}?UD1(p-qV6II3HB&HE;%v4Kli?=tZ`{4+3);t4i?ORVSdztfFFj92)^ z9Tw5#_Fr;&tM_yc;d+gyu!=sk!1mYkS*?EWjLrEAc<4JCdb<5rn6r8vb8fwr`(sqj z)sHjx3hQx9tar!u4aPqW-3#l|IDyrF5bt0R+NT#;M3vij>D?;9dgyqMa};m@Zhzw+ zdF|1PH9RBS!{Rsc6OGli40GB;f0Ie*_dntbTK|a$tp8W9dFD+gvEH8r=-rsZFe1+% s?#UYK5jlp}YFx>Pl-fk{&EPhA 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 @@