diff --git a/app/app.css b/app/app.css index bf917cf..8750517 100644 --- a/app/app.css +++ b/app/app.css @@ -4,26 +4,33 @@ body { flex-direction: column; height: 100vh; } -header nav ul { +main { + flex-grow: 1; + overflow-x: hidden; + overflow-y: auto; +} +nav ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: row; } -header nav ul li { +nav ul li { text-align: center; flex-grow: 1; line-height: 40px; } -header nav ul li:hover { +nav ul li:hover { background-color: red; } -main { - flex-grow: 1; - overflow-x: hidden; - overflow-y: auto; +#player:not([data-state=play]) .pause { + display: none; } -footer { - flex-basis: 40px; +#player[data-state=play] .play { + display: none; +} +#player:not(.random) .random, +#player:not(.repeat) .repeat { + opacity: 0.5; } diff --git a/app/css/app.less b/app/css/app.less index c4415c7..7bac648 100644 --- a/app/css/app.less +++ b/app/css/app.less @@ -5,6 +5,6 @@ body { height: 100vh; } -@import "header.less"; @import "main.less"; -@import "footer.less"; +@import "nav.less"; +@import "player.less"; diff --git a/app/css/footer.less b/app/css/footer.less deleted file mode 100644 index 6c87a64..0000000 --- a/app/css/footer.less +++ /dev/null @@ -1,3 +0,0 @@ -footer { - flex-basis: 40px; -} diff --git a/app/css/header.less b/app/css/header.less deleted file mode 100644 index a956045..0000000 --- a/app/css/header.less +++ /dev/null @@ -1,17 +0,0 @@ -header { - nav ul { - margin: 0; - padding: 0; - list-style: none; - display: flex; - flex-direction: row; - - li { - text-align: center; - flex-grow: 1; - line-height: 40px; - - &:hover { background-color:red;} - } - } -} diff --git a/app/css/nav.less b/app/css/nav.less new file mode 100644 index 0000000..7e653fc --- /dev/null +++ b/app/css/nav.less @@ -0,0 +1,15 @@ +nav ul { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: row; + + li { + text-align: center; + flex-grow: 1; + line-height: 40px; + + &:hover { background-color:red;} + } +} diff --git a/app/css/player.less b/app/css/player.less new file mode 100644 index 0000000..eac23f6 --- /dev/null +++ b/app/css/player.less @@ -0,0 +1,6 @@ +#player { + &:not([data-state=play]) .pause { display: none; } + &[data-state=play] .play { display: none; } + + &:not(.random) .random, &:not(.repeat) .repeat { opacity: 0.5; } +} \ No newline at end of file diff --git a/app/index.html b/app/index.html index bad3dee..29d15f1 100644 --- a/app/index.html +++ b/app/index.html @@ -8,14 +8,19 @@
- +
+ + + + + / + + + + + + +
main
@@ -31,19 +36,14 @@ main
diff --git a/app/js/app.js b/app/js/app.js index f7e4164..494d942 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -1,10 +1,10 @@ import * as mpd from "./mpd.js"; -import * as status from "./status.js"; +import * as player from "./player.js"; import * as art from "./art.js"; async function init() { await mpd.init(); - status.init(); + player.init(); window.mpd = mpd; } diff --git a/app/js/player.js b/app/js/player.js new file mode 100644 index 0000000..1104e58 --- /dev/null +++ b/app/js/player.js @@ -0,0 +1,81 @@ +import * as mpd from "./mpd.js"; +import * as art from "./art.js"; + +const DELAY = 2000; +const DOM = {}; + +let current = {}; +let node; +let idleTimeout = null; + +function formatTime(sec) { + sec = Math.round(sec); + let m = Math.floor(sec / 60); + let s = sec % 60; + return `${m}:${s.toString().padStart(2, "0")}`; +} + +function update(data) { + DOM.elapsed.textContent = formatTime(Number(data["elapsed"] || 0)); // changed time + + if (data["file"] != current["file"]) { // changed song + DOM.duration.textContent = formatTime(Number(data["duration"] || 0)); + DOM.title.textContent = data["Title"] || ""; + DOM.album.textContent = data["Album"] || ""; + DOM.artist.textContent = data["Artist"] || ""; + } + + if (data["Artist"] != current["Artist"] || data["Album"] != current["Album"]) { // changed album (art) + DOM.art.innerHTML = ""; + art.get(data["Artist"], data["Album"], data["file"]).then(src => { + if (!src) { return; } + let image = document.createElement("img"); + image.src = src; + DOM.art.appendChild(image); + }); + } + + node.classList.toggle("random", data["random"] == "1"); + node.classList.toggle("repeat", data["repeat"] == "1"); + node.dataset.state = data["state"]; + + current = data; +} + +async function sync() { + let data = await mpd.status(); + update(data); + idle(); +} + +function idle() { + idleTimeout = setTimeout(sync, DELAY); +} + +function clearIdle() { + idleTimeout && clearTimeout(idleTimeout); + idleTimeout = null; +} + +async function command(cmd) { + clearIdle(); + let data = await mpd.commandAndStatus(cmd); + update(data); + idle(); +} + +export function init() { + node = document.querySelector("#player"); + let all = node.querySelectorAll("[class]"); + Array.from(all).forEach(node => DOM[node.className] = node); + + DOM.play.addEventListener("click", e => command("play")); + DOM.pause.addEventListener("click", e => command("pause 1")); + DOM.prev.addEventListener("click", e => command("previous")); + DOM.next.addEventListener("click", e => command("next")); + + DOM.random.addEventListener("click", e => command(`random ${current["random"] == "1" ? "0" : "1"}`)); + DOM.repeat.addEventListener("click", e => command(`repeat ${current["repeat"] == "1" ? "0" : "1"}`)); + + sync(); +} diff --git a/app/js/status.js b/app/js/status.js deleted file mode 100644 index 33afa16..0000000 --- a/app/js/status.js +++ /dev/null @@ -1,52 +0,0 @@ -import * as mpd from "./mpd.js"; -import * as art from "./art.js"; - -const DELAY = 2000; -const DOM = {}; - -let current = {}; - -async function tick() { - let data = await mpd.status(); - - update(data); - -// console.log(data); - setTimeout(tick, DELAY); -} - -function update(data) { - if (data["Title"] != current["Title"]) { DOM.title.textContent = data["Title"]; } - - if (data["Artist"] != current["Artist"] || data["Album"] != current["Album"]) { - DOM.art.innerHTML = ""; - art.get(data["Artist"], data["Album"], data["file"]).then(src => { - if (!src) { return; } - let image = document.createElement("img"); - image.src = src; - DOM.art.appendChild(image); - }); - } - - current = data; -} - -async function play() { - let data = await mpd.commandAndStatus("pause 0"); - update(data); -} - -async function pause() { - let data = await mpd.commandAndStatus("pause 1"); - update(data); -} - -export function init() { - let all = document.querySelectorAll("footer [class]"); - Array.from(all).forEach(node => DOM[node.className] = node); - - DOM.play.addEventListener("click", e => play()); - DOM.pause.addEventListener("click", e => pause()); - - tick(); -}