features
This commit is contained in:
parent
37fdd5413c
commit
0e43a33c4a
15 changed files with 385 additions and 34 deletions
31
app/app.css
31
app/app.css
|
@ -4,14 +4,13 @@ body {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
header,
|
body > header,
|
||||||
footer {
|
body > footer {
|
||||||
box-shadow: 0 0 3px #000;
|
box-shadow: 0 0 3px #000;
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow-x: hidden;
|
overflow: hidden;
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
nav ul {
|
nav ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -51,7 +50,19 @@ nav ul li.active {
|
||||||
#player:not([data-flags~=repeat]) .repeat {
|
#player:not([data-flags~=repeat]) .repeat {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
.component {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
#queue {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
#queue ul {
|
#queue ul {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -69,3 +80,15 @@ nav ul li.active {
|
||||||
#queue .current {
|
#queue .current {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
#library {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
#library ul {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
|
@ -3,13 +3,16 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
|
> header, > footer {
|
||||||
|
box-shadow: 0 0 3px #000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header, footer {
|
|
||||||
box-shadow: 0 0 3px #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
@import "main.less";
|
@import "main.less";
|
||||||
@import "nav.less";
|
@import "nav.less";
|
||||||
@import "player.less";
|
@import "player.less";
|
||||||
|
@import "component.less";
|
||||||
@import "queue.less";
|
@import "queue.less";
|
||||||
|
@import "library.less";
|
||||||
|
|
5
app/css/component.less
Normal file
5
app/css/component.less
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.component {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
14
app/css/library.less
Normal file
14
app/css/library.less
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#library {
|
||||||
|
.component;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
main {
|
main {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow-x: hidden;
|
overflow: hidden;
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#queue {
|
#queue {
|
||||||
|
.component;
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -24,9 +24,19 @@
|
||||||
</section>
|
</section>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<section id="queue"></section>
|
<section id="queue">
|
||||||
|
<header></header>
|
||||||
|
<ul></ul>
|
||||||
|
</section>
|
||||||
<section id="playlists"></section>
|
<section id="playlists"></section>
|
||||||
<section id="library"></section>
|
<section id="library">
|
||||||
|
<header></header>
|
||||||
|
<ul></ul>
|
||||||
|
</section>
|
||||||
|
<section id="fs">
|
||||||
|
<header></header>
|
||||||
|
<ul></ul>
|
||||||
|
</section>
|
||||||
<section id="misc"></section>
|
<section id="misc"></section>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
|
@ -35,6 +45,7 @@
|
||||||
<li data-for="queue">Q</li>
|
<li data-for="queue">Q</li>
|
||||||
<li data-for="playlists">Playlists</li>
|
<li data-for="playlists">Playlists</li>
|
||||||
<li data-for="library">Library</li>
|
<li data-for="library">Library</li>
|
||||||
|
<li data-for="fs">FS</li>
|
||||||
<li data-for="misc">Misc</li>
|
<li data-for="misc">Misc</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -3,8 +3,10 @@ import * as mpd from "./lib/mpd.js";
|
||||||
import * as player from "./player.js";
|
import * as player from "./player.js";
|
||||||
|
|
||||||
import * as queue from "./queue.js";
|
import * as queue from "./queue.js";
|
||||||
|
import * as library from "./library.js";
|
||||||
|
import * as fs from "./fs.js";
|
||||||
|
|
||||||
const components = { queue };
|
const components = { queue, library, fs };
|
||||||
|
|
||||||
export function activate(what) {
|
export function activate(what) {
|
||||||
for (let id in components) {
|
for (let id in components) {
|
||||||
|
@ -29,8 +31,7 @@ async function init() {
|
||||||
await mpd.init();
|
await mpd.init();
|
||||||
player.init(document.querySelector("#player"));
|
player.init(document.querySelector("#player"));
|
||||||
|
|
||||||
activate("queue");
|
activate("fs");
|
||||||
window.mpd = mpd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
65
app/js/fs.js
Normal file
65
app/js/fs.js
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import * as app from "./app.js";
|
||||||
|
import * as mpd from "./lib/mpd.js";
|
||||||
|
import * as html from "./lib/html.js";
|
||||||
|
import * as player from "./player.js";
|
||||||
|
import * as format from "./lib/format.js";
|
||||||
|
import * as ui from "./lib/ui.js";
|
||||||
|
|
||||||
|
let node;
|
||||||
|
const SORT = "-Track";
|
||||||
|
|
||||||
|
function buildHeader(path) {
|
||||||
|
filter = filter || {};
|
||||||
|
let header = node.querySelector("header");
|
||||||
|
html.clear(header);
|
||||||
|
|
||||||
|
let button = html.button({}, "Music Library", header);
|
||||||
|
button.addEventListener("click", e => listArtists());
|
||||||
|
|
||||||
|
let artist = filter["Artist"];
|
||||||
|
if (artist) {
|
||||||
|
let artistFilter = {"Artist":artist};
|
||||||
|
let button = html.button({}, artist, header);
|
||||||
|
button.addEventListener("click", e => listAlbums(artistFilter));
|
||||||
|
|
||||||
|
let album = filter["Album"];
|
||||||
|
if (album) {
|
||||||
|
let albumFilter = Object.assign({}, artistFilter, {"Album":album});
|
||||||
|
let button = html.button({}, album, header);
|
||||||
|
button.addEventListener("click", e => listSongs(albumFilter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDirectory(data, parent) {
|
||||||
|
let path = data["directory"];
|
||||||
|
let node = ui.group(path, {}, parent);
|
||||||
|
node.addEventListener("click", e => list(path));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFile(data, parent) {
|
||||||
|
return ui.song(data, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildResults(results) {
|
||||||
|
let ul = node.querySelector("ul");
|
||||||
|
html.clear(ul);
|
||||||
|
|
||||||
|
results["directory"].forEach(directory => buildDirectory(directory, ul));
|
||||||
|
results["file"].forEach(file => buildFile(file, ul));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function list(path) {
|
||||||
|
let results = await mpd.listPath(path);
|
||||||
|
buildResults(results);
|
||||||
|
// buildHeader(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function activate() {
|
||||||
|
list("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function init(n) {
|
||||||
|
node = n;
|
||||||
|
}
|
|
@ -15,19 +15,6 @@ function load(key) {
|
||||||
return localStorage.getItem(`${STORAGE_PREFIX}-${key}`);
|
return localStorage.getItem(`${STORAGE_PREFIX}-${key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
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(" "));
|
|
||||||
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) {
|
async function bytesToImage(bytes) {
|
||||||
let blob = new Blob([bytes]);
|
let blob = new Blob([bytes]);
|
||||||
let src = URL.createObjectURL(blob);
|
let src = URL.createObjectURL(blob);
|
||||||
|
@ -62,7 +49,7 @@ export async function get(artist, album, songUrl = null) {
|
||||||
cache[key] = promise;
|
cache[key] = promise;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let data = await getImageData(songUrl);
|
let data = await mpd.albumArt(songUrl);
|
||||||
let bytes = new Uint8Array(data);
|
let bytes = new Uint8Array(data);
|
||||||
let image = await bytesToImage(bytes);
|
let image = await bytesToImage(bytes);
|
||||||
let url = resize(image).toDataURL(MIME);
|
let url = resize(image).toDataURL(MIME);
|
||||||
|
|
|
@ -34,7 +34,19 @@ function processQueue() {
|
||||||
ws.send(current.cmd);
|
ws.send(current.cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function escape(str) {
|
function serializeFilter(filter) {
|
||||||
|
let tokens = ["("];
|
||||||
|
Object.entries(filter).forEach(([key, value], index) => {
|
||||||
|
index && tokens.push(" AND ");
|
||||||
|
tokens.push(`(${key} == "${value}")`);
|
||||||
|
});
|
||||||
|
tokens.push(")");
|
||||||
|
|
||||||
|
let filterStr = tokens.join("");
|
||||||
|
return `"${escape(filterStr)}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escape(str) {
|
||||||
return str.replace(/(['"\\])/g, "\\$1");
|
return str.replace(/(['"\\])/g, "\\$1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +66,10 @@ export async function commandAndStatus(cmd) {
|
||||||
|
|
||||||
export async function status() {
|
export async function status() {
|
||||||
let lines = await command(["status", "currentsong"]);
|
let lines = await command(["status", "currentsong"]);
|
||||||
return parser.linesToStruct(lines);
|
let status = parser.linesToStruct(lines);
|
||||||
|
// duration returned 2x => arrayfied
|
||||||
|
if ("duration" in status) { status["duration"] = status["duration"][0]; }
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listQueue() {
|
export async function listQueue() {
|
||||||
|
@ -62,6 +77,55 @@ export async function listQueue() {
|
||||||
return parser.songList(lines);
|
return parser.songList(lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function enqueue(fileOrFilter, sort = null) {
|
||||||
|
if (typeof(fileOrFilter) == "string") {
|
||||||
|
return command(`addid "${escape(fileOrFilter)}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = ["findadd"];
|
||||||
|
tokens.push(serializeFilter(fileOrFilter));
|
||||||
|
// 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);
|
||||||
|
return [].concat(parsed[tag] || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listSongs(filter) {
|
||||||
|
let tokens = ["find"];
|
||||||
|
tokens.push(serializeFilter(filter));
|
||||||
|
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"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -3,7 +3,18 @@ export function linesToStruct(lines) {
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
let cindex = line.indexOf(":");
|
let cindex = line.indexOf(":");
|
||||||
if (cindex == -1) { throw new Error(`Malformed line "${line}"`); }
|
if (cindex == -1) { throw new Error(`Malformed line "${line}"`); }
|
||||||
result[line.substring(0, cindex)] = line.substring(cindex+2);
|
let key = line.substring(0, cindex);
|
||||||
|
let value = line.substring(cindex+2);
|
||||||
|
if (key in result) {
|
||||||
|
let old = result[key];
|
||||||
|
if (old instanceof Array) {
|
||||||
|
old.push(value);
|
||||||
|
} else {
|
||||||
|
result[key] = [old, value];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +27,7 @@ export function songList(lines) {
|
||||||
if (line.startsWith("file:") && batch.length) {
|
if (line.startsWith("file:") && batch.length) {
|
||||||
let song = linesToStruct(batch);
|
let song = linesToStruct(batch);
|
||||||
songs.push(song);
|
songs.push(song);
|
||||||
|
batch = [];
|
||||||
}
|
}
|
||||||
batch.push(lines.shift());
|
batch.push(lines.shift());
|
||||||
}
|
}
|
||||||
|
@ -27,3 +39,27 @@ export function songList(lines) {
|
||||||
|
|
||||||
return songs;
|
return songs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pathContents(lines) {
|
||||||
|
const prefixes = ["file", "directory", "playlist"];
|
||||||
|
|
||||||
|
let batch = [];
|
||||||
|
let result = {};
|
||||||
|
let batchPrefix = null;
|
||||||
|
prefixes.forEach(prefix => result[prefix] = []);
|
||||||
|
|
||||||
|
while (lines.length) {
|
||||||
|
let line = lines[0];
|
||||||
|
let prefix = line.split(":")[0];
|
||||||
|
if (prefixes.includes(prefix)) { // begin of a new batch
|
||||||
|
if (batch.length) { result[batchPrefix].push(linesToStruct(batch)); }
|
||||||
|
batchPrefix = prefix;
|
||||||
|
batch = [];
|
||||||
|
}
|
||||||
|
batch.push(lines.shift());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.length) { result[batchPrefix].push(linesToStruct(batch)); }
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
47
app/js/lib/ui.js
Normal file
47
app/js/lib/ui.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import * as mpd from "./mpd.js";
|
||||||
|
import * as html from "./html.js";
|
||||||
|
|
||||||
|
function playButton(fileOrFilter, parent) {
|
||||||
|
let button = html.button({}, "▶", parent);
|
||||||
|
button.addEventListener("click", async e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await mpd.command("clear");
|
||||||
|
await mpd.enqueue(fileOrFilter, SORT);
|
||||||
|
await mpd.command("play");
|
||||||
|
app.activate("queue");
|
||||||
|
player.update();
|
||||||
|
});
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addButton(fileOrFilter, parent) {
|
||||||
|
let button = html.button({}, "+", parent);
|
||||||
|
button.addEventListener("click", async e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await mpd.enqueue(fileOrFilter, SORT);
|
||||||
|
// fixme notification?
|
||||||
|
});
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function song(data, parent) {
|
||||||
|
let node = html.node("li", {}, "", parent);
|
||||||
|
|
||||||
|
let file = data["file"];
|
||||||
|
playButton(file, node);
|
||||||
|
addButton(file, node);
|
||||||
|
|
||||||
|
html.node("h3", {}, data["Title"], node);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function group(label, filter, parent) {
|
||||||
|
let node = html.node("li", {}, label, parent);
|
||||||
|
|
||||||
|
playButton(filter, node);
|
||||||
|
addButton(filter, node);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
93
app/js/library.js
Normal file
93
app/js/library.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import * as app from "./app.js";
|
||||||
|
import * as mpd from "./lib/mpd.js";
|
||||||
|
import * as html from "./lib/html.js";
|
||||||
|
import * as player from "./player.js";
|
||||||
|
import * as format from "./lib/format.js";
|
||||||
|
import * as ui from "./lib/ui.js";
|
||||||
|
|
||||||
|
let node;
|
||||||
|
const SORT = "-Track";
|
||||||
|
|
||||||
|
function buildHeader(filter) {
|
||||||
|
filter = filter || {};
|
||||||
|
let header = node.querySelector("header");
|
||||||
|
html.clear(header);
|
||||||
|
|
||||||
|
let button = html.button({}, "Music Library", header);
|
||||||
|
button.addEventListener("click", e => listArtists());
|
||||||
|
|
||||||
|
let artist = filter["Artist"];
|
||||||
|
if (artist) {
|
||||||
|
let artistFilter = {"Artist":artist};
|
||||||
|
let button = html.button({}, artist, header);
|
||||||
|
button.addEventListener("click", e => listAlbums(artistFilter));
|
||||||
|
|
||||||
|
let album = filter["Album"];
|
||||||
|
if (album) {
|
||||||
|
let albumFilter = Object.assign({}, artistFilter, {"Album":album});
|
||||||
|
let button = html.button({}, album, header);
|
||||||
|
button.addEventListener("click", e => listSongs(albumFilter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAlbum(album, filter, parent) {
|
||||||
|
let childFilter = Object.assign({}, filter, {"Album": album});
|
||||||
|
let node = ui.group(album, childFilter, parent);
|
||||||
|
node.addEventListener("click", e => listSongs(childFilter));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildArtist(artist, filter, parent) {
|
||||||
|
let childFilter = Object.assign({}, filter, {"Artist": artist});
|
||||||
|
let node = ui.group(artist, childFilter, parent);
|
||||||
|
node.addEventListener("click", e => listAlbums(childFilter));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSongs(songs, filter) {
|
||||||
|
let ul = node.querySelector("ul");
|
||||||
|
html.clear(ul);
|
||||||
|
|
||||||
|
songs.map(song => ui.song(song, ul));
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAlbums(albums, filter) {
|
||||||
|
let ul = node.querySelector("ul");
|
||||||
|
html.clear(ul);
|
||||||
|
|
||||||
|
albums.map(album => buildAlbum(album, filter, ul));
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildArtists(artists, filter) {
|
||||||
|
let ul = node.querySelector("ul");
|
||||||
|
html.clear(ul);
|
||||||
|
|
||||||
|
artists.map(artist => buildArtist(artist, filter, ul));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listSongs(filter) {
|
||||||
|
let songs = await mpd.listSongs(filter);
|
||||||
|
buildSongs(songs, filter);
|
||||||
|
buildHeader(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listAlbums(filter) {
|
||||||
|
let albums = await mpd.listTags("Album", filter);
|
||||||
|
buildAlbums(albums, filter);
|
||||||
|
buildHeader(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listArtists(filter) {
|
||||||
|
let artists = await mpd.listTags("Artist", filter);
|
||||||
|
buildArtists(artists, filter);
|
||||||
|
buildHeader(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function activate() {
|
||||||
|
listArtists();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function init(n) {
|
||||||
|
node = n;
|
||||||
|
}
|
|
@ -44,12 +44,11 @@ function buildSong(song) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSongs(songs) {
|
function buildSongs(songs) {
|
||||||
html.clear(node);
|
let ul = node.querySelector("ul");
|
||||||
|
html.clear(ul);
|
||||||
|
|
||||||
let ul = html.node("ul");
|
|
||||||
songs.map(buildSong).forEach(li => ul.appendChild(li));
|
songs.map(buildSong).forEach(li => ul.appendChild(li));
|
||||||
|
|
||||||
node.appendChild(ul);
|
|
||||||
updateCurrent();
|
updateCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue