# Info-Board Neu - API-Endpoints Vollständig

## Überblick

Die Backend-API unterteilt sich in mehrere Bereiche:

- **Health & Meta**: System-Status und API-Informationen
- **Player Status**: Status-Ingest und Diagnose vom Player
- **Screen Management**: CRUD und Registrierung von Screens
- **Playlists**: Abruf und Verwaltung von Wiedergabelisten
- **Media**: Upload und Verwaltung von Medien-Assets
- **Message Wall**: Auflösung von Nachrichten-Wand-Anfragen
- **Admin & UI**: Web-Formulare und Provisionierung
- **Provisioning**: Erstinstallation neuer Screens (geplant)

---

## Health & Meta

### GET /healthz

Health-Check für Monitoring.

**Response:**
```json
{
  "status": "ok",
  "service": "morz-infoboard-backend"
}
```

### GET /api/v1

API-Entrypoint mit Tools-Übersicht.

**Response:**
```json
{
  "name": "morz-infoboard-backend",
  "version": "dev",
  "tools": [
    "message-wall-resolve",
    "screen-status-list",
    "screen-status-detail",
    "player-status-ingest",
    "screen-status-delete"
  ]
}
```

### GET /api/v1/meta

Zusätzliche Metainformationen (noch nicht spezifiziert).

---

## Player Status (Diagnose)

Siehe separate Dokumentation in `PLAYER-STATUS-HTTP.md`.

Endpoints:
- `POST /api/v1/player/status` — Status-Ingest vom Player-Agent
- `GET /api/v1/screens/status` — Übersicht aller Screen-Status
- `GET /api/v1/screens/{screenId}/status` — Einzelner Screen-Status
- `DELETE /api/v1/screens/{screenId}/status` — Status löschen

### POST /api/v1/player/status

Der Player-Agent sendet seinen aktuellen Status an den Server.

**Request-Body:**
```json
{
  "screen_id": "info01-dev",
  "ts": "2026-03-22T16:00:00Z",
  "status": "running",
  "server_connectivity": "online",
  "server_url": "http://127.0.0.1:8080",
  "mqtt_broker": "tcp://127.0.0.1:1883",
  "heartbeat_every_seconds": 30,
  "started_at": "2026-03-22T15:59:30Z",
  "last_heartbeat_at": "2026-03-22T16:00:00Z"
}
```

**Response:**
```json
{ "status": "accepted" }
```

---

## Screen Management (JSON API)

### POST /api/v1/screens/register

Agent-Selbstregistrierung — wird vom Player-Agent beim Hochfahren aufgerufen.

Der Agent upsert den Screen automatisch im Default-Tenant ("morz"), so dass alle deployt Screen automatisch im Admin-UI erscheinen.

**Request-Body:**
```json
{
  "slug": "info10",
  "name": "Info10 Bildschirm",
  "orientation": "landscape"
}
```

**Response:**
```json
{
  "id": "uuid...",
  "tenant_id": "uuid...",
  "slug": "info10",
  "name": "Info10 Bildschirm",
  "orientation": "landscape",
  "created_at": "2026-03-22T16:00:00Z",
  "updated_at": "2026-03-22T16:00:00Z"
}
```

**Status:**
- `200 OK` — Screen wurde erzeugt oder aktualisiert (upsert)
- `400 Bad Request` — Slug fehlt oder ungültig
- `500 Internal Server Error` — DB-Fehler oder Default-Tenant nicht vorhanden

---

### GET /api/v1/tenants/{tenantSlug}/screens

Listet alle Screens eines Tenants auf.

**Response:**
```json
[
  {
    "id": "uuid...",
    "tenant_id": "uuid...",
    "slug": "info10",
    "name": "Info10 Bildschirm",
    "orientation": "landscape",
    "created_at": "2026-03-22T16:00:00Z",
    "updated_at": "2026-03-22T16:00:00Z"
  },
  ...
]
```

**Status:**
- `200 OK` — Liste erfolgreich abrufen
- `404 Not Found` — Tenant nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

---

### POST /api/v1/tenants/{tenantSlug}/screens

Erstellt einen neuen Screen für einen Tenant (Admin-API).

**Request-Body:**
```json
{
  "slug": "new-screen",
  "name": "New Display",
  "orientation": "portrait"
}
```

