// deck-it - music panel // Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. function clock(s) { s = Math.max(0, Math.floor(s || 0)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; } async function post(action, body = {}) { try { await fetch(`/api/music/${action}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); } catch { // the next poll shows the real state } } function button(label, action) { const b = document.createElement("button"); b.textContent = label; b.onclick = () => post(action); return b; } export default { title: "Music", interval: 2, mount(el) { el.innerHTML = `
♪
Nothing playing
0:00
`; el.querySelector(".controls").append( button("⏮", "prev"), button("⏯", "play"), button("⏭", "next"), button("Vol−", "voldown"), button("Mute", "mute"), button("Vol+", "volup")); const seek = el.querySelector(".seek input"); seek.addEventListener("pointerdown", () => { el._dragging = true; }); seek.addEventListener("pointerup", () => { el._dragging = false; }); seek.addEventListener("change", () => post("seek", { position: Number(seek.value) })); }, update(st, el) { if (st.art) { let img = el.querySelector("img.art"); if (!img) { img = document.createElement("img"); img.className = "art"; img.alt = ""; el.querySelector(".art").replaceWith(img); } if (img.getAttribute("src") !== st.art) img.src = st.art; } el.querySelector(".title").textContent = st.title || "Nothing playing"; el.querySelector(".artist").textContent = st.artist || ""; el.querySelector(".album").textContent = st.album || ""; el.querySelector(".controls button:nth-child(2)").textContent = st.status === "Playing" ? "⏸" : "▶"; const mute = el.querySelector(".controls button:nth-child(5)"); mute.classList.toggle("on", Boolean(st.volume?.muted)); mute.textContent = st.volume?.muted ? "Unmute" : "Mute"; const seek = el.querySelector(".seek input"); seek.max = Math.max(1, Math.floor(st.length || 0)); if (!el._dragging) seek.value = Math.min(Number(seek.max), Math.floor(st.position || 0)); el.querySelector(".time").textContent = `${clock(st.position)} / ${clock(st.length)}`; }, };