status features
This commit is contained in:
parent
17b5040766
commit
9f14bc261a
5 changed files with 94 additions and 42 deletions
|
@ -36,12 +36,14 @@
|
|||
<span class="artist"></span>
|
||||
<span class="album"></span>
|
||||
<span class="time1"></span>/<span class="time2"></span>
|
||||
<button class="status"></button>
|
||||
<button class="stop"></button>
|
||||
<button class="prev"></button>
|
||||
<button class="next"></button>
|
||||
<button class="repeat"></button>
|
||||
<button class="random"></button>
|
||||
<span class="status">
|
||||
<button class="play">play</button>
|
||||
<button class="pause">pause</button>
|
||||
</span>
|
||||
<button class="prev">prev</button>
|
||||
<button class="next">next</button>
|
||||
<button class="repeat">repeat</button>
|
||||
<button class="random">random</button>
|
||||
</footer>
|
||||
<script type="module" src="js/app.js"></script>
|
||||
</body>
|
||||
|
|
|
@ -6,11 +6,6 @@ async function init() {
|
|||
await mpd.init();
|
||||
status.init();
|
||||
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();
|
||||
|
|
|
@ -10,8 +10,6 @@ async function getImageData(songUrl) {
|
|||
while (1) {
|
||||
let params = ["albumart", `"${mpd.escape(songUrl)}"`, offset];
|
||||
let lines = await mpd.command(params.join(" "));
|
||||
if (lines.length == 1) { return null; }
|
||||
|
||||
data = data.concat(lines[2]);
|
||||
let metadata = parser.linesToStruct(lines.slice(0, 2));
|
||||
if (data.length >= Number(metadata["size"])) { return data; }
|
||||
|
@ -38,14 +36,27 @@ async function resize(image) {
|
|||
return new Promise(resolve => canvas.toBlob(resolve));
|
||||
}
|
||||
|
||||
export async function get(songUrl) {
|
||||
if (songUrl in cache) { return cache[songUrl]; }
|
||||
export async function get(artist, album, songUrl = null) {
|
||||
let key = `${artist}-${album}`;
|
||||
if (key in cache) { return cache[key]; }
|
||||
|
||||
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[songUrl] = url;
|
||||
return url;
|
||||
if (!songUrl) { return null; }
|
||||
|
||||
// promise to be returned in the meantime
|
||||
let resolve;
|
||||
let promise = new Promise(res => resolve = res);
|
||||
cache[key] = promise;
|
||||
|
||||
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];
|
||||
}
|
||||
|
|
|
@ -2,12 +2,18 @@ import * as parser from "./parser.js";
|
|||
|
||||
let ws;
|
||||
let commandQueue = [];
|
||||
let pendingResolve;
|
||||
let current;
|
||||
|
||||
function onMessage(e) {
|
||||
if (pendingResolve) {
|
||||
pendingResolve(JSON.parse(e.data)); // FIXME tady test na ACK
|
||||
pendingResolve = null;
|
||||
if (current) {
|
||||
let lines = JSON.parse(e.data);
|
||||
let last = lines.pop();
|
||||
if (last.startsWith("OK")) {
|
||||
current.resolve(lines);
|
||||
} else {
|
||||
current.reject(last);
|
||||
}
|
||||
current = null;
|
||||
}
|
||||
processQueue();
|
||||
}
|
||||
|
@ -23,11 +29,9 @@ function onClose(e) {
|
|||
}
|
||||
|
||||
function processQueue() {
|
||||
if (pendingResolve || commandQueue.length == 0) { return; }
|
||||
let {cmd, resolve} = commandQueue.shift();
|
||||
pendingResolve = resolve;
|
||||
if (cmd instanceof Array) { cmd = ["command_list_begin", ...cmd, "command_list_end"].join("\n"); }
|
||||
ws.send(cmd);
|
||||
if (current || commandQueue.length == 0) { return; }
|
||||
current = commandQueue.shift();
|
||||
ws.send(current.cmd);
|
||||
}
|
||||
|
||||
export function escape(str) {
|
||||
|
@ -35,24 +39,30 @@ export function escape(str) {
|
|||
}
|
||||
|
||||
export async function command(cmd) {
|
||||
return new Promise(resolve => {
|
||||
commandQueue.push({cmd, resolve});
|
||||
if (cmd instanceof Array) { cmd = ["command_list_begin", ...cmd, "command_list_end"].join("\n"); }
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
commandQueue.push({cmd, resolve, reject});
|
||||
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"]);
|
||||
lines.pop(); // "OK"
|
||||
return parser.linesToStruct(lines);
|
||||
}
|
||||
|
||||
export async function init() {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
ws = new WebSocket("ws://localhost:8080?server=raspberrypi.local");
|
||||
ws = new WebSocket("ws://localhost:8080");
|
||||
} catch (e) { reject(e); }
|
||||
pendingResolve = resolve;
|
||||
current = {resolve, reject};
|
||||
|
||||
ws.addEventListener("error", onError);
|
||||
ws.addEventListener("message", onMessage);
|
||||
|
|
|
@ -1,18 +1,52 @@
|
|||
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.getStatus();
|
||||
DOM.title.textContent = data["Title"];
|
||||
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 node = document.querySelector("footer");
|
||||
DOM.title = node.querySelector(".title");
|
||||
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();
|
||||
}
|
Loading…
Reference in a new issue