cyp/app/js/lib/mpd.js

155 lines
4 KiB
JavaScript
Raw Permalink Normal View History

2019-03-20 03:45:23 +08:00
import * as parser from "./parser.js";
let ws;
let commandQueue = [];
2019-03-20 23:20:17 +08:00
let current;
2019-03-20 03:45:23 +08:00
function onMessage(e) {
2019-03-20 23:20:17 +08:00
if (current) {
let lines = JSON.parse(e.data);
let last = lines.pop();
if (last.startsWith("OK")) {
current.resolve(lines);
} else {
2019-03-30 19:57:01 +08:00
console.warn(last);
2019-03-20 23:20:17 +08:00
current.reject(last);
}
current = null;
2019-03-20 03:45:23 +08:00
}
processQueue();
}
function onError(e) {
console.error(e);
2019-04-12 19:34:28 +08:00
current && current.reject(e);
2019-03-20 03:45:23 +08:00
ws = null; // fixme
}
function onClose(e) {
console.warn(e);
2019-04-12 19:34:28 +08:00
current && current.reject(e);
2019-03-20 03:45:23 +08:00
ws = null; // fixme
}
function processQueue() {
2019-03-20 23:20:17 +08:00
if (current || commandQueue.length == 0) { return; }
current = commandQueue.shift();
ws.send(current.cmd);
2019-03-20 03:45:23 +08:00
}
2019-03-25 22:49:23 +08:00
function serializeFilter(filter) {
let tokens = ["("];
Object.entries(filter).forEach(([key, value], index) => {
index && tokens.push(" AND ");
2019-04-01 17:18:28 +08:00
tokens.push(`(${key} == "${escape(value)}")`);
2019-03-25 22:49:23 +08:00
});
tokens.push(")");
let filterStr = tokens.join("");
return `"${escape(filterStr)}"`;
}
2019-03-29 03:28:55 +08:00
export function escape(str) {
2019-03-20 05:56:39 +08:00
return str.replace(/(['"\\])/g, "\\$1");
}
2019-03-20 03:45:23 +08:00
2019-03-20 05:56:39 +08:00
export async function command(cmd) {
2019-03-20 23:20:17 +08:00
if (cmd instanceof Array) { cmd = ["command_list_begin", ...cmd, "command_list_end"].join("\n"); }
return new Promise((resolve, reject) => {
commandQueue.push({cmd, resolve, reject});
2019-03-20 05:56:39 +08:00
processQueue();
});
2019-03-20 03:45:23 +08:00
}
2019-03-20 23:20:17 +08:00
export async function commandAndStatus(cmd) {
let lines = await command([cmd, "status", "currentsong"]);
2019-04-03 01:52:53 +08:00
let status = parser.linesToStruct(lines);
2019-04-11 19:34:24 +08:00
if (status["duration"] instanceof Array) { status["duration"] = status["duration"][0]; }
2019-04-03 01:52:53 +08:00
return status;
2019-03-20 23:20:17 +08:00
}
export async function status() {
2019-03-20 03:45:23 +08:00
let lines = await command(["status", "currentsong"]);
2019-03-25 22:49:23 +08:00
let status = parser.linesToStruct(lines);
2019-04-11 19:34:24 +08:00
if (status["duration"] instanceof Array) { status["duration"] = status["duration"][0]; }
2019-03-25 22:49:23 +08:00
return status;
2019-03-20 03:45:23 +08:00
}
2019-03-22 22:35:04 +08:00
export async function listQueue() {
let lines = await command("playlistinfo");
return parser.songList(lines);
}
2019-03-28 22:23:28 +08:00
export async function listPlaylists() {
let lines = await command("listplaylists");
let parsed = parser.linesToStruct(lines);
let list = parsed["playlist"];
if (!list) { return []; }
return (list instanceof Array ? list : [list]);
}
2019-03-29 03:28:55 +08:00
export async function enqueueByFilter(filter, sort = null) {
2019-03-25 22:49:23 +08:00
let tokens = ["findadd"];
2019-03-29 03:28:55 +08:00
tokens.push(serializeFilter(filter));
2019-03-25 22:49:23 +08:00
// sort && tokens.push("sort", sort); FIXME not implemented in MPD
return command(tokens.join(" "));
}
export async function listPath(path) {
let lines = await command(`lsinfo "${escape(path)}"`);
return parser.pathContents(lines);
}
export async function listTags(tag, filter = null) {
let tokens = ["list", tag];
if (filter) {
tokens.push(serializeFilter(filter));
let fakeGroup = Object.keys(filter)[0]; // FIXME hack for MPD < 0.21.6
tokens.push("group", fakeGroup);
}
let lines = await command(tokens.join(" "));
let parsed = parser.linesToStruct(lines);
2019-03-30 19:57:01 +08:00
return [].concat(tag in parsed ? parsed[tag] : []);
2019-03-25 22:49:23 +08:00
}
2019-03-30 19:57:01 +08:00
export async function listSongs(filter, window = null) {
2019-03-25 22:49:23 +08:00
let tokens = ["find"];
tokens.push(serializeFilter(filter));
2019-03-30 19:57:01 +08:00
if (window) { tokens.push("window", window.join(":")); }
2019-03-25 22:49:23 +08:00
let lines = await command(tokens.join(" "));
return parser.songList(lines);
}
export async function albumArt(songUrl) {
let data = [];
let offset = 0;
while (1) {
let params = ["albumart", `"${escape(songUrl)}"`, offset];
let lines = await command(params.join(" "));
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"]);
}
2019-03-29 03:28:55 +08:00
return null;
2019-03-25 22:49:23 +08:00
}
2019-03-20 03:45:23 +08:00
export async function init() {
2019-04-15 21:13:38 +08:00
let response = await fetch("/ticket", {method:"POST"});
let ticket = (await response.json()).ticket;
2019-03-20 03:45:23 +08:00
return new Promise((resolve, reject) => {
try {
2019-04-15 21:13:38 +08:00
ws = new WebSocket(`ws://${location.host}/?ticket=${encodeURIComponent(ticket)}`);
2019-03-20 03:45:23 +08:00
} catch (e) { reject(e); }
2019-03-20 23:20:17 +08:00
current = {resolve, reject};
2019-03-20 03:45:23 +08:00
ws.addEventListener("error", onError);
ws.addEventListener("message", onMessage);
ws.addEventListener("close", onClose);
});
}