**Response:**
```json
{
  "id": "uuid...",
  "tenant_id": "uuid...",
  "slug": "new-screen",
  "name": "New Display",
  "orientation": "portrait",
  "created_at": "2026-03-22T16:00:00Z",
  "updated_at": "2026-03-22T16:00:00Z"
}
```

**Status:**
- `201 Created` — Screen erstellt
- `400 Bad Request` — Slug oder Name fehlt
- `404 Not Found` — Tenant nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

---

## Playlist Management (JSON API)

### GET /api/v1/screens/{screenId}/playlist

Abruf der aktiven Playlist für einen Screen (Player-Sync).

Der Player ruft diesen Endpoint auf, um die aktuellen Inhalte zu laden.

**Response:**
```json
{
  "playlist_id": "uuid...",
  "default_duration_seconds": 20,
  "items": [
    {
      "id": "uuid...",
      "type": "web",
      "src": "http://example.com/page1",
      "title": "Startseite",
      "duration_seconds": 30,
      "enabled": true,
      "valid_from": null,
      "valid_until": null
    },
    {
      "id": "uuid...",
      "type": "image",
      "src": "/uploads/banner.jpg",
      "title": "Werbebanner",
      "duration_seconds": 20,
      "enabled": true,
      "valid_from": null,
      "valid_until": null
    }
  ]
}
```

Wenn keine Playlist vorhanden ist, wird eine leere Liste zurückgegeben:
```json
{
  "items": []
}
```

**Status:**
- `200 OK` — Playlist abrufen
- `404 Not Found` — Screen nicht vorhanden

---

### GET /api/v1/playlists/{screenId}

Abrufen einer kompletten Playlist mit Metadaten.

**Response:**
```json
{
  "playlist": {
    "id": "uuid...",
    "screen_id": "uuid...",
    "tenant_id": "uuid...",
    "name": "Hauptplaylist",
    "is_active": true,
    "default_duration_seconds": 20,
    "fallback_enabled": true,
    "fallback_dir": "/fallback",
    "shuffle_enabled": false,
    "created_at": "2026-03-22T16:00:00Z",
    "updated_at": "2026-03-22T16:00:00Z"
  },
  "items": [
    {
      "id": "uuid...",
      "playlist_id": "uuid...",
      "type": "web",
      "src": "http://example.com",
      "title": "Example",
      "duration_seconds": 20,
      "enabled": true,
      "created_at": "2026-03-22T16:00:00Z"
    }
  ]
}
```

**Status:**
- `200 OK` — Erfolgreich abrufen
- `500 Internal Server Error` — DB-Fehler

---

### POST /api/v1/playlists/{playlistId}/items

Fügt ein Item zu einer Playlist hinzu.

**Request-Body (Optionen A: Aus Media-Library):**
```json
{
  "media_asset_id": "uuid...",
  "title": "Optional überschriebener Titel"
}
```

**Request-Body (Optionen B: Direkte URL):**
```json
{
  "type": "web",
  "src": "http://example.com/page",
  "title": "Example Page",
  "duration_seconds": 30,
  "valid_from": "2026-03-22T09:00:00Z",
  "valid_until": "2026-03-22T17:00:00Z"
}
```

**Response:**
```json
{
  "id": "uuid...",
  "playlist_id": "uuid...",
  "type": "web",
  "src": "http://example.com/page",
  "title": "Example Page",
  "duration_seconds": 30,
  "enabled": true,
  "valid_from": "2026-03-22T09:00:00Z",
  "valid_until": "2026-03-22T17:00:00Z",
  "created_at": "2026-03-22T16:00:00Z"
}
```

**Status:**
- `201 Created` — Item erstellt
- `400 Bad Request` — Type oder Src fehlt; Media-Asset nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

---

### PATCH /api/v1/items/{itemId}

Aktualisiert ein Playlist-Item (Titel, Dauer, Zeitfenster, aktiviert).

**Request-Body:**
```json
{
  "title": "Neuer Titel",
  "duration_seconds": 25,
  "enabled": true,
  "valid_from": "2026-03-22T09:00:00Z",
  "valid_until": "2026-03-22T17:00:00Z"
}
```

