10-04-2026

This commit is contained in:
Kevin Adametz 2026-04-10 17:18:17 +02:00
parent 4d6b4930b2
commit 4bb89aad8c
836 changed files with 52961 additions and 5950 deletions

View file

@ -0,0 +1,274 @@
# CABINET Digital Signage System - Projektdokumentation
**Stand:** Februar 2026 | **Version:** 1.3
---
## 1. Projektübersicht
Das CABINET Digital Signage System ist ein In-Store-Display-System für den CABINET Store in Bielefeld. Es besteht aus zwei eigenständigen Modulen, die auf hochkant montierten Displays (9:16 Seitenverhältnis, 1080x1920px) im Store laufen:
| Modul | Datei | Live-URL | Zweck |
|-------|-------|----------|-------|
| **Video-Display** | `index.html` | `cabinet.b2in.eu` | Zeigt eine Endlos-Playlist von Videos mit rotierenden Footer-Inhalten (Headline, Subline, QR-Code) |
| **Angebots-Display** | `offers/player.html` | (noch nicht live) | Zeigt statische Produkt-Slides (Angebote, Preise, Details) als Slide-Rotation |
| **Info-Tablet** | (in Planung) | `cabinet.b2in.eu/info` | Zeigt Store-Status, Öffnungszeiten, Termine im Schaufenster |
Alle Inhalte werden uber das B2in-Backend (Admin-Portal unter `portal.b2in.test`) gepflegt.
---
## 2. Architektur
```
public/_cabinet/
├── index.html # LIVE: Video-Display (Hauptseite)
├── index_1.html # Ältere Version
├── index_2.html # Ältere Version
├── index-dynamic.html # Dynamische Variante
├── index-static-backup.html # Statisches Fallback
├── index copy.html # Kopie/Backup
├── offer.html # Prototyp: Angebots-Slides (all-in-one)
├── offers/ # NEU: Modulares Angebots-System
│ ├── player.html # Slide-Player (lädt config.json + iFrames)
│ ├── config.json # Slide-Konfiguration (Reihenfolge, Dauer, Inhalte)
│ ├── shared-styles.css # Gemeinsame CSS-Styles für alle Slides
│ ├── slide-0-intro.html # Intro-Slide (Store-Vorstellung)
│ ├── slide-1-goya-hero.html # Produkt-Slide: GOYA Sideboard (Hero)
│ ├── slide-2-goya-details.html# Produkt-Slide: GOYA Details/Konditionen
│ └── slide-3-tando.html # Produkt-Slide: TANDO Spiegel (Impuls)
├── assets/ # Medien-Dateien
│ ├── *.mp4 # Videos (Saison-Spots)
│ ├── *.jpg # Produkt-/Hintergrundbilder
│ └── cabinet-intro.jpg # Intro-Bild
├── go.php # QR-Code Redirect + Klick-Tracking
├── logger.php # Remote-Logging-Endpoint (CORS-fähig)
├── view-logs.php # Web-basierter Log-Viewer
├── setup-logging.sh # Logging-Setup-Skript
├── test-logging.html # Test-Seite für Logging
├── logs/ # Log-Dateien (nach Level + Datum)
├── clicks.log # QR-Code-Klick-Log
├── logo-cabinet-300.png # CABINET Logo (300px)
├── logo-cabinet.png.webp # CABINET Logo (WebP)
├── .htaccess.example # Apache-Konfigurationsvorlage
├── infotablet.md # Konzept: Info-Tablet für Schaufenster
├── offer.md # Notizen zum Angebots-System
├── QUICK_START.md # Bedienungsanleitung
├── KIOSK_MODE_SETUP.md # Anleitung Kiosk-Modus (Fully Browser)
├── LOGGING_README.md # Logging-Dokumentation
├── VIDEO_OPTIMIZATION_README.md# Video-Optimierungstipps
└── CABINET_PROJECT.md # Diese Datei
```
---
## 3. Modul A: Video-Display (`index.html`)
### Funktionsweise
- Lädt die Konfiguration (Video-Playlist + Footer-Inhalte) per API vom Backend
- Spielt Videos nacheinander in einer Endlosschleife ab
- Footer rotiert unabhängig alle 30 Sekunden (Headline + Subline + QR-Code)
### API-Anbindung
- **Endpoint:** `GET /api/display/config` (auf `b2in.eu`)
- **Response:**
```json
{
"videoPlaylist": [
{ "src": "assets/fruehjahr_2025.mp4", "position": 25 }
],
"footerContent": [
{ "headline": "Text", "subline": "Subtext", "url": "https://..." }
]
}
```
- **Polling:** Alle 5 Minuten wird die Config neu geladen
- **Präventiver Reload:** Alle 6 Stunden kompletter Page-Reload
### Robustheit
- **Video-Watchdog:** Prüft alle 5 Sek. ob Video läuft, Recovery bei Stuck
- **Start-Timeout:** 10 Sek. Timeout, dann Skip zum nächsten Video
- **Memory-Management:** Cleanup nach jedem Video, Monitoring alle 10 Min.
- **Error-Recovery:** Nach 3 aufeinanderfolgenden Fehlern → Page-Reload
- **Remote-Logging:** Alle Events werden an `logger.php` gesendet
### Layout
```
┌─────────────────┐
│ │
│ VIDEO-BEREICH │ flex-grow: 1 (nimmt restliche Höhe ein)
│ (object-fit: │
│ cover) │
│ │
├──────────────────┤
│ ▬▬▬ Progress ▬▬ │ 3px Progress-Bar
│ CTA-TEXT │ QR │ ~10% Höhe
│ Headline │Code │ Footer mit rotierendem Inhalt
│ Subline │ │
└─────────────────┘
```
---
## 4. Modul B: Angebots-Display (`offers/`)
### Funktionsweise
- `player.html` ist der Haupt-Player
- Lädt `config.json` für Slide-Konfiguration
- Jeder Slide ist ein eigenständiges HTML-Dokument, eingebettet per iFrame
- Slides rotieren automatisch mit konfigurierbarer Dauer (8-12 Sek.)
- Fade-Transition (0.6s) zwischen Slides
### Slide-Typen
| Typ | Beschreibung | Beispiel |
|-----|-------------|---------|
| `intro` | Store-Vorstellung, allgemeiner Willkommenstext | slide-0-intro.html |
| `product-hero` | Produkt mit Hero-Bild, Preis, UVP | slide-1-goya-hero.html |
| `product-details` | Produkt-Details mit Bullet-Liste | slide-2-goya-details.html |
| `product-impulse` | Impulskauf-Produkt ("Jetzt mitnehmen") | slide-3-tando.html |
### Slide-Layout (shared-styles.css)
```
┌─────────────────────────────┐
│ HEADER │ Logo + Tagline
│ [CABINET Logo] [Tagline] │
├─────────────────────────────┤
│ │
│ HERO-BILD │ flex: 1 (nimmt Hauptfläche ein)
│ (Produktfoto) │
│ [Badge] │
│ │
├──────────────┬──────────────┤
│ INFO-BOX │ QR-BOX │ Unterer Bereich
│ Eyebrow │ Titel │
│ Titel │ QR-Code │
│ Preis/UVP │ Kontakt │
│ Bullets │ │
└──────────────┴──────────────┘
```
### Design-System
- **Schrift:** IBM Plex Sans (Google Fonts)
- **Akzentfarbe:** `#009FE3` (Cabinet Blau)
- **Safe-Area:** 64px Padding
- **Max. Auflösung:** 1080x1920px (9:16)
- **QR-Codes:** Werden live per `api.qrserver.com` generiert
### Status
Die Angebots-Slides sind **fertig entwickelt aber noch nicht live**. Die Inhalte (Produkte, Preise, Bilder) sind statisch in den HTML-Dateien hinterlegt. Eine Backend-Anbindung (wie beim Video-Display) fehlt noch.
---
## 5. Modul C: Info-Tablet (in Planung)
Konzept dokumentiert in `infotablet.md`. Ein kleines Tablet im Schaufenster zeigt:
- Store-Status (Geöffnet/Hinweis/Geschlossen)
- Öffnungszeiten (Wochenansicht, heutiger Tag hervorgehoben)
- Nächster freier Beratungstermin
- Kontakt + QR-Code
**API geplant:** `GET /api/cabinet-tablet/status`
---
## 6. Backend-Anbindung
### Datenbank-Tabellen
**`display_videos`** - Video-Playlist
| Spalte | Typ | Beschreibung |
|--------|-----|-------------|
| id | bigint | Primary Key |
| filename | varchar | Dateiname (z.B. `fruehjahr_2025.mp4`) |
| title | varchar | Anzeige-Titel (optional) |
| position | int | Vertikale Position des Videos (0-100, für `object-position`) |
| sort_order | int | Reihenfolge in der Playlist |
| is_active | tinyint | Aktiv/Inaktiv |
**`display_footer_contents`** - Footer-Inhalte (CTA-Texte + QR-Codes)
| Spalte | Typ | Beschreibung |
|--------|-----|-------------|
| id | bigint | Primary Key |
| headline | varchar | Überschrift (z.B. "JETZT TERMIN BUCHEN") |
| subline | varchar | Unterzeile (z.B. "Beratung vor Ort...") |
| url | varchar | Ziel-URL für QR-Code (optional) |
| short_code | varchar | 6-stelliger Code für Redirect (unique) |
| clicks | int | Klick-Zähler (via go.php) |
| sort_order | int | Reihenfolge |
| is_active | tinyint | Aktiv/Inaktiv |
### Backend-Komponenten
| Komponente | Pfad | Funktion |
|-----------|------|----------|
| API-Controller | `app/Http/Controllers/Api/DisplayConfigController.php` | Liefert JSON-Config für Video-Display |
| Video-Model | `app/Models/DisplayVideo.php` | Eloquent-Model mit `active()` Scope |
| Footer-Model | `app/Models/DisplayFooterContent.php` | Model mit Short-Code-Generierung + Short-URL |
| Admin-Seite | `app/Livewire/Admin/Cms/CabinetDisplay.php` | Livewire-Verwaltung für Videos + Footer |
| Admin-Route | `/admin/cms/cabinet` | Admin-Portal-Seite |
| API-Route | `GET /api/display/config` | Öffentlicher API-Endpoint |
### QR-Code / Redirect-System
1. Backend generiert automatisch einen 6-stelligen `short_code` pro Footer-Inhalt
2. QR-Code im Display zeigt URL wie `cabinet.b2in.eu/go.php?z=abc123`
3. `go.php` schlägt den Code in der DB nach, erhöht den Klick-Zähler, und leitet weiter
4. Fallback: Alte Legacy-Codes (t, t1, p, i, f) für Rückwärtskompatibilität
---
## 7. Logging & Monitoring
### Remote-Logging (`logger.php`)
- Empfängt POST-Requests mit JSON-Logdaten vom Display
- CORS-aktiviert für Cross-Origin-Zugriff
- Speichert Logs in drei Formaten:
- Pro Level: `logs/info_2026-02-27.log`, `logs/error_2026-02-27.log`
- Alle zusammen: `logs/all_2026-02-27.log`
- JSON: `logs/json_2026-02-27.log`
- Automatische Log-Rotation: Dateien > 30 Tage werden gelöscht
### Was wird geloggt
- Video-Lifecycle: Start, Ende, Error, Stuck, Timeout
- Heartbeat alle 5 Minuten ("Display läuft")
- Memory-Status alle 10 Minuten
- Online/Offline-Status
- JavaScript-Fehler (global error handler)
- Resource-Loading-Fehler (Bilder, Videos)
### Log-Viewer (`view-logs.php`)
Web-basierte Oberfläche zum Lesen der Logs mit Auto-Refresh und Farbcodierung.
---
## 8. Deployment & Betrieb
### Live-System
- **Domain:** `cabinet.b2in.eu` (Subdomain von b2in.eu)
- **Assets-URL:** `https://b2in.eu/_cabinet/assets/`
- **API-URL:** `https://b2in.eu/api/display/config`
### Test-System
- **URL:** `b2in.test/_cabinet/`
- **API:** `b2in.test/api/display/config`
### Kiosk-Modus
Empfohlen: **Fully Kiosk Browser** (Android) - Details in `KIOSK_MODE_SETUP.md`
- Auto-Start bei Boot
- Vollbild ohne Statusbar
- Auto-Restart bei Crash
- Zeitsteuerung (22:00 Standby, 07:00 Aufwachen)
---
## 9. Offene Punkte / TODO
- [ ] **Offers-System Backend-Anbindung**: `offers/` Slides haben noch keine API-Anbindung - Inhalte sind statisch im HTML
- [ ] **Offers live schalten**: Player + Slides auf Live-System deployen
- [ ] **Info-Tablet umsetzen**: Konzept steht (infotablet.md), Implementierung offen
- [ ] **Offers Config aus DB**: `config.json` sollte dynamisch vom Backend generiert werden
- [ ] **Bilder-Upload**: Produktbilder für Offers werden noch manuell in `assets/` abgelegt

