1
0
mirror of https://github.com/SuperBFG7/ympd synced 2025-04-29 14:13:15 +00:00

Feat: track mpd status changes with idle protocol

This commit is contained in:
jcorporation 2018-08-14 16:10:00 +02:00
parent 15afb2a70e
commit dd5db4f8a3
4 changed files with 99 additions and 82 deletions

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{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} 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}

View File

@ -24,12 +24,13 @@
*/ */
var socket; var socket;
var last_song = ''; var lastSong = '';
var last_state; var lastState;
var current_song = new Object(); var currentSong = new Object();
var playstate = ''; var playstate = '';
var settings = {}; var settings = {};
var alertTimeout; var alertTimeout;
var progressTimer;
let deferredPrompt; let deferredPrompt;
var dragEl; var dragEl;
@ -299,9 +300,9 @@ function appInit() {
domCache.progressBar.value = 0; domCache.progressBar.value = 0;
domCache.progressBar.addEventListener('change', function(event) { domCache.progressBar.addEventListener('change', function(event) {
if (current_song && current_song.currentSongId >= 0) { if (currentSong && currentSong.currentSongId >= 0) {
var seekVal = Math.ceil(current_song.totalTime * (domCache.progressBar.value / 100)); var seekVal = Math.ceil(currentSong.totalTime * (domCache.progressBar.value / 100));
sendAPI({"cmd": "MPD_API_SET_SEEK", "data": {"songid": current_song.currentSongId, "seek": seekVal}}); sendAPI({"cmd": "MPD_API_SET_SEEK", "data": {"songid": currentSong.currentSongId, "seek": seekVal}});
} }
}, false); }, false);
@ -648,7 +649,7 @@ function webSocketConnect() {
} }
socket.onmessage = function got_packet(msg) { socket.onmessage = function got_packet(msg) {
if (msg.data === last_state || msg.data.length == 0) if (msg.data === lastState || msg.data.length == 0)
return; return;
try { try {
@ -674,9 +675,6 @@ function webSocketConnect() {
case 'update_outputs': case 'update_outputs':
sendAPI({"cmd":"MPD_API_GET_OUTPUTS"}, parseOutputs); sendAPI({"cmd":"MPD_API_GET_OUTPUTS"}, parseOutputs);
break; break;
case 'song_change':
songChange(obj);
break;
case 'error': case 'error':
showNotification(obj.data, '', '', 'danger'); showNotification(obj.data, '', '', 'danger');
default: default:
@ -825,8 +823,54 @@ function parseOutputs(obj) {
domCache.outputs.innerHTML = btns; 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) { function parseState(obj) {
if (JSON.stringify(obj) === JSON.stringify(last_state)) if (JSON.stringify(obj) === JSON.stringify(lastState))
return; return;
//Set playstate //Set playstate
@ -841,17 +885,17 @@ function parseState(obj) {
playstate = 'pause'; playstate = 'pause';
} }
if (obj.data.nextsongpos == -1) if (obj.data.nextSongPos == -1)
domCache.btnNext.setAttribute('disabled','disabled'); domCache.btnNext.setAttribute('disabled','disabled');
else else
domCache.btnNext.removeAttribute('disabled'); domCache.btnNext.removeAttribute('disabled');
if (obj.data.songpos <= 0) if (obj.data.songPos <= 0)
domCache.btnPrev.setAttribute('disabled','disabled'); domCache.btnPrev.setAttribute('disabled','disabled');
else else
domCache.btnPrev.removeAttribute('disabled'); domCache.btnPrev.removeAttribute('disabled');
if (obj.data.queue_length == 0) if (obj.data.queueLength == 0)
domCache.btnPlay.setAttribute('disabled','disabled'); domCache.btnPlay.setAttribute('disabled','disabled');
else else
domCache.btnPlay.removeAttribute('disabled'); domCache.btnPlay.removeAttribute('disabled');
@ -873,55 +917,19 @@ function parseState(obj) {
domCache.volumeBar.value = obj.data.volume; domCache.volumeBar.value = obj.data.volume;
//Set play counters //Set play counters
current_song.totalTime = obj.data.totalTime; setCounter(obj.data.currentSongId, obj.data.totalTime, obj.data.elapsedTime);
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); //Get current song
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); sendAPI({"cmd": "MPD_API_GET_CURRENT_SONG"}, songChange);
//clear playback card if not playing //clear playback card if not playing
if (obj.data.songpos == '-1') { if (obj.data.songPos == '-1') {
domCache.currentTrack.innerText = 'Not playing'; domCache.currentTrack.innerText = 'Not playing';
document.getElementById('currentAlbum').innerText = ''; document.getElementById('currentAlbum').innerText = '';
document.getElementById('currentArtist').innerText = ''; document.getElementById('currentArtist').innerText = '';
document.getElementById('currentCover').style.backgroundImage = ''; document.getElementById('currentCover').style.backgroundImage = '';
} }
last_state = obj; lastState = obj;
} }
function getQueue() { function getQueue() {
@ -945,7 +953,7 @@ function parseQueue(obj) {
var nrItems = obj.data.length; var nrItems = obj.data.length;
var table = document.getElementById(app.current.app + 'List'); 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 tbody = table.getElementsByTagName('tbody')[0];
var tr = tbody.getElementsByTagName('tr'); var tr = tbody.getElementsByTagName('tr');
for (var i = 0; i < nrItems; i++) { for (var i = 0; i < nrItems; i++) {
@ -1549,8 +1557,8 @@ function showMenu(el) {
name = el.parentNode.parentNode.getAttribute('data-name'); name = el.parentNode.parentNode.getAttribute('data-name');
} }
if (last_state) if (lastState)
nextsongpos = last_state.data.nextsongpos; nextsongpos = lastState.data.nextsongpos;
var menu = ''; var menu = '';
if ((app.current.app == 'Browse' && app.current.tab == 'Filesystem') || app.current.app == 'Search' || if ((app.current.app == 'Browse' && app.current.tab == 'Filesystem') || app.current.app == 'Search' ||
@ -1850,8 +1858,8 @@ function notificationsSupported() {
function songChange(obj) { function songChange(obj) {
if (obj.type == 'error' || obj.type == 'result') if (obj.type == 'error' || obj.type == 'result')
return; return;
var cur_song = obj.data.title + obj.data.artist + obj.data.album + obj.data.uri + obj.data.currentsongid; var curSong = obj.data.title + obj.data.artist + obj.data.album + obj.data.uri + obj.data.currentSongId;
if (last_song == cur_song) if (lastSong == curSong)
return; return;
var textNotification = ''; var textNotification = '';
var htmlNotification = ''; var htmlNotification = '';
@ -1885,12 +1893,12 @@ function songChange(obj) {
} }
document.title = pageTitle; document.title = pageTitle;
//Update Artist in queue view for http streams //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) if (playingTr)
playingTr.getElementsByTagName('td')[1].innerText = obj.data.title; playingTr.getElementsByTagName('td')[1].innerText = obj.data.title;
showNotification(obj.data.title, textNotification, htmlNotification, 'success'); showNotification(obj.data.title, textNotification, htmlNotification, 'success');
last_song = cur_song; lastSong = curSong;
} }
function doSetFilterLetter(x) { function doSetFilterLetter(x) {

View File

@ -76,13 +76,9 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
cmd_id = get_cmd_id("MPD_API_UNKNOWN"); 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); mpd_response_finish(mpd.conn);
/* // mpd_connection_set_timeout(mpd.conn, mpd.timeout);
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);
switch(cmd_id) { switch(cmd_id) {
case MPD_API_UNKNOWN: case MPD_API_UNKNOWN:
@ -585,9 +581,9 @@ void mympd_notify(struct mg_mgr *s) {
} }
void mympd_parse_idle(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); 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; int len;
for (unsigned j = 0;; ++j) { 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:{" len = json_printf(&out,"{type:state, data:{"
"state:%d, volume:%d, songpos: %d, elapsedTime: %d, " "state:%d, volume:%d, songPos: %d, elapsedTime: %d, "
"totalTime:%d, currentsongid: %d, kbitrate: %d, " "totalTime:%d, currentSongId: %d, kbitrate: %d, "
"audioformat: { sample_rate: %d, bits: %d, channels: %d}, " "audioFormat: { sampleRate: %d, bits: %d, channels: %d}, "
"queue_length: %d, nextsongpos: %d, nextsongid: %d, " "queueLength: %d, nextSongPos: %d, nextSongId: %d, "
"queue_version: %d", "queueVersion: %d",
mpd_status_get_state(status), mpd_status_get_state(status),
mpd_status_get_volume(status), mpd_status_get_volume(status),
mpd_status_get_song_pos(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); mympd_get_cover(mpd_song_get_uri(song),cover,500);
len = json_printf(&out,"{type: song_change, data: { pos: %d, title: %Q, " 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}}", "duration: %d, cover: %Q}}",
mpd_song_get_pos(song), mpd_song_get_pos(song),
mympd_get_tag(song, MPD_TAG_TITLE), mympd_get_tag(song, MPD_TAG_TITLE),

View File

@ -28,6 +28,7 @@
#include <unistd.h> #include <unistd.h>
#include <sys/time.h> #include <sys/time.h>
#include <pwd.h> #include <pwd.h>
#include <pthread.h>
#include "../dist/src/mongoose/mongoose.h" #include "../dist/src/mongoose/mongoose.h"
#include "../dist/src/inih/ini.h" #include "../dist/src/inih/ini.h"
@ -37,6 +38,7 @@
static sig_atomic_t s_signal_received = 0; static sig_atomic_t s_signal_received = 0;
static struct mg_serve_http_opts s_http_server_opts; static struct mg_serve_http_opts s_http_server_opts;
char s_redirect[250]; char s_redirect[250];
struct mg_mgr mgr;
static void signal_handler(int sig_num) { static void signal_handler(int sig_num) {
signal(sig_num, signal_handler); // Reinstantiate signal handler signal(sig_num, signal_handler); // Reinstantiate signal handler
@ -145,8 +147,14 @@ static int inihandler(void* user, const char* section, const char* name, const c
return 1; return 1;
} }
void mympd_poll_loop() {
while (s_signal_received == 0) {
mympd_poll(&mgr, 200);
}
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
struct mg_mgr mgr;
struct mg_connection *nc; struct mg_connection *nc;
struct mg_connection *nc_http; struct mg_connection *nc_http;
struct mg_bind_opts bind_opts; struct mg_bind_opts bind_opts;
@ -255,9 +263,14 @@ int main(int argc, char **argv) {
if (config.ssl == true) if (config.ssl == true)
printf("myMPD started on ssl port %s\n", config.sslport); 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) { while (s_signal_received == 0) {
mympd_poll(&mgr, 60); mg_mgr_poll(&mgr, 100);
mg_mgr_poll(&mgr, 60); mympd_poll(&mgr, 1);
} }
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
mympd_disconnect(); mympd_disconnect();