From 17b50407662a360262f5915d4d1e7ae9bb023376 Mon Sep 17 00:00:00 2001 From: Ondrej Zara Date: Tue, 19 Mar 2019 22:56:39 +0100 Subject: [PATCH] art --- app/Makefile | 7 +++++++ app/app.css | 29 ++++++++++++++++++++++++++ app/css/app.less | 10 +++++++++ app/css/footer.less | 3 +++ app/css/header.less | 17 +++++++++++++++ app/css/main.less | 5 +++++ app/index.html | 48 ++++++++++++++++++++++++++++++++++++++++-- app/js/app.js | 6 ++++++ app/js/art.js | 51 +++++++++++++++++++++++++++++++++++++++++++++ app/js/mpd.js | 18 ++++++++++------ app/js/parser.js | 1 - app/js/status.js | 7 ++++++- package.json | 4 +++- 13 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 app/Makefile create mode 100644 app/app.css create mode 100644 app/css/app.less create mode 100644 app/css/footer.less create mode 100644 app/css/header.less create mode 100644 app/css/main.less create mode 100644 app/js/art.js diff --git a/app/Makefile b/app/Makefile new file mode 100644 index 0000000..8ea2bd8 --- /dev/null +++ b/app/Makefile @@ -0,0 +1,7 @@ +LESS := $(shell npm bin)/lessc +APP := app.css + +all: $(APP) + +$(APP): css/* + $(LESS) css/app.less > $@ diff --git a/app/app.css b/app/app.css new file mode 100644 index 0000000..bf917cf --- /dev/null +++ b/app/app.css @@ -0,0 +1,29 @@ +body { + margin: 0; + display: flex; + flex-direction: column; + height: 100vh; +} +header nav ul { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: row; +} +header nav ul li { + text-align: center; + flex-grow: 1; + line-height: 40px; +} +header nav ul li:hover { + background-color: red; +} +main { + flex-grow: 1; + overflow-x: hidden; + overflow-y: auto; +} +footer { + flex-basis: 40px; +} diff --git a/app/css/app.less b/app/css/app.less new file mode 100644 index 0000000..c4415c7 --- /dev/null +++ b/app/css/app.less @@ -0,0 +1,10 @@ +body { + margin: 0; + display: flex; + flex-direction: column; + height: 100vh; +} + +@import "header.less"; +@import "main.less"; +@import "footer.less"; diff --git a/app/css/footer.less b/app/css/footer.less new file mode 100644 index 0000000..6c87a64 --- /dev/null +++ b/app/css/footer.less @@ -0,0 +1,3 @@ +footer { + flex-basis: 40px; +} diff --git a/app/css/header.less b/app/css/header.less new file mode 100644 index 0000000..a956045 --- /dev/null +++ b/app/css/header.less @@ -0,0 +1,17 @@ +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/main.less b/app/css/main.less new file mode 100644 index 0000000..ce5e90f --- /dev/null +++ b/app/css/main.less @@ -0,0 +1,5 @@ +main { + flex-grow: 1; + overflow-x: hidden; + overflow-y: auto; +} diff --git a/app/index.html b/app/index.html index 6fe5235..300d997 100644 --- a/app/index.html +++ b/app/index.html @@ -1,4 +1,48 @@ - - \ No newline at end of file + + + + Control Your Player + + + +
+ +
+
+ main
+ main
+ main
+ main
+ main
+ main
+ main
+ main
+ main
+ main
+ main
+
+ + + + diff --git a/app/js/app.js b/app/js/app.js index f0db80d..7619f63 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -1,10 +1,16 @@ import * as mpd from "./mpd.js"; import * as status from "./status.js"; +import * as art from "./art.js"; 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(); diff --git a/app/js/art.js b/app/js/art.js new file mode 100644 index 0000000..6f25f14 --- /dev/null +++ b/app/js/art.js @@ -0,0 +1,51 @@ +import * as mpd from "./mpd.js"; +import * as parser from "./parser.js"; + +let cache = {}; +const SIZE = 64; + +async function getImageData(songUrl) { + let data = []; + let offset = 0; + 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; } + offset += Number(metadata["binary"]); + } +} + +async function bytesToImage(bytes) { + let blob = new Blob([bytes]); + let image = document.createElement("img"); + image.src = URL.createObjectURL(blob); + return new Promise(resolve => { + image.onload = () => resolve(image); + }); +} + +async function resize(image) { + let canvas = document.createElement("canvas"); + canvas.width = SIZE; + canvas.height = SIZE; + let ctx = canvas.getContext("2d"); + ctx.drawImage(image, 0, 0, SIZE, SIZE); + + return new Promise(resolve => canvas.toBlob(resolve)); +} + +export async function get(songUrl) { + if (songUrl in cache) { return cache[songUrl]; } + + 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; +} diff --git a/app/js/mpd.js b/app/js/mpd.js index c17777a..3eb664b 100644 --- a/app/js/mpd.js +++ b/app/js/mpd.js @@ -24,27 +24,33 @@ function onClose(e) { function processQueue() { if (pendingResolve || commandQueue.length == 0) { return; } - let cmd = commandQueue.shift(); + let {cmd, resolve} = commandQueue.shift(); + pendingResolve = resolve; if (cmd instanceof Array) { cmd = ["command_list_begin", ...cmd, "command_list_end"].join("\n"); } ws.send(cmd); } -export async function command(cmd) { - commandQueue.push(cmd); - processQueue(); +export function escape(str) { + return str.replace(/(['"\\])/g, "\\$1"); +} - return new Promise(resolve => pendingResolve = resolve); +export async function command(cmd) { + return new Promise(resolve => { + commandQueue.push({cmd, resolve}); + processQueue(); + }); } export async function getStatus() { 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=0:6600"); + ws = new WebSocket("ws://localhost:8080?server=raspberrypi.local"); } catch (e) { reject(e); } pendingResolve = resolve; diff --git a/app/js/parser.js b/app/js/parser.js index 35e7660..d8c1620 100644 --- a/app/js/parser.js +++ b/app/js/parser.js @@ -1,5 +1,4 @@ export function linesToStruct(lines) { - lines.pop(); // "OK" let result = {}; lines.forEach(line => { let cindex = line.indexOf(":"); diff --git a/app/js/status.js b/app/js/status.js index 4342e9e..053dd04 100644 --- a/app/js/status.js +++ b/app/js/status.js @@ -1,13 +1,18 @@ import * as mpd from "./mpd.js"; const DELAY = 2000; +const DOM = {}; async function tick() { let data = await mpd.getStatus(); - console.log(data); + DOM.title.textContent = data["Title"]; +// console.log(data); setTimeout(tick, DELAY); } export function init() { + let node = document.querySelector("footer"); + DOM.title = node.querySelector(".title"); + tick(); } \ No newline at end of file diff --git a/package.json b/package.json index 6fbb639..482b13f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "node-static": "^0.7.11", "ws2mpd": "^1.0.0" }, - "devDependencies": {}, + "devDependencies": { + "less": "^3.9.0" + }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" },