View file

@ -0,0 +1,480 @@
# Chrome Kiosk-Mode Setup für Android Digital Signage
## 🎯 Problem
Bei einem Page-Reload (z.B. nach 6h automatisch) wird der Vollbildmodus beendet - das ist eine Browser-Sicherheitsmaßnahme. Der Vollbildmodus kann nicht automatisch per Script reaktiviert werden.
## ✅ Die Lösung: Chrome Kiosk-Mode
Der **Kiosk-Mode** ist die professionelle Lösung für Digital Signage. Chrome läuft dabei permanent im Vollbild ohne Browser-UI.
---
## 📱 Option 1: Chrome Kiosk-Mode (Empfohlen für Android)
### Voraussetzungen:
- Android-Gerät (Tablet/Display)
- Chrome Browser installiert
- ADB (Android Debug Bridge) für Setup
### Setup-Schritte:
#### 1. **Developer-Optionen aktivieren**
1. Gehe zu **Einstellungen** → **Über das Telefon/Tablet**
2. Tippe **7x auf "Build-Nummer"**
3. Developer-Optionen sind jetzt aktiv
#### 2. **USB-Debugging aktivieren**
1. Gehe zu **Einstellungen** → **Entwickleroptionen**
2. Aktiviere **"USB-Debugging"**
3. Verbinde Gerät per USB mit Computer
#### 3. **ADB installieren (auf Computer)**
**Windows:**
```bash
# Download Android Platform Tools
https://developer.android.com/studio/releases/platform-tools
# Entpacken und zu PATH hinzufügen
```
**macOS:**
```bash
brew install android-platform-tools
```
**Linux:**
```bash
sudo apt install android-tools-adb
```
#### 4. **Chrome im Kiosk-Mode starten**
```bash
# Verbindung testen
adb devices
# Chrome beenden
adb shell am force-stop com.android.chrome
# Chrome im Kiosk-Mode starten
adb shell am start \
-n com.android.chrome/com.google.android.apps.chrome.Main \
-a android.intent.action.VIEW \
-d "https://cabinet.b2in.eu" \
--ez create_new_tab true \
--activity-clear-task \
--activity-clear-top \
--activity-single-top
# Optional: Vollbild erzwingen
adb shell settings put global policy_control immersive.full=*
```
#### 5. **Auto-Start beim Boot (Optional)**
Erstelle eine Boot-App oder nutze Automate-Apps:
**Mit MacroDroid (kostenlose App):**
1. Installiere MacroDroid aus Play Store
2. Erstelle Macro:
- **Trigger:** "Device Boot"
- **Action:** "Launch Application" → Chrome
- **Action:** "Load Webpage" → https://cabinet.b2in.eu
3. Speichern und aktivieren
---
## 📱 Option 2: Dedicated Kiosk-Browser Apps
### Empfohlene Apps für Android Digital Signage:
### **1. Fully Kiosk Browser** (⭐ Empfohlen)
**Features:**
- ✅ Echter Kiosk-Modus (keine UI, kein Zurück-Button)
- ✅ Auto-Start beim Boot
- ✅ Remote-Management
- ✅ Screensaver-Funktion
- ✅ Keep Screen On
- ✅ Remote-Config via Web-Interface
**Installation:**
```
1. Download: https://www.fully-kiosk.com
2. Installiere APK auf Android
3. Öffne App
4. Settings:
- Start URL: https://cabinet.b2in.eu
- Kiosk Mode: ON
- Launch on Boot: ON
- Hide System UI: ON
- Keep Screen On: ON
- Reload on Network Restore: ON
5. Lock App (Admin Pin setzen)
```
**Kosten:**
- Kostenlos für Single-Device
- Plus Version: ~€20 (einmalig) für erweiterte Features
### **2. Kiosk Browser Lockdown**
**Features:**
- ✅ Einfaches Setup
- ✅ Kiosk-Mode
- ✅ Auto-Start
- ✅ Kostenlos
**Installation:**
```
1. Play Store: "Kiosk Browser Lockdown"
2. URL setzen: https://cabinet.b2in.eu
3. Kiosk Mode aktivieren
4. PIN setzen
```
### **3. Chrome mit Custom Launcher**
**Features:**
- ✅ Nutzt Chrome Engine
- ✅ Custom Launcher ersetzt Home-Screen
- ✅ Kostenlos
**Apps:**
- "Screen On" (Play Store)
- "Stay Alive!" (Play Store)
- "Screen Alive" (Play Store)
---
## 💻 Option 3: Chrome Flags (Desktop/Android)
### Für Desktop-Testing:
**Chrome starten mit Flags:**
**Windows:**
```powershell
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
--kiosk "https://cabinet.b2in.eu" ^
--disable-session-crashed-bubble ^
--disable-infobars ^
--noerrdialogs ^
--disable-translate ^
--no-first-run ^
--fast-start ^
--disable-features=TranslateUI ^
--disk-cache-dir=NUL ^
--overscroll-history-navigation=0
```
**macOS:**
```bash
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--kiosk "https://cabinet.b2in.eu" \
--disable-session-crashed-bubble \
--disable-infobars \
--noerrdialogs
```
**Linux:**
```bash
google-chrome \
--kiosk "https://cabinet.b2in.eu" \
--disable-session-crashed-bubble \
--disable-infobars \
--noerrdialogs \
--disable-translate \
--no-first-run
```
### Flags Erklärung:
- `--kiosk` - Vollbild-Modus ohne UI
- `--disable-session-crashed-bubble` - Keine "Chrome wurde nicht korrekt beendet" Meldung
- `--disable-infobars` - Keine Info-Leisten
- `--noerrdialogs` - Keine Error-Dialoge
- `--disable-translate` - Keine Übersetzungs-Popups
- `--no-first-run` - Kein First-Run-Dialog
---
## 🔧 Option 4: Unsere Fallback-Lösung (bereits implementiert)
### Was haben wir implementiert:
#### 1. **LocalStorage-Tracking**
```javascript
// Beim Aktivieren merken
localStorage.setItem('cabinet_fullscreen_was_active', 'true');
// Nach Reload prüfen
if (wasFullscreen) {
// Auffälliger Reminder anzeigen
}
```
#### 2. **Visueller Reminder**
- **Orange pulsierender Button** nach Reload
- Text: "⚠️ Vollbild aktivieren!"
- Automatisch sichtbar wenn Fullscreen vorher aktiv war
#### 3. **Auto-Retry (30s Delay)**
```javascript
// Nach 30s automatisch versuchen (falls Kiosk-Mode aktiv)
setTimeout(() => {
enterFullscreen(); // Wird ignoriert wenn keine User-Geste
}, 30000);
```
#### 4. **Logging**
```javascript
✅ "Fullscreen aktiviert"
⚠️ "Fullscreen-Reminder angezeigt"
"Fullscreen verlassen"
```
### Vorteile:
- ✅ Funktioniert ohne zusätzliche Apps
- ✅ Visueller Hinweis dass Fullscreen reaktiviert werden muss
- ✅ Nutzer wird "erinnert" nach Reload
- ✅ Logging für Monitoring
### Nachteile:
- ❌ Erfordert manuellen Klick nach Reload
- ❌ Nicht vollautomatisch
---
## 🎯 Empfohlenes Setup für Production
### Für Android Digital Signage Displays:
#### **Best Practice:**
```
1. Hardware: Android Tablet/Display
└─ Empfohlen: Android 9+ mit min 2GB RAM
2. Software: Fully Kiosk Browser (Plus)
├─ Start URL: https://cabinet.b2in.eu
├─ Kiosk Mode: ON
├─ Launch on Boot: ON
├─ Hide System UI: ON
├─ Keep Screen On: ON
├─ Reload on Network Restore: ON
└─ Remote Management: ON
3. Network: Kabelgebunden (LAN)
└─ Fallback: 5GHz WiFi mit statischer IP
4. Power: USV/Surge Protection
└─ Auto Power-On nach Stromausfall
5. Monitoring:
├─ Fully Kiosk Remote Admin
└─ Unsere Logs: view-logs.php
```
### Setup-Checklist:
- [ ] Android-Gerät vorbereitet
- [ ] Fully Kiosk Browser installiert
- [ ] Kiosk-Mode konfiguriert
- [ ] Auto-Start aktiviert
- [ ] System-UI versteckt
- [ ] Keep-Screen-On aktiviert
- [ ] Remote-Management aktiviert (Optional)
- [ ] PIN-Schutz gesetzt
- [ ] Display-Helligkeit eingestellt
- [ ] Netzwerk getestet (LAN bevorzugt)
- [ ] Power-Management konfiguriert
- [ ] Test-Lauf 24h durchgeführt
- [ ] Monitoring aktiv (Logs checken)
---
## 🔍 Troubleshooting
### Problem: Kiosk-Mode wird nicht aktiviert
**Lösung 1: Developer-Optionen**
```
Settings → Developer Options → "Stay Awake" ON
```
**Lösung 2: App-Berechtigungen**
```
Settings → Apps → Fully Kiosk → Permissions
- Display over other apps: ALLOW
- Auto-start: ALLOW
```
**Lösung 3: Device Admin**
```
Settings → Security → Device Administrators
- Fully Kiosk Browser: ENABLE
```
### Problem: Display schaltet sich ab
**Lösung:**
```
1. Fully Kiosk Settings:
- Keep Screen On: ON
- Screen Saver: OFF
- Prevent Sleep: ON
2. Android Settings:
- Display → Sleep: NEVER
- Display → Adaptive Brightness: OFF
```
### Problem: Chrome Exit nach Reload
**Lösung:**
```
Nutze Fully Kiosk Browser statt Chrome!
- Fully ist speziell für Kiosk designed
- Kein ungewolltes Exit möglich
- Automatischer Neustart bei Crash
```
### Problem: Zurück-Button verlässt App
**Lösung:**
```
Fully Kiosk Settings:
- Disable Back Button: ON
- Disable Home Button: ON (Device Admin nötig)
- Kiosk Mode: Advanced
```
---
## 📊 Vergleich der Optionen
| Option | Kosten | Komplexität | Zuverlässigkeit | Empfehlung |
|--------|--------|-------------|-----------------|------------|
| **Fully Kiosk** | €20 | ⭐⭐ | ⭐⭐⭐⭐⭐ | 🥇 **BEST** |
| **Chrome Kiosk (ADB)** | Kostenlos | ⭐⭐⭐⭐ | ⭐⭐⭐ | OK für Tech-Versierte |
| **Kiosk Browser Free** | Kostenlos | ⭐⭐ | ⭐⭐⭐ | OK für Testing |
| **Unsere Fallback-Lösung** | Kostenlos | ⭐ | ⭐⭐ | Fallback |
| **Chrome + Custom Launcher** | Kostenlos | ⭐⭐⭐ | ⭐⭐⭐ | Mittel |
---
## 🎬 Quick Start: Fully Kiosk (Empfohlen)
### 5-Minuten-Setup:
```bash
1. Download: https://www.fully-kiosk.com
└─ APK auf Android-Gerät installieren
2. App öffnen → Settings:
Start URL: https://cabinet.b2in.eu
3. Advanced Settings:
[x] Kiosk Mode
[x] Launch on Boot
[x] Hide System UI
[x] Keep Screen On
[x] Prevent Sleep
4. Lock Settings (+ Button):
└─ PIN setzen (z.B. 1234)
5. ✅ Fertig! Display läuft 24/7 im Kiosk-Mode
```
### Remote-Management aktivieren:
```
1. Settings → Remote Administration:
[x] Enable Remote Administration
[x] Remote Admin from Local Network
2. Notiere IP-Adresse:
z.B. http://192.168.1.100:2323
3. Öffne vom PC:
http://192.168.1.100:2323
└─ Password: (Dein PIN)
4. ✅ Remote-Control aktiv!
- Screenshots
- Reload
- Settings ändern
- Screen On/Off
```
---
## 💡 Pro-Tipps
### 1. **Netzwerk-Stabilität**
```
- LAN bevorzugen (kein WiFi)
- Statische IP vergeben
- Router/Switch mit QoS
- Fully Kiosk: "Reload on Network Restore" ON
```
### 2. **Power-Management**
```
- USV verwenden
- BIOS: "AC Power Recovery" → ON
- Android: "Auto Power On" → ON
- Fully Kiosk: "Restart on Crash" → ON
```
### 3. **Display-Pflege**
```
- Bildschirmschoner nach 22:00 Uhr
- Helligkeit reduzieren nachts
- Pixel-Shift aktivieren (gegen Burn-In)
- Display-Timeout: NEVER
```
### 4. **Monitoring**
```
- Fully Kiosk Remote Admin
- Unsere Logs: view-logs.php
- Ping-Monitoring (Nagios/Zabbix)
- Wöchentliche Checks
```
### 5. **Security**
```
- Fully Kiosk mit PIN schützen
- Device Administrator aktivieren
- USB-Debugging OFF (nach Setup)
- Unknown Sources OFF
```
---
## 📞 Support
### Fully Kiosk Support:
- Website: https://www.fully-kiosk.com
- Forum: https://www.fully-kiosk.com/forum
- Email: support@fully-kiosk.com
### Unsere Logs:
- URL: https://cabinet.b2in.eu/view-logs.php
- Check: Fullscreen-Events
- Monitor: Memory & Errors
---
**Empfehlung:** Investiere die €20 für **Fully Kiosk Browser Plus** - es spart viele Stunden Troubleshooting und ist die stabilste Lösung für Digital Signage! 🎯
---
**Last Update:** 2026-01-19
**Version:** 1.3

