[web] Allow removing playlists
This commit is contained in:
@ -46,11 +46,35 @@
|
||||
border-radius: var(--radius);
|
||||
background: var(--card);
|
||||
font-size: 0.875rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.sidebar .playlist-item .info {
|
||||
overflow: hidden;
|
||||
}
|
||||
.sidebar .playlist-item .info .name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sidebar .playlist-item .count {
|
||||
color: var(--muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.sidebar .playlist-item .del-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
padding: 0.25rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.sidebar .playlist-item .del-btn:hover {
|
||||
color: #f87171;
|
||||
}
|
||||
.sidebar .empty {
|
||||
color: var(--muted);
|
||||
font-size: 0.875rem;
|
||||
@ -249,10 +273,16 @@ async function loadStatus() {
|
||||
}
|
||||
list.innerHTML = data.smart_playlists.map(p =>
|
||||
`<div class="playlist-item">
|
||||
<div>${esc(p.name)}</div>
|
||||
<div class="info">
|
||||
<div class="name">${esc(p.name)}</div>
|
||||
<div class="count">${p.tracks} track${p.tracks === 1 ? "" : "s"}</div>
|
||||
</div>
|
||||
<button class="del-btn" data-uri="${esc(p.uri)}" title="Delete playlist">✕</button>
|
||||
</div>`
|
||||
).join("");
|
||||
list.querySelectorAll(".del-btn").forEach(btn => {
|
||||
btn.onclick = () => deletePlaylist(btn.dataset.uri);
|
||||
});
|
||||
} catch {
|
||||
list.innerHTML = '<div class="empty">Could not load playlists</div>';
|
||||
}
|
||||
@ -405,6 +435,17 @@ async function refreshPlaylists() {
|
||||
}
|
||||
}
|
||||
|
||||
async function deletePlaylist(uri) {
|
||||
if (!confirm('Delete "' + uri + '"?')) return;
|
||||
try {
|
||||
await api("POST", "/delete", { uri });
|
||||
showToast("Deleted", "success");
|
||||
loadStatus();
|
||||
} catch (e) {
|
||||
showToast("Delete failed: " + e, "error");
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => { loadStatus(); loadSqStatus(); });
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@ -326,6 +326,26 @@ class StatusHandler(tornado.web.RequestHandler):
|
||||
self.write({"smart_playlists": smart, "count": len(smart)})
|
||||
|
||||
|
||||
class PlaylistDeleteHandler(tornado.web.RequestHandler):
|
||||
def initialize(self, core: CoreProxy) -> None:
|
||||
self.core = core
|
||||
|
||||
def post(self) -> None:
|
||||
data = json.loads(self.request.body)
|
||||
uri = data.get("uri", "")
|
||||
if not uri:
|
||||
self.set_status(400)
|
||||
self.write({"error": "Missing 'uri' in request body"})
|
||||
return
|
||||
try:
|
||||
self.core.playlists.delete(uri).get()
|
||||
self.write({"status": "ok"})
|
||||
except Exception as e:
|
||||
logger.exception("Failed to delete playlist %s", uri)
|
||||
self.set_status(500)
|
||||
self.write({"error": str(e)})
|
||||
|
||||
|
||||
class SmartQueueControlHandler(tornado.web.RequestHandler):
|
||||
def initialize(self) -> None:
|
||||
pass
|
||||
@ -395,5 +415,6 @@ def app_factory(config: Config, core: CoreProxy) -> list[tuple]:
|
||||
{"core": core, "uris": uris, "variety_chance": 0.15}),
|
||||
(r"/refresh", RefreshHandler, {"core": core, "config": config}),
|
||||
(r"/status", StatusHandler, {"core": core, "prefix": prefix}),
|
||||
(r"/delete", PlaylistDeleteHandler, {"core": core}),
|
||||
(r"/smart-queue", SmartQueueControlHandler, {}),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user