Query-Parameter-Passthrough per URL steuerbar (Checkbox im Edit-Modal), Standard aus, sichtbar via Link-Icon in Playlist
- Neue Config-Felder: passthrough_params (true/false) pro URL-Item - Edit-Modal in admin.html + priority.html mit Checkbox - Player checkt item.passthrough === true vor appendParams() - Default: false (keine Parameter-Übergabe ohne Checkbox) - Link-Icon (ti-link / ti-link-off) in Playlist-Zeile als Status-Indikator - Doku: help.html, README.md, AGENTS.md
This commit is contained in:
@@ -132,7 +132,7 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
||||
- Neue Standorte können über den `+`-Button im Header oder über `GET /add-site?name=<name>` angelegt werden (nur Admins).
|
||||
- `delete_site` entfernt den Standort aus Config und löscht das Medienverzeichnis rekursiv (nur Admins).
|
||||
- `config.json["server_url"]` (z. B. `http://signage.ccmake.de`) wird in der Admin-Ansicht für die Player-URLs verwendet.
|
||||
- **Query-Parameter-Passthrough**: Alle Query-Parameter, die an die Player-URL (`/player/<site>/<screen>`) angehängt werden, werden automatisch an alle Playlist-URLs (iframe + Overlay) weitergereicht. Beispiel: `/player/stuttgart/lobby?customer=acme` hängt `?customer=acme` an jede URL in der Playlist an. Funktioniert über `appendParams()` in `player.html` + `player_params` im Flask-Route.
|
||||
- **Query-Parameter-Passthrough**: Query-Parameter an der Player-URL (`/player/<site>/<screen>`) können pro Playlist-URL im Edit-Modal (Checkbox) gesteuert werden. Standardmäßig **aus** – nur URLs mit aktivierter Checkbox erhalten die Parameter. Beispiel: `/player/stuttgart/lobby?customer=acme` hängt `?customer=acme` nur an URLs mit Passthrough=an. Funktioniert über `appendParams()` in `player.html` + `player_params` im Flask-Route.
|
||||
- Screen-Card-Body hat Tabler-Tabs: **Playlist** (1, aktiv), **Einstellungen** (2), **Aktionen** (3), **Digital Voice Agent** (4), **Medien** (5), **Info** (6); Priority-Seite ebenfalls Tabs **Playlist** und **Medien**.
|
||||
- `stay_on_first`: Wenn aktiviert bleibt der Player auf dem ersten Playlist-Element stehen (kein Durchlauf).
|
||||
- Tab-Reihenfolge in Screen-Cards: Playlist → Einstellungen → Aktionen → Digital Voice Agent → Medien → Info.
|
||||
|
||||
12
README.md
12
README.md
@@ -25,6 +25,7 @@ Browserbasiertes Digital-Signage-System für interne Info-Screens.
|
||||
- **Admin Dashboard**: `/admin/dashboard` – Statistiken (Sites, Screens, User, Admins, Superuser) mit Sparkline-Charts + Trendanzeige, Aktivitätsverlauf
|
||||
- **Aktivitätsverlauf**: Alle Erstell-/Lösch-/Änderungsaktionen sowie Login/Logout werden in `history.json` protokolliert und im Dashboard angezeigt
|
||||
- Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien, Info (Tabler Tabs)
|
||||
- **Query-Parameter-Passthrough**: Per Player-URL übergebene Query-Parameter (z. B. `/player/stuttgart/lobby?customer=acme`) können pro Playlist-URL im Edit-Modal aktiviert werden – standardmäßig aus
|
||||
- Priority-Seite ebenfalls mit Tabs: Playlist und Medien
|
||||
- Dark Mode (localStorage-persistiert)
|
||||
- CI-konformes Admin-UI (CANCOM-Design: `brand-surface`, `nav-surface` rot)
|
||||
@@ -271,6 +272,17 @@ Beispiel:
|
||||
http://localhost:5005/player/stuttgart/lobby
|
||||
```
|
||||
|
||||
### Query-Parameter-Passthrough
|
||||
|
||||
Die Player-URL akzeptiert beliebige Query-Parameter, z. B. `?customer=acme&token=xyz`. Ob diese Parameter an eine Playlist-URL weitergegeben werden, wird pro URL im Admin-Playlist-Edit-Modal gesteuert (Checkbox "Query-Parameter an diese URL übergeben", standardmäßig aus).
|
||||
|
||||
Beispiel:
|
||||
```
|
||||
/player/stuttgart/lobby?customer=acme&token=xyz
|
||||
```
|
||||
- URL in Playlist mit **aktiviertem** Passthrough: `https://dashboard.acme.com/status?customer=acme&token=xyz`
|
||||
- URL in Playlist mit **deaktiviertem** Passthrough: `https://dashboard.acme.com/status`
|
||||
|
||||
### Willkommensseite
|
||||
|
||||
```
|
||||
|
||||
21
app.py
21
app.py
@@ -252,7 +252,8 @@ def load_priority_files():
|
||||
"type": "url",
|
||||
"size": "URL",
|
||||
"zoom": url_data.get("zoom", 1.0),
|
||||
"enabled": playlist_item_enabled(item)
|
||||
"enabled": playlist_item_enabled(item),
|
||||
"passthrough": item.get("passthrough_params", False) if isinstance(item, dict) else False
|
||||
})
|
||||
continue
|
||||
|
||||
@@ -980,7 +981,7 @@ def player(site, screen):
|
||||
if is_url(item):
|
||||
url_data = normalize_url(item)
|
||||
if url_data:
|
||||
normal_files.append({"kind": "url", "url": url_data["url"], "zoom": url_data.get("zoom", 1.0), "enabled": True})
|
||||
normal_files.append({"kind": "url", "url": url_data["url"], "zoom": url_data.get("zoom", 1.0), "enabled": True, "passthrough": item.get("passthrough_params", False) if isinstance(item, dict) else False})
|
||||
continue
|
||||
|
||||
if item_name and os.path.exists(os.path.join(folder, item_name)) and allowed_file(item_name):
|
||||
@@ -1010,7 +1011,7 @@ def player(site, screen):
|
||||
if is_url(item):
|
||||
url_data = normalize_url(item)
|
||||
if url_data:
|
||||
prio_files.append({"kind": "url", "url": url_data["url"], "zoom": url_data.get("zoom", 1.0), "enabled": True})
|
||||
prio_files.append({"kind": "url", "url": url_data["url"], "zoom": url_data.get("zoom", 1.0), "enabled": True, "passthrough": item.get("passthrough_params", False) if isinstance(item, dict) else False})
|
||||
continue
|
||||
|
||||
file_path = os.path.join(MEDIA_DIR, "priority", item_name)
|
||||
@@ -1200,7 +1201,8 @@ def admin(site):
|
||||
"type": "url",
|
||||
"size": "URL",
|
||||
"zoom": url_data.get("zoom", 1.0),
|
||||
"enabled": playlist_item_enabled(item)
|
||||
"enabled": playlist_item_enabled(item),
|
||||
"passthrough": item.get("passthrough_params", False) if isinstance(item, dict) else False
|
||||
})
|
||||
continue
|
||||
|
||||
@@ -1608,7 +1610,16 @@ def update_playlist_item(site, screen):
|
||||
for i, item in enumerate(playlist):
|
||||
name = playlist_item_name(item)
|
||||
if name == old_name:
|
||||
playlist[i] = {"url": new_url, "zoom": zoom}
|
||||
updated = {"url": new_url, "zoom": zoom}
|
||||
passthrough = data.get("passthrough", None)
|
||||
if passthrough is not None:
|
||||
updated["passthrough_params"] = bool(passthrough)
|
||||
else:
|
||||
# Bestehenden Wert übernehmen falls vorhanden
|
||||
existing = item.get("passthrough_params") if isinstance(item, dict) else None
|
||||
if existing is not None:
|
||||
updated["passthrough_params"] = existing
|
||||
playlist[i] = updated
|
||||
save_config(config)
|
||||
return jsonify({"success": True})
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
data-bs-toggle="tooltip"
|
||||
title="URL: {{ file.name }}{% if file.zoom %} (Zoom: {{ file.zoom }}x){% endif %}"
|
||||
{% endif %}>
|
||||
{{ file.name }}{% if file.type == "url" and file.zoom != 1.0 %} <strong style="color: #DA002D;">[{{ file.zoom }}x]</strong>{% endif %}
|
||||
{{ file.name }}{% if file.type == "url" and file.zoom != 1.0 %} <strong style="color: #DA002D;">[{{ file.zoom }}x]</strong>{% endif %}{% if file.type == "url" %}{% if file.get('passthrough', False) %} <i class="ti ti-link" style="color:#6c757d;" title="Query-Parameter werden übergeben"></i>{% else %} <i class="ti ti-link-off" style="color:#adb5bd;" title="Query-Parameter werden nicht übergeben"></i>{% endif %}{% endif %}
|
||||
</span>
|
||||
|
||||
<!-- Typ-Badge -->
|
||||
@@ -290,7 +290,8 @@
|
||||
data-zoom="{{ file.zoom | default(1.0) }}"
|
||||
data-site="{{ current_site }}"
|
||||
data-screen="{{ screen }}"
|
||||
data-oldname="{{ file.name | e }}">
|
||||
data-oldname="{{ file.name | e }}"
|
||||
data-passthrough="{{ 'true' if file.get('passthrough', False) else 'false' }}">
|
||||
Edit
|
||||
</button>
|
||||
{% else %}
|
||||
@@ -552,6 +553,7 @@ document.addEventListener("click", function (e) {
|
||||
editState.oldName = btn.dataset.oldname;
|
||||
document.getElementById("edit-url-input").value = btn.dataset.oldname;
|
||||
document.getElementById("edit-zoom-input").value = btn.dataset.zoom;
|
||||
document.getElementById("edit-passthrough-input").checked = btn.dataset.passthrough === "true";
|
||||
document.getElementById("edit-url-error").style.display = "none";
|
||||
new bootstrap.Modal(document.getElementById("edit-url-modal")).show();
|
||||
});
|
||||
@@ -629,7 +631,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
body: JSON.stringify({
|
||||
old_name: editState.oldName,
|
||||
new_url: newUrl,
|
||||
zoom: zoom
|
||||
zoom: zoom,
|
||||
passthrough: document.getElementById("edit-passthrough-input").checked
|
||||
})
|
||||
}).then(function (r) {
|
||||
if (r.ok) {
|
||||
@@ -666,6 +669,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
|
||||
<input type="number" class="form-control" id="edit-zoom-input" step="0.1" min="0.1" value="1.0">
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="edit-passthrough-input">
|
||||
<label class="form-check-label" for="edit-passthrough-input">Query-Parameter an diese URL übergeben</label>
|
||||
</div>
|
||||
<div class="alert alert-danger py-2" id="edit-url-error" style="display:none"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
<li><strong>Sortieren:</strong> Per Drag & Drop die Reihenfolge der Playlist-Einträge ändern (Ziehgriff links).</li>
|
||||
<li><strong>Deaktivieren:</strong> Mit der Checkbox einzelne Einträge temporär ausblenden.</li>
|
||||
<li><strong>Löschen:</strong> Über den roten Papierkorb-Button.</li>
|
||||
<li><strong>Query-Parameter-Passthrough:</strong> Per URL-Edit-Button (Stift-Icon) im Playlist-Eintrag kann pro URL gesteuert werden, ob Query-Parameter von der Player-URL an die Playlist-URL übergeben werden. Standardmäßig <strong>aus</strong> – Checkbox "Query-Parameter an diese URL übergeben" aktivieren wenn gewünscht.</li>
|
||||
</ul>
|
||||
<p class="text-muted small mb-0"><i class="ti ti-info-circle me-1"></i>Änderungen an der Playlist werden automatisch gespeichert. Der Player lädt die Seite automatisch neu (via Hash-Prüfung).</p>
|
||||
</div>
|
||||
|
||||
@@ -456,7 +456,7 @@ function playNext() {
|
||||
// URL im iframe anzeigen (mit Zoom-Unterstützung)
|
||||
if (item.kind === "url") {
|
||||
iframe.style.display = "block";
|
||||
iframe.src = appendParams(item.url, playerParams);
|
||||
iframe.src = (item.passthrough === true) ? appendParams(item.url, playerParams) : item.url;
|
||||
const zoom = item.zoom || 1.0;
|
||||
if (zoom !== 1.0) {
|
||||
const scale = 1 / zoom;
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
data-bs-toggle="tooltip"
|
||||
title="URL: {{ file.name }}{% if file.zoom %} (Zoom: {{ file.zoom }}x){% endif %}"
|
||||
{% endif %}>
|
||||
{{ file.name }}{% if file.type == "url" and file.zoom != 1.0 %} <strong style="color: #DA002D;">[{{ file.zoom }}x]</strong>{% endif %}
|
||||
{{ file.name }}{% if file.type == "url" and file.zoom != 1.0 %} <strong style="color: #DA002D;">[{{ file.zoom }}x]</strong>{% endif %}{% if file.type == "url" %}{% if file.get('passthrough', False) %} <i class="ti ti-link" style="color:#6c757d;" title="Query-Parameter werden übergeben"></i>{% else %} <i class="ti ti-link-off" style="color:#adb5bd;" title="Query-Parameter werden nicht übergeben"></i>{% endif %}{% endif %}
|
||||
</span>
|
||||
|
||||
{% if file.type == "image" %}
|
||||
@@ -192,7 +192,8 @@
|
||||
data-zoom="{{ file.zoom | default(1.0) }}"
|
||||
data-site="{{ current_site }}"
|
||||
data-screen="priority"
|
||||
data-oldname="{{ file.name | e }}">
|
||||
data-oldname="{{ file.name | e }}"
|
||||
data-passthrough="{{ 'true' if file.get('passthrough', False) else 'false' }}">
|
||||
Edit
|
||||
</button>
|
||||
{% else %}
|
||||
@@ -265,6 +266,7 @@ document.addEventListener("click", function (e) {
|
||||
editState.oldName = btn.dataset.oldname;
|
||||
document.getElementById("edit-url-input").value = btn.dataset.oldname;
|
||||
document.getElementById("edit-zoom-input").value = btn.dataset.zoom;
|
||||
document.getElementById("edit-passthrough-input").checked = btn.dataset.passthrough === "true";
|
||||
document.getElementById("edit-url-error").style.display = "none";
|
||||
new bootstrap.Modal(document.getElementById("edit-url-modal")).show();
|
||||
});
|
||||
@@ -337,7 +339,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
body: JSON.stringify({
|
||||
old_name: editState.oldName,
|
||||
new_url: newUrl,
|
||||
zoom: zoom
|
||||
zoom: zoom,
|
||||
passthrough: document.getElementById("edit-passthrough-input").checked
|
||||
})
|
||||
}).then(function (r) {
|
||||
if (r.ok) {
|
||||
@@ -374,6 +377,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
|
||||
<input type="number" class="form-control" id="edit-zoom-input" step="0.1" min="0.1" value="1.0">
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="edit-passthrough-input">
|
||||
<label class="form-check-label" for="edit-passthrough-input">Query-Parameter an diese URL übergeben</label>
|
||||
</div>
|
||||
<div class="alert alert-danger py-2" id="edit-url-error" style="display:none"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
Reference in New Issue
Block a user