From 8dc753b815c28f5b46b3c4350716d88b83e83c51 Mon Sep 17 00:00:00 2001 From: Ondrej Zara Date: Thu, 12 Mar 2020 23:03:26 +0100 Subject: [PATCH] library+fs --- app/css/cyp.less | 4 +- app/css/elements/artist.less | 3 - app/css/elements/back.less | 3 + app/css/elements/library.less | 2 +- app/css/elements/path.less | 3 + app/css/elements/tag.less | 3 + app/cyp.css | 2 +- app/icons/keyboard-backspace.svg | 1 + app/index.html | 3 + app/js/elements/artist.js | 17 ----- app/js/elements/back.js | 16 ++++ app/js/elements/library.js | 127 ++++++++++++++++++++++++++++--- app/js/elements/path.js | 28 +++++++ app/js/elements/playlists.js | 8 +- app/js/elements/queue.js | 8 +- app/js/elements/tag.js | 29 +++++++ app/js/elements/yt.js | 4 +- app/js/icons.js | 3 + app/js/item.js | 7 ++ app/js/mpd-mock.js | 41 +++++----- app/js/mpd.js | 9 +-- 21 files changed, 247 insertions(+), 74 deletions(-) delete mode 100644 app/css/elements/artist.less create mode 100644 app/css/elements/back.less create mode 100644 app/css/elements/path.less create mode 100644 app/css/elements/tag.less create mode 100644 app/icons/keyboard-backspace.svg delete mode 100644 app/js/elements/artist.js create mode 100644 app/js/elements/back.js create mode 100644 app/js/elements/path.js create mode 100644 app/js/elements/tag.js diff --git a/app/css/cyp.less b/app/css/cyp.less index c86400f..5f6e604 100644 --- a/app/css/cyp.less +++ b/app/css/cyp.less @@ -83,4 +83,6 @@ select { @import "elements/range.less"; @import "elements/playlist.less"; @import "elements/library.less"; -@import "elements/artist.less"; +@import "elements/tag.less"; +@import "elements/back.less"; +@import "elements/path.less"; diff --git a/app/css/elements/artist.less b/app/css/elements/artist.less deleted file mode 100644 index e0a565d..0000000 --- a/app/css/elements/artist.less +++ /dev/null @@ -1,3 +0,0 @@ -cyp-artist { - .item; -} diff --git a/app/css/elements/back.less b/app/css/elements/back.less new file mode 100644 index 0000000..d7d814d --- /dev/null +++ b/app/css/elements/back.less @@ -0,0 +1,3 @@ +cyp-back { + .item; +} diff --git a/app/css/elements/library.less b/app/css/elements/library.less index 0576380..a1b3706 100644 --- a/app/css/elements/library.less +++ b/app/css/elements/library.less @@ -1,3 +1,3 @@ cyp-library { - + cyp-song .subtitle { display: none; } } diff --git a/app/css/elements/path.less b/app/css/elements/path.less new file mode 100644 index 0000000..899d918 --- /dev/null +++ b/app/css/elements/path.less @@ -0,0 +1,3 @@ +cyp-path { + .item; +} diff --git a/app/css/elements/tag.less b/app/css/elements/tag.less new file mode 100644 index 0000000..e54bc19 --- /dev/null +++ b/app/css/elements/tag.less @@ -0,0 +1,3 @@ +cyp-tag { + .item; +} diff --git a/app/cyp.css b/app/cyp.css index ae2d49d..fb41ac3 100644 --- a/app/cyp.css +++ b/app/cyp.css @@ -1 +1 @@ -*,*::before,*::after{box-sizing:inherit}html{background-color:var(--fg)}body{margin:0}main{flex:auto;overflow:auto}header,footer{flex:none;z-index:1;box-shadow:var(--box-shadow)}footer{position:relative;overflow:hidden;height:56px}@media (max-width:480px){footer{height:40px}}input,select,button{color:inherit;font:inherit}button{-webkit-appearance:none;-moz-appearance:none;appearance:none;flex-direction:row;align-items:center;flex:none;background-color:transparent;padding:0;border:none;line-height:1;cursor:pointer}button:not([hidden]){display:flex}@media (hover:hover){button:not(:disabled):not(:hover):not(.-thumb){opacity:.9}}select{background-color:transparent;border:1px solid var(--fg);border-radius:4px;padding:2px 4px}@font-face{font-family:"Lato";src:url("font/LatoLatin-Regular.woff2") format("woff2");font-style:normal;font-weight:normal}@font-face{font-family:"Lato";src:url("font/LatoLatin-Bold.woff2") format("woff2");font-style:bold;font-weight:normal}.icon{width:24px}.icon path:not([fill]),.icon polygon:not([fill]),.icon circle:not([fill]){fill:currentColor}.flex-row{flex-direction:row;align-items:center}.flex-row:not([hidden]){display:flex}.flex-column{flex-direction:column}.flex-column:not([hidden]){display:flex}.ellipsis{overflow:hidden;text-overflow:ellipsis}.selectable{border-left:var(--border-width) solid transparent;cursor:pointer}.selectable.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}.item{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}.item:not([hidden]){display:flex}.item.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}.item:nth-child(odd){background-color:var(--bg-alt)}.item>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}.item .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}.item button:first-of-type{margin-left:auto}.item button .icon{width:32px}.component header{flex-direction:row;align-items:center;padding:var(--spacing)}.component header:not([hidden]){display:flex}.component header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}.component header button .icon{margin-right:var(--icon-spacing)}.component ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}.component li{flex-direction:row;align-items:center}.component li:not([hidden]){display:flex}.component li .info{flex-grow:1;overflow:hidden}.component li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}.component li .info h2{font-size:var(--font-size-large);margin:0}.component li:not(.has-art){padding:8px}.component li button .icon{width:32px}.component li:nth-child(odd){background-color:var(--bg-alt)}#library header{flex-direction:row;align-items:center;padding:var(--spacing)}#library header:not([hidden]){display:flex}#library header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}#library header button .icon{margin-right:var(--icon-spacing)}#library ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}#library li{flex-direction:row;align-items:center}#library li:not([hidden]){display:flex}#library li .info{flex-grow:1;overflow:hidden}#library li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}#library li .info h2{font-size:var(--font-size-large);margin:0}#library li:not(.has-art){padding:8px}#library li button .icon{width:32px}#library li:nth-child(odd){background-color:var(--bg-alt)}#library header{white-space:pre}#library .search{order:1}#library .search.open~*{display:none}#library .art img,#library .art .icon{width:64px}#library .art .icon{filter:drop-shadow(var(--text-shadow))}#library .group{cursor:pointer}#library .group h2{font-weight:normal}#library .tiles{display:grid;grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));grid-gap:2px}#library .tiles li{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 header{flex-direction:row;align-items:center;padding:var(--spacing)}#fs header:not([hidden]){display:flex}#fs header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}#fs header button .icon{margin-right:var(--icon-spacing)}#fs ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}#fs li{flex-direction:row;align-items:center}#fs li:not([hidden]){display:flex}#fs li .info{flex-grow:1;overflow:hidden}#fs li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}#fs li .info h2{font-size:var(--font-size-large);margin:0}#fs li:not(.has-art){padding:8px}#fs li button .icon{width:32px}#fs li:nth-child(odd){background-color:var(--bg-alt)}#fs header{white-space:pre}#fs .search{order:1}#fs .search.open~*{display:none}#fs .group{cursor:pointer}.search{flex-direction:row;align-items:center;margin-left:auto;transition:all 300ms;width:32px;max-width:20ch}.search:not([hidden]){display:flex}.search .icon{width:32px;cursor:pointer}.search input{border:none;outline:none;color:inherit;background-color:inherit;border-bottom:1px solid var(--fg);width:0;padding:0;flex-grow:1}.search.open{flex:1}.art{margin-right:var(--icon-spacing)}.art .icon,.art img{vertical-align:top}cyp-app{--font-size-large:112.5%;--icon-spacing:4px;--primary:rgb(var(--primary-raw));--primary-tint:rgba(var(--primary-raw), .1);--box-shadow:0 0 3px #000;--border-width:4px}cyp-app[theme=light]{--fg:#333;--bg:#f0f0f0;--bg-alt:#e0e0e0;--text-shadow:none}cyp-app[theme=dark]{--fg:#f0f0f0;--bg:#333;--bg-alt:#555;--text-shadow:0 1px 1px rgba(0,0,0,0.8)}@media (prefers-color-scheme:dark){cyp-app[theme=auto]{--fg:#f0f0f0;--bg:#333;--bg-alt:#555;--text-shadow:0 1px 1px rgba(0,0,0,0.8)}}@media (prefers-color-scheme:light){cyp-app[theme=auto]{--fg:#333;--bg:#f0f0f0;--bg-alt:#e0e0e0;--text-shadow:none}}cyp-app[color=dodgerblue]{--primary-raw:30, 144, 255}cyp-app[color=darkorange]{--primary-raw:255, 140, 0}cyp-app[color=limegreen]{--primary-raw:50, 205, 50}cyp-app{flex-direction:column;box-sizing:border-box;margin:0 auto;max-width:800px;height:100vh;font-family:lato,sans-serif;line-height:1.25;background-color:var(--bg);color:var(--fg);text-shadow:var(--text-shadow);white-space:nowrap}cyp-app:not([hidden]){display:flex}cyp-menu,cyp-commands{flex-direction:row;align-items:center;height:100%}cyp-menu:not([hidden]),cyp-commands:not([hidden]){display:flex}cyp-menu button,cyp-commands button{height:100%;flex-direction:column;align-items:center;justify-content:center}cyp-menu button:not([hidden]),cyp-commands button:not([hidden]){display:flex}@media (max-width:480px){cyp-menu button,cyp-commands button{flex-direction:row}cyp-menu button span:not([id]),cyp-commands button span:not([id]){display:none}}cyp-menu button .icon+*,cyp-commands button .icon+*{margin-top:2px}cyp-menu button{flex:1 0 0;border-top:var(--border-width) solid transparent;border-bottom:var(--border-width) solid transparent}cyp-menu button .icon{margin-right:var(--icon-spacing)}cyp-menu button.active{border-top-color:var(--primary);color:var(--primary);background-color:var(--primary-tint)}cyp-commands{position:absolute;left:0;top:0;width:100%;transition:top 300ms;background-color:var(--bg)}cyp-commands[hidden]{display:flex;top:100%}cyp-commands button{flex:0 1 80px}cyp-commands button.last{order:1;margin-left:auto}cyp-song{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-song:not([hidden]){display:flex}cyp-song.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-song:nth-child(odd){background-color:var(--bg-alt)}cyp-song>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-song .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-song button:first-of-type{margin-left:auto}cyp-song button .icon{width:32px}cyp-song .multiline{flex-direction:column;min-width:0}cyp-song .multiline:not([hidden]){display:flex}cyp-song .multiline .subtitle{overflow:hidden;text-overflow:ellipsis}cyp-player{flex-direction:row;align-items:center;align-items:stretch}cyp-player:not([hidden]){display:flex}cyp-player:not([data-state=play]) .pause{display:none}cyp-player[data-state=play] .play{display:none}cyp-player:not([data-flags~=random]) .random,cyp-player:not([data-flags~=repeat]) .repeat{opacity:.5}cyp-player x-range{flex-grow:1;--elapsed-color:var(--primary)}cyp-player .art{margin-right:0;height:96px}cyp-player .art img,cyp-player .art .icon{width:96px}cyp-player .info{flex-grow:2;flex-basis:0;padding:0 var(--icon-spacing);overflow:hidden;flex-direction:column;justify-content:space-around}cyp-player .info:not([hidden]){display:flex}cyp-player .info h2{font-size:125%;margin:0}cyp-player .info .title,cyp-player .info .subtitle{overflow:hidden;text-overflow:ellipsis}cyp-player .timeline{flex-direction:row;align-items:center}cyp-player .timeline:not([hidden]){display:flex}cyp-player .timeline .duration,cyp-player .timeline .elapsed{flex-basis:5ch;text-align:center}cyp-player .controls{flex-grow:1;flex-basis:0;max-width:220px;flex-direction:column;justify-content:space-around}cyp-player .controls:not([hidden]){display:flex}cyp-player .controls .playback{flex-direction:row;align-items:center;justify-content:space-around}cyp-player .controls .playback:not([hidden]){display:flex}cyp-player .controls .playback .icon{width:40px}cyp-player .controls .playback .icon-play,cyp-player .controls .playback .icon-pause{width:64px}cyp-player .controls .volume{flex-direction:row;align-items:center}cyp-player .controls .volume:not([hidden]){display:flex}cyp-player .controls .volume .mute{margin-right:4px}cyp-player .misc{display:flex;flex-direction:column;align-self:stretch;justify-content:space-around}cyp-player .misc .icon{width:32px}@media (max-width:519px){cyp-player{flex-wrap:wrap;justify-content:space-between}cyp-player .info{order:1;flex-basis:100%;height:96px}}cyp-queue .current{color:var(--primary)}cyp-settings{--spacing:8px;font-size:var(--font-size-large)}cyp-settings dl{margin:var(--spacing);display:grid;grid-template-columns:max-content 1fr;align-items:center;grid-gap:var(--spacing)}cyp-settings dt{font-weight:bold}cyp-settings dd{margin:0;flex-direction:column;align-items:start}cyp-settings dd:not([hidden]){display:flex}cyp-settings label{flex-direction:row;align-items:center}cyp-settings label:not([hidden]){display:flex}cyp-settings label [type=radio],cyp-settings label [type=checkbox]{margin:0 4px 0 0}cyp-yt header{flex-direction:row;align-items:center;padding:var(--spacing)}cyp-yt header:not([hidden]){display:flex}cyp-yt header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}cyp-yt header button .icon{margin-right:var(--icon-spacing)}cyp-yt ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}cyp-yt li{flex-direction:row;align-items:center}cyp-yt li:not([hidden]){display:flex}cyp-yt li .info{flex-grow:1;overflow:hidden}cyp-yt li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-yt li .info h2{font-size:var(--font-size-large);margin:0}cyp-yt li:not(.has-art){padding:8px}cyp-yt li button .icon{width:32px}cyp-yt li:nth-child(odd){background-color:var(--bg-alt)}cyp-yt header{border-bottom:1px solid var(--fg)}cyp-yt header button+button{margin-left:16px}cyp-yt .clear{margin-left:auto}cyp-yt pre{margin:.5em .5ch;flex-grow:1;overflow:auto;white-space:pre-wrap}cyp-yt.pending header{background-image:linear-gradient(var(--primary), var(--primary));background-repeat:no-repeat;background-size:25% 4px;animation:bar ease-in-out 3s alternate infinite}@keyframes bar{0%{background-position:0 100%}100%{background-position:100% 100%}}x-range{--thumb-size:8px;--thumb-color:#ddd;--thumb-shadow:#000;--thumb-hover-color:#fff;--track-size:4px;--track-color:#888;--track-shadow:#000;--elapsed-color:#ddd;--remaining-color:transparent;--radius:calc(var(--track-size)/2);display:inline-block;position:relative;width:192px;height:16px}x-range .-track,x-range .-elapsed,x-range .-remaining{position:absolute;top:calc(50% - var(--track-size)/2);height:var(--track-size);border-radius:var(--radius)}x-range .-track{width:100%;left:0;background-color:var(--track-color);box-shadow:0 0 1px var(--thumb-shadow)}x-range .-elapsed{left:0;background-color:var(--elapsed-color)}x-range .-remaining{right:0;background-color:var(--remaining-color)}x-range .-inner{position:absolute;left:var(--thumb-size);right:var(--thumb-size);top:0;bottom:0}x-range .-thumb{all:unset;position:absolute;top:50%;transform:translate(-50%, -50%);border-radius:50%;width:calc(2*var(--thumb-size));height:calc(2*var(--thumb-size));background-color:var(--thumb-color);box-shadow:0 0 2px var(--thumb-shadow)}x-range[disabled]{opacity:.5}x-range:not([disabled]) .-thumb:hover{background-color:var(--thumb-hover-color)}cyp-playlist{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-playlist:not([hidden]){display:flex}cyp-playlist.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-playlist:nth-child(odd){background-color:var(--bg-alt)}cyp-playlist>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-playlist .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-playlist button:first-of-type{margin-left:auto}cyp-playlist button .icon{width:32px}cyp-playlist:nth-child(odd){background-color:var(--bg-alt)}cyp-artist{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-artist:not([hidden]){display:flex}cyp-artist.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-artist:nth-child(odd){background-color:var(--bg-alt)}cyp-artist>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-artist .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-artist button:first-of-type{margin-left:auto}cyp-artist button .icon{width:32px} \ No newline at end of file +*,*::before,*::after{box-sizing:inherit}html{background-color:var(--fg)}body{margin:0}main{flex:auto;overflow:auto}header,footer{flex:none;z-index:1;box-shadow:var(--box-shadow)}footer{position:relative;overflow:hidden;height:56px}@media (max-width:480px){footer{height:40px}}input,select,button{color:inherit;font:inherit}button{-webkit-appearance:none;-moz-appearance:none;appearance:none;flex-direction:row;align-items:center;flex:none;background-color:transparent;padding:0;border:none;line-height:1;cursor:pointer}button:not([hidden]){display:flex}@media (hover:hover){button:not(:disabled):not(:hover):not(.-thumb){opacity:.9}}select{background-color:transparent;border:1px solid var(--fg);border-radius:4px;padding:2px 4px}@font-face{font-family:"Lato";src:url("font/LatoLatin-Regular.woff2") format("woff2");font-style:normal;font-weight:normal}@font-face{font-family:"Lato";src:url("font/LatoLatin-Bold.woff2") format("woff2");font-style:bold;font-weight:normal}.icon{width:24px}.icon path:not([fill]),.icon polygon:not([fill]),.icon circle:not([fill]){fill:currentColor}.flex-row{flex-direction:row;align-items:center}.flex-row:not([hidden]){display:flex}.flex-column{flex-direction:column}.flex-column:not([hidden]){display:flex}.ellipsis{overflow:hidden;text-overflow:ellipsis}.selectable{border-left:var(--border-width) solid transparent;cursor:pointer}.selectable.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}.item{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}.item:not([hidden]){display:flex}.item.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}.item:nth-child(odd){background-color:var(--bg-alt)}.item>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}.item .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}.item button:first-of-type{margin-left:auto}.item button .icon{width:32px}.component header{flex-direction:row;align-items:center;padding:var(--spacing)}.component header:not([hidden]){display:flex}.component header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}.component header button .icon{margin-right:var(--icon-spacing)}.component ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}.component li{flex-direction:row;align-items:center}.component li:not([hidden]){display:flex}.component li .info{flex-grow:1;overflow:hidden}.component li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}.component li .info h2{font-size:var(--font-size-large);margin:0}.component li:not(.has-art){padding:8px}.component li button .icon{width:32px}.component li:nth-child(odd){background-color:var(--bg-alt)}#library header{flex-direction:row;align-items:center;padding:var(--spacing)}#library header:not([hidden]){display:flex}#library header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}#library header button .icon{margin-right:var(--icon-spacing)}#library ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}#library li{flex-direction:row;align-items:center}#library li:not([hidden]){display:flex}#library li .info{flex-grow:1;overflow:hidden}#library li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}#library li .info h2{font-size:var(--font-size-large);margin:0}#library li:not(.has-art){padding:8px}#library li button .icon{width:32px}#library li:nth-child(odd){background-color:var(--bg-alt)}#library header{white-space:pre}#library .search{order:1}#library .search.open~*{display:none}#library .art img,#library .art .icon{width:64px}#library .art .icon{filter:drop-shadow(var(--text-shadow))}#library .group{cursor:pointer}#library .group h2{font-weight:normal}#library .tiles{display:grid;grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));grid-gap:2px}#library .tiles li{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 header{flex-direction:row;align-items:center;padding:var(--spacing)}#fs header:not([hidden]){display:flex}#fs header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}#fs header button .icon{margin-right:var(--icon-spacing)}#fs ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}#fs li{flex-direction:row;align-items:center}#fs li:not([hidden]){display:flex}#fs li .info{flex-grow:1;overflow:hidden}#fs li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}#fs li .info h2{font-size:var(--font-size-large);margin:0}#fs li:not(.has-art){padding:8px}#fs li button .icon{width:32px}#fs li:nth-child(odd){background-color:var(--bg-alt)}#fs header{white-space:pre}#fs .search{order:1}#fs .search.open~*{display:none}#fs .group{cursor:pointer}.search{flex-direction:row;align-items:center;margin-left:auto;transition:all 300ms;width:32px;max-width:20ch}.search:not([hidden]){display:flex}.search .icon{width:32px;cursor:pointer}.search input{border:none;outline:none;color:inherit;background-color:inherit;border-bottom:1px solid var(--fg);width:0;padding:0;flex-grow:1}.search.open{flex:1}.art{margin-right:var(--icon-spacing)}.art .icon,.art img{vertical-align:top}cyp-app{--font-size-large:112.5%;--icon-spacing:4px;--primary:rgb(var(--primary-raw));--primary-tint:rgba(var(--primary-raw), .1);--box-shadow:0 0 3px #000;--border-width:4px}cyp-app[theme=light]{--fg:#333;--bg:#f0f0f0;--bg-alt:#e0e0e0;--text-shadow:none}cyp-app[theme=dark]{--fg:#f0f0f0;--bg:#333;--bg-alt:#555;--text-shadow:0 1px 1px rgba(0,0,0,0.8)}@media (prefers-color-scheme:dark){cyp-app[theme=auto]{--fg:#f0f0f0;--bg:#333;--bg-alt:#555;--text-shadow:0 1px 1px rgba(0,0,0,0.8)}}@media (prefers-color-scheme:light){cyp-app[theme=auto]{--fg:#333;--bg:#f0f0f0;--bg-alt:#e0e0e0;--text-shadow:none}}cyp-app[color=dodgerblue]{--primary-raw:30, 144, 255}cyp-app[color=darkorange]{--primary-raw:255, 140, 0}cyp-app[color=limegreen]{--primary-raw:50, 205, 50}cyp-app{flex-direction:column;box-sizing:border-box;margin:0 auto;max-width:800px;height:100vh;font-family:lato,sans-serif;line-height:1.25;background-color:var(--bg);color:var(--fg);text-shadow:var(--text-shadow);white-space:nowrap}cyp-app:not([hidden]){display:flex}cyp-menu,cyp-commands{flex-direction:row;align-items:center;height:100%}cyp-menu:not([hidden]),cyp-commands:not([hidden]){display:flex}cyp-menu button,cyp-commands button{height:100%;flex-direction:column;align-items:center;justify-content:center}cyp-menu button:not([hidden]),cyp-commands button:not([hidden]){display:flex}@media (max-width:480px){cyp-menu button,cyp-commands button{flex-direction:row}cyp-menu button span:not([id]),cyp-commands button span:not([id]){display:none}}cyp-menu button .icon+*,cyp-commands button .icon+*{margin-top:2px}cyp-menu button{flex:1 0 0;border-top:var(--border-width) solid transparent;border-bottom:var(--border-width) solid transparent}cyp-menu button .icon{margin-right:var(--icon-spacing)}cyp-menu button.active{border-top-color:var(--primary);color:var(--primary);background-color:var(--primary-tint)}cyp-commands{position:absolute;left:0;top:0;width:100%;transition:top 300ms;background-color:var(--bg)}cyp-commands[hidden]{display:flex;top:100%}cyp-commands button{flex:0 1 80px}cyp-commands button.last{order:1;margin-left:auto}cyp-song{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-song:not([hidden]){display:flex}cyp-song.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-song:nth-child(odd){background-color:var(--bg-alt)}cyp-song>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-song .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-song button:first-of-type{margin-left:auto}cyp-song button .icon{width:32px}cyp-song .multiline{flex-direction:column;min-width:0}cyp-song .multiline:not([hidden]){display:flex}cyp-song .multiline .subtitle{overflow:hidden;text-overflow:ellipsis}cyp-player{flex-direction:row;align-items:center;align-items:stretch}cyp-player:not([hidden]){display:flex}cyp-player:not([data-state=play]) .pause{display:none}cyp-player[data-state=play] .play{display:none}cyp-player:not([data-flags~=random]) .random,cyp-player:not([data-flags~=repeat]) .repeat{opacity:.5}cyp-player x-range{flex-grow:1;--elapsed-color:var(--primary)}cyp-player .art{margin-right:0;height:96px}cyp-player .art img,cyp-player .art .icon{width:96px}cyp-player .info{flex-grow:2;flex-basis:0;padding:0 var(--icon-spacing);overflow:hidden;flex-direction:column;justify-content:space-around}cyp-player .info:not([hidden]){display:flex}cyp-player .info h2{font-size:125%;margin:0}cyp-player .info .title,cyp-player .info .subtitle{overflow:hidden;text-overflow:ellipsis}cyp-player .timeline{flex-direction:row;align-items:center}cyp-player .timeline:not([hidden]){display:flex}cyp-player .timeline .duration,cyp-player .timeline .elapsed{flex-basis:5ch;text-align:center}cyp-player .controls{flex-grow:1;flex-basis:0;max-width:220px;flex-direction:column;justify-content:space-around}cyp-player .controls:not([hidden]){display:flex}cyp-player .controls .playback{flex-direction:row;align-items:center;justify-content:space-around}cyp-player .controls .playback:not([hidden]){display:flex}cyp-player .controls .playback .icon{width:40px}cyp-player .controls .playback .icon-play,cyp-player .controls .playback .icon-pause{width:64px}cyp-player .controls .volume{flex-direction:row;align-items:center}cyp-player .controls .volume:not([hidden]){display:flex}cyp-player .controls .volume .mute{margin-right:4px}cyp-player .misc{display:flex;flex-direction:column;align-self:stretch;justify-content:space-around}cyp-player .misc .icon{width:32px}@media (max-width:519px){cyp-player{flex-wrap:wrap;justify-content:space-between}cyp-player .info{order:1;flex-basis:100%;height:96px}}cyp-queue .current{color:var(--primary)}cyp-settings{--spacing:8px;font-size:var(--font-size-large)}cyp-settings dl{margin:var(--spacing);display:grid;grid-template-columns:max-content 1fr;align-items:center;grid-gap:var(--spacing)}cyp-settings dt{font-weight:bold}cyp-settings dd{margin:0;flex-direction:column;align-items:start}cyp-settings dd:not([hidden]){display:flex}cyp-settings label{flex-direction:row;align-items:center}cyp-settings label:not([hidden]){display:flex}cyp-settings label [type=radio],cyp-settings label [type=checkbox]{margin:0 4px 0 0}cyp-yt header{flex-direction:row;align-items:center;padding:var(--spacing)}cyp-yt header:not([hidden]){display:flex}cyp-yt header button{font-size:var(--font-size-large);font-weight:bold;overflow:hidden}cyp-yt header button .icon{margin-right:var(--icon-spacing)}cyp-yt ul{flex-grow:1;overflow:auto;list-style:none;margin:0;padding:0}cyp-yt li{flex-direction:row;align-items:center}cyp-yt li:not([hidden]){display:flex}cyp-yt li .info{flex-grow:1;overflow:hidden}cyp-yt li .info .icon{color:var(--primary);margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-yt li .info h2{font-size:var(--font-size-large);margin:0}cyp-yt li:not(.has-art){padding:8px}cyp-yt li button .icon{width:32px}cyp-yt li:nth-child(odd){background-color:var(--bg-alt)}cyp-yt header{border-bottom:1px solid var(--fg)}cyp-yt header button+button{margin-left:16px}cyp-yt .clear{margin-left:auto}cyp-yt pre{margin:.5em .5ch;flex-grow:1;overflow:auto;white-space:pre-wrap}cyp-yt.pending header{background-image:linear-gradient(var(--primary), var(--primary));background-repeat:no-repeat;background-size:25% 4px;animation:bar ease-in-out 3s alternate infinite}@keyframes bar{0%{background-position:0 100%}100%{background-position:100% 100%}}x-range{--thumb-size:8px;--thumb-color:#ddd;--thumb-shadow:#000;--thumb-hover-color:#fff;--track-size:4px;--track-color:#888;--track-shadow:#000;--elapsed-color:#ddd;--remaining-color:transparent;--radius:calc(var(--track-size)/2);display:inline-block;position:relative;width:192px;height:16px}x-range .-track,x-range .-elapsed,x-range .-remaining{position:absolute;top:calc(50% - var(--track-size)/2);height:var(--track-size);border-radius:var(--radius)}x-range .-track{width:100%;left:0;background-color:var(--track-color);box-shadow:0 0 1px var(--thumb-shadow)}x-range .-elapsed{left:0;background-color:var(--elapsed-color)}x-range .-remaining{right:0;background-color:var(--remaining-color)}x-range .-inner{position:absolute;left:var(--thumb-size);right:var(--thumb-size);top:0;bottom:0}x-range .-thumb{all:unset;position:absolute;top:50%;transform:translate(-50%, -50%);border-radius:50%;width:calc(2*var(--thumb-size));height:calc(2*var(--thumb-size));background-color:var(--thumb-color);box-shadow:0 0 2px var(--thumb-shadow)}x-range[disabled]{opacity:.5}x-range:not([disabled]) .-thumb:hover{background-color:var(--thumb-hover-color)}cyp-playlist{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-playlist:not([hidden]){display:flex}cyp-playlist.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-playlist:nth-child(odd){background-color:var(--bg-alt)}cyp-playlist>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-playlist .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-playlist button:first-of-type{margin-left:auto}cyp-playlist button .icon{width:32px}cyp-playlist:nth-child(odd){background-color:var(--bg-alt)}cyp-library cyp-song .subtitle{display:none}cyp-tag{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-tag:not([hidden]){display:flex}cyp-tag.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-tag:nth-child(odd){background-color:var(--bg-alt)}cyp-tag>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-tag .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-tag button:first-of-type{margin-left:auto}cyp-tag button .icon{width:32px}cyp-back{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-back:not([hidden]){display:flex}cyp-back.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-back:nth-child(odd){background-color:var(--bg-alt)}cyp-back>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-back .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-back button:first-of-type{margin-left:auto}cyp-back button .icon{width:32px}cyp-path{flex-direction:row;align-items:center;border-left:var(--border-width) solid transparent;cursor:pointer;padding:8px;padding-left:calc(8px - var(--border-width))}cyp-path:not([hidden]){display:flex}cyp-path.selected{border-left-color:var(--primary);background-color:var(--primary-tint)}cyp-path:nth-child(odd){background-color:var(--bg-alt)}cyp-path>.icon{flex:none;margin-right:var(--icon-spacing);filter:drop-shadow(var(--text-shadow))}cyp-path .title{font-size:var(--font-size-large);min-width:0;overflow:hidden;text-overflow:ellipsis}cyp-path button:first-of-type{margin-left:auto}cyp-path button .icon{width:32px} \ No newline at end of file diff --git a/app/icons/keyboard-backspace.svg b/app/icons/keyboard-backspace.svg new file mode 100644 index 0000000..2c7e2ae --- /dev/null +++ b/app/icons/keyboard-backspace.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/index.html b/app/index.html index 36cc6da..3aa9189 100644 --- a/app/index.html +++ b/app/index.html @@ -105,5 +105,8 @@ + + + diff --git a/app/js/elements/artist.js b/app/js/elements/artist.js deleted file mode 100644 index c6a4a70..0000000 --- a/app/js/elements/artist.js +++ /dev/null @@ -1,17 +0,0 @@ -import * as html from "../html.js"; -import Item from "../item.js"; - - -export default class Artist extends Item { - constructor(name) { - super(); - this.name = name; - } - - connectedCallback() { - html.icon("artist", this); - this._buildTitle(this.name); - } -} - -customElements.define("cyp-artist", Artist); diff --git a/app/js/elements/back.js b/app/js/elements/back.js new file mode 100644 index 0000000..689dbe1 --- /dev/null +++ b/app/js/elements/back.js @@ -0,0 +1,16 @@ +import Item from "../item.js"; +import * as html from "../html.js"; + + +export default class Back extends Item { + constructor(title) { + super(); + this._title = title; + } + connectedCallback() { + this.appendChild(html.icon("keyboard-backspace")); + this._buildTitle(this._title); + } +} + +customElements.define("cyp-back", Back); diff --git a/app/js/elements/library.js b/app/js/elements/library.js index 867800c..8670dcd 100644 --- a/app/js/elements/library.js +++ b/app/js/elements/library.js @@ -1,11 +1,32 @@ import * as html from "../html.js"; import Component from "../component.js"; -import Artist from "./artist.js"; +import Tag from "./tag.js"; +import Path from "./path.js"; +import Back from "./back.js"; +import Song from "./song.js"; +import { escape, serializeFilter } from "../mpd.js"; +const SORT = "-Track"; + +function createEnqueueCommand(node) { + if (node instanceof Song) { + return `add "${escape(node.data["file"])}"`; + } else if (node instanceof Tag) { + return [ + "findadd", + serializeFilter(node.createChildFilter()), + `sort ${SORT}` + ].join(" "); + } else { + throw new Exception(`Cannot create enqueue command for "${node.nodeName}"`); + } +} + class Library extends Component { constructor() { super({selection:"multi"}); + this._initCommands(); } _onAppLoad() { @@ -25,38 +46,124 @@ class Library extends Component { html.button({icon:"artist"}, "Artists and albums", this) .addEventListener("click", _ => this._listTags("AlbumArtist")); - html.button({icon:"folder"}, "Files and directories", this) - .addEventListener("click", _ => this._listFS("")); + html.button({icon:"folder"}, "Files and directories", this) + .addEventListener("click", _ => this._listPath("")); - html.button({icon:"magnify"}, "Search", this) + html.button({icon:"magnify"}, "Search", this) .addEventListener("click", _ => this._showSearch()); } async _listTags(tag, filter = {}) { const values = await this._mpd.listTags(tag, filter); - html.clear(this); - values.forEach(value => this._buildTagValue(tag, value)); + if ("AlbumArtist" in filter) { this._buildBack(filter); } + values.forEach(value => this._buildTag(tag, value, filter)); } - async _listFS(path) { + async _listPath(path) { + let paths = await this._mpd.listPath(path); + html.clear(this); + path && this._buildBack(path); + paths["directory"].forEach(path => this._buildPath(path)); + paths["file"].forEach(path => this._buildPath(path)); +} + + async _listSongs(filter) { + const songs = await this._mpd.listSongs(filter); + html.clear(this); + this._buildBack(filter); + songs.forEach(song => this.appendChild(new Song(song))); } _showSearch() { } - _buildTagValue(tag, value) { + _buildTag(tag, value, filter) { let node; switch (tag) { case "AlbumArtist": - node = new Artist(value); - node.onClick = () => this._listTags("Album", {[tag]:value}); + node = new Tag(tag, value, filter); + this.appendChild(node); + node.onClick = () => this._listTags("Album", node.createChildFilter()); + break; + + case "Album": + node = new Tag(tag, value, filter); + this.appendChild(node); + node.addButton("chevron-double-right", _ => this._listSongs(node.createChildFilter())); break; } + } + + _buildBack(filterOrPath) { + if (typeof(filterOrPath) == "string") { + const path = filterOrPath.split("/").slice(0, -1).join(""); + const node = new Back(".."); + this.appendChild(node); + node.onClick = () => { + this.selection.clear(); + this._listPath(path); + } + return; + } + + const filter = Object.assign({}, filterOrPath) + let tag, title; + + if ("Album" in filter) { + tag = "Album"; + title = filter["AlbumArtist"]; + } else if ("AlbumArtist" in filter) { + tag = "AlbumArtist"; + title = "Artists"; + } + + delete filter[tag]; + const node = new Back(title); this.appendChild(node); + node.onClick = () => { + this.selection.clear(); + this._listTags(tag, filter); + } + } + + _buildPath(data) { + let node = new Path(data); + this.appendChild(node); + + if ("directory" in data) { + const path = data["directory"]; + node.addButton("chevron-double-right", _ => this._listPath(path)); + } + } + + _initCommands() { + const sel = this.selection; + + sel.addCommandAll(); + + sel.addCommand(async items => { + const commands = [ + "clear", + ...items.map(createEnqueueCommand), + "play" + ]; + await this._mpd.command(commands); + this.selection.clear(); + this._app.dispatchEvent(new CustomEvent("queue-change")); // fixme notification? + }, {label:"Play", icon:"play"}); + + sel.addCommand(async items => { + const commands = items.map(createEnqueueCommand); + await this._mpd.command(commands); + this.selection.clear(); + this._app.dispatchEvent(new CustomEvent("queue-change")); // fixme notification? + }, {label:"Enqueue", icon:"plus"}); + + sel.addCommandCancel(); } } diff --git a/app/js/elements/path.js b/app/js/elements/path.js new file mode 100644 index 0000000..dacc129 --- /dev/null +++ b/app/js/elements/path.js @@ -0,0 +1,28 @@ +import Item from "../item.js"; +import * as html from "../html.js"; + + +function baseName(path) { + return path.split("/").pop(); +} + +export default class Path extends Item { + constructor(data) { + super(); + this.data = data; + // FIXME spis ._data a .url + } + connectedCallback() { + let path; + if ("directory" in this.data) { + this.appendChild(html.icon("folder")); + path = this.data["directory"]; + } else { + this.appendChild(html.icon("music")); + path = this.data["file"]; + } + this._buildTitle(path); + } +} + +customElements.define("cyp-path", Path); diff --git a/app/js/elements/playlists.js b/app/js/elements/playlists.js index 8369084..4b6a655 100644 --- a/app/js/elements/playlists.js +++ b/app/js/elements/playlists.js @@ -1,7 +1,7 @@ import * as html from "../html.js"; import Component from "../component.js"; import Playlist from "./playlist.js"; - +import { escape } from "../mpd.js"; class Playlists extends Component { constructor() { @@ -31,7 +31,7 @@ class Playlists extends Component { sel.addCommand(async item => { const name = item.name; - const commands = ["clear", `load "${this._mpd.escape(name)}"`, "play"]; + const commands = ["clear", `load "${escape(name)}"`, "play"]; await this._mpd.command(commands); this.selection.clear(); this._app.dispatchEvent(new CustomEvent("queue-change")); // fixme notification? @@ -39,7 +39,7 @@ class Playlists extends Component { sel.addCommand(async item => { const name = item.name; - await this._mpd.command(`load "${this._mpd.escape(name)}"`); + await this._mpd.command(`load "${escape(name)}"`); this.selection.clear(); this._app.dispatchEvent(new CustomEvent("queue-change")); // fixme notification? }, {label:"Enqueue", icon:"plus"}); @@ -48,7 +48,7 @@ class Playlists extends Component { const name = item.name; if (!confirm(`Really delete playlist '${name}'?`)) { return; } - await this._mpd.command(`rm "${this._mpd.escape(name)}"`); + await this._mpd.command(`rm "${escape(name)}"`); this._sync(); }, {label:"Delete", icon:"delete"}); diff --git a/app/js/elements/queue.js b/app/js/elements/queue.js index 905c291..23d2983 100644 --- a/app/js/elements/queue.js +++ b/app/js/elements/queue.js @@ -1,6 +1,7 @@ import * as html from "../html.js"; import Component from "../component.js"; import Song from "./song.js"; +import { escape } from "../mpd.js"; function generateMoveCommands(items, diff, all) { @@ -69,8 +70,7 @@ class Queue extends Component { const node = new Song(song); this.appendChild(node); - html.button({icon:"play"}, "", node).addEventListener("click", async e => { - e.stopPropagation(); // do not select + node.addButton("play", async _ => { await this._mpd.command(`playid ${song["Id"]}`); }); }); @@ -99,9 +99,9 @@ class Queue extends Component { let name = prompt("Save selected songs as a playlist?", "name"); if (name === null) { return; } - name = this._mpd.escape(name); + name = escape(name); const commands = items.map(item => { - return `playlistadd "${name}" "${this._mpd.escape(item.data["file"])}"`; + return `playlistadd "${name}" "${escape(item.data["file"])}"`; }); await this._mpd.command(commands); // FIXME notify? diff --git a/app/js/elements/tag.js b/app/js/elements/tag.js new file mode 100644 index 0000000..223eb09 --- /dev/null +++ b/app/js/elements/tag.js @@ -0,0 +1,29 @@ +import * as html from "../html.js"; +import Item from "../item.js"; + +const ICONS = { + "AlbumArtist": "artist", + "Album": "album" +} + + +export default class Tag extends Item { + constructor(type, value, filter) { + super(); + this._type = type; + this._value = value; + this._filter = filter; + } + + connectedCallback() { + const icon = ICONS[this._type]; + html.icon(icon, this); + this._buildTitle(this._value); + } + + createChildFilter() { + return Object.assign({[this._type]:this._value}, this._filter); + } +} + +customElements.define("cyp-tag", Tag); diff --git a/app/js/elements/yt.js b/app/js/elements/yt.js index 0d2a493..8ae8833 100644 --- a/app/js/elements/yt.js +++ b/app/js/elements/yt.js @@ -1,6 +1,6 @@ import * as html from "../html.js"; import * as conf from "../conf.js"; - +import { escape } from "../mpd.js"; import Component from "../component.js"; @@ -58,7 +58,7 @@ class YT extends Component { this.classList.remove("pending"); if (response.status == 200) { - this._mpd.command(`update ${this._mpd.escape(conf.ytPath)}`); + this._mpd.command(`update ${escape(conf.ytPath)}`); } } diff --git a/app/js/icons.js b/app/js/icons.js index 2e90081..0cbdbbb 100644 --- a/app/js/icons.js +++ b/app/js/icons.js @@ -23,6 +23,9 @@ ICONS["artist"] = ` ICONS["volume-off"] = ` `; +ICONS["keyboard-backspace"] = ` + +`; ICONS["cancel"] = ` `; diff --git a/app/js/item.js b/app/js/item.js index e4b654f..10b89f9 100644 --- a/app/js/item.js +++ b/app/js/item.js @@ -6,6 +6,13 @@ export default class Item extends HTMLElement { this.addEventListener("click", _ => this.onClick()); } + addButton(icon, cb) { + html.button({icon}, "", this).addEventListener("click", e => { + e.stopPropagation(); // do not select + cb(); + }); + } + onClick() { this.parentNode.selection.toggle(this); } _buildTitle(title) { diff --git a/app/js/mpd-mock.js b/app/js/mpd-mock.js index a396109..7ec493d 100644 --- a/app/js/mpd-mock.js +++ b/app/js/mpd-mock.js @@ -1,7 +1,3 @@ -import * as mpd from "./mpd.js"; - -export const escape = mpd.escape; - export function command(cmd) { console.warn(`mpd-mock does not know "${cmd}"`); } @@ -34,7 +30,7 @@ export function listQueue() { ]; } -export async function listPlaylists() { +export function listPlaylists() { return [ "Playlist 1", "Playlist 2", @@ -42,34 +38,33 @@ export async function listPlaylists() { ]; } -export async function enqueueByFilter(filter, sort = null) { - let tokens = ["findadd"]; - tokens.push(serializeFilter(filter)); -// sort && tokens.push("sort", sort); FIXME not implemented in MPD - return command(tokens.join(" ")); +export function listPath(path) { + return { + "directory": [ + {"directory": "Dir 1"}, + {"directory": "Dir 2"}, + {"directory": "Dir 3"} + ], + "file": [ + {"file": "File 1"}, + {"file": "File 2"}, + {"file": "File 3"} + ] + } } -export async function listPath(path) { - let lines = await command(`lsinfo "${escape(path)}"`); - return parser.pathContents(lines); -} - -export async function listTags(tag, filter = null) { +export function listTags(tag, filter = null) { switch (tag) { case "AlbumArtist": return ["Artist 1", "Artist 2", "Artist 3"]; case "Album": return ["Album 1", "Album 2", "Album 3"]; } } -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); +export function listSongs(filter, window = null) { + return listQueue(); } -export async function albumArt(songUrl) { +export function albumArt(songUrl) { return null; } diff --git a/app/js/mpd.js b/app/js/mpd.js index 49afefe..e403aef 100644 --- a/app/js/mpd.js +++ b/app/js/mpd.js @@ -37,7 +37,7 @@ function processQueue() { ws.send(current.cmd); } -function serializeFilter(filter) { +export function serializeFilter(filter) { let tokens = ["("]; Object.entries(filter).forEach(([key, value], index) => { index && tokens.push(" AND "); @@ -90,13 +90,6 @@ export async function listPlaylists() { return (list instanceof Array ? list : [list]); } -export async function enqueueByFilter(filter, sort = null) { - let tokens = ["findadd"]; - tokens.push(serializeFilter(filter)); -// 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);