**Status:**
- `204 No Content` — Erfolgreich aktualisiert
- `400 Bad Request` — Ungültige Dauer
- `500 Internal Server Error` — DB-Fehler

---

### DELETE /api/v1/items/{itemId}

Löscht ein Playlist-Item.

**Status:**
- `204 No Content` — Erfolgreich gelöscht
- `500 Internal Server Error` — DB-Fehler

---

### PUT /api/v1/playlists/{playlistId}/order

Reordnet die Items einer Playlist anhand einer geordneten Liste von Item-IDs.

**Request-Body:**
```json
[
  "item-id-1",
  "item-id-2",
  "item-id-3"
]
```

**Status:**
- `204 No Content` — Erfolgreich reordert
- `400 Bad Request` — JSON-Array erwartet
- `500 Internal Server Error` — DB-Fehler

---

### PATCH /api/v1/playlists/{playlistId}/duration

Setzt die Standard-Dauer für neue Items einer Playlist.

**Request-Body (Form-Encoded):**
```
default_duration_seconds=25
```

**Status:**
- `204 No Content` — Erfolgreich aktualisiert
- `400 Bad Request` — Ungültige oder fehlende Dauer
- `500 Internal Server Error` — DB-Fehler

---

## Media Management (JSON API)

### GET /api/v1/tenants/{tenantSlug}/media

Listet alle Medien-Assets eines Tenants auf.

**Response:**
```json
[
  {
    "id": "uuid...",
    "tenant_id": "uuid...",
    "title": "Banner Image",
    "description": null,
    "type": "image",
    "source_kind": "upload",
    "storage_path": "/uploads/1234567890_banner.jpg",
    "original_url": null,
    "mime_type": "image/jpeg",
    "size_bytes": 102400,
    "enabled": true,
    "created_at": "2026-03-22T16:00:00Z",
    "updated_at": "2026-03-22T16:00:00Z"
  },
  {
    "id": "uuid...",
    "tenant_id": "uuid...",
    "title": "External Website",
    "type": "web",
    "source_kind": "remote_url",
    "original_url": "http://example.com",
    "storage_path": null,
    "enabled": true,
    "created_at": "2026-03-22T16:00:00Z"
  }
]
```

Wenn keine Assets vorhanden sind, wird eine leere Liste zurückgegeben.

**Status:**
- `200 OK` — Liste erfolgreich abrufen
- `404 Not Found` — Tenant nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

---

### POST /api/v1/tenants/{tenantSlug}/media

Registriert ein neues Medien-Asset (Datei-Upload oder externe URL).

**Request-Typ A: Datei-Upload (Multipart)**
```
Content-Type: multipart/form-data

type: image (oder video, pdf)
title: Mein Bild
file: <binary data>
```

**Request-Typ B: Externe URL (Multipart)**
```
Content-Type: multipart/form-data

type: web
title: Externe Website
url: http://example.com
```

**Response:**
```json
{
  "id": "uuid...",
  "tenant_id": "uuid...",
  "title": "Mein Bild",
  "type": "image",
  "source_kind": "upload",
  "storage_path": "/uploads/1234567890_mein_bild.jpg",
  "mime_type": "image/jpeg",
  "size_bytes": 102400,
  "enabled": true,
  "created_at": "2026-03-22T16:00:00Z"
}
```

**Status:**
- `201 Created` — Asset erstellt
- `400 Bad Request` — Ungültiger Type, fehlende Datei/URL, oder Request zu groß (>512 MB)
- `404 Not Found` — Tenant nicht vorhanden
- `500 Internal Server Error` — Datei-/DB-Fehler

---

### DELETE /api/v1/media/{id}

Löscht ein Medien-Asset (und physische Datei falls lokal gespeichert).

**Status:**
- `204 No Content` — Erfolgreich gelöscht
- `404 Not Found` — Asset nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

---

## Message Wall

### POST /api/v1/tools/message-wall/resolve

Spezialendpoint zur Auflösung von Nachrichten-Wand-Anfragen (noch in Entwicklung).

---

## Authentifizierung (Web-Formulare)

Alle Auth-Routen erfordern keine vorherige Authentifizierung.

### GET /login

Zeigt das Login-Formular.

