Merge pull request #26 from jcorporation/devel

Merge Devel into master
This commit is contained in:
Jürgen Mang 2018-07-30 00:40:12 +02:00 committed by GitHub
commit b2fedfec1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 727 additions and 248 deletions

View File

@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 2.6)
project (mympd C)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
set(CPACK_PACKAGE_VERSION_MAJOR "3")
set(CPACK_PACKAGE_VERSION_MINOR "2")
set(CPACK_PACKAGE_VERSION_PATCH "1")
set(CPACK_PACKAGE_VERSION_MINOR "3")
set(CPACK_PACKAGE_VERSION_PATCH "0")
if(CMAKE_BUILD_TYPE MATCHES RELEASE)
set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs")

View File

@ -14,9 +14,12 @@ Design principles:
- Keep security in mind
Featurelist:
- Browse mpd database by artist
- Control mpd functions (play, pause, etc.)
- Set mpd settings (repeat, random, etc.)
- Browse mpd database by albumartist
- Browse filesystem and playlists
- Queue management
- Playlist management
- Advanced search
- Progressiv Web App enabled
- Local coverart support

View File

@ -212,4 +212,25 @@ div.alertBoxActive {
.opacity05 {
opacity:0.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;
/* Required to make elements draggable in old WebKit */
-khtml-user-drag: element;
-webkit-user-drag: element;
}

View File

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

View File

@ -73,7 +73,7 @@
<div class="alert alert-danger" role="alert">JavaScript is disabled!</div>
</noscript>
<div class="card" id="cardPlayback">
<div class="card-header">Now playing</div>
<div class="card-header">Playback</div>
<div class="card-body">
<div class="album-cover" id="album-cover"></div>
<h1 id="currenttrack"></h1>
@ -104,7 +104,7 @@
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'sendAPI', 'options': [{'cmd':'MPD_API_RM_ALL'}]}" title="Clear queue">
<span class="material-icons">clear_all</span>
</button>
<button type="button" class="btn btn-secondary" data-toggle="modal" data-target="#modalSavequeue" title="Save queue">
<button type="button" class="btn btn-secondary" data-toggle="modal" data-target="#modalSaveQueue" title="Save queue">
<span class="material-icons">save</span>
</button>
</div>
@ -195,6 +195,14 @@
<div class="card-body hide" id="cardBrowsePlaylists">
<div class="btn-toolbar card-toolbar" id="BrowsePlaylistsButtons" role="toolbar">
<div class="btn-group mr-2 hide">
<button data-href="{'cmd': 'appGoto', 'options': ['Browse','Playlists','All']}" id="btnBrowsePlaylistsAll" type="button" class="btn btn-secondary">&laquo; Playlists</button>
</div>
<div class="btn-group mr-2 hide">
<button id="btnPlaylistClear" type="button" class="btn btn-secondary" data-href="{'cmd': 'playlistClear', 'options': []}" title="Clear playlist">
<span class="material-icons">clear_all</span>
</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">
@ -210,9 +218,8 @@
<button data-href="{'cmd': 'gotoPage', 'options': ['next']}" id="BrowsePlaylistsPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
</div>
<div class="table-responsive-md">
<table id="BrowsePlaylistsList" class="table table-hover table-sm">
<table id="BrowsePlaylistsAllList" class="table table-hover table-sm">
<col class="tblnum"/>
<col class="tbltitle"/>
<col class="tbllastmodified"/>
@ -228,6 +235,27 @@
<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" id="BrowsePlaylistsButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
@ -248,10 +276,9 @@
</div>
<div class="card-body hide" id="cardBrowseDatabase">
<div class="btn-toolbar card-toolbar" id="BrowseDatabaseButtons" role="toolbar">
<div class="btn-group mr-2">
<button data-href="{'cmd': 'appGoto', 'options': ['Browse','Database','Artist']}" id="btnBrowseDatabaseArtist" type="button" class="btn btn-secondary hide">&laquo; Artists</button>
<div class="btn-group mr-2 hide">
<button data-href="{'cmd': 'appGoto', 'options': ['Browse','Database','Artist']}" id="btnBrowseDatabaseArtist" type="button" class="btn btn-secondary">&laquo; Artists</button>
</div>
<div class="btn-group mr-2">
<button id="BrowseDatabaseFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button>
@ -286,7 +313,7 @@
</table>
</div>
<div id="BrowseDatabaseAlbumCards" class="row"></div>
<div id="BrowseDatabaseAlbumCards" class="row hide"></div>
<div class="btn-toolbar" id="BrowseDatabaseButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
@ -462,8 +489,8 @@
</nav>
</footer>
<!-- Modal -->
<div class="modal" id="modalConnectionError" role="dialog">
<!-- Modals -->
<div class="modal fade" id="modalConnectionError" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-danger text-light">
@ -476,7 +503,59 @@
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="modalAddToPlaylist" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><span class="material-icons title-icon">playlist_add</span> Add to playlist</h5>
</div>
<div class="modal-body">
<form id="addToPlaylistFrm">
<input type="hidden" id="addToPlaylistUri"/>
<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">Playlist</div>
</div>
<select id="addToPlaylistPlaylist" class="form-control custom-select border-secondary">
</select>
</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': 'addToPlaylist', 'options': []}">Save</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modalRenamePlaylist" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><span class="material-icons title-icon">playlist_add</span>Rename playlist</h5>
</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" role="dialog" aria-labelledby="settingsLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
@ -529,7 +608,7 @@
<option value="track">Track</option>
<option value="album">Album</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="form-group input-group col-md-6 border-secondary">
@ -570,7 +649,6 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal -->
<div class="modal fade" id="modalAbout" tabindex="-1" role="dialog" aria-labelledby="settingsLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
@ -608,7 +686,6 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal -->
<div class="modal fade" id="modalAddstream" tabindex="-1" role="dialog" aria-labelledby="addstreamLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
@ -619,11 +696,12 @@
</button>
</div>
<div class="modal-body">
<form id="addstreamFrm">
<form id="addStreamFrm" class="needs-validation" 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" />
<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>
</form>
@ -636,21 +714,22 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade" id="modalSavequeue" tabindex="-1" role="dialog" aria-labelledby="savequeueLabel" aria-hidden="true">
<div class="modal fade" id="modalSaveQueue" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="savequeueLabel"><span class="material-icons title-icon">save</span> Save Queue</h5>
<h5 class="modal-title"><span class="material-icons title-icon">save</span> Save Queue</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form>
<form class="needs-validation" id="saveQueueFrm" novalidate>
<div class="row">
<div class="form-group col-md-9">
<label class="control-label" for="playlistname">Playlist Name</label>
<input type="text" class="form-control" id="playlistname"/>
<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>
@ -694,7 +773,7 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script src="js/bootstrap-native-v4.min.js" type="text/javascript"></script>
<script src="js/mpd.js" type="text/javascript"></script>
<script src="js/bootstrap-native-v4.min.js"></script>
<script src="js/mpd.js"></script>
</body>
</html>

View File

