diff --git a/app.py b/app.py index f6e2c14..d47130d 100755 --- a/app.py +++ b/app.py @@ -1575,6 +1575,45 @@ def save_playlist(site, screen): return "", 204 +# ------------------------------------------------- +# Admin: Playlist-Item bearbeiten (URL + Zoom) +# ------------------------------------------------- +@app.route("/admin//update-playlist-item/", methods=["POST"]) +@site_access_required +def update_playlist_item(site, screen): + """Aktualisiert URL und Zoom eines einzelnen Playlist-Items.""" + data = request.get_json() + old_name = data.get("old_name") + new_url = data.get("new_url", "").strip() + zoom = data.get("zoom", 1.0) + + if not is_url(new_url): + return jsonify({"error": "Ungültige URL"}), 400 + + try: + zoom = float(zoom) + if zoom <= 0: + zoom = 1.0 + except (ValueError, TypeError): + zoom = 1.0 + + config = load_config() + + if screen == "priority": + playlist = config.setdefault("priority", {}).setdefault("playlist", []) + else: + playlist = config["sites"][site]["screens"][screen].setdefault("playlist", []) + + for i, item in enumerate(playlist): + name = playlist_item_name(item) + if name == old_name: + playlist[i] = {"url": new_url, "zoom": zoom} + save_config(config) + return jsonify({"success": True}) + + return jsonify({"error": "Item nicht gefunden"}), 404 + + # ------------------------------------------------- # Change-Password (für First-Login / Reset) # ------------------------------------------------- diff --git a/templates/admin.html b/templates/admin.html index c1b0770..e620ead 100755 --- a/templates/admin.html +++ b/templates/admin.html @@ -43,11 +43,11 @@ background-color: #ffffff; } - /* ─── Playlist-Zeile als Grid (Checkbox | Name | Typ | Größe | Löschen | Drag) ─── */ + /* ─── Playlist-Zeile als Grid (Checkbox | Name | Typ | Größe | Edit | Löschen | Drag) ─── */ .playlist-row { display: grid; grid-template-columns: - 28px 1fr 90px 80px 90px 32px; + 28px 1fr 90px 80px 40px 50px 32px; align-items: center; gap: 0.75rem; } @@ -271,6 +271,18 @@ {% endif %} + + {% if file.type == "url" %} + + {% endif %} +
@@ -577,8 +589,138 @@ document.addEventListener("DOMContentLoaded", function () { return new bootstrap.Tooltip(tooltipTriggerEl); }); +/* ─── Playlist-Item bearbeiten (URL + Zoom) ─── */ +var editState = { site: "", screen: "", oldName: "" }; + +function editPlaylistItem(site, screen, oldName, zoom) { + editState.site = site; + editState.screen = screen; + editState.oldName = oldName; + document.getElementById("edit-url-input").value = oldName; + document.getElementById("edit-zoom-input").value = zoom; + document.getElementById("edit-url-error").style.display = "none"; + new bootstrap.Modal(document.getElementById("edit-url-modal")).show(); +} + +document.addEventListener("DOMContentLoaded", function () { + + /* ─── SortableJS Drag & Drop initialisieren ─── */ + function initSortable(name) { + const el = document.getElementById("playlist-" + name); + if (!el || typeof Sortable === "undefined") return; + + new Sortable(el, { + animation: 150, + handle: ".drag-handle", + ghostClass: "bg-light", + chosenClass: "bg-light" + }); + } + + // Für jeden Screen eine Sortable-Instanz + {% for screen in screens.keys() %} + initSortable("{{ screen }}"); + {% endfor %} + + /* ─── Playlist-Reihenfolge speichern ─── */ + function savePlaylist(site, screen) { + const items = document.querySelectorAll( + "#playlist-" + screen + " li" + ); + const playlist = []; + + const enabled = {}; + items.forEach((item) => { + const checkbox = item.querySelector(".playlist-enabled"); + const fileName = item.dataset.file; + enabled[fileName] = !!(checkbox && checkbox.checked); + playlist.push(fileName); + }); + + fetch("/admin/" + site + "/playlist/" + screen, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + playlist: playlist, + enabled: enabled + }) + }).then(() => location.reload()); + } + + window.savePlaylist = savePlaylist; + + /* ─── Bootstrap Tooltips initialisieren ─── */ + var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl); + }); + + /* ─── Edit-URL-Modal: Speichern-Button ─── */ + document.getElementById("edit-url-save").addEventListener("click", function () { + const newUrl = document.getElementById("edit-url-input").value.trim(); + const zoom = parseFloat(document.getElementById("edit-zoom-input").value) || 1.0; + const errorEl = document.getElementById("edit-url-error"); + + if (!newUrl.startsWith("http://") && !newUrl.startsWith("https://")) { + errorEl.textContent = "Ungültige URL (muss mit http:// oder https:// beginnen)"; + errorEl.style.display = "block"; + return; + } + + errorEl.style.display = "none"; + + fetch("/admin/" + editState.site + "/update-playlist-item/" + editState.screen, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + old_name: editState.oldName, + new_url: newUrl, + zoom: zoom + }) + }).then(function (r) { + if (r.ok) { + location.reload(); + } else { + return r.json().then(function (d) { + errorEl.textContent = d.error || "Fehler beim Speichern"; + errorEl.style.display = "block"; + }); + } + }).catch(function () { + errorEl.textContent = "Netzwerkfehler"; + errorEl.style.display = "block"; + }); + }); + }); + + + diff --git a/templates/priority.html b/templates/priority.html index 7bb8e9d..8b0952b 100644 --- a/templates/priority.html +++ b/templates/priority.html @@ -29,7 +29,7 @@ .playlist-row { display: grid; grid-template-columns: - 28px 1fr 90px 80px 90px 32px; + 28px 1fr 90px 80px 40px 50px 32px; align-items: center; gap: 0.75rem; } @@ -171,6 +171,18 @@ {% endif %} + + {% if file.type == "url" %} + + {% endif %} + + + +