# CANCOM Simple Signage — Agent Guide ## Start ```bash pip install -r requirements.txt python app.py docker compose up -d ``` `python app.py` (via `.venv/bin/python app.py`) serves on `http://localhost:5005`. `app.py` runs Flask with `debug=True`, `host="0.0.0.0"`, and `port=5005`. ## Source Of Truth - Single Flask app in `app.py`; there is no database. - Persistent state is `config.json` plus files under `media///`. - Port 5005; `README.md` hat den korrekten Port. - Existing repo instructions in this file are the main local guidance; there is no `opencode.json` or workflow config in this repo. ## Multi-Standort-URL-Struktur (seit v4.2.0) Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens. | Route | Beschreibung | |-------|-------------| | `GET /player//` | Player für Screen an einem Standort | | `GET /admin` | Redirect zum ersten Standort | | `GET /admin/` | Admin-Dashboard für einen Standort | | `GET /admin//priority` | Priority-Playlist als separate Seite | | `GET /media///` | Medien-Datei ausliefern | | `GET /media/priority/` | Priority-Medien (global) | | `GET /media//background/` | Hintergrundbild der Willkommensseite | | `GET /playlist///hash` | Playlist-Checksumme für Auto-Reload | | `GET /willkommen?site=` | Willkommensseite für Standort generieren | | `POST /admin//upload-background` | Hintergrundbild für Willkommensseite hochladen | | `POST /admin//delete-background` | Hintergrundbild zurücksetzen auf Standard | | `POST /api/customer` | API-Endpunkt (JSON mit "site"-Feld) | | `GET /admin//add-screen?name=` | Neuen Screen anlegen | | `POST /admin//delete-screen/` | Screen + Medien löschen | | `GET /add-site?name=` | Neuen Standort anlegen | | `POST /admin//update-actions/` | Aktionen-Einstellungen speichern (Custom-URL + Position) | | `POST /admin//update-voice/` | Voice-Agent-Einstellungen speichern (Enabled, Label, Target, Position) | | `POST /admin//delete-site` | Standort + alle Screens/Medien löschen | ## Behavior To Preserve - `GET /player//` renders the playlist and auto-reloads from `/playlist///hash`. - `GET /admin` requires login; `config.json.admin` holds the credentials. - URL playlist items are stored as dicts like `{"url": "https://...", "zoom": 0.8}` and the zoom value must survive save/reorder flows. - `.html` items in `media/` are rendered inline as content, not in an iframe. - `config.priority.enabled` makes the priority playlist show on every screen. - `POST /api/customer` generates `welcome.html` and inserts it at the front of the lobby playlist for the specified site. - New standorte can be added by creating `media///` directories and optionally adding config to `config.json["sites"][]`. - Priority playlist (`config.priority`) is global and affects all sites/screens. - Willkommensseite (`customer.html`) accepts up to 3 customer names; logos are fetched via OpenAI→Brandfetch and displayed in a flex row. - `generate_welcome_html(customer_names, logo_urls)` takes lists for up to 3 customers; logos have equal width (280px) with `max-height: 180px`. - `customer_names` are preserved in form fields after POST (via `value`-Attribute). - Admin-UI nutzt keyadmin-Design: `brand-surface` (#2b2f36), `nav-surface` (rot #DA002D), Dark Mode per `localStorage("signage-theme")`. - Gemeinsame HTML-Bausteine: `_header.html`, `_footer.html`, `_styles.html` (CSS-Variablen `--ccm-*`, Dark Mode, Card-Border-Radius 1rem). - `add_customer_to_lobby_playlist` entfernt `welcome.html` sowohl als String als auch als Dict aus der Playlist vor dem Einfügen. - `add_screen` legt ein Verzeichnis unter `media///` an und einen Config-Eintrag. - `delete_screen` entfernt den Screen aus der Config und löscht das Verzeichnis rekursiv. - Custom-URL-Button: pro Screen konfigurierbar (`custom_url` + `custom_url_label` + `custom_url_enabled` + `custom_url_target` + `custom_url_position`) im Admin-Formular (Aktionen-Tab); wird im Player als Button an wählbarer Position (9 Positionen: oben/mitte/unten × links/mitte/rechts) angezeigt und öffnet die URL wahlweise in einem iframe-Overlay mit Zurück-Button (`overlay`) oder per Direkt-Weiterleitung (`redirect`); Player pausiert während das Overlay geöffnet ist. Die unteren Positionen weichen automatisch 16px über dem Newsticker-Balken aus. - Voice-Agent-Button: globale `config.voice_agent_url` wird pro Screen im Tab "Digital Voice Agent" konfiguriert; Einstellungen (`voice_agent_enabled`, `voice_agent_label`, `voice_agent_target`, `voice_agent_position`) funktionieren identisch zum Aktionen-Tab. Die URL ist read-only im Admin sichtbar und wird global in `config.json` gesetzt. - Voice-Agent-Typewriter: Tagline im Button zeigt wechselnde mehrsprachige Texte mit Buchstaben-für-Buchstaben-Effekt; nach 2,5s Pause fade-out über 0,8s, dann nächster Text. - Voice-Agent-Overlay: positioniert den Zurück-Button exakt an der Position des geklickten Buttons (identisches Positionssystem). - `voice_agent_show_image` (per Screen): optionales Bild (`static/dva.png`) über dem Button, per Admin-Toggle schaltbar (Beta). - Action-Button (custom_url) und Voice-Agent-Button haben einheitliches Styling: weißer Hintergrund, schwarzer Text, 20px Border-Radius, fette Schrift. - Neue Standorte können über den `+`-Button im Header oder über `GET /add-site?name=` angelegt werden. - `delete_site` entfernt den Standort aus Config und löscht das Medienverzeichnis rekursiv. - `config.json["server_url"]` (z. B. `http://signage.ccmake.de`) wird in der Admin-Ansicht für die Player-URLs verwendet. - Screen-Card-Body hat Tabler-Tabs: **Playlist** (1, aktiv), **Einstellungen** (2), **Aktionen** (3), **Digital Voice Agent** (4), **Medien** (5); Priority-Seite ebenfalls Tabs **Playlist** und **Medien**. - `stay_on_first`: Wenn aktiviert bleibt der Player auf dem ersten Playlist-Element stehen (kein Durchlauf). - Tab-Reihenfolge in Screen-Cards: Playlist → Einstellungen → Aktionen → Digital Voice Agent → Medien. - Player-URL im Screen-Header ist ein klickbarer Link in grauer Farbe. ## Repo Quirks - `media/` and media file extensions are gitignored. - The app has no configured tests, lint, typecheck, formatter, or CI. - Hardcoded secrets exist in tracked files; do not commit new secrets or reshuffle them casually. - Wichtige Helper-Funktionen in `app.py`: `load_config()`, `save_config()`, `get_site_list()`, `get_screen_config()`, `is_url()`, `normalize_url()`, `playlist_item_name()`, `playlist_item_enabled()`, `load_priority_files()`, `prio_redirect()`, `get_background_url()`. - Hintergrundbild der Willkommensseite wird pro Standort unter `media//background.*` gespeichert; Fallback auf `static/wallpaper.png` wenn keine Datei existiert. - `get_background_url(site)` prüft auf benutzerdefiniertes Hintergrundbild für einen Standort. - `generate_welcome_html(customer_names, logo_urls, background_url=None)` akzeptiert optionalen `background_url`-Parameter. - `welcome.html` wird in `media//lobby/` gespeichert. - `search_customer_logo` in `generate_welcome_page.py` nutzt OpenAI GPT-4 + Brandfetch CDN. - `admin_priority` rendert `priority.html` mit `site_list`, `current_site`, `priority_files`, `server_url`. - `static/dva.png`: Bild für den Voice-Agent-Button (optional, per Admin-Toggle ein-/ausblendbar).