2026-06-22 08:37:38 +02:00
2026-06-19 14:17:42 +02:00
2026-06-22 08:37:38 +02:00
2026-06-21 17:56:58 +02:00
2026-06-20 16:56:32 +02:00
2026-06-20 15:05:43 +02:00
2026-06-20 12:09:02 +02:00
.
2026-06-10 14:19:17 +02:00
2026-06-21 17:56:58 +02:00
2026-06-20 16:40:02 +02:00
2026-06-22 08:37:38 +02:00
2026-06-20 16:40:02 +02:00

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 Standort konfigurierbares Hintergrundbild
  • Pro Screen eigene Playlist mit Drag-&-Drop-Reihenfolge
  • Auto-Reload bei Playlist-Änderungen
  • Newsticker pro Screen
  • Custom-URL-Button: pro Screen konfigurierbarer Aktions-Button mit frei wählbarer Position (9 Positionen) im Player (öffnet URL in iframe-Overlay mit Zurück-Button oder per Direkt-Weiterleitung)
  • Digital Voice Agent: global konfigurierbarer Voice-Agent-Button pro Screen (9 Positionen, iframe-Overlay mit positionsgetreuem Zurück-Button, Typewriter-Tagline mit mehrsprachigen Wechseltexten, optionales Bild über dem Button)
  • Stay-on-First: Screen kann auf erstem Playlist-Element stehen bleiben (kein automatischer Refresh)
  • User-Verwaltung: Mehrere User mit Rollen (Admin / Superuser / User), E-Mail als Login, Passwort-Hashing (scrypt), Berechtigungen pro Standort
  • Passwort-Workflow: First-Login-Änderung, Admin-Reset mit temporärem Passwort, Notfall-Login bei fehlendem Hash
  • Admin Dashboard: /admin/dashboard Statistiken (Sites, Screens, User, Admins, Superuser) mit Sparkline-Charts + Trendanzeige, Aktivitätsverlauf
  • Aktivitätsverlauf: Alle Erstell-/Lösch-/Änderungsaktionen sowie Login/Logout werden in history.json protokolliert und im Dashboard angezeigt
  • Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Aktionen, Digital Voice Agent, 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/<site>
├── Player UI       /player/<site>/<screen>
├── Priority-Seite  /admin/<site>/priority
├── config.json
├── users.json
├── history.json
├── media/
│   ├── <site>/
│   │   ├── <screen>/
│   │   │   ├── 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
├── users.json                      # User-Datenbank (gehashte Passwörter)
├── history.json                    # Aktivitätsverlauf (Dashboard)
├── media/
│   ├── <site>/
│   │   ├── lobby/
│   │   ├── casino/
│   │   └── videosysteme/
│   └── priority/
├── templates/
│   ├── admin.html                  # Admin-Dashboard (Übersicht)
│   ├── admin_dashboard.html         # Admin Dashboard (Statistiken + Verlauf)
│   ├── priority.html               # Priority-Playlist (eigene Seite)
│   ├── customer.html               # Willkommensseite-Formular
│   ├── player.html                 # Player-Ansicht
│   ├── login.html
│   ├── change_password.html        # Passwort ändern (First-Login / Reset)
│   ├── user_list.html              # User-Liste (Admin)
│   ├── user_create.html            # User anlegen (Admin)
│   ├── user_edit.html              # User bearbeiten (Admin)
│   ├── _header.html                # Gemeinsamer Header
│   ├── _footer.html                # Gemeinsamer Footer
│   └── _styles.html                # Zentrale CSS (Variablen, Dark Mode)
├── static/
│   ├── cancom.svg
│   ├── dva.png
│   └── wallpaper.png
└── AGENTS.md

Installation

Voraussetzungen

  • Python ≥ 3.9
  • pip
  • ffmpeg (optional, für Videokonvertierung)

Setup

pip install -r requirements.txt

Starten

python app.py

Server läuft auf http://localhost:5005.

Docker

Empfohlenes docker-compose.yml mit Bind-Mounts für persistente Daten:

services:
  signage:
    container_name: signage
    build: .
    ports:
      - "5005:5005"
    restart: unless-stopped
    volumes:
      - ./media:/app/media
      - ./config.json:/app/config.json
      - ./users.json:/app/users.json
      - ./history.json:/app/history.json
    environment:
      - TZ=Europe/Berlin

networks:
  default:
    driver: bridge

