1
0
mirror of https://github.com/SuperBFG7/ympd synced 2025-06-08 17:34:11 +00:00

Merge pull request #39 from jcorporation/devel

Merge devel v4.0.0 into master
This commit is contained in:
Jürgen Mang 2018-08-27 21:41:48 +02:00 committed by GitHub
commit 4b5181181e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1510 additions and 955 deletions

View File

@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.6)
project (mympd C) project (mympd C)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
set(CPACK_PACKAGE_VERSION_MAJOR "3") set(CPACK_PACKAGE_VERSION_MAJOR "4")
set(CPACK_PACKAGE_VERSION_MINOR "5") set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH "0") set(CPACK_PACKAGE_VERSION_PATCH "0")
if(CMAKE_BUILD_TYPE MATCHES RELEASE) if(CMAKE_BUILD_TYPE MATCHES RELEASE)
@ -48,9 +48,8 @@ install(FILES dist/htdocs/player.html DESTINATION share/${PROJECT_NAME}/htdocs/)
install(FILES dist/htdocs/sw.min.js DESTINATION share/${PROJECT_NAME}/htdocs/) install(FILES dist/htdocs/sw.min.js DESTINATION share/${PROJECT_NAME}/htdocs/)
install(FILES dist/htdocs/js/player.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) install(FILES dist/htdocs/js/player.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
install(FILES dist/htdocs/js/bootstrap-native-v4.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) install(FILES dist/htdocs/js/bootstrap-native-v4.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
install(FILES dist/htdocs/js/mpd.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) install(FILES dist/htdocs/js/mympd.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
install(FILES dist/htdocs/css/bootstrap.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/) install(FILES dist/htdocs/css/bootstrap.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/)
install(FILES dist/htdocs/css/mpd.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/) install(FILES dist/htdocs/css/mympd.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/)
install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs) install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs)
install(DIRECTORY DESTINATION /var/lib/${PROJECT_NAME}/) install(DIRECTORY DESTINATION /var/lib/${PROJECT_NAME}/)
install(FILES contrib/mympd.conf DESTINATION /etc/${PROJECT_NAME}/)

View File

@ -25,6 +25,8 @@ This fork provides a reworked ui based on Bootstrap 4, a modernized backend and
- Local coverart support - Local coverart support
- HTTP stream support - HTTP stream support
- Local playback of mpd http stream (html5 audio api) - Local playback of mpd http stream (html5 audio api)
- Play statistics and song voting (uses mpd stickers)
- Embedded Webserver (mongoose)
myMPD is work in progress. Bugreportes and feature requests are very welcome. myMPD is work in progress. Bugreportes and feature requests are very welcome.
- Issues and feature requests: https://github.com/jcorporation/myMPD/issues - Issues and feature requests: https://github.com/jcorporation/myMPD/issues
@ -61,7 +63,7 @@ Build Dependencies
Unix Build Instructions Unix Build Instructions
----------------------- -----------------------
1. Install dependencies: cmake, libmpdclient (dev) and OpenSSL (dev), java are available from all major distributions. 1. Install dependencies: cmake, libmpdclient (dev), OpenSSL (dev) and java are available from all major distributions.
2. Build and install: ```cd /path/to/src; ./mkrelease.sh```. 2. Build and install: ```cd /path/to/src; ./mkrelease.sh```.
3. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories (mkrelease.sh tries to do this for you). 3. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories (mkrelease.sh tries to do this for you).
4. Configure your mpd with http stream output to use the local player. 4. Configure your mpd with http stream output to use the local player.

View File

@ -1,6 +1,6 @@
.\" Manpage for myMPD. .\" Manpage for myMPD.
.\" Contact mail@jcgames.de to correct errors or typos. .\" Contact mail@jcgames.de to correct errors or typos.
.TH man 1 "06 Aug 2018" "3.5.0" "myMPD man page" .TH man 1 "22 Aug 2018" "4.0.0" "myMPD man page"
.SH NAME .SH NAME
myMPD \- Standalone MPD Web GUI written in C, utilizing Websockets and Bootstrap/JS myMPD \- Standalone MPD Web GUI written in C, utilizing Websockets and Bootstrap/JS
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -25,3 +25,9 @@ coverimage = folder.jpg
#myMPD statefile #myMPD statefile
statefile = /var/lib/mympd/mympd.state statefile = /var/lib/mympd/mympd.state
#Enable mixramp settings
mixramp = false
#Enable usage of mpd stickers for play statistics and voting
stickers = true

View File

@ -1 +0,0 @@
html{position:relative;min-height:100%}body{margin-bottom:60px}footer{position:absolute;bottom:0}body{padding-top:50px;padding-bottom:50px;background-color:#888}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}#counter{font-size:22px;margin-left:10px;min-width:50px}.card{min-height:350px}@media only screen and (max-width:576px){.header-logo{display:none!important}}.clickable{cursor:pointer}.tblnum,.tblaction{width:30px}.album-cover{background-size:cover;border:1px solid black;border-radius:5px;overflow:hidden;margin-bottom:20px;width:240px;height:240px;background-color:#eee}.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.eot);src:local('Material Icons'),local('MaterialIcons-Regular');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-small{font-size:16px}main{padding-top:20px}.color-darkgrey{color:#6c757d}.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}#progressBar{width:100%;margin-top:8px}#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}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!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}

1
dist/htdocs/css/mympd.min.css vendored Normal file
View File

@ -0,0 +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}.card{min-height:350px}@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;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.eot);src:local('Material Icons'),local('MaterialIcons-Regular');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-small{font-size:16px}.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}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%{width:0%}to{width:100%}}.updateDBprogressAnimate{animation-duration:2s;animation-name:changewidth;animation-iteration-count:infinite}

File diff suppressed because one or more lines are too long

View File

