1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-12-24 18:10:26 +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 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) {

View File

@ -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),

View File

@ -28,6 +28,7 @@
#include <unistd.h>
#include <sys/time.h>
#include <pwd.h>
#include <pthread.h>
#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();