diff --git a/app/app.css b/app/app.css index 2b13e07..04c61d4 100644 --- a/app/app.css +++ b/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; } } diff --git a/app/css/app.less b/app/css/app.less index c704d38..cdbf6fe 100644 --- a/app/css/app.less +++ b/app/css/app.less @@ -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; diff --git a/app/css/component.less b/app/css/component.less index caa9fb9..55729f9 100644 --- a/app/css/component.less +++ b/app/css/component.less @@ -11,35 +11,33 @@ padding: 0; } - .grid { - li { - display: flex; - flex-direction: row; - align-items: center; - padding: 0 4px; - white-space: nowrap; - } + li { + display: flex; + flex-direction: row; + align-items: center; + padding: 0 4px; + white-space: nowrap; + } - h2 { - flex-grow: 1; - font-size: 100%; - font-weight: normal; - margin: 0; - overflow: hidden; - text-overflow: ellipsis; + h2 { + flex-grow: 1; + font-size: 100%; + font-weight: normal; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; - .icon { - margin-right: 4px; - color: var(--primary); - } - } - - li:nth-child(odd) { - background-color: #555; - } - - @media (pointer: coarse) { - .icon { width: 32px; } + .icon { + margin-right: 4px; + color: var(--primary); } } + + li:nth-child(odd) { + background-color: #555; + } + + @media (pointer: coarse) { + .icon { width: 32px; } + } } diff --git a/app/css/library.less b/app/css/library.less index 5b4e475..cf01e49 100644 --- a/app/css/library.less +++ b/app/css/library.less @@ -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; + } } } } diff --git a/app/index.html b/app/index.html index ee1bd13..b47d941 100644 --- a/app/index.html +++ b/app/index.html @@ -35,10 +35,10 @@ - +
- +
@@ -46,7 +46,7 @@
- +
diff --git a/app/js/lib/mpd.js b/app/js/lib/mpd.js index c74606b..7a311f4 100644 --- a/app/js/lib/mpd.js +++ b/app/js/lib/mpd.js @@ -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); } diff --git a/app/js/lib/ui.js b/app/js/lib/ui.js index 43a66f7..71a6cb5 100644 --- a/app/js/lib/ui.js +++ b/app/js/lib/ui.js @@ -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); diff --git a/app/js/library.js b/app/js/library.js index 4494d3e..33d9a3a 100644 --- a/app/js/library.js +++ b/app/js/library.js @@ -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)); } diff --git a/app/js/player.js b/app/js/player.js index 54328eb..400ba65 100644 --- a/app/js/player.js +++ b/app/js/player.js @@ -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 - 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"]); + 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); }