@ -5,11 +5,11 @@ $jscomp.iteratorPrototype=function(a){$jscomp.initSymbolIterator();a={next:a};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.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,e){if(b){c=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var d=a[e];d in c||(c[d]={});c=c[d]}a=a[a.length-1];e=c[a];b=b(e);b!=e&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}}; $jscomp.polyfill=function(a,b,c,e){if(b){c=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var d=a[e];d in c||(c[d]={});c=c[d]}a=a[a.length-1];e=c[a];b=b(e);b!=e&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};
$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 e="";a;)if(a&1&&(e+=b),a>>>=1)b+=b;return e}},"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 e="";a;)if(a&1&&(e+=b),a>>>=1)b+=b;return e}},"es6","es3");
var socket,last_song="",last_state,current_song={},playstate="",settings={},alertTimeout,deferredPrompt,dragEl,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/Any Tag/",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:"Artist",views:{Artist:{state:"0/-/",scrollPos:0},Album:{state:"0/-/",scrollPos:0}}}}},Search:{state:"0/Any Tag/",scrollPos:0}}, var socket,lastSong="",lastState,currentSong={},playstate="",settings={},alertTimeout,progressTimer,deferredPrompt,dragEl,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/Any Tag/",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:"Artist",views:{Artist:{state:"0/-/",scrollPos:0},Album:{state:"0/-/",scrollPos:0}}}}},Search:{state:"0/Any Tag/",
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; 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");domCache.volumePrct=document.getElementById("volumePrct");domCache.volumeControl=document.getElementById("volumeControl");domCache.volumeIcon=document.getElementById("volumeIcon");domCache.btnPlay=document.getElementById("btnPlay");domCache.btnPrev=document.getElementById("btnPrev");domCache.btnNext=document.getElementById("btnNext");domCache.progressBar=document.getElementById("progressBar");domCache.volumeBar=document.getElementById("volumeBar"); domCache.counter=document.getElementById("counter");domCache.volumePrct=document.getElementById("volumePrct");domCache.volumeControl=document.getElementById("volumeControl");domCache.volumeIcon=document.getElementById("volumeIcon");domCache.btnsPlay=document.getElementsByClassName("btnPlay");domCache.btnsPlayLen=domCache.btnsPlay.length;domCache.btnPrev=document.getElementById("btnPrev");domCache.btnNext=document.getElementById("btnNext");domCache.progressBar=document.getElementById("progressBar");
domCache.outputs=document.getElementById("outputs");domCache.btnAdd=document.getElementById("nav-add2homescreen");domCache.currentTrack=document.getElementById("currentTrack"); domCache.volumeBar=document.getElementById("volumeBar");domCache.outputs=document.getElementById("outputs");domCache.btnAdd=document.getElementById("nav-add2homescreen");domCache.currentTrack=document.getElementById("currentTrack");domCache.currentArtist=document.getElementById("currentArtist");domCache.currentAlbum=document.getElementById("currentAlbum");domCache.currentCover=document.getElementById("currentCover");domCache.btnVoteUp=document.getElementById("btnVoteUp");domCache.btnVoteDown=document.getElementById("btnVoteDown");
var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalSavequeue=new Modal(document.getElementById("modalSaveQueue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),modalAddToPlaylist=new Modal(document.getElementById("modalAddToPlaylist")),modalRenamePlaylist=new Modal(document.getElementById("modalRenamePlaylist")); var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalSavequeue=new Modal(document.getElementById("modalSaveQueue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),modalAddToPlaylist=new Modal(document.getElementById("modalAddToPlaylist")),modalRenamePlaylist=new Modal(document.getElementById("modalRenamePlaylist")),modalUpdateDB=new Modal(document.getElementById("modalUpdateDB"));
function appPrepare(a){if(app.current.app!=app.last.app||app.current.tab!=app.last.tab||app.current.view!=app.last.view){for(var b=0;b<domCache.navbarBottomBtnsLen;b++)domCache.navbarBottomBtns[b].classList.remove("active");document.getElementById("cardPlayback").classList.add("hide");document.getElementById("cardQueue").classList.add("hide");document.getElementById("cardBrowse").classList.add("hide");document.getElementById("cardSearch").classList.add("hide");for(b=0;b<domCache.panelHeadingBrowseLen;b++)domCache.panelHeadingBrowse[b].classList.remove("active"); function appPrepare(a){if(app.current.app!=app.last.app||app.current.tab!=app.last.tab||app.current.view!=app.last.view){for(var b=0;b<domCache.navbarBottomBtnsLen;b++)domCache.navbarBottomBtns[b].classList.remove("active");document.getElementById("cardPlayback").classList.add("hide");document.getElementById("cardQueue").classList.add("hide");document.getElementById("cardBrowse").classList.add("hide");document.getElementById("cardSearch").classList.add("hide");for(b=0;b<domCache.panelHeadingBrowseLen;b++)domCache.panelHeadingBrowse[b].classList.remove("active");
document.getElementById("cardBrowsePlaylists").classList.add("hide");document.getElementById("cardBrowseDatabase").classList.add("hide");document.getElementById("cardBrowseFilesystem").classList.add("hide");document.getElementById("card"+app.current.app).classList.remove("hide");document.getElementById("nav"+app.current.app).classList.add("active");void 0!=app.current.tab&&(document.getElementById("card"+app.current.app+app.current.tab).classList.remove("hide"),document.getElementById("card"+app.current.app+ document.getElementById("cardBrowsePlaylists").classList.add("hide");document.getElementById("cardBrowseDatabase").classList.add("hide");document.getElementById("cardBrowseFilesystem").classList.add("hide");document.getElementById("card"+app.current.app).classList.remove("hide");document.getElementById("nav"+app.current.app).classList.add("active");void 0!=app.current.tab&&(document.getElementById("card"+app.current.app+app.current.tab).classList.remove("hide"),document.getElementById("card"+app.current.app+
"Nav"+app.current.tab).classList.add("active"));scrollTo(a)}(a=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+(void 0==app.current.view?"":app.current.view)+"List"))&&a.classList.add("opacity05")} "Nav"+app.current.tab).classList.add("active"));scrollTo(a)}(a=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+(void 0==app.current.view?"":app.current.view)+"List"))&&a.classList.add("opacity05")}
@ -17,53 +17,55 @@ function appGoto(a,b,c,e){var d=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==e?app.apps[a].tabs[b].views[c].state:e)):a="/"+a+"/"+b+"!"+(void 0==e?app.apps[a].tabs[b].state:e)):a="/"+a+"!"+(void 0==e?app.apps[a].state:e);location.hash=a} (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==e?app.apps[a].tabs[b].views[c].state:e)):a="/"+a+"/"+b+"!"+(void 0==e?app.apps[a].tabs[b].state:e)):a="/"+a+"!"+(void 0==e?app.apps[a].state:e);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): 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); 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_GET_CURRENT_SONG"},songChange);else if("Queue"==app.current.app){var b=document.getElementById("searchqueuetag").getElementsByTagName("button");for(a=0;a<b.length;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"),document.getElementById("searchqueuetagdesc").innerText=b[a].innerText);getQueue()}else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view)sendAPI({cmd:"MPD_API_GET_PLAYLISTS", if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);else if("Queue"==app.current.app){var b=document.getElementById("searchqueuetag").getElementsByTagName("button"),c=b.length;for(a=0;a<c;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"),document.getElementById("searchqueuetagdesc").innerText=b[a].innerText);getQueue()}else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view)sendAPI({cmd:"MPD_API_GET_PLAYLISTS",
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_GET_PLAYLIST_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&&"Artist"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTS", 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_GET_PLAYLIST_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&&"Artist"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTS",
data:{offset:app.current.page,filter:app.current.filter}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){sendAPI({cmd:"MPD_API_GET_FILESYSTEM",data:{offset:app.current.page, data:{offset:app.current.page,filter:app.current.filter}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){sendAPI({cmd:"MPD_API_GET_FILESYSTEM",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"));b='<li class="breadcrumb-item"><a data-uri="">root</a></li>'; 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"));b='<li class="breadcrumb-item"><a data-uri="">root</a></li>';
var c=app.current.search.split("/"),e=c.length,d="";for(a=0;a<e;a++){if(e-1==a){b+='<li class="breadcrumb-item active">'+c[a]+"</li>";break}d+=c[a];b+='<li class="breadcrumb-item"><a data-uri="'+d+'">'+c[a]+"</a></li>";d+="/"}a=document.getElementById("BrowseBreadcrumb");a.innerHTML=b;b=a.getElementsByTagName("a");c=b.length;for(a=0;a<c;a++)b[a].addEventListener("click",function(){appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+this.getAttribute("data-uri"))},!1);doSetFilterLetter("BrowseFilesystemFilter")}else if("Search"== c=app.current.search.split("/");var e=c.length,d="";for(a=0;a<e;a++){if(e-1==a){b+='<li class="breadcrumb-item active">'+c[a]+"</li>";break}d+=c[a];b+='<li class="breadcrumb-item"><a data-uri="'+d+'">'+c[a]+"</a></li>";d+="/"}a=document.getElementById("BrowseBreadcrumb");a.innerHTML=b;b=a.getElementsByTagName("a");c=b.length;for(a=0;a<c;a++)b[a].addEventListener("click",function(){appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+this.getAttribute("data-uri"))},!1);doSetFilterLetter("BrowseFilesystemFilter")}else if("Search"==
app.current.app)for(document.getElementById("searchstr").focus(),app.last.app!=app.current.app&&""!=app.current.search&&(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML='<tr><td><span class="material-icons">search</span></td><td colspan="5">Searching...</td></tr>'),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML= app.current.app)for(document.getElementById("searchstr").focus(),app.last.app!=app.current.app&&""!=app.current.search&&(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML='<tr><td><span class="material-icons">search</span></td><td colspan="5">Searching...</td></tr>'),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML=
"",document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),document.getElementById("searchAddAllSongsBtn").setAttribute("disabled","disabled"),document.getElementById("panel-heading-search").innerText="",document.getElementById("SearchList").classList.remove("opacity05"),setPagination(0)),b=document.getElementById("searchtags").getElementsByTagName("button"),c=b.length,a=0;a<c;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"), "",document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),document.getElementById("searchAddAllSongsBtn").setAttribute("disabled","disabled"),document.getElementById("panel-heading-search").innerText="",document.getElementById("SearchList").classList.remove("opacity05"),setPagination(0)),b=document.getElementById("searchtags").getElementsByTagName("button"),c=b.length,a=0;a<c;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"),
document.getElementById("searchtagsdesc").innerText=b[a].innerText);else appGoto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else appGoto("Playback")} document.getElementById("searchtagsdesc").innerText=b[a].innerText);else appGoto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else appGoto("Playback")}
function appInit(){getSettings();webSocketConnect();domCache.volumeBar.value=0;domCache.volumeBar.addEventListener("change",function(a){sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:domCache.volumeBar.value}})},!1);domCache.progressBar.value=0;domCache.progressBar.addEventListener("change",function(a){current_song&&0<=current_song.currentSongId&&sendAPI({cmd:"MPD_API_SET_SEEK",data:{songid:current_song.currentSongId,seek:Math.ceil(domCache.progressBar.value/100*current_song.totalTime)}})},!1);document.getElementById("volumeIcon").parentNode.addEventListener("show.bs.dropdown", function appInit(){getSettings();sendAPI({cmd:"MPD_API_GET_STATE"},parseState);webSocketConnect();domCache.volumeBar.value=0;domCache.volumeBar.addEventListener("change",function(a){sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:domCache.volumeBar.value}})},!1);domCache.progressBar.value=0;domCache.progressBar.addEventListener("change",function(a){currentSong&&0<=currentSong.currentSongId&&sendAPI({cmd:"MPD_API_SET_SEEK",data:{songid:currentSong.currentSongId,seek:Math.ceil(domCache.progressBar.value/
function(){sendAPI({cmd:"MPD_API_GET_OUTPUTS"},parseOutputnames)});document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});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", 100*currentSong.totalTime)}})},!1);document.getElementById("volumeIcon").parentNode.addEventListener("show.bs.dropdown",function(){sendAPI({cmd:"MPD_API_GET_OUTPUTS"},parseOutputs)});document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});document.getElementById("modalUpdateDB").addEventListener("hidden.bs.modal",function(){document.getElementById("updateDBprogress").classList.remove("updateDBprogressAnimate")});document.getElementById("modalSaveQueue").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("addToPlaylistPlaylist").addEventListener("change",function(a){"New Playlist"==this.options[this.selectedIndex].text?(document.getElementById("addToPlaylistNewPlaylistDiv").classList.remove("hide"), 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("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.stopPropagation();a=JSON.parse(this.getAttribute("data-href").replace(/'/g, document.getElementById("inputMixrampdelay").classList.remove("is-invalid")});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");
'"'));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.querySelectorAll(".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(); 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.stopPropagation();a=JSON.parse(this.getAttribute("data-href").replace(/'/g,'"'));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);
sendAPI({cmd:"MPD_API_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_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseFilesystemList").addEventListener("click", 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_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){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"== function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!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")));
a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!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&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowsePlaylistsDetailList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist", 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&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowsePlaylistsAllList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),
decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseDatabaseArtistList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database","Album","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&&(a.preventDefault(),showMenu(a.target))},!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&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseDatabaseArtistList").addEventListener("click",function(a){"TD"==
a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!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&& a.target.nodeName&&appGoto("Browse","Database","Album","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&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseFilesystemAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==
("Add all to queue"==a.target.innerText?addAllFromSearch():"Add all to playlist"==a.target.innerText&&showAddToPlaylist("SEARCH"))},!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.innerText+"/"+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+ 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?addAllFromSearch():"Add all to playlist"==a.target.innerText&&showAddToPlaylist("SEARCH"))},!1);document.getElementById("searchtags").addEventListener("click",function(a){"BUTTON"==
"/"+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.innerText+"/"+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", a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,"0/"+a.target.innerText+"/"+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.innerText+
void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");window.addEventListener("hashchange",appRoute,!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){switch(a.which){case 37:clickPrev();break;case 39:clickNext();break;case 32:clickPlay();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", "/"+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);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus",
{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"); function(){sendAPI({cmd:"MPD_API_GET_STATE"},parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){switch(a.which){case 37:clickPrev();break;case 39:clickNext();break;case 32:clickPlay();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: ",
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")})} 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"== 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.querySelectorAll(".dragover"),d=c.length,f=0;f<d;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.querySelectorAll(".dragover"),d=c.length,f=0;f<d;f++)c[f].classList.remove("dragover");document.getElementById(a.dataTransfer.getData("Text"))&& b.nodeName&&b.classList.remove("dragover")},!1);b.addEventListener("dragover",function(a){a.preventDefault();for(var c=b.getElementsByClassName("dragover"),d=c.length,f=0;f<d;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"),d=c.length,f=0;f<d;f++)c[f].classList.remove("dragover");
document.getElementById(a.dataTransfer.getData("Text")).classList.remove("opacity05")},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();var e=c.target;"TD"==c.target.nodeName&&(e=c.target.parentNode);var d=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),f=e.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05");b.insertBefore(dragEl,e);c=b.querySelectorAll(".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 e=c.target;"TD"==c.target.nodeName&&(e=c.target.parentNode);var d=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),f=e.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05");
e=c.length;for(var g=0;g<e;g++)c[g].classList.remove("dragover");document.getElementById(a).classList.add("opacity05");"Queue"==app.current.app?sendAPI({cmd:"MPD_API_MOVE_TRACK",data:{from:d,to:f}}):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view&&playlistMoveTrack(d,f)},!1)} b.insertBefore(dragEl,e);c=b.getElementsByClassName("dragover");e=c.length;for(var g=0;g<e;g++)c[g].classList.remove("dragover");document.getElementById(a).classList.add("opacity05");"Queue"==app.current.app?sendAPI({cmd:"MPD_API_MOVE_TRACK",data:{from:d,to:f}}):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view&&playlistMoveTrack(d,f)},!1)}
function playlistMoveTrack(a,b){sendAPI({cmd:"MPD_API_PLAYLIST_MOVE_TRACK",data:{plist:app.current.search,from:a,to:b}});sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)} function playlistMoveTrack(a,b){sendAPI({cmd:"MPD_API_PLAYLIST_MOVE_TRACK",data:{plist:app.current.search,from:a,to:b}});sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)}
function webSocketConnect(){socket=new WebSocket(getWsUrl());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");modalConnectionError.hide();appRoute()},socket.onmessage=function(a){if(a.data!==last_state&&0!=a.data.length){try{var b=JSON.parse(a.data)}catch(c){console.log("Invalid JSON data received: "+a.data)}switch(b.type){case "state":parseState(b);break;case "disconnected":showNotification("myMPD lost connection to MPD","","","danger"); function webSocketConnect(){socket=new WebSocket(getWsUrl());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");modalConnectionError.hide();appRoute();sendAPI({cmd:"MPD_API_GET_STATE"},parseState)},socket.onmessage=function(a){if(a.data!==lastState&&0!=a.data.length){try{var b=JSON.parse(a.data)}catch(c){console.log("Invalid JSON data received: "+a.data)}switch(b.type){case "update_state":parseState(b);break;case "disconnected":showNotification("myMPD lost connection to MPD",
break;case "update_queue":"Queue"===app.current.app&&getQueue();break;case "song_change":songChange(b);break;case "error":showNotification(b.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");modalConnectionError.show();setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("Error: "+a)}} "","","danger");break;case "update_queue":"Queue"===app.current.app&&getQueue();sendAPI({cmd:"MPD_API_GET_STATE"},parseState);break;case "update_options":getSettings();break;case "update_outputs":sendAPI({cmd:"MPD_API_GET_OUTPUTS"},parseOutputs);break;case "update_started":updateDBstarted();break;case "update_database":case "update_finished":updateDBfinished(b.type);break;case "error":showNotification(b.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");modalConnectionError.show();
function getWsUrl(){var a=document.URL;if("https"==a.substring(0,5)){var b="wss://";a=a.substr(8)}else b="ws://","http"==a.substring(0,4)&&(a=a.substr(7));a=a.split("#");var c=/\/$/.test(a[0])?"":"/";return b+a[0]+c+"ws"} setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("Error: "+a)}}function getWsUrl(){var a=document.URL;if("https"==a.substring(0,5)){var b="wss://";a=a.substr(8)}else b="ws://","http"==a.substring(0,4)&&(a=a.substr(7));a=a.split("#");var c=/\/$/.test(a[0])?"":"/";return b+a[0]+c+"ws"}
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* 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.mympd_version;document.getElementById("mpdVersion").innerText=a.data.mpd_version}function toggleBtn(a,b){if(a=document.getElementById(a))void 0==b&&(b=a.classList.contains("active")?0:1),1==b?(a.classList.add("active"),a.setAttribute("aria-pressed","true")):(a.classList.remove("active"),a.setAttribute("aria-pressed","false"))} 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?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"), 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"); 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);settings=a.data;settings.mpdstream="http://";settings.mpdstream= 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);b=document.getElementsByClassName("stickers");for(var c=
"127.0.0.1"==settings.mpdhost||"localhost"==settings.mpdhost?settings.mpdstream+window.location.hostname:settings.mpdstream+settings.mpdhost;settings.mpdstream+=":"+settings.streamport+"/"}function getSettings(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings)} b.length,e=1==a.data.stickers?"":"none",d=0;d<c;d++)b[d].style.display=e;1==a.data.mixramp?document.getElementsByClassName("mixramp")[0].style.display="":document.getElementsByClassName("mixramp")[0].style.display="none";settings=a.data;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+"/"}
function parseOutputnames(a){for(var b="",c=a.data.outputs.length,e=0;e<c;e++)b+='<button data-output-id="'+a.data.outputs[e].id+'" class="btn btn-secondary btn-block',1==a.data.outputs[e].state&&(b+=" active"),b+='"><span class="material-icons float-left">volume_up</span> '+a.data.outputs[e].name+"</button>";domCache.outputs.innerHTML=b} function getSettings(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings)}function parseOutputs(a){for(var b="",c=a.data.outputs.length,e=0;e<c;e++)b+='<button data-output-id="'+a.data.outputs[e].id+'" class="btn btn-secondary btn-block',1==a.data.outputs[e].state&&(b+=" active"),b+='"><span class="material-icons float-left">volume_up</span> '+a.data.outputs[e].name+"</button>";domCache.outputs.innerHTML=b}
function parseState(a){if(JSON.stringify(a)!==JSON.stringify(last_state)){1==a.data.state?(domCache.btnPlay.innerText="play_arrow",playstate="stop"):2==a.data.state?(domCache.btnPlay.innerText="pause",playstate="play"):(domCache.btnPlay.innerText="play_arrow",playstate="pause");-1==a.data.nextsongpos?domCache.btnNext.setAttribute("disabled","disabled"):domCache.btnNext.removeAttribute("disabled");0>=a.data.songpos?domCache.btnPrev.setAttribute("disabled","disabled"):domCache.btnPrev.removeAttribute("disabled"); function setCounter(a,b,c){currentSong.totalTime=b;currentSong.elapsedTime=c;currentSong.currentSongId=a;var e=Math.floor(b/60),d=b-60*e,f=Math.floor(c/60),g=c-60*f;domCache.progressBar.value=Math.floor(100*c/b);b=f+":"+(10>g?"0":"")+g+" / "+e+":"+(10>d?"0":"")+d;domCache.counter.innerText=b;lastState&&(c=document.getElementById("queueTrackId"+lastState.data.currentSongId))&&(e=c.getElementsByTagName("td"),e[4].innerText=c.getAttribute("data-duration"),e[0].classList.remove("material-icons"),e[0].innerText=
0==a.data.queue_length?domCache.btnPlay.setAttribute("disabled","disabled"):domCache.btnPlay.removeAttribute("disabled");-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;current_song.totalTime= c.getAttribute("data-songpos"),c.classList.remove("font-weight-bold"));if(c=document.getElementById("queueTrackId"+a))e=c.getElementsByTagName("td"),e[4].innerText=b,e[0].classList.add("material-icons"),e[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))}
a.data.totalTime;current_song.currentSongId=a.data.currentsongid;var b=Math.floor(a.data.totalTime/60),c=a.data.totalTime-60*b,e=Math.floor(a.data.elapsedTime/60),d=a.data.elapsedTime-60*e;domCache.progressBar.value=Math.floor(100*a.data.elapsedTime/a.data.totalTime);b=e+":"+(10>d?"0":"")+d+" / "+b+":"+(10>c?"0":"")+c;domCache.counter.innerText=b;last_state&&(c=document.getElementById("queueTrackId"+last_state.data.currentsongid))&&(e=c.getElementsByTagName("td"),e[4].innerText=c.getAttribute("data-duration"), 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?domCache.btnNext.setAttribute("disabled","disabled"):domCache.btnNext.removeAttribute("disabled");
e[0].classList.remove("material-icons"),e[0].innerText=c.getAttribute("data-songpos"),c.classList.remove("font-weight-bold"));if(c=document.getElementById("queueTrackId"+a.data.currentsongid))e=c.getElementsByTagName("td"),e[4].innerText=b,e[0].classList.add("material-icons"),e[0].innerText="play_arrow",c.classList.add("font-weight-bold");void 0!=last_state&&a.data.queue_version==last_state.data.queue_version||sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);"-1"==a.data.songpos&&(domCache.currentTrack.innerText= 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");-1==a.data.volume?(domCache.volumePrct.innerText="Volumecontrol disabled",domCache.volumeControl.classList.add("hide")):(domCache.volumeControl.classList.remove("hide"),domCache.volumePrct.innerText=
"Not playing",document.getElementById("currentAlbum").innerText="",document.getElementById("currentArtist").innerText="",document.getElementById("currentCover").style.backgroundImage="");last_state=a}}function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH_QUEUE",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue)} 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;setCounter(a.data.currentSongId,a.data.totalTime,a.data.elapsedTime);sendAPI({cmd:"MPD_API_GET_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 parseQueue(a){if("Queue"===app.current.app){0<a.totalTime?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs \u2013 "+beautifyDuration(a.totalTime):0<a.totalEntities?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs":document.getElementById("panel-heading-queue").innerText="";var b=a.data.length,c=document.getElementById(app.current.app+"List");c.setAttribute("data-version",a.queue_version);c=c.getElementsByTagName("tbody")[0]; function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH_QUEUE",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue)}
for(var e=c.getElementsByTagName("tr"),d=0;d<b;d++)if(!e[d]||e[d].getAttribute("data-trackid")!=a.data[d].id||e[d].getAttribute("data-songpos")!=a.data[d].pos+1){var f=Math.floor(a.data[d].duration/60),g=a.data[d].duration-60*f;f=f+":"+(10>g?"0":"")+g;g=document.createElement("tr");g.setAttribute("draggable","true");g.setAttribute("data-trackid",a.data[d].id);g.setAttribute("id","queueTrackId"+a.data[d].id);g.setAttribute("data-songpos",a.data[d].pos+1);g.setAttribute("data-duration",f);g.setAttribute("data-uri", function parseQueue(a){if("Queue"===app.current.app){0<a.totalTime&&a.totalEntities<=settings.maxElementsPerPage?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs \u2013 "+beautifyDuration(a.totalTime):0<a.totalEntities?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs":document.getElementById("panel-heading-queue").innerText="";var b=a.data.length,c=document.getElementById(app.current.app+"List");c.setAttribute("data-version",a.queueVersion);
a.data[d].uri);g.innerHTML="<td>"+(a.data[d].pos+1)+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(g):c.append(g)}for(d=e.length-1;d>=b;d--)e[d].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&& c=c.getElementsByTagName("tbody")[0];for(var e=c.getElementsByTagName("tr"),d=0;d<b;d++)if(!e[d]||e[d].getAttribute("data-trackid")!=a.data[d].id||e[d].getAttribute("data-songpos")!=a.data[d].pos+1){var f=Math.floor(a.data[d].duration/60),g=a.data[d].duration-60*f;f=f+":"+(10>g?"0":"")+g;g=document.createElement("tr");g.setAttribute("draggable","true");g.setAttribute("data-trackid",a.data[d].id);g.setAttribute("id","queueTrackId"+a.data[d].id);g.setAttribute("data-songpos",a.data[d].pos+1);g.setAttribute("data-duration",
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")}} f);g.setAttribute("data-uri",a.data[d].uri);g.innerHTML="<td>"+(a.data[d].pos+1)+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(g):c.append(g)}for(d=e.length-1;d>=b;d--)e[d].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 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],e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].uri);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-type",a.data[d].type);g.setAttribute("data-uri",f);g.setAttribute("data-name", 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],e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].uri);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-type",a.data[d].type);g.setAttribute("data-uri",f);g.setAttribute("data-name",
a.data[d].name);switch(a.data[d].type){case "dir":g.innerHTML='<td><span class="material-icons">folder_open</span></td><td colspan="4">'+a.data[d].name+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';break;case "song":f=Math.floor(a.data[d].duration/60);var h=a.data[d].duration-60*f;g.innerHTML='<td><span class="material-icons">music_note</span></td><td>'+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>h?"0":"")+h+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>'; a.data[d].name);switch(a.data[d].type){case "dir":g.innerHTML='<td><span class="material-icons">folder_open</span></td><td colspan="4">'+a.data[d].name+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';break;case "song":f=Math.floor(a.data[d].duration/60);var h=a.data[d].duration-60*f;g.innerHTML='<td><span class="material-icons">music_note</span></td><td>'+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>h?"0":"")+h+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
@ -76,24 +78,28 @@ h.setAttribute("data-uri",f);h.setAttribute("data-type","plist");h.setAttribute(
"playlistTrackId"+g){h=document.createElement("tr");h.setAttribute("draggable","true");h.setAttribute("id","playlistTrackId"+g);h.setAttribute("data-type",a.data[d].type);h.setAttribute("data-uri",f);h.setAttribute("data-name",a.data[d].name);h.setAttribute("data-songpos",g);f=Math.floor(a.data[d].duration/60);var k=a.data[d].duration-60*f;h.innerHTML="<td>"+g+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>k?"0":"")+k+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>'; "playlistTrackId"+g){h=document.createElement("tr");h.setAttribute("draggable","true");h.setAttribute("id","playlistTrackId"+g);h.setAttribute("data-type",a.data[d].type);h.setAttribute("data-uri",f);h.setAttribute("data-name",a.data[d].name);h.setAttribute("data-songpos",g);f=Math.floor(a.data[d].duration/60);var k=a.data[d].duration-60*f;h.innerHTML="<td>"+g+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>k?"0":"")+k+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
d<e.length?e[d].replaceWith(h):c.append(h)}for(d=e.length-1;d>=b;d--)e[d].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")}} d<e.length?e[d].replaceWith(h):c.append(h)}for(d=e.length-1;d>=b;d--)e[d].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){if("Browse"===app.current.app||"Database"===app.current.tab||"Artist"===app.current.view)if("AlbumArtist"==a.tagtype){document.getElementById("BrowseDatabaseAlbumList").classList.add("hide");document.getElementById("BrowseDatabaseArtistList").classList.remove("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.add("hide");for(var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0], function parseListDBtags(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Artist"===app.current.view)if("AlbumArtist"==a.tagtype){document.getElementById("BrowseDatabaseAlbumList").classList.add("hide");document.getElementById("BrowseDatabaseArtistList").classList.remove("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.add("hide");for(var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0],
e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].value);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-uri",f);g.innerHTML='<td><span class="material-icons">album</span></td><td>'+a.data[d].value+"</td>";d<e.length?e[d].replaceWith(g):c.append(g)}}for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No entries found.</td></tr>'); e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].value);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-uri",f);g.innerHTML='<td><span class="material-icons">album</span></td><td>'+a.data[d].value+"</td>";d<e.length?e[d].replaceWith(g):c.append(g)}}for(d=e.length-1;d>=b;d--)e[d].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("BrowseDatabaseArtistList").classList.remove("opacity05")}else if("Album"==a.tagtype){document.getElementById("BrowseDatabaseAlbumList").classList.remove("hide");document.getElementById("BrowseDatabaseArtistList").classList.add("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.remove("hide");b=a.data.length;c=document.getElementById("BrowseDatabaseAlbumList");e=c.querySelectorAll(".col-md");for(d=0;d<b;d++)f=genId(a.data[d].value),e[d]&&e[d].getAttribute("id")== document.getElementById("BrowseDatabaseArtistList").classList.remove("opacity05")}else if("Album"==a.tagtype){document.getElementById("BrowseDatabaseAlbumList").classList.remove("hide");document.getElementById("BrowseDatabaseArtistList").classList.add("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.remove("hide");b=a.data.length;c=document.getElementById("BrowseDatabaseAlbumList");e=c.getElementsByClassName("col-md");for(d=0;d<b;d++)f=genId(a.data[d].value),e[d]&&e[d].getAttribute("id")==
f||(g=document.createElement("div"),g.classList.add("col-md"),g.classList.add("mr-0"),g.setAttribute("id",f),g.innerHTML='<div class="card mb-4" id="card'+f+'"> <a href="#" class="card-img-top"><img class="card-img-top" src="" ></a> <div class="card-body"> <h5 class="card-title">'+a.searchstr+'</h5> <h4 class="card-title">'+a.data[d].value+'</h4> <table class="table table-sm table-hover" id="tbl'+f+'"><tbody></tbody></table </div></div>',d<e.length?e[d].replaceWith(g):c.append(g),sendAPI({cmd:"MPD_API_GET_ARTISTALBUMTITLES", f||(g=document.createElement("div"),g.classList.add("col-md"),g.classList.add("mr-0"),g.setAttribute("id",f),g.innerHTML='<div class="card mb-4" id="card'+f+'"> <a href="#" class="card-img-top"><img class="card-img-top" src="" ></a> <div class="card-body"> <h5 class="card-title">'+a.searchstr+'</h5> <h4 class="card-title">'+a.data[d].value+'</h4> <table class="table table-sm table-hover" id="tbl'+f+'"><tbody></tbody></table </div></div>',d<e.length?e[d].replaceWith(g):c.append(g),sendAPI({cmd:"MPD_API_GET_ARTISTALBUMTITLES",
data:{albumartist:a.searchstr,album:a.data[d].value}},parseListTitles));for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);document.getElementById("BrowseDatabaseAlbumList").classList.remove("opacity05")}} data:{albumartist:a.searchstr,album:a.data[d].value}},parseListTitles));for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);document.getElementById("BrowseDatabaseAlbumList").classList.remove("opacity05")}}
function parseListTitles(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Album"===app.current.view){var b=genId(a.album),c=document.getElementById("card"+b);b=c.getElementsByTagName("tbody")[0];var e=c.getElementsByTagName("img")[0];c=e.parentNode;e.setAttribute("src",a.cover);c.setAttribute("data-uri",encodeURI(a.data[0].uri.replace(/\/[^\/]+$/,"")));c.setAttribute("data-name",a.album);c.setAttribute("data-type","dir");e="";for(var d=a.data.length,f=0;f<d;f++)e+='<tr data-type="song" data-name="'+ function parseListTitles(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Album"===app.current.view){var b=genId(a.album),c=document.getElementById("card"+b);b=c.getElementsByTagName("tbody")[0];var e=c.getElementsByTagName("img")[0];c=e.parentNode;e.setAttribute("src",a.cover);c.setAttribute("data-uri",encodeURI(a.data[0].uri.replace(/\/[^\/]+$/,"")));c.setAttribute("data-name",a.album);c.setAttribute("data-type","dir");e="";for(var d=a.data.length,f=0;f<d;f++)e+='<tr data-type="song" data-name="'+
a.data[f].title+'" data-uri="'+encodeURI(a.data[f].uri)+'"><td>'+a.data[f].track+"</td><td>"+a.data[f].title+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td></tr>';b.innerHTML=e;c.addEventListener("click",function(a){a.preventDefault();showMenu(this)},!1);b.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&& a.data[f].title+'" data-uri="'+encodeURI(a.data[f].uri)+'"><td>'+a.data[f].track+"</td><td>"+a.data[f].title+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td></tr>';b.innerHTML=e;c.addEventListener("click",function(a){a.preventDefault();showMenu(this)},!1);b.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&&
(a.preventDefault(),showMenu(a.target))},!1)}} (a.preventDefault(),showMenu(a.target))},!1)}}
function setPagination(a){var b=Math.ceil(a/settings.max_elements_per_page),c=app.current.app+(void 0==app.current.tab?"":app.current.tab);0==b&&(b=1);for(var e=["PaginationTop","PaginationBottom"],d=0;2>d;d++){document.getElementById(c+e[d]+"Page").innerText=app.current.page/settings.max_elements_per_page+1+" / "+b;if(1<b){document.getElementById(c+e[d]+"Page").removeAttribute("disabled");for(var f="",g=0;g<b;g++)f+='<button data-page="'+g*settings.max_elements_per_page+'" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+ 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 e=["PaginationTop","PaginationBottom"],d=0;2>d;d++){document.getElementById(c+e[d]+"Page").innerText=app.current.page/settings.maxElementsPerPage+1+" / "+b;if(1<b){document.getElementById(c+e[d]+"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+e[d]+"Pages").innerHTML=f}else document.getElementById(c+e[d]+"Page").setAttribute("disabled","disabled");a>app.current.page+settings.max_elements_per_page?(document.getElementById(c+e[d]+"Next").removeAttribute("disabled"),document.getElementById(c+e[d]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):(document.getElementById(c+e[d]+"Next").setAttribute("disabled","disabled"),document.getElementById(c+e[d]).classList.add("hide"), (g+1)+"</button>";document.getElementById(c+e[d]+"Pages").innerHTML=f}else document.getElementById(c+e[d]+"Page").setAttribute("disabled","disabled");a>app.current.page+settings.maxElementsPerPage?(document.getElementById(c+e[d]+"Next").removeAttribute("disabled"),document.getElementById(c+e[d]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):(document.getElementById(c+e[d]+"Next").setAttribute("disabled","disabled"),document.getElementById(c+e[d]).classList.add("hide"),
document.getElementById(c+"ButtonsBottom").classList.add("hide"));0<app.current.page?(document.getElementById(c+e[d]+"Prev").removeAttribute("disabled"),document.getElementById(c+e[d]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):document.getElementById(c+e[d]+"Prev").setAttribute("disabled","disabled")}} document.getElementById(c+"ButtonsBottom").classList.add("hide"));0<app.current.page?(document.getElementById(c+e[d]+"Prev").removeAttribute("disabled"),document.getElementById(c+e[d]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):document.getElementById(c+e[d]+"Prev").setAttribute("disabled","disabled")}}
function appendQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:b}});showNotification('"'+c+'" added',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:b}});showNotification('"'+c+'" added',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_ADD_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" added',"","","success")}} function appendQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:b}});showNotification('"'+c+'" added',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:b}});showNotification('"'+c+'" added',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_ADD_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" added',"","","success")}}
function appendAfterQueue(a,b,c,e){switch(a){case "song":sendAPI({cmd:"MPD_API_ADD_TRACK_AFTER",data:{uri:b,to:c}}),showNotification('"'+e+'" added to pos '+c,"","","success")}} function appendAfterQueue(a,b,c,e){switch(a){case "song":sendAPI({cmd:"MPD_API_ADD_TRACK_AFTER",data:{uri:b,to:c}}),showNotification('"'+e+'" added to pos '+c,"","","success")}}
function replaceQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_REPLACE_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" replaced',"","","success")}}function songClick(){songDetails(domCache.currentTrack.getAttribute("data-uri"))} function replaceQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_REPLACE_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" replaced',"","","success")}}
function artistClick(){appGoto("Browse","Database","Album","0/-/"+document.getElementById("currentArtist").innerText)}function songDetails(a){sendAPI({cmd:"MPD_API_GET_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()} 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","Album","0/-/"+a)}function songDetails(a){sendAPI({cmd:"MPD_API_GET_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()}
function parseSongDetails(a){var b=document.getElementById("modalSongDetails");b.querySelector(".album-cover").style.backgroundImage='url("'+a.data.cover+'")';b.getElementsByTagName("h1")[0].innerText=a.data.title;b=b.getElementsByTagName("tr");for(var c=b.length,e=0;e<c;e++){var d=b[e].getAttribute("data-name"),f=a.data[d];"duration"==d?(d=Math.floor(f/60),f-=60*d,f=d+":"+(10>f?"0":"")+f):"uri"==d&&(f='<a class="text-success" href="/library/'+f+'">'+f+"</a>");b[e].getElementsByTagName("td")[1].innerHTML= 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;b=b.getElementsByTagName("tr");for(var c=b.length,e=0;e<c;e++){var d=b[e].getAttribute("data-name");if(d){var f=a.data[d];"duration"==d?(d=Math.floor(f/60),f-=60*d,f=d+":"+(10>f?"0":"")+f):"lastPlayed"==d?f=0==f?"never":(new Date(1E3*f)).toUTCString():"like"==d?f=0==f?'<span class="material-icons">thumb_down_alt</span>':
f}}function playlistDetails(a){appGoto("Browse","Playlists","Detail","0/-/"+a)}function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_RM_PLAYLIST_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)} 2==f?'<span class="material-icons">thumb_up_alt</span>':"not voted":"uri"==d&&(f='<a class="text-success" href="/library/'+f+'">'+f+"</a>");b[e].getElementsByTagName("td")[0].innerHTML=f}}}function playlistDetails(a){appGoto("Browse","Playlists","Detail","0/-/"+a)}
function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_RM_PLAYLIST_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)}
function playlistClear(){var a=document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri");sendAPI({cmd:"MPD_API_PLAYLIST_CLEAR",data:{uri:a}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)} function playlistClear(){var a=document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri");sendAPI({cmd:"MPD_API_PLAYLIST_CLEAR",data:{uri:a}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)}
function getAllPlaylists(a){for(var b=a.data.length,c="<option></option><option>New Playlist</option>",e=0;e<b;e++)c+="<option>"+a.data[e].uri+"</option>";document.getElementById("addToPlaylistPlaylist").innerHTML+=c;a.totalEntities>a.returnedEntities&&(a.offset+=settings.max_elements_per_page,sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:a.offset,filter:"-"}},getAllPlaylists))} function getAllPlaylists(a){for(var b=a.data.length,c="<option></option><option>New Playlist</option>",e=0;e<b;e++)c+="<option>"+a.data[e].uri+"</option>";document.getElementById("addToPlaylistPlaylist").innerHTML+=c;a.totalEntities>a.returnedEntities&&(a.offset+=settings.maxElementsPerPage,sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:a.offset,filter:"-"}},getAllPlaylists))}
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))}
function setVoteSongBtns(a,b){""==b||0==b.indexOf("http://")||0==b.indexOf("https://")?(domCache.btnVoteUp.setAttribute("disabled","disabled"),domCache.btnVoteDown.setAttribute("disabled","disabled")):(domCache.btnVoteUp.removeAttribute("disabled"),domCache.btnVoteDown.removeAttribute("disabled"));0==a?(domCache.btnVoteUp.classList.remove("active-fg-green"),domCache.btnVoteDown.classList.add("active-fg-red")):1==a?(domCache.btnVoteUp.classList.remove("active-fg-green"),domCache.btnVoteDown.classList.remove("active-fg-red")):
2==a&&(domCache.btnVoteUp.classList.add("active-fg-green"),domCache.btnVoteDown.classList.remove("active-fg-red"))}
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 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 showAddToPlaylist(a){document.getElementById("addToPlaylistUri").value=a;document.getElementById("addToPlaylistPlaylist").innerHTML="";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"); function showAddToPlaylist(a){document.getElementById("addToPlaylistUri").value=a;document.getElementById("addToPlaylistPlaylist").innerHTML="";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("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"),
@ -103,32 +109,34 @@ function addToPlaylist(){var a=document.getElementById("addToPlaylistUri").value
function addStream(){var a=document.getElementById("streamUrl").value;""!=a&&0==a.indexOf("http")?(sendAPI({cmd:"MPD_API_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"))} function addStream(){var a=document.getElementById("streamUrl").value;""!=a&&0==a.indexOf("http")?(sendAPI({cmd:"MPD_API_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"))}
function showRenamePlaylist(a){document.getElementById("renamePlaylistFrm").classList.remove("was-validated");document.getElementById("renamePlaylistTo").classList.remove("is-invalid");modalRenamePlaylist.show();document.getElementById("renamePlaylistFrom").value=a;document.getElementById("renamePlaylistTo").value=""} function showRenamePlaylist(a){document.getElementById("renamePlaylistFrm").classList.remove("was-validated");document.getElementById("renamePlaylistTo").classList.remove("is-invalid");modalRenamePlaylist.show();document.getElementById("renamePlaylistFrom").value=a;document.getElementById("renamePlaylistTo").value=""}
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(),sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists)):(document.getElementById("renamePlaylistTo").classList.add("is-invalid"),document.getElementById("renamePlaylistFrm").classList.add("was-validated"))} 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(),sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists)):(document.getElementById("renamePlaylistTo").classList.add("is-invalid"),document.getElementById("renamePlaylistFrm").classList.add("was-validated"))}
function showMenu(a){var b=a.getAttribute("data-type"),c=decodeURI(a.getAttribute("data-uri")),e=a.getAttribute("data-name"),d=0;if(null==b||null==c)b=a.parentNode.parentNode.getAttribute("data-type"),c=decodeURI(a.parentNode.parentNode.getAttribute("data-uri")),e=a.parentNode.parentNode.getAttribute("data-name");last_state&&(d=last_state.data.nextsongpos);var f="";"Browse"==app.current.app&&"Filesystem"==app.current.tab||"Search"==app.current.app||"Browse"==app.current.app&&"Database"==app.current.tab&& function showMenu(a){var b=a.getAttribute("data-type"),c=decodeURI(a.getAttribute("data-uri")),e=a.getAttribute("data-name"),d=0;if(null==b||null==c)b=a.parentNode.parentNode.getAttribute("data-type"),c=decodeURI(a.parentNode.parentNode.getAttribute("data-uri")),e=a.parentNode.parentNode.getAttribute("data-name");lastState&&(d=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&&
"Album"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a>"+("song"==b?"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendAfterQueue', 'options': ['"+b+"','"+c+"',"+d+",'"+e+"']}\">Add after current playing song</a>":"")+"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a>"+("plist"!=b?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showAddToPlaylist', 'options': ['"+ "Album"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a>"+("song"==b?"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendAfterQueue', 'options': ['"+b+"','"+c+"',"+d+",'"+e+"']}\">Add after current playing song</a>":"")+"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a>"+("plist"!=b?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showAddToPlaylist', 'options': ['"+
c+"']}\">Add to playlist</a>":"")+("dir"!=b?'<div class="dropdown-divider"></div>':"")+("song"==b?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+c+'\']}" href="#">Songdetails</a>':"")+("plist"==b?"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'playlistDetails', 'options': ['"+c+"']}\">Show playlist</a>":""):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+ c+"']}\">Add to playlist</a>":"")+("dir"!=b?'<div class="dropdown-divider"></div>':"")+("song"==b?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+c+'\']}" href="#">Songdetails</a>':"")+("plist"==b?"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'playlistDetails', 'options': ['"+c+"']}\">Show playlist</a>":""):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+
b+"','"+c+"','"+e+"']}\">Append to queue</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a><div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'playlistDetails', 'options': ['"+c+"']}\">Edit playlist</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showRenamePlaylist', 'options': ['"+c+"']}\">Rename playlist</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delPlaylist', 'options': ['"+ b+"','"+c+"','"+e+"']}\">Append to queue</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a><div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'playlistDetails', 'options': ['"+c+"']}\">Edit playlist</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showRenamePlaylist', 'options': ['"+c+"']}\">Rename playlist</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delPlaylist', 'options': ['"+
c+"']}\">Delete playlist</a>":"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a>"+("false"==document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-ro")?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'removeFromPlaylist', 'options': ['"+ c+"']}\">Delete playlist</a>":"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a>"+("false"==document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-ro")?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'removeFromPlaylist', 'options': ['"+
document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri")+"', '"+a.parentNode.parentNode.getAttribute("data-songpos")+"']}\">Remove</a>":"")+("plist"!=b?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showAddToPlaylist', 'options': ['"+c+"']}\">Add to playlist</a>":""):"Queue"==app.current.app&&(f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['single',"+a.parentNode.parentNode.getAttribute("data-trackid")+ document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri")+"', '"+a.parentNode.parentNode.getAttribute("data-songpos")+"']}\">Remove</a>":"")+("plist"!=b?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showAddToPlaylist', 'options': ['"+c+"']}\">Add to playlist</a>":""):"Queue"==app.current.app&&(f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['single',"+a.parentNode.parentNode.getAttribute("data-trackid")+
"]}\">Remove</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',0,"+a.parentNode.parentNode.getAttribute("data-songpos")+"]}\">Remove all upwards</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',"+(parseInt(a.parentNode.parentNode.getAttribute("data-songpos"))-1)+',-1]}">Remove all downwards</a><div class="dropdown-divider"></div>'+(-1==c.indexOf("http")?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+ "]}\">Remove</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',0,"+a.parentNode.parentNode.getAttribute("data-songpos")+"]}\">Remove all upwards</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',"+(parseInt(a.parentNode.parentNode.getAttribute("data-songpos"))-1)+',-1]}">Remove all downwards</a><div class="dropdown-divider"></div>'+(-1==c.indexOf("http")?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+
c+'\']}" href="#">Songdetails</a>':""));void 0==a.Popover&&(new Popover(a,{trigger:"click",template:'<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-content">'+f+"</div></div>"}),b=a.Popover,a.addEventListener("shown.bs.popover",function(a){document.querySelector(".popover-content").addEventListener("click",function(a){a.preventDefault();a.stopPropagation();a=JSON.parse(a.target.getAttribute("data-href").replace(/'/g,'"'));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null, c+'\']}" href="#">Songdetails</a>':""));void 0==a.Popover&&(new Popover(a,{trigger:"click",template:'<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-content">'+f+"</div></div>"}),b=a.Popover,a.addEventListener("shown.bs.popover",function(a){document.getElementsByClassName("popover-content")[0].addEventListener("click",function(a){a.preventDefault();a.stopPropagation();a=JSON.parse(a.target.getAttribute("data-href").replace(/'/g,'"'));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)},!1),b.show())} $jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1)},!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 e=JSON.parse(c.responseText);"error"==e.type?(showNotification("Error",e.data,e.data,"danger"),console.log("Error: "+e.data)):"result"==e.type&&"ok"!=e.data?showNotification(e.data,"","","success"):void 0!=b&&"function"==typeof b&&b(e)}else console.log("Empty response for request: "+JSON.stringify(a))}; 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 e=JSON.parse(c.responseText);"error"==e.type?(showNotification("Error",e.data,e.data,"danger"),console.log("Error: "+e.data)):"result"==e.type&&"ok"!=e.data?showNotification(e.data,"","","success"):void 0!=b&&"function"==typeof b&&b(e)}else console.log("Empty response for request: "+JSON.stringify(a))};
c.send(JSON.stringify(a))}function openLocalPlayer(){window.open("/player.html#"+settings.mpdstream,"LocalPlayer")}function updateDB(){sendAPI({cmd:"MPD_API_UPDATE_DB"});showNotification("Updating MPD Database...","","","success")}function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_SET_PLAY"}):sendAPI({cmd:"MPD_API_SET_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_SET_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_SET_PREV"})} c.send(JSON.stringify(a))}function openLocalPlayer(){window.open("/player.html#"+settings.mpdstream,"LocalPlayer")}function updateDB(){sendAPI({cmd:"MPD_API_UPDATE_DB"});updateDBstarted()}function updateDBstarted(){document.getElementById("updateDBfinished").innerText="";document.getElementById("updateDBfooter").classList.add("hide");modalUpdateDB.show();document.getElementById("updateDBprogress").classList.add("updateDBprogressAnimate")}
function clickNext(){sendAPI({cmd:"MPD_API_SET_NEXT"})}function delQueueSong(a,b,c){"range"==a?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:b,end:c}}):"single"==a&&sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:b}})}function delPlaylist(a){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{uri:a}});sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists)} function updateDBfinished(a){"update_database"==a?document.getElementById("updateDBfinished").innerText="Database successfully updated":"update_finished"==a&&(document.getElementById("updateDBfinished").innerText="Database update finished.");a=document.getElementById("updateDBprogress");a.classList.remove("updateDBprogressAnimate");a.style.width="100%";document.getElementById("updateDBfooter").classList.remove("hide")}
function confirmSettings(){var a=!0,b=document.getElementById("inputCrossfade");if(!b.getAttribute("disabled")){var c=parseInt(b.value);isNaN(c)?(b.classList.add("is-invalid"),a=!1):b.value=c}b=document.getElementById("inputMixrampdb");b.getAttribute("disabled")||(c=parseFloat(b.value),isNaN(c)?(b.classList.add("is-invalid"),a=!1):b.value=c);b=document.getElementById("inputMixrampdelay");b.getAttribute("disabled")||("nan"==b.value&&(b.value="-1"),c=parseFloat(b.value),isNaN(c)?(b.classList.add("is-invalid"), function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_SET_PLAY"}):sendAPI({cmd:"MPD_API_SET_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_SET_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_SET_PREV"})}function clickNext(){sendAPI({cmd:"MPD_API_SET_NEXT"})}function delQueueSong(a,b,c){"range"==a?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:b,end:c}}):"single"==a&&sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:b}})}
a=!1):b.value=c);1==a?(a=document.getElementById("selectReplaygain"),sendAPI({cmd:"MPD_API_SET_SETTINGS",data:{consume:document.getElementById("btnConsume").classList.contains("active")?1:0,random:document.getElementById("btnRandom").classList.contains("active")?1:0,single:document.getElementById("btnSingle").classList.contains("active")?1:0,repeat:document.getElementById("btnRepeat").classList.contains("active")?1:0,replaygain:a.options[a.selectedIndex].value,crossfade:document.getElementById("inputCrossfade").value, function delPlaylist(a){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{uri:a}});sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists)}
mixrampdb:document.getElementById("inputMixrampdb").value,mixrampdelay:document.getElementById("inputMixrampdelay").vaue,notificationWeb:document.getElementById("btnnotifyWeb").classList.contains("active")?1:0,notificationPage:document.getElementById("btnnotifyPage").classList.contains("active")?1:0}},getSettings),modalSettings.hide()):document.getElementById("settingsFrm").classList.add("was-validated")} function confirmSettings(){var a=!0,b=document.getElementById("inputCrossfade");if(!b.getAttribute("disabled")){var c=parseInt(b.value);isNaN(c)?(b.classList.add("is-invalid"),a=!1):b.value=c}settings.mixramp&&(b=document.getElementById("inputMixrampdb"),b.getAttribute("disabled")||(c=parseFloat(b.value),isNaN(c)?(b.classList.add("is-invalid"),a=!1):b.value=c),b=document.getElementById("inputMixrampdelay"),b.getAttribute("disabled")||("nan"==b.value&&(b.value="-1"),c=parseFloat(b.value),isNaN(c)?
(b.classList.add("is-invalid"),a=!1):b.value=c));1==a?(a=document.getElementById("selectReplaygain"),sendAPI({cmd:"MPD_API_SET_SETTINGS",data:{consume:document.getElementById("btnConsume").classList.contains("active")?1:0,random:document.getElementById("btnRandom").classList.contains("active")?1:0,single:document.getElementById("btnSingle").classList.contains("active")?1:0,repeat:document.getElementById("btnRepeat").classList.contains("active")?1:0,replaygain:a.options[a.selectedIndex].value,crossfade:document.getElementById("inputCrossfade").value,
mixrampdb:1==settings.mixramp?document.getElementById("inputMixrampdb").value:settings.mixrampdb,mixrampdelay:1==settings.mixramp?document.getElementById("inputMixrampdelay").value:settings.mixrampdelay,notificationWeb:document.getElementById("btnnotifyWeb").classList.contains("active")?1:0,notificationPage:document.getElementById("btnnotifyPage").classList.contains("active")?1:0}},getSettings),modalSettings.hide()):document.getElementById("settingsFrm").classList.add("was-validated")}
function addAllFromBrowse(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:app.current.search}});showNotification("Added all songs","","","success")}function addAllFromSearch(){2<=app.current.search.length&&(sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:app.current.search}}),showNotification("Added "+parseInt(document.getElementById("panel-heading-search").innerText)+" songs from search","","","success"))} function addAllFromBrowse(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:app.current.search}});showNotification("Added all songs","","","success")}function addAllFromSearch(){2<=app.current.search.length&&(sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:app.current.search}}),showNotification("Added "+parseInt(document.getElementById("panel-heading-search").innerText)+" songs from search","","","success"))}
function addAllFromSearchPlist(a){if(2<=app.current.search.length){var b=app.current.filter;"Any Tag"==b&&(b="any");sendAPI({cmd:"MPD_API_SEARCH_ADD_PLIST",data:{plist:a,filter:b,searchstr:app.current.search}});showNotification("Added "+parseInt(document.getElementById("panel-heading-search").innerText)+" songs from search to "+a,"","","success")}}function scrollTo(a){document.body.scrollTop=a;document.documentElement.scrollTop=a} function addAllFromSearchPlist(a){if(2<=app.current.search.length){var b=app.current.filter;"Any Tag"==b&&(b="any");sendAPI({cmd:"MPD_API_SEARCH_ADD_PLIST",data:{plist:a,filter:b,searchstr:app.current.search}});showNotification("Added "+parseInt(document.getElementById("panel-heading-search").innerText)+" songs from search to "+a,"","","success")}}function scrollTo(a){document.body.scrollTop=a;document.documentElement.scrollTop=a}
function gotoPage(a){switch(a){case "next":app.current.page+=settings.max_elements_per_page;break;case "prev":app.current.page-=settings.max_elements_per_page;0>=app.current.page&&(app.current.page=0);break;default:app.current.page=a}appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)} function gotoPage(a){switch(a){case "next":app.current.page+=settings.maxElementsPerPage;break;case "prev":app.current.page-=settings.maxElementsPerPage;0>app.current.page&&(app.current.page=0);break;default:app.current.page=a}appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)}
function saveQueue(){var a=document.getElementById("saveQueueName").value,b=a.replace(/\w/g,"");""!=a&&""==b?(sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:a}}),modalSavequeue.hide()):(document.getElementById("saveQueueName").classList.add("is-invalid"),document.getElementById("saveQueueFrm").classList.add("was-validated"))} function saveQueue(){var a=document.getElementById("saveQueueName").value,b=a.replace(/\w/g,"");""!=a&&""==b?(sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:a}}),modalSavequeue.hide()):(document.getElementById("saveQueueName").classList.add("is-invalid"),document.getElementById("saveQueueFrm").classList.add("was-validated"))}
function showNotification(a,b,c,e){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-"+e),b.innerHTML="<div><strong>"+ function showNotification(a,b,c,e){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-"+e),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(){document.getElementById("alertBox").remove()},600))} 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 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(last_song!=b){var c="",e="",d="myMPD: ";document.getElementById("currentCover").style.backgroundImage='url("'+a.data.cover+'")';"undefined"!=typeof a.data.artist&&0<a.data.artist.length&&"-"!=a.data.artist?(c+=a.data.artist,e+=a.data.artist,d+=a.data.artist+" - ",document.getElementById("currentArtist").innerText=a.data.artist):document.getElementById("currentArtist").innerText= 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="",e="",d="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,e+=a.data.artist,d+=a.data.artist+" - ",domCache.currentArtist.innerText=a.data.artist,domCache.currentArtist.setAttribute("data-albumartist",a.data.albumartist)):
"";"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(c+=" - "+a.data.album,e+="<br/>"+a.data.album,document.getElementById("currentAlbum").innerText=a.data.album):document.getElementById("currentAlbum").innerText="";"undefined"!=typeof a.data.title&&0<a.data.title.length?(d+=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", domCache.currentArtist.innerText="";"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(c+=" - "+a.data.album,e+="<br/>"+a.data.album,domCache.currentAlbum.innerText=a.data.album):domCache.currentAlbum.innerText="";"undefined"!=typeof a.data.title&&0<a.data.title.length?(d+=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=d;if(d=document.getElementById("queueTrackId"+a.data.currentsongid))d.getElementsByTagName("td")[1].innerText=a.data.title;showNotification(a.data.title,c,e,"success");last_song=b}}} document.title=d;1==settings.stickers&&setVoteSongBtns(a.data.like,a.data.uri);if(d=document.getElementById("queueTrackId"+a.data.currentSongId))d.getElementsByTagName("td")[1].innerText=a.data.title;showNotification(a.data.title,c,e,"success");lastSong=b}}}
function doSetFilterLetter(a){var b=document.getElementById(a+"Letters").querySelector(".active");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,e=0;e<c;e++)if(a[e].innerText==b){a[e].classList.add("active");break}}} 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,e=0;e<c;e++)if(a[e].innerText==b){a[e].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, 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 chVolume(a){a=parseInt(domCache.volumeBar.value)+a;0>a?a=0:100<a&&(a=100);domCache.volumeBar.value=a;sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:a}})}function beautifyDuration(a){var b=Math.floor(a/86400),c=Math.floor(a/3600)-24*b,e=Math.floor(a/60)-60*c-1440*b;a=a-86400*b-3600*c-60*e;return(0<b?b+"\u2009d ":"")+(0<c?c+"\u2009h "+(10>e?"0":""):"")+e+"\u2009m "+(10>a?"0":"")+a+"\u2009s"} app.current.tab,app.current.view,"0/"+b+"/"+app.current.search)},!1)}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_SET_VOLUME",data:{volume:a}})}function beautifyDuration(a){var b=Math.floor(a/86400),c=Math.floor(a/3600)-24*b,e=Math.floor(a/60)-60*c-1440*b;a=a-86400*b-3600*c-60*e;return(0<b?b+"\u2009d ":"")+(0<c?c+"\u2009h "+(10>e?"0":""):"")+e+"\u2009m "+(10>a?"0":"")+a+"\u2009s"}
function genId(a){return"id"+a.replace(/[^\w]/g,"")}appInit(); function genId(a){return"id"+a.replace(/[^\w]/g,"")}appInit();

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="myMPD - fast and lightweight MPD webclient"><meta name="author" content="mail@jcgames.de"><title>myMPD: Local Player</title><link href="css/bootstrap.min.css" rel="stylesheet"><link href="css/mpd.min.css" rel="stylesheet"><link href="assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"><meta name="apple-mobile-web-app-capable" content="yes" /><meta name="apple-mobile-web-app-status-bar-style" content="black"/><link rel="apple-touch-icon" href="assets/appicon.png"/></head><body><header><nav class="navbar navbar-expand navbar-dark fixed-top bg-dark"><a class="navbar-brand" data-toggle="dropdown" href="#"><span class="material-icons header-logo">play_circle_outline</span>myMPD</a></nav></header><main role="main" class="container"><div class="card"><div class="card-header">Local Player</div><div class="card-body"><audio id="player" preload="none" controls=""></audio></div></div></main><script type="text/javascript" src="js/player.min.js"></script></body></html> <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="myMPD - fast and lightweight MPD webclient"><meta name="author" content="mail@jcgames.de"><title>myMPD: Local Player</title><link href="css/bootstrap.min.css" rel="stylesheet"><link href="css/mympd.min.css" rel="stylesheet"><link href="assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"><meta name="apple-mobile-web-app-capable" content="yes" /><meta name="apple-mobile-web-app-status-bar-style" content="black"/><link rel="apple-touch-icon" href="assets/appicon.png"/></head><body><header><nav class="navbar navbar-expand navbar-dark fixed-top bg-dark"><a class="navbar-brand" data-toggle="dropdown" href="#"><span class="material-icons header-logo">play_circle_outline</span>myMPD</a></nav></header><main role="main" class="container"><div class="card"><div class="card-header">Local Player</div><div class="card-body"><audio id="player" preload="none" controls=""></audio></div></div></main><script type="text/javascript" src="js/player.min.js"></script></body></html>

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= 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)}}: 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, 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-v3.5.0",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mpd.min.css /js/bootstrap-native-v4.min.js /js/mpd.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/favicon.ico /assets/MaterialIcons-Regular.eot /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.0.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/favicon.ico /assets/MaterialIcons-Regular.eot /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){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)}))}))}); self.addEventListener("install",function(a){a.waitUntil(caches.open(CACHE).then(function(a){return a.addAll(urlsToCache)}))});self.addEventListener("fetch",function(a){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

