refactor
This commit is contained in:
parent
5c9be9ceac
commit
5417037db6
13 changed files with 129 additions and 83 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
_youtube
|
||||||
|
|
20
app/app.css
20
app/app.css
|
@ -9,7 +9,7 @@ html {
|
||||||
body {
|
body {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: lato, sans-serif;
|
font-family: lato, sans-serif;
|
||||||
line-height: 1.3;
|
line-height: 1;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-shadow: 0 1px 1px #000;
|
text-shadow: 0 1px 1px #000;
|
||||||
|
@ -75,12 +75,12 @@ nav ul li {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex: 1 0 0;
|
flex: 1 0 0;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
}
|
cursor: pointer;
|
||||||
nav ul li:hover {
|
border-top: 4px solid transparent;
|
||||||
background-color: red;
|
border-bottom: 4px solid transparent;
|
||||||
}
|
}
|
||||||
nav ul li.active {
|
nav ul li.active {
|
||||||
background-color: green;
|
border-top-color: dodgerblue;
|
||||||
}
|
}
|
||||||
#player {
|
#player {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -133,13 +133,13 @@ nav ul li.active {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.component .grid li h2 {
|
.component .grid li h2 {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
@ -171,13 +171,13 @@ nav ul li.active {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#queue .grid li h2 {
|
#queue .grid li h2 {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
@ -212,13 +212,13 @@ nav ul li.active {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#library .grid li h2 {
|
#library .grid li h2 {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
@ -250,13 +250,13 @@ nav ul li.active {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#fs .grid li h2 {
|
#fs .grid li h2 {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
@ -288,13 +288,13 @@ nav ul li.active {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#playlists .grid li h2 {
|
#playlists .grid li h2 {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ html {
|
||||||
body {
|
body {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: lato, sans-serif;
|
font-family: lato, sans-serif;
|
||||||
line-height: 1.3;
|
line-height: 1;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-shadow: 0 1px 1px #000;
|
text-shadow: 0 1px 1px #000;
|
||||||
|
|
|
@ -17,13 +17,13 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,13 @@ nav ul {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex: 1 0 0;
|
flex: 1 0 0;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover { background-color:red;}
|
border-top: 4px solid transparent;
|
||||||
|
border-bottom: 4px solid transparent;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: green;
|
border-top-color: dodgerblue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
<main>
|
<main>
|
||||||
<section id="queue">
|
<section id="queue">
|
||||||
<header>
|
<header>
|
||||||
<button class="clear"></button>
|
<button class="clear" title="Clear the queue"></button>
|
||||||
<button class="save"></button>
|
<button class="save" title="Save the queue"></button>
|
||||||
</header>
|
</header>
|
||||||
<ul class="grid"></ul>
|
<ul class="grid"></ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -24,13 +24,13 @@ function buildHeader(path) {
|
||||||
function buildDirectory(data, parent) {
|
function buildDirectory(data, parent) {
|
||||||
let path = data["directory"];
|
let path = data["directory"];
|
||||||
let name = path.split("/").pop();
|
let name = path.split("/").pop();
|
||||||
let node = ui.group(ui.GROUP_DIRECTORY, name, path, parent);
|
let node = ui.group(ui.CTX_FS, name, path, parent);
|
||||||
node.addEventListener("click", e => list(path));
|
node.addEventListener("click", e => list(path));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFile(data, parent) {
|
function buildFile(data, parent) {
|
||||||
return ui.song(ui.SONG_FILE, data, parent);
|
return ui.song(ui.CTX_FS, data, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildResults(results) {
|
function buildResults(results) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ function serializeFilter(filter) {
|
||||||
return `"${escape(filterStr)}"`;
|
return `"${escape(filterStr)}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function escape(str) {
|
export function escape(str) {
|
||||||
return str.replace(/(['"\\])/g, "\\$1");
|
return str.replace(/(['"\\])/g, "\\$1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,13 +86,9 @@ export async function listPlaylists() {
|
||||||
return (list instanceof Array ? list : [list]);
|
return (list instanceof Array ? list : [list]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enqueue(urlOrFilter, sort = null) {
|
export async function enqueueByFilter(filter, sort = null) {
|
||||||
if (typeof(urlOrFilter) == "string") {
|
|
||||||
return command(`add "${escape(urlOrFilter)}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens = ["findadd"];
|
let tokens = ["findadd"];
|
||||||
tokens.push(serializeFilter(urlOrFilter));
|
tokens.push(serializeFilter(filter));
|
||||||
// sort && tokens.push("sort", sort); FIXME not implemented in MPD
|
// sort && tokens.push("sort", sort); FIXME not implemented in MPD
|
||||||
return command(tokens.join(" "));
|
return command(tokens.join(" "));
|
||||||
}
|
}
|
||||||
|
@ -133,12 +129,9 @@ export async function albumArt(songUrl) {
|
||||||
if (data.length >= Number(metadata["size"])) { return data; }
|
if (data.length >= Number(metadata["size"])) { return data; }
|
||||||
offset += Number(metadata["binary"]);
|
offset += Number(metadata["binary"]);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(name) {
|
|
||||||
return command(`save "${escape(name)}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
|
129
app/js/lib/ui.js
129
app/js/lib/ui.js
|
@ -4,29 +4,40 @@ import * as pubsub from "./pubsub.js";
|
||||||
import * as format from "./format.js";
|
import * as format from "./format.js";
|
||||||
import * as player from "../player.js";
|
import * as player from "../player.js";
|
||||||
|
|
||||||
export const SONG_FILE = 1;
|
export const CTX_FS = 1;
|
||||||
export const SONG_LIBRARY = 2;
|
export const CTX_QUEUE = 2;
|
||||||
export const SONG_QUEUE = 3;
|
export const CTX_LIBRARY = 3;
|
||||||
export const GROUP_DIRECTORY = 4;
|
|
||||||
export const GROUP_LIBRARY = 5;
|
const TYPE_ID = 1;
|
||||||
|
const TYPE_URL = 2;
|
||||||
|
const TYPE_FILTER = 3;
|
||||||
|
const TYPE_PLAYLIST = 4;
|
||||||
|
|
||||||
const SORT = "-Track";
|
const SORT = "-Track";
|
||||||
|
|
||||||
|
function enqueue(type, what) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fileName(data) {
|
function fileName(data) {
|
||||||
return data["file"].split("/").pop();
|
return data["file"].split("/").pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTitle(type, data) {
|
function formatTitle(ctx, data) {
|
||||||
switch (type) {
|
switch (ctx) {
|
||||||
case SONG_FILE:
|
case CTX_FS:
|
||||||
return `🎵 ${fileName(data)}`;
|
return `🎵 ${fileName(data)}`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SONG_LIBRARY:
|
case CTX_LIBRARY:
|
||||||
return data["Artist"] || fileName(data);
|
return data["Artist"] || fileName(data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SONG_QUEUE:
|
case CTX_QUEUE:
|
||||||
let tokens = [];
|
let tokens = [];
|
||||||
data["Artist"] && tokens.push(data["Artist"]);
|
data["Artist"] && tokens.push(data["Artist"]);
|
||||||
data["Title"] && tokens.push(data["Title"]);
|
data["Title"] && tokens.push(data["Title"]);
|
||||||
|
@ -36,77 +47,97 @@ function formatTitle(type, data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playButton(id, parent) {
|
function playButton(type, what, parent) {
|
||||||
let button = html.button({icon:"play", title:"Play"}, "", parent);
|
let button = html.button({icon:"play", title:"Play"}, "", parent);
|
||||||
button.addEventListener("click", async e => {
|
button.addEventListener("click", async e => {
|
||||||
await mpd.command(`playid ${id}`);
|
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");
|
||||||
|
}
|
||||||
player.update();
|
player.update();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return button;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteButton(id, parent) {
|
function deleteButton(type, id, parent) {
|
||||||
let button = html.button({icon:"close", title:"Delete from queue"}, "", 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);
|
||||||
button.addEventListener("click", async e => {
|
button.addEventListener("click", async e => {
|
||||||
await mpd.command(`deleteid ${id}`);
|
switch (type) {
|
||||||
pubsub.publish("queue-change");
|
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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAndPlayButton(urlOrFilter, parent) {
|
function addButton(type, what, parent) {
|
||||||
let button = html.button({icon:"play", title:"Play"}, "", parent);
|
|
||||||
button.addEventListener("click", async e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
await mpd.command("clear");
|
|
||||||
await mpd.enqueue(urlOrFilter, SORT);
|
|
||||||
await mpd.command("play");
|
|
||||||
pubsub.publish("queue-change");
|
|
||||||
player.update();
|
|
||||||
});
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addButton(urlOrFilter, parent) {
|
|
||||||
let button = html.button({icon:"plus", title:"Add to queue"}, "", parent);
|
let button = html.button({icon:"plus", title:"Add to queue"}, "", parent);
|
||||||
button.addEventListener("click", async e => {
|
button.addEventListener("click", async e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await mpd.enqueue(urlOrFilter, SORT);
|
await enqueue(type, what);
|
||||||
pubsub.publish("queue-change");
|
pubsub.publish("queue-change");
|
||||||
// fixme notification?
|
// fixme notification?
|
||||||
});
|
});
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function song(type, data, parent) {
|
export function song(ctx, data, parent) {
|
||||||
let node = html.node("li", {}, "", parent);
|
let node = html.node("li", {}, "", parent);
|
||||||
|
|
||||||
let title = formatTitle(type, data);
|
let title = formatTitle(ctx, data);
|
||||||
html.node("h2", {}, title, node);
|
html.node("h2", {}, title, node);
|
||||||
|
|
||||||
html.node("span", {className:"duration"}, format.time(Number(data["duration"])), node);
|
html.node("span", {className:"duration"}, format.time(Number(data["duration"])), node);
|
||||||
|
|
||||||
if (type == SONG_QUEUE) {
|
switch (ctx) {
|
||||||
let id = data["Id"];
|
case CTX_QUEUE:
|
||||||
node.dataset.songId = id;
|
let id = data["Id"];
|
||||||
playButton(id, node);
|
node.dataset.songId = id;
|
||||||
deleteButton(id, node);
|
playButton(TYPE_ID, id, node);
|
||||||
} else {
|
deleteButton(TYPE_ID, id, node);
|
||||||
let url = data["file"];
|
break;
|
||||||
addAndPlayButton(url, node);
|
|
||||||
addButton(url, node);
|
case CTX_FS:
|
||||||
|
let url = data["file"];
|
||||||
|
playButton(TYPE_URL, url, node);
|
||||||
|
addButton(TYPE_URL, url, node);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function group(type, label, urlOrFilter, parent) {
|
export function group(ctx, label, urlOrFilter, parent) {
|
||||||
let node = html.node("li", {}, "", parent);
|
let node = html.node("li", {}, "", parent);
|
||||||
|
|
||||||
if (type == GROUP_DIRECTORY) { label = `📁 ${label}`; }
|
if (ctx == CTX_FS) { label = `📁 ${label}`; }
|
||||||
html.node("h2", {}, label, node);
|
html.node("h2", {}, label, node);
|
||||||
|
|
||||||
addAndPlayButton(urlOrFilter, node);
|
let type = (ctx == CTX_FS ? TYPE_URL : TYPE_FILTER);
|
||||||
addButton(urlOrFilter, node);
|
|
||||||
|
playButton(type, urlOrFilter, node);
|
||||||
|
addButton(type, urlOrFilter, node);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -117,9 +148,9 @@ export function playlist(name, parent) {
|
||||||
html.icon("playlist-music", node)
|
html.icon("playlist-music", node)
|
||||||
html.node("h2", {}, name, node);
|
html.node("h2", {}, name, node);
|
||||||
|
|
||||||
// addAndPlayButton(url, node);
|
playButton(TYPE_PLAYLIST, name, node);
|
||||||
// addButton(url, node);
|
addButton(TYPE_PLAYLIST, name, node);
|
||||||
// deleteButton(id, node);
|
deleteButton(TYPE_PLAYLIST, name, node);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,14 +29,14 @@ function buildHeader(filter) {
|
||||||
|
|
||||||
function buildAlbum(album, filter, parent) {
|
function buildAlbum(album, filter, parent) {
|
||||||
let childFilter = Object.assign({}, filter, {"Album": album});
|
let childFilter = Object.assign({}, filter, {"Album": album});
|
||||||
let node = ui.group(ui.GROUP_LIBRARY, album, childFilter, parent);
|
let node = ui.group(ui.CTX_LIBRARY, album, childFilter, parent);
|
||||||
node.addEventListener("click", e => listSongs(childFilter));
|
node.addEventListener("click", e => listSongs(childFilter));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildArtist(artist, filter, parent) {
|
function buildArtist(artist, filter, parent) {
|
||||||
let childFilter = Object.assign({}, filter, {"Artist": artist});
|
let childFilter = Object.assign({}, filter, {"Artist": artist});
|
||||||
let node = ui.group(ui.GROUP_LIBRARY, artist, childFilter, parent);
|
let node = ui.group(ui.CTX_LIBRARY, artist, childFilter, parent);
|
||||||
node.addEventListener("click", e => listAlbums(childFilter));
|
node.addEventListener("click", e => listAlbums(childFilter));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ function buildSongs(songs, filter) {
|
||||||
ul.classList.remove("tiles");
|
ul.classList.remove("tiles");
|
||||||
ul.classList.add("grid");
|
ul.classList.add("grid");
|
||||||
|
|
||||||
songs.map(song => ui.song(ui.SONG_LIBRARY, song, ul));
|
songs.map(song => ui.song(ui.CTX_LIBRARY, song, ul));
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildAlbums(albums, filter) {
|
function buildAlbums(albums, filter) {
|
||||||
|
|
|
@ -17,10 +17,15 @@ async function syncLists() {
|
||||||
buildLists(lists);
|
buildLists(lists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPlaylistsChange(message, publisher, data) {
|
||||||
|
syncLists();
|
||||||
|
}
|
||||||
|
|
||||||
export async function activate() {
|
export async function activate() {
|
||||||
syncLists();
|
syncLists();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init(n) {
|
export function init(n) {
|
||||||
node = n;
|
node = n;
|
||||||
|
pubsub.subscribe("playlists-change", onPlaylistsChange);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ function buildSongs(songs) {
|
||||||
let ul = node.querySelector("ul");
|
let ul = node.querySelector("ul");
|
||||||
html.clear(ul);
|
html.clear(ul);
|
||||||
|
|
||||||
songs.map(song => ui.song(ui.SONG_QUEUE, song, ul));
|
songs.map(song => ui.song(ui.CTX_QUEUE, song, ul));
|
||||||
|
|
||||||
updateCurrent();
|
updateCurrent();
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,6 @@ export function init(n) {
|
||||||
save.addEventListener("click", e => {
|
save.addEventListener("click", e => {
|
||||||
let name = prompt("Save current queue as a playlist?", "name");
|
let name = prompt("Save current queue as a playlist?", "name");
|
||||||
if (name === null) { return; }
|
if (name === null) { return; }
|
||||||
mpd.save(name);
|
mpd.command(`save "${mpd.escape(name)}"`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
14
app/tsconfig.json
Normal file
14
app/tsconfig.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"lib": ["es2017", "dom"],
|
||||||
|
"target": "es6",
|
||||||
|
"baseUrl": "js/app",
|
||||||
|
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"strictFunctionTypes": true
|
||||||
|
},
|
||||||
|
"files": ["js/app.js"]
|
||||||
|
}
|
Loading…
Reference in a new issue