@ -31,6 +31,7 @@ var playstate = '';
var settings = {};
var alertTimeout;
let deferredPrompt;
var dragEl;
var app = {};
app.apps = { "Playback": { "state": "0/-/", "scrollPos": 0 },
@ -38,7 +39,12 @@ app.apps = { "Playback": { "state": "0/-/", "scrollPos": 0 },
"Browse": {
"active": "Database",
"tabs": { "Filesystem": { "state": "0/-/", "scrollPos": 0 },
"Playlists": { "state": "0/-/", "scrollPos": 0 },
"Playlists": {
"active": "All",
"views": { "All": { "state": "0/-/", "scrollPos": 0 },
"Detail": { "state": "0/-/", "scrollPos": 0 }
}
},
"Database": {
"active": "Artist",
"views": { "Artist": { "state": "0/-/", "scrollPos": 0 },
@ -73,8 +79,10 @@ domCache.btnAdd = document.getElementById('btnAdd');
var modalConnectionError = new Modal(document.getElementById('modalConnectionError'));
var modalSettings = new Modal(document.getElementById('modalSettings'));
var modalAddstream = new Modal(document.getElementById('modalAddstream'));
var modalSavequeue = new Modal(document.getElementById('modalSavequeue'));
var modalSavequeue = new Modal(document.getElementById('modalSaveQueue'));
var modalSongDetails = new Modal(document.getElementById('modalSongDetails'));
var modalAddToPlaylist = new Modal(document.getElementById('modalAddToPlaylist'));
var modalRenamePlaylist = new Modal(document.getElementById('modalRenamePlaylist'));
var mainMenu = new Dropdown(document.getElementById('mainMenu'));
function appPrepare(scrollPos) {
@ -102,6 +110,11 @@ function appPrepare(scrollPos) {
}
scrollTo(scrollPos);
}
var list = document.getElementById(app.current.app +
(app.current.tab == undefined ? '' : app.current.tab) +
(app.current.view == undefined ? '' : app.current.view) + 'List');
if (list)
list.classList.add('opacity05');
}
function appGoto(a,t,v,s) {
@ -171,13 +184,6 @@ function appRoute() {
sendAPI({"cmd":"MPD_API_GET_CURRENT_SONG"}, songChange);
}
else if (app.current.app == 'Queue' ) {
document.getElementById('QueueList').classList.add('opacity05');
/* if (app.last.app != app.current.app) {
if (app.current.search.length < 2) {
setPagination(app.current.page);
}
}
*/
var btns = document.getElementById('searchqueuetag').getElementsByTagName('button');
for (var i = 0; i < btns.length; i++) {
btns[i].classList.remove('active');
@ -188,23 +194,23 @@ function appRoute() {
}
getQueue();
}
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists') {
document.getElementById('BrowsePlaylistsList').classList.add('opacity05');
sendAPI({"cmd":"MPD_API_GET_PLAYLISTS","data": {"offset": app.current.page, "filter": app.current.filter}},parsePlaylists);
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'All') {
sendAPI({"cmd":"MPD_API_GET_PLAYLISTS","data": {"offset": app.current.page, "filter": app.current.filter}}, parsePlaylists);
doSetFilterLetter('BrowsePlaylistsFilter');
}
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'Detail') {
sendAPI({"cmd":"MPD_API_GET_PLAYLIST_LIST","data": {"offset": app.current.page, "filter": app.current.filter, "uri": app.current.search}}, parsePlaylists);
doSetFilterLetter('BrowsePlaylistsFilter');
}
else if (app.current.app == 'Browse' && app.current.tab == 'Database' && app.current.view == 'Artist') {
document.getElementById('BrowseDatabaseArtistList').classList.add('opacity05');
sendAPI({"cmd":"MPD_API_GET_ARTISTS","data": {"offset": app.current.page, "filter": app.current.filter}}, parseListDBtags);
doSetFilterLetter('BrowseDatabaseFilter');
}
else if (app.current.app == 'Browse' && app.current.tab == 'Database' && app.current.view == 'Album') {
document.getElementById('BrowseDatabaseAlbumCards').classList.add('opacity05');
sendAPI({"cmd":"MPD_API_GET_ARTISTALBUMS","data": {"offset": app.current.page, "filter": app.current.filter, "albumartist": app.current.search}}, parseListDBtags);
doSetFilterLetter('BrowseDatabaseFilter');
}
else if (app.current.app == 'Browse' && app.current.tab == 'Filesystem') {
document.getElementById('BrowseFilesystemList').classList.add('opacity05');
sendAPI({"cmd":"MPD_API_GET_FILESYSTEM","data": {"offset": app.current.page, "path": (app.current.search ? app.current.search : "/"), "filter": app.current.filter}}, parseFilesystem);
// Don't add all songs from root
if (app.current.search)
@ -238,15 +244,11 @@ function appRoute() {
}
else if (app.current.app == 'Search') {
document.getElementById('searchstr').focus();
document.getElementById('SearchList').classList.add('opacity05');
if (app.last.app != app.current.app) {
if (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>';
// else
// setPagination(app.current.page);
// document.getElementById('searchstr').value = app.current.search;
}
if (app.current.search.length >= 2) {
@ -255,8 +257,8 @@ function appRoute() {
document.getElementById('SearchList').getElementsByTagName('tbody')[0].innerHTML = '';
document.getElementById('searchAddAllSongs').setAttribute('disabled', 'disabled');
document.getElementById('panel-heading-search').innerText = '';
// setPagination(app.current.page);
document.getElementById('SearchList').classList.remove('opacity05');
setPagination(0);
}
var btns = document.getElementById('searchtags').getElementsByTagName('button');
@ -291,7 +293,7 @@ function appInit() {
domCache.progressBar.value = 0;
domCache.progressBar.addEventListener('change', function(event) {
if(current_song && current_song.currentSongId >= 0) {
if (current_song && current_song.currentSongId >= 0) {
var seekVal = Math.ceil(current_song.totalTime * (domCache.progressBar.value / 100));
sendAPI({"cmd": "MPD_API_SET_SEEK", "data": {"songid":current_song.currentSongId,"seek": seekVal}});
}
@ -300,6 +302,14 @@ function appInit() {
document.getElementById('modalAbout').addEventListener('shown.bs.modal', function () {
sendAPI({"cmd": "MPD_API_GET_STATS"}, parseStats);
});
document.getElementById('modalSaveQueue').addEventListener('shown.bs.modal', function () {
var plName = document.getElementById('saveQueueName');
plName.focus();
plName.value = '';
plName.classList.remove('is-invalid');
document.getElementById('saveQueueFrm').classList.remove('was-validated');
});
document.getElementById('modalSettings').addEventListener('shown.bs.modal', function () {
getSettings();
@ -310,11 +320,11 @@ function appInit() {
});
document.getElementById('modalAddstream').addEventListener('shown.bs.modal', function () {
document.getElementById('streamurl').focus();
});
document.getElementById('addstreamFrm').addEventListener('submit', function () {
addStream();
var streamUrl = document.getElementById('streamUrl')
streamUrl.focus();
streamUrl.value = '';
streamUrl.classList.remove('is-invalid');
document.getElementById('addStreamFrm').classList.remove('was-validated');
});
addFilterLetter('BrowseFilesystemFilterLetters');
@ -386,7 +396,7 @@ function appInit() {
}
}, false);
document.getElementById('BrowsePlaylistsList').addEventListener('click', function(event) {
document.getElementById('BrowsePlaylistsAllList').addEventListener('click', function(event) {
if (event.target.nodeName == 'TD') {
appendQueue('plist', decodeURI(event.target.parentNode.getAttribute("data-uri")), event.target.parentNode.getAttribute("data-name"));
}
@ -395,6 +405,16 @@ function appInit() {
showMenu(event.target);
}
}, false);
document.getElementById('BrowsePlaylistsDetailList').addEventListener('click', function(event) {
if (event.target.nodeName == 'TD') {
appendQueue('plist', decodeURI(event.target.parentNode.getAttribute("data-uri")), event.target.parentNode.getAttribute("data-name"));
}
else if (event.target.nodeName == 'A') {
event.preventDefault();
showMenu(event.target);
}
}, false);
document.getElementById('BrowseDatabaseArtistList').addEventListener('click', function(event) {
if (event.target.nodeName == 'TD') {
@ -438,6 +458,9 @@ function appInit() {
appGoto('Search', undefined, undefined, '0/' + app.current.filter + '/' + this.value);
}, false);
dragAndDropTable('QueueList');
dragAndDropTable('BrowsePlaylistsDetailList');
window.addEventListener('hashchange', appRoute, false);
document.addEventListener('keydown', function(event) {
@ -464,6 +487,7 @@ function appInit() {
navigator.serviceWorker.register('/sw.js', {scope: '/'}).then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
registration.update();
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
@ -471,21 +495,21 @@ function appInit() {
});
}
window.addEventListener('beforeinstallprompt', (e) => {
window.addEventListener('beforeinstallprompt', function(event) {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
event.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
deferredPrompt = event;
});
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
window.addEventListener('beforeinstallprompt', function(event) {
event.preventDefault();
deferredPrompt = event;
// Update UI notify the user they can add to home screen
domCache.btnAdd.classList.remove('hide');
});
domCache.btnAdd.addEventListener('click', (e) => {
domCache.btnAdd.addEventListener('click', function(event) {
// hide our user interface that shows our A2HS button
domCache.btnAdd.classList.add('hide');
// Show the prompt
@ -500,11 +524,82 @@ function appInit() {
});
});
window.addEventListener('appinstalled', (evt) => {
console.log('appinstalled');
window.addEventListener('appinstalled', function(event) {
console.log('myMPD installed as app');
});
}
function dragAndDropTable(table) {
var tableBody=document.getElementById(table).getElementsByTagName('tbody')[0];
tableBody.addEventListener('dragstart', function(event) {
if (event.target.nodeName == 'TR') {
event.target.classList.add('opacity05');
event.dataTransfer.setDragImage(event.target, 0, 0);
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('Text', event.target.getAttribute('id'));
dragEl = event.target.cloneNode(true);
}
}, false);
tableBody.addEventListener('dragleave', function(event) {
event.preventDefault();
var target = event.target;
if (event.target.nodeName == 'TD')
target = event.target.parentNode;
if (target.nodeName == 'TR')
target.classList.remove('dragover');
}, false);
tableBody.addEventListener('dragover', function(event) {
event.preventDefault();
var tr = tableBody.querySelectorAll('.dragover');
var trLen = tr.length;
for (var i = 0; i < trLen; i++) {
tr[i].classList.remove('dragover');
}
var target = event.target;
if (event.target.nodeName == 'TD')
target = event.target.parentNode;
if (target.nodeName == 'TR')
target.classList.add('dragover');
event.dataTransfer.dropEffect = 'move';
}, false);
tableBody.addEventListener('dragend', function(event) {
var tr = tableBody.querySelectorAll('.dragover');
var trLen = tr.length;
for (var i = 0; i < trLen; i++) {
tr[i].classList.remove('dragover');
}
if (document.getElementById(event.dataTransfer.getData('Text')))
document.getElementById(event.dataTransfer.getData('Text')).classList.remove('opacity05');
}, false);
tableBody.addEventListener('drop', function(event) {
event.stopPropagation();
event.preventDefault();
var target = event.target;
if (event.target.nodeName == 'TD')
target = event.target.parentNode;
var oldSongpos = document.getElementById(event.dataTransfer.getData('Text')).getAttribute('data-songpos');
var newSongpos = target.getAttribute('data-songpos');
document.getElementById(event.dataTransfer.getData('Text')).remove();
dragEl.classList.remove('opacity05');
tableBody.insertBefore(dragEl, target);
var tr = tableBody.querySelectorAll('.dragover');
var trLen = tr.length;
for (var i = 0; i < trLen; i++) {
tr[i].classList.remove('dragover');
}
document.getElementById(table).classList.add('opacity05');
if (app.current.app == 'Queue')
sendAPI({"cmd": "MPD_API_MOVE_TRACK","data": {"from": oldSongpos, "to": newSongpos}});
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'Detail')
playlistMoveTrack(oldSongpos, newSongpos);
}, false);
}
function playlistMoveTrack(from, to) {
sendAPI({"cmd": "MPD_API_PLAYLIST_MOVE_TRACK","data": { "plist": app.current.search, "from": from, "to": to}});
sendAPI({"cmd": "MPD_API_GET_PLAYLIST_LIST","data": {"offset": app.current.page, "filter": app.current.filter, "uri": app.current.search}}, parsePlaylists);
}
function webSocketConnect() {
socket = new WebSocket(getWsUrl());
@ -514,10 +609,11 @@ function webSocketConnect() {
showNotification('Connected to myMPD', '', '', 'success');
modalConnectionError.hide();
appRoute();
sendAPI({"cmd":"MPD_API_GET_OUTPUTNAMES"}, parseOutputnames);
}
socket.onmessage = function got_packet(msg) {
if(msg.data === last_state || msg.data.length == 0)
if (msg.data === last_state || msg.data.length == 0)
return;
try {
@ -534,7 +630,7 @@ function webSocketConnect() {
showNotification('myMPD lost connection to MPD', '', '', 'danger');
break;
case 'update_queue':
if(app.current.app === 'Queue')
if (app.current.app === 'Queue')
getQueue();
break;
case 'song_change':
@ -551,8 +647,8 @@ function webSocketConnect() {
console.log('disconnected');
modalConnectionError.show();
setTimeout(function() {
console.log('reconnect');
webSocketConnect();
console.log('reconnect');
webSocketConnect();
}, 3000);
}
@ -724,7 +820,7 @@ function parseState(obj) {
} else {
domCache.volumeControl.classList.remove('hide');
domCache.volumePrct.innerText = obj.data.volume + ' %';
if(obj.data.volume == 0)
if (obj.data.volume == 0)
domCache.volumeIcon.innerText = 'volume_off';
else if (obj.data.volume < 50)
domCache.volumeIcon.innerText = 'volume_down';
@ -807,14 +903,14 @@ function parseQueue(obj) {
var tr = tbody.getElementsByTagName('tr');
for (var i = 0; i < nrItems; i++) {
if (tr[i])
if (tr[i].getAttribute('data-trackid') == obj.data[i].id)
if (tr[i].getAttribute('data-trackid') == obj.data[i].id && tr[i].getAttribute('data-songpos') == (obj.data[i].pos + 1))
continue;
var minutes = Math.floor(obj.data[i].duration / 60);
var seconds = obj.data[i].duration - minutes * 60;
var duration = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
var row = document.createElement('tr');
row.setAttribute('draggable','true');
row.setAttribute('data-trackid', obj.data[i].id);
row.setAttribute('id','queueTrackId' + obj.data[i].id);
row.setAttribute('data-songpos', (obj.data[i].pos + 1));
@ -831,11 +927,11 @@ function parseQueue(obj) {
else
tbody.append(row);
}
var tr_length=tr.length - 1;
var tr_length = tr.length - 1;
for (var i = tr_length; i >= nrItems; i --) {
tr[i].remove();
}
if (obj.type == 'queuesearch' && nrItems == 0)
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">No results, please refine your search!</td></tr>';
@ -877,23 +973,23 @@ function parseFilesystem(obj) {
switch(obj.data[i].type) {
case 'dir':
row.innerHTML = '<td><span class="material-icons">folder_open</span></td>' +
'<td colspan="4">' + obj.data[i].name + '</td>' +
'<td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
'<td colspan="4">' + obj.data[i].name + '</td>' +
'<td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
break;
case 'song':
var minutes = Math.floor(obj.data[i].duration / 60);
var seconds = obj.data[i].duration - minutes * 60;
row.innerHTML = '<td><span class="material-icons">music_note</span></td>' +
'<td>' + obj.data[i].title + '</td>' +
'<td>' + obj.data[i].artist + '</td>' +
'<td>' + obj.data[i].album + '</td>' +
'<td>' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds +
'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
'<td>' + obj.data[i].title + '</td>' +
'<td>' + obj.data[i].artist + '</td>' +
'<td>' + obj.data[i].album + '</td>' +
'<td>' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds +
'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
break;
case 'plist':
row.innerHTML = '<td><span class="material-icons">list</span></td>' +
'<td colspan="4">' + obj.data[i].name + '</td>' +
'<td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
'<td colspan="4">' + obj.data[i].name + '</td>' +
'<td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
break;
}
if (i < tr.length)
@ -901,7 +997,7 @@ function parseFilesystem(obj) {
else
tbody.append(row);
}
var tr_length=tr.length - 1;
var tr_length = tr.length - 1;
for (var i = tr_length; i >= nrItems; i --) {
tr[i].remove();
}
@ -917,49 +1013,104 @@ function parseFilesystem(obj) {
function parsePlaylists(obj) {
if (app.current.app !== 'Browse' && app.current.tab !== 'Playlists')
return;
var nrItems = obj.data.length;
var tbody = document.getElementById(app.current.app + app.current.tab + 'List').getElementsByTagName('tbody')[0];
var tr = tbody.getElementsByTagName('tr');
for (var i = 0; i < nrItems; i++) {
var uri = encodeURI(obj.data[i].uri);
if (tr[i])
if (tr[i].getAttribute('data-uri') == uri)
continue;
var d = new Date(obj.data[i].last_modified * 1000);
var row = document.createElement('tr');
row.setAttribute('data-uri', uri);
row.setAttribute('data-type', 'plist');
row.setAttribute('data-name', obj.data[i].name);
row.innerHTML = '<td><span class="material-icons">list</span></td>' +
'<td>' + obj.data[i].name + '</td>' +
'<td>'+ d.toUTCString() + '</td>' +
'<td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
if (i < tr.length)
tr[i].replaceWith(row);
else
tbody.append(row);
if (app.current.view == 'All') {
document.getElementById('BrowsePlaylistsAllList').classList.remove('hide');
document.getElementById('BrowsePlaylistsDetailList').classList.add('hide');
document.getElementById('btnBrowsePlaylistsAll').parentNode.classList.add('hide');
document.getElementById('btnPlaylistClear').parentNode.classList.add('hide');
} else {
if (obj.uri.indexOf('.') > -1) {
document.getElementById('BrowsePlaylistsDetailList').setAttribute('data-ro', 'true')
document.getElementById('btnPlaylistClear').parentNode.classList.add('hide');
}
else {
document.getElementById('BrowsePlaylistsDetailList').setAttribute('data-ro', 'false');
document.getElementById('btnPlaylistClear').parentNode.classList.remove('hide');
}
document.getElementById('BrowsePlaylistsDetailList').setAttribute('data-uri', obj.uri);
document.getElementById('BrowsePlaylistsDetailList').getElementsByTagName('caption')[0].innerText = 'Playlist: ' + obj.uri;
document.getElementById('BrowsePlaylistsDetailList').classList.remove('hide');
document.getElementById('BrowsePlaylistsAllList').classList.add('hide');
document.getElementById('btnBrowsePlaylistsAll').parentNode.classList.remove('hide');
}
var tr_length=tr.length - 1;
var nrItems = obj.data.length;
var tbody = document.getElementById(app.current.app + app.current.tab + app.current.view + 'List').getElementsByTagName('tbody')[0];
var tr = tbody.getElementsByTagName('tr');
if (app.current.view == 'All') {
for (var i = 0; i < nrItems; i++) {
var uri = encodeURI(obj.data[i].uri);
if (tr[i])
if (tr[i].getAttribute('data-uri') == uri)
continue;
var d = new Date(obj.data[i].last_modified * 1000);
var row = document.createElement('tr');
row.setAttribute('data-uri', uri);
row.setAttribute('data-type', 'plist');
row.setAttribute('data-name', obj.data[i].name);
row.innerHTML = '<td><span class="material-icons">list</span></td>' +
'<td>' + obj.data[i].name + '</td>' +
'<td>'+ d.toUTCString() + '</td>' +
'<td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
if (i < tr.length)
tr[i].replaceWith(row);
else
tbody.append(row);
}
}
else if (app.current.view == 'Detail') {
for (var i = 0; i < nrItems; i++) {
var uri = encodeURI(obj.data[i].uri);
var songpos = obj.offset + i + 1;
if (tr[i])
if (tr[i].getAttribute('data-uri') == uri && tr[i].getAttribute('id') == 'playlistTrackId' + songpos)
continue;
var row = document.createElement('tr');
row.setAttribute('draggable','true');
row.setAttribute('id','playlistTrackId' + songpos);
row.setAttribute('data-type', obj.data[i].type);
row.setAttribute('data-uri', uri);
row.setAttribute('data-name', obj.data[i].name);
row.setAttribute('data-songpos', songpos);
var minutes = Math.floor(obj.data[i].duration / 60);
var seconds = obj.data[i].duration - minutes * 60;
row.innerHTML = '<td>' + songpos + '</td>' +
'<td>' + obj.data[i].title + '</td>' +
'<td>' + obj.data[i].artist + '</td>' +
'<td>' + obj.data[i].album + '</td>' +
'<td>' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds +
'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
if (i < tr.length)
tr[i].replaceWith(row);
else
tbody.append(row);
}
}
var tr_length = tr.length - 1;
for (var i = tr_length; i >= nrItems; i --) {
tr[i].remove();
}
setPagination(obj.totalEntities);
if (nrItems == 0)
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">No playlists found.</td></tr>'
document.getElementById(app.current.app + (app.current.tab==undefined ? '' : app.current.tab) + 'List').classList.remove('opacity05');
if (nrItems == 0)
if (app.current.view == 'All')
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">No playlists found.</td></tr>';
else
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">Empty playlist.</td></tr>';
document.getElementById(app.current.app + app.current.tab + app.current.view + 'List').classList.remove('opacity05');
}
function parseListDBtags(obj) {
if(app.current.app !== 'Browse' && app.current.tab !== 'Database' && app.current.view !== 'Artist') return;
if (app.current.app !== 'Browse' && app.current.tab !== 'Database' && app.current.view !== 'Artist') return;
if (obj.tagtype == 'AlbumArtist') {
document.getElementById('BrowseDatabaseAlbumCards').classList.add('hide');
document.getElementById('BrowseDatabaseArtistList').classList.remove('hide');
document.getElementById('btnBrowseDatabaseArtist').classList.add('hide');
document.getElementById('btnBrowseDatabaseArtist').parentNode.classList.add('hide');
var nrItems = obj.data.length;
var tbody = document.getElementById(app.current.app + app.current.tab + app.current.view + 'List').getElementsByTagName('tbody')[0];
var tr = tbody.getElementsByTagName('tr');
@ -994,7 +1145,7 @@ function parseListDBtags(obj) {
} else if (obj.tagtype == 'Album') {
document.getElementById('BrowseDatabaseAlbumCards').classList.remove('hide');
document.getElementById('BrowseDatabaseArtistList').classList.add('hide');
document.getElementById('btnBrowseDatabaseArtist').classList.remove('hide');
document.getElementById('btnBrowseDatabaseArtist').parentNode.classList.remove('hide');
var nrItems = obj.data.length;
var cardContainer = document.getElementById('BrowseDatabaseAlbumCards');
var cards = cardContainer.querySelectorAll('.col-md');
@ -1023,7 +1174,7 @@ function parseListDBtags(obj) {
sendAPI({"cmd":"MPD_API_GET_ARTISTALBUMTITLES", "data": { "albumartist": obj.searchstr, "album": obj.data[i].value}}, parseListTitles);
}
var cards_length=cards.length - 1;
var cards_length = cards.length - 1;
for (var i = cards_length; i >= nrItems; i --) {
cards[i].remove();
}
@ -1178,6 +1329,74 @@ function parseSongDetails(obj) {
}
}
function playlistDetails(uri) {
appGoto('Browse', 'Playlists', 'Detail', '0/-/' + uri);
}
function removeFromPlaylist(uri, pos) {
sendAPI({"cmd": "MPD_API_RM_PLAYLIST_TRACK", "data": {"uri": uri, "track": pos}});
document.getElementById('BrowsePlaylistsDetailList').classList.add('opacity05');
sendAPI({"cmd": "MPD_API_GET_PLAYLIST_LIST", "data": {"offset": app.current.page, "filter": app.current.filter, "uri": app.current.search}}, parsePlaylists);
}
function playlistClear() {
var uri = document.getElementById('BrowsePlaylistsDetailList').getAttribute('data-uri');
sendAPI({"cmd": "MPD_API_PLAYLIST_CLEAR", "data": {"uri": uri}});
document.getElementById('BrowsePlaylistsDetailList').classList.add('opacity05');
sendAPI({"cmd": "MPD_API_GET_PLAYLIST_LIST", "data": {"offset": app.current.page, "filter": app.current.filter, "uri": app.current.search}}, parsePlaylists);
}
function getAllPlaylists(obj) {
var nrItems = obj.data.length;
var playlists = '';
for (var i = 0; i < nrItems; i++) {
playlists += '<option>' + obj.data[i].uri + '</option>';
}
document.getElementById('addToPlaylistPlaylist').innerHTML += playlists;
if (obj.totalEntities > obj.returnedEntities) {
obj.offset += settings.max_elements_per_page;
sendAPI({"cmd":"MPD_API_GET_PLAYLISTS","data": {"offset": obj.offset, "filter": "-"}}, getAllPlaylists);
}
}
function showAddToPlaylist(uri) {
modalAddToPlaylist.show();
document.getElementById('addToPlaylistUri').value = uri;
document.getElementById('addToPlaylistPlaylist').innerHTML = '';
sendAPI({"cmd":"MPD_API_GET_PLAYLISTS","data": {"offset": 0, "filter": "-"}}, getAllPlaylists);
}
function addToPlaylist() {
var uri = document.getElementById('addToPlaylistUri').value;
var plistEl = document.getElementById('addToPlaylistPlaylist');
var plist = plistEl.options[plistEl.selectedIndex].text;
sendAPI({"cmd": "MPD_API_ADD_TO_PLAYLIST", "data": {"uri": uri, "plist": plist}});
modalAddToPlaylist.hide();
}
function showRenamePlaylist(from) {
document.getElementById('renamePlaylistFrm').classList.remove('was-validated');
document.getElementById('renamePlaylistTo').classList.remove('is-invalid');
modalRenamePlaylist.show();
document.getElementById('renamePlaylistFrom').value = from;
document.getElementById('renamePlaylistTo').value = '';
}
function renamePlaylist() {
var from = document.getElementById('renamePlaylistFrom').value;
var to = document.getElementById('renamePlaylistTo').value;
var valid = to.replace(/\w/g,'');
if (to != '' && to != from && valid == '') {
sendAPI({"cmd": "MPD_API_PLAYLIST_RENAME", "data": {"from": from, "to": to}});
modalRenamePlaylist.hide();
sendAPI({"cmd":"MPD_API_GET_PLAYLISTS","data": {"offset": app.current.page, "filter": app.current.filter}}, parsePlaylists);
}
else {
document.getElementById('renamePlaylistTo').classList.add('is-invalid');
document.getElementById('renamePlaylistFrm').classList.add('was-validated');
}
}
function showMenu(el) {
var type = el.getAttribute('data-type');
var uri = decodeURI(el.getAttribute('data-uri'));
@ -1201,21 +1420,31 @@ function showMenu(el) {
uri + '\',' + nextsongpos + ',\'' + name + '\']}">Add after current playing song</a>' : '') +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'replaceQueue\', \'options\': [\'' + type + '\',\'' +
uri + '\',\'' + name + '\']}">Replace queue</a>' +
// ( type != 'plist' ? '<div class="dropdown-divider"></div><a class="dropdown-item" href="#">Add to playlist</a>' : '') +
( type != 'plist' ? '<div class="dropdown-divider"></div><a class="dropdown-item" href="#" data-href="{\'cmd\': \'showAddToPlaylist\', \'options\': [\'' + uri + '\']}">Add to playlist</a>' : '') +
( type != 'dir' ? '<div class="dropdown-divider"></div>' : '') +
( type == 'song' ? '<a class="dropdown-item" data-href="{\'cmd\': \'songDetails\', \'options\': [\'' + uri + '\']}" href="#">Songdetails</a>' : '');
// ( type == 'plist' ? '<a class="dropdown-item" href="#">Show playlist</a>' : '');
( type == 'song' ? '<a class="dropdown-item" data-href="{\'cmd\': \'songDetails\', \'options\': [\'' + uri + '\']}" href="#">Songdetails</a>' : '') +
( type == 'plist' ? '<a class="dropdown-item" href="#" data-href="{\'cmd\': \'playlistDetails\', \'options\': [\'' + uri + '\']}">Show playlist</a>' : '');
}
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists') {
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'All') {
menu += '<a class="dropdown-item" href="#" data-href="{\'cmd\': \'appendQueue\', \'options\': [\'' + type + '\',\'' +
uri + '\',\'' + name + '\']}">Append to queue</a>' +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'replaceQueue\', \'options\': [\'' + type + '\',\'' +
uri + '\',\'' + name + '\']}">Replace queue</a>' +
'<div class="dropdown-divider"></div>' +
// '<a class="dropdown-item" href="#">Show playlist</a>' +
// '<a class="dropdown-item" href="#">Rename playlist</a>' +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'playlistDetails\', \'options\': [\'' + uri + '\']}">Edit playlist</a>' +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'showRenamePlaylist\', \'options\': [\'' + uri + '\']}">Rename playlist</a>' +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'delPlaylist\', \'options\': [\'' +
uri + '\',\'' + name + '\']}">Delete playlist</a>';
uri + '\']}">Delete playlist</a>';
}
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'Detail') {
menu += '<a class="dropdown-item" href="#" data-href="{\'cmd\': \'appendQueue\', \'options\': [\'' + type + '\',\'' +
uri + '\',\'' + name + '\']}">Append to queue</a>' +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'replaceQueue\', \'options\': [\'' + type + '\',\'' +
uri + '\',\'' + name + '\']}">Replace queue</a>' +
( document.getElementById('BrowsePlaylistsDetailList').getAttribute('data-ro') == 'false' ?
'<div class="dropdown-divider"></div>' +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'removeFromPlaylist\', \'options\': [\'' + document.getElementById('BrowsePlaylistsDetailList').getAttribute('data-uri') + '\', \'' +
el.parentNode.parentNode.getAttribute('data-songpos') + '\']}">Remove</a>' : '');
}
else if (app.current.app == 'Queue') {
menu += '<a class="dropdown-item" href="#" data-href="{\'cmd\': \'delQueueSong\', \'options\': [\'single\',' +
@ -1262,7 +1491,7 @@ function sendAPI(request, callback) {
if (ajaxRequest.responseText != '') {
var obj = JSON.parse(ajaxRequest.responseText);
if (obj.type == 'error') {
showNotification('Error', obj.data, obj.data, 'error');
showNotification('Error', obj.data, obj.data, 'danger');
console.log('Error: ' + obj.data);
}
else if (obj.type == 'result' && obj.data != 'ok')
@ -1284,11 +1513,11 @@ function openLocalPlayer() {
function updateDB() {
sendAPI({"cmd": "MPD_API_UPDATE_DB"});
// showNotification('Updating MPD Database...', '', '', 'success');
showNotification('Updating MPD Database...', '', '', 'success');
}
function clickPlay() {
if( playstate != 'play')
if (playstate != 'play')
sendAPI({"cmd": "MPD_API_SET_PLAY"});
else
sendAPI({"cmd": "MPD_API_SET_PAUSE"});
@ -1313,9 +1542,9 @@ function delQueueSong(mode, start, end) {
sendAPI({"cmd": "MPD_API_RM_TRACK", "data": { "track": start}});
}
function delPlaylist(plist, name) {
sendAPI({"cmd": "MPD_API_RM_PLAYLIST", "data": {"plist": plist}});
document.getElementById('BrowsePlaylistsList').querySelector('tr[data-uri=' + encodeURI(plist) + ']').remove();
function delPlaylist(uri) {
sendAPI({"cmd": "MPD_API_RM_PLAYLIST", "data": {"uri": uri}});
document.getElementById('BrowsePlaylistsAllList').querySelector('tr[data-uri=' + encodeURI(uri) + ']').remove();
}
function confirmSettings() {
@ -1405,19 +1634,29 @@ function gotoPage(x) {
}
function addStream() {
var streamUrl = document.getElementById('streamurl');
if (streamUrl.value != '')
sendAPI({"cmd": "MPD_API_ADD_TRACK", "data": {"uri": streamUrl.value}});
streamUrl.value = '';
modalAddstream.hide();
var streamUrl = document.getElementById('streamUrl').value;
if (streamUrl != '' && streamUrl.indexOf('http') == 0) {
sendAPI({"cmd": "MPD_API_ADD_TRACK", "data": {"uri": streamUrl}});
modalAddstream.hide();
}
else {
document.getElementById('streamUrl').classList.add('is-invalid');
document.getElementById('addStreamFrm').classList.add('was-validated');
}
}
function saveQueue() {
var plName = document.getElementById('playlistname');
if (plName.value != '')
sendAPI({"cmd":"MPD_API_SAVE_QUEUE", "data": {"plist": plName.value}});
plName.value = '';
modalSavequeue.hide();
var plName = document.getElementById('saveQueueName').value;
var valid = plName.replace(/\w/g,'');
if (plName != '' && valid == '') {
sendAPI({"cmd":"MPD_API_SAVE_QUEUE", "data": {"plist": plName}});
modalSavequeue.hide();
}
else {
document.getElementById('saveQueueName').classList.add('is-invalid');
document.getElementById('saveQueueFrm').classList.add('was-validated');
}
}
function showNotification(notificationTitle,notificationText,notificationHtml,notificationType) {
@ -1439,7 +1678,7 @@ function showNotification(notificationTitle,notificationText,notificationHtml,no
else {
alertBox = document.getElementById('alertBox');
}
alertBox.classList.remove('alert-success', 'alert-error');
alertBox.classList.remove('alert-success', 'alert-danger');
alertBox.classList.add('alert','alert-' + notificationType);
alertBox.innerHTML = '<div><strong>' + notificationTitle + '</strong><br/>' + notificationHtml + '</div>';
document.getElementsByTagName('main')[0].append(alertBox);
@ -1466,7 +1705,7 @@ function notificationsSupported() {
}
function songChange(obj) {
if (obj.type == 'error')
if (obj.type == 'error' || obj.type == 'result')
return;
var cur_song = obj.data.title + obj.data.artist + obj.data.album + obj.data.uri + obj.data.currentsongid;
if (last_song == cur_song)
@ -1477,15 +1716,15 @@ function songChange(obj) {
document.getElementById('album-cover').style.backgroundImage = 'url("' + obj.data.cover + '")';
if(typeof obj.data.artist != 'undefined' && obj.data.artist.length > 0 && obj.data.artist != '-') {
if (typeof obj.data.artist != 'undefined' && obj.data.artist.length > 0 && obj.data.artist != '-') {
textNotification += obj.data.artist;
htmlNotification += '<br/>' + obj.data.artist;
htmlNotification += obj.data.artist;
pageTitle += obj.data.artist + ' - ';
document.getElementById('artist').innerText = obj.data.artist;
} else {
document.getElementById('artist').innerText = '';
}
if(typeof obj.data.album != 'undefined' && obj.data.album.length > 0 && obj.data.album != '-') {
if (typeof obj.data.album != 'undefined' && obj.data.album.length > 0 && obj.data.album != '-') {
textNotification += ' - ' + obj.data.album;
htmlNotification += '<br/>' + obj.data.album;
document.getElementById('album').innerText = obj.data.album;
@ -1493,7 +1732,7 @@ function songChange(obj) {
else {
document.getElementById('album').innerText = '';
}
if(typeof obj.data.title != 'undefined' && obj.data.title.length > 0) {
if (typeof obj.data.title != 'undefined' && obj.data.title.length > 0) {
pageTitle += obj.data.title;
document.getElementById('currenttrack').innerText = obj.data.title;
} else {

116
htdocs/js/mpd.min.js vendored
View File

@ -5,38 +5,47 @@ $jscomp.iteratorPrototype=function(a){$jscomp.initSymbolIterator();a={next:a};a[
$jscomp.checkStringArgs=function(a,b,c){if(null==a)throw new TypeError("The 'this' value for String.prototype."+c+" must not be null or undefined");if(b instanceof RegExp)throw new TypeError("First argument to String.prototype."+c+" must not be a regular expression");return a+""};
$jscomp.polyfill=function(a,b,c,e){if(b){c=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var d=a[e];d in c||(c[d]={});c=c[d]}a=a[a.length-1];e=c[a];b=b(e);b!=e&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};
$jscomp.polyfill("String.prototype.repeat",function(a){return a?a:function(a){var b=$jscomp.checkStringArgs(this,null,"repeat");if(0>a||1342177279<a)throw new RangeError("Invalid count value");a|=0;for(var e="";a;)if(a&1&&(e+=b),a>>>=1)b+=b;return e}},"es6","es3");
var socket,last_song="",last_state,current_song={},playstate="",settings={},alertTimeout,deferredPrompt,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/Any Tag/",scrollPos:0},Browse:{active:"Database",tabs:{Filesystem:{state:"0/-/",scrollPos:0},Playlists:{state:"0/-/",scrollPos:0},Database:{active:"Artist",views:{Artist:{state:"0/-/",scrollPos:0},Album:{state:"0/-/",scrollPos:0}}}}},Search:{state:"0/Any Tag/",scrollPos:0}},current:{app:"Playback",tab:void 0,view:void 0,page:0,filter:"",
search:"",scrollPos:0},last:{app:void 0,tab:void 0,view:void 0,filter:"",search:"",scrollPos:0}},domCache={};domCache.navbarBottomBtns=document.getElementById("navbar-bottom").getElementsByTagName("div");domCache.navbarBottomBtnsLen=domCache.navbarBottomBtns.length;domCache.panelHeadingBrowse=document.getElementById("panel-heading-browse").getElementsByTagName("a");domCache.panelHeadingBrowseLen=domCache.panelHeadingBrowse.length;domCache.counter=document.getElementById("counter");
domCache.volumePrct=document.getElementById("volumePrct");domCache.volumeControl=document.getElementById("volumeControl");domCache.volumeIcon=document.getElementById("volumeIcon");domCache.btnPlay=document.getElementById("btnPlay");domCache.btnPrev=document.getElementById("btnPrev");domCache.btnNext=document.getElementById("btnNext");domCache.progressBar=document.getElementById("progressBar");domCache.volumeBar=document.getElementById("volumeBar");domCache.outputs=document.getElementById("outputs");
domCache.btnAdd=document.getElementById("btnAdd");var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalAddstream=new Modal(document.getElementById("modalAddstream")),modalSavequeue=new Modal(document.getElementById("modalSavequeue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),mainMenu=new Dropdown(document.getElementById("mainMenu"));
var socket,last_song="",last_state,current_song={},playstate="",settings={},alertTimeout,deferredPrompt,dragEl,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/Any Tag/",scrollPos:0},Browse:{active:"Database",tabs:{Filesystem:{state:"0/-/",scrollPos:0},Playlists:{active:"All",views:{All:{state:"0/-/",scrollPos:0},Detail:{state:"0/-/",scrollPos:0}}},Database:{active:"Artist",views:{Artist:{state:"0/-/",scrollPos:0},Album:{state:"0/-/",scrollPos:0}}}}},Search:{state:"0/Any Tag/",scrollPos:0}},
current:{app:"Playback",tab:void 0,view:void 0,page:0,filter:"",search:"",scrollPos:0},last:{app:void 0,tab:void 0,view:void 0,filter:"",search:"",scrollPos:0}},domCache={};domCache.navbarBottomBtns=document.getElementById("navbar-bottom").getElementsByTagName("div");domCache.navbarBottomBtnsLen=domCache.navbarBottomBtns.length;domCache.panelHeadingBrowse=document.getElementById("panel-heading-browse").getElementsByTagName("a");domCache.panelHeadingBrowseLen=domCache.panelHeadingBrowse.length;
domCache.counter=document.getElementById("counter");domCache.volumePrct=document.getElementById("volumePrct");domCache.volumeControl=document.getElementById("volumeControl");domCache.volumeIcon=document.getElementById("volumeIcon");domCache.btnPlay=document.getElementById("btnPlay");domCache.btnPrev=document.getElementById("btnPrev");domCache.btnNext=document.getElementById("btnNext");domCache.progressBar=document.getElementById("progressBar");domCache.volumeBar=document.getElementById("volumeBar");
domCache.outputs=document.getElementById("outputs");domCache.btnAdd=document.getElementById("btnAdd");
var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalAddstream=new Modal(document.getElementById("modalAddstream")),modalSavequeue=new Modal(document.getElementById("modalSaveQueue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),modalAddToPlaylist=new Modal(document.getElementById("modalAddToPlaylist")),modalRenamePlaylist=new Modal(document.getElementById("modalRenamePlaylist")),
mainMenu=new Dropdown(document.getElementById("mainMenu"));
function appPrepare(a){if(app.current.app!=app.last.app||app.current.tab!=app.last.tab||app.current.view!=app.last.view){for(var b=0;b<domCache.navbarBottomBtnsLen;b++)domCache.navbarBottomBtns[b].classList.remove("active");document.getElementById("cardPlayback").classList.add("hide");document.getElementById("cardQueue").classList.add("hide");document.getElementById("cardBrowse").classList.add("hide");document.getElementById("cardSearch").classList.add("hide");for(b=0;b<domCache.panelHeadingBrowseLen;b++)domCache.panelHeadingBrowse[b].classList.remove("active");
document.getElementById("cardBrowsePlaylists").classList.add("hide");document.getElementById("cardBrowseDatabase").classList.add("hide");document.getElementById("cardBrowseFilesystem").classList.add("hide");document.getElementById("card"+app.current.app).classList.remove("hide");document.getElementById("nav"+app.current.app).classList.add("active");void 0!=app.current.tab&&(document.getElementById("card"+app.current.app+app.current.tab).classList.remove("hide"),document.getElementById("card"+app.current.app+
"Nav"+app.current.tab).classList.add("active"));scrollTo(a)}}
"Nav"+app.current.tab).classList.add("active"));scrollTo(a)}(a=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+(void 0==app.current.view?"":app.current.view)+"List"))&&a.classList.add("opacity05")}
function appGoto(a,b,c,e){var d=document.body.scrollTop?document.body.scrollTop:document.documentElement.scrollTop;void 0!=app.apps[app.current.app].scrollPos?app.apps[app.current.app].scrollPos=d:void 0!=app.apps[app.current.app].tabs[app.current.tab].scrollPos?app.apps[app.current.app].tabs[app.current.tab].scrollPos=d:void 0!=app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].scrollPos&&(app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].scrollPos=d);app.apps[a].tabs?
(void 0==b&&(b=app.apps[a].active),app.apps[a].tabs[b].views?(void 0==c&&(c=app.apps[a].tabs[b].active),a="/"+a+"/"+b+"/"+c+"!"+(void 0==e?app.apps[a].tabs[b].views[c].state:e)):a="/"+a+"/"+b+"!"+(void 0==e?app.apps[a].tabs[b].state:e)):a="/"+a+"!"+(void 0==e?app.apps[a].state:e);location.hash=a}
function appRoute(){var a;if(a=decodeURI(location.hash).match(/^#\/(\w+)\/?(\w+)?\/?(\w+)?!((\d+)\/([^\/]+)\/(.*))$/)){app.current.app=a[1];app.current.tab=a[2];app.current.view=a[3];app.apps[app.current.app].state?(app.apps[app.current.app].state=a[4],app.current.scrollPos=app.apps[app.current.app].scrollPos):app.apps[app.current.app].tabs[app.current.tab].state?(app.apps[app.current.app].tabs[app.current.tab].state=a[4],app.apps[app.current.app].active=app.current.tab,app.current.scrollPos=app.apps[app.current.app].tabs[app.current.tab].scrollPos):
app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].state&&(app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].state=a[4],app.apps[app.current.app].active=app.current.tab,app.apps[app.current.app].tabs[app.current.tab].active=app.current.view,app.current.scrollPos=app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].scrollPos);app.current.page=parseInt(a[5]);app.current.filter=a[6];app.current.search=a[7];appPrepare(app.current.scrollPos);
if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);else if("Queue"==app.current.app){document.getElementById("QueueList").classList.add("opacity05");var b=document.getElementById("searchqueuetag").getElementsByTagName("button");for(a=0;a<b.length;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"),document.getElementById("searchqueuetagdesc").innerText=b[a].innerText);getQueue()}else if("Browse"==app.current.app&&"Playlists"==
app.current.tab)document.getElementById("BrowsePlaylistsList").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Artist"==app.current.view)document.getElementById("BrowseDatabaseArtistList").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_ARTISTS",data:{offset:app.current.page,filter:app.current.filter}},
parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)document.getElementById("BrowseDatabaseAlbumCards").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){document.getElementById("BrowseFilesystemList").classList.add("opacity05");
sendAPI({cmd:"MPD_API_GET_FILESYSTEM",data:{offset:app.current.page,path:app.current.search?app.current.search:"/",filter:app.current.filter}},parseFilesystem);app.current.search?document.getElementById("BrowseFilesystemAddAllSongs").removeAttribute("disabled"):document.getElementById("BrowseFilesystemAddAllSongs").setAttribute("disabled","disabled");b='<li class="breadcrumb-item"><a data-uri="">root</a></li>';var c=app.current.search.split("/"),e=c.length,d="";for(a=0;a<e;a++){if(e-1==a){b+='<li class="breadcrumb-item active">'+
c[a]+"</li>";break}d+=c[a];b+='<li class="breadcrumb-item"><a data-uri="'+d+'">'+c[a]+"</a></li>";d+="/"}a=document.getElementById("BrowseBreadcrumb");a.innerHTML=b;b=a.getElementsByTagName("a");c=b.length;for(a=0;a<c;a++)b[a].addEventListener("click",function(){appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+this.getAttribute("data-uri"))},!1);doSetFilterLetter("BrowseFilesystemFilter")}else if("Search"==app.current.app)for(document.getElementById("searchstr").focus(),document.getElementById("SearchList").classList.add("opacity05"),
app.last.app!=app.current.app&&""!=app.current.search&&(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML='<tr><td><span class="material-icons">search</span></td><td colspan="5">Searching...</td></tr>'),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML="",document.getElementById("searchAddAllSongs").setAttribute("disabled",
"disabled"),document.getElementById("panel-heading-search").innerText="",document.getElementById("SearchList").classList.remove("opacity05")),b=document.getElementById("searchtags").getElementsByTagName("button"),c=b.length,a=0;a<c;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"),document.getElementById("searchtagsdesc").innerText=b[a].innerText);else appGoto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else appGoto("Playback")}
if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);else if("Queue"==app.current.app){var b=document.getElementById("searchqueuetag").getElementsByTagName("button");for(a=0;a<b.length;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"),document.getElementById("searchqueuetagdesc").innerText=b[a].innerText);getQueue()}else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view)sendAPI({cmd:"MPD_API_GET_PLAYLISTS",
data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view)sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Artist"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTS",
data:{offset:app.current.page,filter:app.current.filter}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){sendAPI({cmd:"MPD_API_GET_FILESYSTEM",data:{offset:app.current.page,
path:app.current.search?app.current.search:"/",filter:app.current.filter}},parseFilesystem);app.current.search?document.getElementById("BrowseFilesystemAddAllSongs").removeAttribute("disabled"):document.getElementById("BrowseFilesystemAddAllSongs").setAttribute("disabled","disabled");b='<li class="breadcrumb-item"><a data-uri="">root</a></li>';var c=app.current.search.split("/"),e=c.length,d="";for(a=0;a<e;a++){if(e-1==a){b+='<li class="breadcrumb-item active">'+c[a]+"</li>";break}d+=c[a];b+='<li class="breadcrumb-item"><a data-uri="'+
d+'">'+c[a]+"</a></li>";d+="/"}a=document.getElementById("BrowseBreadcrumb");a.innerHTML=b;b=a.getElementsByTagName("a");c=b.length;for(a=0;a<c;a++)b[a].addEventListener("click",function(){appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+this.getAttribute("data-uri"))},!1);doSetFilterLetter("BrowseFilesystemFilter")}else if("Search"==app.current.app)for(document.getElementById("searchstr").focus(),app.last.app!=app.current.app&&""!=app.current.search&&(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML=
'<tr><td><span class="material-icons">search</span></td><td colspan="5">Searching...</td></tr>'),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML="",document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),document.getElementById("panel-heading-search").innerText="",document.getElementById("SearchList").classList.remove("opacity05"),
setPagination(0)),b=document.getElementById("searchtags").getElementsByTagName("button"),c=b.length,a=0;a<c;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"),document.getElementById("searchtagsdesc").innerText=b[a].innerText);else appGoto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else appGoto("Playback")}
function appInit(){getSettings();sendAPI({cmd:"MPD_API_GET_OUTPUTNAMES"},parseOutputnames);webSocketConnect();domCache.volumeBar.value=0;domCache.volumeBar.addEventListener("change",function(a){sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:domCache.volumeBar.value}})},!1);domCache.progressBar.value=0;domCache.progressBar.addEventListener("change",function(a){current_song&&0<=current_song.currentSongId&&sendAPI({cmd:"MPD_API_SET_SEEK",data:{songid:current_song.currentSongId,seek:Math.ceil(domCache.progressBar.value/
100*current_song.totalTime)}})},!1);document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});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("modalAddstream").addEventListener("shown.bs.modal",function(){document.getElementById("streamurl").focus()});document.getElementById("addstreamFrm").addEventListener("submit",function(){addStream()});addFilterLetter("BrowseFilesystemFilterLetters");addFilterLetter("BrowseDatabaseFilterLetters");addFilterLetter("BrowsePlaylistsFilterLetters");for(var a=document.querySelectorAll("button[data-href], a[data-href]"),b=a.length,c=0;c<b;c++)a[c].addEventListener("click",function(a){a.preventDefault();
a.stopPropagation();a=JSON.parse(this.getAttribute("data-href").replace(/'/g,'"'));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1);a=document.querySelectorAll(".pages");b=a.length;for(c=0;c<b;c++)a[c].addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&gotoPage(a.target.getAttribute("data-page"))},!1);document.getElementById("outputs").addEventListener("click",
function(a){"BUTTON"==a.target.nodeName&&a.stopPropagation();sendAPI({cmd:"MPD_API_TOGGLE_OUTPUT",data:{output:a.target.getAttribute("data-output-id"),state:a.target.classList.contains("active")?0:1}});toggleBtn(a.target.id)},!1);document.getElementById("QueueList").addEventListener("click",function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseFilesystemList").addEventListener("click",
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&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowsePlaylistsList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseDatabaseArtistList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database",
"Album","0/-/"+a.target.parentNode.getAttribute("data-uri"))},!1);document.getElementById("SearchList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("searchtags").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,
"0/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app,app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("search").addEventListener("submit",
function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1);document.getElementById("searchstr").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);window.addEventListener("hashchange",appRoute,!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){switch(a.which){case 37:clickPrev();break;case 39:clickNext();break;case 32:clickPlay();break;default:return}a.preventDefault()}},
!1);"serviceWorker"in navigator&&"https"==document.URL.substring(0,5)&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.js",{scope:"/"}).then(function(a){console.log("ServiceWorker registration successful with scope: ",a.scope)},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("appinstalled")})}
function webSocketConnect(){socket=new WebSocket(getWsUrl());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");modalConnectionError.hide();appRoute()},socket.onmessage=function(a){if(a.data!==last_state&&0!=a.data.length){try{var b=JSON.parse(a.data)}catch(c){console.log("Invalid JSON data received: "+a.data)}switch(b.type){case "state":parseState(b);break;case "disconnected":showNotification("myMPD lost connection to MPD","","","danger");
break;case "update_queue":"Queue"===app.current.app&&getQueue();break;case "song_change":songChange(b);break;case "error":showNotification(b.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");modalConnectionError.show();setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("Error: "+a)}}
100*current_song.totalTime)}})},!1);document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});document.getElementById("modalSaveQueue").addEventListener("shown.bs.modal",function(){var a=document.getElementById("saveQueueName");a.focus();a.value="";a.classList.remove("is-invalid");document.getElementById("saveQueueFrm").classList.remove("was-validated")});document.getElementById("modalSettings").addEventListener("shown.bs.modal",
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("modalAddstream").addEventListener("shown.bs.modal",function(){var a=document.getElementById("streamUrl");a.focus();a.value="";a.classList.remove("is-invalid");
document.getElementById("addStreamFrm").classList.remove("was-validated")});addFilterLetter("BrowseFilesystemFilterLetters");addFilterLetter("BrowseDatabaseFilterLetters");addFilterLetter("BrowsePlaylistsFilterLetters");for(var a=document.querySelectorAll("button[data-href], a[data-href]"),b=a.length,c=0;c<b;c++)a[c].addEventListener("click",function(a){a.preventDefault();a.stopPropagation();a=JSON.parse(this.getAttribute("data-href").replace(/'/g,'"'));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,
$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1);a=document.querySelectorAll(".pages");b=a.length;for(c=0;c<b;c++)a[c].addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&gotoPage(a.target.getAttribute("data-page"))},!1);document.getElementById("outputs").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&a.stopPropagation();sendAPI({cmd:"MPD_API_TOGGLE_OUTPUT",data:{output:a.target.getAttribute("data-output-id"),
state:a.target.classList.contains("active")?0:1}});toggleBtn(a.target.id)},!1);document.getElementById("QueueList").addEventListener("click",function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseFilesystemList").addEventListener("click",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&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowsePlaylistsAllList").addEventListener("click",
function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowsePlaylistsDetailList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),
showMenu(a.target))},!1);document.getElementById("BrowseDatabaseArtistList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database","Album","0/-/"+a.target.parentNode.getAttribute("data-uri"))},!1);document.getElementById("SearchList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},
!1);document.getElementById("searchtags").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,"0/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app,app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&
appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("search").addEventListener("submit",function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1);document.getElementById("searchstr").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);dragAndDropTable("QueueList");dragAndDropTable("BrowsePlaylistsDetailList");
window.addEventListener("hashchange",appRoute,!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){switch(a.which){case 37:clickPrev();break;case 39:clickNext();break;case 32:clickPlay();break;default:return}a.preventDefault()}},!1);"serviceWorker"in navigator&&"https"==document.URL.substring(0,5)&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.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.querySelectorAll(".dragover"),d=c.length,f=0;f<d;f++)c[f].classList.remove("dragover");c=a.target;"TD"==a.target.nodeName&&(c=a.target.parentNode);"TR"==c.nodeName&&c.classList.add("dragover");a.dataTransfer.dropEffect="move"},!1);b.addEventListener("dragend",function(a){for(var c=b.querySelectorAll(".dragover"),d=c.length,f=0;f<d;f++)c[f].classList.remove("dragover");document.getElementById(a.dataTransfer.getData("Text"))&&
document.getElementById(a.dataTransfer.getData("Text")).classList.remove("opacity05")},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();var e=c.target;"TD"==c.target.nodeName&&(e=c.target.parentNode);var d=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),f=e.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05");b.insertBefore(dragEl,e);c=b.querySelectorAll(".dragover");
e=c.length;for(var g=0;g<e;g++)c[g].classList.remove("dragover");document.getElementById(a).classList.add("opacity05");"Queue"==app.current.app?sendAPI({cmd:"MPD_API_MOVE_TRACK",data:{from:d,to:f}}):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view&&playlistMoveTrack(d,f)},!1)}
function playlistMoveTrack(a,b){sendAPI({cmd:"MPD_API_PLAYLIST_MOVE_TRACK",data:{plist:app.current.search,from:a,to:b}});sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)}
function webSocketConnect(){socket=new WebSocket(getWsUrl());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");modalConnectionError.hide();appRoute();sendAPI({cmd:"MPD_API_GET_OUTPUTNAMES"},parseOutputnames)},socket.onmessage=function(a){if(a.data!==last_state&&0!=a.data.length){try{var b=JSON.parse(a.data)}catch(c){console.log("Invalid JSON data received: "+a.data)}switch(b.type){case "state":parseState(b);break;case "disconnected":showNotification("myMPD lost connection to MPD",
"","","danger");break;case "update_queue":"Queue"===app.current.app&&getQueue();break;case "song_change":songChange(b);break;case "error":showNotification(b.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");modalConnectionError.show();setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("Error: "+a)}}
function getWsUrl(){var a=document.URL;if("https"==a.substring(0,5)){var b="wss://";a=a.substr(8)}else b="ws://","http"==a.substring(0,4)&&(a=a.substr(7));a=a.split("#");var c=/\/$/.test(a[0])?"":"/";return b+a[0]+c+"ws"}
function parseStats(a){document.getElementById("mpdstats_artists").innerText=a.data.artists;document.getElementById("mpdstats_albums").innerText=a.data.albums;document.getElementById("mpdstats_songs").innerText=a.data.songs;document.getElementById("mpdstats_dbplaytime").innerText=beautifyDuration(a.data.dbplaytime);document.getElementById("mpdstats_playtime").innerText=beautifyDuration(a.data.playtime);document.getElementById("mpdstats_uptime").innerText=beautifyDuration(a.data.uptime);var b=new Date(1E3*
a.data.dbupdated);document.getElementById("mpdstats_dbupdated").innerText=b.toUTCString();document.getElementById("mympdVersion").innerText=a.data.mympd_version;document.getElementById("mpdVersion").innerText=a.data.mpd_version}function toggleBtn(a,b){if(a=document.getElementById(a))void 0==b&&(b=a.classList.contains("active")?0:1),1==b?(a.classList.add("active"),a.setAttribute("aria-pressed","true")):(a.classList.remove("active"),a.setAttribute("aria-pressed","false"))}
@ -51,19 +60,23 @@ a.data.totalTime;current_song.currentSongId=a.data.currentsongid;var b=Math.floo
e[0].classList.remove("material-icons"),e[0].innerText=c.getAttribute("data-songpos"),c.classList.remove("font-weight-bold"));if(c=document.getElementById("queueTrackId"+a.data.currentsongid))e=c.getElementsByTagName("td"),e[4].innerText=b,e[0].classList.add("material-icons"),e[0].innerText="play_arrow",c.classList.add("font-weight-bold");void 0!=last_state&&a.data.queue_version==last_state.data.queue_version||sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);b=a.data.outputs.length;for(c=0;c<
b;c++)toggleBtn("btnoutput"+a.data.outputs[c].id,a.data.outputs[c].state);last_state=a}}function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH_QUEUE",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue)}
function parseQueue(a){if("Queue"===app.current.app){0<a.totalTime?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs \u2013 "+beautifyDuration(a.totalTime):0<a.totalEntities?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs":document.getElementById("panel-heading-queue").innerText="";var b=a.data.length,c=document.getElementById(app.current.app+"List");c.setAttribute("data-version",a.queue_version);c=c.getElementsByTagName("tbody")[0];
for(var e=c.getElementsByTagName("tr"),d=0;d<b;d++)if(!e[d]||e[d].getAttribute("data-trackid")!=a.data[d].id){var f=Math.floor(a.data[d].duration/60),g=a.data[d].duration-60*f;f=f+":"+(10>g?"0":"")+g;g=document.createElement("tr");g.setAttribute("data-trackid",a.data[d].id);g.setAttribute("id","queueTrackId"+a.data[d].id);g.setAttribute("data-songpos",a.data[d].pos+1);g.setAttribute("data-duration",f);g.setAttribute("data-uri",a.data[d].uri);g.innerHTML="<td>"+(a.data[d].pos+1)+"</td><td>"+a.data[d].title+
"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(g):c.append(g)}for(d=e.length-1;d>=b;d--)e[d].remove();"queuesearch"==a.type&&0==b?c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results, please refine your search!</td></tr>':"queue"==a.type&&0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty queue</td></tr>');
setPagination(a.totalEntities);document.getElementById("QueueList").classList.remove("opacity05")}}function parseSearch(a){"Search"===app.current.app&&(document.getElementById("panel-heading-search").innerHTML=a.totalEntities+" Songs found",0<a.totalEntities?document.getElementById("searchAddAllSongs").removeAttribute("disabled"):document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),parseFilesystem(a))}
for(var e=c.getElementsByTagName("tr"),d=0;d<b;d++)if(!e[d]||e[d].getAttribute("data-trackid")!=a.data[d].id||e[d].getAttribute("data-songpos")!=a.data[d].pos+1){var f=Math.floor(a.data[d].duration/60),g=a.data[d].duration-60*f;f=f+":"+(10>g?"0":"")+g;g=document.createElement("tr");g.setAttribute("draggable","true");g.setAttribute("data-trackid",a.data[d].id);g.setAttribute("id","queueTrackId"+a.data[d].id);g.setAttribute("data-songpos",a.data[d].pos+1);g.setAttribute("data-duration",f);g.setAttribute("data-uri",
a.data[d].uri);g.innerHTML="<td>"+(a.data[d].pos+1)+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(g):c.append(g)}for(d=e.length-1;d>=b;d--)e[d].remove();"queuesearch"==a.type&&0==b?c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results, please refine your search!</td></tr>':"queue"==a.type&&
0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty queue</td></tr>');setPagination(a.totalEntities);document.getElementById("QueueList").classList.remove("opacity05")}}
function parseSearch(a){"Search"===app.current.app&&(document.getElementById("panel-heading-search").innerHTML=a.totalEntities+" Songs found",0<a.totalEntities?document.getElementById("searchAddAllSongs").removeAttribute("disabled"):document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),parseFilesystem(a))}
function parseFilesystem(a){if("Browse"===app.current.app||"Filesystem"===app.current.tab||"Search"===app.current.app){for(var b=a.data.length,c=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List").getElementsByTagName("tbody")[0],e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].uri);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-type",a.data[d].type);g.setAttribute("data-uri",f);g.setAttribute("data-name",
a.data[d].name);switch(a.data[d].type){case "dir":g.innerHTML='<td><span class="material-icons">folder_open</span></td><td colspan="4">'+a.data[d].name+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';break;case "song":f=Math.floor(a.data[d].duration/60);var h=a.data[d].duration-60*f;g.innerHTML='<td><span class="material-icons">music_note</span></td><td>'+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>h?"0":"")+h+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
break;case "plist":g.innerHTML='<td><span class="material-icons">list</span></td><td colspan="4">'+a.data[d].name+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>'}d<e.length?e[d].replaceWith(g):c.append(g)}}for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results</td></tr>');document.getElementById(app.current.app+(void 0==app.current.tab?"":
app.current.tab)+"List").classList.remove("opacity05")}}
function parsePlaylists(a){if("Browse"===app.current.app||"Playlists"===app.current.tab){for(var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+"List").getElementsByTagName("tbody")[0],e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].uri);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=new Date(1E3*a.data[d].last_modified),h=document.createElement("tr");h.setAttribute("data-uri",f);h.setAttribute("data-type","plist");h.setAttribute("data-name",a.data[d].name);
h.innerHTML='<td><span class="material-icons">list</span></td><td>'+a.data[d].name+"</td><td>"+g.toUTCString()+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(h):c.append(h)}}for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No playlists found.</td></tr>');document.getElementById(app.current.app+(void 0==app.current.tab?
"":app.current.tab)+"List").classList.remove("opacity05")}}
function parseListDBtags(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Artist"===app.current.view)if("AlbumArtist"==a.tagtype){document.getElementById("BrowseDatabaseAlbumCards").classList.add("hide");document.getElementById("BrowseDatabaseArtistList").classList.remove("hide");document.getElementById("btnBrowseDatabaseArtist").classList.add("hide");for(var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0],
function parsePlaylists(a){if("Browse"===app.current.app||"Playlists"===app.current.tab){"All"==app.current.view?(document.getElementById("BrowsePlaylistsAllList").classList.remove("hide"),document.getElementById("BrowsePlaylistsDetailList").classList.add("hide"),document.getElementById("btnBrowsePlaylistsAll").parentNode.classList.add("hide"),document.getElementById("btnPlaylistClear").parentNode.classList.add("hide")):(-1<a.uri.indexOf(".")?(document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-ro",
"true"),document.getElementById("btnPlaylistClear").parentNode.classList.add("hide")):(document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-ro","false"),document.getElementById("btnPlaylistClear").parentNode.classList.remove("hide")),document.getElementById("BrowsePlaylistsDetailList").setAttribute("data-uri",a.uri),document.getElementById("BrowsePlaylistsDetailList").getElementsByTagName("caption")[0].innerText="Playlist: "+a.uri,document.getElementById("BrowsePlaylistsDetailList").classList.remove("hide"),
document.getElementById("BrowsePlaylistsAllList").classList.add("hide"),document.getElementById("btnBrowsePlaylistsAll").parentNode.classList.remove("hide"));var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0],e=c.getElementsByTagName("tr");if("All"==app.current.view)for(var d=0;d<b;d++){var f=encodeURI(a.data[d].uri);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=new Date(1E3*a.data[d].last_modified),h=document.createElement("tr");
h.setAttribute("data-uri",f);h.setAttribute("data-type","plist");h.setAttribute("data-name",a.data[d].name);h.innerHTML='<td><span class="material-icons">list</span></td><td>'+a.data[d].name+"</td><td>"+g.toUTCString()+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(h):c.append(h)}}else if("Detail"==app.current.view)for(d=0;d<b;d++)if(f=encodeURI(a.data[d].uri),g=a.offset+d+1,!e[d]||e[d].getAttribute("data-uri")!=f||e[d].getAttribute("id")!=
"playlistTrackId"+g){h=document.createElement("tr");h.setAttribute("draggable","true");h.setAttribute("id","playlistTrackId"+g);h.setAttribute("data-type",a.data[d].type);h.setAttribute("data-uri",f);h.setAttribute("data-name",a.data[d].name);h.setAttribute("data-songpos",g);f=Math.floor(a.data[d].duration/60);var k=a.data[d].duration-60*f;h.innerHTML="<td>"+g+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>k?"0":"")+k+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
d<e.length?e[d].replaceWith(h):c.append(h)}for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML="All"==app.current.view?'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No playlists found.</td></tr>':'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty playlist.</td></tr>');document.getElementById(app.current.app+app.current.tab+app.current.view+"List").classList.remove("opacity05")}}
function parseListDBtags(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Artist"===app.current.view)if("AlbumArtist"==a.tagtype){document.getElementById("BrowseDatabaseAlbumCards").classList.add("hide");document.getElementById("BrowseDatabaseArtistList").classList.remove("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.add("hide");for(var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0],
e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].value);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-uri",f);g.innerHTML='<td><span class="material-icons">album</span></td><td>'+a.data[d].value+"</td>";d<e.length?e[d].replaceWith(g):c.append(g)}}for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No entries found.</td></tr>');
document.getElementById("BrowseDatabaseArtistList").classList.remove("opacity05")}else if("Album"==a.tagtype){document.getElementById("BrowseDatabaseAlbumCards").classList.remove("hide");document.getElementById("BrowseDatabaseArtistList").classList.add("hide");document.getElementById("btnBrowseDatabaseArtist").classList.remove("hide");b=a.data.length;c=document.getElementById("BrowseDatabaseAlbumCards");e=c.querySelectorAll(".col-md");for(d=0;d<b;d++)f=genId(a.data[d].value),e[d]&&e[d].getAttribute("id")==
document.getElementById("BrowseDatabaseArtistList").classList.remove("opacity05")}else if("Album"==a.tagtype){document.getElementById("BrowseDatabaseAlbumCards").classList.remove("hide");document.getElementById("BrowseDatabaseArtistList").classList.add("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.remove("hide");b=a.data.length;c=document.getElementById("BrowseDatabaseAlbumCards");e=c.querySelectorAll(".col-md");for(d=0;d<b;d++)f=genId(a.data[d].value),e[d]&&e[d].getAttribute("id")==
f||(g=document.createElement("div"),g.classList.add("col-md"),g.classList.add("mr-0"),g.setAttribute("id",f),g.innerHTML='<div class="card mb-4" id="card'+f+'"> <a href="#" class="card-img-top"><img class="card-img-top" src="" ></a> <div class="card-body"> <h5 class="card-title">'+a.searchstr+'</h5> <h4 class="card-title">'+a.data[d].value+'</h4> <table class="table table-sm table-hover" id="tbl'+f+'"><tbody></tbody></table </div></div>',d<e.length?e[d].replaceWith(g):c.append(g),sendAPI({cmd:"MPD_API_GET_ARTISTALBUMTITLES",
data:{albumartist:a.searchstr,album:a.data[d].value}},parseListTitles));for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);document.getElementById("BrowseDatabaseAlbumCards").classList.remove("opacity05")}}
function parseListTitles(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Album"===app.current.view){var b=genId(a.album),c=document.getElementById("card"+b);b=c.getElementsByTagName("tbody")[0];var e=c.getElementsByTagName("img")[0];c=e.parentNode;e.setAttribute("src",a.cover);c.setAttribute("data-uri",encodeURI(a.data[0].uri.replace(/\/[^\/]+$/,"")));c.setAttribute("data-name",a.album);c.setAttribute("data-type","dir");e="";for(var d=a.data.length,f=0;f<d;f++)e+='<tr data-type="song" data-name="'+
@ -77,27 +90,36 @@ function appendAfterQueue(a,b,c,e){switch(a){case "song":sendAPI({cmd:"MPD_API_A
function replaceQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_REPLACE_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" replaced',"","","success")}}
function songDetails(a){sendAPI({cmd:"MPD_API_GET_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()}
function parseSongDetails(a){var b=document.getElementById("modalSongDetails");b.querySelector(".album-cover").style.backgroundImage='url("'+a.data.cover+'")';b.getElementsByTagName("h1")[0].innerText=a.data.title;b=b.getElementsByTagName("tr");for(var c=b.length,e=0;e<c;e++){var d=b[e].getAttribute("data-name"),f=a.data[d];"duration"==d?(d=Math.floor(f/60),f-=60*d,f=d+":"+(10>f?"0":"")+f):"uri"==d&&(f='<a class="text-success" href="/library/'+f+'">'+f+"</a>");b[e].getElementsByTagName("td")[1].innerHTML=
f}}
function showMenu(a){var b=a.getAttribute("data-type"),c=decodeURI(a.getAttribute("data-uri")),e=a.getAttribute("data-name"),d=0;if(null==b||null==c)b=a.parentNode.parentNode.getAttribute("data-type"),c=decodeURI(a.parentNode.parentNode.getAttribute("data-uri")),e=a.parentNode.parentNode.getAttribute("data-name");last_state&&(d=last_state.data.nextsongpos);var f="";"Browse"==app.current.app&&"Filesystem"==app.current.tab||"Search"==app.current.app||"Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==
app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a>"+("song"==b?"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendAfterQueue', 'options': ['"+b+"','"+c+"',"+d+",'"+e+"']}\">Add after current playing song</a>":"")+"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a>"+("dir"!=b?'<div class="dropdown-divider"></div>':
"")+("song"==b?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+c+'\']}" href="#">Songdetails</a>':""):"Browse"==app.current.app&&"Playlists"==app.current.tab?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a><div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delPlaylist', 'options': ['"+
c+"','"+e+"']}\">Delete playlist</a>":"Queue"==app.current.app&&(f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['single',"+a.parentNode.parentNode.getAttribute("data-trackid")+"]}\">Remove</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',0,"+a.parentNode.parentNode.getAttribute("data-songpos")+"]}\">Remove all upwards</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',"+
(parseInt(a.parentNode.parentNode.getAttribute("data-songpos"))-1)+',-1]}">Remove all downwards</a><div class="dropdown-divider"></div>'+(-1==c.indexOf("http")?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+c+'\']}" href="#">Songdetails</a>':""));void 0==a.Popover&&(new Popover(a,{trigger:"click",template:'<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-content">'+f+"</div></div>"}),b=a.Popover,a.addEventListener("shown.bs.popover",function(a){document.querySelector(".popover-content").addEventListener("click",
function(a){a.preventDefault();a.stopPropagation();a=JSON.parse(a.target.getAttribute("data-href").replace(/'/g,'"'));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1)},!1),b.show())}
function sendAPI(a,b){var c=new XMLHttpRequest;c.open("POST","/api",!0);c.setRequestHeader("Content-type","application/json");c.onreadystatechange=function(){if(4==c.readyState)if(""!=c.responseText){var e=JSON.parse(c.responseText);"error"==e.type?(showNotification("Error",e.data,e.data,"error"),console.log("Error: "+e.data)):"result"==e.type&&"ok"!=e.data?showNotification(e.data,"","","success"):void 0!=b&&"function"==typeof b&&b(e)}else console.log("Empty response for request: "+JSON.stringify(a))};
c.send(JSON.stringify(a))}function openLocalPlayer(){window.open("/player.html#"+settings.mpdstream,"LocalPlayer")}function updateDB(){sendAPI({cmd:"MPD_API_UPDATE_DB"})}function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_SET_PLAY"}):sendAPI({cmd:"MPD_API_SET_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_SET_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_SET_PREV"})}function clickNext(){sendAPI({cmd:"MPD_API_SET_NEXT"})}
function delQueueSong(a,b,c){"range"==a?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:b,end:c}}):"single"==a&&sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:b}})}function delPlaylist(a,b){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{plist:a}});document.getElementById("BrowsePlaylistsList").querySelector("tr[data-uri="+encodeURI(a)+"]").remove()}
f}}function playlistDetails(a){appGoto("Browse","Playlists","Detail","0/-/"+a)}function removeFromPlaylist(a,b){sendAPI({cmd:"MPD_API_RM_PLAYLIST_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)}
function playlistClear(){var a=document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri");sendAPI({cmd:"MPD_API_PLAYLIST_CLEAR",data:{uri:a}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)}
function getAllPlaylists(a){for(var b=a.data.length,c="",e=0;e<b;e++)c+="<option>"+a.data[e].uri+"</option>";document.getElementById("addToPlaylistPlaylist").innerHTML+=c;a.totalEntities>a.returnedEntities&&(a.offset+=settings.max_elements_per_page,sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:a.offset,filter:"-"}},getAllPlaylists))}
function showAddToPlaylist(a){modalAddToPlaylist.show();document.getElementById("addToPlaylistUri").value=a;document.getElementById("addToPlaylistPlaylist").innerHTML="";sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:0,filter:"-"}},getAllPlaylists)}
function addToPlaylist(){var a=document.getElementById("addToPlaylistUri").value,b=document.getElementById("addToPlaylistPlaylist");sendAPI({cmd:"MPD_API_ADD_TO_PLAYLIST",data:{uri:a,plist:b.options[b.selectedIndex].text}});modalAddToPlaylist.hide()}
function showRenamePlaylist(a){document.getElementById("renamePlaylistFrm").classList.remove("was-validated");document.getElementById("renamePlaylistTo").classList.remove("is-invalid");modalRenamePlaylist.show();document.getElementById("renamePlaylistFrom").value=a;document.getElementById("renamePlaylistTo").value=""}
function renamePlaylist(){var a=document.getElementById("renamePlaylistFrom").value,b=document.getElementById("renamePlaylistTo").value,c=b.replace(/\w/g,"");""!=b&&b!=a&&""==c?(sendAPI({cmd:"MPD_API_PLAYLIST_RENAME",data:{from:a,to:b}}),modalRenamePlaylist.hide(),sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists)):(document.getElementById("renamePlaylistTo").classList.add("is-invalid"),document.getElementById("renamePlaylistFrm").classList.add("was-validated"))}
function showMenu(a){var b=a.getAttribute("data-type"),c=decodeURI(a.getAttribute("data-uri")),e=a.getAttribute("data-name"),d=0;if(null==b||null==c)b=a.parentNode.parentNode.getAttribute("data-type"),c=decodeURI(a.parentNode.parentNode.getAttribute("data-uri")),e=a.parentNode.parentNode.getAttribute("data-name");last_state&&(d=last_state.data.nextsongpos);var f="";"Browse"==app.current.app&&"Filesystem"==app.current.tab||"Search"==app.current.app||"Browse"==app.current.app&&"Database"==app.current.tab&&
"Album"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a>"+("song"==b?"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendAfterQueue', 'options': ['"+b+"','"+c+"',"+d+",'"+e+"']}\">Add after current playing song</a>":"")+"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a>"+("plist"!=b?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showAddToPlaylist', 'options': ['"+
c+"']}\">Add to playlist</a>":"")+("dir"!=b?'<div class="dropdown-divider"></div>':"")+("song"==b?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+c+'\']}" href="#">Songdetails</a>':"")+("plist"==b?"<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'playlistDetails', 'options': ['"+c+"']}\">Show playlist</a>":""):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"All"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+
b+"','"+c+"','"+e+"']}\">Append to queue</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a><div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'playlistDetails', 'options': ['"+c+"']}\">Edit playlist</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'showRenamePlaylist', 'options': ['"+c+"']}\">Rename playlist</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delPlaylist', 'options': ['"+
c+"']}\">Delete playlist</a>":"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view?f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'appendQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Append to queue</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'replaceQueue', 'options': ['"+b+"','"+c+"','"+e+"']}\">Replace queue</a>"+("false"==document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-ro")?"<div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'removeFromPlaylist', 'options': ['"+
document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri")+"', '"+a.parentNode.parentNode.getAttribute("data-songpos")+"']}\">Remove</a>":""):"Queue"==app.current.app&&(f+="<a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['single',"+a.parentNode.parentNode.getAttribute("data-trackid")+"]}\">Remove</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',0,"+a.parentNode.parentNode.getAttribute("data-songpos")+
"]}\">Remove all upwards</a><a class=\"dropdown-item\" href=\"#\" data-href=\"{'cmd': 'delQueueSong', 'options': ['range',"+(parseInt(a.parentNode.parentNode.getAttribute("data-songpos"))-1)+',-1]}">Remove all downwards</a><div class="dropdown-divider"></div>'+(-1==c.indexOf("http")?"<a class=\"dropdown-item\" data-href=\"{'cmd': 'songDetails', 'options': ['"+c+'\']}" href="#">Songdetails</a>':""));void 0==a.Popover&&(new Popover(a,{trigger:"click",template:'<div class="popover" role="tooltip"><div class="arrow"></div><div class="popover-content">'+
f+"</div></div>"}),b=a.Popover,a.addEventListener("shown.bs.popover",function(a){document.querySelector(".popover-content").addEventListener("click",function(a){a.preventDefault();a.stopPropagation();a=JSON.parse(a.target.getAttribute("data-href").replace(/'/g,'"'));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1)},!1),b.show())}
function sendAPI(a,b){var c=new XMLHttpRequest;c.open("POST","/api",!0);c.setRequestHeader("Content-type","application/json");c.onreadystatechange=function(){if(4==c.readyState)if(""!=c.responseText){var e=JSON.parse(c.responseText);"error"==e.type?(showNotification("Error",e.data,e.data,"danger"),console.log("Error: "+e.data)):"result"==e.type&&"ok"!=e.data?showNotification(e.data,"","","success"):void 0!=b&&"function"==typeof b&&b(e)}else console.log("Empty response for request: "+JSON.stringify(a))};
c.send(JSON.stringify(a))}function openLocalPlayer(){window.open("/player.html#"+settings.mpdstream,"LocalPlayer")}function updateDB(){sendAPI({cmd:"MPD_API_UPDATE_DB"});showNotification("Updating MPD Database...","","","success")}function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_SET_PLAY"}):sendAPI({cmd:"MPD_API_SET_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_SET_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_SET_PREV"})}
function clickNext(){sendAPI({cmd:"MPD_API_SET_NEXT"})}function delQueueSong(a,b,c){"range"==a?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:b,end:c}}):"single"==a&&sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:b}})}function delPlaylist(a){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{uri:a}});document.getElementById("BrowsePlaylistsAllList").querySelector("tr[data-uri="+encodeURI(a)+"]").remove()}
function confirmSettings(){var a=!0,b=document.getElementById("inputCrossfade");if(!b.getAttribute("disabled")){var c=parseInt(b.value);isNaN(c)?(b.classList.add("is-invalid"),a=!1):b.value=c}b=document.getElementById("inputMixrampdb");b.getAttribute("disabled")||(c=parseFloat(b.value),isNaN(c)?(b.classList.add("is-invalid"),a=!1):b.value=c);b=document.getElementById("inputMixrampdelay");b.getAttribute("disabled")||("nan"==b.value&&(b.value="-1"),c=parseFloat(b.value),isNaN(c)?(b.classList.add("is-invalid"),
a=!1):b.value=c);1==a?(a=document.getElementById("selectReplaygain"),sendAPI({cmd:"MPD_API_SET_SETTINGS",data:{consume:document.getElementById("btnConsume").classList.contains("active")?1:0,random:document.getElementById("btnRandom").classList.contains("active")?1:0,single:document.getElementById("btnSingle").classList.contains("active")?1:0,repeat:document.getElementById("btnRepeat").classList.contains("active")?1:0,replaygain:a.options[a.selectedIndex].value,crossfade:document.getElementById("inputCrossfade").value,
mixrampdb:document.getElementById("inputMixrampdb").value,mixrampdelay:document.getElementById("inputMixrampdelay").vaue,notificationWeb:document.getElementById("btnnotifyWeb").classList.contains("active")?1:0,notificationPage:document.getElementById("btnnotifyPage").classList.contains("active")?1:0}},getSettings),modalSettings.hide()):document.getElementById("settingsFrm").classList.add("was-validated")}
function addAllFromBrowse(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:app.current.search}});showNotification("Added all songs","","","success")}function addAllFromSearch(){2<=app.current.search.length&&(sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:app.current.search}}),showNotification("Added "+parseInt(document.getElementById("panel-heading-search").innerText)+" songs from search","","","success"))}
function scrollTo(a){document.body.scrollTop=a;document.documentElement.scrollTop=a}function gotoPage(a){switch(a){case "next":app.current.page+=settings.max_elements_per_page;break;case "prev":app.current.page-=settings.max_elements_per_page;0>=app.current.page&&(app.current.page=0);break;default:app.current.page=a}appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)}
function addStream(){var a=document.getElementById("streamurl");""!=a.value&&sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:a.value}});a.value="";modalAddstream.hide()}function saveQueue(){var a=document.getElementById("playlistname");""!=a.value&&sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:a.value}});a.value="";modalSavequeue.hide()}
function showNotification(a,b,c,e){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&(document.getElementById("alertBox")?b=document.getElementById("alertBox"):(b=document.createElement("div"),b.setAttribute("id","alertBox"),b.addEventListener("click",function(){hideNotification()},!1)),b.classList.remove("alert-success","alert-error"),b.classList.add("alert","alert-"+e),b.innerHTML="<div><strong>"+
function addStream(){var a=document.getElementById("streamUrl").value;""!=a&&0==a.indexOf("http")?(sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:a}}),modalAddstream.hide()):(document.getElementById("streamUrl").classList.add("is-invalid"),document.getElementById("addStreamFrm").classList.add("was-validated"))}
function saveQueue(){var a=document.getElementById("saveQueueName").value,b=a.replace(/\w/g,"");""!=a&&""==b?(sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:a}}),modalSavequeue.hide()):(document.getElementById("saveQueueName").classList.add("is-invalid"),document.getElementById("saveQueueFrm").classList.add("was-validated"))}
function showNotification(a,b,c,e){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&(document.getElementById("alertBox")?b=document.getElementById("alertBox"):(b=document.createElement("div"),b.setAttribute("id","alertBox"),b.addEventListener("click",function(){hideNotification()},!1)),b.classList.remove("alert-success","alert-danger"),b.classList.add("alert","alert-"+e),b.innerHTML="<div><strong>"+
a+"</strong><br/>"+c+"</div>",document.getElementsByTagName("main")[0].append(b),document.getElementById("alertBox").classList.add("alertBoxActive"),alertTimeout&&clearTimeout(alertTimeout),alertTimeout=setTimeout(function(){hideNotification()},3E3))}function hideNotification(){document.getElementById("alertBox")&&(document.getElementById("alertBox").classList.remove("alertBoxActive"),setTimeout(function(){document.getElementById("alertBox").remove()},600))}
function notificationsSupported(){return"Notification"in window}
function songChange(a){if("error"!=a.type){var b=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid;if(last_song!=b){var c="",e="",d="myMPD: ";document.getElementById("album-cover").style.backgroundImage='url("'+a.data.cover+'")';"undefined"!=typeof a.data.artist&&0<a.data.artist.length&&"-"!=a.data.artist?(c+=a.data.artist,e+="<br/>"+a.data.artist,d+=a.data.artist+" - ",document.getElementById("artist").innerText=a.data.artist):document.getElementById("artist").innerText="";
"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(c+=" - "+a.data.album,e+="<br/>"+a.data.album,document.getElementById("album").innerText=a.data.album):document.getElementById("album").innerText="";"undefined"!=typeof a.data.title&&0<a.data.title.length?(d+=a.data.title,document.getElementById("currenttrack").innerText=a.data.title):document.getElementById("currenttrack").innerText="";document.title=d;if(d=document.getElementById("queueTrackId"+a.data.currentsongid))d.getElementsByTagName("td")[1].innerText=
function songChange(a){if("error"!=a.type&&"result"!=a.type){var b=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid;if(last_song!=b){var c="",e="",d="myMPD: ";document.getElementById("album-cover").style.backgroundImage='url("'+a.data.cover+'")';"undefined"!=typeof a.data.artist&&0<a.data.artist.length&&"-"!=a.data.artist?(c+=a.data.artist,e+=a.data.artist,d+=a.data.artist+" - ",document.getElementById("artist").innerText=a.data.artist):document.getElementById("artist").innerText=
"";"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(c+=" - "+a.data.album,e+="<br/>"+a.data.album,document.getElementById("album").innerText=a.data.album):document.getElementById("album").innerText="";"undefined"!=typeof a.data.title&&0<a.data.title.length?(d+=a.data.title,document.getElementById("currenttrack").innerText=a.data.title):document.getElementById("currenttrack").innerText="";document.title=d;if(d=document.getElementById("queueTrackId"+a.data.currentsongid))d.getElementsByTagName("td")[1].innerText=
a.data.title;showNotification(a.data.title,c,e,"success");last_song=b}}}function doSetFilterLetter(a){var b=document.getElementById(a+"Letters").querySelector(".active");b&&b.classList.remove("active");b=app.current.filter;"0"==b&&(b="#");document.getElementById(a).innerText="Filter"+("-"!=b?": "+b:"");if("-"!=b){a=document.getElementById(a+"Letters").getElementsByTagName("button");for(var c=a.length,e=0;e<c;e++)if(a[e].innerText==b){a[e].classList.add("active");break}}}
function addFilterLetter(a){for(var b='<button class="mr-1 mb-1 btn btn-sm btn-secondary material-icons material-icons-small">delete</button><button class="mr-1 mb-1 btn btn-sm btn-secondary">#</button>',c=65;90>=c;c++)b+='<button class="mr-1 mb-1 btn-sm btn btn-secondary">'+String.fromCharCode(c)+"</button>";a=document.getElementById(a);a.innerHTML=b;a.addEventListener("click",function(a){switch(a.target.innerText){case "delete":b="-";break;case "#":b="0";break;default:b=a.target.innerText}appGoto(app.current.app,
app.current.tab,app.current.view,"0/"+b+"/"+app.current.search)},!1)}function chVolume(a){a=parseInt(domCache.volumeBar.value)+a;0>a?a=0:100<a&&(a=100);domCache.volumeBar.value=a;sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:a}})}function beautifyDuration(a){var b=Math.floor(a/86400),c=Math.floor(a/3600)-24*b,e=Math.floor(a/60)-60*c-1440*b;a=a-86400*b-3600*c-60*e;return(0<b?b+"\u2009d ":"")+(0<c?c+"\u2009h "+(10>e?"0":""):"")+e+"\u2009m "+(10>a?"0":"")+a+"\u2009s"}

