status features

This commit is contained in:
Ondrej Zara 2019-03-20 16:20:17 +01:00
parent 17b5040766
commit 9f14bc261a
5 changed files with 94 additions and 42 deletions

View file

@ -36,12 +36,14 @@
<span class="artist"></span> <span class="artist"></span>
<span class="album"></span> <span class="album"></span>
<span class="time1"></span>/<span class="time2"></span> <span class="time1"></span>/<span class="time2"></span>
<button class="status"></button> <span class="status">
<button class="stop"></button> <button class="play">play</button>
<button class="prev"></button> <button class="pause">pause</button>
<button class="next"></button> </span>
<button class="repeat"></button> <button class="prev">prev</button>
<button class="random"></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

@ -6,11 +6,6 @@ async function init() {
await mpd.init(); await mpd.init();
status.init(); status.init();
window.mpd = mpd; window.mpd = mpd;
art.get("NAS/ABBA/Greatest Hits/01 Dancing Queen.mp3").then(src => {
let image = document.createElement("img");
image.src = src;
document.querySelector("main").appendChild(image);
});
} }
init(); init();

View file

@ -10,8 +10,6 @@ async function getImageData(songUrl) {
while (1) { while (1) {
let params = ["albumart", `"${mpd.escape(songUrl)}"`, offset]; let params = ["albumart", `"${mpd.escape(songUrl)}"`, offset];
let lines = await mpd.command(params.join(" ")); let lines = await mpd.command(params.join(" "));
if (lines.length == 1) { return null; }
data = data.concat(lines[2]); data = data.concat(lines[2]);
let metadata = parser.linesToStruct(lines.slice(0, 2)); let metadata = parser.linesToStruct(lines.slice(0, 2));
if (data.length >= Number(metadata["size"])) { return data; } if (data.length >= Number(metadata["size"])) { return data; }
@ -38,14 +36,27 @@ async function resize(image) {
return new Promise(resolve => canvas.toBlob(resolve)); return new Promise(resolve => canvas.toBlob(resolve));
} }
export async function get(songUrl) { export async function get(artist, album, songUrl = null) {
if (songUrl in cache) { return cache[songUrl]; } let key = `${artist}-${album}`;
if (key in cache) { return cache[key]; }
let data = await getImageData(songUrl); if (!songUrl) { return null; }
let bytes = new Uint8Array(data);
let image = await bytesToImage(bytes); // promise to be returned in the meantime
let blob = await resize(image); let resolve;
let url = URL.createObjectURL(blob); let promise = new Promise(res => resolve = res);
cache[songUrl] = url; cache[key] = promise;
return url;
try {
let data = await getImageData(songUrl);
let bytes = new Uint8Array(data);
let image = await bytesToImage(bytes);
let blob = await resize(image);
let url = URL.createObjectURL(blob);
cache[key] = url;
resolve(url);
} catch (e) {
cache[key] = null;
}
return cache[key];
} }

View file

@ -2,12 +2,18 @@ import * as parser from "./parser.js";
let ws; let ws;
let commandQueue = []; let commandQueue = [];
let pendingResolve; let current;
function onMessage(e) { function onMessage(e) {
if (pendingResolve) { if (current) {
pendingResolve(JSON.parse(e.data)); // FIXME tady test na ACK let lines = JSON.parse(e.data);
pendingResolve = null; let last = lines.pop();
if (last.startsWith("OK")) {
current.resolve(lines);
} else {
current.reject(last);
}
current = null;
} }
processQueue(); processQueue();
} }
@ -23,11 +29,9 @@ function onClose(e) {
} }
function processQueue() { function processQueue() {
if (pendingResolve || commandQueue.length == 0) { return; } if (current || commandQueue.length == 0) { return; }
let {cmd, resolve} = commandQueue.shift(); current = commandQueue.shift();
pendingResolve = resolve; ws.send(current.cmd);
if (cmd instanceof Array) { cmd = ["command_list_begin", ...cmd, "command_list_end"].join("\n"); }
ws.send(cmd);
} }
export function escape(str) { export function escape(str) {
@ -35,24 +39,30 @@ export function escape(str) {
} }
export async function command(cmd) { export async function command(cmd) {
return new Promise(resolve => { if (cmd instanceof Array) { cmd = ["command_list_begin", ...cmd, "command_list_end"].join("\n"); }
commandQueue.push({cmd, resolve});
return new Promise((resolve, reject) => {
commandQueue.push({cmd, resolve, reject});
processQueue(); processQueue();
}); });
} }
export async function getStatus() { export async function commandAndStatus(cmd) {
let lines = await command([cmd, "status", "currentsong"]);
return parser.linesToStruct(lines);
}
export async function status() {
let lines = await command(["status", "currentsong"]); let lines = await command(["status", "currentsong"]);
lines.pop(); // "OK"
return parser.linesToStruct(lines); return parser.linesToStruct(lines);
} }
export async function init() { export async function init() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
ws = new WebSocket("ws://localhost:8080?server=raspberrypi.local"); ws = new WebSocket("ws://localhost:8080");
} catch (e) { reject(e); } } catch (e) { reject(e); }
pendingResolve = resolve; current = {resolve, reject};
ws.addEventListener("error", onError); ws.addEventListener("error", onError);
ws.addEventListener("message", onMessage); ws.addEventListener("message", onMessage);

View file

@ -1,18 +1,52 @@
import * as mpd from "./mpd.js"; import * as mpd from "./mpd.js";
import * as art from "./art.js";
const DELAY = 2000; const DELAY = 2000;
const DOM = {}; const DOM = {};
let current = {};
async function tick() { async function tick() {
let data = await mpd.getStatus(); let data = await mpd.status();
DOM.title.textContent = data["Title"];
update(data);
// console.log(data); // console.log(data);
setTimeout(tick, DELAY); 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() { export function init() {
let node = document.querySelector("footer"); let all = document.querySelectorAll("footer [class]");
DOM.title = node.querySelector(".title"); Array.from(all).forEach(node => DOM[node.className] = node);
DOM.play.addEventListener("click", e => play());
DOM.pause.addEventListener("click", e => pause());
tick(); tick();
} }