View file

@ -0,0 +1,215 @@
# Cabinet Digital Signage - Logging System
## 📋 Übersicht
Dieses erweiterte Logging-System ermöglicht es dir, alle Fehler und Events von den Android-Displays remote zu überwachen, ohne physischen Zugriff auf die Geräte zu benötigen.
## 🎯 Features
### Automatisches Logging von:
- ✅ **JavaScript-Fehler** (Runtime Errors)
- ✅ **Unhandled Promise Rejections** (async/await Fehler)
- ✅ **Console Errors & Warnings**
- ✅ **Resource Loading Failures** (Videos, Bilder)
- ✅ **Video Playback Errors** (mit Media Error Codes)
- ✅ **Network Status** (Online/Offline Events)
- ✅ **Video Stalling** (Buffering-Probleme)
- ✅ **Configuration Loading** (API-Fehler)
- ✅ **Heartbeat** (alle 5 Minuten - zeigt dass Display läuft)
### Log-Levels:
- `FATAL` - Kritische JavaScript-Fehler
- `ERROR` - Fehler (Video-Loading, Network, etc.)
- `WARNING` - Warnungen (Buffering, Connection Lost)
- `INFO` - Informationen (Heartbeat, Video Started, Config Loaded)
## 📂 Dateistruktur
```
public/_cabinet/
├── index.html # Haupt-Display-Datei (mit Logging)
├── logger.php # Backend-Endpoint für Logs
├── view-logs.php # Web-Interface zum Ansehen der Logs
├── logs/ # Log-Verzeichnis (wird automatisch erstellt)
│ ├── all_2026-01-19.log # Alle Logs des Tages
│ ├── error_2026-01-19.log # Nur Errors
│ ├── fatal_2026-01-19.log # Nur Fatal Errors
│ ├── warning_2026-01-19.log # Nur Warnings
│ ├── info_2026-01-19.log # Nur Info
│ └── json_2026-01-19.log # JSON Format (für Parsing)
└── LOGGING_README.md # Diese Datei
```
## 🚀 Setup
### 1. Logs-Verzeichnis erstellen (falls nicht automatisch erstellt)
```bash
mkdir -p public/_cabinet/logs
chmod 755 public/_cabinet/logs
```
### 2. PHP-Konfiguration (falls nötig)
Stelle sicher, dass PHP Schreibrechte auf das `logs/` Verzeichnis hat:
```bash
chown -R www-data:www-data public/_cabinet/logs
# ODER
chmod 777 public/_cabinet/logs # Nur für Development!
```
### 3. Logs ansehen
Öffne im Browser:
```
https://cabinet.b2in.eu/view-logs.php
```
## 📊 Log-Viewer Features
Der `view-logs.php` bietet:
- 📈 **Statistiken** (Anzahl Fatal/Error/Warning/Info)
- 🎨 **Farbcodierung** nach Log-Level
- 🔍 **Dateiauswahl** (verschiedene Log-Dateien)
- ⚡ **Auto-Refresh** (alle 10 Sekunden)
- 📏 **Zeilenanzahl** wählbar (50/100/500/1000/alle)
## 🔒 Sicherheit
**WICHTIG:** Der Log-Viewer sollte in Produktion geschützt werden!
### Option 1: .htaccess Passwortschutz
```apache
# In public/_cabinet/.htaccess
<Files "view-logs.php">
AuthType Basic
AuthName "Restricted Access"
AuthUserFile /pfad/zu/.htpasswd
Require valid-user
</Files>
```
### Option 2: PHP Session-basiert
Füge am Anfang von `view-logs.php` hinzu:
```php
<?php
session_start();
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
// Login-Formular oder Redirect
header('Location: /login.php');
exit;
}
?>
```
## 📝 Kontext-Informationen
Jeder Log-Eintrag enthält automatisch:
- **Timestamp** (ISO 8601)
- **IP-Adresse** des Displays
- **User Agent** (Browser/OS Info)
- **Viewport** (Display-Auflösung)
- **Connection Status** (online/offline)
- **Aktuelles Video** (Dateiname)
- **Video Index** (Position in Playlist)
- **Footer Index** (Aktueller Footer-Content)
- **Playlist-Länge** (Anzahl Videos)
## 🎮 Manuelle Logs
Du kannst auch manuell Logs aus dem JavaScript senden:
```javascript
// Info Log
window.displayLogger.log('Meine Info-Nachricht', {
customData: 'wert'
});
// Warning
window.displayLogger.warn('Warnung', {
reason: 'Irgendwas ist komisch'
});
// Error
window.displayLogger.error('Fehler aufgetreten', {
errorCode: 123
});
// Kontext setzen
window.displayLogger.setContext('customKey', 'customValue');
```
## 🔄 Log-Rotation
Logs werden automatisch rotiert:
- Separate Dateien pro Tag
- Automatische Löschung von Logs älter als 30 Tage
- Verschiedene Dateien pro Log-Level
## 📱 Debugging-Workflow
1. **Problem entdeckt** → Öffne `view-logs.php`
2. **Wähle Log-Datei** → z.B. `error_2026-01-19.log`
3. **Schaue Statistiken** → Wie viele Fehler pro Level?
4. **Analysiere Logs** → Welches Video? Welcher Footer? Welche IP?
5. **Problem beheben** → Update CMS oder Code
6. **Monitoring** → Aktiviere Auto-Refresh zum Live-Monitoring
## 🐛 Häufige Fehlertypen
### MEDIA_ERR_NETWORK (Code 2)
- **Ursache:** Netzwerkprobleme beim Video-Laden
- **Lösung:** Prüfe Internetverbindung, Video-URL, Server-Erreichbarkeit
### MEDIA_ERR_DECODE (Code 3)
- **Ursache:** Video-Codec nicht unterstützt oder Datei korrupt
- **Lösung:** Video neu encodieren (H.264, AAC)
### MEDIA_ERR_SRC_NOT_SUPPORTED (Code 4)
- **Ursache:** Video-Format nicht unterstützt
- **Lösung:** Format zu MP4/WebM ändern
### Promise Rejection
- **Ursache:** Async-Fehler (meist API-Calls)
- **Lösung:** Prüfe API-Endpoint, CORS-Settings
### Stalled Video
- **Ursache:** Buffering-Probleme
- **Lösung:** Video-Bitrate reduzieren, Netzwerk prüfen
## 💡 Best Practices
1. **Regelmäßig Logs checken** - Mindestens 1x pro Woche
2. **Statistiken beachten** - Viele Errors/Warnings? → Aktion nötig
3. **Heartbeat überwachen** - Kommt alle 5 Min? Display läuft
4. **JSON-Logs für Analyse** - Nutze `json_*.log` für automatische Auswertung
5. **Log-Level richtig nutzen**:
- `FATAL` → Sofort reagieren
- `ERROR` → Zeitnah prüfen
- `WARNING` → Im Auge behalten
- `INFO` → Für Debugging
## 🔗 Integration mit Monitoring-Tools
Die JSON-Logs können einfach in Monitoring-Tools integriert werden:
```bash
# Alle Fatal Errors der letzten 24h
grep '"level":"FATAL"' public/_cabinet/logs/json_*.log
# Errors nach IP gruppieren
jq -r '.ip' public/_cabinet/logs/json_*.log | sort | uniq -c
# Häufigste Fehler
jq -r '.message' public/_cabinet/logs/json_*.log | sort | uniq -c | sort -rn
```
## 📞 Support
Bei Fragen oder Problemen:
1. Prüfe die Logs in `view-logs.php`
2. Schaue dir die Kontext-Informationen an
3. Prüfe ob andere Displays gleichen Fehler haben
4. Kontaktiere den Support mit Log-Auszug
---
**Version:** 1.2
**Letztes Update:** 2026-01-19

View file

