Tabs in der Admin UI
This commit is contained in:
@@ -58,6 +58,10 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
|||||||
- Custom-URL-Button: pro Screen konfigurierbar (`custom_url` + `custom_url_label` + `custom_url_target`) im Admin-Formular; wird im Player als Button oben links angezeigt und öffnet die URL wahlweise in einem iframe-Overlay mit Zurück-Button (`overlay`) oder per Direkt-Weiterleitung (`redirect`); Player pausiert während das Overlay geöffnet ist.
|
- Custom-URL-Button: pro Screen konfigurierbar (`custom_url` + `custom_url_label` + `custom_url_target`) im Admin-Formular; wird im Player als Button oben links angezeigt und öffnet die URL wahlweise in einem iframe-Overlay mit Zurück-Button (`overlay`) oder per Direkt-Weiterleitung (`redirect`); Player pausiert während das Overlay geöffnet ist.
|
||||||
- Neue Standorte können über den `+`-Button im Header oder über `GET /add-site?name=<name>` angelegt werden.
|
- Neue Standorte können über den `+`-Button im Header oder über `GET /add-site?name=<name>` angelegt werden.
|
||||||
- `delete_site` entfernt den Standort aus Config und löscht das Medienverzeichnis rekursiv.
|
- `delete_site` entfernt den Standort aus Config und löscht das Medienverzeichnis rekursiv.
|
||||||
|
- `config.json["server_url"]` (z. B. `http://signage.ccmake.de`) wird in der Admin-Ansicht für die Player-URLs verwendet.
|
||||||
|
- Screen-Card-Body hat Tabler-Tabs: **Playlist** (1, aktiv), **Einstellungen** (2), **Medien** (3); Priority-Seite ebenfalls Tabs **Playlist** und **Medien**.
|
||||||
|
- Tab-Reihenfolge in Screen-Cards: Playlist → Einstellungen → Medien.
|
||||||
|
- Player-URL im Screen-Header ist ein klickbarer Link in grauer Farbe.
|
||||||
|
|
||||||
## Repo Quirks
|
## Repo Quirks
|
||||||
|
|
||||||
@@ -67,3 +71,4 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
|||||||
- Wichtige Helper-Funktionen in `app.py`: `load_config()`, `save_config()`, `get_site_list()`, `get_screen_config()`, `is_url()`, `normalize_url()`, `playlist_item_name()`, `playlist_item_enabled()`, `load_priority_files()`, `prio_redirect()`.
|
- Wichtige Helper-Funktionen in `app.py`: `load_config()`, `save_config()`, `get_site_list()`, `get_screen_config()`, `is_url()`, `normalize_url()`, `playlist_item_name()`, `playlist_item_enabled()`, `load_priority_files()`, `prio_redirect()`.
|
||||||
- `welcome.html` wird in `media/<site>/lobby/` gespeichert.
|
- `welcome.html` wird in `media/<site>/lobby/` gespeichert.
|
||||||
- `search_customer_logo` in `generate_welcome_page.py` nutzt OpenAI GPT-4 + Brandfetch CDN.
|
- `search_customer_logo` in `generate_welcome_page.py` nutzt OpenAI GPT-4 + Brandfetch CDN.
|
||||||
|
- `admin_priority` rendert `priority.html` mit `site_list`, `current_site`, `priority_files`, `server_url`.
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -15,6 +15,8 @@ Browserbasiertes Digital-Signage-System für interne Info-Screens.
|
|||||||
- Auto-Reload bei Playlist-Änderungen
|
- Auto-Reload bei Playlist-Änderungen
|
||||||
- Newsticker pro Screen
|
- Newsticker pro Screen
|
||||||
- **Custom-URL-Button**: pro Screen konfigurierbarer Aktions-Button oben links im Player (öffnet URL in iframe-Overlay mit Zurück-Button oder per Direkt-Weiterleitung)
|
- **Custom-URL-Button**: pro Screen konfigurierbarer Aktions-Button oben links im Player (öffnet URL in iframe-Overlay mit Zurück-Button oder per Direkt-Weiterleitung)
|
||||||
|
- Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Medien (Tabler Tabs)
|
||||||
|
- Priority-Seite ebenfalls mit Tabs: Playlist und Medien
|
||||||
- Dark Mode (localStorage-persistiert)
|
- Dark Mode (localStorage-persistiert)
|
||||||
- CI-konformes Admin-UI (CANCOM-Design: `brand-surface`, `nav-surface` rot)
|
- CI-konformes Admin-UI (CANCOM-Design: `brand-surface`, `nav-surface` rot)
|
||||||
|
|
||||||
@@ -118,11 +120,15 @@ docker compose up -d
|
|||||||
| `GET /admin` | Redirect zum ersten konfigurierten Standort |
|
| `GET /admin` | Redirect zum ersten konfigurierten Standort |
|
||||||
| `GET /admin/<site>` | Admin-Dashboard für einen Standort |
|
| `GET /admin/<site>` | Admin-Dashboard für einen Standort |
|
||||||
| `GET /admin/<site>/priority` | Priority-Playlist (separate Seite) |
|
| `GET /admin/<site>/priority` | Priority-Playlist (separate Seite) |
|
||||||
| `GET /admin/<site>/update/<screen>` | Screen-Einstellungen speichern (POST) |
|
| `POST /admin/<site>/update/<screen>` | Screen-Einstellungen speichern |
|
||||||
| `GET /admin/<site>/upload/<screen>` | Medien hochladen (POST) |
|
| `POST /admin/<site>/upload/<screen>` | Medien hochladen |
|
||||||
| `GET /admin/<site>/add-url/<screen>` | URL zur Playlist hinzufügen (POST) |
|
| `POST /admin/<site>/add-url/<screen>` | URL zur Playlist hinzufügen |
|
||||||
| `GET /admin/<site>/delete/<screen>/<filename>` | Datei löschen (POST) |
|
| `POST /admin/<site>/delete/<screen>/<filename>` | Datei löschen |
|
||||||
| `GET /admin/<site>/playlist/<screen>` | Playlist-Reihenfolge speichern (POST JSON) |
|
| `POST /admin/<site>/playlist/<screen>` | Playlist-Reihenfolge speichern (JSON) |
|
||||||
|
| `POST /admin/<site>/delete-screen/<screen>` | Screen + Medien löschen |
|
||||||
|
| `GET /admin/<site>/add-screen?name=<name>` | Neuen Screen anlegen |
|
||||||
|
| `GET /add-site?name=<name>` | Neuen Standort anlegen |
|
||||||
|
| `POST /admin/<site>/delete-site` | Standort + alle Screens/Medien löschen |
|
||||||
| `GET /player/<site>/<screen>` | Player-Ansicht |
|
| `GET /player/<site>/<screen>` | Player-Ansicht |
|
||||||
| `GET /playlist/<site>/<screen>/hash` | Playlist-Checksumme (für Auto-Reload) |
|
| `GET /playlist/<site>/<screen>/hash` | Playlist-Checksumme (für Auto-Reload) |
|
||||||
| `GET /willkommen?site=<site>` | Willkommensseite-Formular (GET + POST) |
|
| `GET /willkommen?site=<site>` | Willkommensseite-Formular (GET + POST) |
|
||||||
@@ -138,10 +144,10 @@ docker compose up -d
|
|||||||
http://localhost:5005/admin/<standort>
|
http://localhost:5005/admin/<standort>
|
||||||
```
|
```
|
||||||
|
|
||||||
- Screens konfigurieren
|
- Screens konfigurieren (Tab-basiert: Playlist, Einstellungen, Medien)
|
||||||
- Medien hochladen / löschen
|
- Medien hochladen / löschen
|
||||||
- Playlist per Drag & Drop sortieren
|
- Playlist per Drag & Drop sortieren
|
||||||
- Priority-Playlist verwalten
|
- Priority-Playlist verwalten (ebenfalls mit Tabs)
|
||||||
- Willkommensseite generieren (bis zu 3 Kundenlogos)
|
- Willkommensseite generieren (bis zu 3 Kundenlogos)
|
||||||
- Custom-URL-Aktionsbutton pro Screen konfigurieren (iframe-Overlay oder Weiterleitung)
|
- Custom-URL-Aktionsbutton pro Screen konfigurieren (iframe-Overlay oder Weiterleitung)
|
||||||
- Standorte anlegen & löschen
|
- Standorte anlegen & löschen
|
||||||
@@ -171,6 +177,7 @@ Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"server_url": "http://signage.ccmake.de",
|
||||||
"admin": { "username": "...", "password": "..." },
|
"admin": { "username": "...", "password": "..." },
|
||||||
"sites": {
|
"sites": {
|
||||||
"stuttgart": {
|
"stuttgart": {
|
||||||
|
|||||||
7
app.py
7
app.py
@@ -625,7 +625,8 @@ def admin(site):
|
|||||||
screen_status=screen_status,
|
screen_status=screen_status,
|
||||||
version=APP_VERSION,
|
version=APP_VERSION,
|
||||||
year=datetime.now().year,
|
year=datetime.now().year,
|
||||||
hostname=os.uname().nodename
|
hostname=os.uname().nodename,
|
||||||
|
server_url=cfg.get("server_url", "")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -637,17 +638,17 @@ def admin(site):
|
|||||||
def admin_priority(site):
|
def admin_priority(site):
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
site_list = sorted(cfg.get("sites", {}).keys())
|
site_list = sorted(cfg.get("sites", {}).keys())
|
||||||
|
|
||||||
priority_files = load_priority_files()
|
priority_files = load_priority_files()
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"priority.html",
|
"priority.html",
|
||||||
site_list=site_list,
|
site_list=site_list,
|
||||||
current_site=site,
|
current_site=site,
|
||||||
|
priority_files=priority_files,
|
||||||
version=APP_VERSION,
|
version=APP_VERSION,
|
||||||
year=datetime.now().year,
|
year=datetime.now().year,
|
||||||
hostname=os.uname().nodename,
|
hostname=os.uname().nodename,
|
||||||
priority_files=priority_files
|
server_url=cfg.get("server_url", "")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"server_url": "http://localhost:5005",
|
||||||
"priority": {
|
"priority": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"playlist": [
|
"playlist": [
|
||||||
@@ -89,12 +90,5 @@
|
|||||||
"admin": {
|
"admin": {
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "cancom@2026!"
|
"password": "cancom@2026!"
|
||||||
},
|
|
||||||
"screens": {
|
|
||||||
"lobby": {
|
|
||||||
"playlist": [
|
|
||||||
"welcome.html"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2724
session-ses_1e96.md
Normal file
2724
session-ses_1e96.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -62,6 +62,14 @@
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-subtitle a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.page-subtitle a:hover {
|
||||||
|
color: var(--ccm-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.drag-handle {
|
.drag-handle {
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
@@ -71,6 +79,26 @@
|
|||||||
.drag-handle:active {
|
.drag-handle:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-body .nav-tabs {
|
||||||
|
margin-left: -1rem;
|
||||||
|
margin-right: -1rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
border-bottom-color: var(--ccm-border);
|
||||||
|
}
|
||||||
|
.card-body .nav-tabs .nav-link {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--ccm-muted);
|
||||||
|
}
|
||||||
|
.card-body .nav-tabs .nav-link.active {
|
||||||
|
background: var(--ccm-surface);
|
||||||
|
border-color: var(--ccm-border) var(--ccm-border) var(--ccm-surface);
|
||||||
|
color: var(--ccm-text);
|
||||||
|
}
|
||||||
|
.card-body .tab-content {
|
||||||
|
background: var(--ccm-surface);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -132,10 +160,8 @@
|
|||||||
<!-- Card Header -->
|
<!-- Card Header -->
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<span>
|
<span>
|
||||||
<div class="page-title">Screen:
|
<div class="page-title">{{ current_site | capitalize }}: <span class="fw-normal">Screen</span> <span class="text-uppercase">- {{ screen }}</span></div>
|
||||||
<div class="text-uppercase"> - {{ screen }} -</div>
|
<div class="page-subtitle"><a href="{{ server_url }}/player/{{ current_site }}/{{ screen }}">{{ server_url }}/player/{{ current_site }}/{{ screen }}</a></div>
|
||||||
</div>
|
|
||||||
<div class="page-subtitle">http://localhost:5005/player/{{ current_site }}/{{ screen }}</div>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="d-flex align-items-center gap-2">
|
||||||
{% if screen_status[screen] == "active" %}
|
{% if screen_status[screen] == "active" %}
|
||||||
@@ -153,8 +179,90 @@
|
|||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<!-- Einstellungen -->
|
<ul class="nav nav-tabs" data-bs-toggle="tabs" role="tablist">
|
||||||
<h2>Einstellungen</h2>
|
<li class="nav-item" role="presentation">
|
||||||
|
<a href="#tab-playlist-{{ screen }}" class="nav-link active" data-bs-toggle="tab" role="tab">
|
||||||
|
<i class="ti ti-list me-1"></i>Playlist
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a href="#settings-{{ screen }}" class="nav-link" data-bs-toggle="tab" role="tab">
|
||||||
|
<i class="ti ti-settings me-1"></i>Einstellungen
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a href="#media-{{ screen }}" class="nav-link" data-bs-toggle="tab" role="tab">
|
||||||
|
<i class="ti ti-upload me-1"></i>Medien
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content mt-3">
|
||||||
|
|
||||||
|
<!-- Tab: Playlist -->
|
||||||
|
<div class="tab-pane active" id="tab-playlist-{{ screen }}" role="tabpanel">
|
||||||
|
<ul class="list-group mb-3" id="playlist-{{ screen }}">
|
||||||
|
{% for file in media_files[screen] %}
|
||||||
|
<li class="list-group-item playlist-row"
|
||||||
|
data-file="{{ file.name }}">
|
||||||
|
|
||||||
|
<input class="form-check-input playlist-enabled"
|
||||||
|
type="checkbox"
|
||||||
|
{% if file.enabled %}checked{% endif %}
|
||||||
|
title="Element aktivieren">
|
||||||
|
|
||||||
|
<span class="playlist-name"
|
||||||
|
{% if file.type == "image" %}
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-html="true"
|
||||||
|
title="<img src='/media/{{ current_site }}/{{ screen }}/{{ file.name }}' style='max-width: 200px; max-height: 150px; border: none; background: white; padding: 2px;' alt='Vorschau'>"
|
||||||
|
{% elif file.type == "url" %}
|
||||||
|
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 %}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{% if file.type == "image" %}
|
||||||
|
<span class="badge bg-purple text-purple-fg">Bild</span>
|
||||||
|
{% elif file.type == "url" or file.type == "html" %}
|
||||||
|
<span class="badge bg-blue text-blue-fg">URL</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge bg-orange text-orange-fg">Video</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span class="playlist-size">
|
||||||
|
{% if file.type == "url" %}
|
||||||
|
{{ file.size }}
|
||||||
|
{% else %}
|
||||||
|
{{ file.size }} KB
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Delete -->
|
||||||
|
<form action="{{ url_for('delete_file', site=current_site, screen=screen, filename=file.name) }}"
|
||||||
|
method="post">
|
||||||
|
<button type="submit"
|
||||||
|
class="btn btn-outline-danger btn-sm"
|
||||||
|
onclick="return confirm('Datei wirklich löschen?')">
|
||||||
|
Löschen
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Drag -->
|
||||||
|
<span class="drag-handle" title="Reihenfolge ändern">⇅</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
onclick="savePlaylist('{{ current_site }}', '{{ screen }}')">
|
||||||
|
Playlist speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab: Einstellungen -->
|
||||||
|
<div class="tab-pane" id="settings-{{ screen }}" role="tabpanel">
|
||||||
<form action="/admin/{{ current_site }}/update/{{ screen }}" method="post">
|
<form action="/admin/{{ current_site }}/update/{{ screen }}" method="post">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<label class="form-label">Intervall (Sekunden)</label>
|
<label class="form-label">Intervall (Sekunden)</label>
|
||||||
@@ -218,11 +326,11 @@
|
|||||||
Einstellungen speichern
|
Einstellungen speichern
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<!-- Tab: Medien -->
|
||||||
|
<div class="tab-pane" id="media-{{ screen }}" role="tabpanel">
|
||||||
<!-- Upload -->
|
<h3>Medien hochladen</h3>
|
||||||
<h2>Medien hochladen / URL hinzufügen - {{ screen }}</h2>
|
|
||||||
<form action="/admin/{{ current_site }}/upload/{{ screen }}"
|
<form action="/admin/{{ current_site }}/upload/{{ screen }}"
|
||||||
method="post" enctype="multipart/form-data">
|
method="post" enctype="multipart/form-data">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
@@ -231,6 +339,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h3>URL hinzufügen</h3>
|
||||||
<form action="/admin/{{ current_site }}/add-url/{{ screen }}" method="post" class="mb-4">
|
<form action="/admin/{{ current_site }}/add-url/{{ screen }}" method="post" class="mb-4">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<label class="form-label">URL</label>
|
<label class="form-label">URL</label>
|
||||||
@@ -241,72 +351,11 @@
|
|||||||
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
|
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
|
||||||
<input type="number" name="zoom" class="form-control" step="0.1" value="1.0" min="0.1">
|
<input type="number" name="zoom" class="form-control" step="0.1" value="1.0" min="0.1">
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-secondary" type="submit">URL hinzufügen</button>
|
<button class="btn btn-primary" type="submit">URL hinzufügen</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
</div>
|
||||||
|
|
||||||
<!-- Playlist -->
|
|
||||||
<h2>Playlist Reihenfolge - {{ screen }}</h2>
|
|
||||||
|
|
||||||
<ul class="list-group mb-3" id="playlist-{{ screen }}">
|
|
||||||
{% for file in media_files[screen] %}
|
|
||||||
<li class="list-group-item playlist-row"
|
|
||||||
data-file="{{ file.name }}">
|
|
||||||
|
|
||||||
<input class="form-check-input playlist-enabled"
|
|
||||||
type="checkbox"
|
|
||||||
{% if file.enabled %}checked{% endif %}
|
|
||||||
title="Element aktivieren">
|
|
||||||
|
|
||||||
<span class="playlist-name"
|
|
||||||
{% if file.type == "image" %}
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-html="true"
|
|
||||||
title="<img src='/media/{{ current_site }}/{{ screen }}/{{ file.name }}' style='max-width: 200px; max-height: 150px; border: none; background: white; padding: 2px;' alt='Vorschau'>"
|
|
||||||
{% elif file.type == "url" %}
|
|
||||||
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 %}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{% if file.type == "image" %}
|
|
||||||
<span class="badge bg-purple text-purple-fg">Bild</span>
|
|
||||||
{% elif file.type == "url" or file.type == "html" %}
|
|
||||||
<span class="badge bg-blue text-blue-fg">URL</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="badge bg-orange text-orange-fg">Video</span>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<span class="playlist-size">
|
|
||||||
{% if file.type == "url" %}
|
|
||||||
{{ file.size }}
|
|
||||||
{% else %}
|
|
||||||
{{ file.size }} KB
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- Delete -->
|
|
||||||
<form action="{{ url_for('delete_file', site=current_site, screen=screen, filename=file.name) }}"
|
|
||||||
method="post">
|
|
||||||
<button type="submit"
|
|
||||||
class="btn btn-outline-danger btn-sm"
|
|
||||||
onclick="return confirm('Datei wirklich löschen?')">
|
|
||||||
Löschen
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- Drag -->
|
|
||||||
<span class="drag-handle" title="Reihenfolge ändern">⇅</span>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<button class="btn btn-primary"
|
|
||||||
onclick="savePlaylist('{{ current_site }}', '{{ screen }}')">
|
|
||||||
Playlist speichern
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -55,6 +55,26 @@
|
|||||||
.drag-handle:active {
|
.drag-handle:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-body .nav-tabs {
|
||||||
|
margin-left: -1rem;
|
||||||
|
margin-right: -1rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
border-bottom-color: var(--ccm-border);
|
||||||
|
}
|
||||||
|
.card-body .nav-tabs .nav-link {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--ccm-muted);
|
||||||
|
}
|
||||||
|
.card-body .nav-tabs .nav-link.active {
|
||||||
|
background: var(--ccm-surface);
|
||||||
|
border-color: var(--ccm-border) var(--ccm-border) var(--ccm-surface);
|
||||||
|
color: var(--ccm-text);
|
||||||
|
}
|
||||||
|
.card-body .tab-content {
|
||||||
|
background: var(--ccm-surface);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -95,40 +115,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="text-muted">
|
|
||||||
Inhalte dieser Playlist werden <strong>auf allen Playern</strong>
|
|
||||||
zusätzlich zur normalen Playlist angezeigt.
|
|
||||||
<br>
|
|
||||||
Die Wiedergabe erfolgt <strong>abwechselnd</strong>
|
|
||||||
(Normal → Priority → Normal → …).
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- Upload -->
|
<ul class="nav nav-tabs" data-bs-toggle="tabs" role="tablist">
|
||||||
<h4>Medien hochladen</h4>
|
<li class="nav-item" role="presentation">
|
||||||
<form action="/admin/{{ current_site }}/upload/priority"
|
<a href="#tab-prio-playlist" class="nav-link active" data-bs-toggle="tab" role="tab">
|
||||||
method="post" enctype="multipart/form-data">
|
<i class="ti ti-list me-1"></i>Playlist
|
||||||
<div class="input-group mb-3">
|
</a>
|
||||||
<input type="file" name="file" class="form-control" required>
|
</li>
|
||||||
<button class="btn btn-primary" type="submit">Upload</button>
|
<li class="nav-item" role="presentation">
|
||||||
</div>
|
<a href="#tab-prio-media" class="nav-link" data-bs-toggle="tab" role="tab">
|
||||||
</form>
|
<i class="ti ti-upload me-1"></i>Medien
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<form action="/admin/{{ current_site }}/add-url/priority" method="post" class="mb-3">
|
<div class="tab-content">
|
||||||
<div class="mb-2">
|
|
||||||
<label class="form-label">URL</label>
|
|
||||||
<input type="url" name="url" class="form-control"
|
|
||||||
placeholder="https://example.com" required>
|
|
||||||
</div>
|
|
||||||
<div class="mb-2">
|
|
||||||
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
|
|
||||||
<input type="number" name="zoom" class="form-control" step="0.1" value="1.0" min="0.1">
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-secondary" type="submit">URL hinzufügen</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- Playlist -->
|
|
||||||
<h4>Playlist Reihenfolge</h4>
|
|
||||||
|
|
||||||
|
<!-- Tab: Playlist -->
|
||||||
|
<div class="tab-pane active" id="tab-prio-playlist" role="tabpanel">
|
||||||
<ul class="list-group mb-3" id="playlist-priority">
|
<ul class="list-group mb-3" id="playlist-priority">
|
||||||
{% for file in priority_files %}
|
{% for file in priority_files %}
|
||||||
<li class="list-group-item playlist-row"
|
<li class="list-group-item playlist-row"
|
||||||
@@ -189,6 +193,36 @@
|
|||||||
onclick="savePlaylist('{{ current_site }}', 'priority')">
|
onclick="savePlaylist('{{ current_site }}', 'priority')">
|
||||||
Priority-Playlist speichern
|
Priority-Playlist speichern
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab: Medien -->
|
||||||
|
<div class="tab-pane" id="tab-prio-media" role="tabpanel">
|
||||||
|
<h3>Medien hochladen</h3>
|
||||||
|
<form action="/admin/{{ current_site }}/upload/priority"
|
||||||
|
method="post" enctype="multipart/form-data">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="file" name="file" class="form-control" required>
|
||||||
|
<button class="btn btn-primary" type="submit">Upload</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h3>URL hinzufügen</h3>
|
||||||
|
<form action="/admin/{{ current_site }}/add-url/priority" method="post" class="mb-3">
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">URL</label>
|
||||||
|
<input type="url" name="url" class="form-control"
|
||||||
|
placeholder="https://example.com" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
|
||||||
|
<input type="number" name="zoom" class="form-control" step="0.1" value="1.0" min="0.1">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" type="submit">URL hinzufügen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user