@ -2,8 +2,16 @@ html {
position: relative; position: relative;
min-height: 100%; min-height: 100%;
} }
body { body {
margin-bottom: 60px; margin-bottom: 60px;
padding-top: 50px;
padding-bottom: 50px;
background-color: #888;
}
main {
padding-top: 20px;
} }
footer { footer {
@ -11,12 +19,6 @@ footer {
bottom: 0; bottom: 0;
} }
body {
padding-top: 50px;
padding-bottom: 50px;
background-color:#888;
}
button { button {
overflow: hidden; overflow: hidden;
} }
@ -30,14 +32,9 @@ button {
cursor: pointer; cursor: pointer;
} }
#counter {
font-size: 22px;
margin-left: 10px;
min-width: 50px;
}
.card { .card {
min-height:350px; min-height: 350px;
} }
@media only screen and (max-width: 576px) { @media only screen and (max-width: 576px) {
@ -54,15 +51,30 @@ button {
width: 30px; width: 30px;
} }
small {
color: #aaa;
}
.card-footer-playback {
padding: 0px;
}
.album-cover { .album-cover {
background-size:cover; background-size: cover;
border:1px solid black; border: 1px solid black;
border-radius:5px; border-radius: 5px;
overflow:hidden; overflow: hidden;
margin-bottom:20px; width: 240px;
width:240px; height: 240px;
height:240px; background-color: #eee;
background-color:#eee; float: left;
margin-right: 20px;
margin-bottom: 20px;
}
.album-desc {
min-width: 240px;
float: left;
} }
.hide { .hide {
@ -74,11 +86,11 @@ button {
} }
.card-toolbar { .card-toolbar {
margin-bottom:10px; margin-bottom: 10px;
} }
.card-toolbar > div, .card-toolbar > form { .card-toolbar > div, .card-toolbar > form {
margin-bottom:5px; margin-bottom: 5px;
} }
@font-face { @font-face {
@ -117,111 +129,115 @@ button {
} }
.material-icons-small { .material-icons-small {
font-size: 16px; font-size: 16px;
} }
main { .color-darkgrey, .color-darkgrey:hover {
padding-top:20px; color:#6c757d !important;
}
.color-darkgrey {
color:#6c757d;
}
.color-darkgrey:hover {
color:#6c757d !important;
} }
#btn-outputs-block > button { #btn-outputs-block > button {
margin-bottom:10px; margin-bottom:10px;
} }
#btn-outputs-block > button:last-child { #btn-outputs-block > button:last-child {
margin-bottom:0px; margin-bottom:0px;
} }
.card-body { .card-body {
overflow-x:hidden; overflow-x:hidden;
} }
#progressBar { .progressBarPlay {
width:100%; font-size: 22px;
margin-top:8px; }
#counter {
cursor: text;
} }
#volumeBar { #volumeBar {
margin-top:2px; margin-top:2px;
width:160px; width:160px;
} }
.title-icon { .title-icon {
float:left; float:left;
margin-right:5px; margin-right:5px;
font-size:1.8rem; font-size:1.8rem;
} }
.header-logo { .header-logo {
font-size:2rem; font-size:2rem;
float:left; float:left;
margin-right:5px; margin-right:5px;
} }
.letters > button { .letters > button {
width:28px; width:28px;
height:28px; height:28px;
} }
.col-md { .col-md {
max-width:250px; max-width:250px;
min-width:250px; min-width:250px;
} }
a.card-img-top { a.card-img-top {
overflow:hidden; overflow:hidden;
display:block; display:block;
} }
button.active { button.active {
color: #fff; color: #fff;
background-color: #28a745 !important; background-color: #28a745 !important;
border-color: #28a745 !important; border-color: #28a745 !important;
}
button.active-fg-green {
color: #28a745 !important;
}
button.active-fg-red {
color: #bd2130 !important;
} }
div#alertBox { div#alertBox {
position:fixed; position:fixed;
top: 50px; top: 50px;
right:10px; right:10px;
width:80%; width:80%;
max-width:400px; max-width:400px;
z-index:1000; z-index:1000;
opacity:0; opacity:0;
visibility:visible; visibility:visible;
transition:opacity 0.5s ease-in; transition:opacity 0.5s ease-in;
} }
div.alertBoxActive { div.alertBoxActive {
opacity:1 !important; opacity:1 !important;
visibility:visible !important; visibility:visible !important;
transition:opacity 0.5s ease-in; transition:opacity 0.5s ease-in;
} }
.popover-content { .popover-content {
padding-top:4px; padding-top:4px;
padding-bottom:4px; padding-bottom:4px;
} }
.opacity05 { .opacity05 {
opacity:0.5; opacity:0.5;
} }
caption { caption {
caption-side: top; caption-side: top;
font-size: 120%; font-size: 120%;
font-weight: bold; font-weight: bold;
color: black; color: black;
} }
.dragover > td { .dragover > td {
border-top:25px solid transparent; border-top:25px solid transparent;
} }
[draggable] { [draggable] {
@ -233,3 +249,17 @@ caption {
-khtml-user-drag: element; -khtml-user-drag: element;
-webkit-user-drag: element; -webkit-user-drag: element;
} }
@keyframes changewidth {
from { margin-left: -20px; }
to { margin-left: 100%; }
}
#updateDBprogress {
width:20px;
}
.updateDBprogressAnimate {
animation-duration: 2s;
animation-name: changewidth;
animation-iteration-count: infinite;
}