Der Container läuft als User mit UID 1000. Daher vor dem ersten Start die Berechtigungen setzen:

chown 1000:1000 config.json users.json history.json
chown -R 1000:1000 media/

Start:

docker compose up -d

Server läuft auf http://localhost:5005 (gunicorn mit 4 Workern, non-root User).


Routen

Route Beschreibung
GET / Weiterleitung zum Login
GET /login Admin-Login
GET /logout Ausloggen
GET /admin Redirect zum ersten konfigurierten Standort
GET /admin/dashboard Admin Dashboard (Statistiken + Verlauf)
GET /admin/<site> Admin-Dashboard für einen Standort
GET /admin/<site>/priority Priority-Playlist (separate Seite)
POST /admin/<site>/update/<screen> Allgemeine Screen-Einstellungen speichern (Intervall, Newsticker, Bilder/Videos, stay_on_first)
POST /admin/<site>/update-actions/<screen> Aktionen-Einstellungen speichern (Custom-URL)
POST /admin/<site>/update-voice/<screen> Voice-Agent-Einstellungen speichern (Enabled, Label, Target, Position, Bild)
POST /admin/<site>/upload/<screen> Medien hochladen
POST /admin/<site>/add-url/<screen> URL zur Playlist hinzufügen
POST /admin/<site>/delete/<screen>/<filename> Datei löschen
POST /admin/<site>/playlist/<screen> Playlist-Reihenfolge speichern (JSON)
POST /admin/<site>/delete-screen/<screen> Screen + Medien löschen
GET /admin/<site>/add-screen?name=<name> Neuen Screen anlegen
GET /add-site?name=<name> Neuen Standort anlegen
POST /admin/<site>/delete-site Standort + alle Screens/Medien löschen
POST /admin/<site>/upload-background Hintergrundbild für Willkommensseite hochladen
POST /admin/<site>/delete-background Hintergrundbild zurücksetzen
GET /player/<site>/<screen> Player-Ansicht
GET /playlist/<site>/<screen>/hash Playlist-Checksumme (für Auto-Reload)
GET /willkommen?site=<site> Willkommensseite-Formular (GET + POST)
POST /api/customer API-Endpunkt für Willkommensseite (JSON)
GET /change-password Passwort ändern (First-Login / nach Admin-Reset)
GET /admin/users User-Liste (Admin)
GET /admin/users/create User anlegen (Admin)
GET /admin/users/edit/<email> User bearbeiten (Admin)
POST /admin/users/create User anlegen (Admin)
POST /admin/users/delete/<email> User löschen (Admin, letzter Admin geschützt)
POST /admin/users/reset-password/<email> Passwort-Reset mit temporärem Passwort (Admin)
POST /admin/users/edit/<email> User bearbeiten speichern (Admin)
GET /media/<site>/<screen>/<file> Medien-Datei ausliefern
GET /media/priority/<file> Priority-Medien (global)
GET /media/<site>/background/<filename> Hintergrundbild der Willkommensseite

Admin Portal

http://localhost:5005/admin/<standort>
  • Screens konfigurieren (Tab-basiert: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien)
  • Medien hochladen / löschen
  • Playlist per Drag & Drop sortieren
  • Priority-Playlist verwalten (ebenfalls mit Tabs)
  • Willkommensseite generieren (bis zu 3 Kundenlogos, Hintergrundbild pro Standort)
  • Custom-URL-Aktionsbutton pro Screen konfigurieren (Position, iframe-Overlay oder Weiterleitung)
  • Digital Voice Agent pro Screen konfigurieren (Position, Bild ein/aus, Typewriter-Tagline)
  • Stay-on-First-Modus pro Screen (kein automatischer Refresh)
  • Standorte anlegen & löschen
  • Userverwaltung: User anlegen, bearbeiten, löschen, Passwort-Reset (temporäres Passwort wird angezeigt)

Admin Dashboard

http://localhost:5005/admin/dashboard
  • 5 Statistik-Cards (Standorte, Screens, User, Admins, Superuser) mit ApexCharts-Sparkline + Trendanzeige
  • Aktivitätsverlauf (History) als Liste mit Zeitstempel, Aktion und User
  • Alle Erstell-/Lösch-/Änderungsaktionen sowie Login/Logout werden protokolliert

Player-URL

http://localhost:5005/player/<standort>/<screen>

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. Pro Standort kann ein eigenes Hintergrundbild hochgeladen werden (Fallback auf static/wallpaper.png).


