This commit is contained in:
Ondrej Zara 2019-03-21 10:32:58 +01:00
parent 9f14bc261a
commit 28b8d4ad84
10 changed files with 143 additions and 106 deletions

View file

@ -4,26 +4,33 @@ body {
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
} }
header nav ul { main {
flex-grow: 1;
overflow-x: hidden;
overflow-y: auto;
}
nav ul {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
header nav ul li { nav ul li {
text-align: center; text-align: center;
flex-grow: 1; flex-grow: 1;
line-height: 40px; line-height: 40px;
} }
header nav ul li:hover { nav ul li:hover {
background-color: red; background-color: red;
} }
main { #player:not([data-state=play]) .pause {
flex-grow: 1; display: none;
overflow-x: hidden;
overflow-y: auto;
} }
footer { #player[data-state=play] .play {
flex-basis: 40px; display: none;
}
#player:not(.random) .random,
#player:not(.repeat) .repeat {
opacity: 0.5;
} }

View file

@ -5,6 +5,6 @@ body {
height: 100vh; height: 100vh;
} }
@import "header.less";
@import "main.less"; @import "main.less";
@import "footer.less"; @import "nav.less";
@import "player.less";

View file

@ -1,3 +0,0 @@
footer {
flex-basis: 40px;
}

View file

@ -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;}
}
}
}

15
app/css/nav.less Normal file
View file

@ -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;}
}
}

6
app/css/player.less Normal file
View file

@ -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; }
}

View file

@ -8,14 +8,19 @@
</head> </head>
<body> <body>
<header> <header>
<nav> <section id="player">
<ul> <span class="art"></span>
<li>Q</li> <span class="title"></span>
<li>Playlists</li> <span class="artist"></span>
<li>Library</li> <span class="album"></span>
<li>Misc</li> <span class="elapsed"></span>/<span class="duration"></span>
</ul> <button class="play">⏯️▶</button>
</nav> <button class="pause">⏸️</button>
<button class="prev">⏮ ⏮️</button>
<button class="next">⏭️</button>
<button class="repeat">🔁</button>
<button class="random">🔀</button>
</section>
</header> </header>
<main> <main>
main<br/> main<br/>
@ -31,19 +36,14 @@
main<br/> main<br/>
</main> </main>
<footer> <footer>
<span class="art"></span> <nav>
<span class="title"></span> <ul>
<span class="artist"></span> <li>Q</li>
<span class="album"></span> <li>Playlists</li>
<span class="time1"></span>/<span class="time2"></span> <li>Library</li>
<span class="status"> <li>Misc</li>
<button class="play">play</button> </ul>
<button class="pause">pause</button> </nav>
</span>
<button class="prev">prev</button>
<button class="next">next</button>
<button class="repeat">repeat</button>
<button class="random">random</button>
</footer> </footer>
<script type="module" src="js/app.js"></script> <script type="module" src="js/app.js"></script>
</body> </body>

View file

@ -1,10 +1,10 @@
import * as mpd from "./mpd.js"; import * as mpd from "./mpd.js";
import * as status from "./status.js"; import * as player from "./player.js";
import * as art from "./art.js"; import * as art from "./art.js";
async function init() { async function init() {
await mpd.init(); await mpd.init();
status.init(); player.init();
window.mpd = mpd; window.mpd = mpd;
} }

81
app/js/player.js Normal file
View file

@ -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();
}

View file

@ -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();
}