View File

@ -17,5 +17,5 @@
],
"name": "myMPD",
"short_name": "myMPD",
"start_url": "/index.html"
"start_url": "/"
}

View File

@ -1,4 +1,4 @@
var CACHE_NAME = 'myMPD-cache-v1';
var CACHE = 'myMPD-cache-v3.3.0';
var urlsToCache = [
'/',
'/player.html',
@ -20,34 +20,31 @@ var urlsToCache = [
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME).then(function(cache) {
console.log('Opened cache');
caches.open(CACHE).then(function(cache) {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
// Cache hit - return response
if (response)
return response
else
return fetch(event.request);
})
);
event.respondWith(
caches.match(event.request).then(function(response) {
if (response)
return response
else
return fetch(event.request);
}
)
);
});
self.addEventListener('activate', function(event) {
var cacheWhitelist = ['myMPD-cache-v1'];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1)
if (cacheName != CACHE)
return caches.delete(cacheName);
})
);

4
htdocs/sw.min.js vendored
View File

@ -10,5 +10,5 @@ function(){function a(a){return function(d){c||(c=!0,a.call(b,d))}}var b=this,c=
void 0;try{d=a.then}catch(h){this.reject_(h);return}"function"==typeof d?this.settleSameAsThenable_(d,a):this.fulfill_(a)};c.prototype.reject_=function(a){this.settle_(2,a)};c.prototype.fulfill_=function(a){this.settle_(1,a)};c.prototype.settle_=function(a,b){if(0!=this.state_)throw Error("Cannot settle("+a+", "+b+"): Promise already settled in state"+this.state_);this.state_=a;this.result_=b;this.executeOnSettledCallbacks_()};c.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var a=
0;a<this.onSettledCallbacks_.length;++a)l.asyncExecute(this.onSettledCallbacks_[a]);this.onSettledCallbacks_=null}};var l=new b;c.prototype.settleSameAsPromise_=function(a){var b=this.createResolveAndReject_();a.callWhenSettled_(b.resolve,b.reject)};c.prototype.settleSameAsThenable_=function(a,b){var c=this.createResolveAndReject_();try{a.call(b,c.resolve,c.reject)}catch(k){c.reject(k)}};c.prototype.then=function(a,b){function d(a,b){return"function"==typeof a?function(b){try{f(a(b))}catch(m){e(m)}}:
b}var f,e,g=new c(function(a,b){f=a;e=b});this.callWhenSettled_(d(a,f),d(b,e));return g};c.prototype.catch=function(a){return this.then(void 0,a)};c.prototype.callWhenSettled_=function(a,b){function c(){switch(d.state_){case 1:a(d.result_);break;case 2:b(d.result_);break;default:throw Error("Unexpected state: "+d.state_);}}var d=this;null==this.onSettledCallbacks_?l.asyncExecute(c):this.onSettledCallbacks_.push(c)};c.resolve=f;c.reject=function(a){return new c(function(b,c){c(a)})};c.race=function(a){return new c(function(b,
c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE_NAME="myMPD-cache-v1",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mpd.css /js/bootstrap-native-v4.min.js /js/mpd.js /js/player.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_NAME).then(function(a){console.log("Opened cache");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){var b=["myMPD-cache-v1"];a.waitUntil(caches.keys().then(function(a){return Promise.all(a.map(function(a){if(-1===b.indexOf(a))return caches.delete(a)}))}))});
c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE="myMPD-cache-v3.3.0",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mpd.css /js/bootstrap-native-v4.min.js /js/mpd.js /js/player.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)}))}))});

