2019-03-25 22:49:23 +08:00
|
|
|
import * as mpd from "./mpd.js";
|
|
|
|
import * as html from "./html.js";
|
2019-03-26 17:09:26 +08:00
|
|
|
import * as pubsub from "./pubsub.js";
|
|
|
|
import * as format from "./format.js";
|
2019-03-30 19:57:01 +08:00
|
|
|
import * as art from "./art.js";
|
2019-03-26 17:09:26 +08:00
|
|
|
import * as player from "../player.js";
|
2019-03-25 22:49:23 +08:00
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
export const CTX_FS = 1;
|
|
|
|
export const CTX_QUEUE = 2;
|
|
|
|
export const CTX_LIBRARY = 3;
|
|
|
|
|
|
|
|
const TYPE_ID = 1;
|
|
|
|
const TYPE_URL = 2;
|
|
|
|
const TYPE_FILTER = 3;
|
|
|
|
const TYPE_PLAYLIST = 4;
|
2019-03-26 17:09:26 +08:00
|
|
|
|
|
|
|
const SORT = "-Track";
|
|
|
|
|
2019-03-30 19:57:01 +08:00
|
|
|
async function enqueue(type, what) {
|
2019-03-29 03:28:55 +08:00
|
|
|
switch (type) {
|
|
|
|
case TYPE_URL: return mpd.command(`add "${mpd.escape(what)}"`); break;
|
|
|
|
case TYPE_FILTER: return mpd.enqueueByFilter(what, SORT); break;
|
|
|
|
case TYPE_PLAYLIST: return mpd.command(`load "${mpd.escape(what)}"`); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-30 19:57:01 +08:00
|
|
|
async function fillArt(parent, filter) {
|
|
|
|
let artist = filter["Artist"];
|
|
|
|
let album = filter["Album"];
|
|
|
|
let src = null;
|
|
|
|
|
|
|
|
if (artist && album) {
|
|
|
|
src = await art.get(artist, album);
|
|
|
|
if (!src) {
|
|
|
|
let songs = await mpd.listSongs(filter, [0,1]);
|
|
|
|
if (songs.length) {
|
|
|
|
src = await art.get(artist, album, songs[0]["file"]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src) {
|
|
|
|
html.node("img", {src}, "", parent);
|
|
|
|
} else {
|
2019-04-01 04:02:26 +08:00
|
|
|
html.icon(album ? "album" : "artist", parent);
|
2019-03-30 19:57:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-26 17:09:26 +08:00
|
|
|
function fileName(data) {
|
|
|
|
return data["file"].split("/").pop();
|
|
|
|
}
|
|
|
|
|
2019-04-01 04:02:26 +08:00
|
|
|
function formatSongInfo(ctx, data) {
|
|
|
|
let lines = [];
|
2019-03-29 04:43:18 +08:00
|
|
|
let tokens = [];
|
2019-03-29 03:28:55 +08:00
|
|
|
switch (ctx) {
|
2019-04-01 04:02:26 +08:00
|
|
|
case CTX_FS: lines.push(fileName(data)); break;
|
2019-03-26 17:09:26 +08:00
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
case CTX_LIBRARY:
|
|
|
|
case CTX_QUEUE:
|
2019-04-01 04:02:26 +08:00
|
|
|
if (data["Title"]) {
|
|
|
|
if (ctx == CTX_LIBRARY && data["Track"]) {
|
|
|
|
tokens.push(data["Track"].padStart(2, "0"));
|
|
|
|
}
|
|
|
|
tokens.push(data["Title"]);
|
|
|
|
lines.push(tokens.join(" "));
|
|
|
|
lines.push(format.subtitle(data));
|
|
|
|
} else {
|
|
|
|
lines.push(fileName(data));
|
|
|
|
lines.push("\u00A0");
|
|
|
|
}
|
2019-03-26 17:09:26 +08:00
|
|
|
break;
|
|
|
|
}
|
2019-04-01 04:02:26 +08:00
|
|
|
|
|
|
|
return lines;
|
2019-03-26 17:09:26 +08:00
|
|
|
}
|
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
function playButton(type, what, parent) {
|
2019-03-26 22:40:23 +08:00
|
|
|
let button = html.button({icon:"play", title:"Play"}, "", parent);
|
2019-03-26 17:09:26 +08:00
|
|
|
button.addEventListener("click", async e => {
|
2019-03-29 03:28:55 +08:00
|
|
|
if (type == TYPE_ID) {
|
|
|
|
await mpd.command(`playid ${what}`);
|
|
|
|
} else {
|
|
|
|
await mpd.command("clear");
|
|
|
|
await enqueue(type, what);
|
|
|
|
await mpd.command("play");
|
|
|
|
pubsub.publish("queue-change");
|
|
|
|
}
|
2019-03-26 17:09:26 +08:00
|
|
|
player.update();
|
|
|
|
});
|
|
|
|
|
|
|
|
return button;
|
2019-03-29 03:28:55 +08:00
|
|
|
|
2019-03-26 17:09:26 +08:00
|
|
|
}
|
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
function deleteButton(type, id, parent) {
|
|
|
|
let title;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TYPE_ID: title = "Delete from queue"; break;
|
|
|
|
case TYPE_PLAYLIST: title = "Delete playlist"; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let button = html.button({icon:"close", title}, "", parent);
|
2019-03-25 22:49:23 +08:00
|
|
|
button.addEventListener("click", async e => {
|
2019-03-29 03:28:55 +08:00
|
|
|
switch (type) {
|
|
|
|
case TYPE_ID:
|
|
|
|
await mpd.command(`deleteid ${id}`);
|
|
|
|
pubsub.publish("queue-change");
|
|
|
|
return;
|
|
|
|
case TYPE_PLAYLIST:
|
|
|
|
let ok = confirm(`Really delete playlist '${id}'?`);
|
|
|
|
if (!ok) { return; }
|
|
|
|
await mpd.command(`rm "${mpd.escape(id)}"`);
|
|
|
|
pubsub.publish("playlists-change");
|
|
|
|
return;
|
|
|
|
}
|
2019-03-25 22:49:23 +08:00
|
|
|
});
|
|
|
|
return button;
|
|
|
|
}
|
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
function addButton(type, what, parent) {
|
2019-03-28 22:23:28 +08:00
|
|
|
let button = html.button({icon:"plus", title:"Add to queue"}, "", parent);
|
2019-03-25 22:49:23 +08:00
|
|
|
button.addEventListener("click", async e => {
|
|
|
|
e.stopPropagation();
|
2019-03-29 03:28:55 +08:00
|
|
|
await enqueue(type, what);
|
2019-03-26 17:09:26 +08:00
|
|
|
pubsub.publish("queue-change");
|
2019-03-25 22:49:23 +08:00
|
|
|
// fixme notification?
|
|
|
|
});
|
|
|
|
return button;
|
|
|
|
}
|
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
export function song(ctx, data, parent) {
|
2019-03-29 23:28:26 +08:00
|
|
|
let node = html.node("li", {className:"song"}, "", parent);
|
2019-04-01 04:02:26 +08:00
|
|
|
let info = html.node("div", {className:"info"}, "", node);
|
|
|
|
|
|
|
|
if (ctx == CTX_FS) { html.icon("music", info); }
|
2019-03-25 22:49:23 +08:00
|
|
|
|
2019-04-01 04:02:26 +08:00
|
|
|
let lines = formatSongInfo(ctx, data);
|
|
|
|
html.node("h2", {}, lines.shift(), info);
|
|
|
|
lines.length && html.node("div", {}, lines.shift(), info);
|
2019-03-25 22:49:23 +08:00
|
|
|
|
2019-03-26 17:09:26 +08:00
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
switch (ctx) {
|
|
|
|
case CTX_QUEUE:
|
|
|
|
let id = data["Id"];
|
|
|
|
node.dataset.songId = id;
|
|
|
|
playButton(TYPE_ID, id, node);
|
|
|
|
deleteButton(TYPE_ID, id, node);
|
|
|
|
break;
|
|
|
|
|
2019-03-29 04:43:18 +08:00
|
|
|
case CTX_LIBRARY:
|
2019-03-29 03:28:55 +08:00
|
|
|
case CTX_FS:
|
|
|
|
let url = data["file"];
|
|
|
|
playButton(TYPE_URL, url, node);
|
|
|
|
addButton(TYPE_URL, url, node);
|
|
|
|
break;
|
2019-03-26 17:09:26 +08:00
|
|
|
}
|
2019-03-25 22:49:23 +08:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
export function group(ctx, label, urlOrFilter, parent) {
|
2019-03-29 23:28:26 +08:00
|
|
|
let node = html.node("li", {className:"group"}, "", parent);
|
2019-03-25 22:49:23 +08:00
|
|
|
|
2019-03-30 19:57:01 +08:00
|
|
|
if (ctx == CTX_LIBRARY) {
|
2019-04-01 04:02:26 +08:00
|
|
|
node.classList.add("has-art");
|
2019-03-30 19:57:01 +08:00
|
|
|
let art = html.node("span", {className:"art"}, "", node);
|
|
|
|
fillArt(art, urlOrFilter);
|
|
|
|
}
|
|
|
|
|
2019-04-01 04:02:26 +08:00
|
|
|
let info = html.node("span", {className:"info"}, "", node);
|
|
|
|
if (ctx == CTX_FS) { html.icon("folder", info); }
|
|
|
|
html.node("h2", {}, label, info);
|
2019-03-30 19:57:01 +08:00
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
let type = (ctx == CTX_FS ? TYPE_URL : TYPE_FILTER);
|
|
|
|
|
|
|
|
playButton(type, urlOrFilter, node);
|
|
|
|
addButton(type, urlOrFilter, node);
|
2019-03-25 22:49:23 +08:00
|
|
|
|
|
|
|
return node;
|
2019-03-28 22:23:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function playlist(name, parent) {
|
|
|
|
let node = html.node("li", {}, "", parent);
|
|
|
|
|
2019-04-01 04:02:26 +08:00
|
|
|
let info = html.node("span", {className:"info"}, "", node);
|
|
|
|
html.icon("playlist-music", info)
|
|
|
|
html.node("h2", {}, name, info);
|
2019-03-28 22:23:28 +08:00
|
|
|
|
2019-03-29 03:28:55 +08:00
|
|
|
playButton(TYPE_PLAYLIST, name, node);
|
|
|
|
addButton(TYPE_PLAYLIST, name, node);
|
|
|
|
deleteButton(TYPE_PLAYLIST, name, node);
|
2019-03-28 22:23:28 +08:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|