1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-06-25 22:23:16 +00:00

Merge pull request #71 from jcorporation/devel

Merge devel into master for 4.5.0 release
This commit is contained in:
Jürgen Mang 2018-11-04 19:37:56 +01:00 committed by GitHub
commit d7a63016ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1279 additions and 555 deletions

View File

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.6)
project (mympd C)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
set(CPACK_PACKAGE_VERSION_MAJOR "4")
set(CPACK_PACKAGE_VERSION_MINOR "4")
set(CPACK_PACKAGE_VERSION_MINOR "5")
set(CPACK_PACKAGE_VERSION_PATCH "0")
if(CMAKE_BUILD_TYPE MATCHES RELEASE)
@ -58,4 +58,5 @@ install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs)
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/pics)
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/tmp)
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/state)
install(DIRECTORY DESTINATION ../etc/${PROJECT_NAME}/syscmds)
install(DIRECTORY dist/smartpls DESTINATION ../var/lib/${PROJECT_NAME})

View File

@ -4,7 +4,7 @@
pkgname=mympd
_pkgname=myMPD
pkgver=4.4.0
pkgver=4.5.0
pkgrel=1
pkgdesc="myMPD is a standalone and mobile friendly web mpdclient."
arch=('x86_64' 'armv7h' 'aarch64')

View File

@ -66,6 +66,11 @@ post_upgrade() {
[ -f /var/lib/mympd/state/jukeboxQueueLength ] || echo -n "1" > /var/lib/mympd/state/jukeboxQueueLength
[ -f /var/lib/mympd/state/notificationPage ] || echo -n "true" > /var/lib/mympd/state/notificationPage
[ -f /var/lib/mympd/state/notificationWeb ] || echo -n "false" > /var/lib/mympd/state/notificationWeb
[ -f /var/lib/mympd/state/colsBrowseDatabase ] || echo -n '["Track","Title","Duration"]' > /var/lib/mympd/state/colsBrowseDatabase
[ -f /var/lib/mympd/state/colsBrowseFilesystem ] || echo -n '["Type","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowseFilesystem
[ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail
[ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue
[ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch
# fix ownership of /var/lib/mympd
echo "INFO: Fixing ownership of /var/lib/mympd"

View File

@ -4,7 +4,7 @@
# (c) 2018 Juergen Mang <mail@jcgames.de>
Name: myMPD
Version: 4.4.0
Version: 4.5.0
Release: 0
License: GPL-2.0
Group: Productivity/Multimedia/Sound/Players
@ -81,6 +81,11 @@ done
[ -f /var/lib/mympd/state/jukeboxQueueLength ] || echo -n "1" > /var/lib/mympd/state/jukeboxQueueLength
[ -f /var/lib/mympd/state/notificationPage ] || echo -n "true" > /var/lib/mympd/state/notificationPage
[ -f /var/lib/mympd/state/notificationWeb ] || echo -n "false" > /var/lib/mympd/state/notificationWeb
[ -f /var/lib/mympd/state/colsBrowseDatabase ] || echo -n '["Track","Title","Duration"]' > /var/lib/mympd/state/colsBrowseDatabase
[ -f /var/lib/mympd/state/colsBrowseFilesystem ] || echo -n '["Type","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowseFilesystem
[ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail
[ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue
[ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch
echo "Fixing ownership of /var/lib/mympd"
chown -R mympd.mympd /var/lib/mympd

View File

@ -17,11 +17,20 @@ sslkey = /etc/mympd/ssl/server.key
#myMPD user
user = mympd
#Enable local player, needs streamport or streamurl setting
localplayer = true
#Port for mpd http stream
streamport = 8000
#Manual streamurl, overwrites streamport
#streamurl = http://jukebox:8000
#Enable coverimages
coverimage = true
#Name for coverimages
coverimage = folder.jpg
coverimagename = folder.jpg
#myMPD state directory
varlibdir = /var/lib/mympd
@ -34,9 +43,14 @@ stickers = true
#List of tags in myMPD gui
taglist = Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer
searchtaglist = Artist,Album,AlbumArtist,Title,Genre,Composer,Performer
browsetaglist = Artist,Album,AlbumArtist,Genre,Composer,Performer
#Enable smart playlists
smartpls = true
#Enable system commands in /etc/mympd/syscmds/
syscmds = false
#Elements per page for pagination, max: 400
max_elements_per_page = 100

1
contrib/syscmds/0Reboot Normal file
View File

@ -0,0 +1 @@
sudo /sbin/reboot

View File

@ -0,0 +1 @@
sudo /sbin/halt

5
debian/postinst vendored
View File

@ -61,6 +61,11 @@ fi
[ -f /var/lib/mympd/state/jukeboxQueueLength ] || echo -n "1" > /var/lib/mympd/state/jukeboxQueueLength
[ -f /var/lib/mympd/state/notificationPage ] || echo -n "true" > /var/lib/mympd/state/notificationPage
[ -f /var/lib/mympd/state/notificationWeb ] || echo -n "false" > /var/lib/mympd/state/notificationWeb
[ -f /var/lib/mympd/state/colsBrowseDatabase ] || echo -n '["Track","Title","Duration"]' > /var/lib/mympd/state/colsBrowseDatabase
[ -f /var/lib/mympd/state/colsBrowseFilesystem ] || echo -n '["Type","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowseFilesystem
[ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail
[ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue
[ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch
echo "Fixing ownership of /var/lib/mympd"
chown -R mympd.mympd /var/lib/mympd

View File

@ -1 +1 @@
html{position:relative;min-height:100%}body{margin-bottom:60px;padding-top:50px;padding-bottom:50px;background-color:#888}main{padding-top:20px}footer{position:absolute;bottom:0}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}@media only screen and (max-width:576px){.header-logo{display:none!important}}.clickable{cursor:pointer}.tblnum,.tblaction{width:30px}small{color:#aaa}.card-footer-playback{padding:0}.album-cover{background-size:cover;background-image:url("/assets/coverimage-loading.png");border:1px solid black;border-radius:5px;overflow:hidden;width:240px;height:240px;background-color:#eee;float:left;margin-right:20px;margin-bottom:20px}.album-desc{min-width:240px;float:left}.hide{display:none!important}.pull-right{float:right!important}.card-toolbar{margin-bottom:10px}.card-toolbar>div,.card-toolbar>form{margin-bottom:5px}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(/assets/MaterialIcons-Regular.woff2) format('woff2'),url(/assets/MaterialIcons-Regular.woff) format('woff'),url(/assets/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:18px;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;vertical-align:top;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}.material-icons-left{font-size:1rem;margin-left:-1em;vertical-align:middle}.material-icons-small{font-size:16px}.material-icons-small-left{font-size:1rem;margin-left:-1em}.color-darkgrey,.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}.progressBarPlay{font-size:22px}#counter{cursor:text}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-top{overflow:hidden;display:block;padding-top:100%;background-size:cover;background-image:url(/assets/coverimage-loading.png)}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}button.active-fg-green{color:#28a745!important}button.active-fg-red{color:#bd2130!important}div#alertBox{position:fixed;top:50px;right:10px;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5}caption{caption-side:top;font-size:120%;font-weight:bold;color:black}.dragover>td{border-top:25px solid transparent}[draggable]{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;user-select:none;-khtml-user-drag:element;-webkit-user-drag:element}@keyframes changewidth{0%{margin-left:-20px}to{margin-left:100%}}#updateDBprogress{width:20px}.updateDBprogressAnimate{animation-duration:2s;animation-name:changewidth;animation-iteration-count:infinite}.modal-body{overflow-x:hidden}.modal-body .album-cover{float:none}#BrowseDatabaseAlbumListCaption{margin-left:15px;margin-right:15px;width:100%}#BrowseDatabaseAlbumListCaption h2{display:inline}#BrowseDatabaseAlbumListCaption small{padding-top:.8rem}#menu-dbupdate{padding-left:1rem}
html{position:relative;min-height:100%}body{margin-bottom:60px;padding-top:50px;padding-bottom:50px;background-color:#888}main{padding-top:20px}footer{position:absolute;bottom:0}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}@media only screen and (max-width:576px){.header-logo{display:none!important}}.clickable{cursor:pointer}[data-col=Pos],[data-col=Type],[data-col=Track],[data-col=Action]{width:30px}small{color:#aaa}.card{min-height:300px}.card-footer-playback{padding:0}.album-cover{background-size:cover;background-image:url("/assets/coverimage-loading.png");border:1px solid black;border-radius:5px;overflow:hidden;width:240px;height:240px;background-color:#eee;float:left;margin-right:20px;margin-bottom:20px}.album-desc{min-width:240px;float:left}.hide{display:none!important}.pull-right{float:right!important}.card-toolbar{margin-bottom:10px}.card-toolbar>div,.card-toolbar>form{margin-bottom:5px}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(/assets/MaterialIcons-Regular.woff2) format('woff2'),url(/assets/MaterialIcons-Regular.woff) format('woff'),url(/assets/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:18px;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;vertical-align:top;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}.material-icons-left{font-size:1rem;margin-left:-1em;vertical-align:middle}.material-icons-small{font-size:16px}.material-icons-small-left{font-size:1rem;margin-left:-1em}.color-darkgrey,.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}.progressBarPlay{font-size:22px}#counter{cursor:text}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-left{overflow:hidden;display:block;width:250px;height:250px;border-radius:5px;background-size:cover;background-image:url(/assets/coverimage-loading.png);margin-bottom:5px;cursor:pointer}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}button.active-fg-green{color:#28a745!important}button.active-fg-red{color:#bd2130!important}div#alertBox{position:fixed;top:50px;right:10px;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5}caption{caption-side:top;font-size:120%;font-weight:bold;color:black}.dragover>td{border-top:2px solid #28a745}.dragover-th{border-left:2px solid #28a745}[draggable]{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;user-select:none;-khtml-user-drag:element;-webkit-user-drag:element;cursor:pointer}@keyframes changewidth{0%{margin-left:-20px}to{margin-left:100%}}#updateDBprogress{width:20px}.updateDBprogressAnimate{animation-duration:2s;animation-name:changewidth;animation-iteration-count:infinite}.modal-body{overflow-x:hidden}.modal-body .album-cover{float:none}#BrowseDatabaseAlbumListCaption{margin-left:15px;margin-right:15px;width:100%}#BrowseDatabaseAlbumListCaption h2{display:inline}#BrowseDatabaseAlbumListCaption small{padding-top:.8rem}#menu-dbupdate{padding-left:1rem}

File diff suppressed because one or more lines are too long

View File

@ -2,8 +2,9 @@ var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO
$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.Symbol=function(){var a=0;return function(b){return $jscomp.SYMBOL_PREFIX+(b||"")+a++}}();
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var a=$jscomp.global.Symbol.iterator;a||(a=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&$jscomp.defineProperty(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(a){var b=0;return $jscomp.iteratorPrototype(function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}})};
$jscomp.iteratorPrototype=function(a){$jscomp.initSymbolIterator();a={next:a};a[$jscomp.global.Symbol.iterator]=function(){return this};return a};$jscomp.makeIterator=function(a){$jscomp.initSymbolIterator();var b=a[Symbol.iterator];return b?b.call(a):$jscomp.arrayIterator(a)};$jscomp.arrayFromIterator=function(a){for(var b,c=[];!(b=a.next()).done;)c.push(b.value);return c};$jscomp.arrayFromIterable=function(a){return a instanceof Array?a:$jscomp.arrayFromIterator($jscomp.makeIterator(a))};
$jscomp.checkStringArgs=function(a,b,c){if(null==a)throw new TypeError("The 'this' value for String.prototype."+c+" must not be null or undefined");if(b instanceof RegExp)throw new TypeError("First argument to String.prototype."+c+" must not be a regular expression");return a+""};
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Object.is",function(a){return a?a:function(a,c){return a===c?0!==a||1/a===1/c:a!==a&&c!==c}},"es6","es3");
$jscomp.polyfill("Array.prototype.includes",function(a){return a?a:function(a,c){var d=this;d instanceof String&&(d=String(d));var b=d.length;c=c||0;for(0>c&&(c=Math.max(c+b,0));c<b;c++){var f=d[c];if(f===a||Object.is(f,a))return!0}return!1}},"es7","es3");
$jscomp.checkStringArgs=function(a,b,c){if(null==a)throw new TypeError("The 'this' value for String.prototype."+c+" must not be null or undefined");if(b instanceof RegExp)throw new TypeError("First argument to String.prototype."+c+" must not be a regular expression");return a+""};$jscomp.polyfill("String.prototype.includes",function(a){return a?a:function(a,c){return-1!==$jscomp.checkStringArgs(this,a,"includes").indexOf(a,c||0)}},"es6","es3");
$jscomp.polyfill("String.prototype.repeat",function(a){return a?a:function(a){var b=$jscomp.checkStringArgs(this,null,"repeat");if(0>a||1342177279<a)throw new RangeError("Invalid count value");a|=0;for(var d="";a;)if(a&1&&(d+=b),a>>>=1)b+=b;return d}},"es6","es3");
var socket,lastSong="",lastState,currentSong={},playstate="",settings={},alertTimeout,progressTimer,deferredPrompt,dragEl,playlistEl,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/any/",scrollPos:0},Browse:{active:"Database",tabs:{Filesystem:{state:"0/-/",scrollPos:0},Playlists:{active:"All",views:{All:{state:"0/-/",scrollPos:0},Detail:{state:"0/-/",scrollPos:0}}},Database:{active:"AlbumArtist",views:{}}}},Search:{state:"0/any/",scrollPos:0}},current:{app:"Playback",tab:void 0,view:void 0,
page:0,filter:"",search:"",scrollPos:0},last:{app:void 0,tab:void 0,view:void 0,filter:"",search:"",scrollPos:0}},domCache={};domCache.navbarBottomBtns=document.getElementById("navbar-bottom").getElementsByTagName("div");domCache.navbarBottomBtnsLen=domCache.navbarBottomBtns.length;domCache.panelHeadingBrowse=document.getElementById("panel-heading-browse").getElementsByTagName("a");domCache.panelHeadingBrowseLen=domCache.panelHeadingBrowse.length;domCache.counter=document.getElementById("counter");
@ -18,7 +19,7 @@ function appGoto(a,b,c,d){var e=document.body.scrollTop?document.body.scrollTop:
(void 0==b&&(b=app.apps[a].active),app.apps[a].tabs[b].views?(void 0==c&&(c=app.apps[a].tabs[b].active),a="/"+a+"/"+b+"/"+c+"!"+(void 0==d?app.apps[a].tabs[b].views[c].state:d)):a="/"+a+"/"+b+"!"+(void 0==d?app.apps[a].tabs[b].state:d)):a="/"+a+"!"+(void 0==d?app.apps[a].state:d);location.hash=a}
function appRoute(){var a;if(a=decodeURI(location.hash).match(/^#\/(\w+)\/?(\w+)?\/?(\w+)?!((\d+)\/([^\/]+)\/(.*))$/)){app.current.app=a[1];app.current.tab=a[2];app.current.view=a[3];app.apps[app.current.app].state?(app.apps[app.current.app].state=a[4],app.current.scrollPos=app.apps[app.current.app].scrollPos):app.apps[app.current.app].tabs[app.current.tab].state?(app.apps[app.current.app].tabs[app.current.tab].state=a[4],app.apps[app.current.app].active=app.current.tab,app.current.scrollPos=app.apps[app.current.app].tabs[app.current.tab].scrollPos):
app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].state&&(app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].state=a[4],app.apps[app.current.app].active=app.current.tab,app.apps[app.current.app].tabs[app.current.tab].active=app.current.view,app.current.scrollPos=app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].scrollPos);app.current.page=parseInt(a[5]);app.current.filter=a[6];app.current.search=a[7];appPrepare(app.current.scrollPos);
if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_PLAYER_CURRENT_SONG"},songChange);else if("Queue"==app.current.app)selectTag("searchqueuetag","searchqueuetagdesc",app.current.filter),getQueue();else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view)sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&
if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_PLAYER_CURRENT_SONG"},songChange);else if("Queue"==app.current.app)selectTag("searchqueuetags","searchqueuetagsdesc",app.current.filter),getQueue();else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view)sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&
"Detail"==app.current.view)sendAPI({cmd:"MPD_API_PLAYLIST_CONTENT_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab)""!=app.current.search?(sendAPI({cmd:"MPD_API_DATABASE_TAG_ALBUM_LIST",data:{offset:app.current.page,filter:app.current.filter,search:app.current.search,tag:app.current.view}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter")):
(sendAPI({cmd:"MPD_API_DATABASE_TAG_LIST",data:{offset:app.current.page,filter:app.current.filter,tag:app.current.view}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter"),selectTag("BrowseDatabaseByTagDropdown","btnBrowseDatabaseByTag",app.current.view));else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){sendAPI({cmd:"MPD_API_DATABASE_FILESYSTEM_LIST",data:{offset:app.current.page,path:app.current.search?app.current.search:"/",filter:app.current.filter}},parseFilesystem);app.current.search?
(document.getElementById("BrowseFilesystemAddAllSongs").removeAttribute("disabled"),document.getElementById("BrowseFilesystemAddAllSongsBtn").removeAttribute("disabled")):(document.getElementById("BrowseFilesystemAddAllSongs").setAttribute("disabled","disabled"),document.getElementById("BrowseFilesystemAddAllSongsBtn").setAttribute("disabled","disabled"));var b='<li class="breadcrumb-item"><a data-uri="">root</a></li>',c=app.current.search.split("/"),d=c.length,e="";for(a=0;a<d;a++){if(d-1==a){b+=
@ -31,81 +32,100 @@ a.innerText?"keyboard_arrow_down":"keyboard_arrow_right"},!1);document.getElemen
document.getElementById("modalSaveQueue").addEventListener("shown.bs.modal",function(){var a=document.getElementById("saveQueueName");a.focus();a.value="";a.classList.remove("is-invalid");document.getElementById("saveQueueFrm").classList.remove("was-validated")});document.getElementById("modalSettings").addEventListener("shown.bs.modal",function(){getSettings();document.getElementById("settingsFrm").classList.remove("was-validated");document.getElementById("inputCrossfade").classList.remove("is-invalid");
document.getElementById("inputMixrampdb").classList.remove("is-invalid");document.getElementById("inputMixrampdelay").classList.remove("is-invalid")});document.getElementById("selectJukeboxMode").addEventListener("change",function(){var a=this.options[this.selectedIndex].value;0==a||2==a?(document.getElementById("inputJukeboxQueueLength").setAttribute("disabled","disabled"),document.getElementById("selectJukeboxPlaylist").setAttribute("disabled","disabled")):1==a&&(document.getElementById("inputJukeboxQueueLength").removeAttribute("disabled"),
document.getElementById("selectJukeboxPlaylist").removeAttribute("disabled"))});document.getElementById("addToPlaylistPlaylist").addEventListener("change",function(a){"New Playlist"==this.options[this.selectedIndex].text?(document.getElementById("addToPlaylistNewPlaylistDiv").classList.remove("hide"),document.getElementById("addToPlaylistNewPlaylist").focus()):document.getElementById("addToPlaylistNewPlaylistDiv").classList.add("hide")},!1);addFilterLetter("BrowseFilesystemFilterLetters");addFilterLetter("BrowseDatabaseFilterLetters");
addFilterLetter("BrowsePlaylistsFilterLetters");for(var a=document.querySelectorAll("[data-href]"),b=a.length,c=0;c<b;c++)a[c].classList.add("clickable"),a[c].addEventListener("click",function(a){a.preventDefault();a=JSON.parse(this.getAttribute("data-href"));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1);a=document.getElementsByClassName("pages");
b=a.length;for(c=0;c<b;c++)a[c].addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&gotoPage(a.target.getAttribute("data-page"))},!1);document.getElementById("outputs").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&a.stopPropagation();sendAPI({cmd:"MPD_API_PLAYER_TOGGLE_OUTPUT",data:{output:a.target.getAttribute("data-output-id"),state:a.target.classList.contains("active")?0:1}});toggleBtn(a.target.id)},!1);document.getElementById("QueueList").addEventListener("click",
function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAYER_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemList").addEventListener("click",function(a){if("TD"==a.target.nodeName)switch(a.target.parentNode.getAttribute("data-type")){case "dir":appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+decodeURI(a.target.parentNode.getAttribute("data-uri")));break;case "song":appendQueue("song",
decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"));break;case "plist":appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"))}else"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsAllList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):
"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsDetailList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseDatabaseTagList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database",app.current.view,"0/-/"+a.target.parentNode.getAttribute("data-uri"))},
!1);document.getElementById("SearchList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromBrowse():"Add all to playlist"==a.target.innerText&&
showAddToPlaylist(app.current.search))},!1);document.getElementById("searchAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromSearchPlist("queue"):"Add all to playlist"==a.target.innerText?showAddToPlaylist("SEARCH"):"Save as smart playlist"==a.target.innerText&&saveSearchAsSmartPlaylist())},!1);document.getElementById("BrowseDatabaseAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&
("Add all to queue"==a.target.innerText?addAllFromBrowseDatabasePlist("queue"):"Add all to playlist"==a.target.innerText&&showAddToPlaylist("DATABASE"))},!1);document.getElementById("searchtags").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,"0/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app,app.current.tab,
app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("search").addEventListener("submit",function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1);document.getElementById("searchstr").addEventListener("keyup",
function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("BrowseDatabaseByTagDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,a.target.getAttribute("data-tag"),"0/"+app.current.filter+"/"+app.current.search)},!1);document.getElementsByTagName("body")[0].addEventListener("click",function(a){hideMenu()},!1);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");
window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus",function(){sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName&&"SELECT"!=a.target.tagName){if(a.shiftKey)switch(a.which){case 83:sendAPI({cmd:"MPD_API_QUEUE_SHUFFLE"});break;case 67:sendAPI({cmd:"MPD_API_QUEUE_CROP"});break;default:return}else switch(a.which){case 37:clickPrev();break;case 39:clickNext();break;case 32:clickPlay();break;case 83:clickStop();
break;case 173:chVolume(-5);break;case 171:chVolume(5);break;case 67:sendAPI({cmd:"MPD_API_QUEUE_CLEAR"});break;default:return}a.preventDefault()}},!1);"serviceWorker"in navigator&&"https"==document.URL.substring(0,5)&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.min.js",{scope:"/"}).then(function(a){console.log("ServiceWorker registration successful with scope: ",a.scope);a.update()},function(a){console.log("ServiceWorker registration failed: ",a)})});window.addEventListener("beforeinstallprompt",
function(a){a.preventDefault();deferredPrompt=a});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a;domCache.btnAdd.classList.remove("hide")});domCache.btnAdd.addEventListener("click",function(a){domCache.btnAdd.classList.add("hide");deferredPrompt.prompt();deferredPrompt.userChoice.then(function(a){"accepted"===a.outcome?console.log("User accepted the A2HS prompt"):console.log("User dismissed the A2HS prompt");deferredPrompt=null})});window.addEventListener("appinstalled",
function(a){console.log("myMPD installed as app")})}
function dragAndDropTable(a){var b=document.getElementById(a).getElementsByTagName("tbody")[0];b.addEventListener("dragstart",function(a){"TR"==a.target.nodeName&&(a.target.classList.add("opacity05"),a.dataTransfer.setDragImage(a.target,0,0),a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("Text",a.target.getAttribute("id")),dragEl=a.target.cloneNode(!0))},!1);b.addEventListener("dragleave",function(a){a.preventDefault();var b=a.target;"TD"==a.target.nodeName&&(b=a.target.parentNode);"TR"==
b.nodeName&&b.classList.remove("dragover")},!1);b.addEventListener("dragover",function(a){a.preventDefault();for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover");c=a.target;"TD"==a.target.nodeName&&(c=a.target.parentNode);"TR"==c.nodeName&&c.classList.add("dragover");a.dataTransfer.dropEffect="move"},!1);b.addEventListener("dragend",function(a){for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover");
document.getElementById(a.dataTransfer.getData("Text"))&&document.getElementById(a.dataTransfer.getData("Text")).classList.remove("opacity05")},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();var d=c.target;"TD"==c.target.nodeName&&(d=c.target.parentNode);var e=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),f=d.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05");
b.insertBefore(dragEl,d);c=b.getElementsByClassName("dragover");d=c.length;for(var g=0;g<d;g++)c[g].classList.remove("dragover");document.getElementById(a).classList.add("opacity05");"Queue"==app.current.app?sendAPI({cmd:"MPD_API_QUEUE_MOVE_TRACK",data:{from:e,to:f}}):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view&&playlistMoveTrack(e,f)},!1)}
function playlistMoveTrack(a,b){sendAPI({cmd:"MPD_API_PLAYLIST_MOVE_TRACK",data:{plist:app.current.search,from:a,to:b}})}
addFilterLetter("BrowsePlaylistsFilterLetters");document.getElementById("syscmds").addEventListener("click",function(a){a.preventDefault();"A"==a.target.nodeName&&(a=JSON.parse(a.target.getAttribute("data-href")),"function"===typeof window[a.cmd]&&window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options)))},!1);for(var a=document.querySelectorAll("[data-href]"),b=a.length,c=0;c<b;c++)a[c].classList.add("clickable"),a[c].addEventListener("click",function(a){a.preventDefault();a=JSON.parse(this.getAttribute("data-href"));
if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1);a=document.getElementsByClassName("pages");b=a.length;for(c=0;c<b;c++)a[c].addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&gotoPage(a.target.getAttribute("data-page"))},!1);document.getElementById("outputs").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&a.stopPropagation();
sendAPI({cmd:"MPD_API_PLAYER_TOGGLE_OUTPUT",data:{output:a.target.getAttribute("data-output-id"),state:a.target.classList.contains("active")?0:1}});toggleBtn(a.target.id)},!1);document.getElementById("QueueList").addEventListener("click",function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAYER_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemList").addEventListener("click",
function(a){if("TD"==a.target.nodeName)switch(a.target.parentNode.getAttribute("data-type")){case "dir":appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+decodeURI(a.target.parentNode.getAttribute("data-uri")));break;case "song":appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"));break;case "plist":appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"))}else"A"==
a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsAllList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsDetailList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),
a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseDatabaseTagList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database",app.current.view,"0/-/"+a.target.parentNode.getAttribute("data-uri"))},!1);document.getElementById("SearchList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):
"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromBrowse():"Add all to playlist"==a.target.innerText&&showAddToPlaylist(app.current.search))},!1);document.getElementById("searchAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromSearchPlist("queue"):
"Add all to playlist"==a.target.innerText?showAddToPlaylist("SEARCH"):"Save as smart playlist"==a.target.innerText&&saveSearchAsSmartPlaylist())},!1);document.getElementById("BrowseDatabaseAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromBrowseDatabasePlist("queue"):"Add all to playlist"==a.target.innerText&&showAddToPlaylist("DATABASE"))},!1);document.getElementById("searchtags").addEventListener("click",function(a){"BUTTON"==
a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,"0/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app,app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetags").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+
"/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("QueueColsDropdown").addEventListener("click",function(a){"INPUT"==a.target.nodeName&&a.stopPropagation()},!1);document.getElementById("BrowseFilesystemColsDropdown").addEventListener("click",function(a){"INPUT"==a.target.nodeName&&a.stopPropagation()},!1);document.getElementById("SearchColsDropdown").addEventListener("click",function(a){"INPUT"==a.target.nodeName&&a.stopPropagation()},!1);document.getElementById("BrowsePlaylistsDetailColsDropdown").addEventListener("click",
function(a){"INPUT"==a.target.nodeName&&a.stopPropagation()},!1);document.getElementById("BrowseDatabaseColsDropdown").addEventListener("click",function(a){"INPUT"==a.target.nodeName&&a.stopPropagation()},!1);document.getElementById("search").addEventListener("submit",function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1);document.getElementById("searchstr").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+
"/"+this.value)},!1);document.getElementById("BrowseDatabaseByTagDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,a.target.getAttribute("data-tag"),"0/"+app.current.filter+"/"+app.current.search)},!1);document.getElementsByTagName("body")[0].addEventListener("click",function(a){hideMenu()},!1);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");dragAndDropTableHeader("Queue");dragAndDropTableHeader("Search");
dragAndDropTableHeader("BrowseFilesystem");dragAndDropTableHeader("BrowsePlaylistsDetail");window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus",function(){sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName&&"SELECT"!=a.target.tagName){if(a.shiftKey)switch(a.which){case 83:sendAPI({cmd:"MPD_API_QUEUE_SHUFFLE"});break;case 67:sendAPI({cmd:"MPD_API_QUEUE_CROP"});break;default:return}else switch(a.which){case 37:clickPrev();
break;case 39:clickNext();break;case 32:clickPlay();break;case 83:clickStop();break;case 173:chVolume(-5);break;case 171:chVolume(5);break;case 67:sendAPI({cmd:"MPD_API_QUEUE_CLEAR"});break;default:return}a.preventDefault()}},!1);"serviceWorker"in navigator&&"https"==document.URL.substring(0,5)&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.min.js",{scope:"/"}).then(function(a){console.log("ServiceWorker registration successful with scope: ",a.scope);a.update()},
function(a){console.log("ServiceWorker registration failed: ",a)})});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a;domCache.btnAdd.classList.remove("hide")});domCache.btnAdd.addEventListener("click",function(a){domCache.btnAdd.classList.add("hide");deferredPrompt.prompt();deferredPrompt.userChoice.then(function(a){"accepted"===a.outcome?console.log("User accepted the A2HS prompt"):
console.log("User dismissed the A2HS prompt");deferredPrompt=null})});window.addEventListener("appinstalled",function(a){console.log("myMPD installed as app")})}
function dragAndDropTable(a){var b=document.getElementById(a).getElementsByTagName("tbody")[0];b.addEventListener("dragstart",function(a){"TR"==a.target.nodeName&&(a.target.classList.add("opacity05"),a.dataTransfer.setDragImage(a.target,0,0),a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("Text",a.target.getAttribute("id")),dragEl=a.target.cloneNode(!0))},!1);b.addEventListener("dragleave",function(a){a.preventDefault();if("TR"==dragEl.nodeName){var b=a.target;"TD"==a.target.nodeName&&
(b=a.target.parentNode);"TR"==b.nodeName&&b.classList.remove("dragover")}},!1);b.addEventListener("dragover",function(a){a.preventDefault();if("TR"==dragEl.nodeName){for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover");c=a.target;"TD"==a.target.nodeName&&(c=a.target.parentNode);"TR"==c.nodeName&&c.classList.add("dragover");a.dataTransfer.dropEffect="move"}},!1);b.addEventListener("dragend",function(a){a.preventDefault();if("TR"==dragEl.nodeName){for(var c=
b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover");document.getElementById(a.dataTransfer.getData("Text"))&&document.getElementById(a.dataTransfer.getData("Text")).classList.remove("opacity05")}},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();if("TR"==dragEl.nodeName){var d=c.target;"TD"==c.target.nodeName&&(d=c.target.parentNode);var e=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),
f=d.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05");b.insertBefore(dragEl,d);c=b.getElementsByClassName("dragover");d=c.length;for(var g=0;g<d;g++)c[g].classList.remove("dragover");document.getElementById(a).classList.add("opacity05");"Queue"==app.current.app?sendAPI({cmd:"MPD_API_QUEUE_MOVE_TRACK",data:{from:e,to:f}}):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view&&playlistMoveTrack(e,
f)}},!1)}
function dragAndDropTableHeader(a){if(document.getElementById(a+"List"))var b=document.getElementById(a+"List").getElementsByTagName("tr")[0];else b=a.getElementsByTagName("tr")[0],a="BrowseDatabase";b.addEventListener("dragstart",function(a){"TH"==a.target.nodeName&&(a.target.classList.add("opacity05"),a.dataTransfer.setDragImage(a.target,0,0),a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("Text",a.target.getAttribute("data-col")),dragEl=a.target.cloneNode(!0))},!1);b.addEventListener("dragleave",function(a){a.preventDefault();
"TH"==dragEl.nodeName&&"TH"==a.target.nodeName&&a.target.classList.remove("dragover-th")},!1);b.addEventListener("dragover",function(a){a.preventDefault();if("TH"==dragEl.nodeName){for(var c=b.getElementsByClassName("dragover-th"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover-th");"TH"==a.target.nodeName&&a.target.classList.add("dragover-th");a.dataTransfer.dropEffect="move"}},!1);b.addEventListener("dragend",function(a){a.preventDefault();if("TH"==dragEl.nodeName){for(var c=b.getElementsByClassName("dragover-th"),
e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover-th");this.querySelector("[data-col="+a.dataTransfer.getData("Text")+"]")&&this.querySelector("[data-col="+a.dataTransfer.getData("Text")+"]").classList.remove("opacity05")}},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();if("TH"==dragEl.nodeName){this.querySelector("[data-col="+c.dataTransfer.getData("Text")+"]").remove();dragEl.classList.remove("opacity05");b.insertBefore(dragEl,c.target);c=b.getElementsByClassName("dragover-th");
for(var d=c.length,e=0;e<d;e++)c[e].classList.remove("dragover-th");document.getElementById(a+"List")?(document.getElementById(a+"List").classList.add("opacity05"),saveCols(a)):saveCols(a,this.parentNode.parentNode)}},!1)}function playlistMoveTrack(a,b){sendAPI({cmd:"MPD_API_PLAYLIST_MOVE_TRACK",data:{plist:app.current.search,from:a,to:b}})}
function webSocketConnect(){var a=getWsUrl();socket=new WebSocket(a);try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD: "+a,"","","success");modalConnectionError.hide();appRoute();sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState)},socket.onmessage=function(b){if(b.data!==lastState&&0!=b.data.length){try{var c=JSON.parse(b.data)}catch(d){console.log("Invalid JSON data received: "+b.data)}switch(c.type){case "update_state":parseState(c);break;case "disconnected":showNotification("Lost connection to myMPD: "+
a,"","","danger");break;case "update_queue":"Queue"===app.current.app&&getQueue();sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState);break;case "update_options":getSettings();break;case "update_outputs":sendAPI({cmd:"MPD_API_PLAYER_OUTPUT_LIST"},parseOutputs);break;case "update_started":updateDBstarted(!1);break;case "update_database":case "update_finished":updateDBfinished(c.type);break;case "update_volume":parseVolume(c);break;case "update_stored_playlist":"Browse"==app.current.app&&"Playlists"==
app.current.tab&&"All"==app.current.view?sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view&&sendAPI({cmd:"MPD_API_PLAYLIST_CONTENT_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists);break;case "error":showNotification(c.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");modalConnectionError.show();
setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(b){alert("Error: "+b)}}function getWsUrl(){var a=window.location.hostname,b=window.location.protocol,c=window.location.port;a=("https:"==b?"wss://":"ws://")+a+(""!=c?":"+c:"")+"/ws";return document.getElementById("wsUrl").innerText=a}
function parseStats(a){document.getElementById("mpdstats_artists").innerText=a.data.artists;document.getElementById("mpdstats_albums").innerText=a.data.albums;document.getElementById("mpdstats_songs").innerText=a.data.songs;document.getElementById("mpdstats_dbPlaytime").innerText=beautifyDuration(a.data.dbPlaytime);document.getElementById("mpdstats_playtime").innerText=beautifyDuration(a.data.playtime);document.getElementById("mpdstats_uptime").innerText=beautifyDuration(a.data.uptime);var b=new Date(1E3*
a.data.dbUpdated);document.getElementById("mpdstats_dbUpdated").innerText=b.toUTCString();document.getElementById("mympdVersion").innerText=a.data.mympdVersion;document.getElementById("mpdVersion").innerText=a.data.mpdVersion}function toggleBtn(a,b){if(a=document.getElementById(a))void 0==b&&(b=a.classList.contains("active")?0:1),1==b||1==b?a.classList.add("active"):a.classList.remove("active")}
function parseSettings(a){toggleBtn("btnRandom",a.data.random);toggleBtn("btnConsume",a.data.consume);toggleBtn("btnSingle",a.data.single);toggleBtn("btnRepeat",a.data.repeat);void 0!=a.data.crossfade?(document.getElementById("inputCrossfade").removeAttribute("disabled"),document.getElementById("inputCrossfade").value=a.data.crossfade):document.getElementById("inputCrossfade").setAttribute("disabled","disabled");void 0!=a.data.mixrampdb?(document.getElementById("inputMixrampdb").removeAttribute("disabled"),
document.getElementById("inputMixrampdb").value=a.data.mixrampdb):document.getElementById("inputMixrampdb").setAttribute("disabled","disabled");void 0!=a.data.mixrampdelay?(document.getElementById("inputMixrampdelay").removeAttribute("disabled"),document.getElementById("inputMixrampdelay").value=a.data.mixrampdelay):document.getElementById("inputMixrampdelay").setAttribute("disabled","disabled");document.getElementById("selectReplaygain").value=a.data.replaygain;var b=document.getElementById("btnnotifyWeb");
notificationsSupported()?a.data.notificationWeb?(toggleBtn("btnnotifyWeb",a.data.notificationWeb),Notification.requestPermission(function(b){"permission"in Notification||(Notification.permission=b);"granted"===b?toggleBtn("btnnotifyWeb",1):(toggleBtn("btnnotifyWeb",0),a.data.notificationWeb=!0)})):toggleBtn("btnnotifyWeb",0):(b.setAttribute("disabled","disabled"),toggleBtn("btnnotifyWeb",0));toggleBtn("btnnotifyPage",a.data.notificationPage);var c=document.getElementsByClassName("stickers"),d=c.length,
e=1==a.data.stickers?"":"none";for(b=0;b<d;b++)c[b].style.display=e;c=document.getElementsByClassName("smartpls");d=c.length;e=1==a.data.smartpls?"":"none";for(b=0;b<d;b++)c[b].style.display=e;1==a.data.mixramp?document.getElementsByClassName("mixramp")[0].style.display="":document.getElementsByClassName("mixramp")[0].style.display="none";document.getElementById("selectJukeboxMode").value=a.data.jukeboxMode;document.getElementById("inputJukeboxQueueLength").value=a.data.jukeboxQueueLength;0==a.data.jukeboxMode||
2==a.data.jukeboxMode?(document.getElementById("inputJukeboxQueueLength").setAttribute("disabled","disabled"),document.getElementById("selectJukeboxPlaylist").setAttribute("disabled","disabled")):1==a.data.jukeboxMode&&(document.getElementById("inputJukeboxQueueLength").removeAttribute("disabled"),document.getElementById("selectJukeboxPlaylist").removeAttribute("disabled"));settings=a.data;playlistEl="selectJukeboxPlaylist";sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:0,filter:"-"}},getAllPlaylists);
settings.mpdstream="http://";settings.mpdstream="127.0.0.1"==settings.mpdhost||"localhost"==settings.mpdhost?settings.mpdstream+window.location.hostname:settings.mpdstream+settings.mpdhost;settings.mpdstream+=":"+settings.streamport+"/";addTagList("BrowseDatabaseByTagDropdown",!1);addTagList("searchqueuetag",!0);addTagList("searchtags",!0);for(b=0;b<a.data.tags.length;b++)app.apps.Browse.tabs.Database.views[a.data.tags[b]]={state:"0/-/",scrollPos:0}}
function getSettings(){sendAPI({cmd:"MPD_API_SETTINGS_GET"},parseSettings)}function parseOutputs(a){for(var b="",c=a.data.outputs.length,d=0;d<c;d++)b+='<button id="btnOutput'+a.data.outputs[d].id+'" data-output-id="'+a.data.outputs[d].id+'" class="btn btn-secondary btn-block',1==a.data.outputs[d].state&&(b+=" active"),b+='"><span class="material-icons float-left">volume_up</span> '+a.data.outputs[d].name+"</button>";domCache.outputs.innerHTML=b}
function setCounter(a,b,c){currentSong.totalTime=b;currentSong.elapsedTime=c;currentSong.currentSongId=a;var d=Math.floor(b/60),e=b-60*d,f=Math.floor(c/60),g=c-60*f;domCache.progressBar.value=Math.floor(100*c/b);b=f+":"+(10>g?"0":"")+g+" / "+d+":"+(10>e?"0":"")+e;domCache.counter.innerText=b;lastState&&(c=document.getElementById("queueTrackId"+lastState.data.currentSongId))&&(d=c.getElementsByTagName("td"),d[4].innerText=c.getAttribute("data-duration"),d[0].classList.remove("material-icons"),d[0].innerText=
c.getAttribute("data-songpos"),c.classList.remove("font-weight-bold"));if(c=document.getElementById("queueTrackId"+a))d=c.getElementsByTagName("td"),d[4].innerText=b,d[0].classList.add("material-icons"),d[0].innerText="play_arrow",c.classList.add("font-weight-bold");progressTimer&&clearTimeout(progressTimer);"play"==playstate&&(progressTimer=setTimeout(function(){currentSong.elapsedTime++;setCounter(currentSong.currentSongId,currentSong.totalTime,currentSong.elapsedTime)},1E3))}
function filterCols(a){var b=settings.tags.slice();0==settings.featTags&&b.push("Title");b.push("Duration");"colsQueue"!=a&&"colsBrowsePlaylistsDetail"!=a||b.push("Pos");"colsBrowseFilesystem"==a&&b.push("Type");for(var c=[],d=0;d<settings[a].length;d++)b.includes(settings[a][d])&&c.push(settings[a][d]);settings[a]=c}
function parseSettings(a){settings=a.data;toggleBtn("btnRandom",settings.random);toggleBtn("btnConsume",settings.consume);toggleBtn("btnSingle",settings.single);toggleBtn("btnRepeat",settings.repeat);void 0!=settings.crossfade?(document.getElementById("inputCrossfade").removeAttribute("disabled"),document.getElementById("inputCrossfade").value=settings.crossfade):document.getElementById("inputCrossfade").setAttribute("disabled","disabled");void 0!=settings.mixrampdb?(document.getElementById("inputMixrampdb").removeAttribute("disabled"),
document.getElementById("inputMixrampdb").value=settings.mixrampdb):document.getElementById("inputMixrampdb").setAttribute("disabled","disabled");void 0!=settings.mixrampdelay?(document.getElementById("inputMixrampdelay").removeAttribute("disabled"),document.getElementById("inputMixrampdelay").value=settings.mixrampdelay):document.getElementById("inputMixrampdelay").setAttribute("disabled","disabled");document.getElementById("selectReplaygain").value=settings.replaygain;a=document.getElementById("btnnotifyWeb");
notificationsSupported()?settings.notificationWeb?(toggleBtn("btnnotifyWeb",settings.notificationWeb),Notification.requestPermission(function(a){"permission"in Notification||(Notification.permission=a);"granted"===a?toggleBtn("btnnotifyWeb",1):(toggleBtn("btnnotifyWeb",0),settings.notificationWeb=!0)})):toggleBtn("btnnotifyWeb",0):(a.setAttribute("disabled","disabled"),toggleBtn("btnnotifyWeb",0));toggleBtn("btnnotifyPage",settings.notificationPage);for(var b="featStickers featSmartpls featPlaylists featTags featLocalplayer featSyscmds featCoverimage".split(" "),
c=0;c<b.length;c++){var d=document.getElementsByClassName(b[c]),e=d.length,f=1==settings[b[c]]?"":"none";for(a=0;a<e;a++)d[a].style.display=f}0==settings.featTags&&(app.apps.Browse.active="Filesystem",app.apps.Search.state="0/filename/",app.apps.Queue.state="0/filename/",settings.colsQueue=["Pos","Title","Duration"],settings.colsSearch=["Title","Duration"],settings.colsBrowseFilesystem=["Type","Title","Duration"],settings.colsBrowseDatabase=["Track","Title","Duration"]);1==settings.mixramp?document.getElementsByClassName("mixramp")[0].style.display=
"":document.getElementsByClassName("mixramp")[0].style.display="none";!settings.tags.includes("AlbumArtist")&&settings.featTags&&(settings.tags.includes("Artist")?app.apps.Browse.tabs.Database.active="Artist":app.apps.Browse.tabs.Database.active=settings.tags[0]);document.getElementById("selectJukeboxMode").value=settings.jukeboxMode;document.getElementById("inputJukeboxQueueLength").value=settings.jukeboxQueueLength;0==settings.jukeboxMode||2==settings.jukeboxMode?(document.getElementById("inputJukeboxQueueLength").setAttribute("disabled",
"disabled"),document.getElementById("selectJukeboxPlaylist").setAttribute("disabled","disabled")):1==settings.jukeboxMode&&(document.getElementById("inputJukeboxQueueLength").removeAttribute("disabled"),document.getElementById("selectJukeboxPlaylist").removeAttribute("disabled"));settings.featPlaylists?(playlistEl="selectJukeboxPlaylist",sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:0,filter:"-"}},getAllPlaylists)):document.getElementById("selectJukeboxPlaylist").innerHTML="<option>Database</option>";
settings.tags.sort();settings.searchtags.sort();settings.browsetags.sort();filterCols("colsSearch");filterCols("colsQueue");filterCols("colsBrowsePlaylistsDetail");filterCols("colsBrowseFilesystem");filterCols("colsBrowseDatabase");settings.featLocalplayer&&(""==settings.streamurl?(settings.mpdstream="http://",settings.mpdstream="127.0.0.1"==settings.mpdhost||"localhost"==settings.mpdhost?settings.mpdstream+window.location.hostname:settings.mpdstream+settings.mpdhost,settings.mpdstream+=":"+settings.streamport+
"/"):settings.mpdstream=settings.streamurl);addTagList("BrowseDatabaseByTagDropdown","browsetags");addTagList("searchqueuetags","searchtags");addTagList("searchtags","searchtags");for(a=0;a<settings.tags.length;a++)app.apps.Browse.tabs.Database.views[settings.tags[a]]={state:"0/-/",scrollPos:0};if(settings.featSyscmds){b="";c=settings.syscmds.length;if(0<c)for(b='<div class="dropdown-divider"></div>',a=0;a<c;a++)b+='<a class="dropdown-item text-light bg-dark" href="#" data-href=\'{"cmd": "execSyscmd", "options": ["'+
settings.syscmds[a]+"\"]}'>"+settings.syscmds[a]+"</a>";document.getElementById("syscmds").innerHTML=b}else document.getElementById("syscmds").innerHTML="";setCols("Queue");setCols("Search");setCols("BrowseFilesystem");setCols("BrowsePlaylistsDetail");setCols("BrowseDatabase",".tblAlbumTitles");"Queue"==app.current.app?getQueue():"Search"==app.current.app?appRoute():"Browse"==app.current.app&&"Filesystem"==app.current.tab?appRoute():"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==
app.current.view?appRoute():"Browse"==app.current.app&&"Database"==app.current.tab&&""!=app.current.search&&appRoute()}
function setCols(a,b){var c="",d=settings.tags.slice();0==settings.featTags&&d.push("Title");d.push("Duration");"Queue"!=a&&"BrowsePlaylistsDetail"!=a||d.push("Pos");"BrowseFilesystem"==a&&d.push("Type");d.sort();for(var e=0;e<d.length;e++)c+='<div class="form-check"><input class="form-check-input" type="checkbox" value="1" name="'+d[e]+'"',settings["cols"+a].includes(d[e])&&(c+="checked"),c+='><label class="form-check-label text-light" for="'+d[e]+'">&nbsp;&nbsp;'+d[e]+"</label></div>";document.getElementById(a+
"ColsDropdown").firstChild.innerHTML=c;c="";for(e=0;e<settings["cols"+a].length;e++){d=settings["cols"+a][e];c+='<th draggable="true" data-col="'+d+'">';if("Track"==d||"Pos"==d)d="#";c+=d+"</th>"}c+="<th></th>";if(void 0==b)document.getElementById(a+"List").getElementsByTagName("tr")[0].innerHTML=c;else for(a=document.querySelectorAll(b),e=0;e<a.length;e++)a[e].getElementsByTagName("tr")[0].innerHTML=c}function getSettings(){sendAPI({cmd:"MPD_API_SETTINGS_GET"},parseSettings)}
function saveCols(a,b){var c=document.getElementById(a+"ColsDropdown").firstChild.getElementsByTagName("input");var d=void 0==b?document.getElementById(a+"List").getElementsByTagName("tr")[0]:"string"==typeof b?document.querySelector(b).getElementsByTagName("tr")[0]:b.getElementsByTagName("tr")[0];for(b=0;b<c.length;b++){var e=d.querySelector("[data-col="+c[b].name+"]");0==c[b].checked?e&&e.remove():e||(e=document.createElement("th"),e.innerText=c[b].name,e.setAttribute("data-col",c[b].name),d.appendChild(e))}a=
{cmd:"MPD_API_COLS_SAVE",data:{table:"cols"+a,cols:[]}};c=d.getElementsByTagName("th");for(b=0;b<c.length;b++)(d=c[b].getAttribute("data-col"))&&a.data.cols.push(d);sendAPI(a,getSettings)}
function parseOutputs(a){for(var b="",c=a.data.outputs.length,d=0;d<c;d++)b+='<button id="btnOutput'+a.data.outputs[d].id+'" data-output-id="'+a.data.outputs[d].id+'" class="btn btn-secondary btn-block',1==a.data.outputs[d].state&&(b+=" active"),b+='"><span class="material-icons float-left">volume_up</span> '+a.data.outputs[d].name+"</button>";domCache.outputs.innerHTML=b}
function setCounter(a,b,c){currentSong.totalTime=b;currentSong.elapsedTime=c;currentSong.currentSongId=a;var d=Math.floor(b/60),e=b-60*d,f=Math.floor(c/60),g=c-60*f;domCache.progressBar.value=Math.floor(100*c/b);b=f+":"+(10>g?"0":"")+g+" / "+d+":"+(10>e?"0":"")+e;domCache.counter.innerText=b;if(lastState&&lastState.data.currentSongId!=a&&(c=document.getElementById("queueTrackId"+lastState.data.currentSongId))){if(d=c.querySelector("[data-col=Duration]"))d.innerText=c.getAttribute("data-duration");
if(d=c.querySelector("[data-col=Pos]"))d.classList.remove("material-icons"),d.innerText=c.getAttribute("data-songpos");c.classList.remove("font-weight-bold")}if(c=document.getElementById("queueTrackId"+a)){if(d=c.querySelector("[data-col=Duration]"))d.innerText=b;(d=c.querySelector("[data-col=Pos]"))&&!d.classList.contains("material-icons")&&(d.classList.add("material-icons"),d.innerText="play_arrow");c.classList.add("font-weight-bold")}progressTimer&&clearTimeout(progressTimer);"play"==playstate&&
(progressTimer=setTimeout(function(){currentSong.elapsedTime++;setCounter(currentSong.currentSongId,currentSong.totalTime,currentSong.elapsedTime)},1E3))}
function parseState(a){if(JSON.stringify(a)!==JSON.stringify(lastState)){if(1==a.data.state){for(var b=0;b<domCache.btnsPlayLen;b++)domCache.btnsPlay[b].innerText="play_arrow";playstate="stop"}else if(2==a.data.state){for(b=0;b<domCache.btnsPlayLen;b++)domCache.btnsPlay[b].innerText="pause";playstate="play"}else{for(b=0;b<domCache.btnsPlayLen;b++)domCache.btnsPlay[b].innerText="play_arrow";playstate="pause"}-1==a.data.nextSongPos&&0==settings.jukeboxMode?domCache.btnNext.setAttribute("disabled","disabled"):
domCache.btnNext.removeAttribute("disabled");0>=a.data.songPos?domCache.btnPrev.setAttribute("disabled","disabled"):domCache.btnPrev.removeAttribute("disabled");if(0==a.data.queueLength)for(b=0;b<domCache.btnsPlayLen;b++)domCache.btnsPlay[b].setAttribute("disabled","disabled");else for(b=0;b<domCache.btnsPlayLen;b++)domCache.btnsPlay[b].removeAttribute("disabled");parseVolume(a);setCounter(a.data.currentSongId,a.data.totalTime,a.data.elapsedTime);lastState&&lastState.data.currentSongId!=a.data.currentSongId&&
sendAPI({cmd:"MPD_API_PLAYER_CURRENT_SONG"},songChange);"-1"==a.data.songPos&&(domCache.currentTrack.innerText="Not playing",domCache.currentAlbum.innerText="",domCache.currentArtist.innerText="",domCache.currentCover.style.backgroundImage="");lastState=a}}
function parseVolume(a){-1==a.data.volume?(domCache.volumePrct.innerText="Volumecontrol disabled",domCache.volumeControl.classList.add("hide")):(domCache.volumeControl.classList.remove("hide"),domCache.volumePrct.innerText=a.data.volume+" %",domCache.volumeIcon.innerText=0==a.data.volume?"volume_off":50>a.data.volume?"volume_down":"volume_up");domCache.volumeBar.value=a.data.volume}
function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_QUEUE_SEARCH",data:{filter:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_QUEUE_LIST",data:{offset:app.current.page}},parseQueue)}
function parseQueue(a){if("Queue"===app.current.app){0<a.totalTime&&a.totalEntities<=settings.maxElementsPerPage?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" "+(1<a.totalEntities?"Songs":"Song")+" \u2013 "+beautifyDuration(a.totalTime):0<a.totalEntities?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" "+(1<a.totalEntities?"Songs":"Song"):document.getElementById("panel-heading-queue").innerText="";var b=a.data.length,c=document.getElementById(app.current.app+
"List");c.setAttribute("data-version",a.queueVersion);c=c.getElementsByTagName("tbody")[0];for(var d=c.getElementsByTagName("tr"),e=0;e<b;e++)if(!d[e]||d[e].getAttribute("data-trackid")!=a.data[e].id||d[e].getAttribute("data-songpos")!=a.data[e].pos+1){var f=Math.floor(a.data[e].duration/60),g=a.data[e].duration-60*f;f=f+":"+(10>g?"0":"")+g;g=document.createElement("tr");g.setAttribute("draggable","true");g.setAttribute("data-trackid",a.data[e].id);g.setAttribute("id","queueTrackId"+a.data[e].id);
g.setAttribute("data-songpos",a.data[e].pos+1);g.setAttribute("data-duration",f);g.setAttribute("data-uri",a.data[e].uri);g.innerHTML="<td>"+(a.data[e].pos+1)+"</td><td>"+a.data[e].title+"</td><td>"+a.data[e].artist+"</td><td>"+a.data[e].album+"</td><td>"+f+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';e<d.length?d[e].replaceWith(g):c.append(g)}for(e=d.length-1;e>=b;e--)d[e].remove();"queuesearch"==a.type&&0==b?c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results, please refine your search!</td></tr>':
"List");c.setAttribute("data-version",a.queueVersion);c=c.getElementsByTagName("tbody")[0];for(var d=c.getElementsByTagName("tr"),e=0;e<b;e++){var f=Math.floor(a.data[e].Duration/60),g=a.data[e].Duration-60*f;a.data[e].Duration=f+":"+(10>g?"0":"")+g;f=document.createElement("tr");f.setAttribute("draggable","true");f.setAttribute("data-trackid",a.data[e].id);f.setAttribute("id","queueTrackId"+a.data[e].id);f.setAttribute("data-songpos",a.data[e].Pos+1);f.setAttribute("data-duration",a.data[e].Duration);
f.setAttribute("data-uri",a.data[e].uri);g="";for(var h=0;h<settings.colsQueue.length;h++)g+='<td data-col="'+settings.colsQueue[h]+'">'+a.data[e][settings.colsQueue[h]]+"</td>";g+='<td data-col="Action"><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';f.innerHTML=g;e<d.length?d[e].replaceWith(f):c.append(f)}for(e=d.length-1;e>=b;e--)d[e].remove();"queuesearch"==a.type&&0==b?c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results, please refine your search!</td></tr>':
"queue"==a.type&&0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty queue</td></tr>');setPagination(a.totalEntities);document.getElementById("QueueList").classList.remove("opacity05")}}
function parseSearch(a){"Search"===app.current.app&&(document.getElementById("panel-heading-search").innerHTML=a.totalEntities+" Songs found",0<a.totalEntities?(document.getElementById("searchAddAllSongs").removeAttribute("disabled"),document.getElementById("searchAddAllSongsBtn").removeAttribute("disabled")):(document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),document.getElementById("searchAddAllSongsBtn").setAttribute("disabled","disabled")),parseFilesystem(a))}
function parseFilesystem(a){if("Browse"===app.current.app||"Filesystem"===app.current.tab||"Search"===app.current.app){for(var b=a.data.length,c=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List").getElementsByTagName("tbody")[0],d=c.getElementsByTagName("tr"),e=0;e<b;e++){var f=encodeURI(a.data[e].uri);if(!d[e]||d[e].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-type",a.data[e].type);g.setAttribute("data-uri",f);g.setAttribute("data-name",
a.data[e].name);switch(a.data[e].type){case "dir":g.innerHTML='<td><span class="material-icons">folder_open</span></td><td colspan="4">'+a.data[e].name+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';break;case "song":f=Math.floor(a.data[e].duration/60);var h=a.data[e].duration-60*f;g.innerHTML='<td><span class="material-icons">music_note</span></td><td>'+a.data[e].title+"</td><td>"+a.data[e].artist+"</td><td>"+a.data[e].album+"</td><td>"+f+":"+(10>h?"0":"")+h+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
break;case "smartpls":case "plist":g.innerHTML='<td><span class="material-icons">list</span></td><td colspan="4">'+a.data[e].name+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>'}e<d.length?d[e].replaceWith(g):c.append(g)}}for(e=d.length-1;e>=b;e--)d[e].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results</td></tr>');document.getElementById(app.current.app+(void 0==app.current.tab?
"":app.current.tab)+"List").classList.remove("opacity05")}}
function parsePlaylists(a){if("Browse"===app.current.app||"Playlists"===app.current.tab){"All"==app.current.view?(document.getElementById("BrowsePlaylistsAllList").classList.remove("hide"),document.getElementById("BrowsePlaylistsDetailList").classList.add("hide"),document.getElementById("btnBrowsePlaylistsAll").parentNode.classList.add("hide"),document.getElementById("btnPlaylistClear").parentNode.classList.add("hide")):(-1<a.uri.indexOf(".")||1==a.smartpls?(document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-ro",
"true"),document.getElementById("btnPlaylistClear").parentNode.classList.add("hide")):(document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-ro","false"),document.getElementById("btnPlaylistClear").parentNode.classList.remove("hide")),document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-uri",a.uri),1==a.smartpls?document.getElementById("BrowsePlaylistsDetailList").getElementsByTagName("caption")[0].innerHTML="Smart playlist: "+a.uri+'<small class="pull-right">'+
a.totalEntities+" Songs </small>":document.getElementById("BrowsePlaylistsDetailList").getElementsByTagName("caption")[0].innerHTML="Playlist: "+a.uri+'<small class="pull-right">'+a.totalEntities+" Songs </small>",document.getElementById("BrowsePlaylistsDetailList").classList.remove("hide"),document.getElementById("BrowsePlaylistsAllList").classList.add("hide"),document.getElementById("btnBrowsePlaylistsAll").parentNode.classList.remove("hide"));var b=a.data.length,c=document.getElementById(app.current.app+
app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0],d=c.getElementsByTagName("tr");if("All"==app.current.view)for(var e=0;e<b;e++){var f=encodeURI(a.data[e].uri);if(!d[e]||d[e].getAttribute("data-uri")!=f){var g=new Date(1E3*a.data[e].last_modified),h=document.createElement("tr");h.setAttribute("data-uri",f);h.setAttribute("data-type",a.data[e].type);h.setAttribute("data-name",a.data[e].name);h.innerHTML='<td><span class="material-icons">list</span></td><td>'+a.data[e].name+
"</td><td>"+g.toUTCString()+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';e<d.length?d[e].replaceWith(h):c.append(h)}}else if("Detail"==app.current.view)for(e=0;e<b;e++)if(f=encodeURI(a.data[e].uri),g=a.offset+e+1,!d[e]||d[e].getAttribute("data-uri")!=f||d[e].getAttribute("id")!="playlistTrackId"+g){h=document.createElement("tr");0==a.smartpls&&h.setAttribute("draggable","true");h.setAttribute("id","playlistTrackId"+g);h.setAttribute("data-type",a.data[e].type);
h.setAttribute("data-uri",f);h.setAttribute("data-name",a.data[e].name);h.setAttribute("data-songpos",g);f=Math.floor(a.data[e].duration/60);var k=a.data[e].duration-60*f;h.innerHTML="<td>"+g+"</td><td>"+a.data[e].title+"</td><td>"+a.data[e].artist+"</td><td>"+a.data[e].album+"</td><td>"+f+":"+(10>k?"0":"")+k+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';e<d.length?d[e].replaceWith(h):c.append(h)}for(e=d.length-1;e>=b;e--)d[e].remove();setPagination(a.totalEntities);
0==b&&(c.innerHTML="All"==app.current.view?'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No playlists found.</td></tr>':'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty playlist.</td></tr>');document.getElementById(app.current.app+app.current.tab+app.current.view+"List").classList.remove("opacity05")}}
function parseListDBtags(a){scrollTo(0);if(""!=app.current.search){document.getElementById("BrowseDatabaseAlbumList").classList.remove("hide");document.getElementById("BrowseDatabaseTagList").classList.add("hide");document.getElementById("btnBrowseDatabaseByTag").parentNode.classList.add("hide");document.getElementById("btnBrowseDatabaseTag").parentNode.classList.remove("hide");document.getElementById("BrowseDatabaseAddAllSongs").parentNode.parentNode.classList.remove("hide");document.getElementById("btnBrowseDatabaseTag").innerHTML=
"&laquo; "+app.current.view;document.getElementById("BrowseDatabaseAlbumListCaption").innerHTML="<h2>"+a.searchtagtype+": "+a.searchstr+'</h2><small class="pull-right">'+a.totalEntities+" Entries</small><hr/>";for(var b=a.data.length,c=document.getElementById("BrowseDatabaseAlbumList"),d=c.getElementsByClassName("col-md"),e=0;e<b;e++){var f=genId(a.data[e].value);if(!d[e]||d[e].getAttribute("id")!=f){var g=document.createElement("div");g.classList.add("col-md");g.classList.add("mr-0");g.setAttribute("id",
f);g.setAttribute("data-album",encodeURI(a.data[e].value));g.innerHTML='<div class="card mb-4" id="card'+f+'"> <a href="#" class="card-img-top"></a> <div class="card-body"> <h5 class="card-title" id="albumartist'+f+'"></h5> <h4 class="card-title">'+a.data[e].value+'</h4> <a class="color-darkgrey" data-toggle="collapse" href="#collapse'+f+'" id="collapseLink'+f+'"> <span class="material-icons">keyboard_arrow_right</span> Show Titles</a> <div class="collapse" id="collapse'+f+'"> <table class="table table-sm table-hover" id="tbl'+
f+'"><tbody></tbody></table> </div> </div></div>';e<d.length?d[e].replaceWith(g):c.append(g);"IntersectionObserver"in window?createListTitleObserver(document.getElementById(f)):sendAPI({cmd:"MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST",data:{album:a.data[e].value,search:app.current.search,tag:app.current.view}},parseListTitles)}}for(e=d.length-1;e>=b;e--)d[e].remove();setPagination(a.totalEntities);document.getElementById("BrowseDatabaseAlbumList").classList.remove("opacity05")}else{document.getElementById("BrowseDatabaseAlbumList").classList.add("hide");
document.getElementById("BrowseDatabaseTagList").classList.remove("hide");document.getElementById("btnBrowseDatabaseByTag").parentNode.classList.remove("hide");document.getElementById("BrowseDatabaseAddAllSongs").parentNode.parentNode.classList.add("hide");document.getElementById("btnBrowseDatabaseTag").parentNode.classList.add("hide");document.getElementById("BrowseDatabaseTagListCaption").innerHTML=app.current.view+'<small class="pull-right">'+a.totalEntities+" Tags</small>";b=a.data.length;c=document.getElementById(app.current.app+
app.current.tab+"TagList").getElementsByTagName("tbody")[0];d=c.getElementsByTagName("tr");for(e=0;e<b;e++)f=encodeURI(a.data[e].value),d[e]&&d[e].getAttribute("data-uri")==f||(g=document.createElement("tr"),g.setAttribute("data-uri",f),g.innerHTML='<td><span class="material-icons">album</span></td><td>'+a.data[e].value+"</td>",e<d.length?d[e].replaceWith(g):c.append(g));for(e=d.length-1;e>=b;e--)d[e].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td>No entries found.</td></tr>');
document.getElementById("BrowseDatabaseTagList").classList.remove("opacity05")}}function createListTitleObserver(a){(new IntersectionObserver(getListTitles,{root:null,rootMargin:"0px"})).observe(a)}function getListTitles(a,b){a.forEach(function(a){0<a.intersectionRatio&&(b.unobserve(a.target),a=decodeURI(a.target.getAttribute("data-album")),sendAPI({cmd:"MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST",data:{album:a,search:app.current.search,tag:app.current.view}},parseListTitles))})}
function parseListTitles(a){var b=genId(a.album),c=document.getElementById("card"+b),d=c.getElementsByTagName("tbody")[0];c=c.getElementsByTagName("a")[0];c.style.backgroundImage='url("'+a.cover+'")';c.setAttribute("data-uri",encodeURI(a.data[0].uri.replace(/\/[^\/]+$/,"")));c.setAttribute("data-name",a.album);c.setAttribute("data-type","dir");document.getElementById("albumartist"+b).innerText=a.albumartist;var e=document.getElementById("collapseLink"+b);new Collapse(e);document.getElementById("collapse"+
b).addEventListener("show.bs.collapse",function(){e.innerHTML='<span class="material-icons">keyboard_arrow_down</span> Hide Titles'},!1);document.getElementById("collapse"+b).addEventListener("hidden.bs.collapse",function(){e.innerHTML='<span class="material-icons">keyboard_arrow_right</span> Show Titles'},!1);b="";for(var f=a.data.length,g=0;g<f;g++)b+='<tr data-type="song" data-name="'+a.data[g].title+'" data-uri="'+encodeURI(a.data[g].uri)+'"><td>'+a.data[g].track+"</td><td>"+a.data[g].title+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td></tr>';
d.innerHTML=b;c.addEventListener("click",function(a){showMenu(this,a)},!1);d.parentNode.addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1)}
function parseFilesystem(a){if("Browse"===app.current.app||"Filesystem"===app.current.tab||"Search"===app.current.app){for(var b=app.current.app+("Filesystem"==app.current.tab?app.current.tab:""),c=a.data.length,d=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List").getElementsByTagName("tbody")[0],e=d.getElementsByTagName("tr"),f=0;f<c;f++){var g=encodeURI(a.data[f].uri),h=document.createElement("tr");h.setAttribute("data-type",a.data[f].Type);h.setAttribute("data-uri",
g);"song"==a.data[f].Type?h.setAttribute("data-name",a.data[f].Title):h.setAttribute("data-name",a.data[f].name);switch(a.data[f].Type){case "dir":case "smartpls":case "plist":g="";for(var k=0;k<settings["cols"+b].length;k++)g+='<td data-col="'+settings["cols"+b][k]+'">',"Type"==settings["cols"+b][k]?g="dir"==a.data[f].Type?g+'<span class="material-icons">folder_open</span>':g+'<span class="material-icons">list</span>':"Title"==settings["cols"+b][k]&&(g+=a.data[f].name),g+="</td>";g+='<td data-col="Action"><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
h.innerHTML=g;break;case "song":g=Math.floor(a.data[f].Duration/60);k=a.data[f].Duration-60*g;a.data[f].Duration=g+":"+(10>k?"0":"")+k;g="";for(k=0;k<settings["cols"+b].length;k++)g+='<td data-col="'+settings["cols"+b][k]+'">',g="Type"==settings["cols"+b][k]?g+'<span class="material-icons">music_note</span>':g+a.data[f][settings["cols"+b][k]],g+="</td>";g+='<td data-col="Action"><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';h.innerHTML=g}f<e.length?e[f].replaceWith(h):d.append(h)}for(f=
e.length-1;f>=c;f--)e[f].remove();setPagination(a.totalEntities);0==c&&(d.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results</td></tr>');document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List").classList.remove("opacity05")}}
function parsePlaylists(a){if("Browse"===app.current.app||"Playlists"===app.current.tab){"All"==app.current.view?(document.getElementById("BrowsePlaylistsAllList").classList.remove("hide"),document.getElementById("BrowsePlaylistsDetailList").classList.add("hide"),document.getElementById("btnBrowsePlaylistsAll").parentNode.classList.add("hide"),document.getElementById("btnPlaylistClear").parentNode.classList.add("hide"),document.getElementById("BrowsePlaylistsDetailColsBtn").parentNode.classList.add("hide")):
(-1<a.uri.indexOf(".")||1==a.smartpls?(document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-ro","true"),document.getElementById("btnPlaylistClear").parentNode.classList.add("hide")):(document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-ro","false"),document.getElementById("btnPlaylistClear").parentNode.classList.remove("hide")),document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-uri",a.uri),1==a.smartpls?document.getElementById("BrowsePlaylistsDetailList").getElementsByTagName("caption")[0].innerHTML=
"Smart playlist: "+a.uri+'<small class="pull-right">'+a.totalEntities+" Songs </small>":document.getElementById("BrowsePlaylistsDetailList").getElementsByTagName("caption")[0].innerHTML="Playlist: "+a.uri+'<small class="pull-right">'+a.totalEntities+" Songs </small>",document.getElementById("BrowsePlaylistsDetailList").classList.remove("hide"),document.getElementById("BrowsePlaylistsAllList").classList.add("hide"),document.getElementById("btnBrowsePlaylistsAll").parentNode.classList.remove("hide"),
settings.featTags&&document.getElementById("BrowsePlaylistsDetailColsBtn").parentNode.classList.remove("hide"));var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0],d=c.getElementsByTagName("tr");if("All"==app.current.view)for(var e=0;e<b;e++){var f=encodeURI(a.data[e].uri);if(!d[e]||d[e].getAttribute("data-uri")!=f){var g=new Date(1E3*a.data[e].last_modified),h=document.createElement("tr");h.setAttribute("data-uri",
f);h.setAttribute("data-type",a.data[e].Type);h.setAttribute("data-name",a.data[e].name);h.innerHTML='<td data-col="Type"><span class="material-icons">list</span></td><td>'+a.data[e].name+"</td><td>"+g.toUTCString()+'</td><td data-col="Action"><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';e<d.length?d[e].replaceWith(h):c.append(h)}}else if("Detail"==app.current.view)for(e=0;e<b;e++){f=encodeURI(a.data[e].uri);h=document.createElement("tr");0==a.smartpls&&h.setAttribute("draggable",
"true");h.setAttribute("id","playlistTrackId"+a.data[e].Pos);h.setAttribute("data-type",a.data[e].Type);h.setAttribute("data-uri",f);h.setAttribute("data-name",a.data[e].Title);h.setAttribute("data-songpos",a.data[e].Pos);f=Math.floor(a.data[e].Duration/60);g=a.data[e].Duration-60*f;a.data[e].Duration=f+":"+(10>g?"0":"")+g;f="";for(g=0;g<settings.colsBrowsePlaylistsDetail.length;g++)f+='<td data-col="'+settings.colsBrowsePlaylistsDetail[g]+'">'+a.data[e][settings.colsBrowsePlaylistsDetail[g]]+"</td>";
f+='<td data-col="Action"><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';h.innerHTML=f;e<d.length?d[e].replaceWith(h):c.append(h)}for(e=d.length-1;e>=b;e--)d[e].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML="All"==app.current.view?'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No playlists found.</td></tr>':'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty playlist.</td></tr>');document.getElementById(app.current.app+
app.current.tab+app.current.view+"List").classList.remove("opacity05")}}
function parseListDBtags(a){scrollTo(0);if(""!=app.current.search){document.getElementById("BrowseDatabaseAlbumList").classList.remove("hide");document.getElementById("BrowseDatabaseTagList").classList.add("hide");document.getElementById("btnBrowseDatabaseByTag").parentNode.classList.add("hide");document.getElementById("btnBrowseDatabaseTag").parentNode.classList.remove("hide");document.getElementById("BrowseDatabaseAddAllSongs").parentNode.parentNode.classList.remove("hide");document.getElementById("BrowseDatabaseColsBtn").parentNode.classList.remove("hide");
document.getElementById("btnBrowseDatabaseTag").innerHTML="&laquo; "+app.current.view;document.getElementById("BrowseDatabaseAlbumListCaption").innerHTML="<h2>"+a.searchtagtype+": "+a.searchstr+'</h2><small class="pull-right">'+a.totalEntities+" Entries</small><hr/>";for(var b=a.data.length,c=document.getElementById("BrowseDatabaseAlbumList"),d=c.getElementsByClassName("card"),e=0;e<b;e++){var f=genId(a.data[e].value),g=document.createElement("div");g.classList.add("card","ml-4","mr-4","mb-4","w-100");
g.setAttribute("id","card"+f);g.setAttribute("data-album",encodeURI(a.data[e].value));var h='<div class="card-header"><span id="albumartist'+f+'"></span> &ndash; '+a.data[e].value+'</div> <div class="card-body"><div class="row">';settings.featCoverimage&&(h+=' <div class="col-md-auto"><a class="card-img-left"></a></div>');h+=' <div class="col"><table class="tblAlbumTitles table table-sm table-hover" id="tbl'+f+'"><thead><tr></tr></thead><tbody></tbody></table></div> </div></div></div>';g.innerHTML=
h;e<d.length?d[e].replaceWith(g):c.append(g);"IntersectionObserver"in window?createListTitleObserver(document.getElementById("card"+f)):sendAPI({cmd:"MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST",data:{album:a.data[e].value,search:app.current.search,tag:app.current.view}},parseListTitles)}for(e=d.length-1;e>=b;e--)d[e].remove();setPagination(a.totalEntities);setCols("BrowseDatabase",".tblAlbumTitles");a=document.querySelectorAll(".tblAlbumTitles");for(e=0;e<a.length;e++)dragAndDropTableHeader(a[e]);document.getElementById("BrowseDatabaseAlbumList").classList.remove("opacity05")}else{document.getElementById("BrowseDatabaseAlbumList").classList.add("hide");
document.getElementById("BrowseDatabaseTagList").classList.remove("hide");document.getElementById("btnBrowseDatabaseByTag").parentNode.classList.remove("hide");document.getElementById("BrowseDatabaseAddAllSongs").parentNode.parentNode.classList.add("hide");document.getElementById("BrowseDatabaseColsBtn").parentNode.classList.add("hide");document.getElementById("btnBrowseDatabaseTag").parentNode.classList.add("hide");document.getElementById("BrowseDatabaseTagListCaption").innerHTML=app.current.view+
'<small class="pull-right">'+a.totalEntities+" Tags</small>";b=a.data.length;c=document.getElementById(app.current.app+app.current.tab+"TagList").getElementsByTagName("tbody")[0];d=c.getElementsByTagName("tr");for(e=0;e<b;e++)f=encodeURI(a.data[e].value),d[e]&&d[e].getAttribute("data-uri")==f||(g=document.createElement("tr"),g.setAttribute("data-uri",f),g.innerHTML='<td data-col="Type"><span class="material-icons">album</span></td><td>'+a.data[e].value+"</td>",e<d.length?d[e].replaceWith(g):c.append(g));
for(e=d.length-1;e>=b;e--)d[e].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td>No entries found.</td></tr>');document.getElementById("BrowseDatabaseTagList").classList.remove("opacity05")}}function createListTitleObserver(a){(new IntersectionObserver(getListTitles,{root:null,rootMargin:"0px"})).observe(a)}
function getListTitles(a,b){a.forEach(function(a){0<a.intersectionRatio&&(b.unobserve(a.target),a=decodeURI(a.target.getAttribute("data-album")),sendAPI({cmd:"MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST",data:{album:a,search:app.current.search,tag:app.current.view}},parseListTitles))})}
function parseListTitles(a){var b=genId(a.Album),c=document.getElementById("card"+b),d=c.getElementsByTagName("tbody")[0],e=c.querySelector(".card-header");e.setAttribute("data-uri",encodeURI(a.data[0].uri.replace(/\/[^\/]+$/,"")));e.setAttribute("data-name",a.Album);e.setAttribute("data-type","dir");e.addEventListener("click",function(a){showMenu(this,a)},!1);e.classList.add("clickable");if(c=c.getElementsByTagName("a")[0])c.style.backgroundImage='url("'+a.cover+'")',c.setAttribute("data-uri",encodeURI(a.data[0].uri.replace(/\/[^\/]+$/,
""))),c.setAttribute("data-name",a.Album),c.setAttribute("data-type","dir"),c.addEventListener("click",function(a){showMenu(this,a)},!1);document.getElementById("albumartist"+b).innerText=a.AlbumArtist;b="";c=a.data.length;for(e=0;e<c;e++){if(a.data[e].Duration){var f=Math.floor(a.data[e].Duration/60),g=a.data[e].Duration-60*f;a.data[e].Duration=f+":"+(10>g?"0":"")+g}b+='<tr data-type="song" data-name="'+a.data[e].Title+'" data-uri="'+encodeURI(a.data[e].uri)+'">';for(f=0;f<settings.colsBrowseDatabase.length;f++)b+=
'<td data-col="'+settings.colsBrowseDatabase[f]+'">'+a.data[e][settings.colsBrowseDatabase[f]]+"</td>";b+='<td data-col="Action"><a href="#" class="material-icons color-darkgrey">playlist_add</a></td></tr>'}d.innerHTML=b;d.parentNode.addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1)}
function setPagination(a){var b=Math.ceil(a/settings.maxElementsPerPage),c=app.current.app+(void 0==app.current.tab?"":app.current.tab);0==b&&(b=1);for(var d=["PaginationTop","PaginationBottom"],e=0;2>e;e++){document.getElementById(c+d[e]+"Page").innerText=app.current.page/settings.maxElementsPerPage+1+" / "+b;if(1<b){document.getElementById(c+d[e]+"Page").removeAttribute("disabled");for(var f="",g=0;g<b;g++)f+='<button data-page="'+g*settings.maxElementsPerPage+'" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+
(g+1)+"</button>";document.getElementById(c+d[e]+"Pages").innerHTML=f}else document.getElementById(c+d[e]+"Page").setAttribute("disabled","disabled");a>app.current.page+settings.maxElementsPerPage?(document.getElementById(c+d[e]+"Next").removeAttribute("disabled"),document.getElementById(c+d[e]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):(document.getElementById(c+d[e]+"Next").setAttribute("disabled","disabled"),document.getElementById(c+d[e]).classList.add("hide"),
document.getElementById(c+"ButtonsBottom").classList.add("hide"));0<app.current.page?(document.getElementById(c+d[e]+"Prev").removeAttribute("disabled"),document.getElementById(c+d[e]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):document.getElementById(c+d[e]+"Prev").setAttribute("disabled","disabled")}}
function appendQueue(a,b,c){switch(a){case "song":case "dir":sendAPI({cmd:"MPD_API_QUEUE_ADD_TRACK",data:{uri:b}});showNotification('"'+c+'" added',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_QUEUE_ADD_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" added',"","","success")}}function appendAfterQueue(a,b,c,d){switch(a){case "song":sendAPI({cmd:"MPD_API_QUEUE_ADD_TRACK_AFTER",data:{uri:b,to:c}}),showNotification('"'+d+'" added to pos '+c,"","","success")}}
function replaceQueue(a,b,c){switch(a){case "song":case "dir":sendAPI({cmd:"MPD_API_QUEUE_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_QUEUE_REPLACE_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" replaced',"","","success")}}function songClick(){var a=domCache.currentTrack.getAttribute("data-uri");""!=a&&songDetails(a)}
function artistClick(){var a=domCache.currentArtist.getAttribute("data-albumartist");""!=a&&appGoto("Browse","Database","AlbumArtist","0/-/"+a)}function albumClick(){var a=domCache.currentAlbum.getAttribute("data-album");""!=a&&appGoto("Browse","Database","Album","0/-/"+a)}function songDetails(a){sendAPI({cmd:"MPD_API_DATABASE_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()}
function parseSongDetails(a){var b=document.getElementById("modalSongDetails");b.getElementsByClassName("album-cover")[0].style.backgroundImage='url("'+a.data.cover+'")';b.getElementsByTagName("h1")[0].innerText=a.data.title;for(var c="",d=0;d<settings.tags.length;d++){var e=a.data[settings.tags[d].toLowerCase()];if("duration"==settings.tags[d]){var f=Math.floor(e/60);e-=60*f;e=f+":"+(10>e?"0":"")+e}c+="<tr><th>"+settings.tags[d]+"</th><td>"+e+"</td></tr>"}c+='<tr><th>Uri</th><td><a class="text-success" href="/library/'+
a.data.uri+'">'+a.data.uri+"</a></td></tr>";1==settings.stickers&&(d="not voted",0==a.data.like?d='<span class="material-icons">thumb_down_alt</span>':2==a.data.like&&(d='<span class="material-icons">thumb_up_alt</span>'),c+='<tr><th colspan="2">Statistics</th></tr><tr><th>Play count</th><td>'+a.data.playCount+"</td></tr><tr><th>Skip count</th><td>"+a.data.skipCount+"</td></tr><tr><th>Last played</th><td>"+(0==a.data.lastPlayed?"never":(new Date(1E3*a.data.lastPlayed)).toUTCString())+"</td></tr><tr><th>Like</th><td>"+
d+"</td></tr>");b.getElementsByTagName("tbody")[0].innerHTML=c}function playlistDetails(a){document.getElementById("BrowsePlaylistsAllList").classList.add("opacity05");appGoto("Browse","Playlists","Detail","0/-/"+a)}function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_PLAYLIST_RM_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05")}
function playlistClear(){var a=document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri");sendAPI({cmd:"MPD_API_PLAYLIST_CLEAR_AND_LIST",data:{uri:a}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05")}
function artistClick(){var a=domCache.currentArtist.getAttribute("data-albumartist");""!=a&&(settings.tags.includes("AlbumArtist")?appGoto("Browse","Database","AlbumArtist","0/-/"+a):settings.tags.includes("Artist")&&appGoto("Browse","Database","Artist","0/-/"+a))}function albumClick(){var a=domCache.currentAlbum.getAttribute("data-album");""!=a&&appGoto("Browse","Database","Album","0/-/"+a)}
function songDetails(a){sendAPI({cmd:"MPD_API_DATABASE_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()}
function parseSongDetails(a){var b=document.getElementById("modalSongDetails");b.getElementsByClassName("album-cover")[0].style.backgroundImage='url("'+a.data.cover+'")';b.getElementsByTagName("h1")[0].innerText=a.data.Title;for(var c="",d=0;d<settings.tags.length;d++)c+="<tr><th>"+settings.tags[d]+"</th><td>"+a.data[settings.tags[d]]+"</td></tr>";var e=a.data.Duration;d=Math.floor(e/60);e-=60*d;c+="<tr><th>Duration</th><td>"+(d+":"+(10>e?"0":"")+e)+"</td></tr>";c=settings.featLibrary?c+('<tr><th>Filename</th><td><a class="text-success" href="/library/'+
a.data.uri+'">'+a.data.uri+"</a></td></tr>"):c+("<tr><th>Filename</th><td>"+a.data.uri+"</td></tr>");1==settings.featStickers&&(d="not voted",0==a.data.like?d='<span class="material-icons">thumb_down_alt</span>':2==a.data.like&&(d='<span class="material-icons">thumb_up_alt</span>'),c+='<tr><th colspan="2">Statistics</th></tr><tr><th>Play count</th><td>'+a.data.playCount+"</td></tr><tr><th>Skip count</th><td>"+a.data.skipCount+"</td></tr><tr><th>Last played</th><td>"+(0==a.data.lastPlayed?"never":
(new Date(1E3*a.data.lastPlayed)).toUTCString())+"</td></tr><tr><th>Like</th><td>"+d+"</td></tr>");b.getElementsByTagName("tbody")[0].innerHTML=c}function execSyscmd(a){sendAPI({cmd:"MPD_API_SYSCMD",data:{cmd:a}})}function playlistDetails(a){document.getElementById("BrowsePlaylistsAllList").classList.add("opacity05");appGoto("Browse","Playlists","Detail","0/-/"+a)}
function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_PLAYLIST_RM_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05")}function playlistClear(){var a=document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri");sendAPI({cmd:"MPD_API_PLAYLIST_CLEAR_AND_LIST",data:{uri:a}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05")}
function getAllPlaylists(a){var b=a.data.length,c="";0==a.offset&&("addToPlaylistPlaylist"==playlistEl?c="<option></option><option>New Playlist</option>":"selectJukeboxPlaylist"==playlistEl&&(c="<option>Database</option>"));for(var d=0;d<b;d++)c+="<option","selectJukeboxPlaylist"==playlistEl&&a.data[d].uri==settings.jukeboxPlaylist&&(c+=" selected"),c+=">"+a.data[d].uri+"</option>";0==a.offset?document.getElementById(playlistEl).innerHTML=c:document.getElementById(playlistEl).innerHTML+=c;a.totalEntities>
a.returnedEntities&&(a.offset+=settings.maxElementsPerPage,sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:a.offset,filter:"-"}},getAllPlaylists))}function updateSmartPlaylists(){sendAPI({cmd:"MPD_API_SMARTPLS_UPDATE_ALL"})}
function voteSong(a){var b=domCache.currentTrack.getAttribute("data-uri");""!=b&&(2==a&&domCache.btnVoteUp.classList.contains("active-fg-green")?a=1:0==a&&domCache.btnVoteDown.classList.contains("active-fg-red")&&(a=1),sendAPI({cmd:"MPD_API_LIKE",data:{uri:b,like:a}}),setVoteSongBtns(a,b))}
@ -114,15 +134,15 @@ function setVoteSongBtns(a,b){""==b||0==b.indexOf("http://")||0==b.indexOf("http
function toggleAddToPlaylistFrm(){var a=document.getElementById("toggleAddToPlaylistBtn");toggleBtn("toggleAddToPlaylistBtn");a.classList.contains("active")?(document.getElementById("addToPlaylistFrm").classList.remove("hide"),document.getElementById("addStreamFooter").classList.add("hide"),document.getElementById("addToPlaylistFooter").classList.remove("hide")):(document.getElementById("addToPlaylistFrm").classList.add("hide"),document.getElementById("addStreamFooter").classList.remove("hide"),document.getElementById("addToPlaylistFooter").classList.add("hide"))}
function saveSearchAsSmartPlaylist(){parseSmartPlaylist({type:"smartpls",data:{playlist:"",type:"search",tag:app.current.filter,searchstr:app.current.search}})}
function parseSmartPlaylist(a){var b=document.getElementById("saveSmartPlaylistName");b.value=a.data.playlist;b.classList.remove("is-invalid");document.getElementById("saveSmartPlaylistType").value=a.data.type;document.getElementById("saveSmartPlaylistFrm").classList.remove("was-validated");document.getElementById("saveSmartPlaylistSearch").classList.add("hide");document.getElementById("saveSmartPlaylistSticker").classList.add("hide");document.getElementById("saveSmartPlaylistNewest").classList.add("hide");
for(var c='<option value="any">Any Tag</option>',d=0;d<settings.tags.length;d++)"Track"!=settings.tags[d]&&(c+='<option value="'+settings.tags[d]+'">'+settings.tags[d]+"</option>");document.getElementById("selectSaveSmartPlaylistTag").innerHTML=c;"search"==a.data.type?(document.getElementById("saveSmartPlaylistSearch").classList.remove("hide"),document.getElementById("selectSaveSmartPlaylistTag").value=a.data.tag,document.getElementById("inputSaveSmartPlaylistSearchstr").value=a.data.searchstr):"sticker"==
a.data.type?(document.getElementById("saveSmartPlaylistSticker").classList.remove("hide"),document.getElementById("selectSaveSmartPlaylistSticker").value=a.data.sticker,document.getElementById("inputSaveSmartPlaylistStickerMaxentries").value=a.data.maxentries):"newest"==a.data.type&&(document.getElementById("saveSmartPlaylistNewest").classList.remove("hide"),a=a.data.timerange/24/60/60,document.getElementById("inputSaveSmartPlaylistNewestTimerange").value=a);modalSaveSmartPlaylist.show();b.focus()}
function chkInt(a,b){if(""!=a.value.replace(/\d/g,""))return a.classList.add("is-invalid"),b.classList.add("was-validated"),!1;a.classList.remove("is-invalid");return!0}
var c;settings.featTags&&(c='<option value="any">Any Tag</option>');c+='<option value="filename">Filename</option>';for(var d=0;d<settings.searchtags.length;d++)c+='<option value="'+settings.searchtags[d]+'">'+settings.searchtags[d]+"</option>";document.getElementById("selectSaveSmartPlaylistTag").innerHTML=c;"search"==a.data.type?(document.getElementById("saveSmartPlaylistSearch").classList.remove("hide"),document.getElementById("selectSaveSmartPlaylistTag").value=a.data.tag,document.getElementById("inputSaveSmartPlaylistSearchstr").value=
a.data.searchstr):"sticker"==a.data.type?(document.getElementById("saveSmartPlaylistSticker").classList.remove("hide"),document.getElementById("selectSaveSmartPlaylistSticker").value=a.data.feat_sticker,document.getElementById("inputSaveSmartPlaylistStickerMaxentries").value=a.data.maxentries):"newest"==a.data.type&&(document.getElementById("saveSmartPlaylistNewest").classList.remove("hide"),a=a.data.timerange/24/60/60,document.getElementById("inputSaveSmartPlaylistNewestTimerange").value=a);modalSaveSmartPlaylist.show();
b.focus()}function chkInt(a,b){if(""!=a.value.replace(/\d/g,""))return a.classList.add("is-invalid"),b.classList.add("was-validated"),!1;a.classList.remove("is-invalid");return!0}
function saveSmartPlaylist(){var a=document.getElementById("saveSmartPlaylistName").value,b=document.getElementById("saveSmartPlaylistType").value,c=a.replace(/[\w\-]/g,""),d=document.getElementById("saveSmartPlaylistFrm");if(""!=a&&""==c){if("search"==b)d=document.getElementById("selectSaveSmartPlaylistTag"),d=d.options[d.selectedIndex].value,c=document.getElementById("inputSaveSmartPlaylistSearchstr").value,sendAPI({cmd:"MPD_API_SMARTPLS_SAVE",data:{type:b,playlist:a,tag:d,searchstr:c}});else if("sticker"==
b){c=document.getElementById("selectSaveSmartPlaylistSticker");c=c.options[c.selectedIndex].value;var e=document.getElementById("inputSaveSmartPlaylistStickerMaxentries");if(!chkInt(e,d))return;sendAPI({cmd:"MPD_API_SMARTPLS_SAVE",data:{type:b,playlist:a,sticker:c,maxentries:e.value}})}else if("newest"==b){c=document.getElementById("inputSaveSmartPlaylistNewestTimerange");if(!chkInt(c,d))return;d=86400*parseInt(c.value);sendAPI({cmd:"MPD_API_SMARTPLS_SAVE",data:{type:b,playlist:a,timerange:d}})}else{document.getElementById("saveSmartPlaylistType").classList.add("is-invalid");
return}modalSaveSmartPlaylist.hide();showNotification("Saved smart playlist "+a,"","","success")}else document.getElementById("saveSmartPlaylistName").classList.add("is-invalid"),d.classList.add("was-validated")}
function showAddToPlaylist(a){document.getElementById("addToPlaylistUri").value=a;document.getElementById("addToPlaylistPlaylist").innerHTML="";document.getElementById("addToPlaylistNewPlaylist").value="";document.getElementById("addToPlaylistNewPlaylistDiv").classList.add("hide");document.getElementById("addToPlaylistFrm").classList.remove("was-validated");document.getElementById("addToPlaylistNewPlaylist").classList.remove("is-invalid");toggleBtn("toggleAddToPlaylistBtn",0);var b=document.getElementById("streamUrl");
b.focus();b.value="";b.classList.remove("is-invalid");document.getElementById("addStreamFrm").classList.remove("was-validated");"stream"!=a?(document.getElementById("addStreamFooter").classList.add("hide"),document.getElementById("addStreamFrm").classList.add("hide"),document.getElementById("addToPlaylistFooter").classList.remove("hide"),document.getElementById("addToPlaylistFrm").classList.remove("hide"),document.getElementById("addToPlaylistLabel").innerText="Add to playlist"):(document.getElementById("addStreamFooter").classList.remove("hide"),
document.getElementById("addStreamFrm").classList.remove("hide"),document.getElementById("addToPlaylistFooter").classList.add("hide"),document.getElementById("addToPlaylistFrm").classList.add("hide"),document.getElementById("addToPlaylistLabel").innerText="Add Stream");modalAddToPlaylist.show();playlistEl="addToPlaylistPlaylist";sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:0,filter:"-"}},getAllPlaylists)}
document.getElementById("addStreamFrm").classList.remove("hide"),document.getElementById("addToPlaylistFooter").classList.add("hide"),document.getElementById("addToPlaylistFrm").classList.add("hide"),document.getElementById("addToPlaylistLabel").innerText="Add Stream");modalAddToPlaylist.show();settings.featPlaylists&&(playlistEl="addToPlaylistPlaylist",sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:0,filter:"-"}},getAllPlaylists))}
function addToPlaylist(){var a=document.getElementById("addToPlaylistUri").value;if("stream"==a&&(a=document.getElementById("streamUrl").value,""==a||-1==a.indexOf("http"))){document.getElementById("streamUrl").classList.add("is-invalid");document.getElementById("addStreamFrm").classList.add("was-validated");return}var b=document.getElementById("addToPlaylistPlaylist");b=b.options[b.selectedIndex].text;if("New Playlist"==b){b=document.getElementById("addToPlaylistNewPlaylist").value;var c=b.replace(/[\w\-]/g,
"");if(""==b||""!=c){document.getElementById("addToPlaylistNewPlaylist").classList.add("is-invalid");document.getElementById("addToPlaylistFrm").classList.add("was-validated");return}}""!=b?("SEARCH"==a?addAllFromSearchPlist(b):"DATABASE"==a?addAllFromBrowseDatabasePlist(b):sendAPI({cmd:"MPD_API_PLAYLIST_ADD_TRACK",data:{uri:a,plist:b}}),modalAddToPlaylist.hide()):(document.getElementById("addToPlaylistPlaylist").classList.add("is-invalid"),document.getElementById("addToPlaylistFrm").classList.add("was-validated"))}
function addStream(){var a=document.getElementById("streamUrl").value;""!=a&&0==a.indexOf("http")?(sendAPI({cmd:"MPD_API_QUEUE_ADD_TRACK",data:{uri:a}}),modalAddToPlaylist.hide(),showNotification("Added stream "+a+"to queue","","","success")):(document.getElementById("streamUrl").classList.add("is-invalid"),document.getElementById("addStreamFrm").classList.add("was-validated"))}
@ -130,13 +150,13 @@ function showRenamePlaylist(a){document.getElementById("renamePlaylistFrm").clas
function renamePlaylist(){var a=document.getElementById("renamePlaylistFrom").value,b=document.getElementById("renamePlaylistTo").value,c=b.replace(/[\w\-]/g,"");""!=b&&b!=a&&""==c?(sendAPI({cmd:"MPD_API_PLAYLIST_RENAME",data:{from:a,to:b}}),modalRenamePlaylist.hide()):(document.getElementById("renamePlaylistTo").classList.add("is-invalid"),document.getElementById("renamePlaylistFrm").classList.add("was-validated"))}
function showSmartPlaylist(a){sendAPI({cmd:"MPD_API_SMARTPLS_GET",data:{playlist:a}},parseSmartPlaylist)}function dirname(a){return a.replace(/\/[^\/]*$/,"")}function b64EncodeUnicode(a){return btoa(encodeURIComponent(a).replace(/%([0-9A-F]{2})/g,function(a,c){return String.fromCharCode("0x"+c)}))}function b64DecodeUnicode(a){return decodeURIComponent(atob(a).split("").map(function(a){return"%"+("00"+a.charCodeAt(0).toString(16)).slice(-2)}).join(""))}
function addMenuItem(a,b){return'<a class="dropdown-item" href="#" data-href=\''+b64EncodeUnicode(JSON.stringify(a))+"'>"+b+"</a>"}function hideMenu(){var a=document.querySelector("[data-popover]");a&&(new Popover(a,{}),a.Popover.hide(),a.removeAttribute("data-popover"))}
function showMenu(a,b){b.preventDefault();b.stopPropagation();if(!a.getAttribute("data-init")){hideMenu();b=a.getAttribute("data-type");var c=decodeURI(a.getAttribute("data-uri")),d=a.getAttribute("data-name"),e=0;if(null==b||null==c)b=a.parentNode.parentNode.getAttribute("data-type"),c=decodeURI(a.parentNode.parentNode.getAttribute("data-uri")),d=a.parentNode.parentNode.getAttribute("data-name");lastState&&(e=lastState.data.nextSongPos);var f="";"Browse"==app.current.app&&"Filesystem"==app.current.tab||
"Search"==app.current.app||"Browse"==app.current.app&&"Database"==app.current.tab?(f+=addMenuItem({cmd:"appendQueue",options:[b,c,d]},"Append to queue")+("song"==b?addMenuItem({cmd:"appendAfterQueue",options:[b,c,e,d]},"Add after current playing song"):"")+addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+("plist"!=b&&"smartpls"!=b?addMenuItem({cmd:"showAddToPlaylist",options:[c]},"Add to playlist"):"")+("song"==b?addMenuItem({cmd:"songDetails",options:[c]},"Songdetails"):"")+("plist"==
b||"smartpls"==b?addMenuItem({cmd:"playlistDetails",options:[c]},"View playlist"):""),"Search"==app.current.app&&(c=dirname(c),f+='<div class="dropdown-divider"></div><a class="dropdown-item" id="advancedMenuLink" data-toggle="collapse" href="#advancedMenu"><span class="material-icons material-icons-small-left">keyboard_arrow_right</span>Album actions</a><div class="collapse" id="advancedMenu">'+addMenuItem({cmd:"appendQueue",options:[b,c,d]},"Append to queue")+addMenuItem({cmd:"appendAfterQueue",
options:[b,c,e,d]},"Add after current playing song")+addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+addMenuItem({cmd:"showAddToPlaylist",options:[c]},"Add to playlist")+"</div>")):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view?f+=addMenuItem({cmd:"appendQueue",options:[b,c,d]},"Append to queue")+addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+("smartpls"==b?addMenuItem({cmd:"playlistDetails",options:[c]},"View playlist"):addMenuItem({cmd:"playlistDetails",
options:[c]},"Edit playlist"))+("smartpls"==b?addMenuItem({cmd:"showSmartPlaylist",options:[c]},"Edit smart playlist"):"")+(0!=c.indexOf("myMPDsmart")?addMenuItem({cmd:"showRenamePlaylist",options:[c]},"Rename playlist")+addMenuItem({cmd:"showDelPlaylist",options:[c]},"Delete playlist"):""):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view?(e=document.getElementById("BrowsePlaylistsDetailList"),f+=addMenuItem({cmd:"appendQueue",options:[b,c,d]},"Append to queue")+
addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+("false"==e.getAttribute("data-ro")?addMenuItem({cmd:"removeFromPlaylist",options:[e.getAttribute("data-uri"),a.parentNode.parentNode.getAttribute("data-songpos")]},"Remove"):"")+addMenuItem({cmd:"showAddToPlaylist",options:[c]},"Add to playlist")+(-1==c.indexOf("http")?addMenuItem({cmd:"songDetails",options:[c]},"Songdetails"):"")):"Queue"==app.current.app&&(f+=addMenuItem({cmd:"delQueueSong",options:["single",a.parentNode.parentNode.getAttribute("data-trackid")]},
"Remove")+addMenuItem({cmd:"delQueueSong",options:["range",0,a.parentNode.parentNode.getAttribute("data-songpos")]},"Remove all upwards")+addMenuItem({cmd:"delQueueSong",options:["range",parseInt(a.parentNode.parentNode.getAttribute("data-songpos"))-1,-1]},"Remove all downwards")+(-1==c.indexOf("http")?addMenuItem({cmd:"songDetails",options:[c]},"Songdetails"):""));new Popover(a,{trigger:"click",delay:0,dismissible:!0,template:'<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-content">'+
function showMenu(a,b){b.preventDefault();b.stopPropagation();hideMenu();if(!a.getAttribute("data-init")){b=a.getAttribute("data-type");var c=decodeURI(a.getAttribute("data-uri")),d=a.getAttribute("data-name"),e=0;if(null==b||null==c)b=a.parentNode.parentNode.getAttribute("data-type"),c=decodeURI(a.parentNode.parentNode.getAttribute("data-uri")),d=a.parentNode.parentNode.getAttribute("data-name");lastState&&(e=lastState.data.nextSongPos);var f="";"Browse"==app.current.app&&"Filesystem"==app.current.tab||
"Search"==app.current.app||"Browse"==app.current.app&&"Database"==app.current.tab?(f+=addMenuItem({cmd:"appendQueue",options:[b,c,d]},"Append to queue")+("song"==b?addMenuItem({cmd:"appendAfterQueue",options:[b,c,e,d]},"Add after current playing song"):"")+addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+("plist"!=b&&"smartpls"!=b&&settings.featPlaylists?addMenuItem({cmd:"showAddToPlaylist",options:[c]},"Add to playlist"):"")+("song"==b?addMenuItem({cmd:"songDetails",options:[c]},
"Songdetails"):"")+("plist"==b||"smartpls"==b?addMenuItem({cmd:"playlistDetails",options:[c]},"View playlist"):""),"Search"==app.current.app&&(c=dirname(c),f+='<div class="dropdown-divider"></div><a class="dropdown-item" id="advancedMenuLink" data-toggle="collapse" href="#advancedMenu"><span class="material-icons material-icons-small-left">keyboard_arrow_right</span>Album actions</a><div class="collapse" id="advancedMenu">'+addMenuItem({cmd:"appendQueue",options:[b,c,d]},"Append to queue")+addMenuItem({cmd:"appendAfterQueue",
options:[b,c,e,d]},"Add after current playing song")+addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+(settings.featPlaylists?addMenuItem({cmd:"showAddToPlaylist",options:[c]},"Add to playlist"):"")+"</div>")):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view?f+=addMenuItem({cmd:"appendQueue",options:[b,c,d]},"Append to queue")+addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+("smartpls"==b?addMenuItem({cmd:"playlistDetails",options:[c]},
"View playlist"):addMenuItem({cmd:"playlistDetails",options:[c]},"Edit playlist"))+("smartpls"==b?addMenuItem({cmd:"showSmartPlaylist",options:[c]},"Edit smart playlist"):"")+(0!=c.indexOf("myMPDsmart")?addMenuItem({cmd:"showRenamePlaylist",options:[c]},"Rename playlist")+addMenuItem({cmd:"showDelPlaylist",options:[c]},"Delete playlist"):""):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view?(e=document.getElementById("BrowsePlaylistsDetailList"),f+=addMenuItem({cmd:"appendQueue",
options:[b,c,d]},"Append to queue")+addMenuItem({cmd:"replaceQueue",options:[b,c,d]},"Replace queue")+("false"==e.getAttribute("data-ro")?addMenuItem({cmd:"removeFromPlaylist",options:[e.getAttribute("data-uri"),a.parentNode.parentNode.getAttribute("data-songpos")]},"Remove"):"")+addMenuItem({cmd:"showAddToPlaylist",options:[c]},"Add to playlist")+(-1==c.indexOf("http")?addMenuItem({cmd:"songDetails",options:[c]},"Songdetails"):"")):"Queue"==app.current.app&&(f+=addMenuItem({cmd:"delQueueSong",options:["single",
a.parentNode.parentNode.getAttribute("data-trackid")]},"Remove")+addMenuItem({cmd:"delQueueSong",options:["range",0,a.parentNode.parentNode.getAttribute("data-songpos")]},"Remove all upwards")+addMenuItem({cmd:"delQueueSong",options:["range",parseInt(a.parentNode.parentNode.getAttribute("data-songpos"))-1,-1]},"Remove all downwards")+(-1==c.indexOf("http")?addMenuItem({cmd:"songDetails",options:[c]},"Songdetails"):""));new Popover(a,{trigger:"click",delay:0,dismissible:!0,template:'<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-content">'+
f+"</div></div>"});b=a.Popover;a.setAttribute("data-init","true");a.addEventListener("shown.bs.popover",function(a){a.target.setAttribute("data-popover","true");document.getElementsByClassName("popover-content")[0].addEventListener("click",function(a){a.preventDefault();a.stopPropagation();if("A"==a.target.nodeName&&(a=a.target.getAttribute("data-href"))){a=JSON.parse(b64DecodeUnicode(a));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));
break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}hideMenu()}},!1);if(a=document.getElementById("advancedMenuLink"))a.addEventListener("click",function(a){a=this.getElementsByTagName("span")[0];a.innerText="keyboard_arrow_right"==a.innerText?"keyboard_arrow_down":"keyboard_arrow_right"},!1),new Collapse(a)},!1);b.show()}}
function sendAPI(a,b){var c=new XMLHttpRequest;c.open("POST","/api",!0);c.setRequestHeader("Content-type","application/json");c.onreadystatechange=function(){if(4==c.readyState)if(""!=c.responseText){var d=JSON.parse(c.responseText);"error"==d.type?(showNotification("Error",d.data,d.data,"danger"),console.log("Error: "+d.data)):"result"==d.type&&"ok"!=d.data?showNotification(d.data,"","","success"):void 0!=b&&"function"==typeof b&&b(d)}else console.log("Empty response for request: "+JSON.stringify(a))};
@ -156,12 +176,12 @@ function saveQueue(){var a=document.getElementById("saveQueueName").value,b=a.re
function showNotification(a,b,c,d){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&(document.getElementById("alertBox")?b=document.getElementById("alertBox"):(b=document.createElement("div"),b.setAttribute("id","alertBox"),b.addEventListener("click",function(){hideNotification()},!1)),b.classList.remove("alert-success","alert-danger"),b.classList.add("alert","alert-"+d),b.innerHTML="<div><strong>"+
a+"</strong><br/>"+c+"</div>",document.getElementsByTagName("main")[0].append(b),document.getElementById("alertBox").classList.add("alertBoxActive"),alertTimeout&&clearTimeout(alertTimeout),alertTimeout=setTimeout(function(){hideNotification()},3E3))}function hideNotification(){document.getElementById("alertBox")&&(document.getElementById("alertBox").classList.remove("alertBoxActive"),setTimeout(function(){var a=document.getElementById("alertBox");a&&a.remove()},600))}
function notificationsSupported(){return"Notification"in window}
function songChange(a){if("error"!=a.type&&"result"!=a.type){var b=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentSongId;if(lastSong!=b){var c="",d="",e="myMPD: ";domCache.currentCover.style.backgroundImage='url("'+a.data.cover+'")';"undefined"!=typeof a.data.artist&&0<a.data.artist.length&&"-"!=a.data.artist?(c+=a.data.artist,d+=a.data.artist,e+=a.data.artist+" - ",domCache.currentArtist.innerText=a.data.artist,domCache.currentArtist.setAttribute("data-albumartist",a.data.albumartist)):
domCache.currentArtist.innerText="";"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(c+=" - "+a.data.album,d+="<br/>"+a.data.album,domCache.currentAlbum.innerText=a.data.album,domCache.currentAlbum.setAttribute("data-album",a.data.album)):domCache.currentAlbum.innerText="";"undefined"!=typeof a.data.title&&0<a.data.title.length?(e+=a.data.title,domCache.currentTrack.innerText=a.data.title,domCache.currentTrack.setAttribute("data-uri",a.data.uri)):(domCache.currentTrack.innerText=
"",domCache.currentTrack.setAttribute("data-uri",""));document.title=e;1==settings.stickers&&setVoteSongBtns(a.data.like,a.data.uri);if(e=document.getElementById("queueTrackId"+a.data.currentSongId))e.getElementsByTagName("td")[1].innerText=a.data.title;showNotification(a.data.title,c,d,"success");lastSong=b}}}
function songChange(a){if("error"!=a.type&&"result"!=a.type){var b=a.data.Title+a.data.Artist+a.data.Album+a.data.uri+a.data.currentSongId;if(lastSong!=b){var c="",d="",e="myMPD: ";domCache.currentCover.style.backgroundImage='url("'+a.data.cover+'")';"undefined"!=typeof a.data.Artist&&0<a.data.Artist.length&&"-"!=a.data.Artist?(c+=a.data.Artist,d+=a.data.Artist,e+=a.data.Artist+" - ",domCache.currentArtist.innerText=a.data.Artist,void 0!=a.data.AlbumArtist?domCache.currentArtist.setAttribute("data-albumartist",
a.data.AlbumArtist):void 0!=a.data.Artist?domCache.currentArtist.setAttribute("data-albumartist",a.data.Artist):domCache.currentArtist.setAttribute("data-albumartist","")):domCache.currentArtist.innerText="";"undefined"!=typeof a.data.Album&&0<a.data.Album.length&&"-"!=a.data.Album?(c+=" - "+a.data.Album,d+="<br/>"+a.data.Album,domCache.currentAlbum.innerText=a.data.Album,domCache.currentAlbum.setAttribute("data-album",a.data.Album)):domCache.currentAlbum.innerText="";"undefined"!=typeof a.data.Title&&
0<a.data.Title.length?(e+=a.data.Title,domCache.currentTrack.innerText=a.data.Title,domCache.currentTrack.setAttribute("data-uri",a.data.uri)):(domCache.currentTrack.innerText="",domCache.currentTrack.setAttribute("data-uri",""));document.title=e;1==settings.featStickers&&setVoteSongBtns(a.data.like,a.data.uri);if(e=document.getElementById("queueTrackId"+a.data.currentSongId))e.getElementsByTagName("td")[1].innerText=a.data.Title;showNotification(a.data.Title,c,d,"success");lastSong=b}}}
function doSetFilterLetter(a){var b=document.getElementById(a+"Letters").getElementsByClassName("active")[0];b&&b.classList.remove("active");b=app.current.filter;"0"==b&&(b="#");document.getElementById(a).innerText="Filter"+("-"!=b?": "+b:"");if("-"!=b){a=document.getElementById(a+"Letters").getElementsByTagName("button");for(var c=a.length,d=0;d<c;d++)if(a[d].innerText==b){a[d].classList.add("active");break}}}
function addFilterLetter(a){for(var b='<button class="mr-1 mb-1 btn btn-sm btn-secondary material-icons material-icons-small">delete</button><button class="mr-1 mb-1 btn btn-sm btn-secondary">#</button>',c=65;90>=c;c++)b+='<button class="mr-1 mb-1 btn-sm btn btn-secondary">'+String.fromCharCode(c)+"</button>";a=document.getElementById(a);a.innerHTML=b;a.addEventListener("click",function(a){switch(a.target.innerText){case "delete":b="-";break;case "#":b="0";break;default:b=a.target.innerText}appGoto(app.current.app,
app.current.tab,app.current.view,"0/"+b+"/"+app.current.search)},!1)}function selectTag(a,b,c){a=document.getElementById(a);var d=a.querySelector(".active");d&&d.classList.remove("active");if(d=a.querySelector("[data-tag="+c+"]"))d.classList.add("active"),document.getElementById(b).innerText=d.innerText}
function addTagList(a,b){var c="",d="Title MUSICBRAINZ_TRACKID Count Disc Comment Name".split(" ");1==b&&(c+='<button type="button" class="btn btn-secondary btn-sm btn-block" data-tag="any">Any Tag</button>');for(var e=0;e<settings.tags.length;e++)"Track"!=settings.tags[e]&&(0==b&&-1<d.indexOf(settings.tags[e])||(c+='<button type="button" class="btn btn-secondary btn-sm btn-block" data-tag="'+settings.tags[e]+'">'+settings.tags[e]+"</button>"));document.getElementById(a).innerHTML=c}
function addTagList(a,b){var c="";"searchtags"==b&&(1==settings.featTags&&(c+='<button type="button" class="btn btn-secondary btn-sm btn-block" data-tag="any">Any Tag</button>'),c+='<button type="button" class="btn btn-secondary btn-sm btn-block" data-tag="filename">Filename</button>');for(var d=0;d<settings[b].length;d++)c+='<button type="button" class="btn btn-secondary btn-sm btn-block" data-tag="'+settings[b][d]+'">'+settings[b][d]+"</button>";document.getElementById(a).innerHTML=c}
function gotoTagList(){appGoto(app.current.app,app.current.tab,app.current.view,"0/-/")}function chVolume(a){a=parseInt(domCache.volumeBar.value)+a;0>a?a=0:100<a&&(a=100);domCache.volumeBar.value=a;sendAPI({cmd:"MPD_API_PLAYER_VOLUME_SET",data:{volume:a}})}function beautifyDuration(a){var b=Math.floor(a/86400),c=Math.floor(a/3600)-24*b,d=Math.floor(a/60)-60*c-1440*b;a=a-86400*b-3600*c-60*d;return(0<b?b+"\u2009d ":"")+(0<c?c+"\u2009h "+(10>d?"0":""):"")+d+"\u2009m "+(10>a?"0":"")+a+"\u2009s"}
function genId(a){return"id"+a.replace(/[^\w\-]/g,"")}appInit();

View File

@ -10,5 +10,5 @@ function(){function a(a){return function(d){c||(c=!0,a.call(b,d))}}var b=this,c=
void 0;try{d=a.then}catch(h){this.reject_(h);return}"function"==typeof d?this.settleSameAsThenable_(d,a):this.fulfill_(a)};c.prototype.reject_=function(a){this.settle_(2,a)};c.prototype.fulfill_=function(a){this.settle_(1,a)};c.prototype.settle_=function(a,b){if(0!=this.state_)throw Error("Cannot settle("+a+", "+b+"): Promise already settled in state"+this.state_);this.state_=a;this.result_=b;this.executeOnSettledCallbacks_()};c.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var a=
0;a<this.onSettledCallbacks_.length;++a)l.asyncExecute(this.onSettledCallbacks_[a]);this.onSettledCallbacks_=null}};var l=new b;c.prototype.settleSameAsPromise_=function(a){var b=this.createResolveAndReject_();a.callWhenSettled_(b.resolve,b.reject)};c.prototype.settleSameAsThenable_=function(a,b){var c=this.createResolveAndReject_();try{a.call(b,c.resolve,c.reject)}catch(k){c.reject(k)}};c.prototype.then=function(a,b){function d(a,b){return"function"==typeof a?function(b){try{f(a(b))}catch(m){e(m)}}:
b}var f,e,g=new c(function(a,b){f=a;e=b});this.callWhenSettled_(d(a,f),d(b,e));return g};c.prototype.catch=function(a){return this.then(void 0,a)};c.prototype.callWhenSettled_=function(a,b){function c(){switch(d.state_){case 1:a(d.result_);break;case 2:b(d.result_);break;default:throw Error("Unexpected state: "+d.state_);}}var d=this;null==this.onSettledCallbacks_?l.asyncExecute(c):this.onSettledCallbacks_.push(c)};c.resolve=f;c.reject=function(a){return new c(function(b,c){c(a)})};c.race=function(a){return new c(function(b,
c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE="myMPD-cache-v4.4.0",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mympd.min.css /js/bootstrap-native-v4.min.js /js/mympd.min.js /js/player.min.js /assets/appicon-167.png /assets/appicon-192.png /assets/appicon-512.png /assets/coverimage-httpstream.png /assets/coverimage-notavailable.png /assets/coverimage-loading.png /assets/favicon.ico /assets/MaterialIcons-Regular.ttf /assets/MaterialIcons-Regular.woff /assets/MaterialIcons-Regular.woff2".split(" ");
c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE="myMPD-cache-v4.5.0",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mympd.min.css /js/bootstrap-native-v4.min.js /js/mympd.min.js /js/player.min.js /assets/appicon-167.png /assets/appicon-192.png /assets/appicon-512.png /assets/coverimage-httpstream.png /assets/coverimage-notavailable.png /assets/coverimage-loading.png /assets/favicon.ico /assets/MaterialIcons-Regular.ttf /assets/MaterialIcons-Regular.woff /assets/MaterialIcons-Regular.woff2".split(" ");
self.addEventListener("install",function(a){a.waitUntil(caches.open(CACHE).then(function(a){return a.addAll(urlsToCache)}))});self.addEventListener("fetch",function(a){if(a.request.url.match("^http://"))return!1;a.respondWith(caches.match(a.request).then(function(b){return b?b:fetch(a.request)}))});self.addEventListener("activate",function(a){a.waitUntil(caches.keys().then(function(a){return Promise.all(a.map(function(a){if(a!=CACHE)return caches.delete(a)}))}))});

View File

@ -43,7 +43,7 @@ button {
cursor: pointer;
}
.tblnum, .tblaction {
[data-col=Pos], [data-col=Type], [data-col=Track], [data-col=Action] {
width: 30px;
}
@ -51,6 +51,10 @@ small {
color: #aaa;
}
.card {
min-height: 300px;
}
.card-footer-playback {
padding: 0px;
}
@ -186,14 +190,19 @@ small {
.col-md {
max-width: 250px;
min-width: 250px;
}
a.card-img-top {
a.card-img-left {
overflow: hidden;
display: block;
padding-top: 100%;
width: 250px;
height: 250px;
border-radius: 5px;
background-size: cover;
background-image: url(/assets/coverimage-loading.png);
background-image: url(/assets/coverimage-loading.png);
margin-bottom: 5px;
cursor: pointer;
}
button.active {
@ -245,7 +254,11 @@ caption {
}
.dragover > td {
border-top:25px solid transparent;
border-top:2px solid #28a745;
}
.dragover-th {
border-left:2px solid #28a745;
}
[draggable] {
@ -256,6 +269,7 @@ caption {
/* Required to make elements draggable in old WebKit */
-khtml-user-drag: element;
-webkit-user-drag: element;
cursor: pointer;
}
@keyframes changewidth {

View File

@ -28,12 +28,13 @@
<div class="collapse" id="menu-dbupdate">
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "updateDB", "options": []}'>Update Database</a>
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "rescanDB", "options": []}'>Rescan Database</a>
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "updateSmartPlaylists", "options": []}'>Update Smart Playlists</a>
<a class="dropdown-item text-light bg-dark featSmartpls" href="#" data-href='{"cmd": "updateSmartPlaylists", "options": []}'>Update Smart Playlists</a>
</div>
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "openLocalPlayer", "options": []}'>Local Player</a>
<a class="dropdown-item text-light bg-dark featLocalplayer" href="#" data-href='{"cmd": "openLocalPlayer", "options": []}'>Local Player</a>
<a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a>
<a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</a>
<a id="nav-add2homescreen" class="dropdown-item text-light bg-dark hide" href="#">Add2HomeScreen</a>
<div id="syscmds" class="featSyscmds"></div>
</div>
</div>
<div class="btn-toolbar col-auto pl-0 pr-0">
@ -80,19 +81,21 @@
<div class="card" id="cardPlayback">
<div class="card-header">Playback
<div class="btn-group btn-group-sm stickers pull-right">
<div class="btn-group btn-group-sm featStickers pull-right">
<button title="Dislike song" id="btnVoteDown" data-href='{"cmd": "voteSong", "options": [0]}' class="btn btn-sm btn-light material-icons">thumb_down</button>
<button title="Like song" id="btnVoteUp" data-href='{"cmd": "voteSong", "options": [2]}' class="btn btn-sm btn-light material-icons">thumb_up</button>
</div>
</div>
<div class="card-body">
<div class="album-cover" id="currentCover"></div>
<div class="album-cover featCoverimage" id="currentCover"></div>
<div class="album-desc">
<h2 id="currentTrack" data-href='{"cmd": "songClick", "options": []}'></h2>
<small>Artist</small>
<h4 id="currentArtist" data-href='{"cmd": "artistClick", "options": []}'></h4>
<small>Album</small>
<h4 id="currentAlbum" data-href='{"cmd": "albumClick", "options": []}'></h4>
<div class="featTags">
<small>Artist</small>
<h4 id="currentArtist" data-href='{"cmd": "artistClick", "options": []}'></h4>
<small>Album</small>
<h4 id="currentAlbum" data-href='{"cmd": "albumClick", "options": []}'></h4>
</div>
</div>
</div>
<div class="card-footer card-footer-playback">
@ -110,14 +113,13 @@
</div>
<div class="card-body">
<div class="btn-toolbar card-toolbar" id="queue-buttons">
<div class="btn-group mr-2">
<div class="btn-group mr-2 featPlaylists">
<button type="button" class="btn btn-secondary material-icons" data-toggle="modal" data-target="#modalSaveQueue" title="Save queue">
save
</button>
</div>
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary material-icons"
data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_SHUFFLE"}]}' title="Shuffle queue">
<button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_SHUFFLE"}]}' title="Shuffle queue">
shuffle
</button>
</div>
@ -137,9 +139,9 @@
<div class="input-group-append">
<button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
<span class="material-icons">search</span>
<span id="searchqueuetagdesc">Any Tag</span>
<span id="searchqueuetagsdesc">Any Tag</span>
</button>
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchqueuetag">
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchqueuetags">
</div>
</div>
</div>
@ -153,16 +155,16 @@
</div>
<button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="QueuePaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
<div class="btn-group mr-2 featTags">
<button id="QueueColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button>
<div class="dropdown-menu bg-dark px-2" id="QueueColsDropdown"><form></form>
<button data-href='{"cmd": "saveCols", "options": ["Queue"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button>
</div>
</div>
</div>
<div class="table-responsive-md">
<table id="QueueList" class="table table-hover table-sm" data-version="">
<col class="tblnum"/>
<col class="tbltitle"/>
<col class="tblartist"/>
<col class="tblalbum"/>
<col class="tbllength"/>
<col class="tblaction"/>
<thead>
<tr>
<th>#</th>
@ -199,10 +201,10 @@
<div class="card hide" id="cardBrowse">
<div class="card-header" id="panel-heading-browse">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<li class="nav-item featTags">
<a data-href='{"cmd": "appGoto", "options": ["Browse", "Database"]}' class="nav-link text-dark" href="#" id="cardBrowseNavDatabase">Database</a>
</li>
<li class="nav-item">
<li class="nav-item featPlaylists">
<a data-href='{"cmd": "appGoto", "options": ["Browse", "Playlists"]}' class="nav-link text-dark" href="#" id="cardBrowseNavPlaylists">Playlists</a>
</li>
<li class="nav-item">
@ -235,13 +237,15 @@
</div>
<button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowsePlaylistsPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
<div class="btn-group mr-2 hide featTags">
<button id="BrowsePlaylistsDetailColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button>
<div class="dropdown-menu bg-dark px-2" id="BrowsePlaylistsDetailColsDropdown"><form></form>
<button data-href='{"cmd": "saveCols", "options": ["BrowsePlaylistsDetail"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button>
</div>
</div>
</div>
<div class="table-responsive-md">
<table id="BrowsePlaylistsAllList" class="table table-hover table-sm">
<col class="tblnum"/>
<col class="tbltitle"/>
<col class="tbllastmodified"/>
<col class="tblaction"/>
<thead>
<tr>
<th></th>
@ -255,12 +259,6 @@
</table>
<table id="BrowsePlaylistsDetailList" class="table table-hover table-sm hide">
<caption>Playlist List</caption>
<col class="tblnum"/>
<col class="tbltitle"/>
<col class="tblartist"/>
<col class="tblalbum"/>
<col class="tbllength"/>
<col class="tblaction"/>
<thead>
<tr>
<th>#</th>
@ -314,10 +312,17 @@
<button id="BrowseDatabaseAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button>
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="BrowseDatabaseAddAllSongsDropdown">
<button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button>
<button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button>
<button type="button" class="btn btn-secondary btn-sm btn-block featPlaylists">Add all to playlist</button>
</div>
</div>
</div>
<div class="btn-group mr-2 featTags hide">
<button id="BrowseDatabaseColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button>
<div class="dropdown-menu bg-dark px-2" id="BrowseDatabaseColsDropdown"><form></form>
<button data-href='{"cmd": "saveCols", "options": ["BrowseDatabase", ".tblAlbumTitles"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button>
</div>
</div>
<div id="BrowseDatabasePaginationTop" class="btn-group mr-2 hide">
<button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseDatabasePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
@ -332,8 +337,6 @@
<div class="table-responsive-md">
<table id="BrowseDatabaseTagList" class="table table-hover table-sm">
<col class="tblnum"/>
<col class="tbltitle"/>
<thead>
<tr>
<th></th>
@ -381,7 +384,7 @@
<button id="BrowseFilesystemAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button>
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="BrowseFilesystemAddAllSongsDropdown">
<button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button>
<button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button>
<button type="button" class="btn btn-secondary btn-sm btn-block featPlaylists">Add all to playlist</button>
</div>
</div>
</div>
@ -394,6 +397,12 @@
</div>
<button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseFilesystemPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
<div class="btn-group mr-2 featTags">
<button id="BrowseFilesystemColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button>
<div class="dropdown-menu bg-dark px-2" id="BrowseFilesystemColsDropdown"><form></form>
<button data-href='{"cmd": "saveCols", "options": ["BrowseFilesystem"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button>
</div>
</div>
</div>
<ol id="BrowseBreadcrumb" class="breadcrumb">
@ -401,12 +410,6 @@
<div class="table-responsive-md">
<table id="BrowseFilesystemList" class="table table-hover table-sm">
<col class="tblnum"/>
<col class="tbltitle"/>
<col class="tblartist"/>
<col class="tblalbum"/>
<col class="tbllength"/>
<col class="tblaction"/>
<thead>
<tr>
<th></th>
@ -466,8 +469,8 @@
<button id="searchAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button>
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchAddAllSongsDropdown">
<button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button>
<button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button>
<button type="button" class="btn btn-secondary btn-sm btn-block smartpls">Save as smart playlist</button>
<button type="button" class="btn btn-secondary btn-sm btn-block featPlaylists">Add all to playlist</button>
<button type="button" class="btn btn-secondary btn-sm btn-block featSmartpls">Save as smart playlist</button>
</div>
</div>
</div>
@ -480,15 +483,15 @@
</div>
<button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="SearchPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
<div class="btn-group mr-2 featTags">
<button id="SearchColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button>
<div class="dropdown-menu bg-dark px-2" id="SearchColsDropdown"><form></form>
<button data-href='{"cmd": "saveCols", "options": ["Search"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button>
</div>
</div>
</div>
<div class="table-responsive-md">
<table id="SearchList" class="table table-hover table-sm">
<col class="tblnum"/>
<col class="tbltitle"/>
<col class="tblartist"/>
<col class="tblalbum"/>
<col class="tbllength"/>
<col class="tblaction"/>
<thead>
<tr>
<th></th>
@ -586,7 +589,7 @@
<div class="invalid-feedback">Invalid uri</div>
</div>
</div>
<div class="row">
<div class="row featPlaylists">
<div class="form-group col-md-12">
<button id="toggleAddToPlaylistBtn" class="btn btn-secondary" data-href='{"cmd": "toggleAddToPlaylistFrm", "options":[]}'>Add to playlist</button>
</div>
@ -922,7 +925,7 @@
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<div class="album-cover"></div>
<div class="album-cover featCoverimage"></div>
<h1></h1>
<div class="table-responsive-md">
<table class="table table-sm">

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
var CACHE = 'myMPD-cache-v4.4.0';
var CACHE = 'myMPD-cache-v4.5.0';
var urlsToCache = [
'/',
'/player.html',

View File

@ -2,6 +2,7 @@
#include <string.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
#include "list.h"
int list_init(struct list *l) {
@ -10,8 +11,8 @@ int list_init(struct list *l) {
return 0;
}
int list_get_value(const struct list *l, char *data) {
int value = 0;
int list_get_value(const struct list *l, const char *data) {
int value = -1;
struct node *current = l->list;
while (current != NULL) {
if (strcmp(current->data, data) == 0) {
@ -78,8 +79,36 @@ int list_shuffle(struct list *l) {
return 0;
}
int list_sort_by_value(struct list *l, bool order) {
int swapped;
struct node *ptr1;
struct node *lptr = NULL;
if (l->list == NULL)
return 1;
do {
swapped = 0;
ptr1 = l->list;
while (ptr1->next != lptr) {
if (order == true && ptr1->value > ptr1->next->value) {
list_swap_item(ptr1, ptr1->next);
swapped = 1;
}
else if (order == false && ptr1->value < ptr1->next->value) {
list_swap_item(ptr1, ptr1->next);
swapped = 1;
}
ptr1 = ptr1->next;
}
lptr = ptr1;
}
while (swapped);
return 0;
}
int list_replace(struct list *l, int pos, char *data, int value) {
int list_replace(struct list *l, int pos, const char *data, int value) {
int i = 0;
struct node *current = l->list;
while (current->next != NULL) {
@ -96,7 +125,7 @@ int list_replace(struct list *l, int pos, char *data, int value) {
return 0;
}
int list_push(struct list *l, char *data, int value) {
int list_push(struct list *l, const char *data, int value) {
struct node *n = malloc(sizeof(struct node));
n->value = value;
n->data = strdup(data);

View File

@ -12,10 +12,11 @@ struct list {
int list_init(struct list *l);
int list_push(struct list *l, char *data, int value);
int list_replace(struct list *l, int pos, char *data, int value);
int list_push(struct list *l, const char *data, int value);
int list_replace(struct list *l, int pos, const char *data, int value);
int list_free(struct list *l);
int list_get_value(const struct list *l, char *data);
int list_get_value(const struct list *l, const char *data);
int list_shuffle(struct list *l);
int list_sort_by_value(struct list *l, bool order);
int list_swap_item(struct node *n1, struct node *n2);
struct node *list_node_at(const struct list * l, unsigned index);

View File

@ -41,7 +41,7 @@ const char * mpd_cmd_strs[] = {
};
static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) {
for (unsigned i = 0; i < sizeof(mpd_cmd_strs) / sizeof(mpd_cmd_strs[0]); i ++)
for (unsigned i = 0; i < sizeof(mpd_cmd_strs) / sizeof(mpd_cmd_strs[0]); i++)
if (!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i])))
return i;
@ -109,6 +109,53 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}");
}
break;
case MPD_API_COLS_SAVE:
je = json_scanf(msg.p, msg.len, "{data: {table: %Q}}", &p_charbuf1);
if (je == 1) {
char column_list[800];
snprintf(column_list, 800, "%.*s", msg.len, msg.p);
char *cols = strchr(column_list, '[');
int len = strlen(cols);
if (len > 1)
cols[len - 2] = '\0';
if (strcmp(p_charbuf1,"colsQueue")==0) {
free(mympd_state.colsQueue);
mympd_state.colsQueue = strdup(cols);
}
else if (strcmp(p_charbuf1,"colsSearch")==0) {
free(mympd_state.colsSearch);
mympd_state.colsSearch = strdup(cols);
}
else if (strcmp(p_charbuf1,"colsBrowseDatabase")==0) {
free(mympd_state.colsBrowseDatabase);
mympd_state.colsBrowseDatabase = strdup(cols);
}
else if (strcmp(p_charbuf1,"colsBrowsePlaylistsDetail")==0) {
free(mympd_state.colsBrowsePlaylistsDetail);
mympd_state.colsBrowsePlaylistsDetail = strdup(cols);
}
else if (strcmp(p_charbuf1,"colsBrowseFilesystem")==0) {
free(mympd_state.colsBrowseFilesystem);
mympd_state.colsBrowseFilesystem = strdup(cols);
}
mympd_state_set(p_charbuf1, cols);
free(p_charbuf1);
}
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
break;
case MPD_API_SYSCMD:
if (config.syscmds == true) {
je = json_scanf(msg.p, msg.len, "{data: {cmd: %Q}}", &p_charbuf1);
if (je == 1) {
int_buf1 = list_get_value(&syscmds, p_charbuf1);
if (int_buf1 > -1)
n = mympd_syscmd(mpd.buf, p_charbuf1, int_buf1);
free(p_charbuf1);
}
}
else
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}");
break;
case MPD_API_PLAYER_STATE:
n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length);
break;
@ -281,10 +328,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
case MPD_API_QUEUE_MOVE_TRACK:
je = json_scanf(msg.p, msg.len, "{data: {from: %u, to: %u}}", &uint_buf1, &uint_buf2);
if (je == 2) {
uint_buf1 --;
uint_buf2 --;
uint_buf1--;
uint_buf2--;
if (uint_buf1 < uint_buf2)
uint_buf2 --;
uint_buf2--;
mpd_run_move(mpd.conn, uint_buf1, uint_buf2);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
}
@ -292,10 +339,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
case MPD_API_PLAYLIST_MOVE_TRACK:
je = json_scanf(msg.p, msg.len, "{data: {plist: %Q, from: %u, to: %u }}", &p_charbuf1, &uint_buf1, &uint_buf2);
if (je == 3) {
uint_buf1 --;
uint_buf2 --;
uint_buf1--;
uint_buf2--;
if (uint_buf1 < uint_buf2)
uint_buf2 --;
uint_buf2--;
mpd_send_playlist_move(mpd.conn, p_charbuf1, uint_buf1, uint_buf2);
mpd_response_finish(mpd.conn);
free(p_charbuf1);
@ -340,7 +387,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
}
break;
case MPD_API_QUEUE_LIST:
je = json_scanf(msg.p, msg.len, "{data: {offset: %u }}", &uint_buf1);
je = json_scanf(msg.p, msg.len, "{data: {offset: %u}}", &uint_buf1);
if (je == 1) {
n = mympd_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length);
}
@ -349,7 +396,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
n = mympd_put_current_song(mpd.buf);
break;
case MPD_API_DATABASE_SONGDETAILS:
je = json_scanf(msg.p, msg.len, "{data: { uri: %Q }}", &p_charbuf1);
je = json_scanf(msg.p, msg.len, "{data: { uri: %Q}}", &p_charbuf1);
if (je == 1) {
n = mympd_put_songdetails(mpd.buf, p_charbuf1);
free(p_charbuf1);
@ -407,7 +454,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
}
break;
case MPD_API_PLAYLIST_LIST:
je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q}}", &uint_buf1, &p_charbuf1);
je = json_scanf(msg.p, msg.len, "{data: {offset: %u, filter: %Q}}", &uint_buf1, &p_charbuf1);
if (je == 2) {
n = mympd_put_playlists(mpd.buf, uint_buf1, p_charbuf1);
free(p_charbuf1);
@ -613,7 +660,7 @@ void mympd_notify(struct mg_mgr *s) {
void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) {
int len = 0;
for (unsigned j = 0;; j ++) {
for (unsigned j = 0;; j++) {
enum mpd_idle idle_event = 1 << j;
const char *idle_name = mpd_idle_name(idle_event);
if (idle_name == NULL)
@ -676,18 +723,24 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) {
void mympd_mpd_features() {
struct mpd_pair *pair;
char s[2] = ",";
char *str = strdup(config.taglist);
char *taglist = strdup(config.taglist);
char *searchtaglist = strdup(config.searchtaglist);
char *browsetaglist = strdup(config.browsetaglist);
char *token;
mpd.protocol = mpd_connection_get_server_version(mpd.conn);
// Defaults
mpd.feat_sticker = false;
mpd.feat_playlists = false;
mpd.feat_tags = false;
mpd_send_allowed_commands(mpd.conn);
while ((pair = mpd_recv_command_pair(mpd.conn)) != NULL) {
if (strcmp(pair->value, "sticker") == 0)
mpd.feat_sticker = true;
if (strcmp(pair->value, "listplaylists") == 0)
mpd.feat_playlists = true;
mpd_return_pair(mpd.conn, pair);
}
mpd_response_finish(mpd.conn);
@ -700,7 +753,11 @@ void mympd_mpd_features() {
printf("Stickers are disabled, disabling smartplaylists\n");
config.smartpls = false;
}
if (mpd.feat_playlists == false && config.smartpls == true) {
printf("Playlists are disabled, disabling smartplaylists\n");
config.smartpls = false;
}
printf("MPD supported tags: ");
list_free(&mpd_tags);
mpd_send_list_tag_types(mpd.conn);
@ -710,18 +767,45 @@ void mympd_mpd_features() {
mpd_return_pair(mpd.conn, pair);
}
mpd_response_finish(mpd.conn);
printf("\nmyMPD enabled tags: ");
list_free(&mympd_tags);
token = strtok(str, s);
while (token != NULL) {
if (list_get_value(&mpd_tags, token) == 1) {
list_push(&mympd_tags, token, 1);
printf("%s ", token);
if (mpd_tags.length == 0) {
printf("none\nTags are disabled\n");
mpd.feat_tags = false;
}
else {
mpd.feat_tags = true;
printf("\nmyMPD enabled tags: ");
token = strtok(taglist, s);
while (token != NULL) {
if (list_get_value(&mpd_tags, token) == 1) {
list_push(&mympd_tags, token, 1);
printf("%s ", token);
}
token = strtok(NULL, s);
}
token = strtok(NULL, s);
}
printf("\n");
printf("\nmyMPD enabled searchtags: ");
token = strtok(searchtaglist, s);
while (token != NULL) {
if (list_get_value(&mpd_tags, token) == 1) {
list_push(&mympd_searchtags, token, 1);
printf("%s ", token);
}
token = strtok(NULL, s);
}
printf("\nmyMPD enabled browsetags: ");
token = strtok(browsetaglist, s);
while (token != NULL) {
if (list_get_value(&mpd_tags, token) == 1) {
list_push(&mympd_browsetags, token, 1);
printf("%s ", token);
}
token = strtok(NULL, s);
}
printf("\n");
}
free(taglist);
free(searchtaglist);
free(browsetaglist);
}
void mympd_idle(struct mg_mgr *s, int timeout) {
@ -859,25 +943,15 @@ void mympd_get_sticker(const char *uri, t_sticker *sticker) {
LOG_ERROR_AND_RECOVER("mpd_send_sticker_list");
}
char* mympd_get_song_uri_from_song_id(int song_id) {
char *str;
void mympd_count_song_id(int song_id, char *name, int value) {
struct mpd_song *song;
if (song_id > -1) {
song = mpd_run_get_queue_song_id(mpd.conn, song_id);
if (song) {
str = strdup(mpd_song_get_uri(song));
mympd_count_song_uri(mpd_song_get_uri(song), name, value);
mpd_song_free(song);
}
else
return NULL;
}
else
return NULL;
return str;
}
void mympd_count_song_id(int song_id, char *name, int value) {
mympd_count_song_uri(mympd_get_song_uri_from_song_id(song_id), name, value);
}
void mympd_count_song_uri(const char *uri, char *name, int value) {
@ -921,7 +995,14 @@ void mympd_like_song_uri(const char *uri, int value) {
}
void mympd_last_played_song_id(int song_id) {
mympd_last_played_song_uri(mympd_get_song_uri_from_song_id(song_id));
struct mpd_song *song;
if (song_id > -1) {
song = mpd_run_get_queue_song_id(mpd.conn, song_id);
if (song) {
mympd_last_played_song_uri(mpd_song_get_uri(song));
mpd_song_free(song);
}
}
}
void mympd_last_played_song_uri(const char *uri) {
@ -974,6 +1055,11 @@ void mympd_jukebox() {
if (addSongs < 1)
return;
if (mpd.feat_playlists == false && strcmp(mympd_state.jukeboxPlaylist, "Database") != 0) {
printf("Jukebox: Playlists are disabled\n");
return;
}
srand((unsigned int)time(NULL));
@ -984,7 +1070,7 @@ void mympd_jukebox() {
//add songs
if (strcmp(mympd_state.jukeboxPlaylist, "Database") == 0) {
if (!mpd_send_list_all(mpd.conn, "/")) {
LOG_ERROR_AND_RECOVER("mpd_send_list_playlist");
LOG_ERROR_AND_RECOVER("mpd_send_list_all");
list_free(&add_list);
return;
}
@ -1026,12 +1112,12 @@ void mympd_jukebox() {
else if (mympd_state.jukeboxMode == 2) {
//add album
if (!mpd_search_db_tags(mpd.conn, MPD_TAG_ALBUM)) {
LOG_ERROR_AND_RECOVER("mpd_send_list_playlist");
LOG_ERROR_AND_RECOVER("mpd_search_db_tags");
list_free(&add_list);
return;
}
if (!mpd_search_commit(mpd.conn)) {
LOG_ERROR_AND_RECOVER("mpd_send_list_playlist");
LOG_ERROR_AND_RECOVER("mpd_search_commit");
list_free(&add_list);
return;
}
@ -1064,6 +1150,7 @@ void mympd_jukebox() {
list_shuffle(&add_list);
nkeep = 0;
struct node *current = add_list.list;
while (current != NULL) {
if (mympd_state.jukeboxMode == 1) {
@ -1071,6 +1158,8 @@ void mympd_jukebox() {
if (!mpd_run_add(mpd.conn, current->data)) {
LOG_ERROR_AND_RECOVER("mpd_run_add");
}
else
nkeep++;
}
else {
printf("Jukebox adding album: %s\n", current->data);
@ -1078,12 +1167,19 @@ void mympd_jukebox() {
LOG_ERROR_AND_RECOVER("mpd_send_command");
return;
}
else
nkeep++;
mpd_response_finish(mpd.conn);
}
current = current->next;
}
list_free(&add_list);
mpd_run_play(mpd.conn);
if (nkeep > 0)
mpd_run_play(mpd.conn);
else {
printf("Error adding song(s), trying again...\n");
mympd_jukebox();
}
}
int randrange(int n) {
@ -1197,7 +1293,7 @@ bool mympd_state_get(char *name, char *value) {
return false;
}
bool mympd_state_set(char *name, char *value) {
bool mympd_state_set(const char *name, const char *value) {
char tmpfile[400];
char cfgfile[400];
snprintf(cfgfile, 400, "%s/state/%s", config.varlibdir, name);
@ -1214,6 +1310,32 @@ bool mympd_state_set(char *name, char *value) {
return true;
}
int mympd_syscmd(char *buffer, char *cmd, int order) {
int len;
char filename[400];
char *line;
size_t n = 0;
ssize_t read;
snprintf(filename, 400, "%s/syscmds/%d%s", config.etcdir, order, cmd);
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s\"}", cmd);
return len;
}
read = getline(&line, &n, fp);
fclose(fp);
if (read > 0) {
strtok(line, "\n");
system(line);
len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Executed cmd %s\"}", line);
} else {
len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s\"}", cmd);
}
CHECK_RETURN_LEN();
return len;
}
int mympd_put_settings(char *buffer) {
struct mpd_status *status;
char *replaygain = strdup("");
@ -1238,8 +1360,8 @@ int mympd_put_settings(char *buffer) {
len = json_printf(&out, "{type: settings, data: {"
"repeat: %d, single: %d, crossfade: %d, consume: %d, random: %d, "
"mixrampdb: %f, mixrampdelay: %f, mpdhost: %Q, mpdport: %d, passwort_set: %B, "
"streamport: %d, coverimage: %Q, stickers: %B, mixramp: %B, smartpls: %B, maxElementsPerPage: %d, "
"mixrampdb: %f, mixrampdelay: %f, mpdhost: %Q, mpdport: %d, passwort_set: %B, featSyscmds: %B, featPlaylists: %B, featTags: %B, featLibrary: %B, "
"featLocalplayer: %B, streamport: %d, streamurl: %Q, featCoverimage: %B, coverimagename: %Q, featStickers: %B, mixramp: %B, featSmartpls: %B, maxElementsPerPage: %d, "
"replaygain: %Q, notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, jukeboxQueueLength: %d, "
"tags: [",
mpd_status_get_repeat(status),
@ -1252,8 +1374,15 @@ int mympd_put_settings(char *buffer) {
config.mpdhost,
config.mpdport,
config.mpdpass ? "true" : "false",
config.streamport,
config.syscmds,
mpd.feat_playlists,
mpd.feat_tags,
mpd.feat_library,
config.localplayer,
config.streamport,
config.streamurl,
config.coverimage,
config.coverimagename,
config.stickers,
config.mixramp,
config.smartpls,
@ -1268,15 +1397,52 @@ int mympd_put_settings(char *buffer) {
mpd_status_free(status);
free(replaygain);
nr = 0;
struct node *current = mympd_tags.list;
while (current != NULL) {
if (nr ++)
if (nr++)
len += json_printf(&out, ",");
len += json_printf(&out, "%Q", current->data);
current = current->next;
}
len += json_printf(&out, "]}}");
len += json_printf(&out, "], searchtags: [");
nr = 0;
current = mympd_searchtags.list;
while (current != NULL) {
if (nr++)
len += json_printf(&out, ",");
len += json_printf(&out, "%Q", current->data);
current = current->next;
}
len += json_printf(&out, "], browsetags: [");
nr = 0;
current = mympd_browsetags.list;
while (current != NULL) {
if (nr++)
len += json_printf(&out, ",");
len += json_printf(&out, "%Q", current->data);
current = current->next;
}
len += json_printf(&out, "]");
if (config.syscmds == true) {
len += json_printf(&out, ", syscmds: [");
nr = 0;
current = syscmds.list;
while (current != NULL) {
if (nr++)
len += json_printf(&out, ",");
len += json_printf(&out, "%Q", current->data);
current = current->next;
}
len += json_printf(&out, "]");
}
len += json_printf(&out, ", colsQueue: %s", mympd_state.colsQueue);
len += json_printf(&out, ", colsSearch: %s", mympd_state.colsSearch);
len += json_printf(&out, ", colsBrowseDatabase: %s", mympd_state.colsBrowseDatabase);
len += json_printf(&out, ", colsBrowsePlaylistsDetail: %s", mympd_state.colsBrowsePlaylistsDetail);
len += json_printf(&out, ", colsBrowseFilesystem: %s", mympd_state.colsBrowseFilesystem);
len += json_printf(&out, "}}");
CHECK_RETURN_LEN();
return len;
@ -1295,7 +1461,7 @@ int mympd_put_outputs(char *buffer) {
len = json_printf(&out, "{type: outputs, data: {outputs: [");
nr = 0;
while ((output = mpd_recv_output(mpd.conn)) != NULL) {
if (nr ++)
if (nr++)
len += json_printf(&out, ",");
len += json_printf(&out,"{id: %d, name: %Q, state: %d}",
mpd_output_get_id(output),
@ -1326,7 +1492,11 @@ int replacechar(char *str, char orig, char rep) {
int mympd_get_cover(const char *uri, char *cover, int cover_len) {
char *path = strdup(uri);
int len;
if (strncasecmp("http:", path, 5) == 0 || strncasecmp("https:", path, 6) == 0) {
if (!config.coverimage) {
len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png");
}
else if (strncasecmp("http:", path, 5) == 0 || strncasecmp("https:", path, 6) == 0) {
if(strlen(path) > 8) {
if (strncasecmp("http:", path, 5) == 0)
path += 7;
@ -1335,24 +1505,25 @@ int mympd_get_cover(const char *uri, char *cover, int cover_len) {
replacechar(path, '/', '_');
replacechar(path, '.', '_');
snprintf(cover, cover_len, "%s/pics/%s.png", SRC_PATH, path);
if (access(cover, F_OK ) == -1 ) {
if (access(cover, F_OK ) == -1 )
len = snprintf(cover, cover_len, "/assets/coverimage-httpstream.png");
} else {
else
len = snprintf(cover, cover_len, "/pics/%s.png", path);
}
} else {
} else
len = snprintf(cover, cover_len, "/assets/coverimage-httpstream.png");
}
}
else {
dirname(path);
snprintf(cover, cover_len, "%s/library/%s/%s", SRC_PATH, path, config.coverimage);
if (access(cover, F_OK ) == -1 ) {
if (mpd.feat_library) {
dirname(path);
snprintf(cover, cover_len, "%s/library/%s/%s", SRC_PATH, path, config.coverimagename);
if (access(cover, F_OK ) == -1 )
len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png");
else
len = snprintf(cover, cover_len, "/library/%s/%s", path, config.coverimagename);
} else
len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png");
} else {
len = snprintf(cover, cover_len, "/library/%s/%s", path, config.coverimage);
}
}
free(path);
return len;
}
@ -1370,19 +1541,15 @@ int mympd_put_current_song(char *buffer) {
mympd_get_cover(mpd_song_get_uri(song), cover, 500);
len = json_printf(&out, "{type: song_change, data: {pos: %d, title: %Q, "
"artist: %Q, album: %Q, uri: %Q, currentSongId: %d, albumartist: %Q, "
"duration: %d, cover: %Q",
len = json_printf(&out, "{type: song_change, data: {pos: %d, currentSongId: %d, cover: %Q, ",
mpd_song_get_pos(song),
mympd_get_tag(song, MPD_TAG_TITLE),
mympd_get_tag(song, MPD_TAG_ARTIST),
mympd_get_tag(song, MPD_TAG_ALBUM),
mpd_song_get_uri(song),
mpd.song_id,
mympd_get_tag(song, MPD_TAG_ALBUM_ARTIST),
mpd_song_get_duration(song),
cover
);
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
mpd_response_finish(mpd.conn);
@ -1412,26 +1579,16 @@ int mympd_put_songdetails(char *buffer, char *uri) {
char cover[500];
len = json_printf(&out, "{type: song_details, data: {");
mpd_send_list_all_meta(mpd.conn, uri);
if (!mpd_send_list_all_meta(mpd.conn, uri))
RETURN_ERROR_AND_RECOVER("mpd_send_list_all_meta");
if ((entity = mpd_recv_entity(mpd.conn)) != NULL) {
song = mpd_entity_get_song(entity);
mympd_get_cover(uri, cover, 500);
len += json_printf(&out, "duration: %d, artist: %Q, album: %Q, title: %Q, albumartist: %Q, cover: %Q, uri: %Q, "
"genre: %Q, track: %Q, date: %Q, composer: %Q, performer: %Q",
mpd_song_get_duration(song),
mympd_get_tag(song, MPD_TAG_ARTIST),
mympd_get_tag(song, MPD_TAG_ALBUM),
mympd_get_tag(song, MPD_TAG_TITLE),
mympd_get_tag(song, MPD_TAG_ALBUM_ARTIST),
cover,
uri,
mympd_get_tag(song, MPD_TAG_GENRE),
mympd_get_tag(song, MPD_TAG_TRACK),
mympd_get_tag(song, MPD_TAG_DATE),
mympd_get_tag(song, MPD_TAG_COMPOSER),
mympd_get_tag(song, MPD_TAG_PERFORMER)
);
len += json_printf(&out, "cover: %Q, ", cover);
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
mpd_entity_free(entity);
}
mpd_response_finish(mpd.conn);
@ -1477,18 +1634,18 @@ int mympd_put_queue(char *buffer, unsigned int offset, unsigned *queue_version,
song = mpd_entity_get_song(entity);
drtn = mpd_song_get_duration(song);
totalTime += drtn;
entity_count ++;
if (entities_returned ++)
entity_count++;
if (entities_returned++)
len += json_printf(&out, ",");
len += json_printf(&out, "{id: %d, pos: %d, duration: %d, artist: %Q, album: %Q, title: %Q, uri: %Q}",
len += json_printf(&out, "{id: %d, Pos: %d, ",
mpd_song_get_id(song),
mpd_song_get_pos(song),
mpd_song_get_duration(song),
mympd_get_tag(song, MPD_TAG_ARTIST),
mympd_get_tag(song, MPD_TAG_ALBUM),
mympd_get_tag(song, MPD_TAG_TITLE),
mpd_song_get_uri(song)
mpd_song_get_pos(song)
);
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
len += json_printf(&out, "}");
}
mpd_entity_free(entity);
}
@ -1528,11 +1685,11 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter
while ((entity = mpd_recv_entity(mpd.conn)) != NULL) {
const struct mpd_song *song;
const struct mpd_directory *dir;
entity_count ++;
entity_count++;
if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) {
switch (mpd_entity_get_type(entity)) {
case MPD_ENTITY_TYPE_UNKNOWN:
entity_count --;
entity_count--;
break;
case MPD_ENTITY_TYPE_SONG:
song = mpd_entity_get_song(entity);
@ -1540,18 +1697,17 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter
if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, entityName, 1) == 0 ||
(strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0 )
) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ",");
len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q}",
mpd_song_get_uri(song),
mympd_get_tag(song, MPD_TAG_ALBUM),
mympd_get_tag(song, MPD_TAG_ARTIST),
mpd_song_get_duration(song),
entityName,
entityName
);
len += json_printf(&out, "{Type: song, ");
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
len += json_printf(&out, "}");
} else {
entity_count --;
entity_count--;
}
break;
@ -1561,21 +1717,21 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter
char *dirName = strrchr(entityName, '/');
if (dirName != NULL)
dirName ++;
dirName++;
else
dirName = strdup(entityName);
if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, dirName, 1) == 0 ||
(strncmp(filter, "0", 1) == 0 && isalpha(*dirName) == 0 )
) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ",");
len += json_printf(&out, "{type: dir, uri: %Q, name: %Q}",
len += json_printf(&out, "{Type: dir, uri: %Q, name: %Q}",
entityName,
dirName
);
} else {
entity_count --;
entity_count--;
}
break;
@ -1584,27 +1740,27 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter
entityName = mpd_playlist_get_path(pl);
char *plName = strrchr(entityName, '/');
if (plName != NULL) {
plName ++;
plName++;
} else {
plName = strdup(entityName);
}
if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, plName, 1) == 0 ||
(strncmp(filter, "0", 1) == 0 && isalpha(*plName) == 0 )
) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ",");
snprintf(smartpls_file, 400, "%s/smartpls/%s", config.varlibdir, plName);
if (access(smartpls_file, F_OK ) != -1)
smartpls = true;
else
smartpls = false;
len += json_printf(&out, "{type: %Q, uri: %Q, name: %Q}",
len += json_printf(&out, "{Type: %Q, uri: %Q, name: %Q}",
(smartpls == true ? "smartpls" : "plist"),
entityName,
plName
);
} else {
entity_count --;
entity_count--;
}
break;
}
@ -1649,22 +1805,22 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *
len = json_printf(&out, "{type: listDBtags, data: [");
while ((pair = mpd_recv_pair_tag(mpd.conn, mpd_tag_name_parse(mpdtagtype))) != NULL) {
entity_count ++;
entity_count++;
if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) {
if (strcmp(pair->value, "") == 0) {
entity_count --;
entity_count--;
}
else if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, pair->value, 1) == 0 ||
(strncmp(filter, "0", 1) == 0 && isalpha(*pair->value) == 0 )
) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ", ");
len += json_printf(&out, "{type: %Q, value: %Q}",
mpdtagtype,
pair->value
);
} else {
entity_count --;
entity_count--;
}
}
mpd_return_pair(mpd.conn, pair);
@ -1709,25 +1865,22 @@ int mympd_put_songs_in_album(char *buffer, char *album, char *search, char *tag)
len = json_printf(&out, "{type: listTitles, data: [");
while ((song = mpd_recv_song(mpd.conn)) != NULL) {
entity_count ++;
entity_count++;
if (entity_count <= config.max_elements_per_page) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ", ");
else {
mympd_get_cover(mpd_song_get_uri(song), cover, 500);
albumartist = strdup(mympd_get_tag(song, MPD_TAG_ALBUM_ARTIST));
}
len += json_printf(&out, "{type: song, uri: %Q, duration: %d, title: %Q, track: %Q}",
mpd_song_get_uri(song),
mpd_song_get_duration(song),
mympd_get_tag(song, MPD_TAG_TITLE),
mympd_get_tag(song, MPD_TAG_TRACK)
);
len += json_printf(&out, "{Type: song, ");
PUT_SONG_TAGS();
len += json_printf(&out, "}");
}
mpd_song_free(song);
}
len += json_printf(&out, "], totalEntities: %d, returnedEntities: %d, album: %Q, search: %Q, tag: %Q, cover: %Q, albumartist: %Q}",
len += json_printf(&out, "], totalEntities: %d, returnedEntities: %d, Album: %Q, search: %Q, tag: %Q, cover: %Q, AlbumArtist: %Q}",
entity_count,
entities_returned,
album,
@ -1759,27 +1912,27 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) {
len = json_printf(&out, "{type: playlists, data: [");
while ((pl = mpd_recv_playlist(mpd.conn)) != NULL) {
entity_count ++;
entity_count++;
if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) {
plpath = mpd_playlist_get_path(pl);
if (strncmp(filter,"-",1) == 0 || strncasecmp(filter, plpath, 1) == 0 ||
(strncmp(filter, "0", 1) == 0 && isalpha(*plpath) == 0 )
) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ", ");
snprintf(smartpls_file, 400, "%s/smartpls/%s", config.varlibdir, plpath);
if (access(smartpls_file, F_OK ) != -1)
smartpls = true;
else
smartpls = false;
len += json_printf(&out, "{type: %Q, uri: %Q, name: %Q, last_modified: %d}",
len += json_printf(&out, "{Type: %Q, uri: %Q, name: %Q, last_modified: %d}",
(smartpls == true ? "smartpls" : "plist"),
plpath,
plpath,
mpd_playlist_get_last_modified(pl)
);
} else {
entity_count --;
entity_count--;
}
}
mpd_playlist_free(pl);
@ -1812,25 +1965,24 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *
while ((entity = mpd_recv_entity(mpd.conn)) != NULL) {
const struct mpd_song *song;
entity_count ++;
entity_count++;
if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) {
song = mpd_entity_get_song(entity);
entityName = mympd_get_tag(song, MPD_TAG_TITLE);
if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, entityName, 1) == 0 ||
(strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0 )
) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ",");
len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q}",
mpd_song_get_uri(song),
mympd_get_tag(song, MPD_TAG_ALBUM),
mympd_get_tag(song, MPD_TAG_ARTIST),
mpd_song_get_duration(song),
entityName,
entityName
);
len += json_printf(&out, "{Type: song, ");
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
len += json_printf(&out, ", Pos: %d", entity_count);
len += json_printf(&out, "}");
} else {
entity_count --;
entity_count--;
}
}
mpd_entity_free(entity);
@ -1876,18 +2028,16 @@ int mympd_search(char *buffer, char *searchstr, char *filter, char *plist, unsig
if (strcmp(plist, "") == 0) {
while ((song = mpd_recv_song(mpd.conn)) != NULL) {
entity_count ++;
entity_count++;
if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ", ");
len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q}",
mpd_song_get_uri(song),
mympd_get_tag(song, MPD_TAG_ALBUM),
mympd_get_tag(song, MPD_TAG_ARTIST),
mpd_song_get_duration(song),
mympd_get_tag(song, MPD_TAG_TITLE),
mympd_get_tag(song, MPD_TAG_TITLE)
);
len += json_printf(&out, "{Type: song, ");
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
len += json_printf(&out, "}");
}
mpd_song_free(song);
}
@ -1921,12 +2071,12 @@ int mympd_queue_crop(char *buffer) {
len = json_printf(&out, "{type: error, data: %Q}", "A playlist longer than 1 song in length is required to crop.");
}
else if (mpd_status_get_state(status) == MPD_STATE_PLAY || mpd_status_get_state(status) == MPD_STATE_PAUSE) {
playing_song_pos ++;
playing_song_pos++;
if (playing_song_pos < length)
mpd_run_delete_range(mpd.conn, playing_song_pos, -1);
playing_song_pos --;
playing_song_pos--;
if (playing_song_pos > 0 )
mpd_run_delete_range(mpd.conn, 0, playing_song_pos --);
mpd_run_delete_range(mpd.conn, 0, playing_song_pos--);
len = json_printf(&out, "{type: result, data: ok}");
} else {
len = json_printf(&out, "{type: error, data: %Q}", "You need to be playing to crop the playlist");
@ -1964,18 +2114,19 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char
len = json_printf(&out, "{type: queuesearch, data: [");
while ((song = mpd_recv_song(mpd.conn)) != NULL) {
entity_count ++;
entity_count++;
if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) {
if (entities_returned ++)
if (entities_returned++)
len += json_printf(&out, ", ");
len += json_printf(&out, "{type: song, id: %d, pos: %d, album: %Q, artist: %Q, duration: %d, title: %Q}",
len += json_printf(&out, "{type: song, id: %d, pos: %d, ",
mpd_song_get_id(song),
mpd_song_get_pos(song),
mympd_get_tag(song, MPD_TAG_ALBUM),
mympd_get_tag(song, MPD_TAG_ARTIST),
mpd_song_get_duration(song),
mympd_get_tag(song, MPD_TAG_TITLE)
mpd_song_get_pos(song)
);
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
len += json_printf(&out, "}");
mpd_song_free(song);
}
}
@ -2148,6 +2299,7 @@ int mympd_smartpls_update_all() {
printf("Can't parse file %s\n", filename);
}
free(smartpltype);
free(content);
}
closedir (dir);
} else {
@ -2194,12 +2346,12 @@ int mympd_smartpls_update_search(char *playlist, char *tag, char *searchstr) {
int mympd_smartpls_update(char *playlist, char *sticker, int maxentries) {
struct mpd_pair *pair;
char *uri = NULL;
char *name;
char *p_value;
const char *p_value;
char *crap;
long value;
long value_max = 0;
long i = 0;
unsigned int j;
if (!mpd_send_sticker_find(mpd.conn, "song", "", sticker)) {
LOG_ERROR_AND_RECOVER("mpd_send_sticker_find");
@ -2214,13 +2366,14 @@ int mympd_smartpls_update(char *playlist, char *sticker, int maxentries) {
uri = strdup(pair->value);
}
else if (strcmp(pair->name, "sticker") == 0) {
name = strtok(strdup(pair->value), "=");
p_value = strtok(NULL, "=");
value = strtol(p_value, &crap, 10);
if (value > 1)
list_push(&add_list, uri, value);
if (value > value_max)
value_max = value;
p_value = mpd_parse_sticker(pair->value, &j);
if (p_value != NULL) {
value = strtol(p_value, &crap, 10);
if (value > 1)
list_push(&add_list, uri, value);
if (value > value_max)
value_max = value;
}
}
mpd_return_pair(mpd.conn, pair);
}

View File

@ -29,17 +29,15 @@
#include "list.h"
#define RETURN_ERROR_AND_RECOVER(X) do { \
printf("MPD X: %s\n", mpd_connection_get_error_message(mpd.conn)); \
len = json_printf(&out, "{ type:error, data : %Q }", \
mpd_connection_get_error_message(mpd.conn) \
); \
printf("MPD %s: %s\n", X, mpd_connection_get_error_message(mpd.conn)); \
len = json_printf(&out, "{ type:error, data : %Q }", mpd_connection_get_error_message(mpd.conn)); \
if (!mpd_connection_clear_error(mpd.conn)) \
mpd.conn_state = MPD_FAILURE; \
return len; \
} while (0)
#define LOG_ERROR_AND_RECOVER(X) do { \
printf("MPD X: %s\n", mpd_connection_get_error_message(mpd.conn)); \
printf("MPD %s: %s\n", X, mpd_connection_get_error_message(mpd.conn)); \
if (!mpd_connection_clear_error(mpd.conn)) \
mpd.conn_state = MPD_FAILURE; \
} while (0)
@ -50,6 +48,23 @@
return len; \
} while (0)
#define PUT_SONG_TAGS() do { \
struct node *current = mympd_tags.list; \
int tagnr = 0; \
while (current != NULL) { \
if (tagnr ++) \
len += json_printf(&out, ","); \
len += json_printf(&out, "%Q: %Q", current->data, mympd_get_tag(song, mpd_tag_name_parse(current->data))); \
current = current->next; \
} \
len += json_printf(&out, ", Duration: %d, uri: %Q", mpd_song_get_duration(song), mpd_song_get_uri(song)); \
} while (0)
#define PUT_MIN_SONG_TAGS() do { \
len += json_printf(&out, "Title: %Q, Duration: %d, uri: %Q", mympd_get_tag(song, MPD_TAG_TITLE), mpd_song_get_duration(song), mpd_song_get_uri(song)); \
} while (0)
#define MAX_SIZE 2048 * 400
#define MAX_ELEMENTS_PER_PAGE 400
@ -109,6 +124,8 @@
X(MPD_API_MESSAGE_SEND) \
X(MPD_API_WELCOME) \
X(MPD_API_LIKE) \
X(MPD_API_SYSCMD) \
X(MPD_API_COLS_SAVE) \
X(MPD_API_UNKNOWN)
enum mpd_cmd_ids {
@ -145,10 +162,17 @@ struct t_mpd {
const unsigned* protocol;
// Supported tags
bool feat_sticker;
bool feat_playlists;
bool feat_tags;
bool feat_library;
} mpd;
struct list mpd_tags;
struct list mympd_tags;
struct list mympd_searchtags;
struct list mympd_browsetags;
struct list syscmds;
typedef struct {
long mpdport;
@ -160,14 +184,21 @@ typedef struct {
const char* sslcert;
const char* sslkey;
const char* user;
long streamport;
const char* coverimage;
bool coverimage;
const char* coverimagename;
bool stickers;
bool mixramp;
const char* taglist;
const char* searchtaglist;
const char* browsetaglist;
bool smartpls;
const char* varlibdir;
long max_elements_per_page;
const char* etcdir;
unsigned long max_elements_per_page;
bool syscmds;
bool localplayer;
long streamport;
const char* streamurl;
} t_config;
t_config config;
@ -185,6 +216,11 @@ typedef struct {
int jukeboxMode;
const char* jukeboxPlaylist;
int jukeboxQueueLength;
char* colsQueue;
char* colsSearch;
char* colsBrowseDatabase;
char* colsBrowsePlaylistsDetail;
char* colsBrowseFilesystem;
} t_mympd_state;
t_mympd_state mympd_state;
@ -206,7 +242,8 @@ void mympd_last_played_song_id(int song_id);
void mympd_get_sticker(const char *uri, t_sticker *sticker);
void mympd_jukebox();
bool mympd_state_get(char *name, char *value);
bool mympd_state_set(char *name, char *value);
bool mympd_state_set(const char *name, const char *value);
int mympd_syscmd(char *buffer, char *cmd, int order);
int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange);
int mympd_smartpls_put(char *buffer, char *playlist);
int mympd_smartpls_update_all();
@ -215,6 +252,7 @@ int mympd_smartpls_update(char *sticker, char *playlist, int maxentries);
int mympd_smartpls_update_newest(char *playlist, int timerange);
int mympd_smartpls_update_search(char *playlist, char *tag, char *searchstr);
int mympd_get_updatedb_state(char *buffer);
void mympd_get_song_uri_from_song_id(int song_id, char *uri);
int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length);
int mympd_put_outputs(char *buffer);
int mympd_put_current_song(char *buffer);

View File

@ -29,6 +29,7 @@
#include <sys/time.h>
#include <pwd.h>
#include <grp.h>
#include <libgen.h>
#include "../dist/src/mongoose/mongoose.h"
#include "../dist/src/frozen/frozen.h"
@ -130,12 +131,8 @@ static int inihandler(void* user, const char* section, const char* name, const c
if (MATCH("mpdhost"))
p_config->mpdhost = strdup(value);
else if (MATCH("mpdhost"))
p_config->mpdhost = strdup(value);
else if (MATCH("mpdport"))
p_config->mpdport = strtol(value, &crap, 10);
else if (MATCH("mpdhost"))
p_config->mpdhost = strdup(value);
else if (MATCH("mpdpass"))
p_config->mpdpass = strdup(value);
else if (MATCH("webport"))
@ -154,7 +151,12 @@ static int inihandler(void* user, const char* section, const char* name, const c
else if (MATCH("streamport"))
p_config->streamport = strtol(value, &crap, 10);
else if (MATCH("coverimage"))
p_config->coverimage = strdup(value);
if (strcmp(value, "true") == 0)
p_config->coverimage = true;
else
p_config->coverimage = false;
else if (MATCH("coverimagename"))
p_config->coverimagename = strdup(value);
else if (MATCH("varlibdir"))
p_config->varlibdir = strdup(value);
else if (MATCH("stickers"))
@ -174,6 +176,10 @@ static int inihandler(void* user, const char* section, const char* name, const c
p_config->mixramp = false;
else if (MATCH("taglist"))
p_config->taglist = strdup(value);
else if (MATCH("searchtaglist"))
p_config->searchtaglist = strdup(value);
else if (MATCH("browsetaglist"))
p_config->browsetaglist = strdup(value);
else if (MATCH("max_elements_per_page")) {
p_config->max_elements_per_page = strtol(value, &crap, 10);
if (p_config->max_elements_per_page > MAX_ELEMENTS_PER_PAGE) {
@ -181,12 +187,48 @@ static int inihandler(void* user, const char* section, const char* name, const c
p_config->max_elements_per_page = MAX_ELEMENTS_PER_PAGE;
}
}
else if (MATCH("syscmds"))
if (strcmp(value, "true") == 0)
p_config->syscmds = true;
else
p_config->syscmds = false;
else if (MATCH("localplayer"))
if (strcmp(value, "true") == 0)
p_config->localplayer = true;
else
p_config->localplayer = false;
else if (MATCH("streamurl"))
p_config->streamurl = strdup(value);
else
return 0; /* unknown section/name, error */
return 1;
}
void read_syscmds() {
DIR *dir;
struct dirent *ent;
char dirname[400];
char *cmd;
long order;
if (config.syscmds == true) {
snprintf(dirname, 400, "%s/syscmds", config.etcdir);
printf("Reading syscmds: %s\n", dirname);
if ((dir = opendir (dirname)) != NULL) {
while ((ent = readdir(dir)) != NULL) {
if (strncmp(ent->d_name, ".", 1) == 0)
continue;
order = strtol(ent->d_name, &cmd, 10);
if (strcmp(cmd, "") != 0)
list_push(&syscmds, strdup(cmd), order);
}
closedir(dir);
}
}
else
printf("Syscmds are disabled\n");
}
void read_statefiles() {
char *crap;
char value[400];
@ -224,7 +266,7 @@ void read_statefiles() {
if (mympd_state_get("jukeboxPlaylist", value))
mympd_state.jukeboxPlaylist = strdup(value);
else {
mympd_state.jukeboxPlaylist = "Database";
mympd_state.jukeboxPlaylist = strdup("Database");
mympd_state_set("jukeboxPlaylist", "Database");
}
@ -234,6 +276,41 @@ void read_statefiles() {
mympd_state.jukeboxQueueLength = 1;
mympd_state_set("jukeboxQueueLength", "1");
}
if (mympd_state_get("colsQueue", value))
mympd_state.colsQueue = strdup(value);
else {
mympd_state.colsQueue = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state_set("colsQueue", mympd_state.colsQueue);
}
if (mympd_state_get("colsSearch", value))
mympd_state.colsSearch = strdup(value);
else {
mympd_state.colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state_set("colsSearch", mympd_state.colsSearch);
}
if (mympd_state_get("colsBrowseDatabase", value))
mympd_state.colsBrowseDatabase = strdup(value);
else {
mympd_state.colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]");
mympd_state_set("colsBrowseDatabase", mympd_state.colsBrowseDatabase);
}
if (mympd_state_get("colsBrowsePlaylistsDetail", value))
mympd_state.colsBrowsePlaylistsDetail = strdup(value);
else {
mympd_state.colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state_set("colsBrowsePlaylistsDetail", mympd_state.colsBrowsePlaylistsDetail);
}
if (mympd_state_get("colsBrowseFilesystem", value))
mympd_state.colsBrowseFilesystem = strdup(value);
else {
mympd_state.colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state_set("colsBrowseFilesystem", mympd_state.colsBrowseFilesystem);
}
}
bool testdir(char *name, char *dirname) {
@ -266,19 +343,28 @@ int main(int argc, char **argv) {
config.sslport = "443";
config.sslcert = "/etc/mympd/ssl/server.pem";
config.sslkey = "/etc/mympd/ssl/server.key";
config.user = "nobody";
config.user = "mympd";
config.streamport = 8000;
config.coverimage = "folder.jpg";
config.streamurl = "";
config.coverimage = true;
config.coverimagename = "folder.jpg";
config.varlibdir = "/var/lib/mympd";
config.stickers = true;
config.mixramp = true;
config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer";
config.searchtaglist = "Artist,Album,AlbumArtist,Title,Genre,Composer,Performer";
config.browsetaglist = "Artist,Album,AlbumArtist,Genre,Composer,Performer";
config.smartpls = true;
config.max_elements_per_page = 100;
char *etcdir = strdup(argv[1]);
config.etcdir = dirname(etcdir);
config.syscmds = false;
config.localplayer = true;
mpd.timeout = 3000;
mpd.last_update_sticker_song_id = -1;
mpd.last_song_id = -1;
mpd.feat_library = false;
if (argc == 2) {
printf("Parsing config file: %s\n", argv[1]);
@ -366,6 +452,14 @@ int main(int argc, char **argv) {
if (!testdir("Document root", SRC_PATH))
return EXIT_FAILURE;
snprintf(testdirname, 400, "%s/library", SRC_PATH);
if (testdir("Link to mpd music_directory", testdirname)) {
mpd.feat_library = true;
printf("Enabling coverimage support\n");
}
else
printf("Disabling coverimage support\n");
snprintf(testdirname, 400, "%s/tmp", config.varlibdir);
if (!testdir("Temp dir", testdirname))
return EXIT_FAILURE;
@ -380,6 +474,10 @@ int main(int argc, char **argv) {
read_statefiles();
list_init(&syscmds);
read_syscmds();
list_sort_by_value(&syscmds, true);
list_init(&mpd_tags);
list_init(&mympd_tags);