@ -0,0 +1,292 @@
# Cabinet Digital Signage - Quick Start Guide
## 🚀 Schnellstart nach Video-Optimierung
### Was wurde geändert?
Die Display-Software wurde **massiv optimiert** um das Problem mit schwarzen Bildschirmen nach längerer Laufzeit zu beheben.
### ✅ Version 1.3 Features:
1. **Video-Cleanup** - Speicher wird nach jedem Video freigegeben
2. **Watchdog** - Überwacht ob Videos laufen und recovered automatisch
3. **Start-Timeout** - Videos die nicht starten werden übersprungen
4. **Error Recovery** - Automatischer Skip bei defekten Videos
5. **Memory-Monitoring** - Speicherüberwachung alle 10 Minuten
6. **Präventiver Reload** - Automatischer Neustart alle 6 Stunden
7. **Performance-Boost** - CSS Hardware-Beschleunigung aktiviert
---
## 📱 Display Setup
### 1. Display aufrufen
```
https://cabinet.b2in.eu
```
### 2. Vollbild aktivieren
- Klicke auf **"V 1.3"** Button oben links
- Display wechselt in Vollbildmodus
- Button verschwindet automatisch
⚠️ **WICHTIG:** Bei einem Page-Reload (nach 6h oder bei Fehlern) wird der Vollbildmodus beendet. Du siehst dann einen **orange pulsierenden Button** mit "⚠️ Vollbild aktivieren!". Einfach erneut klicken.
💡 **Besser:** Nutze **Fully Kiosk Browser** für permanenten Vollbild ohne manuelles Klicken. Siehe `KIOSK_MODE_SETUP.md`
### 3. Laufen lassen
- Display läuft jetzt automatisch 24/7
- Automatischer Reload alle 6 Stunden
- Watchdog überwacht Video-Playback
- Bei Problemen: Automatische Self-Recovery
---
## 📊 Monitoring
### Log-Viewer öffnen:
```
https://cabinet.b2in.eu/view-logs.php
```
### Was solltest du sehen:
#### ✅ Normale Logs (alles OK):
```
[INFO] Video started: video1.mp4
[INFO] Video cleanup durchgeführt
[INFO] Heartbeat - Display is running
[INFO] Memory Status: 45% (230MB / 512MB)
[INFO] Playlist-Loop abgeschlossen
```
#### ⚠️ Warnungen (beobachten):
```
[WARNING] Video stalled (buffering)
[WARNING] Hohe Speicherauslastung (85%)
[WARNING] Überspringe zum nächsten Video
```
#### ❌ Fehler (Action nötig):
```
[ERROR] Video start timeout
[ERROR] Video Error: MEDIA_ERR_NETWORK
[ERROR] Kritischer Zustand erkannt
[FATAL] JavaScript Error
```
---
## 🎬 Video-Upload Checklist
Beim Hochladen neuer Videos beachten:
### ✅ Must-Have:
- [ ] Format: **MP4** (H.264 + AAC)
- [ ] Auflösung: **Max 1920x1080**
- [ ] Bitrate: **5-10 Mbps**
- [ ] Dateigröße: **Max 100 MB**
- [ ] Länge: **15-60 Sekunden** (optimal)
### ⚠️ Vermeiden:
- ❌ Zu große Dateien (>100MB)
- ❌ Zu hohe Bitrate (>10 Mbps)
- ❌ Zu lange Videos (>3 Min)
- ❌ Exotische Formate (MOV, AVI, WMV)
- ❌ 4K Videos (overkill für Display)
### 🔧 Video optimieren (FFmpeg):
```bash
ffmpeg -i input.mp4 \
-c:v libx264 -preset slow -crf 23 \
-c:a aac -b:a 128k \
-vf scale=1920:1080 \
-movflags +faststart \
output.mp4
```
---
## 🐛 Troubleshooting
### Problem: Schwarzer Bildschirm
#### Schritt 1: Logs checken
```
→ Öffne view-logs.php
→ Schaue nach ERROR oder FATAL Logs
```
#### Schritt 2: Was sagt das Log?
**"Video start timeout"**
- Video lädt nicht → Check Video-URL
- Video zu groß → Komprimieren
- Netzwerkproblem → Check Internet
**"Video Error: MEDIA_ERR_NETWORK"**
- Netzwerk-Issue → Check Router/Internet
- Server down → Check b2in.eu erreichbar
**"Video definitiv stuck"**
- ✅ Watchdog hat recovered!
- Video wurde übersprungen
- Nächstes Video sollte laufen
**"Hohe Speicherauslastung"**
- Videos zu groß → Komprimieren
- Zu viele Videos → Playlist verkleinern
- Warte auf automatischen Reload (alle 6h)
#### Schritt 3: Manuelle Actions
**Display neu laden:**
```javascript
// In Browser-Console (F12):
location.reload();
```
**Display komplett neustarten:**
```
1. Browser schließen
2. Warten 10 Sekunden
3. Browser neu öffnen
4. URL aufrufen
5. Vollbild aktivieren
```
### Problem: Video buffert ständig
#### Ursachen:
- Internetverbindung zu langsam
- Video-Bitrate zu hoch
- Netzwerk überlastet
#### Lösung:
1. **Check Internet:** Speedtest machen
2. **Videos optimieren:** Bitrate reduzieren (5 Mbps)
3. **Playlist reduzieren:** Weniger Videos = weniger Daten
4. **Router prüfen:** Neustart? Kabel OK?
### Problem: Footer läuft, Video nicht
#### Das war das Haupt-Problem! Jetzt gefixt durch:
- ✅ Video-Cleanup nach jedem Video
- ✅ Watchdog erkennt stuck Videos
- ✅ Automatischer Skip/Recovery
- ✅ Memory-Management
- ✅ Präventiver Reload alle 6h
#### Falls es DOCH noch passiert:
```
1. Logs checken: view-logs.php
2. Memory-Status prüfen
3. Watchdog-Logs suchen
4. Falls >3 Fehler: Display reload automatisch
5. Falls nicht: Manuell reloaden
```
---
## 📞 Support
### Bei Problemen:
1. **Logs sichern:**
- Öffne view-logs.php
- Download/Screenshot der Fehler
- Besonders ERROR und FATAL Logs
2. **Info sammeln:**
- Welches Display (IP/Standort)?
- Wann trat Problem auf?
- Was zeigen die Logs?
- Memory-Status?
3. **Kontakt:**
- Mit Log-Auszug
- Screenshots
- Display-Info
---
## 🎯 Monitoring-Routine
### Täglich (optional):
- [ ] Kurzer Blick auf Display (läuft es?)
- [ ] Bei Problemen: Logs checken
### Wöchentlich:
- [ ] Logs checken (view-logs.php)
- [ ] Statistiken ansehen (Fatal/Error/Warning)
- [ ] Memory-Status prüfen
- [ ] Watchdog-Interventionen zählen
### Monatlich:
- [ ] Alte Logs aufräumen (>30 Tage automatisch)
- [ ] Video-Performance überprüfen
- [ ] Playlist aktualisieren
- [ ] Display-Uptime checken
---
## 💡 Tipps & Tricks
### Performance optimieren:
- Halte Playlist klein (5-10 Videos)
- Optimiere Videos vor Upload
- Nutze konsistente Video-Auflösung
- Vermeide sehr lange Videos (>2 Min)
### Zuverlässigkeit erhöhen:
- Lass präventiven Reload aktiv (6h)
- Check Logs wöchentlich
- Halte Internet-Verbindung stabil
- Nutze kabelgebundenes Netzwerk statt WiFi
### Memory sparen:
- Videos komprimieren (H.264, CRF 23)
- Playlist auf 10 Videos limitieren
- Preload='metadata' (bereits aktiv)
- Automatischer Cleanup (bereits aktiv)
---
## ✨ Neue Features nutzen
### Auto-Recovery:
```
Display recovered jetzt automatisch von:
✅ Stuck Videos (Watchdog)
✅ Video-Ladefehlern (Skip)
✅ Start-Timeouts (Skip)
✅ Memory-Problemen (Reload nach 6h)
✅ Kritischen Fehlern (Reload)
```
### Logging:
```
Alle Events werden geloggt:
📊 Video-Start/Ende
📊 Memory-Status
📊 Fehler und Warnungen
📊 Watchdog-Interventionen
📊 Heartbeats (Display läuft)
```
### Monitoring:
```
Live-Überwachung möglich:
🔍 view-logs.php
🔍 Auto-Refresh alle 10s
🔍 Statistiken (Fatal/Error/Warning)
🔍 Farbcodierung nach Schwere
```
---
**Version:** 1.3
**Release:** 2026-01-19
**Status:** ✅ Production Ready
🎉 **Viel Erfolg mit dem optimierten Display!**

View file

@ -0,0 +1,368 @@
# Cabinet Digital Signage - Video-Optimierung & Robustness
## 🔧 Problem
Nach einer gewissen Laufzeit wurden die Videos nicht mehr angezeigt (schwarzer Bildschirm), obwohl der Footer weiterhin funktionierte. Dies deutet auf Memory-Leaks oder einen fehlerhaften Video-Loop hin.
## ✅ Implementierte Lösungen
### 1. **Robuster Video-Cleanup** (Memory-Management)
#### Was wurde gemacht:
- **Explizites Video-Cleanup** vor jedem neuen Video:
```javascript
videoElement.pause();
videoElement.removeAttribute('src');
videoElement.load(); // Triggert Garbage Collection
```
- **100ms Delay** nach Cleanup, bevor neues Video geladen wird
- **Preload auf 'metadata'** gesetzt (statt 'auto') → weniger Memory-Verbrauch
#### Warum das hilft:
- Browser gibt Speicher des alten Videos frei
- Verhindert Memory-Leaks bei langen Laufzeiten
- Reduziert gleichzeitig geladene Video-Daten
### 2. **Video Watchdog** (Überwachung)
#### Was wurde gemacht:
- **Watchdog läuft alle 5 Sekunden**
- Prüft ob Video noch läuft (vergleicht `currentTime`)
- Erkennt wenn Video "stecken bleibt"
- Automatischer Skip zum nächsten Video nach 2x "stuck"
#### Was wird überwacht:
```javascript
- currentTime (bewegt sich das Video?)
- isPaused (ist Video pausiert?)
- hasEnded (ist Video zu Ende?)
- isStuck (currentTime ändert sich nicht)
```
#### Warum das hilft:
- Erkennt frozen Videos automatisch
- Verhindert dass Display hängen bleibt
- Selbstheilende Funktion ohne manuellen Eingriff
### 3. **Start-Timeout** (10 Sekunden)
#### Was wurde gemacht:
- Timeout von 10 Sekunden für Video-Start
- Falls Video nicht innerhalb von 10s startet → Skip zum nächsten
- Timeout wird geclearet wenn Video erfolgreich startet
#### Warum das hilft:
- Verhindert endloses Warten bei defekten Videos
- Display bleibt nicht schwarz wenn Video nicht lädt
- Automatische Recovery
### 4. **Error Recovery** (Fehlerbehandlung)
#### Was wurde gemacht:
- **Automatischer Skip** bei Video-Fehlern
- **Consecutive Error Tracking** (zählt aufeinanderfolgende Fehler)
- **Max 3 aufeinanderfolgende Fehler** → dann Page-Reload nach 30s
- **Error-Logging** mit detaillierten Media Error Codes
#### Error Codes:
- `MEDIA_ERR_ABORTED` (1) - Video-Laden abgebrochen
- `MEDIA_ERR_NETWORK` (2) - Netzwerkfehler
- `MEDIA_ERR_DECODE` (3) - Dekodierungsfehler
- `MEDIA_ERR_SRC_NOT_SUPPORTED` (4) - Format nicht unterstützt
#### Warum das hilft:
- Display recovered automatisch von Fehlern
- Verhindert dass ein defektes Video alles blockiert
- Bei wiederholten Problemen: Komplett-Neustart
### 5. **Memory-Monitoring** (Performance-Überwachung)
#### Was wurde gemacht:
- **Memory-Check alle 10 Minuten**
- Loggt Speicherverbrauch in MB und Prozent
- **Warnung bei >80% Speicherauslastung**
- Loggt Video-Buffer-Status
#### Beispiel-Log:
```json
{
"message": "Memory Status",
"context": {
"usedMB": 245,
"limitMB": 512,
"percentUsed": 48
}
}
```
#### Warum das hilft:
- Frühzeitiges Erkennen von Memory-Problemen
- Daten für Troubleshooting und Optimierung
- Proaktives Monitoring statt reaktives Debugging
### 6. **Präventive Maßnahmen**
#### A) Präventiver Page-Reload (alle 6 Stunden)
```javascript
setTimeout(() => {
location.reload();
}, 6 * 60 * 60 * 1000);
```
- Verhindert Memory-Leaks über sehr lange Laufzeit
- Fresh Start alle 6 Stunden
- Erfolgt automatisch im Hintergrund
#### B) Critical Error Check (alle 30 Sekunden)
- Überwacht kritische Zustände
- Bei 3 kritischen Fehlern → Reload nach 5s
- Selbstheilende Funktion
#### C) CSS Performance-Optimierungen
```css
#video-player {
will-change: transform;
transform: translateZ(0);
backface-visibility: hidden;
}
```
- Aktiviert Hardware-Beschleunigung
- Reduziert Rendering-Last
- Optimiert für Video-Playback
### 7. **Erweiterte Video-Events**
#### Neue Events die geloggt werden:
- `ended` - Video zu Ende
- `error` - Video-Fehler (mit Error Code)
- `stalled` - Buffering (Video hängt)
- `waiting` - Waiting for data
- `playing` - Video spielt ab
- `canplay` - Video kann abgespielt werden
#### Warum das hilft:
- Vollständige Transparenz über Video-Status
- Erkennen von Buffering-Problemen
- Debuggen von Playback-Issues
## 📊 Monitoring & Debugging
### Was wird jetzt geloggt:
#### Erfolgreiche Events:
```
✅ Video cleanup durchgeführt
✅ Video started: video1.mp4
✅ Video läuft wieder normal
✅ Playlist-Loop abgeschlossen, starte von vorne
```
#### Warnungen:
```
⚠️ Video scheint stecken geblieben zu sein
⚠️ Hohe Speicherauslastung (85%)
⚠️ Video stalled (buffering)
⚠️ Überspringe zum nächsten Video
```
#### Fehler:
```
❌ Video start timeout (10s überschritten)
❌ Video definitiv stuck - starte nächstes
❌ Video Error: MEDIA_ERR_NETWORK
❌ Kritischer Zustand erkannt
```
### Log-Analyse:
#### Beispiel 1: Memory-Problem
```
[INFO] Memory Status: 420MB / 512MB (82%)
[WARNING] Hohe Speicherauslastung
→ Action: Beobachten, evtl. Videos optimieren
```
#### Beispiel 2: Stuck Video
```
[WARNING] Video scheint stecken geblieben (2x)
[ERROR] Video definitiv stuck - starte nächstes
[WARNING] Überspringe zum nächsten Video: watchdog_stuck
→ Action: Watchdog hat Recovery durchgeführt ✓
```
#### Beispiel 3: Netzwerkprobleme
```
[ERROR] Video Error: MEDIA_ERR_NETWORK
[WARNING] Überspringe zum nächsten Video: error_MEDIA_ERR_NETWORK
[INFO] Video started: video2.mp4
→ Action: Netzwerk kurz unterbrochen, automatisch recovered ✓
```
## 🎯 Best Practices für Videos
### 1. **Video-Format**
- **Container:** MP4 (H.264 + AAC)
- **Codec:** H.264 (High Profile, Level 4.0)
- **Audio:** AAC, 128-256 kbps
- **Auflösung:** Max 1920x1080 (Full HD)
- **Framerate:** 25 oder 30 fps
- **Bitrate:** 5-10 Mbps (nicht höher!)
### 2. **Video-Länge**
- **Optimal:** 15-60 Sekunden
- **Maximum:** 2-3 Minuten
- **Warum:** Kürzere Videos = weniger Memory-Verbrauch
### 3. **Dateigrößen**
- **Optimal:** 10-50 MB pro Video
- **Maximum:** 100 MB pro Video
- **Warum:** Schnelleres Laden, weniger Buffering
### 4. **Playlist-Größe**
- **Optimal:** 5-10 Videos
- **Maximum:** 20 Videos
- **Warum:** Übersichtlich, nicht zu viel Content im Loop
### 5. **Video-Optimierung**
Nutze Tools wie:
- **FFmpeg** für Re-Encoding
- **HandBrake** für Kompression
- **Adobe Media Encoder** für Profis
#### FFmpeg Beispiel:
```bash
ffmpeg -i input.mp4 \
-c:v libx264 -preset slow -crf 23 \
-c:a aac -b:a 128k \
-vf scale=1920:1080 \
-movflags +faststart \
output.mp4
```
## 🔍 Troubleshooting
### Problem: Videos werden nach einiger Zeit schwarz
#### Mögliche Ursachen:
1. **Memory-Leak** → Check Memory-Logs
2. **Video zu groß** → Komprimieren
3. **Netzwerkprobleme** → Check Network-Logs
4. **Browser-Cache voll** → Wird jetzt automatisch gecleart
#### Lösung:
- ✅ Implementiert: Automatischer Cleanup
- ✅ Implementiert: Watchdog erkennt Problem
- ✅ Implementiert: Automatischer Skip/Recovery
- ✅ Implementiert: Präventiver Reload nach 6h
### Problem: Video startet nicht
#### Check in Logs:
```
[ERROR] Video start timeout
[ERROR] Video play failed: MEDIA_ERR_SRC_NOT_SUPPORTED
```
#### Lösung:
1. **Video-Format prüfen** (MP4 H.264?)
2. **Video-Pfad prüfen** (erreichbar?)
3. **Video neu encodieren**
4. **Watchdog springt automatisch zum nächsten**
### Problem: Video buffert ständig
#### Check in Logs:
```
[WARNING] Video stalled (buffering)
[WARNING] Video waiting (buffering)
```
#### Lösung:
1. **Netzwerkverbindung prüfen**
2. **Video-Bitrate reduzieren**
3. **Preload auf 'metadata'** (bereits implementiert)
4. **Video komprimieren**
### Problem: Hohe Speicherauslastung
#### Check in Logs:
```
[WARNING] Hohe Speicherauslastung (85%)
```
#### Lösung:
1. **Videos komprimieren**
2. **Playlist verkleinern**
3. **Präventiver Reload** (bereits aktiv nach 6h)
4. **Browser-Cache leeren** (manuell)
## 📈 Performance-Metriken
### Empfohlene Werte:
- **Memory Usage:** < 70% des Heap-Limits
- **Video Start Time:** < 2 Sekunden
- **Buffering Events:** < 1 pro Stunde
- **Consecutive Errors:** 0
- **Watchdog Interventions:** < 1 pro Tag
### Critical Werte (Action required):
- **Memory Usage:** > 85%
- **Video Start Time:** > 10 Sekunden (Timeout!)
- **Buffering Events:** > 5 pro Stunde
- **Consecutive Errors:** ≥ 3
- **Watchdog Interventions:** > 10 pro Tag
## 🚀 Testing
### 1. **Test im Browser**
```
1. Öffne https://cabinet.b2in.eu
2. Öffne Developer Tools (F12)
3. Console Tab öffnen
4. Logs beobachten:
- "Video cleanup durchgeführt"
- "Video started: ..."
- Memory-Status nach 30s
```
### 2. **Stress-Test**
```
1. Lass Display 24h laufen
2. Check Logs auf Probleme
3. Memory-Status nach 24h prüfen
4. Watchdog-Interventionen zählen
```
### 3. **Network-Test**
```
1. Simuliere schlechte Verbindung (DevTools → Network → Throttling)
2. Beobachte Error-Recovery
3. Check ob automatischer Skip funktioniert
```
### 4. **Memory-Test**
```
1. Lass Display 6h laufen
2. Check Memory-Logs alle 10 Min
3. Sollte < 70% bleiben
4. Nach 6h: automatischer Reload
```
## 📋 Changelog
### Version 1.3 (2026-01-19)
- ✅ Video-Cleanup vor jedem neuen Video
- ✅ Video Watchdog (5s Interval)
- ✅ Start-Timeout (10s)
- ✅ Error Recovery mit Consecutive Error Tracking
- ✅ Memory-Monitoring (alle 10 Min)
- ✅ Präventiver Reload (alle 6h)
- ✅ Critical Error Check (alle 30s)
- ✅ CSS Performance-Optimierungen
- ✅ Erweiterte Video-Events (playing, canplay, waiting)
- ✅ Detaillierte Error-Codes mit Logging
---
**Status:** ✅ Ready for Production
**Tested on:** Chrome 120+ / Android 11+
**Last Update:** 2026-01-19