View File

@ -46,7 +46,7 @@ const char * mpd_cmd_strs[] = {
static inline enum mpd_cmd_ids get_cmd_id(const char *cmd)
{
for(int i = 0; i < sizeof(mpd_cmd_strs)/sizeof(mpd_cmd_strs[0]); i++)
if(!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i])))
if (!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i])))
return i;
return -1;
@ -73,12 +73,12 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
else
cmd_id = get_cmd_id("MPD_API_UNKNOWN");
if(cmd_id == -1)
if (cmd_id == -1)
cmd_id = get_cmd_id("MPD_API_UNKNOWN");
switch(cmd_id) {
case MPD_API_UNKNOWN:
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request: %.*s\"}", msg.len, msg.p );
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}");
break;
case MPD_API_SET_SETTINGS:
json_scanf(msg.p, msg.len, "{ data: { notificationWeb: %d, notificationPage: %d} }", &state.a, &state.b);
@ -140,7 +140,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
case MPD_API_UPDATE_DB:
uint_rc = mpd_run_update(mpd.conn, NULL);
if (uint_rc > 0)
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Updating MPD Database...\"}");
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
break;
case MPD_API_SET_PAUSE:
mpd_run_toggle_pause(mpd.conn);
@ -181,14 +181,29 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
}
break;
case MPD_API_MOVE_TRACK:
je = json_scanf(msg.p, msg.len, "{ data: { track:%u, pos:%u } }", &uint_buf1, &uint_buf2);
je = json_scanf(msg.p, msg.len, "{ data: { from:%u, to:%u } }", &uint_buf1, &uint_buf2);
if (je == 2) {
uint_buf1 -= 1;
uint_buf2 -= 1;
uint_buf1 --;
uint_buf2 --;
if (uint_buf1 < uint_buf2)
uint_buf2 --;
mpd_run_move(mpd.conn, uint_buf1, uint_buf2);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
}
break;
case MPD_API_PLAYLIST_MOVE_TRACK:
je = json_scanf(msg.p, msg.len, "{ data: { plist: %Q, from:%u, to:%u } }", &p_charbuf1, &uint_buf1, &uint_buf2);
if (je == 3) {
uint_buf1 --;
uint_buf2 --;
if (uint_buf1 < uint_buf2)
uint_buf2 --;
mpd_send_playlist_move(mpd.conn, p_charbuf1, uint_buf1, uint_buf2);
mpd_response_finish(mpd.conn);
free(p_charbuf1);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
}
break;
case MPD_API_PLAY_TRACK:
je = json_scanf(msg.p, msg.len, "{ data: { track:%u } }", &uint_buf1);
if (je == 1) {
@ -254,6 +269,15 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
free(p_charbuf2);
}
break;
case MPD_API_PLAYLIST_RENAME:
je = json_scanf(msg.p, msg.len, "{ data: { from:%Q, to:%Q } }", &p_charbuf1, &p_charbuf2);
if (je == 2) {
mpd_run_rename(mpd.conn, p_charbuf1, p_charbuf2);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2);
free(p_charbuf1);
free(p_charbuf2);
}
break;
case MPD_API_GET_PLAYLISTS:
je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q } }", &uint_buf1, &p_charbuf1);
if (je == 2) {
@ -261,6 +285,39 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
free(p_charbuf1);
}
break;
case MPD_API_GET_PLAYLIST_LIST:
je = json_scanf(msg.p, msg.len, "{ data: { uri: %Q, offset:%u, filter:%Q } }", &p_charbuf1, &uint_buf1, &p_charbuf2);
if (je == 3) {
n = mympd_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2);
free(p_charbuf1);
free(p_charbuf2);
}
break;
case MPD_API_ADD_TO_PLAYLIST:
je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q, uri:%Q } }", &p_charbuf1, &p_charbuf2);
if (je == 2) {
mpd_run_playlist_add(mpd.conn, p_charbuf1, p_charbuf2);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1);
free(p_charbuf1);
free(p_charbuf2);
}
break;
case MPD_API_PLAYLIST_CLEAR:
je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1);
if (je == 1) {
mpd_run_playlist_clear(mpd.conn, p_charbuf1);
free(p_charbuf1);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
}
break;
case MPD_API_RM_PLAYLIST_TRACK:
je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q, track:%u } }", &p_charbuf1, &uint_buf1);
if (je == 2) {
mpd_run_playlist_delete(mpd.conn, p_charbuf1, uint_buf1);
free(p_charbuf1);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
}
break;
case MPD_API_GET_FILESYSTEM:
je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q, path:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2);
if (je == 3) {
@ -300,7 +357,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1);
if (je == 1) {
int_buf = mpd_run_add_id(mpd.conn, p_charbuf1);
if(int_buf != -1)
if (int_buf != -1)
mpd_run_play_id(mpd.conn, int_buf);
free(p_charbuf1);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
@ -370,7 +427,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
}
break;
case MPD_API_RM_PLAYLIST:
je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q } }", &p_charbuf1);
je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1);
if (je == 1) {
mpd_run_rm(mpd.conn, p_charbuf1);
free(p_charbuf1);
@ -437,12 +494,12 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) {
return 0;
}
if(!c->user_data)
if (!c->user_data)
c->user_data = calloc(1, sizeof(struct t_mpd_client_session));
struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->user_data;
if(mpd.conn_state != MPD_CONNECTED) {
if (mpd.conn_state != MPD_CONNECTED) {
n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}");
#ifdef DEBUG
fprintf(stdout,"Notify: disconnected\n");
@ -455,7 +512,7 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) {
#endif
mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf));
if(s->song_id != mpd.song_id) {
if (s->song_id != mpd.song_id) {
n=mympd_put_current_song(mpd.buf);
#ifdef DEBUG
fprintf(stdout,"Notify: %s\n",mpd.buf);
@ -464,7 +521,7 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) {
s->song_id = mpd.song_id;
}
if(s->queue_version != mpd.queue_version) {
if (s->queue_version != mpd.queue_version) {
n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}");
#ifdef DEBUG
fprintf(stdout,"Notify: update_queue\n");
@ -497,7 +554,7 @@ void mympd_poll(struct mg_mgr *s) {
return;
}
if(mpd.password && !mpd_run_password(mpd.conn, mpd.password)) {
if (mpd.password && !mpd_run_password(mpd.conn, mpd.password)) {
fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn));
for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) {
mympd_notify_callback(c, mpd_connection_get_error_message(mpd.conn));
@ -516,7 +573,7 @@ void mympd_poll(struct mg_mgr *s) {
case MPD_DISCONNECT:
case MPD_RECONNECT:
if(mpd.conn != NULL)
if (mpd.conn != NULL)
mpd_connection_free(mpd.conn);
mpd.conn = NULL;
mpd.conn_state = MPD_DISCONNECTED;
@ -628,7 +685,7 @@ int mympd_put_settings(char *buffer) {
int je;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 };
if( access( mpd.statefile, F_OK ) != -1 ) {
if (access( mpd.statefile, F_OK ) != -1 ) {
char *content = json_fread(mpd.statefile);
je = json_scanf(content, strlen(content), "{notificationWeb: %d, notificationPage: %d}", &state.a, &state.b);
if (je != 2) {
@ -737,9 +794,11 @@ int mympd_put_current_song(char *buffer) {
char cover[500];
song = mpd_run_current_song(mpd.conn);
if(song == NULL)
return 0;
if (song == NULL) {
len = json_printf(&out,"{type: result, data: ok}");
return len;
}
mympd_get_cover(mpd_song_get_uri(song),cover,500);
len = json_printf(&out,"{type: song_change, data: { pos: %d, title: %Q, "
@ -862,7 +921,7 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter
const struct mpd_song *song;
const struct mpd_directory *dir;
entity_count ++;
if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
switch (mpd_entity_get_type(entity)) {
case MPD_ENTITY_TYPE_UNKNOWN:
entity_count --;
@ -959,7 +1018,7 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *
int len;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
if(mpd_search_db_tags(mpd.conn, mpd_tag_name_parse(mpdtagtype)) == false)
if (mpd_search_db_tags(mpd.conn, mpd_tag_name_parse(mpdtagtype)) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_db_tags");
if (mpd_tag_name_parse(mpdsearchtagtype) != MPD_TAG_UNKNOWN) {
@ -967,13 +1026,13 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *
RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint");
}
if(mpd_search_commit(mpd.conn) == false)
if (mpd_search_commit(mpd.conn) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_commit");
len = json_printf(&out, "{type: listDBtags, data: [ ");
while((pair = mpd_recv_pair_tag(mpd.conn, mpd_tag_name_parse(mpdtagtype))) != NULL) {
entity_count ++;
if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,pair->value,1) == 0 ||
( strncmp(filter,"0",1) == 0 && isalpha(*pair->value) == 0 )
) {
@ -1012,7 +1071,7 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) {
char cover[500];
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
if(mpd_search_db_songs(mpd.conn, true) == false) {
if (mpd_search_db_songs(mpd.conn, true) == false) {
RETURN_ERROR_AND_RECOVER("mpd_search_db_songs");
}
@ -1022,14 +1081,14 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) {
if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint");
if(mpd_search_commit(mpd.conn) == false)
if (mpd_search_commit(mpd.conn) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_commit");
else {
len = json_printf(&out, "{ type: listTitles, data: [ ");
while((song = mpd_recv_song(mpd.conn)) != NULL) {
entity_count ++;
if(entity_count <= MAX_ELEMENTS_PER_PAGE) {
if (entity_count <= MAX_ELEMENTS_PER_PAGE) {
if (entities_returned ++) len += json_printf(&out, ", ");
else mympd_get_cover(mpd_song_get_uri(song),cover,500);
len += json_printf(&out, "{ type: song, uri: %Q, duration: %d, title: %Q, track: %Q }",
@ -1070,7 +1129,7 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) {
while((pl = mpd_recv_playlist(mpd.conn)) != NULL) {
entity_count ++;
if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
plpath = mpd_playlist_get_path(pl);
if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,plpath,1) == 0 ||
( strncmp(filter,"0",1) == 0 && isalpha(*plpath) == 0 )
@ -1101,6 +1160,57 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) {
return len;
}
int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter) {
struct mpd_entity *entity;
unsigned int entity_count = 0;
unsigned int entities_returned = 0;
const char *entityName;
int len;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
if (!mpd_send_list_playlist_meta(mpd.conn, uri))
RETURN_ERROR_AND_RECOVER("mpd_send_list_meta");
len = json_printf(&out, "{ type: playlist_detail, data: [ ");
while((entity = mpd_recv_entity(mpd.conn)) != NULL) {
const struct mpd_song *song;
entity_count ++;
if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
song = mpd_entity_get_song(entity);
entityName = mympd_get_tag(song, MPD_TAG_TITLE);
if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,entityName,1) == 0 ||
( strncmp(filter,"0",1) == 0 && isalpha(*entityName) == 0 )
) {
if (entities_returned ++) len += json_printf(&out,",");
len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q }",
mpd_song_get_uri(song),
mympd_get_tag(song, MPD_TAG_ALBUM),
mympd_get_tag(song, MPD_TAG_ARTIST),
mpd_song_get_duration(song),
entityName,
entityName
);
} else {
entity_count --;
}
}
mpd_entity_free(entity);
}
len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, filter: %Q, uri: %Q }",
entity_count,
offset,
entities_returned,
filter,
uri
);
if (len > MAX_SIZE)
fprintf(stderr,"Buffer truncated\n");
return len;
}
int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) {
struct mpd_song *song;
unsigned long entity_count = 0;
@ -1108,9 +1218,8 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear
int len;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
if(mpd_search_db_songs(mpd.conn, false) == false) {
if (mpd_search_db_songs(mpd.conn, false) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_db_songs");
}
if (mpd_tag_name_parse(mpdtagtype) != MPD_TAG_UNKNOWN) {
if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdtagtype), searchstr) == false)
@ -1121,14 +1230,14 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear
RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint");
}
if(mpd_search_commit(mpd.conn) == false)
if (mpd_search_commit(mpd.conn) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_commit");
else {
len = json_printf(&out, "{ type: search, data: [ ");
while((song = mpd_recv_song(mpd.conn)) != NULL) {
entity_count ++;
if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (entities_returned ++) len += json_printf(&out, ", ");
len += json_printf(&out, "{ type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q }",
mpd_song_get_uri(song),
@ -1150,7 +1259,8 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear
);
}
if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n");
if (len > MAX_SIZE)
fprintf(stderr,"Buffer truncated\n");
return len;
}
@ -1160,7 +1270,7 @@ int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) {
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
len = 0;
if(mpd_search_add_db_songs(mpd.conn, false) == false) {
if (mpd_search_add_db_songs(mpd.conn, false) == false) {
RETURN_ERROR_AND_RECOVER("mpd_search_add_db_songs");
}
@ -1173,14 +1283,15 @@ int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) {
RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint");
}
if(mpd_search_commit(mpd.conn) == false)
if (mpd_search_commit(mpd.conn) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_commit");
while((song = mpd_recv_song(mpd.conn)) != NULL) {
mpd_song_free(song);
}
if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n");
if (len > MAX_SIZE)
fprintf(stderr,"Buffer truncated\n");
return len;
}
@ -1191,7 +1302,7 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char
int len;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
if(mpd_search_queue_songs(mpd.conn, false) == false) {
if (mpd_search_queue_songs(mpd.conn, false) == false) {
RETURN_ERROR_AND_RECOVER("mpd_search_queue_songs");
}
@ -1204,14 +1315,14 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char
RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint");
}
if(mpd_search_commit(mpd.conn) == false)
if (mpd_search_commit(mpd.conn) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_commit");
else {
len = json_printf(&out, "{ type: queuesearch, data: [ ");
while((song = mpd_recv_song(mpd.conn)) != NULL) {
entity_count ++;
if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) {
if (entities_returned ++) len += json_printf(&out, ", ");
len += json_printf(&out, "{ type: song, id: %d, pos: %d, album: %Q, artist: %Q, duration: %d, title: %Q }",
mpd_song_get_id(song),

View File

@ -52,6 +52,11 @@
X(MPD_API_REPLACE_TRACK) \
X(MPD_API_ADD_PLAYLIST) \
X(MPD_API_REPLACE_PLAYLIST) \
X(MPD_API_RM_PLAYLIST_TRACK) \
X(MPD_API_PLAYLIST_CLEAR) \
X(MPD_API_PLAYLIST_RENAME) \
X(MPD_API_PLAYLIST_MOVE_TRACK) \
X(MPD_API_ADD_TO_PLAYLIST) \
X(MPD_API_PLAY_TRACK) \
X(MPD_API_SAVE_QUEUE) \
X(MPD_API_RM_TRACK) \
@ -75,6 +80,7 @@
X(MPD_API_SEND_SHUFFLE) \
X(MPD_API_GET_STATS) \
X(MPD_API_GET_PLAYLISTS) \
X(MPD_API_GET_PLAYLIST_LIST) \
X(MPD_API_RM_PLAYLIST) \
X(MPD_API_GET_ARTISTALBUMS) \
X(MPD_API_GET_ARTISTALBUMTITLES) \
@ -146,6 +152,7 @@ int mympd_put_settings(char *buffer);
int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter);
int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album);
int mympd_put_playlists(char *buffer, unsigned int offset, char *filter);
int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter);
int mympd_put_songdetails(char *buffer, char *uri);
void mympd_disconnect();
#endif

View File

@ -249,7 +249,7 @@ int main(int argc, char **argv) {
}
}
if(run_as_user != NULL) {
if (run_as_user != NULL) {
printf("Droping privileges\n");
struct passwd *pw;
if ((pw = getpwnam(run_as_user)) == NULL) {