diff --git a/dist/htdocs/css/mpd.min.css b/dist/htdocs/css/mpd.min.css index ad460dd..f6993c3 100644 --- a/dist/htdocs/css/mpd.min.css +++ b/dist/htdocs/css/mpd.min.css @@ -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{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} \ No newline at end of file +html{position:relative;min-height:100%}body{margin-bottom:60px;padding-top:50px;padding-bottom:50px;background-color:#888}main{padding-top:20px}footer{position:absolute;bottom:0}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}#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}.color-darkgrey,.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}#progressBar{width:100%;margin-top:8px}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-top{overflow:hidden;display:block}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}div#alertBox{position:fixed;top:50px;right:10px;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5}caption{caption-side:top;font-size:120%;font-weight:bold;color:black}.dragover>td{border-top:25px solid transparent}[draggable]{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;user-select:none;-khtml-user-drag:element;-webkit-user-drag:element} \ No newline at end of file diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 3d5d6b0..e3f272e 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -24,12 +24,13 @@ */ var socket; -var last_song = ''; -var last_state; -var current_song = new Object(); +var lastSong = ''; +var lastState; +var currentSong = new Object(); var playstate = ''; var settings = {}; var alertTimeout; +var progressTimer; let deferredPrompt; var dragEl; @@ -299,9 +300,9 @@ function appInit() { domCache.progressBar.value = 0; domCache.progressBar.addEventListener('change', function(event) { - 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}}); + if (currentSong && currentSong.currentSongId >= 0) { + var seekVal = Math.ceil(currentSong.totalTime * (domCache.progressBar.value / 100)); + sendAPI({"cmd": "MPD_API_SET_SEEK", "data": {"songid": currentSong.currentSongId, "seek": seekVal}}); } }, false); @@ -648,7 +649,7 @@ function webSocketConnect() { } socket.onmessage = function got_packet(msg) { - if (msg.data === last_state || msg.data.length == 0) + if (msg.data === lastState || msg.data.length == 0) return; try { @@ -674,9 +675,6 @@ function webSocketConnect() { case 'update_outputs': sendAPI({"cmd":"MPD_API_GET_OUTPUTS"}, parseOutputs); break; - case 'song_change': - songChange(obj); - break; case 'error': showNotification(obj.data, '', '', 'danger'); default: @@ -825,8 +823,54 @@ function parseOutputs(obj) { domCache.outputs.innerHTML = btns; } +function setCounter(currentSongId, totalTime, elapsedTime) { + currentSong.totalTime = totalTime; + currentSong.elapsedTime = elapsedTime; + currentSong.currentSongId = currentSongId; + var total_minutes = Math.floor(totalTime / 60); + var total_seconds = totalTime - total_minutes * 60; + var elapsed_minutes = Math.floor(elapsedTime / 60); + var elapsed_seconds = elapsedTime - elapsed_minutes * 60; + + domCache.progressBar.value = Math.floor(100 * elapsedTime / totalTime); + + var counterText = elapsed_minutes + ":" + + (elapsed_seconds < 10 ? '0' : '') + elapsed_seconds + " / " + + total_minutes + ":" + (total_seconds < 10 ? '0' : '') + total_seconds; + domCache.counter.innerText = counterText; + + //Set playing track in queue view + if (lastState) { + var tr = document.getElementById('queueTrackId' + lastState.data.currentSongId); + if (tr) { + var trtds = tr.getElementsByTagName('td'); + trtds[4].innerText = tr.getAttribute('data-duration'); + trtds[0].classList.remove('material-icons'); + trtds[0].innerText = tr.getAttribute('data-songpos'); + tr.classList.remove('font-weight-bold'); + } + } + var tr = document.getElementById('queueTrackId' + currentSongId); + if (tr) { + var trtds = tr.getElementsByTagName('td'); + trtds[4].innerText = counterText; + trtds[0].classList.add('material-icons'); + trtds[0].innerText = 'play_arrow'; + tr.classList.add('font-weight-bold'); + } + + if (progressTimer) + clearTimeout(progressTimer); + if (playstate == 'play') { + progressTimer = setTimeout(function() { + currentSong.elapsedTime ++; + setCounter(currentSong.currentSongId, currentSong.totalTime, currentSong.elapsedTime); + }, 1000); + } +} + function parseState(obj) { - if (JSON.stringify(obj) === JSON.stringify(last_state)) + if (JSON.stringify(obj) === JSON.stringify(lastState)) return; //Set playstate @@ -841,17 +885,17 @@ function parseState(obj) { playstate = 'pause'; } - if (obj.data.nextsongpos == -1) + if (obj.data.nextSongPos == -1) domCache.btnNext.setAttribute('disabled','disabled'); else domCache.btnNext.removeAttribute('disabled'); - if (obj.data.songpos <= 0) + if (obj.data.songPos <= 0) domCache.btnPrev.setAttribute('disabled','disabled'); else domCache.btnPrev.removeAttribute('disabled'); - if (obj.data.queue_length == 0) + if (obj.data.queueLength == 0) domCache.btnPlay.setAttribute('disabled','disabled'); else domCache.btnPlay.removeAttribute('disabled'); @@ -873,55 +917,19 @@ function parseState(obj) { domCache.volumeBar.value = obj.data.volume; //Set play counters - current_song.totalTime = obj.data.totalTime; - current_song.currentSongId = obj.data.currentsongid; - var total_minutes = Math.floor(obj.data.totalTime / 60); - var total_seconds = obj.data.totalTime - total_minutes * 60; - var elapsed_minutes = Math.floor(obj.data.elapsedTime / 60); - var elapsed_seconds = obj.data.elapsedTime - elapsed_minutes * 60; - - domCache.progressBar.value = Math.floor(100 * obj.data.elapsedTime / obj.data.totalTime); - - var counterText = elapsed_minutes + ":" + - (elapsed_seconds < 10 ? '0' : '') + elapsed_seconds + " / " + - total_minutes + ":" + (total_seconds < 10 ? '0' : '') + total_seconds; - domCache.counter.innerText = counterText; - - //Set playing track in queue view - if (last_state) { - var tr = document.getElementById('queueTrackId' + last_state.data.currentsongid); - if (tr) { - var trtds = tr.getElementsByTagName('td'); - trtds[4].innerText = tr.getAttribute('data-duration'); - trtds[0].classList.remove('material-icons'); - trtds[0].innerText = tr.getAttribute('data-songpos'); - tr.classList.remove('font-weight-bold'); - } - } - var tr = document.getElementById('queueTrackId' + obj.data.currentsongid); - if (tr) { - var trtds = tr.getElementsByTagName('td'); - trtds[4].innerText = counterText; - trtds[0].classList.add('material-icons'); - trtds[0].innerText = 'play_arrow'; - tr.classList.add('font-weight-bold'); - } - - //Get current song on queue change for http streams - if (last_state == undefined || obj.data.queue_version != last_state.data.queue_version) - sendAPI({"cmd": "MPD_API_GET_CURRENT_SONG"}, songChange); - - + setCounter(obj.data.currentSongId, obj.data.totalTime, obj.data.elapsedTime); + //Get current song + sendAPI({"cmd": "MPD_API_GET_CURRENT_SONG"}, songChange); //clear playback card if not playing - if (obj.data.songpos == '-1') { + if (obj.data.songPos == '-1') { domCache.currentTrack.innerText = 'Not playing'; document.getElementById('currentAlbum').innerText = ''; document.getElementById('currentArtist').innerText = ''; document.getElementById('currentCover').style.backgroundImage = ''; } - last_state = obj; + lastState = obj; } function getQueue() { @@ -945,7 +953,7 @@ function parseQueue(obj) { var nrItems = obj.data.length; var table = document.getElementById(app.current.app + 'List'); - table.setAttribute('data-version', obj.queue_version); + table.setAttribute('data-version', obj.queueVersion); var tbody = table.getElementsByTagName('tbody')[0]; var tr = tbody.getElementsByTagName('tr'); for (var i = 0; i < nrItems; i++) { @@ -1549,8 +1557,8 @@ function showMenu(el) { name = el.parentNode.parentNode.getAttribute('data-name'); } - if (last_state) - nextsongpos = last_state.data.nextsongpos; + if (lastState) + nextsongpos = lastState.data.nextsongpos; var menu = ''; if ((app.current.app == 'Browse' && app.current.tab == 'Filesystem') || app.current.app == 'Search' || @@ -1850,8 +1858,8 @@ function notificationsSupported() { function songChange(obj) { 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) + var curSong = obj.data.title + obj.data.artist + obj.data.album + obj.data.uri + obj.data.currentSongId; + if (lastSong == curSong) return; var textNotification = ''; var htmlNotification = ''; @@ -1885,12 +1893,12 @@ function songChange(obj) { } document.title = pageTitle; //Update Artist in queue view for http streams - var playingTr = document.getElementById('queueTrackId' + obj.data.currentsongid); + var playingTr = document.getElementById('queueTrackId' + obj.data.currentSongId); if (playingTr) playingTr.getElementsByTagName('td')[1].innerText = obj.data.title; showNotification(obj.data.title, textNotification, htmlNotification, 'success'); - last_song = cur_song; + lastSong = curSong; } function doSetFilterLetter(x) { diff --git a/src/mpd_client.c b/src/mpd_client.c index 51fb840..1dc6dd9 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -75,14 +75,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { if (cmd_id == -1) cmd_id = get_cmd_id("MPD_API_UNKNOWN"); - mpd_send_noidle(mpd.conn); + mpd_send_noidle(mpd.conn); +// mympd_parse_idle(mg_mgr); mpd_response_finish(mpd.conn); -/* - enum mpd_idle idle_event; - mpd_connection_set_timeout(mpd.conn, 1); - idle_event = mpd_recv_idle(mpd.conn, false); -*/ - mpd_connection_set_timeout(mpd.conn, mpd.timeout); +// mpd_connection_set_timeout(mpd.conn, mpd.timeout); switch(cmd_id) { case MPD_API_UNKNOWN: @@ -515,7 +511,7 @@ void mympd_poll(struct mg_mgr *s, int timeout) { switch (mpd.conn_state) { case MPD_DISCONNECTED: /* Try to connect */ - fprintf(stdout, "MPD Connecting to %s: %d\n", config.mpdhost, config.mpdport); + fprintf(stdout, "MPD Connecting to %s:%d\n", config.mpdhost, config.mpdport); mpd.conn = mpd_connection_new(config.mpdhost, config.mpdport, mpd.timeout); if (mpd.conn == NULL) { fprintf(stderr, "Out of memory."); @@ -585,9 +581,9 @@ void mympd_notify(struct mg_mgr *s) { } void mympd_parse_idle(struct mg_mgr *s) { - mpd_connection_set_timeout(mpd.conn, 60); +// mpd_connection_set_timeout(mpd.conn, 60); enum mpd_idle idle_bitmask = mpd_recv_idle(mpd.conn, false); - mpd_connection_set_timeout(mpd.conn, mpd.timeout); +// mpd_connection_set_timeout(mpd.conn, mpd.timeout); int len; for (unsigned j = 0;; ++j) { @@ -668,11 +664,11 @@ int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsig } len = json_printf(&out,"{type:state, data:{" - "state:%d, volume:%d, songpos: %d, elapsedTime: %d, " - "totalTime:%d, currentsongid: %d, kbitrate: %d, " - "audioformat: { sample_rate: %d, bits: %d, channels: %d}, " - "queue_length: %d, nextsongpos: %d, nextsongid: %d, " - "queue_version: %d", + "state:%d, volume:%d, songPos: %d, elapsedTime: %d, " + "totalTime:%d, currentSongId: %d, kbitrate: %d, " + "audioFormat: { sampleRate: %d, bits: %d, channels: %d}, " + "queueLength: %d, nextSongPos: %d, nextSongId: %d, " + "queueVersion: %d", mpd_status_get_state(status), mpd_status_get_volume(status), mpd_status_get_song_pos(status), @@ -841,7 +837,7 @@ int mympd_put_current_song(char *buffer) { mympd_get_cover(mpd_song_get_uri(song),cover,500); len = json_printf(&out,"{type: song_change, data: { pos: %d, title: %Q, " - "artist: %Q, album: %Q, uri: %Q, currentsongid: %d, albumartist: %Q, " + "artist: %Q, album: %Q, uri: %Q, currentSongId: %d, albumartist: %Q, " "duration: %d, cover: %Q}}", mpd_song_get_pos(song), mympd_get_tag(song, MPD_TAG_TITLE), diff --git a/src/mympd.c b/src/mympd.c index 0febbec..8503376 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "../dist/src/mongoose/mongoose.h" #include "../dist/src/inih/ini.h" @@ -37,6 +38,7 @@ static sig_atomic_t s_signal_received = 0; static struct mg_serve_http_opts s_http_server_opts; char s_redirect[250]; +struct mg_mgr mgr; static void signal_handler(int sig_num) { signal(sig_num, signal_handler); // Reinstantiate signal handler @@ -145,8 +147,14 @@ static int inihandler(void* user, const char* section, const char* name, const c return 1; } +void mympd_poll_loop() { + while (s_signal_received == 0) { + mympd_poll(&mgr, 200); + } +} + int main(int argc, char **argv) { - struct mg_mgr mgr; + struct mg_connection *nc; struct mg_connection *nc_http; struct mg_bind_opts bind_opts; @@ -254,10 +262,15 @@ int main(int argc, char **argv) { printf("myMPD started on http port %s\n", config.webport); if (config.ssl == true) printf("myMPD started on ssl port %s\n", config.sslport); + + //Create thread for mpd idle connection + //pthread_t tid; + //pthread_create(&tid, NULL, &mympd_poll_loop, NULL); + //Main loop for http handling while (s_signal_received == 0) { - mympd_poll(&mgr, 60); - mg_mgr_poll(&mgr, 60); + mg_mgr_poll(&mgr, 100); + mympd_poll(&mgr, 1); } mg_mgr_free(&mgr); mympd_disconnect();