Angelehnt an die bestehende USB Tankstelle, um Gottesdienste und andere Veranstaltungen per Auswahl auf einen USB Speicher zu kopieren.
  • Rust 86%
  • Slint 12.2%
  • Shell 1.8%
Find a file
2026-06-23 23:38:41 +02:00
.forgejo/workflows Build-Deps: cmake/clang + freetype2/libxkbcommon-devel (aws-lc-rs) 2026-06-22 23:27:42 +02:00
.vscode Add Nextcloud login flow and daemon shutdown handling 2026-06-23 13:22:21 +02:00
deploy Gerüst: Cargo-Projekt, RPM-Paketierung, Kiosk-Deployment, CI 2026-06-22 21:49:32 +02:00
examples Lokaler Cache: Datei-Download und Index/Speicherung/Verdrängung 2026-06-23 23:24:07 +02:00
packaging Build-Deps: cmake/clang + freetype2/libxkbcommon-devel (aws-lc-rs) 2026-06-22 23:27:42 +02:00
scripts Build-Deps: cmake/clang + freetype2/libxkbcommon-devel (aws-lc-rs) 2026-06-22 23:27:42 +02:00
src Implement shared logging functionality for daemon and HTTP server 2026-06-23 23:38:41 +02:00
ui Feiertage: gesetzliche Feiertage lokal berechnen (GUI + WebUI) 2026-06-23 23:31:08 +02:00
.gitignore Build: Slint-Build-Libs im Container + Artefakte ignorieren 2026-06-22 22:48:47 +02:00
build.rs Plan #3: dunkles Slint-Theme -> Platzhalter unterscheidbar 2026-06-23 17:48:36 +02:00
Cargo.lock Lokaler Cache: Datei-Download und Index/Speicherung/Verdrängung 2026-06-23 23:24:07 +02:00
Cargo.toml Lokaler Cache: Datei-Download und Index/Speicherung/Verdrängung 2026-06-23 23:24:07 +02:00
LICENSE Initial commit 2026-06-22 13:33:02 +00:00
Plan.md Feiertage: gesetzliche Feiertage lokal berechnen (GUI + WebUI) 2026-06-23 23:31:08 +02:00
README.md README: Hinweis Cross-Build Fedora->Leap (im Container bauen) 2026-06-22 19:53:55 +02:00

ngenSermon2Usb

Angelehnt an die bestehende USB Tankstelle, um Gottesdienste und andere Veranstaltungen per Auswahl auf einen USB Speicher zu kopieren.

Plan

Hinweis: Das ist erstmal nur ein Plan.

  • Umgesetzt in Rust mit Slint als GUI-Toolkit.
  • Quellen für die Gottesdienste und Veranstaltungen: ein Nextcloud-Ordner.
  • Pro Gottesdienst bzw. Veranstaltung gibt es einen Ordner mit MP3-Dateien und einer JSON- oder TXT-Datei.
  • Ein Ordner wird erst dann in die dem Benutzer angezeigte Liste aufgenommen, wenn die zugehörige JSON- oder TXT-Datei vorhanden ist.
  • Die GUI zeigt auf der linken Seite eine Übersicht über den USB-Speicher an und reagiert dabei auch auf das An- und Abstöpseln des Speichers.
  • Im Hauptbereich gibt es eine Auswahl, welchen Veranstaltungstyp man möchte. Diese Liste lässt sich in einem passwortgeschützten Settings-Bereich verwalten.
  • Zusätzlich soll es eine HTTPS-Einstellungsseite und eine HTTPS-Statusseite geben, die ebenfalls passwortgeschützt sind.

Technische Entscheidungen