View File

@ -9,11 +9,11 @@
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="theme-color" content="#343a40"> <meta name="theme-color" content="#343a40">
<link href="css/bootstrap.min.css" rel="stylesheet"> <link href="/css/bootstrap.min.css" rel="stylesheet">
<link href="css/mpd.css" rel="stylesheet"> <link href="/css/mympd.css" rel="stylesheet">
<link href="assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"> <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">
<link rel="manifest" href="mympd.webmanifest"> <link rel="manifest" href="/mympd.webmanifest">
<link rel="apple-touch-icon" href="assets/appicon-167.png"> <link rel="apple-touch-icon" href="/assets/appicon-167.png">
</head> </head>
<body> <body>
<header> <header>
@ -23,37 +23,37 @@
<span class="material-icons header-logo">play_circle_outline</span>myMPD <span class="material-icons header-logo">play_circle_outline</span>myMPD
</a> </a>
<div class="dropdown-menu bg-dark"> <div class="dropdown-menu bg-dark">
<a id="nav-addstream" class="dropdown-item text-light bg-dark" href="#" data-href="{'cmd':'showAddToPlaylist','options':['stream']}">Add Stream</a> <a class="dropdown-item text-light bg-dark" href="#" data-href="{'cmd':'showAddToPlaylist','options':['stream']}">Add Stream</a>
<a id="nav-updatedb" 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':'updateDB','options':[]}">Update Database</a>
<a id="nav-localplayer" class="dropdown-item text-light bg-dark" href="#" data-href="{'cmd':'openLocalPlayer','options':[]}">Local Player</a> <a class="dropdown-item text-light bg-dark" href="#" data-href="{'cmd':'openLocalPlayer','options':[]}">Local Player</a>
<a id="nav-settings" 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="#modalSettings">Settings</a>
<a id="nav-about" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</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> <a id="nav-add2homescreen" class="dropdown-item text-light bg-dark hide" href="#">Add2HomeScreen</a>
</div> </div>
</div> </div>
<div class="btn-toolbar col-auto pl-0 pr-0" role="toolbar"> <div class="btn-toolbar col-auto pl-0 pr-0">
<div class="btn-group mr-2" role="group" id="playControlBtns"> <div class="btn-group mr-2" id="playControlBtns">
<button data-href="{'cmd':'clickPrev','options':[]}" id="btnPrev" type="button" class="btn btn-secondary pl-2 pr-2 material-icons"> <button data-href="{'cmd':'clickPrev','options':[]}" id="btnPrev" type="button" class="btn btn-secondary pl-2 pr-2 material-icons">
skip_previous skip_previous
</button> </button>
<button data-href="{'cmd':'clickStop','options':[]}" id="btnStop" type="button" class="btn btn-secondary pl-2 pr-2 material-icons"> <button data-href="{'cmd':'clickStop','options':[]}" id="btnStop" type="button" class="btn btn-secondary pl-2 pr-2 material-icons">
stop stop
</button> </button>
<button data-href="{'cmd':'clickPlay','options':[]}" id="btnPlay" type="button" class="btn btn-secondary pl-2 pr-2 material-icons"> <button data-href="{'cmd':'clickPlay','options':[]}" id="btnPlay" type="button" class="btnPlay btn btn-secondary pl-2 pr-2 material-icons">
pause pause
</button> </button>
<button data-href="{'cmd':'clickNext','options':[]}" id="btnNext" type="button" class="btn btn-secondary pl-2 pr-2 material-icons"> <button data-href="{'cmd':'clickNext','options':[]}" id="btnNext" type="button" class="btn btn-secondary pl-2 pr-2 material-icons">
skip_next skip_next
</button> </button>
</div> </div>
<div class="btn-group" role="group"> <div class="btn-group">
<button id="volumeIcon" class="btn btn-secondary dropdown-toggle pl-2 pr-2 material-icons" type="button" data-toggle="dropdown"> <button id="volumeIcon" class="btn btn-secondary dropdown-toggle pl-2 pr-2 material-icons" type="button" data-toggle="dropdown">
volume_up volume_up
</button> </button>
<div class="dropdown-menu dropdown-menu-right bg-dark"> <div class="dropdown-menu dropdown-menu-right bg-dark">
<h2 class="dropdown-header text-light">Volume: <span id="volumePrct"></span></h2> <h2 class="dropdown-header text-light">Volume: <span id="volumePrct"></span></h2>
<form class="px-4 py-0 pb-3" id="volumeControl"> <form class="px-4 py-0 pb-3" id="volumeControl">
<div class="btn-group" role="group"> <div class="btn-group">
<button data-href="{'cmd':'chVolume','options':[-5]}" class="btn btn-secondary">&minus;</button> <button data-href="{'cmd':'chVolume','options':[-5]}" class="btn btn-secondary">&minus;</button>
<div class="btn btn-secondary"> <div class="btn btn-secondary">
<input type="range" min="0" max="100" step="1" class="form-control-range" id="volumeBar"> <input type="range" min="0" max="100" step="1" class="form-control-range" id="volumeBar">
@ -70,41 +70,49 @@
</header> </header>
<main class="container"> <main class="container">
<noscript> <noscript>
<div class="alert alert-danger" role="alert">JavaScript is disabled!</div> <div class="alert alert-danger">JavaScript is disabled!</div>
</noscript> </noscript>
<div class="card" id="cardPlayback"> <div class="card" id="cardPlayback">
<div class="card-header">Playback</div> <div class="card-header">Playback
<div class="btn-group btn-group-sm stickers 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="card-body">
<div class="album-cover" id="currentCover"></div> <div class="album-cover" id="currentCover"></div>
<h1 id="currentTrack" data-href="{'cmd': 'songClick', 'options': []}"></h1> <div class="album-desc">
<h3 id="currentArtist" data-href="{'cmd': 'artistClick', 'options': []}"></h3> <h2 id="currentTrack" data-href="{'cmd': 'songClick', 'options': []}"></h2>
<h4 id="currentAlbum"></h4> <small>Artist</small>
<div class="row"> <h4 id="currentArtist" data-href="{'cmd': 'artistClick', 'options': []}"></h4>
<div class="col-8"> <small>Album</small>
<input type="range" min="0" max="100" step="1" class="form-control-range" id="progressBar"> <h4 id="currentAlbum"></h4>
</div> </div>
<div class="col-4"> </div>
<p id="counter" class="text">&nbsp;&nbsp;</p> <div class="card-footer card-footer-playback">
</div> <div class="d-flex align-items-center">
<button data-href="{'cmd':'clickPlay','options':[]}" class="mr-1 ml-1 btn btn-light material-icons btnPlay progressBarPlay">pause</button>
<input type="range" min="0" max="100" step="1" class="mr-1 ml-1 form-control-range flex-grow-1" id="progressBar">
<div class="btn ml-1 mr-1" id="counter">&nbsp;&nbsp;</div>
</div> </div>
</div> </div>
</div> </div>
<div class="card hide" id="cardQueue"> <div class="card hide" id="cardQueue">
<div class="card-header"> <div class="card-header">
<a href="#" data-target="#queue-buttons" class="text-dark">Queue</a> Queue<span id="panel-heading-queue" class="text pull-right"></span>
<span id="panel-heading-queue" class="text pull-right"></span>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="btn-toolbar card-toolbar" id="queue-buttons" role="toolbar"> <div class="btn-toolbar card-toolbar" id="queue-buttons">
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-toggle="modal" data-target="#modalSaveQueue" title="Save queue"> <button type="button" class="btn btn-secondary material-icons" data-toggle="modal" data-target="#modalSaveQueue" title="Save queue">
<span class="material-icons">save</span> save
</button> </button>
</div> </div>
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'sendAPI', 'options': [{'cmd':'MPD_API_SEND_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">
<span class="material-icons">shuffle</span> shuffle
</button> </button>
</div> </div>
<div class="input-group mr-2"> <div class="input-group mr-2">
@ -117,7 +125,7 @@
</div> </div>
</div> </div>
</div> </div>
<form id="searchqueue" role="search"> <form id="searchqueue">
<div class="input-group mr-2"> <div class="input-group mr-2">
<input type="text" class="form-control" placeholder="Search Queue" id="searchqueuestr"/> <input type="text" class="form-control" placeholder="Search Queue" id="searchqueuestr"/>
<div class="input-group-append"> <div class="input-group-append">
@ -168,10 +176,10 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="btn-toolbar hide" id="QueueButtonsBottom" role="toolbar"> <div class="btn-toolbar hide" id="QueueButtonsBottom">
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd':'scrollTo','options':[0]}" title="To top"> <button type="button" class="btn btn-secondary material-icons" data-href="{'cmd':'scrollTo','options':[0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span> keyboard_arrow_up
</button> </button>
</div> </div>
<div id="QueuePaginationBottom" class="btn-group mr-2 dropup"> <div id="QueuePaginationBottom" class="btn-group mr-2 dropup">
@ -203,13 +211,13 @@
</div> </div>
<div class="card-body hide" id="cardBrowsePlaylists"> <div class="card-body hide" id="cardBrowsePlaylists">
<div class="btn-toolbar card-toolbar" id="BrowsePlaylistsButtons" role="toolbar"> <div class="btn-toolbar card-toolbar" id="BrowsePlaylistsButtons">
<div class="btn-group mr-2 hide"> <div class="btn-group mr-2 hide">
<button data-href="{'cmd': 'appGoto', 'options': ['Browse','Playlists','All']}" id="btnBrowsePlaylistsAll" type="button" class="btn btn-secondary">&laquo; Playlists</button> <button data-href="{'cmd': 'appGoto', 'options': ['Browse','Playlists','All']}" id="btnBrowsePlaylistsAll" type="button" class="btn btn-secondary">&laquo; Playlists</button>
</div> </div>
<div class="btn-group mr-2 hide"> <div class="btn-group mr-2 hide">
<button id="btnPlaylistClear" type="button" class="btn btn-secondary" data-href="{'cmd': 'playlistClear', 'options': []}" title="Clear playlist"> <button id="btnPlaylistClear" type="button" class="btn btn-secondary material-icons" data-href="{'cmd': 'playlistClear', 'options': []}" title="Clear playlist">
<span class="material-icons">clear_all</span> clear_all
</button> </button>
</div> </div>
<div class="btn-group mr-2"> <div class="btn-group mr-2">
@ -266,10 +274,10 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="btn-toolbar hide" id="BrowsePlaylistsButtonsBottom" role="toolbar"> <div class="btn-toolbar hide" id="BrowsePlaylistsButtonsBottom">
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top"> <button type="button" class="btn btn-secondary material-icons" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span> keyboard_arrow_up
</button> </button>
</div> </div>
<div id="BrowsePlaylistsPaginationBottom" class="btn-group mr-2 dropup"> <div id="BrowsePlaylistsPaginationBottom" class="btn-group mr-2 dropup">
@ -285,7 +293,7 @@
</div> </div>
<div class="card-body hide" id="cardBrowseDatabase"> <div class="card-body hide" id="cardBrowseDatabase">
<div class="btn-toolbar card-toolbar" id="BrowseDatabaseButtons" role="toolbar"> <div class="btn-toolbar card-toolbar" id="BrowseDatabaseButtons">
<div class="btn-group mr-2 hide"> <div class="btn-group mr-2 hide">
<button data-href="{'cmd': 'appGoto', 'options': ['Browse','Database','Artist']}" id="btnBrowseDatabaseArtist" type="button" class="btn btn-secondary">&laquo; Artists</button> <button data-href="{'cmd': 'appGoto', 'options': ['Browse','Database','Artist']}" id="btnBrowseDatabaseArtist" type="button" class="btn btn-secondary">&laquo; Artists</button>
</div> </div>
@ -309,12 +317,10 @@
<table id="BrowseDatabaseArtistList" class="table table-hover table-sm"> <table id="BrowseDatabaseArtistList" class="table table-hover table-sm">
<col class="tblnum"/> <col class="tblnum"/>
<col class="tbltitle"/> <col class="tbltitle"/>
<col class="tblaction"/>
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th>Artist</th> <th>Artist</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody class="clickable"> <tbody class="clickable">
@ -324,10 +330,10 @@
<div id="BrowseDatabaseAlbumList" class="row hide"></div> <div id="BrowseDatabaseAlbumList" class="row hide"></div>
<div class="btn-toolbar hide" id="BrowseDatabaseButtonsBottom" role="toolbar"> <div class="btn-toolbar hide" id="BrowseDatabaseButtonsBottom">
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top"> <button type="button" class="btn btn-secondary material-icons" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span> keyboard_arrow_up
</button> </button>
</div> </div>
<div id="BrowseDatabasePaginationBottom" class="btn-group mr-2 dropup"> <div id="BrowseDatabasePaginationBottom" class="btn-group mr-2 dropup">
@ -344,7 +350,7 @@
</div> </div>
<div class="card-body hide" id="cardBrowseFilesystem"> <div class="card-body hide" id="cardBrowseFilesystem">
<div class="btn-toolbar card-toolbar" id="BrowseFilesystemButtons" role="toolbar"> <div class="btn-toolbar card-toolbar" id="BrowseFilesystemButtons">
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button id="BrowseFilesystemFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button> <button id="BrowseFilesystemFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button>
<div class="dropdown-menu bg-dark px-2 letters" id="BrowseFilesystemFilterLetters"> <div class="dropdown-menu bg-dark px-2 letters" id="BrowseFilesystemFilterLetters">
@ -396,10 +402,10 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="btn-toolbar hide" id="BrowseFilesystemButtonsBottom" role="toolbar"> <div class="btn-toolbar hide" id="BrowseFilesystemButtonsBottom">
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top"> <button type="button" class="btn btn-secondary material-icons" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span> keyboard_arrow_up
</button> </button>
</div> </div>
<div id="BrowseFilesystemPaginationBottom" class="btn-group mr-2 dropup"> <div id="BrowseFilesystemPaginationBottom" class="btn-group mr-2 dropup">
@ -416,12 +422,13 @@
</div> </div>
<div class="card hide" id="cardSearch"> <div class="card hide" id="cardSearch">
<div class="card-header">Search <div class="card-header">
Search
<span id="panel-heading-search" class="text pull-right"></span> <span id="panel-heading-search" class="text pull-right"></span>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="btn-toolbar card-toolbar" id="SearchButtons" role="toolbar"> <div class="btn-toolbar card-toolbar" id="SearchButtons">
<form id="search" role="search"> <form id="search">
<div class="input-group mr-2"> <div class="input-group mr-2">
<input type="text" class="form-control" placeholder="Search" id="searchstr"/> <input type="text" class="form-control" placeholder="Search" id="searchstr"/>
<div class="input-group-append"> <div class="input-group-append">
@ -481,10 +488,10 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="btn-toolbar hide" id="SearchButtonsBottom" role="toolbar"> <div class="btn-toolbar hide" id="SearchButtonsBottom">
<div class="btn-group mr-2"> <div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top"> <button type="button" class="btn btn-secondary material-icons" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span> keyboard_arrow_up
</button> </button>
</div> </div>
<div id="SearchPaginationBottom" class="btn-group mr-2 dropup"> <div id="SearchPaginationBottom" class="btn-group mr-2 dropup">
@ -513,8 +520,8 @@
</footer> </footer>
<!-- Modals --> <!-- Modals -->
<div class="modal fade" id="modalConnectionError" tabindex="-1" role="dialog"> <div class="modal fade" id="modalConnectionError" tabindex="-1">
<div class="modal-dialog" role="document"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header bg-danger text-light"> <div class="modal-header bg-danger text-light">
<h5 class="modal-title"><span class="material-icons title-icon">error</span> Connection Error</h5> <h5 class="modal-title"><span class="material-icons title-icon">error</span> Connection Error</h5>
@ -526,14 +533,34 @@
</div> </div>
</div> </div>
<div class="modal fade" id="modalAddToPlaylist" tabindex="-1" role="dialog"> <div class="modal fade" id="modalUpdateDB" tabindex="-1">
<div class="modal-dialog" role="document"> <div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><span class="material-icons title-icon">update</span> Updating MPD database</h5>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>Dependent on the size of your music collection this can take a while.</p>
<div class="progress">
<div id="updateDBprogress" class="progress-bar bg-success" role="progressbar"></div>
</div>
<br/>
<p id="updateDBfinished"></p>
</div>
<div class="modal-footer hide" id="updateDBfooter">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modalAddToPlaylist" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title"><span class="material-icons title-icon">playlist_add</span> <span id="addToPlaylistLabel">Add to playlist</span></h5> <h5 class="modal-title"><span class="material-icons title-icon">playlist_add</span> <span id="addToPlaylistLabel">Add to playlist</span></h5>
<button type="button" class="close" data-dismiss="modal"> <button type="button" class="close" data-dismiss="modal">&times;</button>
<span>&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="addStreamFrm" class="needs-validation hide" novalidate> <form id="addStreamFrm" class="needs-validation hide" novalidate>
@ -577,14 +604,12 @@
</div> </div>
</div> </div>
<div class="modal fade" id="modalRenamePlaylist" tabindex="-1" role="dialog"> <div class="modal fade" id="modalRenamePlaylist" tabindex="-1">
<div class="modal-dialog" role="document"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title"><span class="material-icons title-icon">playlist_add</span>Rename playlist</h5> <h5 class="modal-title"><span class="material-icons title-icon">playlist_add</span>Rename playlist</h5>
<button type="button" class="close" data-dismiss="modal"> <button type="button" class="close" data-dismiss="modal">&times;</button>
<span>&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="needs-validation" id="renamePlaylistFrm" novalidate> <form class="needs-validation" id="renamePlaylistFrm" novalidate>
@ -607,14 +632,12 @@
</div> </div>
</div> </div>
<div class="modal fade" id="modalSettings" tabindex="-1" role="dialog"> <div class="modal fade" id="modalSettings" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="settingsLabel"><span class="material-icons title-icon">settings</span> Settings</h5> <h5 class="modal-title" id="settingsLabel"><span class="material-icons title-icon">settings</span> Settings</h5>
<button type="button" class="close" data-dismiss="modal"> <button type="button" class="close" data-dismiss="modal">&times;</button>
<span>&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="needs-validation" id="settingsFrm" novalidate> <form class="needs-validation" id="settingsFrm" novalidate>
@ -661,7 +684,7 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row"> <div class="row mixramp">
<div class="form-group input-group col-md-6 border-secondary"> <div class="form-group input-group col-md-6 border-secondary">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text bg-secondary text-light border-secondary">Mixramp DB</div> <div class="input-group-text bg-secondary text-light border-secondary">Mixramp DB</div>
@ -700,14 +723,12 @@
</div> </div>
</div> </div>
<div class="modal fade" id="modalAbout" tabindex="-1" role="dialog"> <div class="modal fade" id="modalAbout" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="aboutLabel"><span class="material-icons title-icon">play_circle_outline</span> About</h5> <h5 class="modal-title" id="aboutLabel"><span class="material-icons title-icon">play_circle_outline</span> About</h5>
<button type="button" class="close" data-dismiss="modal"> <button type="button" class="close" data-dismiss="modal">&times;</button>
<span>&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<h4><a class="text-success" rel="noreferrer" href="https://github.com/jcorporation/ympd">myMPD</a>&nbsp;&ndash;&nbsp;<small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4> <h4><a class="text-success" rel="noreferrer" href="https://github.com/jcorporation/ympd">myMPD</a>&nbsp;&ndash;&nbsp;<small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4>
@ -723,8 +744,8 @@
<tr><th>Artists</th><td id="mpdstats_artists"></td></tr> <tr><th>Artists</th><td id="mpdstats_artists"></td></tr>
<tr><th>Albums</th><td id="mpdstats_albums"></td></tr> <tr><th>Albums</th><td id="mpdstats_albums"></td></tr>
<tr><th>Songs</th><td id="mpdstats_songs"></td></tr> <tr><th>Songs</th><td id="mpdstats_songs"></td></tr>
<tr><th>DB Play Time</th><td id="mpdstats_dbplaytime"></td></tr> <tr><th>DB Play Time</th><td id="mpdstats_dbPlaytime"></td></tr>
<tr><th>DB Updated</th><td id="mpdstats_dbupdated"></td></tr> <tr><th>DB Updated</th><td id="mpdstats_dbUpdated"></td></tr>
<tr><td colspan="2" class="pt-3"><h5>Play Statistics</h5></td></tr> <tr><td colspan="2" class="pt-3"><h5>Play Statistics</h5></td></tr>
<tr><th>Uptime</th><td id="mpdstats_uptime"></td></tr> <tr><th>Uptime</th><td id="mpdstats_uptime"></td></tr>
<tr><th>Play Time</th><td id="mpdstats_playtime"></td></tr> <tr><th>Play Time</th><td id="mpdstats_playtime"></td></tr>
@ -737,14 +758,12 @@
</div> </div>
</div> </div>
<div class="modal fade" id="modalSaveQueue" tabindex="-1" role="dialog"> <div class="modal fade" id="modalSaveQueue" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title"><span class="material-icons title-icon">save</span> Save Queue</h5> <h5 class="modal-title"><span class="material-icons title-icon">save</span> Save Queue</h5>
<button type="button" class="close" data-dismiss="modal"> <button type="button" class="close" data-dismiss="modal">&times;</button>
<span>&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="needs-validation" id="saveQueueFrm" novalidate> <form class="needs-validation" id="saveQueueFrm" novalidate>
@ -765,27 +784,30 @@
</div> </div>
</div> </div>
<div class="modal fade" id="modalSongDetails" tabindex="-1" role="dialog"> <div class="modal fade" id="modalSongDetails" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title"><span class="material-icons title-icon">music_note</span> Song Details</h5> <h5 class="modal-title"><span class="material-icons title-icon">music_note</span> Song Details</h5>
<button type="button" class="close" data-dismiss="modal"> <button type="button" class="close" data-dismiss="modal">&times;</button>
<span>&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="album-cover"></div> <div class="album-cover"></div>
<h1></h1> <h1></h1>
<table class="table"> <table class="table table-sm">
<tbody> <tbody>
<tr data-name="artist"><td>Artist</td><td></td></tr> <tr data-name="artist"><th>Artist</th><td></td></tr>
<tr data-name="album"><td>Album</td><td></td></tr> <tr data-name="album"><th>Album</th><td></td></tr>
<tr data-name="track"><td>Track</td><td></td></tr> <tr data-name="track"><th>Track</th><td></td></tr>
<tr data-name="albumartist"><td>Albumartist</td><td></td></tr> <tr data-name="albumartist"><th>Albumartist</th><td></td></tr>
<tr data-name="genre"><td>Genre</td><td></td></tr> <tr data-name="genre"><th>Genre</th><td></td></tr>
<tr data-name="date"><td>Date</td><td></td></tr> <tr data-name="date"><th>Date</th><td></td></tr>
<tr data-name="uri"><td>Uri</td><td></td></tr> <tr data-name="uri"><th>Uri</th><td></td></tr>
<tr class="stickers"><th colspan="2">Statistics</th></tr>
<tr class="stickers" data-name="playCount"><th>Play count</th><td></td></tr>
<tr class="stickers" data-name="skipCount"><th>Skip count</th><td></td></tr>
<tr class="stickers" data-name="lastPlayed"><th>Last played</th><td></td></tr>
<tr class="stickers" data-name="like"><th>Like</th><td></td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -796,7 +818,7 @@
</div> </div>
</div> </div>
<script src="js/bootstrap-native-v4.min.js"></script> <script src="/js/bootstrap-native-v4.min.js"></script>
<script src="js/mpd.js"></script> <script src="/js/mympd.js"></script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
<meta name="author" content="mail@jcgames.de"> <meta name="author" content="mail@jcgames.de">
<title>myMPD: Local Player</title> <title>myMPD: Local Player</title>
<link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/mpd.css" rel="stylesheet"> <link href="css/mympd.css" rel="stylesheet">
<link href="assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"> <link href="assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black"/> <meta name="apple-mobile-web-app-status-bar-style" content="black"/>

View File

@ -1,11 +1,11 @@
var CACHE = 'myMPD-cache-v3.5.0'; var CACHE = 'myMPD-cache-v4.0.0';
var urlsToCache = [ var urlsToCache = [
'/', '/',
'/player.html', '/player.html',
'/css/bootstrap.min.css', '/css/bootstrap.min.css',
'/css/mpd.css', '/css/mympd.css',
'/js/bootstrap-native-v4.min.js', '/js/bootstrap-native-v4.min.js',
'/js/mpd.js', '/js/mympd.js',
'/js/player.js', '/js/player.js',
'/assets/appicon-167.png', '/assets/appicon-167.png',
'/assets/appicon-192.png', '/assets/appicon-192.png',

View File

@ -7,16 +7,16 @@ then
echo "Minifying javascript" echo "Minifying javascript"
[ htdocs/js/player.js -nt dist/htdocs/js/player.min.js ] && \ [ htdocs/js/player.js -nt dist/htdocs/js/player.min.js ] && \
java -jar dist/buildtools/closure-compiler.jar htdocs/js/player.js > dist/htdocs/js/player.min.js java -jar dist/buildtools/closure-compiler.jar htdocs/js/player.js > dist/htdocs/js/player.min.js
[ htdocs/js/mpd.js -nt dist/htdocs/js/mpd.min.js ] && \ [ htdocs/js/mympd.js -nt dist/htdocs/js/mympd.min.js ] && \
java -jar dist/buildtools/closure-compiler.jar htdocs/js/mpd.js > dist/htdocs/js/mpd.min.js java -jar dist/buildtools/closure-compiler.jar htdocs/js/mympd.js > dist/htdocs/js/mympd.min.js
[ htdocs/sw.js -nt dist/htdocs/sw.min.js ] && \ [ htdocs/sw.js -nt dist/htdocs/sw.min.js ] && \
java -jar dist/buildtools/closure-compiler.jar htdocs/sw.js > dist/htdocs/sw.min.js java -jar dist/buildtools/closure-compiler.jar htdocs/sw.js > dist/htdocs/sw.min.js
else else
echo "dist/buildtools/closure-compiler.jar not found, using non-minified files" echo "dist/buildtools/closure-compiler.jar not found, using non-minified files"
[ htdocs/js/player.js -nt dist/htdocs/js/player.min.js ] && \ [ htdocs/js/player.js -nt dist/htdocs/js/player.min.js ] && \
cp htdocs/js/player.js dist/htdocs/js/player.min.js cp htdocs/js/player.js dist/htdocs/js/player.min.js
[ htdocs/js/mpd.js -nt dist/htdocs/js/mpd.min.js ] && \ [ htdocs/js/mympd.js -nt dist/htdocs/js/mympd.min.js ] && \
cp htdocs/js/mpd.js dist/htdocs/js/mpd.min.js cp htdocs/js/mympd.js dist/htdocs/js/mympd.min.js
[ htdocs/sw.js -nt dist/htdocs/sw.min.js ] && \ [ htdocs/sw.js -nt dist/htdocs/sw.min.js ] && \
cp htdocs/sw.js dist/htdocs/sw.min.js cp htdocs/sw.js dist/htdocs/sw.min.js
fi fi
@ -24,19 +24,19 @@ fi
if [ -f dist/buildtools/closure-stylesheets.jar ] && [ "$java" != "" ] if [ -f dist/buildtools/closure-stylesheets.jar ] && [ "$java" != "" ]
then then
echo "Minifying stylesheets" echo "Minifying stylesheets"
[ htdocs/css/mpd.css -nt dist/htdocs/css/mpd.min.css ] && \ [ htdocs/css/mympd.css -nt dist/htdocs/css/mympd.min.css ] && \
java -jar dist/buildtools/closure-stylesheets.jar --allow-unrecognized-properties htdocs/css/mpd.css > dist/htdocs/css/mpd.min.css java -jar dist/buildtools/closure-stylesheets.jar --allow-unrecognized-properties htdocs/css/mympd.css > dist/htdocs/css/mympd.min.css
else else
echo "dist/buildtools/closure-stylesheets.jar not found, using non-minified files" echo "dist/buildtools/closure-stylesheets.jar not found, using non-minified files"
[ htdocs/css/mpd.css -nt dist/htdocs/css/mpd.min.css ] && \ [ htdocs/css/mympd.css -nt dist/htdocs/css/mympd.min.css ] && \
cp htdocs/css/mpd.css dist/htdocs/css/mpd.min.css cp htdocs/css/mympd.css dist/htdocs/css/mympd.min.css
fi fi
echo "Replacing javascript and stylesheets with minified files" echo "Replacing javascript and stylesheets with minified files"
sed -e 's/mpd\.css/mpd\.min\.css/' -e 's/mpd\.js/mpd\.min\.js/' htdocs/index.html > dist/htdocs/index.html sed -e 's/mympd\.css/mympd\.min\.css/' -e 's/mympd\.js/mympd\.min\.js/' htdocs/index.html > dist/htdocs/index.html
sed -e 's/mpd\.css/mpd\.min\.css/' -e 's/player\.js/player\.min\.js/' htdocs/player.html > dist/htdocs/player.html sed -e 's/mympd\.css/mympd\.min\.css/' -e 's/player\.js/player\.min\.js/' htdocs/player.html > dist/htdocs/player.html
sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/mpd\.js/mpd\.min\.js/' -e 's/player\.js/player\.min\.js/' dist/htdocs/sw.min.js sed -i -e 's/mympd\.css/mympd\.min\.css/' -e 's/mympd\.js/mympd\.min\.js/' -e 's/player\.js/player\.min\.js/' dist/htdocs/sw.min.js
sed -i -e 's/\/sw\.js/\/sw\.min\.js/' dist/htdocs/js/mpd.min.js sed -i -e 's/\/sw\.js/\/sw\.min\.js/' dist/htdocs/js/mympd.min.js
echo "Minifying html" echo "Minifying html"
perl -i -pe 's/^\s*//gm; s/\s*$//gm' dist/htdocs/index.html perl -i -pe 's/^\s*//gm; s/\s*$//gm' dist/htdocs/index.html
perl -i -pe 's/^\s*//gm; s/\s*$//gm' dist/htdocs/player.html perl -i -pe 's/^\s*//gm; s/\s*$//gm' dist/htdocs/player.html
@ -55,21 +55,33 @@ sudo chown nobody /var/lib/mympd
echo "Trying to link musicdir to library" echo "Trying to link musicdir to library"
if [ -f /etc/mpd.conf ] if [ -f /etc/mpd.conf ]
then then
LIBRARY=$(grep music /etc/mpd.conf | awk {'print $2'} | sed -e 's/"//g') LIBRARY=$(grep ^music_directory /etc/mpd.conf | awk {'print $2'} | sed -e 's/"//g')
[ "$LIBRARY" != "" ] && [ ! -e /usr/share/mympd/htdocs/library ] && ln -s "$LIBRARY" /usr/share/mympd/htdocs/library [ "$LIBRARY" != "" ] && [ ! -e /usr/share/mympd/htdocs/library ] && sudo ln -s "$LIBRARY" /usr/share/mympd/htdocs/library
else else
echo "/etc/mpd.conf not found, you must link your musicdir manually to /usr/share/mympd/htdocs/library" echo "/etc/mpd.conf not found, you must link your music_directory manually to /usr/share/mympd/htdocs/library"
fi fi
echo "Creating dir for cover pictures"
[ -d /usr/share/mympd/htdocs/pics ] || mkdir /usr/share/mympd/htdocs/pics
echo "Installing systemd service" echo "Installing systemd service"
if [ -d /etc/systemd/system ] if [ -d /etc/systemd/system ]
then then
if [ contrib/mympd.service -nt /etc/systemd/system/mympd.service ] if [ contrib/mympd.service -nt /etc/systemd/system/mympd.service ]
then then
sudo cp -v contrib/mympd.service /etc/systemd/system/ sudo cp contrib/mympd.service /etc/systemd/system/
systemctl daemon-reload sudo systemctl daemon-reload
fi fi
systemctl enable mympd sudo systemctl enable mympd
fi
if [ -f /etc/mympd/mympd.conf ]
then
echo "/etc/mympd/mympd.conf already exists"
cp contrib/mympd.conf /etc/mympd/mympd.conf.dist
else
[ -d /etc/mympd ] || mkdir /etc/mympd
cp contrib/mympd.conf /etc/mympd/mympd.conf
fi fi
if [ -d /etc/mympd/ssl ] if [ -d /etc/mympd/ssl ]
@ -81,4 +93,4 @@ else
fi fi
echo "myMPD installed" echo "myMPD installed"
echo "Edit /etc/mympd/mympd.conf before starting mympd" echo "Edit /etc/mympd/mympd.conf before starting myMPD"

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,11 @@
return len; \ return len; \
} while (0) } while (0)
#define LOG_ERROR_AND_RECOVER(X) do { \
fprintf(stderr, "MPD X: %s\n", mpd_connection_get_error_message(mpd.conn)); \
if (!mpd_connection_clear_error(mpd.conn)) \
mpd.conn_state = MPD_FAILURE; \
} while (0)
#define MAX_SIZE 1024 * 100 #define MAX_SIZE 1024 * 100
#define MAX_ELEMENTS_PER_PAGE 100 #define MAX_ELEMENTS_PER_PAGE 100
@ -44,54 +49,56 @@
#define GEN_ENUM(X) X, #define GEN_ENUM(X) X,
#define GEN_STR(X) #X, #define GEN_STR(X) #X,
#define MPD_CMDS(X) \ #define MPD_CMDS(X) \
X(MPD_API_GET_QUEUE) \ X(MPD_API_QUEUE_CLEAR) \
X(MPD_API_GET_FILESYSTEM) \ X(MPD_API_QUEUE_CROP) \
X(MPD_API_ADD_TRACK_AFTER) \ X(MPD_API_QUEUE_SAVE) \
X(MPD_API_ADD_TRACK) \ X(MPD_API_QUEUE_LIST) \
X(MPD_API_ADD_PLAY_TRACK) \ X(MPD_API_QUEUE_SEARCH) \
X(MPD_API_REPLACE_TRACK) \ X(MPD_API_QUEUE_RM_TRACK) \
X(MPD_API_ADD_PLAYLIST) \ X(MPD_API_QUEUE_RM_RANGE) \
X(MPD_API_REPLACE_PLAYLIST) \ X(MPD_API_QUEUE_MOVE_TRACK) \
X(MPD_API_RM_PLAYLIST_TRACK) \ X(MPD_API_QUEUE_ADD_TRACK_AFTER) \
X(MPD_API_QUEUE_ADD_TRACK) \
X(MPD_API_QUEUE_ADD_PLAY_TRACK) \
X(MPD_API_QUEUE_REPLACE_TRACK) \
X(MPD_API_QUEUE_ADD_PLAYLIST) \
X(MPD_API_QUEUE_REPLACE_PLAYLIST) \
X(MPD_API_QUEUE_SHUFFLE) \
X(MPD_API_PLAYLIST_RM) \
X(MPD_API_PLAYLIST_CLEAR) \ X(MPD_API_PLAYLIST_CLEAR) \
X(MPD_API_PLAYLIST_RENAME) \ X(MPD_API_PLAYLIST_RENAME) \
X(MPD_API_PLAYLIST_MOVE_TRACK) \ X(MPD_API_PLAYLIST_MOVE_TRACK) \
X(MPD_API_ADD_TO_PLAYLIST) \ X(MPD_API_PLAYLIST_ADD_TRACK) \
X(MPD_API_PLAY_TRACK) \ X(MPD_API_PLAYLIST_RM_TRACK) \
X(MPD_API_SAVE_QUEUE) \ X(MPD_API_PLAYLIST_LIST) \
X(MPD_API_RM_TRACK) \ X(MPD_API_PLAYLIST_CONTENT_LIST) \
X(MPD_API_RM_RANGE) \ X(MPD_API_DATABASE_SEARCH_ADD_PLAYLIST) \
X(MPD_API_QUEUE_CLEAR) \ X(MPD_API_DATABASE_SEARCH_ADD_QUEUE) \
X(MPD_API_QUEUE_CROP) \ X(MPD_API_DATABASE_SEARCH) \
X(MPD_API_MOVE_TRACK) \ X(MPD_API_DATABASE_UPDATE) \
X(MPD_API_SEARCH_QUEUE) \ X(MPD_API_DATABASE_FILESYSTEM_LIST) \
X(MPD_API_SEARCH_ADD_PLIST) \ X(MPD_API_DATABASE_ARTISTALBUM_LIST) \
X(MPD_API_SEARCH_ADD) \ X(MPD_API_DATABASE_ARTISTALBUMTITLE_LIST) \
X(MPD_API_SEARCH) \ X(MPD_API_DATABASE_ARTIST_LIST) \
X(MPD_API_SEND_MESSAGE) \ X(MPD_API_DATABASE_STATS) \
X(MPD_API_SET_VOLUME) \ X(MPD_API_DATABASE_SONGDETAILS) \
X(MPD_API_SET_PAUSE) \ X(MPD_API_PLAYER_PLAY_TRACK) \
X(MPD_API_SET_PLAY) \ X(MPD_API_PLAYER_VOLUME) \
X(MPD_API_SET_STOP) \ X(MPD_API_PLAYER_PAUSE) \
X(MPD_API_SET_SEEK) \ X(MPD_API_PLAYER_PLAY) \
X(MPD_API_SET_NEXT) \ X(MPD_API_PLAYER_STOP) \
X(MPD_API_SET_PREV) \ X(MPD_API_PLAYER_SEEK) \
X(MPD_API_UPDATE_DB) \ X(MPD_API_PLAYER_NEXT) \
X(MPD_API_GET_OUTPUTS) \ X(MPD_API_PLAYER_PREV) \
X(MPD_API_TOGGLE_OUTPUT) \ X(MPD_API_PLAYER_OUTPUT_LIST) \
X(MPD_API_SEND_SHUFFLE) \ X(MPD_API_PLAYER_TOGGLE_OUTPUT) \
X(MPD_API_GET_STATS) \ X(MPD_API_PLAYER_CURRENT_SONG) \
X(MPD_API_GET_PLAYLISTS) \ X(MPD_API_PLAYER_STATE) \
X(MPD_API_GET_PLAYLIST_LIST) \ X(MPD_API_SETTINGS_GET) \
X(MPD_API_RM_PLAYLIST) \ X(MPD_API_SETTINGS_SET) \
X(MPD_API_GET_ARTISTALBUMS) \ X(MPD_API_MESSAGE_SEND) \
X(MPD_API_GET_ARTISTALBUMTITLES) \
X(MPD_API_GET_ARTISTS) \
X(MPD_API_GET_CURRENT_SONG) \
X(MPD_API_GET_SONGDETAILS) \
X(MPD_API_WELCOME) \ X(MPD_API_WELCOME) \
X(MPD_API_GET_SETTINGS) \ X(MPD_API_LIKE) \
X(MPD_API_SET_SETTINGS) \
X(MPD_API_UNKNOWN) X(MPD_API_UNKNOWN)
enum mpd_cmd_ids { enum mpd_cmd_ids {
@ -116,43 +123,59 @@ struct t_mpd {
int song_id; int song_id;
int next_song_id; int next_song_id;
int last_song_id;
unsigned queue_version; unsigned queue_version;
unsigned queue_length;
int timeout;
int last_update_sticker_song_id;
} mpd; } mpd;
typedef struct { typedef struct {
int mpdport; long mpdport;
const char* mpdhost; const char* mpdhost;
const char* mpdpass; const char* mpdpass;
const char* webport; const char* webport;
bool ssl; bool ssl;
const char* sslport; const char* sslport;
const char* sslcert; const char* sslcert;
const char* sslkey; const char* sslkey;
const char* user; const char* user;
int streamport; long streamport;
const char* coverimage; const char* coverimage;
const char* statefile; const char* statefile;
} configuration; bool stickers;
bool mixramp;
} t_config;
configuration config; t_config config;
typedef struct {
long playCount;
long skipCount;
long lastPlayed;
long like;
} t_sticker;
static int is_websocket(const struct mg_connection *nc) { static int is_websocket(const struct mg_connection *nc) {
return nc->flags & MG_F_IS_WEBSOCKET; return nc->flags & MG_F_IS_WEBSOCKET;
} }
struct t_mpd_client_session { void mympd_idle(struct mg_mgr *sm, int timeout);
int song_id; void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask);
int next_song_id;
unsigned queue_version;
};
void mympd_poll(struct mg_mgr *s);
void callback_mympd(struct mg_connection *nc, const struct mg_str msg); void callback_mympd(struct mg_connection *nc, const struct mg_str msg);
int mympd_close_handler(struct mg_connection *c); void mympd_notify(struct mg_mgr *s);
int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); void mympd_count_song_id(int song_id, char *name, int value);
void mympd_count_song_uri(const char *uri, char *name, int value);
void mympd_like_song_uri(const char *uri, int value);
void mympd_last_played_song_uri(const char *uri);
void mympd_last_played_song_id(int song_id);
void mympd_get_sticker(const char *uri, t_sticker *sticker);
int mympd_get_updatedb_state(char *buffer);
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_outputs(char *buffer);
int mympd_put_current_song(char *buffer); int mympd_put_current_song(char *buffer);
int mympd_put_queue(char *buffer, unsigned int offset); int mympd_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length);
int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter); int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter);
int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr);
int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr); int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr);
@ -169,4 +192,3 @@ int mympd_put_songdetails(char *buffer, char *uri);
int mympd_queue_crop(char *buffer); int mympd_queue_crop(char *buffer);
void mympd_disconnect(); void mympd_disconnect();
#endif #endif

