From 54a14277c8afe34d61153171b28cdc9fc08609f6 Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 19:56:24 +0100 Subject: [PATCH 01/11] Fix: show lastPlayed in song details --- htdocs/js/mympd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/js/mympd.js b/htdocs/js/mympd.js index 9fab572..54df2b0 100644 --- a/htdocs/js/mympd.js +++ b/htdocs/js/mympd.js @@ -1482,7 +1482,7 @@ function parseSongDetails(obj) { songDetails += '<tr><th colspan="2">Statistics</th></tr>' + '<tr><th>Play count</th><td>' + obj.data.playCount + '</td></tr>' + '<tr><th>Skip count</th><td>' + obj.data.skipCount + '</td></tr>' + - '<tr><th>Last played</th><td>' + (obj.data.lastPlayed == 0 ? 'never' : new Date(value * 1000).toUTCString()) + '</td></tr>' + + '<tr><th>Last played</th><td>' + (obj.data.lastPlayed == 0 ? 'never' : new Date(obj.data.lastPlayed * 1000).toUTCString()) + '</td></tr>' + '<tr><th>Like</th><td>' + like + '</td></tr>'; } From 77be445fc1eb7b13a9ad03747a7f2ea8f892b765 Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 19:59:08 +0100 Subject: [PATCH 02/11] Fix: bump version to 4.2.1 --- CMakeLists.txt | 2 +- PKGBUILD | 2 +- contrib/myMPD.spec | 9 +++++---- debian/changelog | 4 ++-- htdocs/sw.js | 2 +- mkdebian.sh | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6a7d76..664d075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project (mympd C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") set(CPACK_PACKAGE_VERSION_MAJOR "4") set(CPACK_PACKAGE_VERSION_MINOR "2") -set(CPACK_PACKAGE_VERSION_PATCH "0") +set(CPACK_PACKAGE_VERSION_PATCH "1") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "/usr/share/${PROJECT_NAME}/htdocs") diff --git a/PKGBUILD b/PKGBUILD index a689af7..2156840 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -5,7 +5,7 @@ pkgname=mympd _pkgname=myMPD -pkgver=4.2.0 +pkgver=4.2.1 pkgrel=1 pkgdesc="A standalone MPD Web GUI based on YMPD - Default port set to 80" arch=('x86_64' 'armv7h' 'aarch64') diff --git a/contrib/myMPD.spec b/contrib/myMPD.spec index 9b7da3f..bd899a0 100644 --- a/contrib/myMPD.spec +++ b/contrib/myMPD.spec @@ -4,13 +4,13 @@ # (c) 2018 Juergen Mang <mail@jcgames.de Name: myMPD -Version: 4.2.0 +Version: 4.2.1 Release: 0 License: GPL-2.0 Group: Productivity/Multimedia/Sound/Players Summary: Standalone webclient for mpd Url: https://github.com/jcorporation/myMPD -Source: https://github.com/jcorporation/myMPD/archive/v4.2.0.zip +Source: https://github.com/jcorporation/myMPD/archive/v4.2.1.zip BuildRequires: gcc BuildRequires: cmake BuildRequires: unzip @@ -50,9 +50,10 @@ then chown -R mympd.mympd /var/lib/mympd fi -if [ -d /usr/lib/systemd/ ] +SYSTEMCTL=$(which systemctl > /dev/null 2>&1) +if [ "$?" == "0" ] then - [ -d /usr/lib/systemd/system ] || sudo mkdir /usr/lib/systemd/system + [ -d /usr/lib/systemd/system ] || sudo mkdir -p /usr/lib/systemd/system cp /usr/share/mympd/mympd.service /usr/lib/systemd/system/ systemctl daemon-reload systemctl enable mympd diff --git a/debian/changelog b/debian/changelog index 0a70634..2d2c8bf 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -mympd (4.2.0-1) stable; urgency=medium +mympd (4.2.1-1) stable; urgency=medium * Release from master - -- Juergen Mang <mail@jcgames.de> Mon, 17 Sep 2018 17:59:00 +0200 + -- Juergen Mang <mail@jcgames.de> Thu, 20 Sep 2018 19:32:00 +0200 diff --git a/htdocs/sw.js b/htdocs/sw.js index 3e63157..cce5f88 100644 --- a/htdocs/sw.js +++ b/htdocs/sw.js @@ -1,4 +1,4 @@ -var CACHE = 'myMPD-cache-v4.2.0'; +var CACHE = 'myMPD-cache-v4.2.1'; var urlsToCache = [ '/', '/player.html', diff --git a/mkdebian.sh b/mkdebian.sh index 45e05dc..5214536 100755 --- a/mkdebian.sh +++ b/mkdebian.sh @@ -1,4 +1,4 @@ #!/bin/bash ./mkclean.sh -tar -czvf ../mympd_4.2.0.orig.tar.gz * +tar -czvf ../mympd_4.2.1.orig.tar.gz * dpkg-buildpackage -rfakeroot From 82029ce39dbc5692116659598b3428f59e1d0fa0 Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 21:13:04 +0100 Subject: [PATCH 03/11] Fix: dont't remove user/group on uninstall Fix: moved pics directory to /var/lib/mympd --- CMakeLists.txt | 3 +-- contrib/archlinux.install | 20 +++++++------------- contrib/myMPD.spec | 33 ++++++++++++++------------------- debian/postinst | 9 ++++++--- debian/postrm | 12 ++---------- debian/rules | 1 + 6 files changed, 31 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 664d075..0a5bff0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,5 +54,4 @@ install(FILES dist/htdocs/js/mympd.min.js DESTINATION share/${PROJECT_NAME}/htdo install(FILES dist/htdocs/css/bootstrap.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 DESTINATION share/${PROJECT_NAME}/htdocs/pics) -install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/) +install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/pics) diff --git a/contrib/archlinux.install b/contrib/archlinux.install index 6e10041..35963b7 100644 --- a/contrib/archlinux.install +++ b/contrib/archlinux.install @@ -27,6 +27,9 @@ post_upgrade() { echo "/etc/mpd.conf not found, you must link your musicdir manually to /usr/share/mympd/htdocs/library" fi + echo "Linking pics directory" + [ -e /usr/share/mympd/htdocs/pics ] || ln -s /var/lib/mympd/pics /usr/share/mympd/htdocs/ + # install systemd service echo "INFO: Installing systemd service" if [ -d /etc/systemd/system ] @@ -77,18 +80,9 @@ post_remove() { echo -e "\e[93mINFO\e[0m: /etc/mympd/mympd.conf backed up to /etc/mympd/mympd.conf.pacsave" fi - # unlink music dir - echo "unlinking musicdir to library" - [ -e /usr/share/mympd/htdocs/library ] && rm -v /usr/share/mympd/htdocs/library - - # remove mympd user and group - getent passwd mympd > /dev/null - echo -e "\e[93mWARNING\e[0m: Removing \e[1mmympd \e[0muser and group!" - [ "$?" != "2" ] && userdel -r mympd - getent group mympd > /dev/null - [ "$?" != "2" ] && groupdel mympd - # remove leftover directories - rmdir -v /usr/share/{mympd/htdocs/,mympd/} + rm -v -f /usr/share/mympd/htdocs/pics + rm -v -f /usr/share/mympd/htdocs/library + rmdir -v /usr/share/mympd/htdocs/ + rmdir -v /usr/share/mympd/ } - diff --git a/contrib/myMPD.spec b/contrib/myMPD.spec index bd899a0..0e67dbd 100644 --- a/contrib/myMPD.spec +++ b/contrib/myMPD.spec @@ -50,8 +50,7 @@ then chown -R mympd.mympd /var/lib/mympd fi -SYSTEMCTL=$(which systemctl > /dev/null 2>&1) -if [ "$?" == "0" ] +if [ -d /etc/systemd ] then [ -d /usr/lib/systemd/system ] || sudo mkdir -p /usr/lib/systemd/system cp /usr/share/mympd/mympd.service /usr/lib/systemd/system/ @@ -65,6 +64,8 @@ then [ "$LIBRARY" != "" ] && [ ! -e /usr/share/mympd/htdocs/library ] && ln -s "$LIBRARY" /usr/share/mympd/htdocs/library fi +[ -e /usr/share/mympd/htdocs/pics ] || ln -s /var/lib/mympd/pics /usr/share/mympd/htdocs/ + if [ ! -f /etc/mympd/mympd.conf ] then mv /etc/mympd/mympd.conf.dist /etc/mympd/mympd.conf @@ -75,28 +76,22 @@ fi /usr/share/mympd/crcert.sh %postun -if [ -f /usr/lib/systemd/system/mympd.service ] +if [ "$1" = "0" ] then - if `systemctl is-active --quiet mympd` + if [ -f /usr/lib/systemd/system/mympd.service ] then - echo "stopping mympd.service" && systemctl stop mympd + if `systemctl is-active --quiet mympd` + then + echo "stopping mympd.service" && systemctl stop mympd + fi + echo "disabling mympd.service" && systemctl disable mympd + rm -v -f /usr/lib/systemd/system/mympd.service + systemctl daemon-reload fi - echo "disabling mympd.service" && systemctl disable mympd - rm -v -f /usr/lib/systemd/system/mympd.service - systemctl daemon-reload + rm -v -f /usr/share/mympd/htdocs/pics + rm -v -f /usr/share/mympd/htdocs/library fi -rm -v -fr /var/lib/mympd -[ -e /usr/share/mympd/htdocs/library ] && rm -v /usr/share/mympd/htdocs/library -rmdir -v /usr/share/{mympd/htdocs/,mympd/} - -getent passwd mympd > /dev/null -echo "Removing mympd user and group" -[ "$?" != "2" ] && userdel -r mympd -getent group mympd > /dev/null -[ "$?" != "2" ] && groupdel mympd - - %files %defattr(-,root,root,-) %doc README.md LICENSE diff --git a/debian/postinst b/debian/postinst index 199efbc..ca6f8d6 100755 --- a/debian/postinst +++ b/debian/postinst @@ -6,7 +6,7 @@ getent group mympd > /dev/null getent passwd mympd > /dev/null [ "$?" = "2" ] && useradd -r mympd -g mympd -d /var/lib/mympd -s /usr/sbin/nologin -if ! [ $(stat -c '%U:%G' /var/lib/mympd/) == 'mympd:mympd' ] +if ! [ $(stat -c '%U:%G' /var/lib/mympd/) = 'mympd:mympd' ] then echo "Fixing ownership of /var/lib/mympd" chown -R mympd.mympd /var/lib/mympd @@ -21,10 +21,13 @@ else echo "/etc/mpd.conf not found, you must link your music_directory manually to /usr/share/mympd/htdocs/library" fi -if [ -d /usr/lib/systemd/ ] +echo "Linking pics directory" +[ -e /usr/share/mympd/htdocs/pics ] || ln -s /var/lib/mympd/pics /usr/share/mympd/htdocs/ + +if [ -d /etc/systemd ] then echo "Installing systemd service" - [ -d /usr/lib/systemd/system ] || mkdir /usr/lib/systemd/system + [ -d /usr/lib/systemd/system ] || mkdir -p /usr/lib/systemd/system if [ contrib/mympd.service -nt /usr/lib/systemd/system/mympd.service ] then cp contrib/mympd.service /usr/lib/systemd/system/ diff --git a/debian/postrm b/debian/postrm index d93626e..9ddda89 100755 --- a/debian/postrm +++ b/debian/postrm @@ -9,13 +9,5 @@ then systemctl daemon-reload fi -rm -v -fr /var/lib/mympd - -[ -e /usr/share/mympd/htdocs/library ] && rm -v /usr/share/mympd/htdocs/library -rmdir -v /usr/share/{mympd/htdocs/,mympd/} - -getent passwd mympd > /dev/null -echo "Removing mympd user and group" -[ "$?" != "2" ] && userdel -r mympd -getent group mympd > /dev/null -[ "$?" != "2" ] && groupdel mympd +rm -v -f /usr/share/mympd/htdocs/pics +rm -v -f /usr/share/mympd/htdocs/library diff --git a/debian/rules b/debian/rules index 132d6ee..20e2c14 100755 --- a/debian/rules +++ b/debian/rules @@ -16,6 +16,7 @@ binary-arch: cd $(BUILDDIR); cmake -P cmake_install.cmake mkdir debian/tmp/DEBIAN cp debian/postinst debian/tmp/DEBIAN + cp debian/postrm debian/tmp/DEBIAN dpkg-gencontrol -pmympd dpkg --build debian/tmp .. From c605bbb29fbaf085738a3742afa14c44ff3a802f Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 21:23:33 +0100 Subject: [PATCH 04/11] Fix: test of /var/lib/mympd ownership --- contrib/archlinux.install | 2 +- contrib/myMPD.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/archlinux.install b/contrib/archlinux.install index 35963b7..133983d 100644 --- a/contrib/archlinux.install +++ b/contrib/archlinux.install @@ -11,7 +11,7 @@ post_upgrade() { [ "$?" = "2" ] && useradd --system -d /var/lib/mympd -s /usr/sbin/nologin -g mympd mympd # fix ownership of /var/lib/mympd - if ! [ $(stat -c '%U:%G' /var/lib/mympd/) == 'mympd:mympd' ] + if ! [ $(stat -c '%U:%G' /var/lib/mympd/) = 'mympd:mympd' ] then echo "INFO: Fixing ownership of /var/lib/mympd" chown -R mympd.mympd /var/lib/mympd diff --git a/contrib/myMPD.spec b/contrib/myMPD.spec index 0e67dbd..f40de92 100644 --- a/contrib/myMPD.spec +++ b/contrib/myMPD.spec @@ -44,7 +44,7 @@ getent group mympd > /dev/null getent passwd mympd > /dev/null [ "$?" = "2" ] && useradd -r mympd -g mympd -d /var/lib/mympd -s /usr/sbin/nologin -if ! [ $(stat -c '%U:%G' /var/lib/mympd/) == 'mympd:mympd' ] +if ! [ $(stat -c '%U:%G' /var/lib/mympd/) = 'mympd:mympd' ] then echo "Fixing ownership of /var/lib/mympd" chown -R mympd.mympd /var/lib/mympd From 1df960d4461743c74af3143f3d65a3bb981f624e Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 21:24:17 +0100 Subject: [PATCH 05/11] Fix: update minified files --- dist/htdocs/js/mympd.min.js | 2 +- dist/htdocs/sw.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/htdocs/js/mympd.min.js b/dist/htdocs/js/mympd.min.js index e0d8e8e..13b13f6 100644 --- a/dist/htdocs/js/mympd.min.js +++ b/dist/htdocs/js/mympd.min.js @@ -93,7 +93,7 @@ function appendQueue(a,b,c){switch(a){case "song":case "dir":sendAPI({cmd:"MPD_A function replaceQueue(a,b,c){switch(a){case "song":case "dir":sendAPI({cmd:"MPD_API_QUEUE_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_QUEUE_REPLACE_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" replaced',"","","success")}}function songClick(){var a=domCache.currentTrack.getAttribute("data-uri");""!=a&&songDetails(a)} function artistClick(){var a=domCache.currentArtist.getAttribute("data-albumartist");""!=a&&appGoto("Browse","Database","AlbumArtist","0/-/"+a)}function albumClick(){var a=domCache.currentAlbum.getAttribute("data-album");""!=a&&appGoto("Browse","Database","Album","0/-/"+a)}function songDetails(a){sendAPI({cmd:"MPD_API_DATABASE_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()} function parseSongDetails(a){var b=document.getElementById("modalSongDetails");b.getElementsByClassName("album-cover")[0].style.backgroundImage='url("'+a.data.cover+'")';b.getElementsByTagName("h1")[0].innerText=a.data.title;var c="";for(f in settings.tags)if(1==settings.tags[f]){var d=a.data[f.toLowerCase()];if("duration"==f){var e=Math.floor(d/60);d-=60*e;d=e+":"+(10>d?"0":"")+d}c+="<tr><th>"+f+"</th><td>"+d+"</td></tr>"}c+='<tr><th>Uri</th><td><a class="text-success" href="/library/'+a.data.uri+ -'">'+a.data.uri+"</a></td></tr>";if(1==settings.stickers){var f="not voted";0==a.data.like?f='<span class="material-icons">thumb_down_alt</span>':2==a.data.like&&(f='<span class="material-icons">thumb_up_alt</span>');c+='<tr><th colspan="2">Statistics</th></tr><tr><th>Play count</th><td>'+a.data.playCount+"</td></tr><tr><th>Skip count</th><td>"+a.data.skipCount+"</td></tr><tr><th>Last played</th><td>"+(0==a.data.lastPlayed?"never":(new Date(1E3*d)).toUTCString())+"</td></tr><tr><th>Like</th><td>"+ +'">'+a.data.uri+"</a></td></tr>";if(1==settings.stickers){var f="not voted";0==a.data.like?f='<span class="material-icons">thumb_down_alt</span>':2==a.data.like&&(f='<span class="material-icons">thumb_up_alt</span>');c+='<tr><th colspan="2">Statistics</th></tr><tr><th>Play count</th><td>'+a.data.playCount+"</td></tr><tr><th>Skip count</th><td>"+a.data.skipCount+"</td></tr><tr><th>Last played</th><td>"+(0==a.data.lastPlayed?"never":(new Date(1E3*a.data.lastPlayed)).toUTCString())+"</td></tr><tr><th>Like</th><td>"+ f+"</td></tr>"}b.getElementsByTagName("tbody")[0].innerHTML=c}function playlistDetails(a){appGoto("Browse","Playlists","Detail","0/-/"+a)}function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_PLAYLIST_RM_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_PLAYLIST_CONTENT_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_PLAYLIST_CONTENT_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)} function getAllPlaylists(a){var b=a.data.length,c="";0==a.offset&&("addToPlaylistPlaylist"==playlistEl?c="<option></option><option>New Playlist</option>":"jukeboxPlaylist"==playlistEl&&(c="<option>Database</option>"));for(var d=0;d<b;d++)c+="<option","jukeboxPlaylist"==playlistEl&&a.data[d].uri==settings.jukeboxPlaylist&&(c+=" selected"),c+=">"+a.data[d].uri+"</option>";0==a.offset?document.getElementById(playlistEl).innerHTML=c:document.getElementById(playlistEl).innerHTML+=c;a.totalEntities>a.returnedEntities&& diff --git a/dist/htdocs/sw.min.js b/dist/htdocs/sw.min.js index 01bc1cc..5fe336d 100644 --- a/dist/htdocs/sw.min.js +++ b/dist/htdocs/sw.min.js @@ -10,5 +10,5 @@ function(){function a(a){return function(d){c||(c=!0,a.call(b,d))}}var b=this,c= void 0;try{d=a.then}catch(h){this.reject_(h);return}"function"==typeof d?this.settleSameAsThenable_(d,a):this.fulfill_(a)};c.prototype.reject_=function(a){this.settle_(2,a)};c.prototype.fulfill_=function(a){this.settle_(1,a)};c.prototype.settle_=function(a,b){if(0!=this.state_)throw Error("Cannot settle("+a+", "+b+"): Promise already settled in state"+this.state_);this.state_=a;this.result_=b;this.executeOnSettledCallbacks_()};c.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var a= 0;a<this.onSettledCallbacks_.length;++a)l.asyncExecute(this.onSettledCallbacks_[a]);this.onSettledCallbacks_=null}};var l=new b;c.prototype.settleSameAsPromise_=function(a){var b=this.createResolveAndReject_();a.callWhenSettled_(b.resolve,b.reject)};c.prototype.settleSameAsThenable_=function(a,b){var c=this.createResolveAndReject_();try{a.call(b,c.resolve,c.reject)}catch(k){c.reject(k)}};c.prototype.then=function(a,b){function d(a,b){return"function"==typeof a?function(b){try{f(a(b))}catch(m){e(m)}}: b}var f,e,g=new c(function(a,b){f=a;e=b});this.callWhenSettled_(d(a,f),d(b,e));return g};c.prototype.catch=function(a){return this.then(void 0,a)};c.prototype.callWhenSettled_=function(a,b){function c(){switch(d.state_){case 1:a(d.result_);break;case 2:b(d.result_);break;default:throw Error("Unexpected state: "+d.state_);}}var d=this;null==this.onSettledCallbacks_?l.asyncExecute(c):this.onSettledCallbacks_.push(c)};c.resolve=f;c.reject=function(a){return new c(function(b,c){c(a)})};c.race=function(a){return new c(function(b, -c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE="myMPD-cache-v4.2.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(" "); +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.2.1",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)}))}))}); From 9272b69c9f531a6b72e8ebe795d1bf531ded3771 Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 21:36:38 +0100 Subject: [PATCH 06/11] Fix: link/remove library and pics --- mkclean.sh | 2 ++ mkdebug.sh | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mkclean.sh b/mkclean.sh index 0ccc345..50d573c 100755 --- a/mkclean.sh +++ b/mkclean.sh @@ -5,4 +5,6 @@ rm -rf debian/tmp rm -f debian/files rm -f htdocs/js/bootstrap-native-v4.min.js rm -f htdocs/css/bootstrap.min.css +rm -f htdocs/library +rm -f htdocs/pics find ./ -name \*~ -delete diff --git a/mkdebug.sh b/mkdebug.sh index f46b39a..9b8fa67 100755 --- a/mkdebug.sh +++ b/mkdebug.sh @@ -1,8 +1,20 @@ #/bin/sh -cp dist/htdocs/js/bootstrap-native-v4.min.js htdocs/js/ +cp dist/htdocs/js/bootstrap-native-v4.js htdocs/js/bootstrap-native-v4.min.js cp dist/htdocs/css/bootstrap.min.css htdocs/css/ +echo "Trying to link musicdir to library" +if [ -f /etc/mpd.conf ] +then + LIBRARY=$(grep ^music_directory /etc/mpd.conf | awk {'print $2'} | sed -e 's/"//g') + [ "$LIBRARY" != "" ] && [ ! -e htdocs/library ] && ln -s "$LIBRARY" htdocs/library +else + echo "/etc/mpd.conf not found, you must link your music_directory manually to htdocs/library" +fi + +echo "Linking pics directory" +[ -e htdocs/pics ] || ln -s /var/lib/mympd/pics htdocs/ + [ -d debug ] || mkdir debug cd debug cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=DEBUG .. From 4961daa6a3366d922093562883b8f2f7927bdcb2 Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 23:04:35 +0100 Subject: [PATCH 07/11] Fix: simplified packaging --- PKGBUILD | 51 +++---------------------------------- contrib/myMPD.spec | 4 +-- dist/htdocs/js/mympd.min.js | 4 +-- htdocs/index.html | 4 +-- htdocs/js/mympd.js | 2 +- htdocs/player.html | 4 +-- mkclean.sh | 12 +++++++-- mkdebug.sh | 10 +++++--- mkrelease.sh | 11 +++----- 9 files changed, 33 insertions(+), 69 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 2156840..ff6356d 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,7 +1,6 @@ -# Maintainer: K. Loz <cultofrobots [at] protonmail [dot] com> -# Based on jcorporation's myMPD mkrelease.sh +# Maintainer: Juergen Mang <mail [at] jcgames [dot] de> # Website: https://github.com/jcorporation/myMPD -# PKGBUILD Based on mympd-archphile by Mike Andonov <info [at] archphile [dot] org> +# PKGBUILD Based on https://github.com/CultofRobots/archphile-custom/tree/master/mympd pkgname=mympd _pkgname=myMPD @@ -12,7 +11,7 @@ arch=('x86_64' 'armv7h' 'aarch64') url="http://github.org/jcorporation/myMPD" license=('GPL') depends=('libmpdclient' 'openssl') -makedepends=('cmake' 'git') +makedepends=('cmake') optdepends=() provides=() conflicts=() @@ -21,49 +20,6 @@ install=contrib/archlinux.install source=("https://github.com/jcorporation/${_pkgname}/archive/v${pkgver}.tar.gz") sha256sums=('SKIP') -prepare() { - export java=$(which java 2> /dev/null) - - if [ -f ${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-compiler.jar ] && [ "$java" != "$NULL" ] - then - echo "Minifying javascript" - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/player.js -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/player.min.js ] && \ - java -jar ${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-compiler.jar ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/player.js > ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/player.min.js - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/mympd.js -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/mympd.min.js ] && \ - java -jar ${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-compiler.jar ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/mympd.js > ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/mympd.min.js - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/sw.js -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/sw.min.js ] && \ - java -jar ${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-compiler.jar ${srcdir}/${_pkgname}-${pkgver}/htdocs/sw.js > ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/sw.min.js - else - echo "${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-compiler.jar not found, using non-minified files" - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/player.js -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/player.min.js ] && \ - cp ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/player.js ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/player.min.js - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/mympd.js -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/mympd.min.js ] && \ - cp ${srcdir}/${_pkgname}-${pkgver}/htdocs/js/mympd.js ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/mympd.min.js - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/sw.js -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/sw.min.js ] && \ - cp ${srcdir}/${_pkgname}-${pkgver}/htdocs/sw.js ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/sw.min.js - fi - - if [ -f ${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-stylesheets.jar ] && [ "$java" != "$NULL" ] - then - echo "Minifying stylesheets" - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/css/mympd.css -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/css/mympd.min.css ] && \ - java -jar ${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-stylesheets.jar --allow-unrecognized-properties ${srcdir}/${_pkgname}-${pkgver}/htdocs/css/mympd.css > ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/css/mympd.min.css - else - echo "${srcdir}/${_pkgname}-${pkgver}/dist/buildtools/closure-stylesheets.jar not found, using non-minified files" - [ ${srcdir}/${_pkgname}-${pkgver}/htdocs/css/mympd.css -nt ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/css/mympd.min.css ] && \ - cp ${srcdir}/${_pkgname}-${pkgver}/htdocs/css/mympd.css ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/css/mympd.min.css - fi - - echo "Replacing javascript and stylesheets with minified files" - sed -e 's/mympd\.css/mympd\.min\.css/' -e 's/mympd\.js/mympd\.min\.js/' ${srcdir}/${_pkgname}-${pkgver}/htdocs/index.html > ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/index.html - sed -e 's/mympd\.css/mympd\.min\.css/' -e 's/player\.js/player\.min\.js/' ${srcdir}/${_pkgname}-${pkgver}/htdocs/player.html > ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/player.html - sed -i -e 's/mympd\.css/mympd\.min\.css/' -e 's/mympd\.js/mympd\.min\.js/' -e 's/player\.js/player\.min\.js/' ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/sw.min.js - sed -i -e 's/\/sw\.js/\/sw\.min\.js/' ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/js/mympd.min.js - echo "Minifying html" - perl -i -pe 's/^\s*//gm; s/\s*$//gm' ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/index.html - perl -i -pe 's/^\s*//gm; s/\s*$//gm' ${srcdir}/${_pkgname}-${pkgver}/dist/htdocs/player.html -} - build() { cd "${srcdir}/${_pkgname}-${pkgver}" @@ -80,4 +36,3 @@ package() { install -Dm644 "${srcdir}/${_pkgname}-${pkgver}/contrib/mympd.service" "$pkgdir/usr/lib/systemd/system/mympd.service" /usr/share/mympd/crcert.sh } - diff --git a/contrib/myMPD.spec b/contrib/myMPD.spec index f40de92..58b864b 100644 --- a/contrib/myMPD.spec +++ b/contrib/myMPD.spec @@ -1,7 +1,7 @@ # # spec file for package myMPD # -# (c) 2018 Juergen Mang <mail@jcgames.de +# (c) 2018 Juergen Mang <mail@jcgames.de> Name: myMPD Version: 4.2.1 @@ -102,5 +102,5 @@ fi /var/lib/mympd %changelog -* Wed Sep 17 2018 Juergen Mang <mail@jcgames.de> - master +* Fri Sep 21 2018 Juergen Mang <mail@jcgames.de> - master - Version from master diff --git a/dist/htdocs/js/mympd.min.js b/dist/htdocs/js/mympd.min.js index 13b13f6..451bc1f 100644 --- a/dist/htdocs/js/mympd.min.js +++ b/dist/htdocs/js/mympd.min.js @@ -40,8 +40,8 @@ function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("search").addEventListener("submit",function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1); document.getElementById("searchstr").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("BrowseDatabaseByTagDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,a.target.getAttribute("data-tag"),"0/"+app.current.filter+"/"+app.current.search)},!1);document.getElementsByTagName("body")[0].addEventListener("click",function(a){a=document.getElementsByClassName("popover"); for(var b=0;b<a.length;b++)a[b].remove()},!1);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus",function(){sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){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: ",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")})} +"https"==document.URL.substring(0,5)&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.min.js",{scope:"/"}).then(function(a){console.log("ServiceWorker registration successful with scope: ",a.scope);a.update()},function(a){console.log("ServiceWorker registration failed: ",a)})});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a; +domCache.btnAdd.classList.remove("hide")});domCache.btnAdd.addEventListener("click",function(a){domCache.btnAdd.classList.add("hide");deferredPrompt.prompt();deferredPrompt.userChoice.then(function(a){"accepted"===a.outcome?console.log("User accepted the A2HS prompt"):console.log("User dismissed the A2HS prompt");deferredPrompt=null})});window.addEventListener("appinstalled",function(a){console.log("myMPD installed as app")})} function dragAndDropTable(a){var b=document.getElementById(a).getElementsByTagName("tbody")[0];b.addEventListener("dragstart",function(a){"TR"==a.target.nodeName&&(a.target.classList.add("opacity05"),a.dataTransfer.setDragImage(a.target,0,0),a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("Text",a.target.getAttribute("id")),dragEl=a.target.cloneNode(!0))},!1);b.addEventListener("dragleave",function(a){a.preventDefault();var b=a.target;"TD"==a.target.nodeName&&(b=a.target.parentNode);"TR"== b.nodeName&&b.classList.remove("dragover")},!1);b.addEventListener("dragover",function(a){a.preventDefault();for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover");c=a.target;"TD"==a.target.nodeName&&(c=a.target.parentNode);"TR"==c.nodeName&&c.classList.add("dragover");a.dataTransfer.dropEffect="move"},!1);b.addEventListener("dragend",function(a){for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover"); document.getElementById(a.dataTransfer.getData("Text"))&&document.getElementById(a.dataTransfer.getData("Text")).classList.remove("opacity05")},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();var d=c.target;"TD"==c.target.nodeName&&(d=c.target.parentNode);var e=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),f=d.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05"); diff --git a/htdocs/index.html b/htdocs/index.html index 4a4dac3..9bdc732 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -10,7 +10,7 @@ <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="theme-color" content="#343a40"> <link href="/css/bootstrap.min.css" rel="stylesheet"> - <link href="/css/mympd.css" rel="stylesheet"> + <link href="/css/mympd.min.css" rel="stylesheet"> <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"> <link rel="manifest" href="/mympd.webmanifest"> <link rel="apple-touch-icon" href="/assets/appicon-167.png"> @@ -833,6 +833,6 @@ </div> <script src="/js/bootstrap-native-v4.min.js"></script> - <script src="/js/mympd.js"></script> + <script src="/js/mympd.min.js"></script> </body> </html> diff --git a/htdocs/js/mympd.js b/htdocs/js/mympd.js index 54df2b0..12379e4 100644 --- a/htdocs/js/mympd.js +++ b/htdocs/js/mympd.js @@ -550,7 +550,7 @@ function appInit() { if ('serviceWorker' in navigator && document.URL.substring(0, 5) == 'https') { window.addEventListener('load', function() { - navigator.serviceWorker.register('/sw.js', {scope: '/'}).then(function(registration) { + navigator.serviceWorker.register('/sw.min.js', {scope: '/'}).then(function(registration) { // Registration was successful console.log('ServiceWorker registration successful with scope: ', registration.scope); registration.update(); diff --git a/htdocs/player.html b/htdocs/player.html index 3752433..c3ac9b9 100644 --- a/htdocs/player.html +++ b/htdocs/player.html @@ -7,7 +7,7 @@ <meta name="author" content="mail@jcgames.de"> <title>myMPD: Local Player</title> <link href="css/bootstrap.min.css" rel="stylesheet"> - <link href="css/mympd.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"/> @@ -29,6 +29,6 @@ </div> </div> </main> - <script type="text/javascript" src="js/player.js"></script> + <script type="text/javascript" src="js/player.min.js"></script> </body> </html> diff --git a/mkclean.sh b/mkclean.sh index 50d573c..688a570 100755 --- a/mkclean.sh +++ b/mkclean.sh @@ -3,8 +3,16 @@ rm -rf release rm -rf debug rm -rf debian/tmp rm -f debian/files -rm -f htdocs/js/bootstrap-native-v4.min.js -rm -f htdocs/css/bootstrap.min.css + rm -f htdocs/library rm -f htdocs/pics + +rm -f htdocs/sw.min.js +rm -f htdocs/js/mympd.min.js +rm -f htdocs/js/player.min.js +rm -f htdocs/js/bootstrap-native-v4.min.js + +rm -f htdocs/css/mympd.min.css +rm -f htdocs/css/bootstrap.min.css + find ./ -name \*~ -delete diff --git a/mkdebug.sh b/mkdebug.sh index 9b8fa67..dfcd094 100755 --- a/mkdebug.sh +++ b/mkdebug.sh @@ -1,7 +1,11 @@ #/bin/sh +[ -e $PWD/htdocs/sw.min.js ] || ln -s $PWD/htdocs/sw.js $PWD/htdocs/sw.min.js +[ -e $PWD/htdocs/js/mympd.min.js ] || ln -s $PWD/htdocs/js/mympd.js $PWD/htdocs/js/mympd.min.js +[ -e $PWD/htdocs/js/player.min.js ] || ln -s $PWD/htdocs/js/player.js $PWD/htdocs/js/player.min.js +[ -e $PWD/htdocs/js/bootstrap-native-v4.min.js ] || ln -s $PWD/dist/htdocs/js/bootstrap-native-v4.js $PWD/htdocs/js/bootstrap-native-v4.min.js -cp dist/htdocs/js/bootstrap-native-v4.js htdocs/js/bootstrap-native-v4.min.js -cp dist/htdocs/css/bootstrap.min.css htdocs/css/ +[ -e $PWD/htdocs/css/mympd.min.css ] || ln -s $PWD/htdocs/css/mympd.css $PWD/htdocs/css/mympd.min.css +[ -e $PWD/htdocs/css/bootstrap.min.css ] || ln -s $PWD/dist/htdocs/css/bootstrap.min.css $PWD/htdocs/css/bootstrap.min.css echo "Trying to link musicdir to library" if [ -f /etc/mpd.conf ] @@ -13,7 +17,7 @@ else fi echo "Linking pics directory" -[ -e htdocs/pics ] || ln -s /var/lib/mympd/pics htdocs/ +[ -e $PWD/htdocs/pics ] || ln -s /var/lib/mympd/pics htdocs/ [ -d debug ] || mkdir debug cd debug diff --git a/mkrelease.sh b/mkrelease.sh index f2f346c..a243aca 100755 --- a/mkrelease.sh +++ b/mkrelease.sh @@ -32,14 +32,11 @@ else cp htdocs/css/mympd.css dist/htdocs/css/mympd.min.css fi -echo "Replacing javascript and stylesheets with minified files" -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/mympd\.css/mympd\.min\.css/' -e 's/player\.js/player\.min\.js/' htdocs/player.html > dist/htdocs/player.html -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/mympd.min.js 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/player.html +[ htdocs/index.html -nt dist/htdocs/index.html ] && \ + perl -pe 's/^\s*//gm; s/\s*$//gm' htdocs/index.html > dist/htdocs/index.html +[ htdocs/player.html -nt dist/htdocs/player.html ] && \ + perl -pe 's/^\s*//gm; s/\s*$//gm' htdocs/player.html > dist/htdocs/player.html echo "Compiling and installing mympd" [ -d release ] || mkdir release From 05b93894e8371f4ad4efad7bdad9f4ad9954159d Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 23:22:44 +0100 Subject: [PATCH 08/11] Feat: add cppcheck --- mkrelease.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mkrelease.sh b/mkrelease.sh index a243aca..89f8fb8 100755 --- a/mkrelease.sh +++ b/mkrelease.sh @@ -47,3 +47,6 @@ sudo make install cd .. debian/postinst + +echo "Running cppcheck" +[ -x /usr/bin/cppcheck ] && cppcheck --enable=warning --inconclusive --force --inline-suppr src/*.c src/*.h From 006eb6d28ce3a124abcbf531f0b3cd3e680837df Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Thu, 20 Sep 2018 23:43:58 +0100 Subject: [PATCH 09/11] Fix: don't use serviceworker for http:// uris -> fixes http stream in local player --- htdocs/sw.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/htdocs/sw.js b/htdocs/sw.js index cce5f88..b1d4ed4 100644 --- a/htdocs/sw.js +++ b/htdocs/sw.js @@ -28,15 +28,16 @@ self.addEventListener('install', function(event) { }); self.addEventListener('fetch', function(event) { - event.respondWith( - caches.match(event.request).then(function(response) { - if (response) - return response - else - return fetch(event.request); - } - ) - ); + if (event.request.url.match('^http://')) + return false; + event.respondWith( + caches.match(event.request).then(function(response) { + if (response) + return response + else + return fetch(event.request); + }) + ); }); self.addEventListener('activate', function(event) { From f71bef42fee5f29945dc7797ed635e305b3c7042 Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Sun, 23 Sep 2018 22:48:12 +0100 Subject: [PATCH 10/11] Feat: add rescan database command --- htdocs/css/mympd.css | 10 ++++++++++ htdocs/index.html | 6 +++++- htdocs/js/mympd.js | 15 +++++++++++++++ src/mpd_client.c | 5 +++++ src/mpd_client.h | 1 + 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/htdocs/css/mympd.css b/htdocs/css/mympd.css index 65e5cbf..3ad943d 100644 --- a/htdocs/css/mympd.css +++ b/htdocs/css/mympd.css @@ -128,6 +128,12 @@ small { font-feature-settings: 'liga'; } +.material-icons-left { + font-size: 1rem; + margin-left: -1em; + vertical-align:middle; +} + .material-icons-small { font-size: 16px; } @@ -281,3 +287,7 @@ caption { width:100%; margin-left: 15px; } + +#menu-dbupdate { + padding-left:1rem; +} \ No newline at end of file diff --git a/htdocs/index.html b/htdocs/index.html index 9bdc732..7df746f 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -24,7 +24,11 @@ </a> <div class="dropdown-menu bg-dark"> <a 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": "updateDB", "options": []}'>Update Database</a> + <a id="navDBupdate" class="dropdown-item text-light bg-dark" data-toggle="collapse" href="#menu-dbupdate"><span class="material-icons material-icons-left">keyboard_arrow_right</span>Database</a> + <div class="collapse" id="menu-dbupdate"> + <a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "updateDB", "options": []}'>Update</a> + <a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "rescanDB", "options": []}'>Rescan</a> + </div> <a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "openLocalPlayer", "options": []}'>Local Player</a> <a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a> <a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</a> diff --git a/htdocs/js/mympd.js b/htdocs/js/mympd.js index 12379e4..0f750ab 100644 --- a/htdocs/js/mympd.js +++ b/htdocs/js/mympd.js @@ -309,6 +309,16 @@ function appInit() { sendAPI({"cmd": "MPD_API_PLAYER_SEEK", "data": {"songid": currentSong.currentSongId, "seek": seekVal}}); } }, false); + + document.getElementById('navDBupdate').addEventListener('click', function(event) { + event.stopPropagation(); + event.preventDefault(); + var icon = this.getElementsByTagName('span')[0]; + if (icon.innerText == 'keyboard_arrow_right') + icon.innerText = 'keyboard_arrow_down'; + else + icon.innerText = 'keyboard_arrow_right'; + }, false); document.getElementById('volumeIcon').parentNode.addEventListener('show.bs.dropdown', function () { sendAPI({"cmd": "MPD_API_PLAYER_OUTPUT_LIST"}, parseOutputs); @@ -1840,6 +1850,11 @@ function updateDB() { updateDBstarted(true); } +function rescanDB() { + sendAPI({"cmd": "MPD_API_DATABASE_RESCAN"}); + updateDBstarted(true); +} + function updateDBstarted(showModal) { if (showModal == true) { document.getElementById('updateDBfinished').innerText = ''; diff --git a/src/mpd_client.c b/src/mpd_client.c index 1814ec8..cdbcfd7 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -179,6 +179,11 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { if (uint_rc > 0) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; + case MPD_API_DATABASE_RESCAN: + uint_rc = mpd_run_rescan(mpd.conn, NULL); + if (uint_rc > 0) + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + break; case MPD_API_PLAYER_PAUSE: mpd_run_toggle_pause(mpd.conn); n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); diff --git a/src/mpd_client.h b/src/mpd_client.h index f0d3e02..96763a1 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -74,6 +74,7 @@ X(MPD_API_PLAYLIST_CONTENT_LIST) \ X(MPD_API_DATABASE_SEARCH) \ X(MPD_API_DATABASE_UPDATE) \ + X(MPD_API_DATABASE_RESCAN) \ X(MPD_API_DATABASE_FILESYSTEM_LIST) \ X(MPD_API_DATABASE_TAG_LIST) \ X(MPD_API_DATABASE_TAG_ALBUM_LIST) \ From a8a8503a26aa99a52dc7036ad7fff79af76d3a30 Mon Sep 17 00:00:00 2001 From: jcorporation <mail@jcgames.de> Date: Sun, 23 Sep 2018 22:56:00 +0100 Subject: [PATCH 11/11] Fix: update minified files --- dist/htdocs/css/mympd.min.css | 2 +- dist/htdocs/index.html | 2 +- dist/htdocs/js/mympd.min.js | 35 ++++++++++++++++++----------------- dist/htdocs/sw.min.js | 2 +- htdocs/sw.js | 6 +++--- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/dist/htdocs/css/mympd.min.css b/dist/htdocs/css/mympd.min.css index 88b784e..722d16d 100644 --- a/dist/htdocs/css/mympd.min.css +++ b/dist/htdocs/css/mympd.min.css @@ -1 +1 @@ -html{position:relative;min-height:100%}body{margin-bottom:60px;padding-top:50px;padding-bottom:50px;background-color:#888}main{padding-top:20px}footer{position:absolute;bottom:0}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}.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}.material-icons-small-left{font-size:1rem;margin-left:-1em}.color-darkgrey,.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}.progressBarPlay{font-size:22px}#counter{cursor:text}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-top{overflow:hidden;display:block}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}button.active-fg-green{color:#28a745!important}button.active-fg-red{color:#bd2130!important}div#alertBox{position:fixed;top:50px;right:10px;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5}caption{caption-side:top;font-size:120%;font-weight:bold;color:black}.dragover>td{border-top:25px solid transparent}[draggable]{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;user-select:none;-khtml-user-drag:element;-webkit-user-drag:element}@keyframes changewidth{0%{margin-left:-20px}to{margin-left:100%}}#updateDBprogress{width:20px}.updateDBprogressAnimate{animation-duration:2s;animation-name:changewidth;animation-iteration-count:infinite}.modal-body{overflow-x:hidden}.modal-body .album-cover{float:none}#BrowseDatabaseAlbumListCaption{width:100%;margin-left:15px} \ No newline at end of file +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-left{font-size:1rem;margin-left:-1em;vertical-align:middle}.material-icons-small{font-size:16px}.material-icons-small-left{font-size:1rem;margin-left:-1em}.color-darkgrey,.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}.progressBarPlay{font-size:22px}#counter{cursor:text}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-top{overflow:hidden;display:block}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}button.active-fg-green{color:#28a745!important}button.active-fg-red{color:#bd2130!important}div#alertBox{position:fixed;top:50px;right:10px;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5}caption{caption-side:top;font-size:120%;font-weight:bold;color:black}.dragover>td{border-top:25px solid transparent}[draggable]{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;user-select:none;-khtml-user-drag:element;-webkit-user-drag:element}@keyframes changewidth{0%{margin-left:-20px}to{margin-left:100%}}#updateDBprogress{width:20px}.updateDBprogressAnimate{animation-duration:2s;animation-name:changewidth;animation-iteration-count:infinite}.modal-body{overflow-x:hidden}.modal-body .album-cover{float:none}#BrowseDatabaseAlbumListCaption{width:100%;margin-left:15px}#menu-dbupdate{padding-left:1rem} \ No newline at end of file diff --git a/dist/htdocs/index.html b/dist/htdocs/index.html index c20df44..821a2b7 100644 --- a/dist/htdocs/index.html +++ b/dist/htdocs/index.html @@ -1 +1 @@ -<!DOCTYPE html><html lang="en"><head><title>myMPD</title><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"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="theme-color" content="#343a40"><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"><link rel="manifest" href="/mympd.webmanifest"><link rel="apple-touch-icon" href="/assets/appicon-167.png"></head><body><header><nav class="navbar navbar-expand navbar-dark fixed-top bg-dark"><div class="dropdown col-auto mr-auto pl-0" id="mainMenu"><a class="dropdown-toggle navbar-brand" data-toggle="dropdown" href="#"><span class="material-icons header-logo">play_circle_outline</span>myMPD</a><div class="dropdown-menu bg-dark"><a 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": "updateDB", "options": []}'>Update Database</a><a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "openLocalPlayer", "options": []}'>Local Player</a><a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a><a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</a><a id="nav-add2homescreen" class="dropdown-item text-light bg-dark hide" href="#">Add2HomeScreen</a></div></div><div class="btn-toolbar col-auto pl-0 pr-0"><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">skip_previous</button><button data-href='{"cmd": "clickStop", "options": []}' id="btnStop" type="button" class="btn btn-secondary pl-2 pr-2 material-icons">stop</button><button data-href='{"cmd": "clickPlay", "options": []}' id="btnPlay" type="button" class="btnPlay btn btn-secondary pl-2 pr-2 material-icons">pause</button><button data-href='{"cmd": "clickNext", "options": []}' id="btnNext" type="button" class="btn btn-secondary pl-2 pr-2 material-icons">skip_next</button></div><div class="btn-group"><button id="volumeIcon" class="btn btn-secondary dropdown-toggle pl-2 pr-2 material-icons" type="button" data-toggle="dropdown">volume_up</button><div class="dropdown-menu dropdown-menu-right bg-dark"><h2 class="dropdown-header text-light">Volume: <span id="volumePrct"></span></h2><form class="px-4 py-0 pb-3" id="volumeControl"><div class="btn-group"><button data-href='{"cmd": "chVolume", "options": [-5]}' class="btn btn-secondary">−</button><div class="btn btn-secondary"><input type="range" min="0" max="100" step="1" class="form-control-range" id="volumeBar"></div><button data-href='{"cmd": "chVolume", "options": [5]}' class="btn btn-secondary">+</button></div></form><div class="dropdown-divider"></div><form id="outputs" class="px-4 py-3"></form></div></div></div></nav></header><main class="container"><noscript><div class="alert alert-danger">JavaScript is disabled!</div></noscript><div class="card" id="cardPlayback"><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="album-cover" id="currentCover"></div><div class="album-desc"><h2 id="currentTrack" data-href='{"cmd": "songClick", "options": []}'></h2><small>Artist</small><h4 id="currentArtist" data-href='{"cmd": "artistClick", "options": []}'></h4><small>Album</small><h4 id="currentAlbum" data-href='{"cmd": "albumClick", "options": []}'></h4></div></div><div class="card-footer card-footer-playback"><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"> </div></div></div></div><div class="card hide" id="cardQueue"><div class="card-header">Queue<span id="panel-heading-queue" class="text pull-right"></span></div><div class="card-body"><div class="btn-toolbar card-toolbar" id="queue-buttons"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-toggle="modal" data-target="#modalSaveQueue" title="Save queue">save</button></div><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons"data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_SHUFFLE"}]}' title="Shuffle queue">shuffle</button></div><div class="input-group mr-2"><div class="input-group-prepend"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_CLEAR"}]}' title="Clear queue">clear_all</button><button id="clearQueueBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="clearQueueDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block" data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_CLEAR"}]}' >Clear queue</button><button type="button" class="btn btn-secondary btn-sm btn-block" data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_CROP"}]}' >Crop queue</button></div></div></div><form id="searchqueue"><div class="input-group mr-2"><input type="text" class="form-control" placeholder="Search Queue" id="searchqueuestr"/><div class="input-group-append"><button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"><span class="material-icons">search</span><span id="searchqueuetagdesc">Any Tag</span></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchqueuetag"></div></div></div></form><div id="QueuePaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="QueuePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="QueuePaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="QueuePaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="QueuePaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="QueueList" class="table table-hover table-sm" data-version=""><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th>#</th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="QueueButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options: [0]}' title="To top">keyboard_arrow_up</button></div><div id="QueuePaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="QueuePaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="QueuePaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="QueuePaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="QueuePaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div></div><div class="card hide" id="cardBrowse"><div class="card-header" id="panel-heading-browse"><ul class="nav nav-tabs card-header-tabs"><li class="nav-item"><a data-href='{"cmd": "appGoto", "options": ["Browse", "Database"]}' class="nav-link text-dark" href="#" id="cardBrowseNavDatabase">Database</a></li><li class="nav-item"><a data-href='{"cmd": "appGoto", "options": ["Browse", "Playlists"]}' class="nav-link text-dark" href="#" id="cardBrowseNavPlaylists">Playlists</a></li><li class="nav-item"><a data-href='{"cmd": "appGoto", "options": ["Browse", "Filesystem"]}' class="nav-link text-dark" href="#" id="cardBrowseNavFilesystem">Filesystem</a></li></ul></div><div class="card-body hide" id="cardBrowsePlaylists"><div class="btn-toolbar card-toolbar" id="BrowsePlaylistsButtons"><div class="btn-group mr-2 hide"><button data-href='{"cmd": "appGoto", "options": ["Browse", "Playlists", "All"]}' id="btnBrowsePlaylistsAll" type="button" class="btn btn-secondary">« Playlists</button></div><div class="btn-group mr-2 hide"><button id="btnPlaylistClear" type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "playlistClear", "options": []}' title="Clear playlist">clear_all</button></div><div class="btn-group mr-2"><button id="BrowsePlaylistsFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button><div class="dropdown-menu bg-dark px-2 letters" id="BrowsePlaylistsFilterLetters"></div></div><div id="BrowsePlaylistsPaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowsePlaylistsPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowsePlaylistsPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowsePlaylistsPaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowsePlaylistsPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="BrowsePlaylistsAllList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><col class="tbllastmodified"/><col class="tblaction"/><thead><tr><th></th><th>Playlist</th><th>Last modified</th><th></th></tr></thead><tbody class="clickable"></tbody></table><table id="BrowsePlaylistsDetailList" class="table table-hover table-sm hide"><caption>Playlist List</caption><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th>#</th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="BrowsePlaylistsButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="BrowsePlaylistsPaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowsePlaylistsPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowsePlaylistsPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowsePlaylistsPaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowsePlaylistsPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div><div class="card-body hide" id="cardBrowseDatabase"><div class="btn-toolbar card-toolbar" id="BrowseDatabaseButtons"><div class="btn-group mr-2"><button id="btnBrowseDatabaseByTag" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">AlbumArtist</button><div class="dropdown-menu bg-dark px-2" id="BrowseDatabaseByTagDropdown"></div></div><div class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoTagList", "options": []}' id="btnBrowseDatabaseTag" type="button" class="btn btn-secondary">« Artists</button></div><div class="btn-group mr-2"><button id="BrowseDatabaseFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button><div class="dropdown-menu bg-dark px-2 letters" id="BrowseDatabaseFilterLetters"></div></div><div class="input-group mr-2 hide"><div class="input-group-prepend"><button data-href='{"cmd": "addAllFromBrowseDatabase", "options": []}' id="BrowseDatabaseAddAllSongs" class="btn btn-secondary">Add all</button><button id="BrowseDatabaseAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="BrowseDatabaseAddAllSongsDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button></div></div></div><div id="BrowseDatabasePaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseDatabasePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseDatabasePaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseDatabasePaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseDatabasePaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="BrowseDatabaseTagList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><thead><tr><th></th><th>Artist</th></tr></thead><tbody class="clickable"></tbody></table></div><div id="BrowseDatabaseAlbumList" class="row hide"><h2 id="BrowseDatabaseAlbumListCaption"></h2></div><div class="btn-toolbar hide" id="BrowseDatabaseButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="BrowseDatabasePaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseDatabasePaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseDatabasePaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseDatabasePaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseDatabasePaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div><div class="card-body hide" id="cardBrowseFilesystem"><div class="btn-toolbar card-toolbar" id="BrowseFilesystemButtons"><div class="btn-group mr-2"><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></div><div class="input-group mr-2"><div class="input-group-prepend"><button data-href='{"cmd": "addAllFromBrowseFilesystem", "options": []}' id="BrowseFilesystemAddAllSongs" class="btn btn-secondary">Add all</button><button id="BrowseFilesystemAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="BrowseFilesystemAddAllSongsDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button></div></div></div><div id="BrowseFilesystemPaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseFilesystemPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseFilesystemPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseFilesystemPaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseFilesystemPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><ol id="BrowseBreadcrumb" class="breadcrumb"></ol><div class="table-responsive-md"><table id="BrowseFilesystemList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th></th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="BrowseFilesystemButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="BrowseFilesystemPaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseFilesystemPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseFilesystemPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseFilesystemPaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseFilesystemPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div></div><div class="card hide" id="cardSearch"><div class="card-header">Search<span id="panel-heading-search" class="text pull-right"></span></div><div class="card-body"><div class="btn-toolbar card-toolbar" id="SearchButtons"><form id="search"><div class="input-group mr-2"><input type="text" class="form-control" placeholder="Search" id="searchstr"/><div class="input-group-append"><button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"><span class="material-icons">search</span><span id="searchtagsdesc">Any Tag</span></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchtags"></div></div></div></form><div class="input-group mr-2"><div class="input-group-prepend"><button id="searchAddAllSongs" class="btn btn-secondary" data-href='{"cmd": "addAllFromSearch", "options": []}'>Add all</button><button id="searchAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchAddAllSongsDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button></div></div></div><div id="SearchPaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="SearchPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="SearchPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="SearchPaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="SearchPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="SearchList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th></th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="SearchButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="SearchPaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="SearchPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="SearchPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="SearchPaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="SearchPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div></div></main><footer class="footer"><nav class="navbar navbar-expand navbar-dark fixed-bottom bg-dark"><div class="d-flex flex-fill navbar-nav" id="navbar-bottom"><div id="navPlayback" class="nav-item flex-fill text-center"><a data-href='{"cmd": "appGoto", "options": ["Playback"]}' class="nav-link" href="#">Playback</a></div><div id="navQueue" class="nav-item flex-fill text-center"><a data-href='{"cmd": "appGoto", "options": ["Queue"]}' class="nav-link" href="#">Queue</a></div><div class="nav-item flex-fill text-center" id="navBrowse"><a data-href='{"cmd": "appGoto", "options": ["Browse"]}' class="nav-link" href="#">Browse</a></div><div class="nav-item flex-fill text-center" id="navSearch"><a data-href='{"cmd": "appGoto", "options": ["Search"]}' class="nav-link" href="#">Search</a></div></div></nav></footer><!-- Modals --><div class="modal fade" id="modalConnectionError" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header bg-danger text-light"><h5 class="modal-title"><span class="material-icons title-icon">error</span> Connection Error</h5></div><div class="modal-body"><p>Connection to myMPD failed. Trying to reconnect.</p></div></div></div></div><div class="modal fade" id="modalUpdateDB" tabindex="-1"><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">×</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-header"><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></div><div class="modal-body"><form id="addStreamFrm" class="needs-validation hide" novalidate><div class="row"><div class="form-group col-md-12"><label class="control-label" for="streamUrl">Stream URL</label><input type="text" class="form-control" id="streamUrl" placeholder="http://uri.to/stream.mp3"/><div class="invalid-feedback">Invalid uri</div></div></div><div class="row"><div class="form-group col-md-12"><button id="toggleAddToPlaylistBtn" class="btn btn-secondary" data-href='{"cmd": "toggleAddToPlaylistFrm", "options":[]}'>Add to playlist</button></div></div></form><form class="needs-validation hide" id="addToPlaylistFrm" novalidate><input type="hidden" id="addToPlaylistUri"/><div class="form-group"><label for="addToPlaylistPlaylist">Playlist</label><select id="addToPlaylistPlaylist" class="form-control custom-select"></select><div class="invalid-feedback">Please choose playlist.</div></div><div class="form-group hide" id="addToPlaylistNewPlaylistDiv"><label for="addToPlaylistNewPlaylist">Create Playlist</label><input id="addToPlaylistNewPlaylist" class="form-control"/><div class="invalid-feedback">Invalid filename.</div></div></form></div><div class="modal-footer" id="addToPlaylistFooter"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "addToPlaylist", "options": []}'>Add</button></div><div class="modal-footer hide" id="addStreamFooter"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "addStream", "options": []}'>Add</button></div></div></div></div><div class="modal fade" id="modalRenamePlaylist" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><form class="needs-validation" id="renamePlaylistFrm" novalidate><div class="form-group"><label for="renamePlaylistFrom">From</label><input type="text" class="form-control" id="renamePlaylistFrom" readonly></div><div class="form-group"><label for="renamePlaylistTo">To</label><input type="text" class="form-control" id="renamePlaylistTo"/><div class="invalid-feedback">Invalid filename.</div></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "renamePlaylist", "options": []}'>Save</button></div></div></div></div><div class="modal fade" id="modalSettings" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><form class="needs-validation" id="settingsFrm" novalidate><h4>MPD</h4><div class="row"><div class="form-group col-md-6"><button data-href='{"cmd":"toggleBtn", "options":["btnRandom"]}' id="btnRandom" type="button" class="btn btn-secondary btn-block" title="Random">Random</button></div><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnConsume"]}' id="btnConsume" type="button" class="btn btn-secondary btn-block" title="Consume">Consume</button></div></div><div class="row"><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnSingle"]}' id="btnSingle" type="button" class="btn btn-secondary btn-block" title="Single">Single</button></div><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnRepeat"]}' id="btnRepeat" type="button" class="btn btn-secondary btn-block" title="Repeat">Repeat</button></div></div><div class="row"><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Crossfade</div></div><input id="inputCrossfade" type="text" class="form-control border-secondary" value=""><div class="invalid-feedback">Must be a number.</div></div><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Replaygain</div></div><select id="selectReplaygain" class="form-control custom-select border-secondary"><option value="off">Off</option><option value="track">Track</option><option value="album">Album</option></select></div></div><div class="row mixramp"><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Mixramp DB</div></div><input id="inputMixrampdb" type="text" class="form-control border-secondary" value=""><div class="invalid-feedback">Must be a number.</div></div><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Mixramp Delay</div></div><input id="inputMixrampdelay" type="text" class="form-control border-secondary" value=""><div class="invalid-feedback">Must be a number.</div></div></div><hr/><h4>Jukebox</h4><div class="row"><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnJukebox"]}' id="btnJukebox" type="button" class="btn btn-secondary btn-block" title="Jukebox">Jukebox</button></div><div class="form-group input-group col-md-6 border-secondary"><select id="jukeboxPlaylist" class="form-control custom-select border-secondary"></select></div></div><hr/><h4>Notifications</h4><div class="row"><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnnotifyPage"]}' type="button" class="btn btn-secondary btn-block" id="btnnotifyPage">Page Notifications</button></div><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnnotifyWeb"]}' type="button" class="btn btn-secondary btn-block" id="btnnotifyWeb">Web Notifications</button></div></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "confirmSettings", "options": []}'>Save</button></div></div></div></div><div class="modal fade" id="modalAbout" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><h4><a class="text-success" rel="noreferrer" href="https://github.com/jcorporation/ympd">myMPD</a> – <small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4><p>myMPD is a lightweight MPD web client that runs without a dedicated webserver or interpreter. It's tuned for minimal resource usage and requires only very litte dependencies. myMPD is a fork of <a class="text-success" href="http://www.ympd.org">ympd</a>.</p><ul><li>Version: <span id="mympdVersion"></span></li><li>Homepage: <a class="text-success" target="_blank" rel="noreferrer" href="https://github.com/jcorporation/mympd">https://github.com/jcorporation/mympd</a></li><li>Autor: Jürgen Mang <<a class="text-success" href="mailto:mail@jcgames.de">mail@jcgames.de</a>></li></ul><table class="table table-sm"><tbody><tr><td colspan="2" class="pt-3"><h5>Database Statistics</h5></td></tr><tr><th>Artists</th><td id="mpdstats_artists"></td></tr><tr><th>Albums</th><td id="mpdstats_albums"></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 Updated</th><td id="mpdstats_dbUpdated"></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>Play Time</th><td id="mpdstats_playtime"></td></tr><tr><td colspan="2" class="pt-3"><h5>MPD</h5></td></tr><tr><th>Protocol Version</th><td id="mpdVersion"></td></tr></tbody></table></div></div></div></div><div class="modal fade" id="modalSaveQueue" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h5 class="modal-title"><span class="material-icons title-icon">save</span> Save Queue</h5><button type="button" class="close" data-dismiss="modal">×</button></div><div class="modal-body"><form class="needs-validation" id="saveQueueFrm" novalidate><div class="row"><div class="form-group col-md-9"><label class="control-label" for="saveQueueName">Playlist Name</label><input type="text" class="form-control" id="saveQueueName"/><div class="invalid-feedback">Invalid filename</div></div></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "saveQueue", "options": []}'>Save Queue</button></div></div></div></div><div class="modal fade" id="modalSongDetails" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><div class="album-cover"></div><h1></h1><div class="table-responsive-md"><table class="table table-sm"><tbody></tbody></table></div></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button></div></div></div></div><script src="/js/bootstrap-native-v4.min.js"></script><script src="/js/mympd.min.js"></script></body></html> \ No newline at end of file +<!DOCTYPE html><html lang="en"><head><title>myMPD</title><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"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="theme-color" content="#343a40"><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"><link rel="manifest" href="/mympd.webmanifest"><link rel="apple-touch-icon" href="/assets/appicon-167.png"></head><body><header><nav class="navbar navbar-expand navbar-dark fixed-top bg-dark"><div class="dropdown col-auto mr-auto pl-0" id="mainMenu"><a class="dropdown-toggle navbar-brand" data-toggle="dropdown" href="#"><span class="material-icons header-logo">play_circle_outline</span>myMPD</a><div class="dropdown-menu bg-dark"><a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "showAddToPlaylist", "options": ["stream"]}'>Add Stream</a><a id="navDBupdate" class="dropdown-item text-light bg-dark" data-toggle="collapse" href="#menu-dbupdate"><span class="material-icons material-icons-left">keyboard_arrow_right</span>Database</a><div class="collapse" id="menu-dbupdate"><a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "updateDB", "options": []}'>Update</a><a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "rescanDB", "options": []}'>Rescan</a></div><a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "openLocalPlayer", "options": []}'>Local Player</a><a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a><a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</a><a id="nav-add2homescreen" class="dropdown-item text-light bg-dark hide" href="#">Add2HomeScreen</a></div></div><div class="btn-toolbar col-auto pl-0 pr-0"><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">skip_previous</button><button data-href='{"cmd": "clickStop", "options": []}' id="btnStop" type="button" class="btn btn-secondary pl-2 pr-2 material-icons">stop</button><button data-href='{"cmd": "clickPlay", "options": []}' id="btnPlay" type="button" class="btnPlay btn btn-secondary pl-2 pr-2 material-icons">pause</button><button data-href='{"cmd": "clickNext", "options": []}' id="btnNext" type="button" class="btn btn-secondary pl-2 pr-2 material-icons">skip_next</button></div><div class="btn-group"><button id="volumeIcon" class="btn btn-secondary dropdown-toggle pl-2 pr-2 material-icons" type="button" data-toggle="dropdown">volume_up</button><div class="dropdown-menu dropdown-menu-right bg-dark"><h2 class="dropdown-header text-light">Volume: <span id="volumePrct"></span></h2><form class="px-4 py-0 pb-3" id="volumeControl"><div class="btn-group"><button data-href='{"cmd": "chVolume", "options": [-5]}' class="btn btn-secondary">−</button><div class="btn btn-secondary"><input type="range" min="0" max="100" step="1" class="form-control-range" id="volumeBar"></div><button data-href='{"cmd": "chVolume", "options": [5]}' class="btn btn-secondary">+</button></div></form><div class="dropdown-divider"></div><form id="outputs" class="px-4 py-3"></form></div></div></div></nav></header><main class="container"><noscript><div class="alert alert-danger">JavaScript is disabled!</div></noscript><div class="card" id="cardPlayback"><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="album-cover" id="currentCover"></div><div class="album-desc"><h2 id="currentTrack" data-href='{"cmd": "songClick", "options": []}'></h2><small>Artist</small><h4 id="currentArtist" data-href='{"cmd": "artistClick", "options": []}'></h4><small>Album</small><h4 id="currentAlbum" data-href='{"cmd": "albumClick", "options": []}'></h4></div></div><div class="card-footer card-footer-playback"><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"> </div></div></div></div><div class="card hide" id="cardQueue"><div class="card-header">Queue<span id="panel-heading-queue" class="text pull-right"></span></div><div class="card-body"><div class="btn-toolbar card-toolbar" id="queue-buttons"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-toggle="modal" data-target="#modalSaveQueue" title="Save queue">save</button></div><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons"data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_SHUFFLE"}]}' title="Shuffle queue">shuffle</button></div><div class="input-group mr-2"><div class="input-group-prepend"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_CLEAR"}]}' title="Clear queue">clear_all</button><button id="clearQueueBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="clearQueueDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block" data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_CLEAR"}]}' >Clear queue</button><button type="button" class="btn btn-secondary btn-sm btn-block" data-href='{"cmd": "sendAPI", "options": [{"cmd": "MPD_API_QUEUE_CROP"}]}' >Crop queue</button></div></div></div><form id="searchqueue"><div class="input-group mr-2"><input type="text" class="form-control" placeholder="Search Queue" id="searchqueuestr"/><div class="input-group-append"><button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"><span class="material-icons">search</span><span id="searchqueuetagdesc">Any Tag</span></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchqueuetag"></div></div></div></form><div id="QueuePaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="QueuePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="QueuePaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="QueuePaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="QueuePaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="QueueList" class="table table-hover table-sm" data-version=""><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th>#</th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="QueueButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options: [0]}' title="To top">keyboard_arrow_up</button></div><div id="QueuePaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="QueuePaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="QueuePaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="QueuePaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="QueuePaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div></div><div class="card hide" id="cardBrowse"><div class="card-header" id="panel-heading-browse"><ul class="nav nav-tabs card-header-tabs"><li class="nav-item"><a data-href='{"cmd": "appGoto", "options": ["Browse", "Database"]}' class="nav-link text-dark" href="#" id="cardBrowseNavDatabase">Database</a></li><li class="nav-item"><a data-href='{"cmd": "appGoto", "options": ["Browse", "Playlists"]}' class="nav-link text-dark" href="#" id="cardBrowseNavPlaylists">Playlists</a></li><li class="nav-item"><a data-href='{"cmd": "appGoto", "options": ["Browse", "Filesystem"]}' class="nav-link text-dark" href="#" id="cardBrowseNavFilesystem">Filesystem</a></li></ul></div><div class="card-body hide" id="cardBrowsePlaylists"><div class="btn-toolbar card-toolbar" id="BrowsePlaylistsButtons"><div class="btn-group mr-2 hide"><button data-href='{"cmd": "appGoto", "options": ["Browse", "Playlists", "All"]}' id="btnBrowsePlaylistsAll" type="button" class="btn btn-secondary">« Playlists</button></div><div class="btn-group mr-2 hide"><button id="btnPlaylistClear" type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "playlistClear", "options": []}' title="Clear playlist">clear_all</button></div><div class="btn-group mr-2"><button id="BrowsePlaylistsFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button><div class="dropdown-menu bg-dark px-2 letters" id="BrowsePlaylistsFilterLetters"></div></div><div id="BrowsePlaylistsPaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowsePlaylistsPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowsePlaylistsPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowsePlaylistsPaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowsePlaylistsPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="BrowsePlaylistsAllList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><col class="tbllastmodified"/><col class="tblaction"/><thead><tr><th></th><th>Playlist</th><th>Last modified</th><th></th></tr></thead><tbody class="clickable"></tbody></table><table id="BrowsePlaylistsDetailList" class="table table-hover table-sm hide"><caption>Playlist List</caption><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th>#</th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="BrowsePlaylistsButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="BrowsePlaylistsPaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowsePlaylistsPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowsePlaylistsPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowsePlaylistsPaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowsePlaylistsPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div><div class="card-body hide" id="cardBrowseDatabase"><div class="btn-toolbar card-toolbar" id="BrowseDatabaseButtons"><div class="btn-group mr-2"><button id="btnBrowseDatabaseByTag" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">AlbumArtist</button><div class="dropdown-menu bg-dark px-2" id="BrowseDatabaseByTagDropdown"></div></div><div class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoTagList", "options": []}' id="btnBrowseDatabaseTag" type="button" class="btn btn-secondary">« Artists</button></div><div class="btn-group mr-2"><button id="BrowseDatabaseFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button><div class="dropdown-menu bg-dark px-2 letters" id="BrowseDatabaseFilterLetters"></div></div><div class="input-group mr-2 hide"><div class="input-group-prepend"><button data-href='{"cmd": "addAllFromBrowseDatabase", "options": []}' id="BrowseDatabaseAddAllSongs" class="btn btn-secondary">Add all</button><button id="BrowseDatabaseAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="BrowseDatabaseAddAllSongsDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button></div></div></div><div id="BrowseDatabasePaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseDatabasePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseDatabasePaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseDatabasePaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseDatabasePaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="BrowseDatabaseTagList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><thead><tr><th></th><th>Artist</th></tr></thead><tbody class="clickable"></tbody></table></div><div id="BrowseDatabaseAlbumList" class="row hide"><h2 id="BrowseDatabaseAlbumListCaption"></h2></div><div class="btn-toolbar hide" id="BrowseDatabaseButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="BrowseDatabasePaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseDatabasePaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseDatabasePaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseDatabasePaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseDatabasePaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div><div class="card-body hide" id="cardBrowseFilesystem"><div class="btn-toolbar card-toolbar" id="BrowseFilesystemButtons"><div class="btn-group mr-2"><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></div><div class="input-group mr-2"><div class="input-group-prepend"><button data-href='{"cmd": "addAllFromBrowseFilesystem", "options": []}' id="BrowseFilesystemAddAllSongs" class="btn btn-secondary">Add all</button><button id="BrowseFilesystemAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="BrowseFilesystemAddAllSongsDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button></div></div></div><div id="BrowseFilesystemPaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseFilesystemPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseFilesystemPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseFilesystemPaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseFilesystemPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><ol id="BrowseBreadcrumb" class="breadcrumb"></ol><div class="table-responsive-md"><table id="BrowseFilesystemList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th></th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="BrowseFilesystemButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="BrowseFilesystemPaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="BrowseFilesystemPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="BrowseFilesystemPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="BrowseFilesystemPaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="BrowseFilesystemPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div></div><div class="card hide" id="cardSearch"><div class="card-header">Search<span id="panel-heading-search" class="text pull-right"></span></div><div class="card-body"><div class="btn-toolbar card-toolbar" id="SearchButtons"><form id="search"><div class="input-group mr-2"><input type="text" class="form-control" placeholder="Search" id="searchstr"/><div class="input-group-append"><button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"><span class="material-icons">search</span><span id="searchtagsdesc">Any Tag</span></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchtags"></div></div></div></form><div class="input-group mr-2"><div class="input-group-prepend"><button id="searchAddAllSongs" class="btn btn-secondary" data-href='{"cmd": "addAllFromSearch", "options": []}'>Add all</button><button id="searchAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button><div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchAddAllSongsDropdown"><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to queue</button><button type="button" class="btn btn-secondary btn-sm btn-block">Add all to playlist</button></div></div></div><div id="SearchPaginationTop" class="btn-group mr-2 hide"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="SearchPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="SearchPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="SearchPaginationTopPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="SearchPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div><div class="table-responsive-md"><table id="SearchList" class="table table-hover table-sm"><col class="tblnum"/><col class="tbltitle"/><col class="tblartist"/><col class="tblalbum"/><col class="tbllength"/><col class="tblaction"/><thead><tr><th></th><th>Title</th><th>Artist</th><th>Album</th><th>Duration</th><th></th></tr></thead><tbody class="clickable"></tbody></table></div><div class="btn-toolbar hide" id="SearchButtonsBottom"><div class="btn-group mr-2"><button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options": [0]}' title="To top">keyboard_arrow_up</button></div><div id="SearchPaginationBottom" class="btn-group mr-2 dropup"><button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="SearchPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button><div class="input-group-append"><button id="SearchPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button><div class="dropdown-menu bg-dark px-2 pages" id="SearchPaginationBottomPages"></div></div><button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="SearchPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button></div></div></div></div></main><footer class="footer"><nav class="navbar navbar-expand navbar-dark fixed-bottom bg-dark"><div class="d-flex flex-fill navbar-nav" id="navbar-bottom"><div id="navPlayback" class="nav-item flex-fill text-center"><a data-href='{"cmd": "appGoto", "options": ["Playback"]}' class="nav-link" href="#">Playback</a></div><div id="navQueue" class="nav-item flex-fill text-center"><a data-href='{"cmd": "appGoto", "options": ["Queue"]}' class="nav-link" href="#">Queue</a></div><div class="nav-item flex-fill text-center" id="navBrowse"><a data-href='{"cmd": "appGoto", "options": ["Browse"]}' class="nav-link" href="#">Browse</a></div><div class="nav-item flex-fill text-center" id="navSearch"><a data-href='{"cmd": "appGoto", "options": ["Search"]}' class="nav-link" href="#">Search</a></div></div></nav></footer><!-- Modals --><div class="modal fade" id="modalConnectionError" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header bg-danger text-light"><h5 class="modal-title"><span class="material-icons title-icon">error</span> Connection Error</h5></div><div class="modal-body"><p>Connection to myMPD failed. Trying to reconnect.</p></div></div></div></div><div class="modal fade" id="modalUpdateDB" tabindex="-1"><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">×</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-header"><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></div><div class="modal-body"><form id="addStreamFrm" class="needs-validation hide" novalidate><div class="row"><div class="form-group col-md-12"><label class="control-label" for="streamUrl">Stream URL</label><input type="text" class="form-control" id="streamUrl" placeholder="http://uri.to/stream.mp3"/><div class="invalid-feedback">Invalid uri</div></div></div><div class="row"><div class="form-group col-md-12"><button id="toggleAddToPlaylistBtn" class="btn btn-secondary" data-href='{"cmd": "toggleAddToPlaylistFrm", "options":[]}'>Add to playlist</button></div></div></form><form class="needs-validation hide" id="addToPlaylistFrm" novalidate><input type="hidden" id="addToPlaylistUri"/><div class="form-group"><label for="addToPlaylistPlaylist">Playlist</label><select id="addToPlaylistPlaylist" class="form-control custom-select"></select><div class="invalid-feedback">Please choose playlist.</div></div><div class="form-group hide" id="addToPlaylistNewPlaylistDiv"><label for="addToPlaylistNewPlaylist">Create Playlist</label><input id="addToPlaylistNewPlaylist" class="form-control"/><div class="invalid-feedback">Invalid filename.</div></div></form></div><div class="modal-footer" id="addToPlaylistFooter"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "addToPlaylist", "options": []}'>Add</button></div><div class="modal-footer hide" id="addStreamFooter"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "addStream", "options": []}'>Add</button></div></div></div></div><div class="modal fade" id="modalRenamePlaylist" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><form class="needs-validation" id="renamePlaylistFrm" novalidate><div class="form-group"><label for="renamePlaylistFrom">From</label><input type="text" class="form-control" id="renamePlaylistFrom" readonly></div><div class="form-group"><label for="renamePlaylistTo">To</label><input type="text" class="form-control" id="renamePlaylistTo"/><div class="invalid-feedback">Invalid filename.</div></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "renamePlaylist", "options": []}'>Save</button></div></div></div></div><div class="modal fade" id="modalSettings" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><form class="needs-validation" id="settingsFrm" novalidate><h4>MPD</h4><div class="row"><div class="form-group col-md-6"><button data-href='{"cmd":"toggleBtn", "options":["btnRandom"]}' id="btnRandom" type="button" class="btn btn-secondary btn-block" title="Random">Random</button></div><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnConsume"]}' id="btnConsume" type="button" class="btn btn-secondary btn-block" title="Consume">Consume</button></div></div><div class="row"><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnSingle"]}' id="btnSingle" type="button" class="btn btn-secondary btn-block" title="Single">Single</button></div><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnRepeat"]}' id="btnRepeat" type="button" class="btn btn-secondary btn-block" title="Repeat">Repeat</button></div></div><div class="row"><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Crossfade</div></div><input id="inputCrossfade" type="text" class="form-control border-secondary" value=""><div class="invalid-feedback">Must be a number.</div></div><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Replaygain</div></div><select id="selectReplaygain" class="form-control custom-select border-secondary"><option value="off">Off</option><option value="track">Track</option><option value="album">Album</option></select></div></div><div class="row mixramp"><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Mixramp DB</div></div><input id="inputMixrampdb" type="text" class="form-control border-secondary" value=""><div class="invalid-feedback">Must be a number.</div></div><div class="form-group input-group col-md-6 border-secondary"><div class="input-group-prepend"><div class="input-group-text bg-secondary text-light border-secondary">Mixramp Delay</div></div><input id="inputMixrampdelay" type="text" class="form-control border-secondary" value=""><div class="invalid-feedback">Must be a number.</div></div></div><hr/><h4>Jukebox</h4><div class="row"><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnJukebox"]}' id="btnJukebox" type="button" class="btn btn-secondary btn-block" title="Jukebox">Jukebox</button></div><div class="form-group input-group col-md-6 border-secondary"><select id="jukeboxPlaylist" class="form-control custom-select border-secondary"></select></div></div><hr/><h4>Notifications</h4><div class="row"><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnnotifyPage"]}' type="button" class="btn btn-secondary btn-block" id="btnnotifyPage">Page Notifications</button></div><div class="form-group col-md-6" data-toggle="buttons"><button data-href='{"cmd": "toggleBtn", "options": ["btnnotifyWeb"]}' type="button" class="btn btn-secondary btn-block" id="btnnotifyWeb">Web Notifications</button></div></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "confirmSettings", "options": []}'>Save</button></div></div></div></div><div class="modal fade" id="modalAbout" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><h4><a class="text-success" rel="noreferrer" href="https://github.com/jcorporation/ympd">myMPD</a> – <small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4><p>myMPD is a lightweight MPD web client that runs without a dedicated webserver or interpreter. It's tuned for minimal resource usage and requires only very litte dependencies. myMPD is a fork of <a class="text-success" href="http://www.ympd.org">ympd</a>.</p><ul><li>Version: <span id="mympdVersion"></span></li><li>Homepage: <a class="text-success" target="_blank" rel="noreferrer" href="https://github.com/jcorporation/mympd">https://github.com/jcorporation/mympd</a></li><li>Autor: Jürgen Mang <<a class="text-success" href="mailto:mail@jcgames.de">mail@jcgames.de</a>></li></ul><table class="table table-sm"><tbody><tr><td colspan="2" class="pt-3"><h5>Database Statistics</h5></td></tr><tr><th>Artists</th><td id="mpdstats_artists"></td></tr><tr><th>Albums</th><td id="mpdstats_albums"></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 Updated</th><td id="mpdstats_dbUpdated"></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>Play Time</th><td id="mpdstats_playtime"></td></tr><tr><td colspan="2" class="pt-3"><h5>MPD</h5></td></tr><tr><th>Protocol Version</th><td id="mpdVersion"></td></tr></tbody></table></div></div></div></div><div class="modal fade" id="modalSaveQueue" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h5 class="modal-title"><span class="material-icons title-icon">save</span> Save Queue</h5><button type="button" class="close" data-dismiss="modal">×</button></div><div class="modal-body"><form class="needs-validation" id="saveQueueFrm" novalidate><div class="row"><div class="form-group col-md-9"><label class="control-label" for="saveQueueName">Playlist Name</label><input type="text" class="form-control" id="saveQueueName"/><div class="invalid-feedback">Invalid filename</div></div></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="button" class="btn btn-success" data-href='{"cmd": "saveQueue", "options": []}'>Save Queue</button></div></div></div></div><div class="modal fade" id="modalSongDetails" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><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></div><div class="modal-body"><div class="album-cover"></div><h1></h1><div class="table-responsive-md"><table class="table table-sm"><tbody></tbody></table></div></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button></div></div></div></div><script src="/js/bootstrap-native-v4.min.js"></script><script src="/js/mympd.min.js"></script></body></html> \ No newline at end of file diff --git a/dist/htdocs/js/mympd.min.js b/dist/htdocs/js/mympd.min.js index 451bc1f..d1b7331 100644 --- a/dist/htdocs/js/mympd.min.js +++ b/dist/htdocs/js/mympd.min.js @@ -26,22 +26,23 @@ if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_PLAYER_CURRENT_SONG"},songC 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_DATABASE_SEARCH",data:{plist:"",offset:app.current.page,filter:app.current.filter,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)),selectTag("searchtags","searchtagsdesc",app.current.filter)):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();sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState);webSocketConnect();domCache.volumeBar.value=0;domCache.volumeBar.addEventListener("click",function(a){a.stopPropagation()},!1);domCache.volumeBar.addEventListener("change",function(a){sendAPI({cmd:"MPD_API_PLAYER_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_PLAYER_SEEK", -data:{songid:currentSong.currentSongId,seek:Math.ceil(domCache.progressBar.value/100*currentSong.totalTime)}})},!1);document.getElementById("volumeIcon").parentNode.addEventListener("show.bs.dropdown",function(){sendAPI({cmd:"MPD_API_PLAYER_OUTPUT_LIST"},parseOutputs)});document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_DATABASE_STATS"},parseStats)});document.getElementById("modalUpdateDB").addEventListener("hidden.bs.modal",function(){document.getElementById("updateDBprogress").classList.remove("updateDBprogressAnimate")}); -document.getElementById("modalSaveQueue").addEventListener("shown.bs.modal",function(){var a=document.getElementById("saveQueueName");a.focus();a.value="";a.classList.remove("is-invalid");document.getElementById("saveQueueFrm").classList.remove("was-validated")});document.getElementById("modalSettings").addEventListener("shown.bs.modal",function(){getSettings();document.getElementById("settingsFrm").classList.remove("was-validated");document.getElementById("inputCrossfade").classList.remove("is-invalid"); -document.getElementById("inputMixrampdb").classList.remove("is-invalid");document.getElementById("inputMixrampdelay").classList.remove("is-invalid")});document.getElementById("addToPlaylistPlaylist").addEventListener("change",function(a){"New Playlist"==this.options[this.selectedIndex].text?(document.getElementById("addToPlaylistNewPlaylistDiv").classList.remove("hide"),document.getElementById("addToPlaylistNewPlaylist").focus()):document.getElementById("addToPlaylistNewPlaylistDiv").classList.add("hide")}, -!1);addFilterLetter("BrowseFilesystemFilterLetters");addFilterLetter("BrowseDatabaseFilterLetters");addFilterLetter("BrowsePlaylistsFilterLetters");for(var a=document.querySelectorAll("[data-href]"),b=a.length,c=0;c<b;c++)a[c].classList.add("clickable"),a[c].addEventListener("click",function(a){a.preventDefault();a=JSON.parse(this.getAttribute("data-href"));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null, -$jscomp.arrayFromIterable(a.options))}},!1);a=document.getElementsByClassName("pages");b=a.length;for(c=0;c<b;c++)a[c].addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&gotoPage(a.target.getAttribute("data-page"))},!1);document.getElementById("outputs").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&a.stopPropagation();sendAPI({cmd:"MPD_API_PLAYER_TOGGLE_OUTPUT",data:{output:a.target.getAttribute("data-output-id"),state:a.target.classList.contains("active")?0:1}}); -toggleBtn(a.target.id)},!1);document.getElementById("QueueList").addEventListener("click",function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAYER_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemList").addEventListener("click",function(a){if("TD"==a.target.nodeName)switch(a.target.parentNode.getAttribute("data-type")){case "dir":appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+ -"/"+decodeURI(a.target.parentNode.getAttribute("data-uri")));break;case "song":appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"));break;case "plist":appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"))}else"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsAllList").addEventListener("click",function(a){"TD"==a.target.nodeName? -appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsDetailList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseDatabaseTagList").addEventListener("click", -function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database",app.current.view,"0/-/"+a.target.parentNode.getAttribute("data-uri"))},!1);document.getElementById("SearchList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"== -a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromBrowse():"Add all to playlist"==a.target.innerText&&showAddToPlaylist(app.current.search))},!1);document.getElementById("searchAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromSearchPlist("queue"):"Add all to playlist"==a.target.innerText&&showAddToPlaylist("SEARCH"))},!1);document.getElementById("BrowseDatabaseAddAllSongsDropdown").addEventListener("click", -function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromBrowseDatabasePlist("queue"):"Add all to playlist"==a.target.innerText&&showAddToPlaylist("DATABASE"))},!1);document.getElementById("searchtags").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,"0/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app, -app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("search").addEventListener("submit",function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1); -document.getElementById("searchstr").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("BrowseDatabaseByTagDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,a.target.getAttribute("data-tag"),"0/"+app.current.filter+"/"+app.current.search)},!1);document.getElementsByTagName("body")[0].addEventListener("click",function(a){a=document.getElementsByClassName("popover"); -for(var b=0;b<a.length;b++)a[b].remove()},!1);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus",function(){sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){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: ",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")})} +data:{songid:currentSong.currentSongId,seek:Math.ceil(domCache.progressBar.value/100*currentSong.totalTime)}})},!1);document.getElementById("navDBupdate").addEventListener("click",function(a){a.stopPropagation();a.preventDefault();a=this.getElementsByTagName("span")[0];a.innerText="keyboard_arrow_right"==a.innerText?"keyboard_arrow_down":"keyboard_arrow_right"},!1);document.getElementById("volumeIcon").parentNode.addEventListener("show.bs.dropdown",function(){sendAPI({cmd:"MPD_API_PLAYER_OUTPUT_LIST"}, +parseOutputs)});document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_DATABASE_STATS"},parseStats)});document.getElementById("modalUpdateDB").addEventListener("hidden.bs.modal",function(){document.getElementById("updateDBprogress").classList.remove("updateDBprogressAnimate")});document.getElementById("modalSaveQueue").addEventListener("shown.bs.modal",function(){var a=document.getElementById("saveQueueName");a.focus();a.value="";a.classList.remove("is-invalid"); +document.getElementById("saveQueueFrm").classList.remove("was-validated")});document.getElementById("modalSettings").addEventListener("shown.bs.modal",function(){getSettings();document.getElementById("settingsFrm").classList.remove("was-validated");document.getElementById("inputCrossfade").classList.remove("is-invalid");document.getElementById("inputMixrampdb").classList.remove("is-invalid");document.getElementById("inputMixrampdelay").classList.remove("is-invalid")});document.getElementById("addToPlaylistPlaylist").addEventListener("change", +function(a){"New Playlist"==this.options[this.selectedIndex].text?(document.getElementById("addToPlaylistNewPlaylistDiv").classList.remove("hide"),document.getElementById("addToPlaylistNewPlaylist").focus()):document.getElementById("addToPlaylistNewPlaylistDiv").classList.add("hide")},!1);addFilterLetter("BrowseFilesystemFilterLetters");addFilterLetter("BrowseDatabaseFilterLetters");addFilterLetter("BrowsePlaylistsFilterLetters");for(var a=document.querySelectorAll("[data-href]"),b=a.length,c=0;c< +b;c++)a[c].classList.add("clickable"),a[c].addEventListener("click",function(a){a.preventDefault();a=JSON.parse(this.getAttribute("data-href"));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1);a=document.getElementsByClassName("pages");b=a.length;for(c=0;c<b;c++)a[c].addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&gotoPage(a.target.getAttribute("data-page"))}, +!1);document.getElementById("outputs").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&a.stopPropagation();sendAPI({cmd:"MPD_API_PLAYER_TOGGLE_OUTPUT",data:{output:a.target.getAttribute("data-output-id"),state:a.target.classList.contains("active")?0:1}});toggleBtn(a.target.id)},!1);document.getElementById("QueueList").addEventListener("click",function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAYER_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}): +"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemList").addEventListener("click",function(a){if("TD"==a.target.nodeName)switch(a.target.parentNode.getAttribute("data-type")){case "dir":appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+decodeURI(a.target.parentNode.getAttribute("data-uri")));break;case "song":appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"));break;case "plist":appendQueue("plist", +decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"))}else"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsAllList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowsePlaylistsDetailList").addEventListener("click", +function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseDatabaseTagList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database",app.current.view,"0/-/"+a.target.parentNode.getAttribute("data-uri"))},!1);document.getElementById("SearchList").addEventListener("click",function(a){"TD"== +a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&showMenu(a.target,a)},!1);document.getElementById("BrowseFilesystemAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromBrowse():"Add all to playlist"==a.target.innerText&&showAddToPlaylist(app.current.search))},!1);document.getElementById("searchAddAllSongsDropdown").addEventListener("click", +function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromSearchPlist("queue"):"Add all to playlist"==a.target.innerText&&showAddToPlaylist("SEARCH"))},!1);document.getElementById("BrowseDatabaseAddAllSongsDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&("Add all to queue"==a.target.innerText?addAllFromBrowseDatabasePlist("queue"):"Add all to playlist"==a.target.innerText&&showAddToPlaylist("DATABASE"))},!1);document.getElementById("searchtags").addEventListener("click", +function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,"0/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app,app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view, +app.current.page+"/"+a.target.getAttribute("data-tag")+"/"+app.current.search)},!1);document.getElementById("search").addEventListener("submit",function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1);document.getElementById("searchstr").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("BrowseDatabaseByTagDropdown").addEventListener("click",function(a){"BUTTON"== +a.target.nodeName&&appGoto(app.current.app,app.current.tab,a.target.getAttribute("data-tag"),"0/"+app.current.filter+"/"+app.current.search)},!1);document.getElementsByTagName("body")[0].addEventListener("click",function(a){a=document.getElementsByClassName("popover");for(var b=0;b<a.length;b++)a[b].remove()},!1);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus",function(){sendAPI({cmd:"MPD_API_PLAYER_STATE"}, +parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){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: ",a.scope);a.update()},function(a){console.log("ServiceWorker registration failed: ", +a)})});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a;domCache.btnAdd.classList.remove("hide")});domCache.btnAdd.addEventListener("click",function(a){domCache.btnAdd.classList.add("hide");deferredPrompt.prompt();deferredPrompt.userChoice.then(function(a){"accepted"===a.outcome?console.log("User accepted the A2HS prompt"):console.log("User dismissed the A2HS prompt"); +deferredPrompt=null})});window.addEventListener("appinstalled",function(a){console.log("myMPD installed as app")})} function dragAndDropTable(a){var b=document.getElementById(a).getElementsByTagName("tbody")[0];b.addEventListener("dragstart",function(a){"TR"==a.target.nodeName&&(a.target.classList.add("opacity05"),a.dataTransfer.setDragImage(a.target,0,0),a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("Text",a.target.getAttribute("id")),dragEl=a.target.cloneNode(!0))},!1);b.addEventListener("dragleave",function(a){a.preventDefault();var b=a.target;"TD"==a.target.nodeName&&(b=a.target.parentNode);"TR"== b.nodeName&&b.classList.remove("dragover")},!1);b.addEventListener("dragover",function(a){a.preventDefault();for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover");c=a.target;"TD"==a.target.nodeName&&(c=a.target.parentNode);"TR"==c.nodeName&&c.classList.add("dragover");a.dataTransfer.dropEffect="move"},!1);b.addEventListener("dragend",function(a){for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f<e;f++)c[f].classList.remove("dragover"); document.getElementById(a.dataTransfer.getData("Text"))&&document.getElementById(a.dataTransfer.getData("Text")).classList.remove("opacity05")},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();var d=c.target;"TD"==c.target.nodeName&&(d=c.target.parentNode);var e=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),f=d.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05"); @@ -120,7 +121,7 @@ options:[d]},"Rename playlist")+addMenuItem({cmd:"delPlaylist",options:[d]},"Del a.stopPropagation();if("A"==a.target.nodeName&&(a=a.target.getAttribute("data-href"))){a=JSON.parse(atob(a));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}g.hide()}},!1);if(a=document.getElementById("advancedMenuLink"))a.addEventListener("click",function(a){a=this.getElementsByTagName("span")[0];a.innerText="keyboard_arrow_right"==a.innerText?"keyboard_arrow_down": "keyboard_arrow_right"},!1),new Collapse(a)},!1));g.show()} function sendAPI(a,b){var c=new XMLHttpRequest;c.open("POST","/api",!0);c.setRequestHeader("Content-type","application/json");c.onreadystatechange=function(){if(4==c.readyState)if(""!=c.responseText){var d=JSON.parse(c.responseText);"error"==d.type?(showNotification("Error",d.data,d.data,"danger"),console.log("Error: "+d.data)):"result"==d.type&&"ok"!=d.data?showNotification(d.data,"","","success"):void 0!=b&&"function"==typeof b&&b(d)}else console.log("Empty response for request: "+JSON.stringify(a))}; -c.send(JSON.stringify(a))}function openLocalPlayer(){window.open("/player.html#"+settings.mpdstream,"LocalPlayer")}function updateDB(){sendAPI({cmd:"MPD_API_DATABASE_UPDATE"});updateDBstarted(!0)} +c.send(JSON.stringify(a))}function openLocalPlayer(){window.open("/player.html#"+settings.mpdstream,"LocalPlayer")}function updateDB(){sendAPI({cmd:"MPD_API_DATABASE_UPDATE"});updateDBstarted(!0)}function rescanDB(){sendAPI({cmd:"MPD_API_DATABASE_RESCAN"});updateDBstarted(!0)} function updateDBstarted(a){1==a?(document.getElementById("updateDBfinished").innerText="",document.getElementById("updateDBfooter").classList.add("hide"),updateDBprogress.style.width="20px",updateDBprogress.style.marginLeft="-20px",modalUpdateDB.show(),document.getElementById("updateDBprogress").classList.add("updateDBprogressAnimate")):showNotification("Database update started","","","success")} function updateDBfinished(a){document.getElementById("modalUpdateDB").classList.contains("show")?("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%",a.style.marginLeft="0px",document.getElementById("updateDBfooter").classList.remove("hide")): "update_database"==a?showNotification("Database successfully updated.","","","success"):"update_finished"==a&&showNotification("Database update finished.","","","success")}function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_PLAYER_PLAY"}):sendAPI({cmd:"MPD_API_PLAYER_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_PLAYER_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_PLAYER_PREV"})}function clickNext(){sendAPI({cmd:"MPD_API_PLAYER_NEXT"})} diff --git a/dist/htdocs/sw.min.js b/dist/htdocs/sw.min.js index 5fe336d..5b46321 100644 --- a/dist/htdocs/sw.min.js +++ b/dist/htdocs/sw.min.js @@ -11,4 +11,4 @@ void 0;try{d=a.then}catch(h){this.reject_(h);return}"function"==typeof d?this.se 0;a<this.onSettledCallbacks_.length;++a)l.asyncExecute(this.onSettledCallbacks_[a]);this.onSettledCallbacks_=null}};var l=new b;c.prototype.settleSameAsPromise_=function(a){var b=this.createResolveAndReject_();a.callWhenSettled_(b.resolve,b.reject)};c.prototype.settleSameAsThenable_=function(a,b){var c=this.createResolveAndReject_();try{a.call(b,c.resolve,c.reject)}catch(k){c.reject(k)}};c.prototype.then=function(a,b){function d(a,b){return"function"==typeof a?function(b){try{f(a(b))}catch(m){e(m)}}: b}var f,e,g=new c(function(a,b){f=a;e=b});this.callWhenSettled_(d(a,f),d(b,e));return g};c.prototype.catch=function(a){return this.then(void 0,a)};c.prototype.callWhenSettled_=function(a,b){function c(){switch(d.state_){case 1:a(d.result_);break;case 2:b(d.result_);break;default:throw Error("Unexpected state: "+d.state_);}}var d=this;null==this.onSettledCallbacks_?l.asyncExecute(c):this.onSettledCallbacks_.push(c)};c.resolve=f;c.reject=function(a){return new c(function(b,c){c(a)})};c.race=function(a){return new c(function(b, c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE="myMPD-cache-v4.2.1",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){if(a.request.url.match("^http://"))return!1;a.respondWith(caches.match(a.request).then(function(b){return b?b:fetch(a.request)}))});self.addEventListener("activate",function(a){a.waitUntil(caches.keys().then(function(a){return Promise.all(a.map(function(a){if(a!=CACHE)return caches.delete(a)}))}))}); diff --git a/htdocs/sw.js b/htdocs/sw.js index b1d4ed4..a163b4e 100644 --- a/htdocs/sw.js +++ b/htdocs/sw.js @@ -3,10 +3,10 @@ var urlsToCache = [ '/', '/player.html', '/css/bootstrap.min.css', - '/css/mympd.css', + '/css/mympd.min.css', '/js/bootstrap-native-v4.min.js', - '/js/mympd.js', - '/js/player.js', + '/js/mympd.min.js', + '/js/player.min.js', '/assets/appicon-167.png', '/assets/appicon-192.png', '/assets/appicon-512.png',