From 8de8a116bb8519c5f72e48229a7f504fcdb7308b Mon Sep 17 00:00:00 2001 From: Erik Thiele Date: Wed, 2 Sep 2026 15:06:20 +0200 Subject: [PATCH] fix: hochgeladene Logos auf Anzeigegroesse zuschneiden Ein hochgeladenes Logo erschien auf der Willkommensseite deutlich kleiner als ein automatisch gefundenes. Ursache ist nicht die CSS - beide nutzen dieselbe Klasse - sondern der Bildinhalt: nach dem Freistellen ist der Rand zwar durchsichtig, zaehlt aber weiter zur Bildflaeche. .customer-logo skaliert mit object-fit: contain auf 120px Hoehe, ein Logo mit breitem Rand erscheint dadurch nur einen Bruchteil davon gross (gemessen: 30px statt 120px). Brandfetch-Logos sind bereits eng beschnitten und fuellen den Kasten aus. trim_transparent_border() schneidet die durchsichtigen Raender nach dem Freistellen weg. Greift auch bei bereits transparenten PNGs; bei abgewaehltem Freistellen passiert nichts, da es dort keinen transparenten Rand gibt. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 1 + README.md | 3 ++- generate_welcome_page.py | 23 +++++++++++++++++++++++ templates/help.html | 1 + 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 8c4fde7..8d3627b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -122,6 +122,7 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens. - `add_customer_to_playlists` (früher `add_customer_to_lobby_playlist`) entfernt `welcome.html` als String *und* als Dict aus **allen** Screens des Standorts und fügt sie danach nur in den Zielscreens an Position 0 wieder ein – ein abgewählter Screen bleibt so nicht mit einer alten Begrüßung stehen. Eingefügt wird ein Dict, kein String, damit `enabled` und `schedule` mitgeführt werden können. - `welcome_target_screens(config, site)` fällt nur dann auf `lobby` zurück, wenn **kein** Screen des Standorts den Schlüssel `welcome_enabled` besitzt. Wurde er überall bewusst auf `False` gesetzt, ist die leere Liste das gewollte Ergebnis. - **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. +- Nach dem Freistellen schneidet `trim_transparent_border()` die durchsichtigen Raender weg. Das ist kein Schoenheitsdetail: `.customer-logo` skaliert mit `object-fit: contain` auf 120px Hoehe, ein breiter Rand zaehlt dabei mit und laesst ein hochgeladenes Logo deutlich kleiner erscheinen als ein eng beschnittenes Brandfetch-Logo (gemessen: 30px statt 120px). Der Schnitt greift auch bei bereits transparenten PNGs, aber nicht bei abgewaehltem Freistellen - dort gibt es keinen transparenten Rand. - `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. diff --git a/README.md b/README.md index 6f7db23..44e923e 100755 --- a/README.md +++ b/README.md @@ -300,7 +300,7 @@ Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → ` **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. -**Freistellen:** Beim Upload wird der Hintergrund automatisch entfernt, damit das Logo nicht in einem weissen Kasten auf dem Hintergrundbild steht. Verfahren: Flood Fill von den Bildrändern her (bewusst kein globaler Farbvergleich – so bleibt Weiss *innerhalb* des Logos erhalten), weiche Kante gegen Treppeneffekte, Speicherung immer als PNG. Bereits transparente Bilder werden unverändert übernommen; ist der Bildrand nicht einfarbig, bleibt das Bild unangetastet und die Oberfläche weist darauf hin. Mit der Checkbox „Hintergrund behalten" lässt sich das Freistellen pro Kunde abschalten. Umgesetzt mit Pillow – bewusst ohne ML-Modell, da der Container mit read-only Root-FS läuft und kein Modell-Cache zur Verfügung steht. +**Freistellen und Zuschneiden:** Beim Upload wird der Hintergrund automatisch entfernt, damit das Logo nicht in einem weissen Kasten auf dem Hintergrundbild steht. Anschliessend werden die durchsichtigen Ränder weggeschnitten (`trim_transparent_border`). Ohne diesen Schnitt erscheint ein hochgeladenes Logo kleiner als ein automatisch gefundenes: die Willkommensseite skaliert mit `object-fit: contain` auf eine feste Höhe von 120px, und ein breiter Rand zählt dabei mit – Brandfetch-Logos sind bereits eng beschnitten. Verfahren: Flood Fill von den Bildrändern her (bewusst kein globaler Farbvergleich – so bleibt Weiss *innerhalb* des Logos erhalten), weiche Kante gegen Treppeneffekte, Speicherung immer als PNG. Bereits transparente Bilder werden unverändert übernommen; ist der Bildrand nicht einfarbig, bleibt das Bild unangetastet und die Oberfläche weist darauf hin. Mit der Checkbox „Hintergrund behalten" lässt sich das Freistellen pro Kunde abschalten. Umgesetzt mit Pillow – bewusst ohne ML-Modell, da der Container mit read-only Root-FS läuft und kein Modell-Cache zur Verfügung steht. **Anzeigen bis:** Optionales Datum im Formular. Ist es gesetzt, bekommt der Eintrag eine Zeitsteuerung (`daily 00:00–23:59` mit `valid_until`) und verschwindet danach auf allen Screens von selbst. @@ -511,6 +511,7 @@ 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 +- `trim_transparent_border(img)` – durchsichtige Ränder wegschneiden, damit das Logo den Anzeigebereich ausfüllt - `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 diff --git a/generate_welcome_page.py b/generate_welcome_page.py index b8f10ad..8dda753 100644 --- a/generate_welcome_page.py +++ b/generate_welcome_page.py @@ -65,6 +65,22 @@ def _border_color(px, w, h): return mitte, streuung <= LOGO_BORDER_UNIFORMITY +def trim_transparent_border(img): + """Schneidet durchsichtige Raender weg, sodass das Logo die Bildflaeche + ausfuellt. + + Ohne das erscheint ein hochgeladenes Logo kleiner als ein automatisch + gefundenes: die Willkommensseite skaliert mit object-fit: contain auf + eine feste Hoehe, und ein breiter durchsichtiger Rand zaehlt dabei mit. + Brandfetch-Logos sind bereits eng beschnitten.""" + if img.mode != "RGBA": + return img + bbox = img.getchannel("A").getbbox() + if not bbox or bbox == (0, 0, img.width, img.height): + return img + return img.crop(bbox) + + def remove_logo_background(image, tolerance=LOGO_TOLERANCE): """Stellt ein Logo frei: entfernt den zusammenhängenden Hintergrund von den Rändern her und macht ihn transparent. @@ -175,6 +191,13 @@ def save_uploaded_logo(file_storage, site, slot, freistellen=True): else: img = img.convert("RGBA") + # Nach dem Freistellen zuschneiden, damit das Logo genauso gross erscheint + # wie ein automatisch gefundenes + vorher = img.size + img = trim_transparent_border(img) + if img.size != vorher: + print(f"✅ Generate - Logo zugeschnitten: {vorher[0]}x{vorher[1]} → {img.size[0]}x{img.size[1]}") + logo_dir = os.path.join(MEDIA_DIR, site, LOGO_DIRNAME) os.makedirs(logo_dir, exist_ok=True) dateiname = f"logo_{slot}.png" diff --git a/templates/help.html b/templates/help.html index 87b110f..716bfea 100644 --- a/templates/help.html +++ b/templates/help.html @@ -254,6 +254,7 @@
  • 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.
  • +
  • Grösse: Nach dem Freistellen werden leere Ränder weggeschnitten, damit das hochgeladene Logo auf der Willkommensseite genauso gross erscheint wie ein automatisch gefundenes.
  • Ein einmal hochgeladenes Logo bleibt beim erneuten Generieren erhalten, solange der Kundenname im selben Feld steht.
  • Pro Standort kann ein eigenes Hintergrundbild hochgeladen werden (Tab "Medien" im Standort-Dashboard)
  • Pro Kunde können Ansprechpartner eingegeben werden (Markdown-Format):