View File

@ -36,7 +36,7 @@
static sig_atomic_t s_signal_received = 0; static sig_atomic_t s_signal_received = 0;
static struct mg_serve_http_opts s_http_server_opts; static struct mg_serve_http_opts s_http_server_opts;
char s_redirect[250];
static void signal_handler(int sig_num) { static void signal_handler(int sig_num) {
signal(sig_num, signal_handler); // Reinstantiate signal handler signal(sig_num, signal_handler); // Reinstantiate signal handler
@ -48,8 +48,9 @@ static void handle_api(struct mg_connection *nc, struct http_message *hm) {
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n"); mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n");
char buf[1000] = {0}; char buf[1000] = {0};
memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); int len = sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len;
struct mg_str d = {buf, strlen(buf)}; memcpy(buf, hm->body.p, len);
struct mg_str d = {buf, len};
callback_mympd(nc, d); callback_mympd(nc, d);
if (!is_websocket(nc)) if (!is_websocket(nc))
@ -60,35 +61,32 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
switch(ev) { switch(ev) {
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
#ifdef DEBUG #ifdef DEBUG
fprintf(stdout,"New Websocket connection\n"); fprintf(stderr, "New Websocket connection\n");
#endif #endif
struct mg_str d = {(char *) "{\"cmd\": \"MPD_API_WELCOME\"}", 25 }; struct mg_str d = mg_mk_str("{\"cmd\": \"MPD_API_WELCOME\"}");
callback_mympd(nc, d); callback_mympd(nc, d);
break; break;
} }
case MG_EV_HTTP_REQUEST: { case MG_EV_HTTP_REQUEST: {
struct http_message *hm = (struct http_message *) ev_data; struct http_message *hm = (struct http_message *) ev_data;
#ifdef DEBUG #ifdef DEBUG
printf("HTTP request: %.*s\n", hm->uri.len, hm->uri.p); fprintf(stderr, "HTTP request: %.*s\n", hm->uri.len, hm->uri.p);
#endif #endif
if (mg_vcmp(&hm->uri, "/api") == 0) { if (mg_vcmp(&hm->uri, "/api") == 0)
handle_api(nc, hm); handle_api(nc, hm);
} else
else { mg_serve_http(nc, hm, s_http_server_opts);
mg_serve_http(nc, hm, s_http_server_opts);
}
break; break;
} }
case MG_EV_CLOSE: { case MG_EV_CLOSE: {
if (is_websocket(nc)) { if (is_websocket(nc)) {
#ifdef DEBUG #ifdef DEBUG
printf("Websocket connection closed\n"); fprintf(stderr, "Websocket connection closed\n");
#endif #endif
mympd_close_handler(nc);
} }
else { else {
#ifdef DEBUG #ifdef DEBUG
fprintf(stdout,"HTTP Close\n"); fprintf(stderr,"HTTP connection closed\n");
#endif #endif
} }
break; break;
@ -99,6 +97,10 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
static void ev_handler_http(struct mg_connection *nc_http, int ev, void *ev_data) { static void ev_handler_http(struct mg_connection *nc_http, int ev, void *ev_data) {
switch(ev) { switch(ev) {
case MG_EV_HTTP_REQUEST: { case MG_EV_HTTP_REQUEST: {
struct http_message *hm = (struct http_message *) ev_data;
struct mg_str *host_hdr = mg_get_http_header(hm, "Host");
char s_redirect[250];
snprintf(s_redirect, 250, "https://%.*s:%s/", host_hdr->len, host_hdr->p, config.sslport);
printf("Redirecting to %s\n", s_redirect); printf("Redirecting to %s\n", s_redirect);
mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL));
break; break;
@ -107,15 +109,17 @@ static void ev_handler_http(struct mg_connection *nc_http, int ev, void *ev_data
} }
static int inihandler(void* user, const char* section, const char* name, const char* value) { static int inihandler(void* user, const char* section, const char* name, const char* value) {
configuration* p_config = (configuration*)user; t_config* p_config = (t_config*)user;
char *crap;
#define MATCH(n) strcmp(name, n) == 0 #define MATCH(n) strcmp(name, n) == 0
if (MATCH("mpdhost")) if (MATCH("mpdhost"))
p_config->mpdhost = strdup(value); p_config->mpdhost = strdup(value);
else if (MATCH("mpdhost")) else if (MATCH("mpdhost"))
p_config->mpdhost = strdup(value); p_config->mpdhost = strdup(value);
else if (MATCH("mpdport")) else if (MATCH("mpdport"))
p_config->mpdport = atoi(value); p_config->mpdport = strtol(value, &crap, 10);
else if (MATCH("mpdhost")) else if (MATCH("mpdhost"))
p_config->mpdhost = strdup(value); p_config->mpdhost = strdup(value);
else if (MATCH("mpdpass")) else if (MATCH("mpdpass"))
@ -134,11 +138,21 @@ static int inihandler(void* user, const char* section, const char* name, const c
else if (MATCH("user")) else if (MATCH("user"))
p_config->user = strdup(value); p_config->user = strdup(value);
else if (MATCH("streamport")) else if (MATCH("streamport"))
p_config->streamport = atoi(value); p_config->streamport = strtol(value, &crap, 10);
else if (MATCH("coverimage")) else if (MATCH("coverimage"))
p_config->coverimage = strdup(value); p_config->coverimage = strdup(value);
else if (MATCH("statefile")) else if (MATCH("statefile"))
p_config->statefile = strdup(value); p_config->statefile = strdup(value);
else if (MATCH("stickers"))
if (strcmp(value, "true") == 0)
p_config->stickers = true;
else
p_config->stickers = false;
else if (MATCH("mixramp"))
if (strcmp(value, "true") == 0)
p_config->mixramp = true;
else
p_config->mixramp = false;
else else
return 0; /* unknown section/name, error */ return 0; /* unknown section/name, error */
@ -149,14 +163,9 @@ int main(int argc, char **argv) {
struct mg_mgr mgr; struct mg_mgr mgr;
struct mg_connection *nc; struct mg_connection *nc;
struct mg_connection *nc_http; struct mg_connection *nc_http;
unsigned int current_timer = 0, last_timer = 0;
struct mg_bind_opts bind_opts; struct mg_bind_opts bind_opts;
const char *err; const char *err;
char hostname[1024];
hostname[1023] = '\0';
gethostname(hostname, 1023);
//defaults //defaults
config.mpdhost = "127.0.0.1"; config.mpdhost = "127.0.0.1";
config.mpdport = 6600; config.mpdport = 6600;
@ -170,23 +179,34 @@ int main(int argc, char **argv) {
config.streamport = 8000; config.streamport = 8000;
config.coverimage = "folder.jpg"; config.coverimage = "folder.jpg";
config.statefile = "/var/lib/mympd/mympd.state"; config.statefile = "/var/lib/mympd/mympd.state";
config.stickers = true;
config.mixramp = true;
mpd.timeout = 3000;
mpd.last_update_sticker_song_id = -1;
mpd.last_song_id = -1;
if (argc == 2) { if (argc == 2) {
printf("Parsing config file: %s\n", argv[1]);
if (ini_parse(argv[1], inihandler, &config) < 0) { if (ini_parse(argv[1], inihandler, &config) < 0) {
printf("Can't load '%s'\n", argv[1]); printf("Can't load config file \"%s\"\n", argv[1]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
else { else {
fprintf(stdout, "myMPD %s\n" printf("myMPD %s\n"
"Copyright (C) 2018 Juergen Mang <mail@jcgames.de>\n" "Copyright (C) 2018 Juergen Mang <mail@jcgames.de>\n"
"https://github.com/jcorporation/myMPD\n" "https://github.com/jcorporation/myMPD\n"
"Built " __DATE__ " "__TIME__"\n\n", "Built " __DATE__ " "__TIME__"\n\n"
MYMPD_VERSION); "Usage: %s /path/to/mympd.conf\n",
printf("Usage: %s /path/to/mympd.conf\n", argv[0]); MYMPD_VERSION,
argv[0]
);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
printf("Starting myMPD %s\n", MYMPD_VERSION);
signal(SIGTERM, signal_handler); signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0);
@ -195,32 +215,32 @@ int main(int argc, char **argv) {
mg_mgr_init(&mgr, NULL); mg_mgr_init(&mgr, NULL);
if (config.ssl == true) { if (config.ssl == true) {
snprintf(s_redirect, 200, "https://%s:%s/", hostname, config.sslport);
nc_http = mg_bind(&mgr, config.webport, ev_handler_http); nc_http = mg_bind(&mgr, config.webport, ev_handler_http);
if (nc_http == NULL) { if (nc_http == NULL) {
fprintf(stderr, "Error starting server on port %s\n", config.webport ); printf("Error listening on port %s\n", config.webport);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
memset(&bind_opts, 0, sizeof(bind_opts)); memset(&bind_opts, 0, sizeof(bind_opts));
bind_opts.ssl_cert = config.sslcert; bind_opts.ssl_cert = config.sslcert;
bind_opts.ssl_key = config.sslkey; bind_opts.ssl_key = config.sslkey;
bind_opts.error_string = &err; bind_opts.error_string = &err;
nc = mg_bind_opt(&mgr, config.sslport, ev_handler, bind_opts); nc = mg_bind_opt(&mgr, config.sslport, ev_handler, bind_opts);
if (nc == NULL) { if (nc == NULL) {
fprintf(stderr, "Error starting server on port %s: %s\n", config.sslport, err); printf("Error listening on port %s: %s\n", config.sslport, err);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
else { else {
nc = mg_bind(&mgr, config.webport, ev_handler); nc = mg_bind(&mgr, config.webport, ev_handler);
if (nc == NULL) { if (nc == NULL) {
fprintf(stderr, "Error starting server on port %s\n", config.webport ); printf("Error listening on port %s\n", config.webport);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
if (config.user != NULL) { if (config.user != NULL) {
printf("Droping privileges\n"); printf("Droping privileges to %s\n", config.user);
struct passwd *pw; struct passwd *pw;
if ((pw = getpwnam(config.user)) == NULL) { if ((pw = getpwnam(config.user)) == NULL) {
printf("Unknown user\n"); printf("Unknown user\n");
@ -238,9 +258,9 @@ int main(int argc, char **argv) {
} }
if (getuid() == 0) { if (getuid() == 0) {
printf("myMPD should not be run with root privileges\n"); printf("myMPD should not be run with root privileges\n");
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (config.ssl == true) if (config.ssl == true)
@ -250,17 +270,13 @@ int main(int argc, char **argv) {
s_http_server_opts.document_root = SRC_PATH; s_http_server_opts.document_root = SRC_PATH;
s_http_server_opts.enable_directory_listing = "no"; s_http_server_opts.enable_directory_listing = "no";
printf("myMPD started on http port %s\n", config.webport); printf("Listening on http port %s\n", config.webport);
if (config.ssl == true) if (config.ssl == true)
printf("myMPD started on ssl port %s\n", config.sslport); printf("Listening on ssl port %s\n", config.sslport);
while (s_signal_received == 0) { while (s_signal_received == 0) {
mg_mgr_poll(&mgr, 200); mg_mgr_poll(&mgr, 100);
current_timer = time(NULL); mympd_idle(&mgr, 0);
if (current_timer - last_timer) {
last_timer = current_timer;
mympd_poll(&mgr);
}
} }
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
mympd_disconnect(); mympd_disconnect();