View file

@ -0,0 +1,175 @@
# B2in Display Frontend-Implementation
**Ziel:** Frontend-Webapp für das B2in Schaufenster-Display (9:16 Portrait, 4355 Zoll).
**Pfad:** `public/_cabinet/b2in/index.html`
**Konzept-Basis:** `public/_cabinet/_docs/b2in-displays.md`
---
## Ist-Zustand
Es gibt bereits drei Display-Typen unter `public/_cabinet/`:
| Typ | Pfad | Status | Beschreibung |
|-----|------|--------|-------------|
| **Video-Display** | `index.html` | Live, stabil | CABINET-Branding. Videos + Footer-Rotation. API: `/api/display/config` |
| **Offers** | `offers/index.html` | Entwickelt, nicht live | CABINET-Branding. iFrame-basierte Slide-Rotation mit `config.json` |
| **Info-Tablet** | `info/index.html` | Entwickelt | CABINET-Branding. Store-Status/Öffnungszeiten. API: `/api/cabinet-tablet/status` |
| **B2in Display** | `b2in/` | **Neu wird jetzt gebaut** | B2in-Branding. Playlist mit Videos/Bildern + Text. API: `/api/b2in-display/playlist` |
---
## Architektur-Übersicht
```
public/_cabinet/b2in/
├── index.html ← Haupt-Webapp (Playlist-Engine + UI)
├── b2in-styles.css ← Display-spezifische Styles
└── (assets/) ← Medien werden per API/URL referenziert, nicht lokal
```
Shared CSS aus `public/_cabinet/shared/cabinet-base.css` wird **nicht** importiert das B2in-Display hat ein eigenständiges Branding (dunkel, B2in statt CABINET). Es ist ein komplett eigenes Design-System.
---
## Schritte
### Schritt 1: HTML-Grundgerüst + CSS
**Datei:** `public/_cabinet/b2in/index.html` + `b2in-styles.css`
Layout gemäß Konzept (Abschnitt 2.1):
```
┌─────────────────────────┐
│ HEADER │ ← B2in-Logo (links) + Claim (rechts)
│ B2in · Claim │ Fest, immer sichtbar
├─────────────────────────┤
│ │
│ ┌─────────────────┐ │ ← Gradient oben (dunkel → transparent)
│ │ VIDEO / BILD │ │ 16:9 Content-Bereich
│ │ (16:9 Media) │ │ object-fit: cover
│ └─────────────────┘ │ ← Gradient unten (transparent → dunkel)
│ │
├─────────────────────────┤
│ TEXTFELD │ ← Headline (max 40 Zeichen)
│ Headline + Subline │ Subline (max 80 Zeichen)
├─────────────────────────┤ Wechselt synchron mit Media
│ FOOTER │ ← "Marcel Scheibe" + "b2in.de" + QR-Code
│ Name · URL · QR │ Fest, immer sichtbar
└─────────────────────────┘
```
**Design-Entscheidungen:**
- Dunkler Hintergrund (#0a0a0a) B2in-Branding, nicht CABINET-weiß
- 9:16 Aspect Ratio Container (wie bestehende Displays)
- Gradient-Overlays oben/unten über dem Media-Bereich
- IBM Plex Sans Font (wie alle Cabinet-Displays)
- Akzentfarbe: B2in-Blau (wird aus Branding-Guide übernommen)
- Kein CABINET-Logo, kein Accent #009FE3
### Schritt 2: Playlist-Engine (JavaScript)
**Klasse:** `B2inDisplayApp`
Kernfunktionalität:
1. **API laden**`GET /api/b2in-display/playlist` (wird später gebaut, erstmal Mock/Fallback)
2. **Playlist sortieren** → nach `sort_order`, nur `is_active === true`
3. **Gewichtung anwenden** → 70/30 Immobilien/Möbel-Verteilung berechnen
4. **Rotation starten** → Item für Item durchspielen
Item-Wechsel-Logik:
- **Video:** Abspielen bis Ende → nächstes Item
- **Bild:** `duration_seconds` abwarten → nächstes Item
- **Am Ende der Playlist:** Von vorne beginnen
### Schritt 3: Video-Handling
Bewährtes Pattern aus dem bestehenden `index.html` übernehmen:
- `autoplay muted playsinline` für Browser-Autoplay-Policy
- Memory-Management: `src` leeren + `load()` nach jedem Video
- Preloading: Nächstes Item im Hintergrund vorladen
- Start-Timeout (10s) → bei Timeout überspringen
- Watchdog: Prüft alle 5s ob Video noch läuft
- Error-Handling: Bei Fehler → Item überspringen, nie schwarzer Screen
### Schritt 4: Bild-Handling
- `<img>` Element mit `object-fit: cover`
- Duration aus Item oder globaler `default_image_duration`
- Ken-Burns-Effekt (optionaler langsamer Zoom per CSS)
- Preload via `new Image()` im Hintergrund
### Schritt 5: Transitions
Drei Typen (per CMS-Setting steuerbar):
| Typ | Umsetzung |
|-----|-----------|
| `fade` | Opacity 1→0, dann 0→1 |
| `crossfade` | Neues Element über dem alten einblenden (empfohlen, Standard) |
| `slide` | CSS transform translateX |
Text-Synchronisation:
1. Text fade-out (400ms)
2. Neuen Text setzen
3. Text fade-in (400ms)
4. Startet 200ms vor Media-Wechsel
### Schritt 6: Polling + Stabilität
Gleicher Ansatz wie Info-Tablet, aber mit 60s Intervall:
- **Lightweight Check** alle 60s → `GET /api/b2in-display/check`
- **Full Fetch** nur bei Timestamp-Änderung
- Laufendes Video/Bild wird zu Ende gespielt, dann neue Playlist
- localStorage-Cache als Offline-Fallback
- Auto-Reload alle 6 Stunden
- Connection-Recovery: 3 Fehler → 5 Min Pause → Retry
- 30 Min offline → Page-Reload
### Schritt 7: Standby-Modus
Wenn `display_active === false`:
- Nur B2in-Logo auf dunklem Hintergrund
- Kein Content, kein Textfeld, kein Footer-Text
- Polling läuft weiter (wartet auf Aktivierung)
### Schritt 8: Error-Overlay
Bei kritischen Fehlern (keine Playlist, kein Media):
- Dezentes B2in-Logo auf dunklem Hintergrund
- Niemals Browser-Fehler oder weißer Screen
- Automatischer Retry im Hintergrund
---
## API-Abhängigkeit
Das Frontend wird **zunächst mit Mock-Daten** gebaut. Die API (`/api/b2in-display/playlist` + `/check`) wird als separater Schritt im Backend implementiert. Das Frontend erkennt automatisch, ob die API verfügbar ist, und fällt auf eingebettete Demo-Daten zurück.
**Mock-Playlist für Entwicklung:**
```javascript
const MOCK_PLAYLIST = {
settings: {
display_active: true,
footer_name: "Marcel Scheibe",
footer_url: "b2in.de",
transition: { type: "crossfade", duration_ms: 800 },
default_image_duration: 10
},
items: [
// Demo-Items mit Platzhalter-Medien
],
updated_at: new Date().toISOString()
};
```
---
## Abgrenzung (was NICHT in diesem Schritt passiert)
- Kein CMS-Backend (Model, Migration, Controller, Admin-UI) → separater Schritt
- Kein Media-Upload → Videos/Bilder werden per URL referenziert
- Keine Gewichtungs-Logik im Backend → wird im Frontend berechnet
- Keine Änderung am bestehenden Video-Display (`index.html`) → bleibt unberührt

View file

@ -0,0 +1,386 @@
# B2in Schaufenster-Display Entwicklungskonzept
**Status:** Entwicklungsvorlage | Februar 2026
---
## 1. Projektübersicht
### 1.1 Zweck
Ein großformatiges Display im Schaufenster des CABINET Stores in Bielefeld zeigt B2in-Content im Hochformat (9:16). Ziel: Laufkundschaft in 35 Sekunden eine klare Botschaft vermitteln B2in als Marke für internationale Immobilien und exklusive Einrichtungskonzepte. Das Display läuft parallel zum CABINET-Schaufenster und ist eigenständig gebrandet.
### 1.2 Technischer Ansatz
Die Anzeige wird als Webapp unter einer Subdomain ( Subdomain (`cabinet.b2in.eu/info <- live / [`https://b2in.test/_cabinet](https://b2in.test/_cabinet)/info ← testserver) bereitgestellt. Ein Smart TV mit android greift im Vollbildmodus über den Browser darauf zu. Inhalte (Videos, Bilder, Texte) werden über das B2in-Backend (CMS) gepflegt und rotieren automatisch.
### 1.3 Branding
Ausschließlich B2in-Branding. Kein CABINET-Logo, kein Azizi, keine Partnerlogos. Der Frame zeigt: B2in-Logo, Claim "Connecting Design & Property", Marcel Scheibe als Person, b2in.de und QR-Code.
---
## 2. Display-Architektur
### 2.1 Formatierung
Das Display steht im **9:16 Hochformat (Portrait)**. Das Videomaterial liegt im **16:9 Querformat** vor. Daraus ergibt sich ein natürlicher Split:
`┌─────────────────────────┐
│ HEADER │ ← Fester Bereich: Logo + Claim
│ B2in · Claim │
├─────────────────────────┤
│ │
│ │ ← Oberer Leerraum (Gradient zum Video)
│ ┌───────────────────┐ │
│ │ │ │
│ │ VIDEO / BILD │ │ ← 16:9 Content-Bereich (rotierend)
│ │ (16:9) │ │
│ │ │ │
│ └───────────────────┘ │
│ │ ← Unterer Leerraum (Gradient zum Text)
│ │
├─────────────────────────┤
│ TEXTFELD │ ← Rotierender Text (Headline + Subline)
│ Headline + Subline │
├─────────────────────────┤
│ FOOTER │ ← Fester Bereich: Name + URL + QR
│ Marcel Scheibe · QR │
└─────────────────────────┘`
### 2.2 Bereiche im Detail
| # | Bereich | Inhalt | Verhalten |
| --- | --- | --- | --- |
| 1 | **Header** | B2in-Logo (links) + Claim "Connecting Design & Property" (rechts). | Fest. Steht permanent, unabhängig vom Content. |
| 2 | **Content-Bereich** | 16:9 Video (MP4) oder Einzelbild (JPG/PNG). Nimmt ca. 5060% der Screenhöhe ein. Oben und unten Gradient-Übergang zum dunklen Hintergrund. | Rotiert automatisch. Reihenfolge und Dauer per CMS steuerbar. |
| 3 | **Textfeld** | Headline (max. 40 Zeichen) + Subline (max. 80 Zeichen). Passt sich dem aktuellen Content an. | Wechselt synchron mit dem Video/Bild. Sanfte Fade-Transition. |
| 4 | **Footer** | "Marcel Scheibe" (Name) + "b2in.de" (URL) + QR-Code (rechts). | Fest. Steht permanent. |
---
## 3. Content-Rotation (Playlist-System)
### 3.1 Konzept
Das Display zeigt eine Playlist von Content-Items, die automatisch durchrotieren. Jedes Item besteht aus einem Medienelement (Video oder Bild) plus passendem Text.
### 3.2 Gewichtung
Die Rotation folgt einer definierten Gewichtung:
| Kategorie | Anteil | Beispiel-Content |
| --- | --- | --- |
| **Immobilien** | ~70% | Dubai-Projekte, internationale Lifestyle-Aufnahmen, Architektur |
| **Möbel / Einrichtung** | ~30% | Lokale Händler-Highlights, Einrichtungskonzepte, Interior-Design |
Die Gewichtung wird über ein `category`-Feld pro Item gesteuert. Das Frontend berechnet die Rotation so, dass die Verteilung über einen Durchlauf eingehalten wird (z. B. bei 10 Items: 7× Immobilien, 3× Möbel).
### 3.3 Content-Item Struktur
Jedes Playlist-Item hat folgende Eigenschaften:
| Feld | Typ | Beschreibung |
| --- | --- | --- |
| `id` | Auto | Eindeutige ID |
| `title` | Text (intern) | Interner Name zur Identifikation im CMS (wird nicht angezeigt) |
| `category` | Dropdown | `immobilien` | `moebel` bestimmt die Gewichtung in der Rotation |
| `media_type` | Dropdown | `video` | `image` |
| `media_file` | Upload (MP4/JPG/PNG) | Mediendatei. Videos: MP4, H.264, max. 1080p. Bilder: JPG/PNG, min. 1920x1080. |
| `media_url` | URL (optional) | Alternative zu Upload: Externe Video-/Bild-URL. Wird bevorzugt wenn vorhanden. |
| `headline` | Text | Max. 40 Zeichen. Wird im Textfeld angezeigt. |
| `subline` | Text | Max. 80 Zeichen. Erklärender Text unter der Headline. |
| `duration_seconds` | Zahl | Anzeigedauer in Sekunden. Für Bilder: empfohlen 812 Sek. Für Videos: wird ignoriert, Dauer ergibt sich aus Videolänge. |
| `sort_order` | Zahl | Reihenfolge in der Playlist. Niedrigste Zahl zuerst. |
| `is_active` | Toggle | Aktiv/Inaktiv. Inaktive Items werden übersprungen. |
### 3.4 Beispiel-Playlist
| # | Titel (intern) | Kategorie | Typ | Headline | Subline | Dauer |
| --- | --- | --- | --- | --- | --- | --- |
| 1 | Dubai Skyline Video | immobilien | video | Internationale Immobilien — Ihr Einstieg. | Beratung, Begleitung und Vermittlung. Persönlich. Transparent. | (Video) |
| 2 | Dubai Villa Rendering | immobilien | image | Ihr Zuhause. Weltweit. | Von Dubai bis Europa wir finden Ihre Immobilie. | 10s |
| 3 | Möbel Showroom | moebel | image | Exklusive Einrichtung — Lokal. Für Sie. | Kuratierte Möbelkonzepte von lokalen Fachhändlern. | 10s |
| 4 | Dubai Pool Lifestyle | immobilien | video | Dubai. Lissabon. Und morgen? | Internationale Immobilien als Kapitalanlage. | (Video) |
| 5 | Dubai Apartment Tour | immobilien | video | Neubau ab 3.000 €/m² | Wertsteigerung. Steuervorteile. Persönliche Begleitung. | (Video) |
| 6 | Interior Design Mood | moebel | image | Lokale Händler. Echte Stücke. | Ausstellungsstücke und Designmöbel aus Ihrer Region. | 10s |
| 7 | Dubai Cityscape Drone | immobilien | video | Ihr Immobilien-Dolmetscher. | Marcel Scheibe begleitet Sie durch den gesamten Kaufprozess. | (Video) |
---
## 4. CMS-Felder (B2in-Backend)
Im B2in-Backend wird ein eigener Bereich „B2in Display" angelegt mit zwei Ebenen: globale Einstellungen und die Playlist-Items.
### 4.1 Globale Einstellungen
| Feldname | Typ | Beschreibung |
| --- | --- | --- |
| `display_active` | Toggle | Master-Schalter. Wenn deaktiviert, zeigt das Display einen Standby-Screen (nur Logo). |
| `footer_name` | Text | Name im Footer. Standard: "Marcel Scheibe" |
| `footer_url` | Text | URL im Footer. Standard: "b2in.de" |
| `qr_code_image` | Upload (PNG/SVG) | QR-Code Asset für den Footer. |
| `rotation_weight_immobilien` | Zahl (%) | Soll-Anteil Immobilien-Content. Standard: 70. |
| `rotation_weight_moebel` | Zahl (%) | Soll-Anteil Möbel-Content. Standard: 30. |
| `default_image_duration` | Zahl (Sek.) | Standard-Anzeigedauer für Bilder, wenn beim Item nichts eingetragen. Standard: 10. |
| `transition_type` | Dropdown | `fade` | `slide` | `crossfade` Übergangseffekt zwischen Items. Standard: crossfade. |
| `transition_duration_ms` | Zahl | Dauer der Transition in Millisekunden. Standard: 800. |
### 4.2 Playlist-Items
Wiederholbare Einträge (Repeater-Feld oder eigene Collection), Felder wie in Abschnitt 3.3 definiert.
### 4.3 Zeichenlimit-Hinweise für Marcel
Im CMS sollten bei den Textfeldern visuelle Hinweise erscheinen:
- **Headline:** Zähler „12/40 Zeichen" wird rot ab 35
- **Subline:** Zähler „45/80 Zeichen" wird rot ab 70
- **Begründung:** Auf dem Display müssen die Texte in 3 Sekunden lesbar sein. Längere Texte werden nicht gelesen.
---
## 5. API-Endpoint
### 5.1 Endpoint-Struktur
`GET /api/b2in-display/playlist`
**Response (JSON):**
json
`{
"settings": {
"display_active": true,
"footer_name": "Marcel Scheibe",
"footer_url": "b2in.de",
"qr_code_url": "/assets/qr-b2in.svg",
"rotation_weights": {
"immobilien": 70,
"moebel": 30
},
"default_image_duration": 10,
"transition": {
"type": "crossfade",
"duration_ms": 800
}
},
"items": [
{
"id": 1,
"category": "immobilien",
"media_type": "video",
"media_url": "/media/display/dubai-skyline.mp4",
"headline": "Internationale Immobilien — Ihr Einstieg.",
"subline": "Beratung, Begleitung und Vermittlung. Persönlich. Transparent.",
"duration_seconds": null,
"sort_order": 1,
"is_active": true
},
{
"id": 2,
"category": "moebel",
"media_type": "image",
"media_url": "/media/display/showroom-lokal.jpg",
"headline": "Exklusive Einrichtung — Lokal. Für Sie.",
"subline": "Kuratierte Möbelkonzepte von lokalen Fachhändlern.",
"duration_seconds": 10,
"sort_order": 3,
"is_active": true
}
],
"updated_at": "2026-02-26T14:30:00Z"
}`
### 5.2 Update-Mechanismus (Polling)
Gleicher Ansatz wie beim CABINET Info-Tablet:
**1. Lightweight Check (alle 60 Sekunden):**
`GET /api/b2in-display/check`
json
`{ "updated_at": "2026-02-26T14:30:00Z" }`
**2. Full Fetch (nur bei Änderung):**
Wenn Timestamp abweicht → komplette Playlist neu laden. Laufendes Video wird zu Ende gespielt, dann greift die neue Playlist.
**Warum 60 statt 30 Sekunden?** Das Display zeigt eine Playlist, keine Echtzeit-Infos. Wenn Marcel ein neues Video hochlädt, ist eine Verzögerung von maximal 60 Sekunden völlig akzeptabel.
---
## 6. Frontend-Logik
### 6.1 Playlist-Engine
Das Frontend implementiert eine einfache Playlist-Engine:
`Ablauf:
1. Playlist laden (API-Call)
2. Nur aktive Items filtern (is_active === true)
3. Items nach sort_order sortieren
4. Gewichtung anwenden:
- Items nach Kategorie gruppieren
- Rotation so mischen, dass die %-Verteilung eingehalten wird
- Beispiel bei 70/30: I, I, M, I, I, I, M, I, I, M (bei 10 Items)
5. Erstes Item anzeigen
6. Bei Video: Warten bis Video endet → nächstes Item
7. Bei Bild: duration_seconds abwarten → nächstes Item
8. Am Ende der Playlist: Von vorne beginnen
9. Zwischen Items: Transition (crossfade/fade/slide)`
### 6.2 Video-Handling
| Aspekt | Spezifikation |
| --- | --- |
| **Format** | MP4, H.264 Codec, max. 1080p (1920x1080) |
| **Autoplay** | Videos starten automatisch, gemutet (Browser-Policy). Ton ist nicht relevant für Schaufenster-Display. |
| **Ladezeit** | Videos werden im Hintergrund vorgeladen (nächstes Item in der Playlist wird gepreloaded während das aktuelle läuft). |
| **Fehlerfälle** | Wenn ein Video nicht lädt (404, Netzwerkfehler): Item überspringen, nächstes anzeigen. Fehler loggen. |
| **Loop** | Kein Loop pro Video. Jedes Video spielt einmal, dann kommt das nächste Playlist-Item. |
### 6.3 Bild-Handling
| Aspekt | Spezifikation |
| --- | --- |
| **Format** | JPG oder PNG, min. 1920x1080 Pixel |
| **Skalierung** | `object-fit: cover` Bild füllt den 16:9-Bereich, wird bei Bedarf beschnitten. |
| **Dauer** | Aus `duration_seconds` des Items oder `default_image_duration` aus den globalen Settings. |
| **Ken-Burns-Effekt (optional)** | Langsamer Zoom-In während der Anzeigedauer. Macht statische Bilder lebendiger. Per CSS-Animation, kein JavaScript nötig. |
### 6.4 Text-Synchronisation
Headline und Subline wechseln synchron mit dem Media-Content:
1. Aktuelles Textfeld ausblenden (fade-out, 400ms)
2. Neuen Text setzen
3. Neues Textfeld einblenden (fade-in, 400ms)
4. Timing: Text-Transition startet 200ms vor dem Media-Wechsel, damit beides gleichzeitig erscheint.
---
## 7. Technische Stabilität & Kiosk-Betrieb
### 7.1 Hardware-Setup
| Komponente | Empfehlung |
| --- | --- |
| **Display** | TV/Monitor im Hochformat (9:16), 4355 Zoll. Full HD ausreichend (1080x1920 in Portrait). LED/LCD mit hoher Helligkeit (min. 350 nits, ideal 500+ nits für Schaufenster mit Sonneneinstrahlung). |
| **Media Player** | Dedizierter Android-Stick oder -Box (z. B. Amazon Fire TV Stick 4K, Xiaomi Mi Box, oder professionell: BrightSign). Alternativ: Smart TV mit Browser. |
| **Kiosk-Software** | Fully Kiosk Browser (wenn Android). Bei BrightSign: eigene Kiosk-Funktionalität. |
| **Halterung** | VESA-Wandhalterung, Portrait-Montage. Ggf. mit Blickwinkelschutz-Folie wenn das Display zu nah an der Scheibe steht. |
| **Stromversorgung** | Dauerstrom. Optional: Zeitschaltuhr oder Smart Plug für automatisches Ein/Aus (z. B. 07:0022:00). |
| **WLAN** | Stabiles Store-WLAN. Bei Verbindungsproblemen: LAN-Adapter als Fallback. |
### 7.2 Webapp-seitige Stabilität
| Maßnahme | Beschreibung |
| --- | --- |
| **Auto-Reload** | Kompletter Page-Reload alle 6 Stunden. Räumt Speicher auf besonders wichtig bei Video-Wiedergabe, die Memory Leaks verursachen kann. |
| **Video-Memory-Management** | Nach jedem Video: `src` des Video-Elements leeren und neu setzen, damit der Browser den Speicher freigibt. Kein Stapeln von Video-Elementen. |
| **Offline-Fallback** | Wenn API nicht erreichbar: Letzte Playlist aus localStorage abspielen. Kein schwarzer Bildschirm. |
| **Connection-Recovery** | Polling-Fehler 3x hintereinander → 5 Min. Pause → erneut versuchen. Nach 30 Min. ohne Verbindung → Page-Reload. |
| **Lokaler Cache** | Playlist-JSON wird in localStorage gespeichert. Mediendateien werden im Browser-Cache gehalten (Cache-Control Headers serverseitig setzen, z. B. `max-age=86400`). |
| **Standby-Modus** | Optionaler CMS-Toggle: Wenn `display_active = false`, zeigt die Webapp nur das B2in-Logo auf dunklem Hintergrund. Kein Content, kein Textfeld. |
| **Error-Overlay** | Bei kritischen Fehlern (keine Playlist, kein Media): Dezentes B2in-Logo auf dunklem Hintergrund. Niemals ein Browser-Fehlerbild oder weißer Screen. |
| **Automatischer Playlist-Neustart** | Wenn die Playlist durchgelaufen ist und das nächste Polling eine Änderung zeigt: Neue Playlist nahtlos übernehmen. |
### 7.3 Performance-Hinweise
- Videos sollten für Web optimiert sein: MP4, H.264, AAC Audio (auch wenn gemutet), `moov atom` am Anfang der Datei (für schnellen Start)
- Empfohlene Bitrate: 58 Mbit/s für 1080p
- Bilder sollten als WebP oder komprimiertes JPG vorliegen (max. 500 KB pro Bild)
- Preloading: Immer das nächste Item vorladen, während das aktuelle abgespielt wird
---
## 8. Frontend-Spezifikation
### 8.1 Technologie
- HTML/CSS/JS (Vanilla). Kein Framework nötig.
- Responsive für gängige Display-Auflösungen im 9:16 Portrait (1080x1920, 720x1280)
### 8.3 Transitions zwischen Items
| Transition-Typ | Beschreibung |
| --- | --- |
| `fade` | Aktuelles Item blendet aus (opacity 1→0), neues blendet ein (opacity 0→1). |
| `crossfade` | Neues Item blendet über dem aktuellen ein. Sanfter, da kein schwarzer Zwischenzustand. **Empfohlen.** |
| `slide` | Aktuelles Item gleitet nach links raus, neues kommt von rechts rein. |
Standard: **crossfade** mit 800ms. Per CMS änderbar.
---
## 9. Leitplanken für Content-Pflege
**Diese Regeln sollten im CMS als Hinweistext sichtbar sein:**
### ✓ Ja, so machen wir es:
- **Ein Item, eine Botschaft.** Jedes Playlist-Item hat eine klare Aussage: Immobilien ODER Möbel.
- **Texte kurz halten.** Headline max. 40 Zeichen, Subline max. 80 Zeichen. Was in 3 Sekunden nicht gelesen werden kann, wird nicht gelesen.
- **Nur B2in-Content.** Keine Partnerlogos im Video/Bild. Kein Azizi-Branding. Kein CABINET.
- **Hochwertige Medien.** Nur professionelles Video-/Bildmaterial. Keine Handy-Fotos, keine Screenshots.
- **Regelmäßig aktualisieren.** Playlist mindestens quartalsweise prüfen. Abgelaufene Projekte rausnehmen.
### ✗ Das vermeiden wir:
- **Zu viele Items.** Max. 810 Items in der Playlist. Mehr führt zu langer Rotation, Wiederholung wird selten.
- **Text im Video.** Wenn das Video bereits Text enthält, Headline und Subline reduzieren oder leer lassen sonst doppelt sich die Information.
- **Preislisten oder Grundrisse.** Das Display weckt Neugier, es informiert nicht. Details gehören auf die Website.
- **Verschiedene Botschaften mischen.** Kein Item, das gleichzeitig Dubai, Möbel und CABINET bewirbt.
---
## 10. Umsetzungs-Checkliste
| # | Aufgabe | Verantwortlich | Status |
| --- | --- | --- | --- |
| 1 | CMS-Bereich "B2in Display" anlegen: Globale Settings + Playlist-Repeater (Abschnitt 4) | Backend-Entwicklung | Offen |
| 2 | API-Endpoint `/api/b2in-display/playlist` + `/check` implementieren (Abschnitt 5) | Backend-Entwicklung | Offen |
| 3 | Media-Upload-Funktion im CMS (MP4 + JPG/PNG) mit Größen-Validierung | Backend-Entwicklung | Offen |
| 4 | Frontend-Webapp mit Playlist-Engine bauen (Abschnitt 6) | Frontend-Entwicklung | Offen |
| 5 | Video-Preloading + Memory-Management implementieren (Abschnitt 6.2 + 7.2) | Frontend-Entwicklung | Offen |
| 6 | Polling-Mechanismus + Offline-Fallback + Auto-Reload (Abschnitt 5.2 + 7.2) | Frontend-Entwicklung | Offen |
| 7 | Subdomain einrichten + SSL + Deployment | DevOps | Offen |
| 8 | Display-Hardware beschaffen + montieren (Abschnitt 7.1) | Marcel / Hardware | Offen |
| 9 | Kiosk-Software einrichten (Fully Kiosk oder BrightSign) | Technik | Offen |
| 10 | QR-Code generieren (Ziel: b2in.de) + als Asset hinterlegen | Design | Offen |
| 11 | Initiales Video-/Bildmaterial aufbereiten (Format, Komprimierung, Qualität) | Content / Design | Offen |
| 12 | Beispiel-Playlist im CMS anlegen + End-to-End-Test | QA | Offen |
| 13 | Installation im Store: Position, Helligkeit, Blickwinkel, WLAN-Stabilität | Marcel + Technik | Offen |
---
## Anhang A: Zusammenfassung der Auto-Logiken
Folgende Dinge passieren automatisch, ohne dass Marcel etwas pflegen muss:
- Playlist rotiert endlos durch alle aktiven Items
- Gewichtung (70/30) wird automatisch aus den Kategorien berechnet
- Videos starten automatisch (gemutet) und gehen nach Ende zum nächsten Item
- Bilder werden nach definierter Dauer gewechselt
- Polling prüft alle 60 Sekunden auf CMS-Änderungen
- Bei Playlist-Änderung: Laufendes Item wird zu Ende gespielt, dann greift neue Playlist
- Auto-Reload alle 6 Stunden für Speicher-Hygiene
- Offline: Letzte Playlist läuft weiter aus dem Cache
- Bei Video-Fehler: Item wird übersprungen, kein schwarzer Screen
- Standby-Modus bei `display_active = false`: Nur Logo auf dunklem Hintergrund
## Anhang B: Unterschiede zum CABINET Info-Tablet
| Aspekt | CABINET Info-Tablet | B2in Display |
| --- | --- | --- |
| **Zweck** | Store-Information | Marken-/Content-Anzeige |
| **Branding** | CABINET | B2in |
| **Content-Typ** | Text/Daten (statisch) | Video/Bild (Playlist) |
| **Interaktion** | Keine | Keine |
| **Update-Frequenz** | Bei Bedarf (12x/Tag) | Playlist rotiert dauerhaft |
| **Polling-Intervall** | 30 Sekunden | 60 Sekunden |
| **Hardware** | Android-Tablet 810" | TV/Monitor 4355" + Media Player |
| **CMS-Komplexität** | 12 einfache Felder | Globale Settings + Playlist-Repeater |

View file

@ -0,0 +1,200 @@
# CABINET Info-Tablet Entwicklungskonzept
**Status:** Entwicklungsvorlage | Februar 2026
---
## 1. Projektübersicht
### 1.1 Zweck
Ein kleines Tablet im Schaufenster neben dem Eingang des CABINET Stores in Bielefeld zeigt Passanten und Kunden auf einen Blick die wichtigsten Store-Informationen: aktueller Öffnungsstatus, Öffnungszeiten, Sonderhinweise und den nächsten freien Beratungstermin.
### 1.2 Technischer Ansatz
Die Anzeige wird als responsive Webapp unter einer Subdomain (`cabinet.b2in.eu/info <- live / [`https://b2in.test/_cabinet](https://b2in.test/_cabinet)/info ← testserver) bereitgestellt. Das Tablet greift im Vollbild-Kioskmodus über den Browser darauf zu. Die Inhalte werden über das bestehende B2in-Backend (CMS) gepflegt.
Ordner /info
### 1.3 Branding
Ausschließlich CABINET-Branding. Kein B2in-Logo, kein Hinweis auf andere Marken. Das Tablet ist ein reines Store-Informationstool.
---
## 2. Content-Struktur & Layout
Das Display ist in fünf feste Bereiche unterteilt, von oben nach unten:
| # | Bereich | Inhalt | Verhalten |
| --- | --- | --- | --- |
| 1 | **Header** | CABINET Logo (links) + aktuelles Datum mit Wochentag (rechts). | Datum aktualisiert sich automatisch um Mitternacht. |
| 2 | **Status-Banner** | Drei Zustände: **Geöffnet** (grün): „Wir sind geöffnet" + „Heute bis [Uhrzeit] für Sie da." · **Hinweis** (gelb): Frei definierbare Headline + Subtext. Z. B. „Heute erst ab 11:00 Uhr" · **Geschlossen** (rot): Frei definierbare Headline + Subtext. Z. B. „Betriebsurlaub bis 03.03." | Farbe, Icon und Text wechseln je nach Status. CMS-gesteuert. |
| 3 | **Öffnungszeiten** | Wochenansicht MoSo. Heutiger Tag visuell hervorgehoben (fetter Text, leichter Hintergrund). Wenn Sonderöffnung aktiv: heutige Zeit wird in Orange angezeigt. | Heutiger Tag wird automatisch erkannt. Sonderöffnung überschreibt Standard-Zeit für heute. |
| 4 | **Termin-Karte** | Dunkle Karte mit Kalender-Icon. Zeigt: „Nächster freier Termin" + Datum/Uhrzeit + „Beratung ca. 45 Min." | CMS-gesteuert. Optional: Kann später an Kalendersystem angebunden werden. |
| 5 | **Footer** | Telefonnummer (links) + E-Mail (links) + QR-Code (rechts). QR-Code führt zur Website oder Terminbuchung. | Statisch. QR-Code wird einmalig generiert und als Asset hinterlegt. |
---
## 3. CMS-Felder (B2in-Backend)
Folgende Felder werden im B2in-Backend als eigener Bereich „CABINET Info-Tablet" angelegt. Alle Felder sind einfache Eingabefelder ohne komplexe Logik.
| Feldname | Typ | Validierung | Beschreibung |
| --- | --- | --- | --- |
| `store_status` | Dropdown | `open` | `notice` | `closed` | Bestimmt Farbe und Grundtext des Status-Banners. |
| `notice_headline` | Text | Max. 40 Zeichen | Headline im Status-Banner bei Status „notice" oder „closed". Wird bei „open" ignoriert. |
| `notice_subtext` | Text | Max. 80 Zeichen | Erklärender Subtext unter der Headline. Optional. |
| `override_open_today` | Zeit (HH:MM) | Optional, Format HH:MM | Sonder-Öffnungszeit für heute. Überschreibt die Standard-Öffnungszeit in der Wochenansicht. |
| `override_close_today` | Zeit (HH:MM) | Optional, Format HH:MM | Sonder-Schlusszeit für heute. |
| `next_appointment_date` | Datum | TT.MM.JJJJ | Datum des nächsten freien Beratungstermins. |
| `next_appointment_time` | Zeit (HH:MM) | Format HH:MM | Uhrzeit des nächsten freien Termins. |
| `hours_monday` `hours_sunday` | Text | z. B. „10:0018:00" oder „Geschlossen" | Standard-Öffnungszeiten pro Wochentag. 7 Felder. |
| `contact_phone` | Text | Freitext | Telefonnummer im Footer. |
| `contact_email` | Text | E-Mail-Format | E-Mail-Adresse im Footer. |
**Wichtig:** Die Felder `override_open_today` und `override_close_today` sollten sich automatisch um Mitternacht zurücksetzen (auf leer), damit die Sonderöffnung nicht versehentlich am nächsten Tag weiterläuft.
---
## 4. API-Endpoint
Das Backend stellt einen einfachen JSON-Endpoint bereit, den die Webapp abfragt.
### 4.1 Endpoint-Struktur
`GET /api/cabinet-tablet/status`
**Response (JSON):**
json
`{
"store_status": "notice",
"notice_headline": "Heute erst ab 11:00 Uhr",
"notice_subtext": "Wegen eines Kundentermins öffnen wir heute später.",
"override_open_today": "11:00",
"override_close_today": null,
"next_appointment": {
"date": "2026-02-27",
"time": "14:00"
},
"hours": {
"monday": "10:00 18:00",
"tuesday": "10:00 18:00",
"wednesday": "10:00 18:00",
"thursday": "10:00 18:00",
"friday": "10:00 18:00",
"saturday": "10:00 14:00",
"sunday": "Geschlossen"
},
"contact": {
"phone": "0521 123 456 0",
"email": "info@cabinet-bielefeld.de"
},
"updated_at": "2026-02-26T09:15:00Z"
}`
### 4.2 Update-Mechanismus (Polling)
Die Webapp fragt den Endpoint in zwei Stufen ab:
**1. Lightweight Check (alle 30 Sekunden):**
Die Webapp ruft einen minimalen Endpoint ab, der nur den `updated_at` Timestamp zurückgibt.
`GET /api/cabinet-tablet/check`
json
`{ "updated_at": "2026-02-26T09:15:00Z" }`
**2. Full Fetch (nur bei Änderung):**
Wenn der Timestamp sich vom lokal gespeicherten unterscheidet, wird der komplette Status-Endpoint abgerufen und die Anzeige aktualisiert kein Page-Reload, nur DOM-Update per JavaScript.
**Warum Polling statt WebSockets?** Für ein einzelnes Tablet, das auf ein CMS reagiert, das vielleicht 12x am Tag geändert wird, ist Polling robuster und einfacher zu warten. WebSockets wären Overkill und eine zusätzliche Fehlerquelle.
---
## 5. Technische Stabilität & Kiosk-Betrieb
### 5.1 Kiosk-App (Android)
**Empfehlung: Fully Kiosk Browser** (ca. 7 € einmalig pro Gerät, Industriestandard für Digital Signage).
Funktionen, die wir nutzen:
- Vollbild-/Kioskmodus: Kein Zugriff auf Android-UI, Statusbar ausgeblendet
- Auto-Restart bei Browser-Absturz: App startet sich automatisch neu und lädt die URL
- Zeitsteuerung: Tablet geht nachts (z. B. 22:00) in Standby, wacht morgens (z. B. 07:00) auf
- Remote-Management: Über Fully Cloud kann die URL und Einstellungen remote geändert werden
- Bildschirm-Timeout: Screen bleibt dauerhaft an während der definierten Betriebszeit
- Motion Detection (optional): Bildschirm wird heller wenn jemand davor steht
### 5.2 Webapp-seitige Stabilität
Zusätzlich zur Kiosk-App bauen wir folgende Sicherheiten direkt in die Webapp ein:
| Maßnahme | Beschreibung |
| --- | --- |
| **Auto-Reload** | Kompletter Page-Reload alle 6 Stunden (z. B. um 03:00, 09:00, 15:00, 21:00). Räumt Speicher auf und verhindert Memory Leaks durch langlebige Browser-Sessions. |
| **Offline-Fallback** | Wenn die API nicht erreichbar ist, zeigt die Webapp den letzten bekannten Stand an + dezenten Hinweis „Stand: [Zeitpunkt]". Kein Fehlerbildschirm, der Passanten verwirrt. |
| **Connection-Recovery** | Wenn das Polling 3x hintereinander fehlschlägt, wartet die Webapp 5 Minuten, dann versucht sie es erneut. Nach 30 Minuten ohne Verbindung: automatischer Page-Reload. |
| **Lokaler Cache** | Die letzte API-Response wird im localStorage gespeichert. Bei Neustart (z. B. nach Browser-Crash) wird sofort der Cache angezeigt, während im Hintergrund frische Daten geladen werden. |
| **Keine Interaktion** | Die Webapp hat keine klickbaren Elemente (außer dem QR-Code, der ohnehin nur visuell ist). Kein Scrollen, kein Touch-Event. Verhindert versehentliche Bedienung. |
| **Automatische Datumsaktualisierung** | Um Mitternacht: Neuen Wochentag setzen, heutigen Tag in der Öffnungszeiten-Liste aktualisieren, Sonderöffnungszeiten zurücksetzen. |
---
## 6. Frontend-Spezifikation
### 6.1 Technologie
- Einfache statische HTML/CSS/JS-Seite (kein Framework notwendig)
- Responsive für Tablet-Größen hochkannt (ca. 1.600 x 2.456** Pixel **, 256 PPI**)
- Keine externen Abhängigkeiten außer einer Google-Fonts-Einbindung (Fallback auf System-Fonts)
- JavaScript Vanilla kein React, Vue o. Ä. nötig
### 6.3 Animations & Transitions
Wenn sich der Status ändert (z. B. von „open" zu „notice"), soll der Übergang sanft per CSS-Transition (300ms ease) erfolgen kein harter Wechsel. Gleiches gilt für Änderungen an Öffnungszeiten oder Termin.
---
## 7. Hardware-Empfehlung
| Komponente | Empfehlung |
| --- | --- |
| **Tablet** | Android-Tablet, 810 Zoll, min. 2 GB RAM. Muss nicht high-end sein es zeigt nur eine Webseite. HUAWEI MatePad T |
| **Kiosk-App** | Fully Kiosk Browser (ca. 7 € Lizenz). |
| **Halterung** | Wandhalterung oder Standfuß mit Diebstahlschutz. Hochkant montiert. |
| **Stromversorgung** | Dauerhaft am Strom (Ladekabel mit Kabelkanal). Akku-Ladung auf 80 % begrenzen (Fully Kiosk unterstützt das), um Akku-Verschleiß zu minimieren. |
| **WLAN** | Stabiles Store-WLAN. Empfehlung: Festes WLAN-Profil im Tablet hinterlegen, Auto-Reconnect aktivieren. |
---
## 8. Umsetzungs-Checkliste
| # | Aufgabe | Verantwortlich | Status |
| --- | --- | --- | --- |
| 1 | CMS-Felder im B2in-Backend anlegen (Abschnitt 3) | Backend-Entwicklung | Offen |
| 2 | API-Endpoint implementieren (Abschnitt 4) | Backend-Entwicklung | Offen |
| 3 | Frontend-Webapp bauen (Abschnitt 2 + 6) | Frontend-Entwicklung | Offen |
| 4 | Polling-Mechanismus + Stabilität implementieren (Abschnitt 4.2 + 5.2) | Frontend-Entwicklung | Offen |
| 5 | Subdomain einrichten + Deployment | DevOps | Offen |
| 6 | Tablet beschaffen + Fully Kiosk einrichten (Abschnitt 7) | Marcel / Hardware | Offen |
| 7 | QR-Code generieren + als Asset hinterlegen | Design | Offen |
| 8 | End-to-End-Test: CMS-Änderung → Tablet zeigt Update | QA | Offen |
| 9 | Installation im Store + Feintuning Helligkeit/Position | Marcel + Technik | Offen |
---
## Anhang: Zusammenfassung der Auto-Logiken
Folgende Dinge passieren automatisch, ohne dass Marcel etwas pflegen muss:
- Datum und Wochentag im Header aktualisieren sich um Mitternacht
- Heutiger Tag in der Öffnungszeitenliste wird automatisch hervorgehoben
- Sonderöffnungszeiten (override-Felder) setzen sich um Mitternacht zurück
- Bei Status „open": Banner-Text generiert sich automatisch aus der heutigen Schlusszeit
- Polling läuft im Hintergrund (alle 30 Sek.), DOM-Updates ohne Flackern
- Auto-Reload alle 6 Stunden für Speicher-Hygiene
- Offline-Fallback: Letzter Stand wird angezeigt, kein leerer Bildschirm
- Fully Kiosk: Auto-Restart bei Crash, Standby-Zeiten, Bildschirm-Management