diff --git a/AGENTS.md b/AGENTS.md index 1ec370b..8c4fde7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,6 +124,9 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens. - **Logo-Upload**: Pro Kunde kann im Willkommensseiten-Formular eine Bilddatei hochgeladen werden (`logo_file_1..3`); ein Upload hat Vorrang vor `search_customer_logo`. Ablage als `media//logos/logo_.png`, Auslieferung über `/media//logos/`. Die URL traegt einen `?v=`, damit Player kein altes Logo aus dem Cache zeigen. - `remove_logo_background()` arbeitet mit Pillow (Flood Fill von den Raendern, weiche Kante). **Bewusst kein ML-Modell** wie rembg: der Container laeuft mit read-only Root-FS ohne Modell-Cache. Flood Fill statt globalem Farbvergleich ist wesentlich - sonst verschwindet auch Weiss *innerhalb* des Logos. Bereits transparente Bilder werden unveraendert durchgereicht; bei unruhigem Bildrand bleibt das Bild unangetastet und die Funktion gibt einen Hinweis zurueck. - `welcome_data` in der Site-Config wird **feldweise** gespeichert (Slot 1-3, auch leere), nicht nur die erfolgreichen Kunden - sonst verschiebt sich die Zuordnung von Name, Logo und Ansprechpartner beim naechsten Aufruf. Ein zuvor hochgeladenes Logo wird beim erneuten Generieren wiederverwendet, solange der Kundenname im selben Feld unveraendert ist. +- **Der Schalter `welcome_enabled` wirkt sofort**: `update_screen` ruft bei einer Aenderung `sync_welcome_for_screen()`, das die vorhandene `welcome.html` in den Screen kopiert und vorne einfuegt bzw. beim Abschalten Eintrag und Datei entfernt. Kein erneutes Generieren noetig - die Seite ist je Standort identisch. Die Funktion aendert nur das uebergebene config-Dict, gespeichert wird vom Aufrufer. +- `save_welcome_page` legt zusaetzlich eine Standort-Kopie unter `media//welcome.html` an, die **nie geloescht** wird. Ohne sie waere nach dem Abschalten aller Screens keine Quelle mehr da und der Schalter wuerde stillschweigend nichts tun. +- Beim Generieren wird `welcome.html` aus abgewaehlten Screens auch als **Datei** entfernt, nicht nur aus der Playlist: bei leerer Playlist spielt `player()` sonst noch nicht verzeichnete Dateien aus dem Ordner ab. - Weder `save_welcome_page` noch `add_customer_to_playlists` legen Screens an. Das frühere `screens.setdefault("lobby", {})` erzeugte an Standorten ohne Lobby stillschweigend einen Screen. - `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. diff --git a/README.md b/README.md index cadef1f..6f7db23 100755 --- a/README.md +++ b/README.md @@ -294,7 +294,9 @@ http://localhost:5005/willkommen?site=stuttgart Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `welcome.html` wird in den Playlists aller Zielscreens vorne eingefügt. Pro Standort kann ein eigenes Hintergrundbild hochgeladen werden (Fallback auf `static/wallpaper.png`). -**Zielscreens:** Jeder Screen mit `welcome_enabled` (Schalter „Willkommensseite hier anzeigen" im Tab Einstellungen). Besitzt kein Screen des Standorts den Schlüssel, greift das alte Verhalten und die Seite geht nur in `lobby`. Wird ein Screen später abgewählt, verschwindet die Willkommensseite dort beim nächsten Generieren. +**Zielscreens:** Jeder Screen mit `welcome_enabled` (Schalter „Willkommensseite hier anzeigen" im Tab Einstellungen). Besitzt kein Screen des Standorts den Schlüssel, greift das alte Verhalten und die Seite geht nur in `lobby`. + +**Der Schalter wirkt sofort:** Beim Einschalten wird eine bereits vorhandene Willkommensseite unmittelbar in die Playlist des Screens gesetzt, beim Ausschalten wieder entfernt (samt Datei) – ohne erneutes Generieren. Die Seite ist je Standort identisch, ein zweiter Handgriff wäre also nur Aufwand ohne anderes Ergebnis. Beim Generieren wird zusätzlich eine Standort-Kopie unter `media//welcome.html` abgelegt, die nie gelöscht wird; dadurch findet der Schalter auch dann noch eine Quelle, wenn die Seite zwischenzeitlich auf allen Screens abgeschaltet war. **Logo-Upload:** Findet die automatische Suche kein Logo (oder passt es nicht), kann pro Kunde eine Bilddatei hochgeladen werden. Ein Upload hat Vorrang vor der Suche. Die Datei wird als `media//logos/logo_.png` abgelegt und über `/media//logos/` ausgeliefert. @@ -509,6 +511,8 @@ Optional pro Eintrag. Ohne `schedule` läuft der Eintrag immer (sofern `enabled` - `welcome_target_screens(config, site)` – Zielscreens der Willkommensseite (Fallback `lobby`) - `save_uploaded_logo(file, site, slot, freistellen=True)` – hochgeladenes Logo freistellen und als PNG ablegen - `remove_logo_background(image)` – Hintergrund per Flood Fill entfernen, gibt `(bild, hinweis)` zurück +- `sync_welcome_for_screen(config, site, screen, enabled)` – Willkommensseite beim Umlegen des Schalters platzieren bzw. entfernen +- `find_welcome_source(site, config)` – vorhandene `welcome.html` als Kopiervorlage finden - `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` diff --git a/app.py b/app.py index 337a6a5..863d32a 100755 --- a/app.py +++ b/app.py @@ -1591,7 +1591,15 @@ def update_screen(site, screen): cfg["newsticker_text"] = request.form.get("newsticker_text", "") cfg["newsticker_enabled"] = "newsticker_enabled" in request.form cfg["stay_on_first"] = "stay_on_first" in request.form - cfg["welcome_enabled"] = "welcome_enabled" in request.form + welcome_an = "welcome_enabled" in request.form + vorher = cfg.get("welcome_enabled") + cfg["welcome_enabled"] = welcome_an + + # Beim Umlegen des Schalters die Willkommensseite sofort platzieren bzw. + # entfernen - sie ist je Standort identisch, ein erneutes Generieren + # waere also nur ein zusaetzlicher Handgriff ohne anderes Ergebnis. + if vorher != welcome_an: + generate_welcome_page.sync_welcome_for_screen(config, site, screen, welcome_an) save_config(config) return redirect(f"/admin/{site}") diff --git a/generate_welcome_page.py b/generate_welcome_page.py index 7b2bc41..b8f10ad 100644 --- a/generate_welcome_page.py +++ b/generate_welcome_page.py @@ -499,6 +499,12 @@ def save_welcome_page(customer_names, logo_urls, contacts=None, site="stuttgart" html_content = generate_welcome_html(customer_names, logo_urls, contacts=contacts, site=site, background_url=background_url) + # Standort-Kopie zuerst - sie ueberlebt das Abschalten aller Screens + master = _welcome_master_path(site) + os.makedirs(os.path.dirname(master), exist_ok=True) + with open(master, "w", encoding="utf-8") as f: + f.write(html_content) + for screen in screens: welcome_dir = os.path.join(MEDIA_DIR, site, screen) os.makedirs(welcome_dir, exist_ok=True) @@ -530,6 +536,87 @@ def _strip_welcome(playlist, filename): ] +def _welcome_path(site, screen): + return os.path.join(MEDIA_DIR, site, screen, WELCOME_FILENAME) + + +def _welcome_master_path(site): + """Standort-Kopie der Willkommensseite. Sie liegt neben den Screen-Ordnern, + wird beim Generieren mitgeschrieben und nie geloescht - damit der Schalter + eines Screens die Seite auch dann noch platzieren kann, wenn sie zuvor + auf allen Screens abgeschaltet war.""" + return os.path.join(MEDIA_DIR, site, WELCOME_FILENAME) + + +def find_welcome_source(site, config): + """Sucht eine vorhandene welcome.html für einen Standort. + Die Seite ist je Standort identisch, jede Kopie taugt als Quelle. + Gibt den Pfad zurück oder None, wenn noch nie eine erzeugt wurde.""" + master = _welcome_master_path(site) + if os.path.exists(master): + return master + + screens = config.get("sites", {}).get(site, {}).get("screens", {}) + for screen in sorted(screens): + pfad = _welcome_path(site, screen) + if os.path.exists(pfad): + return pfad + return None + + +def _remove_welcome_file(site, screen): + """Löscht die welcome.html eines Screens, falls vorhanden.""" + pfad = _welcome_path(site, screen) + if os.path.exists(pfad): + try: + os.remove(pfad) + except OSError as e: + print(f"⚠️ Generate - welcome.html in {site}/{screen} nicht löschbar: {e}") + + +def sync_welcome_for_screen(config, site, screen, enabled): + """Bringt einen einzelnen Screen mit dem Schalter "Willkommensseite hier + anzeigen" in Einklang - ohne dass neu generiert werden muss. + + enabled=True : vorhandene welcome.html hineinkopieren und vorne einfügen + enabled=False: Eintrag entfernen und die Kopie löschen + + Verändert nur das übergebene config-Dict; das Speichern übernimmt der + Aufrufer. Gibt True zurück, wenn sich an der Playlist etwas geändert hat.""" + screen_cfg = config.get("sites", {}).get(site, {}).get("screens", {}).get(screen) + if screen_cfg is None: + return False + + playlist = screen_cfg.setdefault("playlist", []) + vorher = list(playlist) + + if not enabled: + _strip_welcome(playlist, WELCOME_FILENAME) + _remove_welcome_file(site, screen) + return playlist != vorher + + quelle = find_welcome_source(site, config) + if not quelle: + # Noch keine Willkommensseite erzeugt - dann gibt es nichts zu platzieren. + return False + + ziel = _welcome_path(site, screen) + if os.path.abspath(quelle) != os.path.abspath(ziel): + try: + os.makedirs(os.path.dirname(ziel), exist_ok=True) + with open(quelle, "rb") as q, open(ziel, "wb") as z: + z.write(q.read()) + except OSError as e: + print(f"❌ Generate - welcome.html nach {site}/{screen} nicht kopierbar: {e}") + return False + + # Vorhandenen Eintrag entfernen und vorne neu einfügen + _strip_welcome(playlist, WELCOME_FILENAME) + playlist.insert(0, {"name": WELCOME_FILENAME, "enabled": True}) + print(f"✅ Generate - Willkommensseite in {site}/{screen} platziert") + return True + + def add_customer_to_playlists(html_filename, site="stuttgart", screens=None, valid_until=None): """Setzt die Willkommensseite an Position 0 der Playlist jedes Zielscreens. @@ -570,6 +657,11 @@ def add_customer_to_playlists(html_filename, site="stuttgart", screens=None, val _strip_welcome(playlist, html_filename) if name in targets: playlist.insert(0, dict(entry)) + else: + # Auch die Datei entfernen - sonst bliebe auf einem abgewaehlten + # Screen eine verwaiste Kopie liegen, die bei leerer Playlist + # sogar noch abgespielt wuerde. + _remove_welcome_file(site, name) save_config(config) diff --git a/templates/help.html b/templates/help.html index 72eb143..87b110f 100644 --- a/templates/help.html +++ b/templates/help.html @@ -250,7 +250,7 @@
  • Aufruf über /willkommen?site=<standort> (oder Menüpunkt)
  • Zielscreens: Die generierte welcome.html wird automatisch an den Anfang der Playlist jedes Screens gesetzt, bei dem im Tab "Einstellungen" die Option Willkommensseite hier anzeigen aktiv ist. So lässt sich dieselbe Begrüßung auf mehreren Screens zeigen, ohne sie mehrfach anzulegen. Ist an einem Standort nichts eingestellt, geht sie wie bisher in die Lobby.
  • -
  • Wird ein Screen später abgewählt, verschwindet die Willkommensseite dort beim nächsten Generieren automatisch.
  • +
  • Der Schalter wirkt sofort: Schaltest du ihn bei einem weiteren Screen ein, erscheint eine bereits erzeugte Willkommensseite dort umgehend in der Playlist – ohne dass du sie neu generieren musst. Schaltest du ihn aus, verschwindet sie dort wieder.
  • Anzeigen bis: Optionales Datum im Formular. Ist es gesetzt, verschwindet die Willkommensseite nach diesem Tag auf allen Screens von selbst – kein manuelles Aufräumen nach dem Besuch. Ohne Angabe bleibt sie stehen.
  • Logo hochladen: Findet die automatische Suche kein Logo oder passt es nicht, kann pro Kunde eine eigene Bilddatei hochgeladen werden. Ein Upload hat immer Vorrang vor der automatischen Suche.
  • Freistellen: Der Hintergrund eines hochgeladenen Logos wird automatisch entfernt, damit es auf der Willkommensseite nicht in einem weissen Kasten steht. Das gelingt am besten bei einem Logo auf einfarbigem Grund – ein PNG mit bereits transparentem Hintergrund wird unverändert übernommen. Bei unruhigem Bildrand bleibt das Bild unangetastet und es erscheint ein Hinweis. Mit „Hintergrund behalten" lässt sich das Freistellen abschalten.