Files
signage/README.md
2026-06-07 12:35:10 +02:00

273 lines
7.8 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<site>
├── Player UI /player/<site>/<screen>
├── Priority-Seite /admin/<site>/priority
├── config.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
├── media/
│ ├── <site>/
│ │ ├── 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 -r requirements.txt
```
### 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/<site>` | Admin-Dashboard für einen Standort |
| `GET /admin/<site>/priority` | Priority-Playlist (separate Seite) |
| `POST /admin/<site>/update/<screen>` | Screen-Einstellungen speichern |
| `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 |
| `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 /media/<site>/<screen>/<file>` | Medien-Datei ausliefern |
| `GET /media/priority/<file>` | Priority-Medien (global) |
### Admin-Portal
```
http://localhost:5005/admin/<standort>
```
- 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/<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.
---
## Konfiguration (`config.json`)
```json
{
"server_url": "http://signage.ccmake.de",
"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"
},
"casino": { "playlist": [...], "interval": 15 }
}
}
},
"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"` |
## 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.