- Wenn ein gueltiges `morz_session`-Cookie vorhanden ist, wird direkt zum jeweiligen Dashboard
  weitergeleitet (`/admin` fuer Admins, `/tenant/{slug}/dashboard` fuer Tenant-User).

**Response:** HTML-Seite mit Benutzername/Passwort-Formular und optionaler Flash-Message.

---

### POST /login

Verarbeitet die Login-Eingabe.

**Request (Form-Encoded):**
```
username=admin&password=geheim
```

**Verhalten:**
- Passwort wird per `bcrypt.CompareHashAndPassword` geprueft
- Bei Erfolg wird ein `morz_session`-Cookie gesetzt (HttpOnly, Secure, 24h TTL)
- Weiterleitung je nach Rolle: `admin` → `/admin`, `tenant` → `/tenant/{slug}/dashboard`
- Bei Fehler: Rueckkehr zur Login-Seite mit Flash-Message

**Status:**
- `303 See Other` — Erfolg, Weiterleitung
- `303 See Other` — Fehler, Rueckkehr zur Login-Seite mit `?msg=`

---

### POST /logout

Meldet den aktuellen Benutzer ab.

**Verhalten:**
- Session wird in der DB geloescht (`DeleteSession`)
- Cookie wird mit `MaxAge=-1` geloescht
- Weiterleitung zu `/login`

**Status:**
- `303 See Other`

---

## Tenant Self-Service Dashboard (Web-Formulare)

Alle Tenant-Routen erfordern `RequireAuth` + `RequireTenantAccess`.
Admins koennen auf jeden Tenant zugreifen; Tenant-User nur auf ihren eigenen.

### GET /tenant/{tenantSlug}/dashboard

Zeigt das Tenant-Self-Service-Dashboard.

**Tabs:**
- Tab A "Meine Monitore" — Screen-Karten mit Live-Status (via JS-Fetch aus `/api/v1/screens/status`)
- Tab B "Mediathek" — Upload-Formular und Dateiliste

**Query-Parameter:**
- `tab=media` — oeffnet direkt Tab B (z. B. nach Upload-Redirect)
- `flash=uploaded` / `flash=deleted` — zeigt Erfolgs-Flash-Message

**Response:** HTML-Seite.

---

### POST /tenant/{tenantSlug}/upload

Laedt ein Medium fuer den Tenant hoch.

**Request (Multipart Form):**
```
type: image (oder video, pdf)
title: Mein Bild
file: <binary data>
```

oder fuer eine Web-URL:
```
type: web
title: Externe Website
url: http://example.com
```

**Verhalten:**
- Datei wird in `MORZ_INFOBOARD_UPLOAD_DIR` gespeichert
- MIME-Typ wird aus dem Upload-Header abgeleitet
- Max. Upload-Groesse: 512 MB

**Status:**
- `303 See Other` → `/tenant/{slug}/dashboard?tab=media&flash=uploaded`
- `400 Bad Request` — fehlender Typ oder Datei
- `404 Not Found` — Tenant nicht vorhanden

---

### POST /tenant/{tenantSlug}/media/{mediaId}/delete

Loescht ein Medien-Asset des Tenants.

**Verhalten:**
- Eigentuemer-Pruefung: `asset.TenantID` muss mit dem Tenant uebereinstimmen
- Physische Datei wird geloescht sofern vorhanden

**Status:**
- `303 See Other` → `/tenant/{slug}/dashboard?tab=media&flash=deleted`
- `403 Forbidden` — Asset gehoert nicht diesem Tenant
- `404 Not Found` — Tenant oder Asset nicht vorhanden

---

## Admin UI (Web-Formulare)

### GET /admin

Administrations-Dashboard mit Übersicht aller Screens und Tenants.

Rückgabe: HTML-Seite mit:
- Liste aller Screens
- Status-Information
- Provisioning-Formulare
- Screen-Verwaltung

---

### POST /admin/screens/provision

Startet einen Provisionierungs-Job für einen neuen oder bestehenden Screen.

**Request-Body (Form-Encoded oder JSON):**
```json
{
  "screen_id": "uuid...",
  "target_ip": "192.168.1.100",
  "target_port": 22,
  "remote_user": "root",
  "auth_mode": "password",
  "provided_secret_ref": "secret-key-123"
}
```

