feat: Logo-Upload mit automatischem Freistellen
Findet die automatische Suche kein Logo, kann pro Kunde eine Bilddatei hochgeladen werden. Ein Upload hat Vorrang vor der Suche. Freistellen: - remove_logo_background() entfernt den Hintergrund per Flood Fill von den Bildraendern her. Bewusst kein globaler Farbvergleich - sonst verschwindet auch Weiss innerhalb des Logos (etwa Text in einer Flaeche). Weiche Kante gegen Treppeneffekte. - Umgesetzt mit Pillow, das ueber qrcode[pil] ohnehin vorhanden ist. Bewusst kein ML-Modell wie rembg: der Container laeuft mit read-only Root-FS, ein Modell-Cache zur Laufzeit waere dort nicht moeglich. - Bereits transparente Bilder bleiben unangetastet. Ist der Bildrand nicht einfarbig, wird nicht freigestellt und die Oberflaeche weist darauf hin, statt ein beschaedigtes Logo zu liefern. - Checkbox "Hintergrund behalten" schaltet das Freistellen pro Kunde ab. - Gespeichert wird immer als PNG (JPEG kann keine Transparenz), grosse Bilder werden auf 800px verkleinert. Ablage unter media/<site>/logos/logo_<slot>.png, neue Route /media/<site>/logos/<datei>. Die URL traegt einen Zeitstempel, damit Player kein altes Logo aus dem Cache zeigen. Nebenbei behoben: welcome_data speicherte nur die erfolgreichen Kunden statt der Felder 1-3. Fiel Kunde 1 aus, rutschte Kunde 2 beim naechsten Aufruf in Feld 1 und Logo wie Ansprechpartner verschoben sich mit. Jetzt feldweise - dadurch bleibt auch ein hochgeladenes Logo beim erneuten Generieren erhalten, solange der Kundenname im selben Feld steht. Die drei Kundenbloecke im Formular sind zu einer Schleife zusammengefasst, damit die Upload-Felder nicht dreifach gepflegt werden muessen. README, AGENTS.md und Hilfeseite nachgezogen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
82
app.py
82
app.py
@@ -493,6 +493,14 @@ def media_background(site, filename):
|
||||
return send_from_directory(path, filename)
|
||||
|
||||
|
||||
@app.route("/media/<site>/logos/<path:filename>")
|
||||
def media_logo(site, filename):
|
||||
"""Liefert ein manuell hochgeladenes Kundenlogo aus.
|
||||
Ohne Login erreichbar wie die übrigen Medien - der Player ist nicht angemeldet."""
|
||||
path = os.path.join(MEDIA_DIR, site, generate_welcome_page.LOGO_DIRNAME)
|
||||
return send_from_directory(path, filename)
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
# User-Verwaltung (Flask-Login mit users.json)
|
||||
# -------------------------------------------------
|
||||
@@ -901,6 +909,7 @@ def add_customer():
|
||||
customer_names = ["", "", ""]
|
||||
contacts = ["", "", ""]
|
||||
valid_until = (request.form.get("valid_until") or "").strip()
|
||||
logo_hinweise = []
|
||||
site = request.args.get("site") or request.form.get("site") or ""
|
||||
|
||||
# Fallback auf ersten für den User zugänglichen Standort
|
||||
@@ -934,31 +943,52 @@ def add_customer():
|
||||
error = "Kein Standort ausgewählt"
|
||||
else:
|
||||
try:
|
||||
found_logos = []
|
||||
for name in filled_names:
|
||||
logo = generate_welcome_page.search_customer_logo(name)
|
||||
found_logos.append(logo)
|
||||
# Logo je Kunde bestimmen: ein hochgeladenes Logo hat Vorrang vor
|
||||
# der automatischen Suche - der Upload ist eine bewusste Entscheidung.
|
||||
# Zuletzt verwendete Logos, um einen früheren Upload nicht bei jedem
|
||||
# erneuten Generieren zu verlieren
|
||||
frueher = load_config().get("sites", {}).get(site, {}).get("welcome_data", {})
|
||||
frueher_namen = frueher.get("names", [])
|
||||
frueher_urls = frueher.get("logo_urls", [])
|
||||
logo_prefix = f"/media/{site}/{generate_welcome_page.LOGO_DIRNAME}/"
|
||||
|
||||
valid = [(n, l) for n, l in zip(filled_names, found_logos) if l]
|
||||
found_logos = [None, None, None]
|
||||
for i, name in enumerate(customer_names):
|
||||
if not name:
|
||||
continue
|
||||
datei = request.files.get(f"logo_file_{i + 1}")
|
||||
if datei and datei.filename:
|
||||
url, hinweis = generate_welcome_page.save_uploaded_logo(
|
||||
datei, site, i + 1,
|
||||
freistellen=(f"logo_keep_bg_{i + 1}" not in request.form))
|
||||
found_logos[i] = url
|
||||
if hinweis:
|
||||
logo_hinweise.append(f"{name}: {hinweis}")
|
||||
elif not url:
|
||||
logo_hinweise.append(f"{name}: Logo konnte nicht übernommen werden")
|
||||
continue
|
||||
|
||||
# Kein neuer Upload: ein zuvor hochgeladenes Logo weiterverwenden,
|
||||
# solange der Kundenname in diesem Feld derselbe geblieben ist
|
||||
alt_url = frueher_urls[i] if i < len(frueher_urls) else None
|
||||
alt_name = frueher_namen[i] if i < len(frueher_namen) else None
|
||||
if alt_url and alt_name == name and str(alt_url).startswith(logo_prefix):
|
||||
found_logos[i] = alt_url
|
||||
continue
|
||||
|
||||
found_logos[i] = generate_welcome_page.search_customer_logo(name)
|
||||
|
||||
logo_urls = list(found_logos)
|
||||
valid = [(customer_names[i], found_logos[i], contacts[i])
|
||||
for i in range(len(customer_names))
|
||||
if customer_names[i] and found_logos[i]]
|
||||
|
||||
if not valid:
|
||||
error = "Kein Logo konnte gefunden werden"
|
||||
error = "Kein Logo gefunden – bitte für die betroffenen Kunden ein Logo hochladen"
|
||||
else:
|
||||
valid_names = [n for n, l in valid]
|
||||
valid_urls = [l for n, l in valid]
|
||||
valid_contacts = []
|
||||
for name in valid_names:
|
||||
for i, cn in enumerate(customer_names):
|
||||
if cn == name:
|
||||
valid_contacts.append(contacts[i] if i < len(contacts) else "")
|
||||
break
|
||||
else:
|
||||
valid_contacts.append("")
|
||||
for i, name in enumerate(customer_names):
|
||||
for j, (vn, vu) in enumerate(zip(valid_names, valid_urls)):
|
||||
if vn == name:
|
||||
logo_urls[i] = vu
|
||||
break
|
||||
valid_names = [n for n, u, k in valid]
|
||||
valid_urls = [u for n, u, k in valid]
|
||||
valid_contacts = [k for n, u, k in valid]
|
||||
|
||||
bg_url = get_background_url(site)
|
||||
targets = generate_welcome_page.welcome_target_screens(load_config(), site)
|
||||
@@ -978,7 +1008,14 @@ def add_customer():
|
||||
# Letzte Kundendaten für Background-Update speichern
|
||||
config = load_config()
|
||||
site_cfg = config.setdefault("sites", {}).setdefault(site, {})
|
||||
site_cfg["welcome_data"] = {"names": valid_names, "logo_urls": valid_urls, "contacts": valid_contacts}
|
||||
# Feldweise speichern (Slot 1-3), nicht nur die erfolgreichen -
|
||||
# sonst rutscht Kunde 2 beim naechsten Aufruf in Feld 1 und die
|
||||
# Zuordnung von Logo und Ansprechpartner verschiebt sich mit.
|
||||
site_cfg["welcome_data"] = {
|
||||
"names": list(customer_names),
|
||||
"logo_urls": list(found_logos),
|
||||
"contacts": list(contacts)
|
||||
}
|
||||
save_config(config)
|
||||
add_history_entry("customer_added", f"Kunden ({', '.join(valid_names)}) zur Willkommensseite an Standort '{site}' hinzugefügt (Screens: {', '.join(targets)})", current_user.email)
|
||||
success = (f"✅ Willkommensseite mit {len(valid_names)} Kunden erstellt "
|
||||
@@ -1019,6 +1056,7 @@ def add_customer():
|
||||
customer_names=customer_names,
|
||||
contacts=contacts,
|
||||
welcome_screens=generate_welcome_page.welcome_target_screens(config, site) if site else [],
|
||||
logo_hinweise=logo_hinweise,
|
||||
site=site,
|
||||
site_list=site_list,
|
||||
current_site=site,
|
||||
|
||||
Reference in New Issue
Block a user