Die Willkommensseite ist je Standort identisch. Bisher musste man nach
dem Einschalten des Schalters "Willkommensseite hier anzeigen" erneut
generieren, damit sie auf dem Screen ankam - ein Handgriff ohne anderes
Ergebnis, und ohne Rueckmeldung, dass er noetig ist.
- sync_welcome_for_screen() kopiert beim Einschalten eine vorhandene
welcome.html in den Screen und setzt sie vorne in die Playlist; beim
Ausschalten werden Eintrag und Datei entfernt. Aufgerufen aus
update_screen(), nur wenn sich der Schalter wirklich aendert.
- save_welcome_page() legt zusaetzlich eine Standort-Kopie unter
media/<site>/welcome.html an, die nie geloescht wird. Ohne sie waere
nach dem Abschalten aller Screens keine Quelle mehr vorhanden und der
Schalter wuerde wieder stillschweigend nichts tun.
Nebenbei behoben: beim Generieren wurde welcome.html aus abgewaehlten
Screens nur aus der Playlist entfernt, die Datei blieb liegen. Bei
leerer Playlist spielt player() noch nicht verzeichnete Dateien aus dem
Ordner ab - die Begruessung waere dort also weiter zu sehen gewesen.
README, AGENTS.md und Hilfeseite nachgezogen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
templates/customer.html:
- <div class="page"> wurde nie geschlossen. Ergaenzt, analog zu
help.html.
- Der {% include "_footer.html" %} war zusaetzlich in ein eigenes
<footer class="app-footer"> gewickelt, obwohl das Include dieses
Element bereits vollstaendig mitbringt - es standen also zwei
ineinander verschachtelte footer im Markup. Wrapper entfernt.
- Leeren <script>-Block entfernt.
Drei Stellen behaupteten noch, die Willkommensseite ginge in die
Lobby-Playlist:
- README.md: Abschnitt "Willkommensseite", dazu Zielscreens und
"Anzeigen bis" beschrieben
- generate_welcome_page.py: Modul-Docstring
- templates/customer.html: Kommentarblock ueber dem Formular
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Willkommensseite ging bisher fest in die Lobby-Playlist. Welche
Screens Begruessungsschirme sind, ist aber eine Eigenschaft der
Installation und nicht des einzelnen Kundenbesuchs - deshalb ein Flag
pro Screen statt einer Auswahl bei jedem Vorgang.
- Neue Screen-Einstellung "welcome_enabled" (Checkbox im Tab
Einstellungen). Die generierte welcome.html wird in jeden markierten
Screen des Standorts geschrieben und dort an Position 0 gesetzt.
- welcome_target_screens() faellt nur auf "lobby" zurueck, wenn kein
Screen des Standorts den Schluessel besitzt. Bestehende Standorte
verhalten sich damit ohne Konfigurationsaufwand unveraendert.
- add_customer_to_playlists() (frueher add_customer_to_lobby_playlist)
raeumt welcome.html aus allen Screens des Standorts und fuegt sie nur
in den Zielscreens wieder ein. Ein abgewaehlter Screen bleibt so nicht
mit einer alten Begruessung stehen.
- Eingefuegt wird ein Dict statt eines Strings, damit der Eintrag
enabled und schedule tragen kann.
- Optionales "Anzeigen bis" im Formular: setzt eine Zeitsteuerung, die
Seite verschwindet danach auf allen Screens von selbst.
- Das Formular zeigt die aktuellen Zielscreens an und meldet
verstaendlich, wenn keiner markiert ist.
- screens.setdefault("lobby", {}) entfernt - das legte an Standorten
ohne Lobby stillschweigend einen Screen an. Es werden nur noch
vorhandene Screens bespielt.
Die Seite referenziert alle Bilder absolut (/static/..., /media/...),
ist also ortsunabhaengig; je Screen liegt eine identische Kopie.
README, AGENTS.md und die Hilfeseite nachgezogen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
README:
- Feature-Liste und Routen-Tabelle ergaenzt
(POST /admin/<site>/update-playlist-item/<screen>)
- Neuer Abschnitt "Playlist-Eintraege" mit dem Dict-Format fuer URL- und
Datei-Eintraege sowie dem schedule-Objekt: Felder, Beispiele und die
Verhaltensregeln (nur Uhrzeit bei daily/weekly, days nur bei weekly,
Fenster ueber Mitternacht gehoert zum Starttag, unlesbare Angaben
blenden nicht aus, Aktiv-Zustand fliesst in den Playlist-Hash ein)
- Helper-Liste aktualisiert: playlist_item_enabled() gibt es nicht mehr,
dafuer is_item_active(), _schedule_time(), _window_start_date(),
_in_validity_range() und der Filter schedule_text()
Hilfeseite (/admin/help):
- Neuer Abschnitt "Zeitsteuerung" mit Modus-Tabelle, Beispielen und den
Punkten, ueber die man sonst stolpert
- Playlist-Abschnitt: Edit-Button gilt jetzt fuer alle Eintragstypen,
"Zeit"-Badge erklaert
- Priority-Abschnitt: Zeitsteuerung steht dort ebenfalls zur Verfuegung
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der vorige Commit 26efed8 war nicht lauffaehig - fehlgeschlagene
Patch-Anwendungen hatten Zeilen aus ihren Bloecken geschoben.
Reparaturen:
- admin(): zerschossene Playlist-Schleife neu aufgebaut
- player(): return render_template war in den else-Zweig gerutscht,
wodurch die View bei aktiver Priority-Playlist None lieferte (HTTP 500)
- playlist_item_enabled() existierte nicht mehr (umbenannt zu
is_item_active), zwei Aufrufe im Player blieben stehen -> NameError
- update_playlist_item(): Absturz bei new_url=null, und der
enabled-Status ging beim Bearbeiten verloren
- Dateityp-Erkennung mit else-Fallback, ftype konnte ungebunden bleiben
Zeitsteuerung:
- Zeitfenster ueber Mitternacht (22:00-02:00) funktionieren; bei weekly
gehoert ein solches Fenster zum Wochentag seines Starts
- Optionaler Gueltigkeitszeitraum (valid_from/valid_until) fuer daily
und weekly, beide Grenzen einzeln optional und inklusiv
- daily/weekly speichern nur noch die Uhrzeit ("08:00") statt eines
Zeitstempels mit bedeutungslosem Datumsanteil; _schedule_time() liest
weiterhin auch Altbestaende mit vollem ISO-Zeitstempel
- playlist_hash() bezieht den ausgewerteten Aktiv-Zustand ein, sonst
bemerkt der Player ein ablaufendes Zeitfenster erst beim naechsten
Reload aus anderem Grund
- Zeitsteuerung auch fuer Datei-Eintraege, nicht nur URLs
- Neuer Filter schedule_text fuer den Tooltip am "Zeit"-Badge
Oberflaeche:
- required lag auf ausgeblendeten Feldern und blockierte das Absenden
des Formulars "URL hinzufuegen" ohne sichtbare Meldung
- updateScheduleDaysVisibility() war innerhalb von DOMContentLoaded
definiert, der Edit-Handler darueber -> ReferenceError bei jedem Klick
- data-schedule mit tojson zerlegte das Attribut, da tojson " nicht
escaped
- Zeitsteuerungs-Block als schlichte Formularzeilen statt als card, die
global den roten Rand und grauen Header erhaelt
- Zeitsteuerung auch in der Priority-Playlist
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Neue Config-Felder: passthrough_params (true/false) pro URL-Item
- Edit-Modal in admin.html + priority.html mit Checkbox
- Player checkt item.passthrough === true vor appendParams()
- Default: false (keine Parameter-Übergabe ohne Checkbox)
- Link-Icon (ti-link / ti-link-off) in Playlist-Zeile als Status-Indikator
- Doku: help.html, README.md, AGENTS.md
- player_params als dict(request.args) an player.html übergeben
- appendParams() fügt Parameter an iframe.src + Overlay-URLs an
- Funktioniert mit & ohne bestehende Query-Parameter in der Ziel-URL
- Beispiel: /player/stuttgart/lobby?customer=acme → https://dashboard?customer=acme
Die globale Event-Delegation lag versehentlich innerhalb
des DOMContentLoaded-Callbacks. Wenn initSortable fehlschlug,
wurde der Click-Handler nie registriert.
- editState + document click handler jetzt global (vor DOMContentLoaded)
- Duplicate Code entfernt
- Debug-alerts entfernt
- Neue Route POST /admin/<site>/update-playlist-item/<screen>
- Bearbeiten-Button (✎) für URL-Items in admin.html + priority.html
- Modal mit URL + Zoom-Feld, Validierung und Speichern
- Grid um Edit-Spalte erweitert (28px|1fr|90px|80px|40px|50px|32px)
- User kann nach TOTP-Eingabe das Gerät für 1–30 Tage speichern
- Cookie mfa_trust mit zufälligem Token, gespeichert in trusted_devices[]
- Nächstes POST /login: Cookie prüfen → gültiges Token → MFA überspringen
- Abgelaufene Einträge werden beim Login automatisch bereinigt
- Logout + MFA-Deaktivierung löschen Cookie + trusted_devices
- mfa_verify.html: checkbox + days-select (1/3/7/14/30 Tage)
- md_to_html: Listen-Support (- /*) und Zeilenumbrüche erhalten
- Kein Firmenname mehr unter den Logos (customer-name entfernt)
- text-align: left + align-items: stretch für linksbündige Texte
- Feste Logo-Höhe 120px für gleiche Text-Startposition
- Portrait-Modus: Text links vom Logo (via order), gap 40px
- Schrift 1.3rem, margin-top 24px, Listeneinzug entfernt
- Reset-Button im Willkommensseite-Formular
- Markdown-Hilfetext aktualisiert