# CANCOM Simple Signage Browserbasiertes Digital-Signage-System für interne Info-Screens. --- ## Features - Browserbasierter Player (Kiosk-Modus, Chromium, Firefox) - Bilder, Videos (MP4/H.264), HTML-Seiten, URL-Playlist-Einträge - **Multi-Standort**: Standorte (sites) gruppieren Screens - **Priority-Playlist**: globale Inhalte wirken auf alle Player - **Willkommensseite**: bis zu 3 Kundenlogos via OpenAI + Brandfetch - Pro Screen eigene Playlist mit Drag-&-Drop-Reihenfolge - Auto-Reload bei Playlist-Änderungen - Newsticker pro Screen - **Custom-URL-Button**: pro Screen konfigurierbarer Aktions-Button oben links im Player (öffnet URL in iframe-Overlay mit Zurück-Button oder per Direkt-Weiterleitung) - Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Medien (Tabler Tabs) - Priority-Seite ebenfalls mit Tabs: Playlist und Medien - Dark Mode (localStorage-persistiert) - CI-konformes Admin-UI (CANCOM-Design: `brand-surface`, `nav-surface` rot) --- ## Architektur ``` Browser (Player) │ ▼ Flask App (Server) ├── Admin UI /admin/ ├── Player UI /player// ├── Priority-Seite /admin//priority ├── config.json ├── media/ │ ├── / │ │ ├── / │ │ │ ├── bild.jpg │ │ │ ├── video.mp4 │ │ │ └── welcome.html │ └── priority/ └── generate_welcome_page.py ``` - **Server:** Python 3 + Flask - **Player:** Jeder moderne Browser (Chrome Kiosk, Edge, Firefox) - **State:** `config.json` + Dateisystem - **Frontend:** Tabler Core + Tabler Icons + SortableJS (CDN) --- ## Projektstruktur ``` signage/ ├── app.py # Flask-App (alle Routen) ├── generate_welcome_page.py # Logo-Suche + Willkommensseite-Generierung ├── config.json # Persistente Konfiguration ├── media/ │ ├── / │ │ ├── lobby/ │ │ ├── casino/ │ │ └── videosysteme/ │ └── priority/ ├── templates/ │ ├── admin.html # Admin-Dashboard (Übersicht) │ ├── priority.html # Priority-Playlist (eigene Seite) │ ├── customer.html # Willkommensseite-Formular │ ├── player.html # Player-Ansicht │ ├── login.html │ ├── _header.html # Gemeinsamer Header │ ├── _footer.html # Gemeinsamer Footer │ └── _styles.html # Zentrale CSS (Variablen, Dark Mode) ├── static/ │ ├── cancom.svg │ └── wallpaper.png └── AGENTS.md ``` --- ## Installation ### Voraussetzungen - Python ≥ 3.9 - pip - ffmpeg (optional, für Videokonvertierung) ### Setup ```bash pip install flask flask-login werkzeug openai ``` ### Starten ```bash python app.py ``` Server läuft auf `http://localhost:5005`. ### Docker ```bash docker compose up -d ``` --- ## Routen | Route | Beschreibung | |-------|-------------| | `GET /` | 404 – kein Root-Endpunkt | | `GET /login` | Admin-Login | | `GET /logout` | Ausloggen | | `GET /admin` | Redirect zum ersten konfigurierten Standort | | `GET /admin/` | Admin-Dashboard für einen Standort | | `GET /admin//priority` | Priority-Playlist (separate Seite) | | `POST /admin//update/` | Screen-Einstellungen speichern | | `POST /admin//upload/` | Medien hochladen | | `POST /admin//add-url/` | URL zur Playlist hinzufügen | | `POST /admin//delete//` | Datei löschen | | `POST /admin//playlist/` | Playlist-Reihenfolge speichern (JSON) | | `POST /admin//delete-screen/` | Screen + Medien löschen | | `GET /admin//add-screen?name=` | Neuen Screen anlegen | | `GET /add-site?name=` | Neuen Standort anlegen | | `POST /admin//delete-site` | Standort + alle Screens/Medien löschen | | `GET /player//` | Player-Ansicht | | `GET /playlist///hash` | Playlist-Checksumme (für Auto-Reload) | | `GET /willkommen?site=` | Willkommensseite-Formular (GET + POST) | | `POST /api/customer` | API-Endpunkt für Willkommensseite (JSON) | | `GET /media///` | Medien-Datei ausliefern | | `GET /media/priority/` | Priority-Medien (global) | | `GET /add-site?name=` | Neuen Standort anlegen | | `POST /admin//delete-site` | Standort + alle Screens/Medien löschen | ### Admin-Portal ``` http://localhost:5005/admin/ ``` - Screens konfigurieren (Tab-basiert: Playlist, Einstellungen, Medien) - Medien hochladen / löschen - Playlist per Drag & Drop sortieren - Priority-Playlist verwalten (ebenfalls mit Tabs) - Willkommensseite generieren (bis zu 3 Kundenlogos) - Custom-URL-Aktionsbutton pro Screen konfigurieren (iframe-Overlay oder Weiterleitung) - Standorte anlegen & löschen ### Player-URL ``` http://localhost:5005/player// ``` Beispiel: ``` http://localhost:5005/player/stuttgart/lobby ``` ### Willkommensseite ``` http://localhost:5005/willkommen?site=stuttgart ``` Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `welcome.html` wird in der Lobby-Playlist vorne eingefügt. --- ## Konfiguration (`config.json`) ```json { "server_url": "http://signage.ccmake.de", "admin": { "username": "...", "password": "..." }, "sites": { "stuttgart": { "screens": { "lobby": { "playlist": [...], "interval": 10 }, "casino": { "playlist": [...], "interval": 15 } } } }, "priority": { "enabled": true, "playlist": [...] } } ``` --- ## Helper-Funktionen (`app.py`) - `load_config()` / `save_config()` – JSON lesen/schreiben - `get_site_list(cfg)` – alle Standorte sortiert - `get_screen_config(cfg, site, screen)` – Screen-Konfiguration mit Defaults - `is_url(item)` – prüft ob Item eine URL ist - `normalize_url(item)` – normalisiert URL-Item zu `{"url", "zoom"}` - `playlist_item_name(item)` – Name aus String oder Dict extrahieren - `playlist_item_enabled(item)` – Enabled-Status prüfen - `load_priority_files()` – Priority-Playlist + Dateien laden - `prio_redirect(site)` – Redirect-Pfad zur Priority-Seite --- ## Dark Mode Wird über `localStorage("signage-theme")` persistiert. Umschalt-Button im Header. CSS-Variablen `--ccm-*` in `_styles.html`. --- ## Sicherheit - Admin-Bereich per Flask-Login geschützt - Player-Seiten Read-Only - Externe Nutzung via Reverse Proxy + TLS empfohlen --- ## Git & Medien > Das Repository ist für Code gedacht, nicht für Medien. `media/` und Medien-Dateiendungen sind in `.gitignore` ausgeschlossen. --- ## Video-Empfehlungen - Format: MP4 (H.264) - Auflösung: max. 1920×1080 ```bash ffmpeg -i input.mov -c:v libx264 -pix_fmt yuv420p -movflags +faststart output.mp4 ``` --- ## Maintainer **CANCOM Simple Signage** – Interne Lösung, nicht für externe Weitergabe bestimmt.