Globale Konfiguration (config.json)

Feld Typ Beschreibung
server_url string Öffentliche Server-URL für Player-Links
voice_agent_url string URL des Digital Voice Agents (global, read-only im Admin)

User-Rollen (users.json)

Rolle Zugriff Userverwaltung
Admin Alle Standorte Ja (anlegen/bearbeiten/löschen/Reset)
Superuser Alle Standorte Nein
User Nur freigegebene Standorte Nein
  • Usernamen sind E-Mail-Adressen
  • Passwörter werden als scrypt-Hash gespeichert
  • users.json wird beim ersten Start aus config.json.admin befüllt
  • Notfall: users.json löschen → Server-Neustart erzeugt neuen Admin aus config.json
Feld Typ Beschreibung
server_url string Öffentliche Server-URL für Player-Links
voice_agent_url string URL des Digital Voice Agents (global, read-only im Admin)

Konfiguration (config.json)

{
  "server_url": "http://signage.ccmake.de",
  "voice_agent_url": "https://voice-agent.example.com",
  "admin": { "username": "...", "password": "..." },
  "sites": {
    "stuttgart": {
      "screens": {
        "lobby": {
          "playlist": [...],
          "interval": 10,
          "show_images": true,
          "show_videos": true,
          "newsticker_text": "...",
          "newsticker_enabled": false,
          "custom_url_enabled": false,
          "custom_url": "https://...",
          "custom_url_label": "Infos",
          "custom_url_target": "overlay",
          "custom_url_position": "top-left",
          "stay_on_first": false,
          "voice_agent_enabled": true,
          "voice_agent_show_image": false,
          "voice_agent_label": "Digitaler Assistent",
          "voice_agent_target": "overlay",
          "voice_agent_position": "top-left"
        },
        "casino": { "playlist": [...], "interval": 15 }
      },
      "welcome_data": {
        "names": ["Firma A"],
        "logo_urls": ["https://..."],
        "background_url": null
      }
    }
  },
  "priority": {
    "enabled": true,
    "playlist": [...]
  }
}

Screen-Konfiguration (pro Screen in config.json)

Feld Typ Beschreibung
playlist Array Playlist-Einträge (Strings oder Dicts mit url/zoom)
interval int Anzeige-Intervall in Sekunden
show_images bool Bilder anzeigen
show_videos bool Videos anzeigen
newsticker_text string Text für Newsticker (max. 200 Zeichen)
newsticker_enabled bool Newsticker anzeigen
custom_url_enabled bool Custom-URL-Button im Player anzeigen
custom_url string URL des Aktions-Buttons
custom_url_label string Button-Beschriftung
custom_url_target string "overlay" (iframe) oder "redirect"
custom_url_position string Position des Buttons: top-left, top-center, top-right, middle-left, middle-center, middle-right, bottom-left, bottom-center, bottom-right
stay_on_first bool Player bleibt auf erstem Element stehen (kein Refresh)
voice_agent_enabled bool Voice-Agent-Button im Player anzeigen
voice_agent_show_image bool Bild (dva.png) über dem Button anzeigen
voice_agent_label string Button-Beschriftung (Default: "Digitaler Assistent")
voice_agent_target string "overlay" (iframe) oder "redirect"
voice_agent_position string Position des Buttons: top-left, top-center, top-right, middle-left, middle-center, middle-right, bottom-left, bottom-center, bottom-right

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
  • get_background_url(site) URL zum benutzerdefinierten Hintergrundbild oder None
  • load_users() / save_users() / get_user() User aus users.json
  • init_user_db() initialen Admin aus config.json anlegen
  • get_accessible_sites(cfg, user) Standorte nach User-Berechtigung filtern
  • admin_required / site_access_required Zugriffs-Dekoratoren

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 (E-Mail + Passwort)
  • Passwörter gehasht (scrypt) in users.json
  • Drei Rollen: Admin (alle Standorte + Userverwaltung), Superuser (alle Standorte), User (nur zugewiesene Standorte)
  • Zugriffskontrolle per Decorator (@admin_required, @site_access_required)
  • Letzter Admin kann nicht gelöscht werden
  • 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
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.

Description
No description provided
Readme 3.6 MiB
Languages
HTML 63.3%
Python 36.4%
Dockerfile 0.2%
Shell 0.1%