**Status:**
- `200 OK` — Job erfolgreich erstellt
- `400 Bad Request` — Fehlende oder ungültige Parameter
- `404 Not Found` — Screen nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

**Hinweis:** Der eigentliche Provisionierungs-Job läuft asynchron über einen Worker ab.

---

### POST /admin/screens

Erstellt einen neuen Screen über das Admin-Formular.

**Request-Body (Form-Encoded):**
```
slug=new-screen&name=Neuer+Bildschirm&orientation=landscape
```

**Status:**
- `200 OK` oder `201 Created` — Screen erstellt
- `400 Bad Request` — Fehlende Parameter
- `500 Internal Server Error` — DB-Fehler

Rückleitung zur Admin-Seite.

---

### POST /admin/screens/{screenId}/delete

Löscht einen Screen.

**Status:**
- `200 OK` — Screen gelöscht
- `404 Not Found` — Screen nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

Rückleitung zur Admin-Seite.

---

## Playlist Management UI (Web-Formulare)

### GET /manage/{screenSlug}

Verwaltungs-UI für die Playlist eines Screens.

Rückgabe: HTML-Seite mit:
- Liste der Medien-Assets
- Playlist-Editor
- Upload-Formular
- Item-Verwaltung

---

### POST /manage/{screenSlug}/upload

Datei-Upload über das Manage-Formular.

**Request (Multipart):**
```
type: image (oder video, pdf)
title: Neues Bild
file: <binary data>
```

**Status:**
- `201 Created` — Asset erfolgreich hochgeladen
- `404 Not Found` — Screen nicht vorhanden
- `500 Internal Server Error` — Fehler

Rückleitung zum Manage-Formular.

---

### POST /manage/{screenSlug}/items

Fügt ein Item zur Playlist hinzu (Formular).

**Request (Form-Encoded):**
```
media_asset_id=uuid...&duration_seconds=25
oder
type=web&src=http://example.com&duration_seconds=30
```

**Status:**
- `201 Created` — Item erstellt
- `404 Not Found` — Screen nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

Rückleitung zum Manage-Formular.

---

### POST /manage/{screenSlug}/items/{itemId}

Aktualisiert ein Item (Formular).

**Request (Form-Encoded):**
```
title=Neuer+Titel&duration_seconds=25&enabled=on
```

**Status:**
- `200 OK` oder `204 No Content` — Erfolgreich aktualisiert
- `404 Not Found` — Item nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

Rückleitung zum Manage-Formular.

---

### POST /manage/{screenSlug}/items/{itemId}/delete

Löscht ein Item (Formular).

**Status:**
- `204 No Content` — Erfolgreich gelöscht
- `404 Not Found` — Item nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

Rückleitung zum Manage-Formular.

---

### POST /manage/{screenSlug}/reorder

Reordert Items (Formular).

**Request (Form-Encoded mit Array-Syntax):**
```
items=item-id-1&items=item-id-2&items=item-id-3
```

**Status:**
- `204 No Content` — Erfolgreich reordert
- `500 Internal Server Error` — DB-Fehler

Rückleitung zum Manage-Formular.

---

### POST /manage/{screenSlug}/media/{mediaId}/delete

Löscht ein Medien-Asset (Formular).

**Status:**
- `204 No Content` — Erfolgreich gelöscht
- `404 Not Found` — Asset nicht vorhanden
- `500 Internal Server Error` — DB-Fehler

Rückleitung zum Manage-Formular.

---

## Datei-Serving

### GET /uploads/{filename}

Stellt hochgeladene Medien-Dateien bereit.

**Query-Parameter:**
- Keine

**Status:**
- `200 OK` — Datei gefunden
- `404 Not Found` — Datei nicht vorhanden

---

## Diagnostic Pages (HTML)

### GET /status

HTML-Diagnoseseite für den Browser mit Übersicht aller Screen-Status.

- Automatisches Refresh alle 15 Sekunden
- Shortcut-Links für Filter
- Filterformular (wie JSON-Read-Pfad)
- Direkte JSON-Detail-Links pro Screen

**Query-Parameter:** Dieselben wie `GET /api/v1/screens/status`
- `q` — Screen-ID Substring-Suche
- `derived_state` — `online`, `degraded`, `offline`
- `server_connectivity` — `online`, `degraded`, `offline`, `unknown`
- `stale` — `true`, `false`
- `updated_since` — RFC3339-Zeitstempel
- `limit` — positive Ganzzahl