Nextcloud / Datenquelle

  • Zugriff ohne lokalen Nextcloud-Client, direkt per API/WebDAV.
  • App-Passwort wird automatisch über den Login Flow v2 angelegt (einmalige Browser-Bestätigung); das echte Nutzerpasswort wird nicht gespeichert.
  • Lokaler Cache für Offline-Betrieb und schnellere Anzeige (s. Abschnitt Lokaler Cache).
  • Aktualisierung primär per Push (kein dauerndes Pollen):
    • notify_push (App „Client Push") — der Client öffnet ausgehend eine WebSocket-Verbindung (wss://<server>/push/ws, Endpunkt aus der Capabilities-API notify_push.endpoints.websocket), authentifiziert mit Benutzername + App-Passwort und empfängt Ereignisse (notify_file, notify_activity, …).
    • Das Ereignis ist nur ein Signal „etwas hat sich geändert" (in neueren Versionen mit Pfad-Präfix), nicht der Diff → daraufhin ein gezielter, kurzer Scan des betroffenen Ordners statt Vollabgleich.
    • Vorteil: Client verbindet sich nach außen → funktioniert hinter NAT/Firewall (LAN-Kiosk + externe Nextcloud). Voraussetzung serverseitig: App notify_push aktiv (seit ~NC 21 gebündelt) + Redis.
  • Polling alle 3 Minuten nur als Fallback, falls notify_push nicht verfügbar ist oder der Socket abreißt (Reconnect mit Backoff).
  • (Webhooks-App webhook_listeners, NC 30+, scheidet meist aus: Nextcloud müsste den Kiosk erreichen können — bei LAN-Kiosk + externer Cloud nicht gegeben.)

Gleichzeitige Anfragen begrenzen

Mehrere gleichzeitige Aufrufe an Nextcloud werden bewusst eingeschränkt (verhindert Server-Überlast/Drosselung und schont die Kiosk-Ressourcen; glättet auch die notify_push-getriggerten Nachladungen):

  • Eine gemeinsame HTTP-Client-Instanz mit Connection-Pool/Keep-Alive (z. B. reqwest) — kein neuer Connect pro Datei.
  • Begrenzte Parallelität per Semaphore, kleines konfigurierbares Limit (Default 24 gleichzeitige Requests); überschüssige Arbeit in eine Queue (FIFO).
  • Backoff/Retry bei 429/503, dabei den Retry-After-Header respektieren.
  • Ggf. getrennte Limits für leichte Metadaten-/List-Requests vs. große Datei-Downloads.
  • Beim Kopieren seriell oder max. 2 parallel — der USB-Schreibvorgang ist meist ohnehin der Flaschenhals.

Lokaler Cache

  • Einstellbare Schwelle, ab der der Cache reduziert wird — z. B. als maximale Cache-Größe oder als minimal freizuhaltender Speicher. Mit Hysterese: beim Überschreiten der oberen Schwelle wird bis zu einer unteren Schwelle aufgeräumt, damit nicht ständig nachgeräumt wird.
  • Verdrängungsstrategie als Kompromiss zwischen „ältestes zuerst löschen" und „am wenigsten gebraucht löschen": pro Eintrag ein Score aus Alter (letzter Zugriff/Download) und Nutzungshäufigkeit; Einträge mit dem niedrigsten Score zuerst entfernen. Gewichtung der beiden Faktoren konfigurierbar.
  • Geschützt vor Löschung: gerade angezeigte/markierte oder aktuell kopierte Veranstaltungen.
  • Cache-Pfad in den Settings einstellbar: Der Speicherort des lokalen Caches lässt sich konfigurieren (z. B. auf eine größere/andere Platte legen).
  • Unkritisch, da die Quelle Nextcloud bleibt — verworfene Cache-Daten lassen sich bei Bedarf jederzeit neu laden.
  • Cache-Pfad, Schwelle und Gewichtung werden im passwortgeschützten Settings-Bereich eingestellt.

Gottesdienst-Struktur (Quelle: ngenRecord)

  • Die Gottesdienste werden vom Schwesterprojekt ngenRecord erzeugt. Nur der MP3-Export wird nach Nextcloud gespeichert — der Aufnahme-Ordner mit Rohdaten (session.raw, WAV, Peaks) nicht.
  • Pro Gottesdienst ein Unterordner mit den MP3-Dateien.
  • ngenRecord legt beim Fertigstellen die zugehörige JSON- oder TXT-Datei im Export-Ordner an. Dieses Vorhandensein ist genau der Trigger, dass der Ordner „fertig" ist und in der Liste erscheint.
  • Ordnername trägt das Datum am Anfang im Schema YYYY-MM-DD … (aus ngenRecords path_template "{date} {sorthelper} {daytime}", z. B. 2026-06-18 c Mittag). Daraus lassen sich Jahr und Monat zuverlässig ableiten.
    • Im Produktivsystem erzeugt die Export-Pfad-Vorlage in ngenRecord diesen datierten Pro-Event-Unterordner bereits. (Nur im Testsystem ist export_path_template leer/flach.)

Zusatzdatei (JSON/TXT)

  • Für die Anzeige ist zunächst nur wichtig, dass eine Datei existiert (s. o. Trigger).
  • Anzeige-Text des Albums = Dateiname der JSON/TXT ohne Endung (kein Parsen des Inhalts nötig). Beitragstitel = MP3-Dateiname ohne Endung.
  • Datum und Tageszeit kommen weiterhin aus dem Ordnernamen (YYYY-MM-DD …).
  • Parsen der Inhalte ggf. später (für reichere Titel/Metadaten).

Veranstaltungstypen & Ordner-Zuordnung

  • Je Veranstaltungstyp ein eigener Nextcloud-Oberordner (z. B. Gottesdienste/, Konferenzen/, Seminare/).
  • Die Zuordnung Typ → Oberordner erfolgt manuell in den Settings (Anzeigename ⇄ Nextcloud-Pfad). Die Veranstaltungstyp-Liste im Hauptbereich speist sich daraus.

USB-Speicher

  • Plug/Unplug-Erkennung: unter Linux via udev, unter Windows selbst ermittelt.
  • Mehrere Sticks werden vorerst physikalisch verhindert (Mehrfach-Unterstützung evtl. später).
  • Ziel-Layout: Album-Ordner direkt in den Wurzelordner des Sticks.

Kopier-Modi

  • Standard (ohne Autoradio): kompletter Veranstaltungsordner mit Struktur, ohne Umbenennung.
  • Autoradio-Optimierung (per GUI-Option) — wirkt nur auf die Dateinamen:
    • Umbenennung der MP3s, Albumname vorangestellt.
    • FAT32-Sanitizing (Umlaute/Sonderzeichen ersetzen).
    • Flache Struktur (keine Unterordner).
    • Track-Nummern-Präfix für die richtige Reihenfolge.

Verhalten bei vorhandenem Album

  • Bereits vorhandenes Album wird einfach überschrieben, mit Meldung „schon vorhanden".

Kopiert-Status

  • Erkennung zuerst nur nach Ordnername (Album liegt auf dem Stick → „✓ kopiert").
  • Ein Button „Vollständigkeit" ist eingeplant (späterer Abgleich von Dateianzahl/-größe, um unvollständige Kopien zu erkennen).

Sortierung

  • Liste nach Datum absteigend (neueste oben), innerhalb eines Tages nach Tageszeit (sorthelper af).
  • Tracks im Album nach Dateiname / Track-Nummer.

Abbrechen

  • Während des Kopierens gibt es einen Abbrechen-Button. Beim Abbrechen wird gefragt:
    • aktuellen Beitrag noch fertig kopieren und dann stoppen, oder
    • alles bereits Kopierte der Auswahl wieder löschen (Rollback).

Speicher freigeben (Aufräum-Optionen)

Wählbare Möglichkeiten, um Platz auf dem USB-Speicher zu schaffen (z. B. wenn der Platz nicht reicht). Jeweils mit Bestätigung, da löschend:

  • Gezielt löschen (Ordner/Album auswählen): einen oder mehrere bestimmte Ordner bzw. Alben auf dem Stick auswählen (Mehrfachauswahl) und gezielt entfernen.
  • Alles löschen (Formatieren): kompletten Stick als FAT32 formatieren — entfernt alle Daten.
  • Älteste löschen (nach Album/Ordnername): Veranstaltungsordner mit dem ältesten Datum im Ordnernamen (YYYY-MM-DD-Präfix, = MP3-Album) zuerst entfernen.
  • Zuerst gespeicherte löschen (nach Dateizugriffsdatum): nach dem Zeitpunkt der Speicherung auf dem Stick (Datei-Zeitstempel) — die zuerst kopierten Dateien zuerst entfernen.

Bei den automatischen „Löschen"-Varianten (Älteste / Zuerst gespeicherte) wird so lange entfernt, bis genug Platz frei ist.

Verfügbarkeit: Das gezielte Löschen wird jederzeit als eigene Aktion angeboten und zusätzlich, wenn der Platz auf dem USB-Speicher knapp wird (zusammen mit den übrigen Aufräum-Optionen).

  • Vorschau vor dem Löschen: Bevor tatsächlich gelöscht wird, zeigt eine Vorschau genau, welche Ordner (Alben) entfernt würden — bzw. einzelne Dateien, falls lose Dateien direkt im Wurzelverzeichnis des Sticks liegen. Erst nach Bestätigung wird gelöscht.

GUI: Der Unterschied zwischen Variante 2 und 3 wird dem Nutzer angezeigt — Variante 2 richtet sich nach dem Veranstaltungsdatum (Ordnername), Variante 3 danach, wann auf den Stick kopiert wurde (Dateizeitstempel). Diese kurze Erklärung steht direkt bei den beiden Optionen (s. GUI-Skizze: „Speicher freigeben").

Settings & Passwörter

  • Passwörter möglichst sicher, als Hash gespeichert.
  • Getrennte Passwörter für GUI-Settings und HTTPS-Seiten; jeweils vorerst nur ein Passwort.
  • GUI-Settings und HTTPS-Settings sind inhaltlich identisch (gleicher Funktions-/Einstellungsumfang, dieselbe darunterliegende Konfiguration) — nur die Oberfläche unterscheidet sich (lokale Touch-GUI vs. Browser im LAN).

    Anweisung für AI/Implementierung: GUI-Settings und HTTPS-Settings immer gleich/synchron halten. Wird eine Einstellung an einer Stelle hinzugefügt oder geändert, ist sie an der anderen mitzuführen; beide greifen auf dieselbe Konfigurationsquelle zu.

  • Der Nextcloud-Login (Login Flow v2) lässt sich daher von beiden Oberflächen anstoßen (lokal mit Touch-Tastatur oder über die HTTPS-Seite von einem anderen Gerät).
  • Die Veranstaltungstyp-Liste wird lokal geführt, mit Backup in Nextcloud.
  • Auswahl bei Gottesdiensten nach Jahr und Monat (abgeleitet aus dem YYYY-MM-DD-Präfix des Ordnernamens, s. Gottesdienst-Struktur).

Konfigurations-Ebenen — wo welche Einstellung

Der Kiosk hat mehrere Konfigurations-Ebenen; nicht jede Einstellung gehört in dieselbe.

Die Ebenen:

  1. Runtime-Settings — GUI = HTTPS, passwortgeschützt, dieselbe Konfigquelle (s. Settings & Passwörter). Was ein Betreuer im Betrieb ohne Root/Neuinstallation ändern soll.
  2. Lokale Konfig/Zustand — von der App selbst geschrieben, nicht von Hand gepflegt (Hashes, Token, Cache-Index, aktuelle Version).
  3. Nextcloud-Backup — Teilmenge der Runtime-Settings, für Wiederherstellung.
  4. Install-Zeit — Agama-Profil + First-Boot-Script, einmalig beim Aufsetzen (OS-/Root-Ebene; s. Kiosk-Installation (openSUSE Leap 16)).
  5. Paket/RPM — technische Systemintegration, kommt mit dem Update mit (via .spec), nicht von Hand (s. Build-Script & Paketierung).
  6. Build/CI-Zeit — Geheimnisse + Versionierung in Forgejo Actions.

Zuordnung:

Einstellung Ebene Begründung
Veranstaltungstyp ↔ Nextcloud-Oberordner Runtime + NC-Backup fachlich, ändert sich oft; Liste lokal geführt, Backup in NC
Nextcloud-Login (Login Flow v2), App-Passwort Runtime (anstoßen) + Lokal (Token sicher/Hash) von beiden Oberflächen auslösbar; Geheimnis nie im Klartext
Cache: Pfad, Schwellen, Score-Gewichtung Runtime fachliche Tuning-Größen (s. Lokaler Cache)
Autoradio-Standard, USB-Verhalten Runtime Bedien-Default
Feiertage (Liste/Auswahl/Import) Runtime + NC-Backup inhaltlich, pflegbar (s. Feiertage)
Passwörter (GUI/HTTPS) Runtime (setzen) → Lokal als Hash s. Settings & Passwörter
Concurrency-Limit (24) Runtime bewusst konfigurierbar (s. Gleichzeitige Anfragen begrenzen)
Update-Kanal, Prüfintervall/Zeitfenster, Auto-Install Runtime s. Automatischer Update-Mechanismus
HTTPS-Port Runtime (mit OS-Kopplung, s. u.) Port wählbar
Sprache/Tastatur/Zeitzone (de_DE) Install-Zeit „vorerst nur Deutsch" → fix, kein Runtime-Umschalter
Hostname, Netzwerk (DHCP/statisch/WLAN) Install-Zeit OS-Grundlage, selten geändert
Kiosk-User + Autologin, graphical.target Install-Zeit einmalige Provisionierung
Pakete (cage, App), Forgejo-.repo + Token Install-Zeit Bootstrapping der Update-Quelle
transactional-update/btrfs-Snapshots Install-Zeit Rollback-Fundament
cage@.service, Daemon-Unit, udev-/polkit-Regel, Update-Timer Paket/RPM gehört zur App-Version, kommt mit Updates mit
GPG-Signaturschlüssel, Registry-Upload-Token Build/CI Secrets in Forgejo Actions, nie ins Image
Version/Release Build/CI aus Git-Tag

Faustregel:

  • Fachlich / ändert sich im Betrieb → Runtime (GUI=HTTPS).
  • Braucht Root, einmalig beim Aufsetzen → Install-Zeit (Agama).
  • Technisch + versionsgebunden → ins RPM (kommt mit Updates).
  • Geheim → CI-Secret bzw. lokaler Schlüsselspeicher, nie in Settings/Image.

Kopplungen (Runtime-Setting mit OS-Wirkung): Diese sind Runtime-Settings, wirken aber auf OS-Ebene — deshalb setzt sie der privilegierte Hintergrunddienst um (s. Hintergrunddienst):

  • HTTPS-Port ändern → Daemon passt die firewalld-Regel an.
  • Cache-Pfad auf andere Platte → Ziel muss gemountet sein (fstab = Install-Zeit) → Daemon prüft/meldet, wenn es fehlt.
  • Update-Zeitfenster → der systemd-Timer feuert regelmäßig, aber der Daemon entscheidet anhand von Zeitfenster und „kein Kopiervorgang aktiv" → OS-Ebene bleibt statisch, nur die App-Logik kennt den Zustand.

Feiertage

  • In den Settings wird eine Namensliste der Feiertage angezeigt; einzelne Feiertage sind auswählbar (welche markiert/berücksichtigt werden).
  • Eine deutschlandweite Feiertagsliste kann aus dem Internet geholt werden.
  • Von Hand lassen sich weitere Feiertage hinzufügen.
  • Feiertage steuern die abweichende Zeilen-Markierung in der Gottesdienst-Liste (s. Listendarstellung).

HTTPS-Server (Einstellungs-/Statusseite)

  • Erreichbar nur im LAN / lokal.
  • Nicht öffentliches Zertifikat (selbstsigniert / lokal).
  • Statusseite zeigt Kopiervorgänge und Logs.
  • Integrierter Dateibrowser (Cache + USB): zeigt den lokalen Cache und den USB-Inhalt zur Diagnose. In den eigenen HTTPS-Server integriert (z. B. axum mit tower-http ServeDir bzw. actix-files), hinter dem bestehenden HTTPS-Passwort — also kein zweites Login. (Nextcloud-Inhalt bleibt über die Nextcloud-Web-UI browsbar.)
  • HTTPS-Passwort beim ersten Aufruf anlegen: Beim ersten Aufruf der HTTPS-Seite muss zuerst ein Passwort festgelegt werden, bevor Zugriff gewährt wird.
  • Nextcloud-Verbindung über die HTTPS-Einstellungsseite einrichtbar: Die Verbindung zu Nextcloud (Login Flow v2) lässt sich auch über die HTTPS-Einstellungsseite einrichten (GUI- und HTTPS-Settings sind identisch).
  • Der Server soll die GUI möglichst gar nicht beeinträchtigen: läuft in einem eigenen Thread / eigener Runtime (z. B. tokio auf einem Hintergrund-Thread), getrennt vom Slint-UI-Thread. Kein Blockieren des UI-Threads, niedrigere Priorität; Datenaustausch nur über entkoppelte, nicht-blockierende Kanäle (z. B. geteilter Zustand/Channel statt direkter UI-Aufrufe).

Betrieb, Kiosk & Deployment

Zielplattform

  • Kiosk-Modus unter Linux oder Windows.
  • Bedienung per Touch: allgemein eine Touch-Tastatur anbieten (für Passwörter, Eingaben).
  • Sprache: vorerst nur Deutsch.

Kiosk-Installation (openSUSE Leap 16)

  • Zielbild: Das Gerät bootet direkt in die Vollbild-App — ohne Desktop/Login, mit Touch-Bedienung (s. Zielplattform). Wiederanlauf nach Absturz/Update automatisch.
  • Unbeaufsichtigte OS-Installation per Agama (Leap 16 hat den YaST-Installer durch Agama ersetzt; klassisches AutoYaST entfällt): Start mit Kernel-Option inst.auto=<URL-zum-Profil>. Das JSON-Profil beschreibt Partitionierung, Netzwerk, Lokalisierung (de_DE.UTF-8, Tastatur de, Zeitzone Europe/Berlin), genau einen Benutzer mit Autologin, die Software-Auswahl (Minimal-Basis + cage + das App-RPM) sowie init/post-Skripte für die restliche Einrichtung.
  • Update-Quelle gleich mit einrichten: Das Install-Skript trägt die Forgejo-RPM-Registry als .repo ein (s. Automatischer Update-Mechanismus), sodass die App von dort installiert und später aktualisiert wird.
  • Kiosk-Laufzeit mit cage (Wayland-Kiosk-Compositor: zeigt genau eine maximierte App und blockiert alles andere) — passt zu Slint (läuft nativ auf Wayland). Betrieb als systemd-Service (cage@tty1.service) mit dem Autologin-User und Restart=always → deckt zugleich den Watchdog/Autostart ab (vgl. Fehlerfall „Absturz der Anwendung"). Default-Target graphical.target. (Alternativen: weston mit kiosk-shell oder labwc.)
  • Touch-Tastatur: bringt die App selbst mit (s. GUI-Skizze, Einstellungen) → keine separate Wayland-Bildschirmtastatur nötig.
  • Systemintegration kommt aus dem RPM (via .spec/%post, s. Build-Script & Paketierung) und wird beim Install/Update automatisch gesetzt:
    • systemd-Units: cage@.service (App) und der privilegierte Hintergrunddienst (s. Hintergrunddienst).
    • udev-Regel für die USB-Plug/Unplug-Erkennung (s. USB-Speicher).
    • polkit-/sudo-Regel für Formatieren und Mount/Unmount/Eject (gebündelt im Dienst, nicht in der GUI).
    • firewalld: HTTPS-Port nur im LAN freigeben (s. HTTPS-Server; eine Port-Änderung zur Laufzeit setzt der Dienst um, s. Konfigurations-Ebenen).
  • Rollback-Fundament: Dateisystem auf btrfs mit transactional-update/Snapshots, damit fehlerhafte Updates automatisch zurückrollen (s. Automatischer Update-Mechanismus).
  • Alternative — fertiges Image mit KIWI: Statt „installieren + nachkonfigurieren" lässt sich ein vorkonfiguriertes Kiosk-Image bauen (sinnvoll bei vielen identischen Geräten; höherer Build-Aufwand). Für ein bis wenige Geräte ist der Agama-Weg einfacher.

Hintergrunddienst (zu prüfen)

  • Vermutlich sinnvoll: ein eigener Hintergrunddienst (Daemon/Service), getrennt von der GUI. Begründung:
    • Privilegierte Operationen wie Formatieren und Mount/Unmount/Eject brauchen oft erhöhte Rechte — besser gebündelt im Dienst als in der GUI.
    • Läuft unabhängig von der GUI weiter: Nextcloud-Sync/Cache-Pflege, HTTPS-Server, Watchdog/Autostart.
    • Die (unprivilegierte) GUI delegiert privilegierte/lange Aufgaben an den Dienst über eine lokale Schnittstelle (Socket/IPC).
  • Genaue Aufteilung GUI ⇄ Dienst noch festzulegen.

Automatischer Update-Mechanismus

  • Ziel: Der Kiosk läuft unbeaufsichtigt → Programm-Updates müssen automatisch, sicher und mit Rückfallebene ablaufen, ohne den laufenden Betrieb (v. a. Kopiervorgänge) zu stören.
  • Auslieferung als RPM-Paket über die eingebaute Forgejo-Paket-Registry des bestehenden Git-Hosts (git.bg-ak.de). RPMs sind GPG-signiert → Authentizität/Integrität prüft das Paketsystem; ein selbstgebauter Download-und-Austausch-Mechanismus entfällt. Kein externes openSUSE Build Service nötig. Bei privater Registry kommen die Zugangsdaten (Personal Access Token) in die .repo-Datei: https://<user>:<token>@git.bg-ak.de/api/packages/<owner>/<gruppe>.repo (passt zum Leap-16-Setup mit zypper).
  • Build & Veröffentlichung per Forgejo Actions (CI): Bei einem Release/Tag baut die CI das RPM und lädt es in die Forgejo-Registry hoch; die Kioske ziehen es danach automatisch per zypper.
  • Auslösung per systemd-Timer (kein Dauer-Polling): konfigurierbares Zeitfenster (z. B. nachts), prüft gezielt auf eine neue Version dieses einen Pakets statt vollem Distributions-Upgrade.
  • Client zieht aktiv vom Repo (ausgehende Verbindung) → funktioniert hinter NAT/Firewall (gleiche Logik wie bei notify_push, s. Nextcloud / Datenquelle); keine eingehenden Verbindungen nötig.
  • Koordination mit dem Betrieb: Update nur, wenn kein Kopiervorgang läuft und der USB-Speicher nicht aktiv beschrieben wird; sonst auf das nächste Fenster verschieben. Der privilegierte Hintergrunddienst (s. Hintergrunddienst) führt das Update aus, da er Rechte und App-Zustand kennt.
  • Atomar mit Rollback: vorzugsweise transactional-update (btrfs-Snapshots) für atomare Updates mit automatischem Rollback bei fehlerhaftem Start; mindestens ein Healthcheck nach Neustart — startet die neue Version nicht sauber, automatischer Rückfall auf die vorige (Snapshot bzw. vorheriges RPM).
  • Neustart nach Update: Nach erfolgreichem Update wird der Kiosk-Service (App) im selben Wartungsfenster sauber neu gestartet (vgl. Autostart/Watchdog, Restart=always).
  • Konfig-Migration: Bei Versionssprüngen werden Einstellungen automatisch migriert (vorwärtskompatibles Lesen, Defaults für unbekannte Felder — vgl. Fehlerfall „Konfiguration beschädigt").
  • Einstellbar im passwortgeschützten Settings-Bereich (GUI und HTTPS identisch, s. Settings & Passwörter): Update-Kanal (Stabil/Test), Prüfintervall/Zeitfenster, automatisch installieren (ja/nein bzw. nur benachrichtigen).
  • Status/Logs: Die HTTPS-Statusseite zeigt aktuelle Version, letzte erfolgreiche Aktualisierung, verfügbare Version und Update-Verlauf/Fehler. Im Kiosk höchstens ein dezenter Hinweis, keine technischen Details (s. Leitlinien Fehlerfälle & Meldungen).
  • (Optional) OS-Sicherheitspatches können über denselben Timer mitlaufen (zypper patch), bleiben aber konzeptionell getrennt vom App-Update.

Build-Script & Paketierung

  • Ein einziges Build-Script (z. B. scripts/build-rpm.sh), das lokal und in der CI identisch läuft („ein Skript, zwei Aufrufer") — so weicht der CI-Build nie vom Entwickler-Build ab.
  • Schritte des Scripts:
    1. Version aus dem Git-Tag ableiten (git describe/Tag → RPM-Version/Release).
    2. Release-Binary bauen: cargo build --release (Slint-Frontend inklusive).
    3. RPM paketieren aus einer .spec-Datei (via rpmbuild): enthält das Binary plus die Kiosk-Dateien — cage@.service, Daemon-systemd-Unit, udev-Regel und polkit-Regel (s. Hintergrunddienst) — und %post-Scriptlets, die die Dienste aktivieren/neu starten (greift in den Update-Neustart, s. Automatischer Update-Mechanismus). (Alternative für einfache Fälle: cargo-generate-rpm ohne volle .spec.)
    4. RPM signieren (GPG) → wird beim Update vom Paketsystem geprüft.
    5. Upload in die Forgejo-RPM-Registry (nur in der CI; lokal optional per Flag).
  • Build in einer openSUSE-Leap-16-Umgebung (Container/Buildroot), damit das Binary zu glibc/Bibliotheksständen des Zielsystems passt. Ein Cross-Build vom Entwickler-Rechner (z. B. Fedora) direkt funktioniert nicht verlässlich: glibc ist nicht vorwärtskompatibel (ein auf neuerem System gebautes Binary läuft nicht auf älterem Leap), und systemd-RPM-Makros (%service_add_post vs. %systemd_post) sowie Paketnamen unterscheiden sich zwischen den Distributionen. Deshalb lokal im Leap-Container bauenpodman run --rm -v "$PWD:/src:Z" -w /src registry.opensuse.org/opensuse/leap:16.0 ./scripts/build-rpm.sh (Wrapper: scripts/build-in-container.sh) — derselbe Aufruf wie in Forgejo Actions, dessen Runner ebenfalls im Leap-Image läuft.
  • Abhängigkeiten (Slint-/Wayland-Laufzeit, USB-/udev-Bibliotheken) werden als RPM-Requires deklariert, damit zypper sie beim Install/Update mitzieht.
  • Aufruf aus Forgejo Actions: Der CI-Workflow ruft bei Release/Tag genau dieses Script auf (s. Build & Veröffentlichung per Forgejo Actions).

GUI-Skizze

Erste Skizze des Layouts (Kiosk, Touch). Dient als Diskussionsgrundlage, nicht final.

Listendarstellung (wichtig)

  • In der Liste steht eine Zeile pro Album (= ein Veranstaltungs-/Gottesdienst-Ordner) — nicht jeder Beitrag (jede MP3) einzeln.
  • Albumname = Dateiname der JSON/TXT ohne Endung (s. Zusatzdatei).
  • Bei Veranstaltungstypen außer Gottesdienst wird nur der Albumname ohne Datum angezeigt (und kein Jahr/Monat-Filter).
  • Beim Gottesdienst werden Sonntage als komplette Zeile farbig hinterlegt markiert. Feiertage werden mit einer anderen Farbe markiert. Der Wochentag wird aus dem Datum berechnet (aus dem YYYY-MM-DD-Präfix), nicht aus der Tageszeit. Die Feiertage stammen aus einer in den Settings verwalteten Liste (s. Feiertage).
  • In der Gottesdienst-Zeile wird nur der Tag (TT) angezeigt (Jahr/Monat stehen schon im Filter), dazu die Tageszeit (z. B. „Morgen"/„Abend") — denn an manchen Tagen gibt es zwei Veranstaltungen, die so unterscheidbar bleiben.
  • Nur über einen besonderen Button am Ende der Zeile ([ ☰ ]) gelangt man zu den Einzelbeiträgen des Albums. Standardweg ist das Kopieren des kompletten Albums.

Hauptbildschirm (Gottesdienst gewählt)

┌───────────────────────────────────────────────────────────────────────────────┐
│  ngenSermon2Usb                                              [ ⚙ Einstellungen ] │
├──────────────────────┬────────────────────────────────────────────────────────┤
│  USB-Speicher        │  Veranstaltungstyp                                       │
│                      │  ┌──────────┐┌──────────┐┌──────────┐┌──────────┐       │
│  ● Angesteckt        │  │Gottesdnst││ Konferenz││  Seminar ││  Sonstig │       │
│  SanDisk Ultra 32 GB │  └────▲─────┘└──────────┘└──────────┘└──────────┘       │
│                      │                                                          │
│  Belegt:             │  Jahr  [ 2026 ▾ ]      Monat  [ Juni ▾ ]                 │
│  ▓▓▓▓▓░░░░░  12/32 GB │                                                          │
│                      │  ┌──────────────────────────────────────────────────┐   │
│  Inhalt:             │  │▒▒21  Morgen  Predigtreihe Römer 8 ....... [ ☰ ]▒▒│ ← So
│   • 15.06. Predigt   │  │  18  Mittag  Bibelstunde ................ [ ☰ ]  │   │
│   • 08.06. Predigt   │  │▒▒14  Morgen  Gemeindesonntag .. ✓ kopiert [ ☰ ]▒▒│ ← So
│                      │  │▒▒14  Abend   Abendmahlsfeier ............ [ ☰ ]▒▒│ ← So (2. am Tag)
│  [ ⏏ Sicher entfernen]│  │  11  Abend   Gebetsabend ................ [ ☰ ]  │   │
│                      │  │▒▒07  Morgen  Themengottesdienst ......... [ ☰ ]▒▒│ ← So
│                      │  └──────────────────────────────────────────────────┘   │
│                      │  ☐ Für Autoradio optimieren                             │
│                      │             [      ⬇  Auf USB kopieren      ]            │
│                      │  ▓▓▓▓▓▓▓░░ Kopiere 3/8 …     [ ✖ Abbrechen ]            │
└──────────────────────┴────────────────────────────────────────────────────────┘

Legende: ▒▒…▒▒ = farbig hinterlegte Sonntagszeile · Feiertage in anderer Farbe · [ ☰ ] = Button „Einzelbeiträge öffnen" · ✓ kopiert = liegt bereits auf dem Stick.

Anderer Veranstaltungstyp (z. B. Konferenz) — nur Album, ohne Datum

│  ┌──────────────────────────────────────────────────┐
│  │  Jugendkonferenz 2026 ................... [ ☰ ]  │   (kein Datum,
│  │  Ehe-Seminar ............................ [ ☰ ]  │    keine Sonntags-
│  │  Lobpreisabend .............. ✓ kopiert   [ ☰ ]  │    Markierung,
│  └──────────────────────────────────────────────────┘    kein Jahr/Monat)

Einzelbeiträge (öffnet sich über [ ☰ ])

┌──────────────────────────────────────────────────────────┐
│   Zurück     Gemeindesonntag  14.06.2026                │
│  ┌────────────────────────────────────────────────────┐  │
│  │ ☐  01  Begrüßung & Moderation                      │  │
│  │ ☐  02  Lobpreis                                    │  │
│  │ ☑  03  Predigt  Max Müller                        │  │
│  │ ☐  04  Abschluss                                   │  │
│  └────────────────────────────────────────────────────┘  │
│         [ Alle ]      [  ⬇ Auswahl auf USB kopieren  ]    │
└──────────────────────────────────────────────────────────┘

Kein USB angesteckt

│  USB-Speicher        │             [  ⬇  Auf USB kopieren  ]   (deaktiviert)
│  ○ Kein Speicher     │
│    Bitte anstecken   │      ⚠  Bitte einen USB-Speicher anstecken.

Kopieren abbrechen (Dialog)

┌──────────────────────────────────────────────────────────┐
│  Kopieren abbrechen?                                      │
│  Gerade wird kopiert: „03  Predigt  Max Müller"        │
│                                                           │
│  ( ) Aktuellen Beitrag noch fertig kopieren, dann stoppen │
│  ( ) Alles bereits Kopierte wieder löschen (Rückgängig)  │
│                                                           │
│        [ Weiter kopieren ]        [ Abbrechen … ]         │
└──────────────────────────────────────────────────────────┘

Speicher freigeben (Dialog)

Der Unterschied zwischen den Lösch-Varianten wird direkt im Dialog erklärt:

┌──────────────────────────────────────────────────────────────┐
│  Speicher auf dem USB-Stick freigeben                         │
│  Es werden noch 250 MB benötigt.                              │
│                                                               │
│  (•) Älteste Veranstaltung zuerst                            │
│      → nach dem Veranstaltungsdatum (Ordnername, MP3-Album)   │
│                                                               │
│  ( ) Zuerst gespeicherte zuerst                              │
│      → danach, wann es auf den Stick kopiert wurde (Zeitstpl.)│
│                                                               │
│  ( ) Alles löschen (Formatieren)                             │
│      → entfernt ALLE Daten auf dem Stick                      │
│                                                               │
│  ( ) Bestimmte Ordner/Alben auswählen (Mehrfachauswahl)      │
│      → unten gezielt ankreuzen, was gelöscht werden soll      │
│                                                               │
│  Vorschau  wird gelöscht (260 MB):                          │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 📁 2026-05-03 Morgen  Themengottesdienst …    120 MB │   │
│  │ 📁 2026-05-10 Abend   Gebetsabend …            95 MB │   │
│  │ 📄 alte_ansage.mp3   (lose im Wurzelordner)    45 MB │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                               │
│              [ Abbrechen ]      [ Löschen … ]                 │
└──────────────────────────────────────────────────────────────┘
  • Die Vorschau listet genau die betroffenen Ordner (Alben) auf — und einzelne Dateien, falls lose Dateien direkt im Wurzelverzeichnis des Sticks liegen (Symbol 📄). Sie aktualisiert sich je nach gewählter Variante; gelöscht wird erst nach Bestätigung.

  • Bei „Bestimmte Ordner/Alben auswählen" wird die Liste ankreuzbar (Mehrfachauswahl); gelöscht wird genau die Auswahl:

    Bestimmte Ordner/Alben löschen:
    ┌──────────────────────────────────────────────────────┐
    │ ☑ 📁 2026-06-07 Morgen  Themengottesdienst …  120 MB │
    │ ☐ 📁 2026-06-08 Abend   Gebetsabend …          95 MB │
    │ ☑ 📁 2026-05-31 Morgen  Predigtreihe Römer …  130 MB │
    │ ☐ 📄 alte_ansage.mp3   (lose im Wurzelordner)  45 MB │
    └──────────────────────────────────────────────────────┘
    Ausgewählt: 2 Einträge (250 MB)        [ Auswahl löschen … ]
    
  • Hinweis: „Veranstaltungsdatum" und „Kopierzeitpunkt" können abweichen, wenn alte Gottesdienste erst spät auf den Stick gezogen werden — deshalb beide Varianten getrennt.

Einstellungen (passwortgeschützt, mit Touch-Tastatur)

┌───────────────────────────────────────────────┐
│  Einstellungen  Anmeldung                     │
│   Passwort:  [ • • • • • •            ]        │
│   ┌─────────────────────────────────────────┐ │
│   │ q w e r t z u i o p ü    ⌫              │ │   ← eingeblendete
│   │ a s d f g h j k l ö ä    ↵              │ │     Touch-Tastatur
│   │ ⇧ y x c v b n m , . -                   │ │
│   │ [        Leertaste        ] 123         │ │
│   └─────────────────────────────────────────┘ │
└───────────────────────────────────────────────┘

Nach Login: Tabs für Veranstaltungstypen verwalten, Nextcloud (Login Flow v2), USB / Autoradio-Standard, Feiertage, Cache (Pfad & Schwelle), HTTPS-Passwort. Die HTTPS-Seiten (Status/Logs, Einstellungen) sind kein Teil dieser GUI, sondern werden im Browser auf einem anderen Gerät im LAN aufgerufen.

Fehlerfälle & Meldungen

Leitlinien:

  • Kiosk-Meldungen sind kurz, verständlich und nennen eine konkrete Handlung (für Nutzer ohne technisches Wissen).
  • Technische Details (Stacktraces, Codes, Pfade) erscheinen nicht im Kiosk, sondern auf der HTTPS-Status-/Log-Seite.
  • Wo sinnvoll wird automatisch wiederholt (v. a. Netzwerk), ohne den Nutzer zu blockieren.
  • Bei Abbruch eines Kopiervorgangs werden unvollständige Dateien auf dem Stick wieder entfernt.
  • Fehler dürfen die GUI nie einfrieren und nicht zum Absturz führen.

Nextcloud / Datenquelle

Situation Meldung (Kiosk) Behebung
Server nicht erreichbar / kein Netzwerk „Keine Verbindung zur Nextcloud es werden die zuletzt geladenen Veranstaltungen angezeigt." Aus lokalem Cache weiterarbeiten; automatischer Retry im Hintergrund; Netzwerk/Server prüfen (Details im Log).
Anmeldung fehlgeschlagen / App-Passwort ungültig oder widerrufen „Anmeldung bei Nextcloud fehlgeschlagen. Bitte in den Einstellungen neu anmelden." In den Einstellungen Login Flow v2 erneut durchlaufen → neues App-Passwort.
Login Flow nicht bestätigt / Zeitüberschreitung „Anmeldung nicht abgeschlossen bitte den Link im Browser bestätigen." Anmeldevorgang erneut starten.
Quellordner fehlt / falscher Pfad „Quellordner nicht gefunden. Bitte Einstellungen prüfen." Nextcloud-Pfad in den Einstellungen korrigieren.
Einzelne Datei nicht ladbar / Download unvollständig „Eine Datei konnte nicht geladen werden neuer Versuch …" Automatischer Retry; bleibt es fehlerhaft, Eintrag mit Hinweis überspringen.
Ordner ohne JSON/TXT (keine Fehlermeldung) Erwartetes Verhalten: Ordner erscheint einfach nicht in der Liste, bis ngenRecord die Datei beim Fertigstellen anlegt.

USB-Speicher

Situation Meldung (Kiosk) Behebung
Kein Stick angesteckt „Kein USB-Speicher erkannt. Bitte Stick anstecken." Kopieren-Button bleibt deaktiviert, bis ein Stick erkannt wird.
Stick während des Kopierens abgezogen „USB-Speicher wurde entfernt Kopiervorgang abgebrochen." Unvollständige Dateien werden (falls noch erreichbar) entfernt; Stick wieder anstecken und neu starten.
Nicht genug Platz „Nicht genug Speicherplatz: benötigt X MB, frei Y MB." Speicher freigeben oder anderen Stick verwenden; Größe wird vor dem Kopieren geprüft.
Schreibgeschützt „USB-Speicher ist schreibgeschützt." Schreibschutz-Schalter am Stick lösen oder anderen Stick verwenden.
Dateisystem nicht beschreibbar/unterstützt „Auf den USB-Speicher kann nicht geschrieben werden (Dateisystem)." Stick mit FAT32/exFAT formatieren (Hinweis: Formatieren löscht alle Daten).
Zugriffs-/Mount-Fehler „Auf den USB-Speicher kann nicht zugegriffen werden. Bitte erneut anstecken." Stick neu anstecken; Details im Log (Rechte/Mount).
Mehrere Sticks erkannt „Mehrere USB-Speicher erkannt. Bitte nur einen anstecken." Wird i. d. R. physikalisch verhindert; nur einen Speicher anstecken.
Sicheres Entfernen fehlgeschlagen (noch in Benutzung) „Speicher wird noch verwendet und kann nicht sicher entfernt werden." Kurz warten, bis der Schreibvorgang abgeschlossen ist, dann erneut.

Kopiervorgang

Situation Meldung (Kiosk) Behebung
Album liegt bereits auf dem Stick „Album schon vorhanden wird überschrieben." Kein Eingriff nötig; wird überschrieben.
Kopieren vom Nutzer abgebrochen „Kopieren abgebrochen." Je nach Auswahl: aktuellen Beitrag fertigstellen oder bereits Kopiertes wieder löschen (s. Dialog „Kopieren abbrechen").
Quelldatei zwischenzeitlich entfernt/geändert „Eine Datei der Veranstaltung ist nicht mehr verfügbar Liste wird aktualisiert." Liste neu laden; Vorgang erneut starten.
Kopie unvollständig / Prüfsumme passt nicht „Kopieren unvollständig. Bitte erneut versuchen." Teil-Dateien aufräumen und erneut kopieren.
Autoradio-Optimierung fehlgeschlagen „Autoradio-Optimierung fehlgeschlagen bitte erneut versuchen oder Option deaktivieren." Optimierung deaktiviert kopieren oder erneut versuchen (Details im Log).
Ungültige/zu lange Dateinamen für FAT32 (automatisch bereinigt) Bei aktiver Autoradio-Optimierung werden Sonderzeichen/Längen bereinigt; sonst Hinweis.

Einstellungen / Anmeldung

Situation Meldung (Kiosk) Behebung
Falsches Passwort „Falsches Passwort." Erneut eingeben; nach mehreren Fehlversuchen kurze Verzögerung gegen Erraten.
Kein Passwort gesetzt (Erststart) „Bitte ein Passwort für die Einstellungen festlegen." Beim Ersteinrichten Passwort vergeben (wird als Hash gespeichert).
Erster Aufruf der HTTPS-Seite ohne Passwort „Bitte zunächst ein Passwort für die HTTPS-Seite festlegen." Beim ersten Aufruf HTTPS-Passwort anlegen; erst danach Zugriff.
Einstellungen/Konfiguration beschädigt „Einstellungen konnten nicht gelesen werden Standardwerte werden verwendet." Backup aus Nextcloud wiederherstellen oder neu konfigurieren.

HTTPS-Server (Meldungen auf Status-/Log-Seite bzw. beim Start)

Situation Meldung Behebung
Port bereits belegt „HTTPS-Server konnte nicht starten: Port belegt." Anderen Port in den Einstellungen wählen; blockierenden Dienst beenden.
Zertifikat fehlt/ungültig/abgelaufen „Zertifikat ungültig oder abgelaufen." Neues (selbstsigniertes) Zertifikat erzeugen; auf den Clients neu vertrauen.
Binden/Netzwerk fehlgeschlagen „HTTPS-Server nicht erreichbar." Netzwerk/Firewall im LAN prüfen (Details im Log). Beeinträchtigt die GUI nicht.

Updates (Meldungen auf Status-/Log-Seite; im Kiosk nur dezenter Hinweis)

Situation Meldung Behebung
Repo/Netzwerk nicht erreichbar „Update konnte nicht geprüft werden wird später erneut versucht." Automatischer Retry im nächsten Zeitfenster; Netzwerk/Repo prüfen (Details im Log).
Signatur/Prüfsumme ungültig „Update abgelehnt Signatur ungültig." Update wird nicht installiert; Paketquelle/Schlüssel prüfen, Administrator informieren.
Update während laufendem Kopiervorgang fällig (keine Meldung) Erwartetes Verhalten: Update wird verschoben, bis Kopiervorgang/USB-Zugriff beendet ist.
Zu wenig Speicher für Update „Update nicht möglich zu wenig Speicherplatz." Cache/Logs aufräumen (s. Lokaler Cache), dann erneut; Details im Log.
Update/Neustart fehlgeschlagen „Aktualisierung fehlgeschlagen vorige Version wiederhergestellt." Automatischer Rollback auf die letzte funktionierende Version; Fehler im Log, Administrator informieren.

System / lokal

Situation Meldung Behebung
Lokaler Speicher (Cache) voll „Zu wenig lokaler Speicher bitte Administrator informieren." Cache wird ab der eingestellten Schwelle automatisch reduziert (s. Lokaler Cache); zusätzlich Logs aufräumen (Logrotation), ggf. Schwelle senken oder Speicher erweitern.
Fehlende lokale Schreibrechte (Log) Rechte des Datenverzeichnisses prüfen.
Absturz der Anwendung (automatischer Neustart) Autostart/Watchdog startet die App neu; letzter Zustand steht im Log.