+ in unserer
+ Niederlassung Stuttgart! +
diff --git a/.DS_Store b/.DS_Store index 51e553f..d11ac64 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index c6967ae..cbec4df 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ __pycache__/ test* *.old .venv/ +.DS_Store media/ *.mp4 *.mov diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1bb8819 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 5005 + +CMD ["python", "app.py"] diff --git a/app.py b/app.py index 1a61da7..2893fdc 100755 --- a/app.py +++ b/app.py @@ -1,7 +1,9 @@ import os import json -import socket import hashlib +# Import welcome page functions +import generate_welcome_page + from datetime import datetime from flask import ( @@ -16,6 +18,7 @@ from flask_login import ( ) from werkzeug.utils import secure_filename + # ------------------------------------------------- # Grundkonfiguration # ------------------------------------------------- @@ -101,6 +104,87 @@ def logout(): logout_user() return redirect("/login") +# ------------------------------------------------- +# Customer / Willkommensseite +# ------------------------------------------------- +@app.route("/customer", methods=["GET", "POST"]) +@login_required +def add_customer(): + """Add new customer with logo search and welcome page""" + error = None + success = None + logo_url = None + + if request.method == "POST": + customer_name = request.form.get("customer_name", "").strip() + + if not customer_name: + error = "Kundenname erforderlich" + else: + try: + # Search for logo + logo_url = generate_welcome_page.search_customer_logo(customer_name) + + if not logo_url: + error = "Logo konnte nicht gefunden werden" + else: + # Generate and save welcome page + html_filename = generate_welcome_page.save_welcome_page(customer_name, logo_url) + + if html_filename: + # Add to lobby playlist + if generate_welcome_page.add_customer_to_lobby_playlist(html_filename): + success = f"✅ Kunde '{customer_name}' erfolgreich hinzugefügt!" + else: + error = "Fehler beim Hinzufügen zur Playliste" + else: + error = "Fehler beim Speichern der Willkommensseite" + except ValueError as e: + error = f"Konfigurationsfehler: {str(e)}" + except Exception as e: + error = f"Fehler: {str(e)}" + + return render_template("customer.html", error=error, success=success, logo_url=logo_url) + +@app.route("/api/customer", methods=["POST"]) +@login_required +def api_add_customer(): + """API endpoint for customer creation""" + try: + data = request.get_json() + customer_name = data.get("customer_name", "").strip() + + if not customer_name: + return jsonify({"error": "Customer name required"}), 400 + + # Search for logo + logo_url = generate_welcome_page.search_customer_logo(customer_name) + + if not logo_url: + return jsonify({"error": "Logo not found"}), 400 + + # Generate and save welcome page + html_filename = generate_welcome_page.save_welcome_page(customer_name, logo_url) + + if not html_filename: + return jsonify({"error": "Failed to create welcome page"}), 500 + + # Add to lobby playlist + if generate_welcome_page.add_customer_to_lobby_playlist(html_filename): + return jsonify({ + "success": True, + "message": f"Customer '{customer_name}' added successfully", + "welcome_page": html_filename, + "logo_url": logo_url + }), 201 + else: + return jsonify({"error": "Failed to add to playlist"}), 500 + + except ValueError as e: + return jsonify({"error": str(e)}), 400 + except Exception as e: + return jsonify({"error": str(e)}), 500 + # ------------------------------------------------- # Medien ausliefern # ------------------------------------------------- @@ -136,6 +220,8 @@ def player(screen): return show_images if ext.endswith(".mp4"): return show_videos + if ext.endswith((".html", ".htm")): + return True return False # ------------------------------ @@ -266,7 +352,13 @@ def admin(): continue ext = os.path.splitext(item)[1].lower() - ftype = "video" if ext == ".mp4" else "image" + if ext == ".mp4": + ftype = "video" + if ext in (".jpg", ".jpeg", ".png"): + ftype = "image" + if ext == ".html": + ftype = "html" + size = os.path.getsize(file_path) // 1024 files.append({ diff --git a/config.json b/config.json index b4ff958..9648a9a 100755 --- a/config.json +++ b/config.json @@ -3,36 +3,49 @@ "enabled": true, "playlist": [ { - "url": "https://www.meteoblue.com/en/meteotv/d7b0fd", - "zoom": 1.0 + "url": "https://wbxroompresence.cancom.io/standort?find=Stuttgart", + "zoom": 0.8 } ] }, "screens": { "lobby": { - "interval": 15, + "interval": 10, "show_images": true, "show_videos": false, "playlist": [ - "Hilfe_KI.jpg", - "e00a687f-d82a-446d-b8f3-07895fbc7309.png", - "Video_CANCOM_LIVE_2025_Stuttgart.MP4", + "welcome.html", { - "url": "https://wbxroompresence.cancom.io/standort?find=Stuttgart", - "zoom": 0.8 - } + "url": "https://www.meteoblue.com/en/meteotv/d7b0fd", + "zoom": 1.0 + }, + "Cancom_Leitsatz.JPG" ], - "newsticker_text": "Herzlich willkommen bei der CANCOM - wir begr\u00fc\u00dfen unsere G\u00e4ste - wir w\u00fcnschen ihnen einen sch\u00f6nen Tag", + "newsticker_text": "Herzlich Willkommen bei der CANCOM - wir begr\u00fc\u00dfen unsere G\u00e4ste und w\u00fcnschen Ihnen einen angenehmen Tag! !", "newsticker_enabled": true }, "casino": { - "interval": 10, + "interval": 15, "show_images": true, "show_videos": true, "playlist": [ - "https://wbxroompresence.cancom.io/standort?find=Stuttgart" + "https://wbxroompresence.cancom.io/standort?find=Stuttgart", + { + "url": "https://www.meteoblue.com/en/meteotv/d7b0fd", + "zoom": 1.0 + }, + "Video_CANCOM_Jahresruckblick_2025.MP4", + "Video_CANCOM_LIVE_2025_Stuttgart.MP4" ], - "newsticker_text": "Hallo dies ist der Newsticker", + "newsticker_text": "Herzlich willkommen bei der CANCOM - wir w\u00fcnschen ihnen einen sch\u00f6nen Tag", + "newsticker_enabled": true + }, + "videosysteme": { + "interval": 20, + "show_images": false, + "show_videos": false, + "playlist": [], + "newsticker_text": "Hallo dies ist ein Test f\u00fcr Michael", "newsticker_enabled": false } }, diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b68ed57 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.9" + +services: + signage: + container_name: signage + build: + context: . + dockerfile: Dockerfile + platforms: + - linux/amd64 + image: gitea.teamthiele.de/ethiele/signage:latest + ports: + - "5005:5005" + restart: unless-stopped + environment: + - OPENAI_API_KEY= "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" + - BRANDFETCH_API_KEY = "eBwCQW_DcQ_jvdqZRNph0JBtRA36XoMTufaaU4AoirOBnqXX9fMqHDw7uYfiz8kSFYMNKGMZtuHxrmud9hn0WQ" diff --git a/env.sh b/env.sh new file mode 100755 index 0000000..a6a8bb6 --- /dev/null +++ b/env.sh @@ -0,0 +1,2 @@ +export OPENAI_API_KEY="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" +export BRANDFETCH_API_KEY="eBwCQW_DcQ_jvdqZRNph0JBtRA36XoMTufaaU4AoirOBnqXX9fMqHDw7uYfiz8kSFYMNKGMZtuHxrmud9hn0WQ" \ No newline at end of file diff --git a/generate_welcome_page.py b/generate_welcome_page.py new file mode 100644 index 0000000..460a936 --- /dev/null +++ b/generate_welcome_page.py @@ -0,0 +1,241 @@ +import os +import json +from openai import OpenAI + +# ------------------------------------------------- +# Customer / Willkommensseite +# ------------------------------------------------- +WELCOME_DIR = os.path.join(os.path.dirname(__file__), "media", "lobby") + +os.makedirs(WELCOME_DIR, exist_ok=True) + +WELCOME_FILENAME = "welcome.html" + +def get_openai_client(): + """Initialize OpenAI client from environment variable""" + api_key = os.getenv("OPENAI_API_KEY") + if not api_key: + raise ValueError("OPENAI_API_KEY environment variable not set") + return OpenAI(api_key=api_key) + +def get_brandfetch_logo(domain): + """Try to fetch a logo URL from Brandfetch CDN.""" + print(f"🔍 Versuche Brandfetch für Domain: {domain}") + # Direct CDN URL construction - Brandfetch provides logos via cdn.brandfetch.io + logo_url = f"https://cdn.brandfetch.io/{domain}?c=1idyd4Tpb2nKaXIIc8T" + + # if validate_logo_url(logo_url): + print(f"✅ Brandfetch Logo gefunden: {logo_url}") + return logo_url + + # return None + + +def search_customer_logo(customer_name): + """Search for customer logo URL using OpenAI and web search""" + try: + client = get_openai_client() + + response = client.chat.completions.create( + model="gpt-4", + messages=[ + { + "role": "system", + "content": """Du bist ein Experte für die Suche nach Domain Namen. Du findest anhand des Firmennamens die richtige Domain dazu + Gib nur den Domain Namen zurück, keine weiteren Informationen. + FALLBACK: Wenn du kein direkt Aufrufbare Domain findest, antworte mit "FALLBACK".""" + }, + { + "role": "user", + "content": f"Finde die Domain für dasUnternehmen: {customer_name}" + } + ], + temperature=0.1, + max_tokens=100 + ) + + logo_url = response.choices[0].message.content.strip() + print (f"🔍 OpenAI Logo-URL: {logo_url}") + if logo_url.upper() == "FALLBACK": + logo_url = None + + if logo_url: + brandfetch_url = get_brandfetch_logo(logo_url) + if brandfetch_url: + return brandfetch_url + + return None + except Exception as e: + print(f"❌ Logo search error: {e}") + return None + + +def generate_welcome_html(customer_name, logo_url): + """Generate a welcome page HTML with full-screen design""" + # CANCOM SVG logo inline + cancom_svg = '''''' + + html_content = f""" + +
+ + +