---

### GET /status/{screenId}

HTML-Detailseite für einen einzelnen Screen.

Zeigt:
- Screen-Information
- Derived State
- Player Status
- Connectivity
- Freshness und Timestamps
- Endpunkte und Links

---

## Player Local UI (Agent)

### GET /player

Zeigt die lokale Player-UI (HTML) auf dem Gerät.

Rückgabe: HTML-Seite mit:
- Splash-Screen
- Systeminformationen-Overlay
- Verbindungsstatus-Punkt
- Basis für Playlist-Anzeige

---

### GET /api/now-playing

JSON-API des Player-Agents (lokal).

**Response:**
```json
{
  "playlist": [
    {
      "src": "http://backend.local/api/v1/screens/info10/playlist",
      "type": "web",
      "title": "Startseite",
      "duration_seconds": 30
    }
  ],
  "status": "running",
  "connectivity": "online"
}
```

---

### GET /api/sysinfo

Systeminformationen des Player-Agents (lokal).

**Response:**
```json
{
  "items": [
    {
      "label": "Hostname",
      "value": "infoboard-01"
    },
    {
      "label": "Uptime",
      "value": "5d 2h 30m"
    }
  ]
}
```

---

## Fehlerbehandlung

Alle JSON-Responses folgen diesem Fehlermodell (falls implementiert):

```json
{
  "error": {
    "code": "error_code_here",
    "message": "Human-readable error message",
    "details": null
  }
}
```

Typische HTTP-Status:
- `200 OK` — Erfolgreiche Anfrage (Read)
- `201 Created` — Ressource erstellt
- `204 No Content` — Erfolgreiche Anfrage ohne Response-Body
- `400 Bad Request` — Eingabe-Validierung fehlgeschlagen
- `404 Not Found` — Ressource nicht vorhanden
- `500 Internal Server Error` — Server-Fehler

---

## In Vorbereitung (Phase 6 / künftig)

Die folgenden Endpoints sind derzeit vorbereitet, aber noch nicht vollständig implementiert:

- `POST /api/v1/player/screenshot` — Upload von Player-Screenshots an den Backend-Server
  - Wird vom Agent unter `player/agent/internal/screenshot/screenshot.go` mit dem Intervall `MORZ_INFOBOARD_SCREENSHOT_EVERY` aufgerufen
  - Multipart-Request mit `screen_id`, `screenshot` (Datei), `mime_type`
  - Benötigt Backend-Handler für Persistierung und/oder Verarbeitung

---

## Änderungshistorie

- **2026-03-23 (Update):** Security-Enhancements und Upload-Konsolidierung (Doris / Doku-Review)
  - CSRF-Schutz (Double-Submit-Cookie) in `internal/httpapi/csrf.go`
  - Rate-Limiting für `/login` in `internal/httpapi/ratelimit.go`
  - Upload-Logik konsolidiert in `internal/fileutil/fileutil.go` und `internal/httpapi/uploads.go`
  - Neue Env-Variable `MORZ_INFOBOARD_REGISTER_SECRET` dokumentiert
  - Screenshot-Modul im Agent vorbereitet mit `MORZ_INFOBOARD_SCREENSHOT_EVERY`
- **2026-03-23 (Update):** Auth- und Tenant-Dashboard-Endpoints ergaenzt (Doris / Doku-Review)
  - `GET /login`, `POST /login`, `POST /logout` dokumentiert
  - `GET /tenant/{tenantSlug}/dashboard` dokumentiert
  - `POST /tenant/{tenantSlug}/upload` dokumentiert
  - `POST /tenant/{tenantSlug}/media/{mediaId}/delete` dokumentiert
- **2026-03-23:** Initiale Dokumentation aller HTTP-Endpoints basierend auf Code-Review
  - Alle Screen-Management-Endpoints dokumentiert
  - Alle Playlist-Management-Endpoints dokumentiert
  - Alle Media-Management-Endpoints dokumentiert
  - Admin-UI und Manage-UI-Endpoints dokumentiert
  - Player-Status-Diagnostik dokumentiert
  - Player-Local-API dokumentiert
