library refactor
This commit is contained in:
parent
9d8a81ebc8
commit
181c988649
9 changed files with 118 additions and 74 deletions
63
app/app.css
63
app/app.css
|
@ -12,7 +12,7 @@ body {
|
|||
line-height: 1;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
text-shadow: 0 1px 1px #000;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8);
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
|
@ -137,14 +137,14 @@ nav ul li.active {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.component .grid li {
|
||||
.component li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.component .grid h2 {
|
||||
.component h2 {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
|
@ -152,15 +152,15 @@ nav ul li.active {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.component .grid h2 .icon {
|
||||
.component h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
}
|
||||
.component .grid li:nth-child(odd) {
|
||||
.component li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
.component .grid .icon {
|
||||
.component .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
|
@ -176,14 +176,14 @@ nav ul li.active {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#queue .grid li {
|
||||
#queue li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#queue .grid h2 {
|
||||
#queue h2 {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
|
@ -191,15 +191,15 @@ nav ul li.active {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#queue .grid h2 .icon {
|
||||
#queue h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
}
|
||||
#queue .grid li:nth-child(odd) {
|
||||
#queue li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#queue .grid .icon {
|
||||
#queue .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
|
@ -218,14 +218,14 @@ nav ul li.active {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#library .grid li {
|
||||
#library li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#library .grid h2 {
|
||||
#library h2 {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
|
@ -233,26 +233,33 @@ nav ul li.active {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#library .grid h2 .icon {
|
||||
#library h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
}
|
||||
#library .grid li:nth-child(odd) {
|
||||
#library li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#library .grid .icon {
|
||||
#library .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
#library .tiles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-gap: 2px;
|
||||
}
|
||||
#library .tiles li {
|
||||
border: 3px solid lime;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
height: 200px;
|
||||
}
|
||||
#library .tiles li h2 {
|
||||
font-size: 150%;
|
||||
margin: 4px 0;
|
||||
}
|
||||
#fs {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
@ -265,14 +272,14 @@ nav ul li.active {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#fs .grid li {
|
||||
#fs li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#fs .grid h2 {
|
||||
#fs h2 {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
|
@ -280,15 +287,15 @@ nav ul li.active {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#fs .grid h2 .icon {
|
||||
#fs h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
}
|
||||
#fs .grid li:nth-child(odd) {
|
||||
#fs li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#fs .grid .icon {
|
||||
#fs .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
|
@ -307,14 +314,14 @@ nav ul li.active {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#playlists .grid li {
|
||||
#playlists li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#playlists .grid h2 {
|
||||
#playlists h2 {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
|
@ -322,15 +329,15 @@ nav ul li.active {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#playlists .grid h2 .icon {
|
||||
#playlists h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
}
|
||||
#playlists .grid li:nth-child(odd) {
|
||||
#playlists li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#playlists .grid .icon {
|
||||
#playlists .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ body {
|
|||
line-height: 1;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
text-shadow: 0 1px 1px #000;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8);
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.grid {
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -41,5 +40,4 @@
|
|||
@media (pointer: coarse) {
|
||||
.icon { width: 32px; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,19 @@
|
|||
|
||||
.tiles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-gap: 2px;
|
||||
|
||||
li {
|
||||
border: 3px solid lime;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
height: 200px;
|
||||
|
||||
h2 {
|
||||
font-size: 150%;
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,10 @@
|
|||
<button class="clear" data-icon="close" title="Clear the queue"></button>
|
||||
<button class="save" data-icon="content-save" title="Save the queue"></button>
|
||||
</header>
|
||||
<ul class="grid"></ul>
|
||||
<ul></ul>
|
||||
</section>
|
||||
<section id="playlists">
|
||||
<ul class="grid"></ul>
|
||||
<ul></ul>
|
||||
</section>
|
||||
<section id="library">
|
||||
<header></header>
|
||||
|
@ -46,7 +46,7 @@
|
|||
</section>
|
||||
<section id="fs">
|
||||
<header></header>
|
||||
<ul class="grid"></ul>
|
||||
<ul></ul>
|
||||
</section>
|
||||
<section id="yt">
|
||||
<button class="go" data-icon="download">Go!</button>
|
||||
|
|
|
@ -11,6 +11,7 @@ function onMessage(e) {
|
|||
if (last.startsWith("OK")) {
|
||||
current.resolve(lines);
|
||||
} else {
|
||||
console.warn(last);
|
||||
current.reject(last);
|
||||
}
|
||||
current = null;
|
||||
|
@ -108,12 +109,13 @@ export async function listTags(tag, filter = null) {
|
|||
}
|
||||
let lines = await command(tokens.join(" "));
|
||||
let parsed = parser.linesToStruct(lines);
|
||||
return [].concat(parsed[tag] || []);
|
||||
return [].concat(tag in parsed ? parsed[tag] : []);
|
||||
}
|
||||
|
||||
export async function listSongs(filter) {
|
||||
export async function listSongs(filter, window = null) {
|
||||
let tokens = ["find"];
|
||||
tokens.push(serializeFilter(filter));
|
||||
if (window) { tokens.push("window", window.join(":")); }
|
||||
let lines = await command(tokens.join(" "));
|
||||
return parser.songList(lines);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as mpd from "./mpd.js";
|
|||
import * as html from "./html.js";
|
||||
import * as pubsub from "./pubsub.js";
|
||||
import * as format from "./format.js";
|
||||
import * as art from "./art.js";
|
||||
import * as player from "../player.js";
|
||||
|
||||
export const CTX_FS = 1;
|
||||
|
@ -15,7 +16,7 @@ const TYPE_PLAYLIST = 4;
|
|||
|
||||
const SORT = "-Track";
|
||||
|
||||
function enqueue(type, what) {
|
||||
async 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;
|
||||
|
@ -23,6 +24,28 @@ function enqueue(type, what) {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
html.icon("music", parent);
|
||||
}
|
||||
}
|
||||
|
||||
function fileName(data) {
|
||||
return data["file"].split("/").pop();
|
||||
}
|
||||
|
@ -106,7 +129,6 @@ function addButton(type, what, parent) {
|
|||
export function song(ctx, data, parent) {
|
||||
let node = html.node("li", {className:"song"}, "", parent);
|
||||
|
||||
|
||||
let title = formatTitle(ctx, data);
|
||||
let h2 = html.node("h2", {}, "", node);
|
||||
if (ctx == CTX_FS || ctx == CTX_LIBRARY) { html.icon("music", h2); }
|
||||
|
@ -136,10 +158,16 @@ export function song(ctx, data, parent) {
|
|||
export function group(ctx, label, urlOrFilter, parent) {
|
||||
let node = html.node("li", {className:"group"}, "", parent);
|
||||
|
||||
if (ctx == CTX_LIBRARY) {
|
||||
let art = html.node("span", {className:"art"}, "", node);
|
||||
fillArt(art, urlOrFilter);
|
||||
}
|
||||
|
||||
let h2 = html.node("h2", {}, "", node);
|
||||
if (ctx == CTX_FS) { html.icon("folder", h2); }
|
||||
html.text(label, h2);
|
||||
|
||||
|
||||
let type = (ctx == CTX_FS ? TYPE_URL : TYPE_FILTER);
|
||||
|
||||
playButton(type, urlOrFilter, node);
|
||||
|
|
|
@ -44,8 +44,6 @@ function buildArtist(artist, filter, parent) {
|
|||
function buildSongs(songs, filter) {
|
||||
let ul = node.querySelector("ul");
|
||||
html.clear(ul);
|
||||
ul.classList.remove("tiles");
|
||||
ul.classList.add("grid");
|
||||
|
||||
songs.map(song => ui.song(ui.CTX_LIBRARY, song, ul));
|
||||
}
|
||||
|
@ -53,8 +51,6 @@ function buildSongs(songs, filter) {
|
|||
function buildAlbums(albums, filter) {
|
||||
let ul = node.querySelector("ul");
|
||||
html.clear(ul);
|
||||
ul.classList.add("tiles");
|
||||
ul.classList.remove("grid");
|
||||
|
||||
albums.map(album => buildAlbum(album, filter, ul));
|
||||
}
|
||||
|
@ -62,8 +58,6 @@ function buildAlbums(albums, filter) {
|
|||
function buildArtists(artists, filter) {
|
||||
let ul = node.querySelector("ul");
|
||||
html.clear(ul);
|
||||
ul.classList.add("tiles");
|
||||
ul.classList.remove("grid");
|
||||
|
||||
artists.map(artist => buildArtist(artist, filter, ul));
|
||||
}
|
||||
|
|
|
@ -15,9 +15,16 @@ function sync(data) {
|
|||
DOM.elapsed.textContent = format.time(Number(data["elapsed"] || 0)); // changed time
|
||||
|
||||
if (data["file"] != current["file"]) { // changed song
|
||||
if (data["file"]) { // playing at all?
|
||||
DOM.duration.textContent = format.time(Number(data["duration"] || 0));
|
||||
DOM.title.textContent = data["Title"] || data["file"].split("/").pop();
|
||||
DOM["artist-album"].textContent = format.artistAlbum(data["Artist"], data["Album"]);
|
||||
} else {
|
||||
DOM.duration.textContent = "";
|
||||
DOM.title.textContent = "";
|
||||
DOM["artist-album"].textContent = "";
|
||||
}
|
||||
|
||||
pubsub.publish("song-change", null, data);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue