diff --git a/contrib/mympd.conf.dist b/contrib/mympd.conf.dist index 9df4d1c..a7a0c46 100644 --- a/contrib/mympd.conf.dist +++ b/contrib/mympd.conf.dist @@ -17,9 +17,15 @@ sslkey = /etc/mympd/ssl/server.key #myMPD user user = mympd +#Enable local player, needs streamport or streamurl setting +localplayer = true + #Port for mpd http stream streamport = 8000 +#Manual streamurl, overwrites streamport +#streamurl = http://jukebox:8000 + #Name for coverimages coverimage = folder.jpg @@ -34,9 +40,14 @@ stickers = true #List of tags in myMPD gui taglist = Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer +searchtaglist = Artist,Album,AlbumArtist,Title,Genre,Composer,Performer +browsetaglist = Artist,Album,AlbumArtist,Genre,Composer,Performer #Enable smart playlists smartpls = true +#Enable system commands in /etc/mympd/syscmds/ +syscmds = false + #Elements per page for pagination, max: 400 max_elements_per_page = 100 diff --git a/htdocs/index.html b/htdocs/index.html index 950b3e8..d3a6fbe 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -30,7 +30,7 @@ Rescan Database Update Smart Playlists - Local Player + Local Player Settings About Add2HomeScreen @@ -139,9 +139,9 @@
-
diff --git a/htdocs/js/mympd.js b/htdocs/js/mympd.js index 3db0dfb..2886b9e 100644 --- a/htdocs/js/mympd.js +++ b/htdocs/js/mympd.js @@ -193,7 +193,7 @@ function appRoute() { sendAPI({"cmd": "MPD_API_PLAYER_CURRENT_SONG"}, songChange); } else if (app.current.app == 'Queue' ) { - selectTag('searchqueuetag', 'searchqueuetagdesc', app.current.filter); + selectTag('searchqueuetags', 'searchqueuetagsdesc', app.current.filter); getQueue(); } else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'All') { @@ -528,7 +528,7 @@ function appInit() { appGoto(app.current.app, app.current.tab, app.current.view, '0/' + app.current.filter + '/' + this.value); }, false); - document.getElementById('searchqueuetag').addEventListener('click', function(event) { + document.getElementById('searchqueuetags').addEventListener('click', function(event) { if (event.target.nodeName == 'BUTTON') appGoto(app.current.app, app.current.tab, app.current.view, app.current.page + '/' + event.target.getAttribute('data-tag') + '/' + app.current.search); }, false); @@ -962,36 +962,38 @@ function filterCols(x) { } function parseSettings(obj) { - toggleBtn('btnRandom', obj.data.random); - toggleBtn('btnConsume', obj.data.consume); - toggleBtn('btnSingle', obj.data.single); - toggleBtn('btnRepeat', obj.data.repeat); + settings = obj.data; + + toggleBtn('btnRandom', settings.random); + toggleBtn('btnConsume', settings.consume); + toggleBtn('btnSingle', settings.single); + toggleBtn('btnRepeat', settings.repeat); - if (obj.data.crossfade != undefined) { + if (settings.crossfade != undefined) { document.getElementById('inputCrossfade').removeAttribute('disabled'); - document.getElementById('inputCrossfade').value = obj.data.crossfade; + document.getElementById('inputCrossfade').value = settings.crossfade; } else { document.getElementById('inputCrossfade').setAttribute('disabled', 'disabled'); } - if (obj.data.mixrampdb != undefined) { + if (settings.mixrampdb != undefined) { document.getElementById('inputMixrampdb').removeAttribute('disabled'); - document.getElementById('inputMixrampdb').value = obj.data.mixrampdb; + document.getElementById('inputMixrampdb').value = settings.mixrampdb; } else { document.getElementById('inputMixrampdb').setAttribute('disabled', 'disabled'); } - if (obj.data.mixrampdelay != undefined) { + if (settings.mixrampdelay != undefined) { document.getElementById('inputMixrampdelay').removeAttribute('disabled'); - document.getElementById('inputMixrampdelay').value = obj.data.mixrampdelay; + document.getElementById('inputMixrampdelay').value = settings.mixrampdelay; } else { document.getElementById('inputMixrampdelay').setAttribute('disabled', 'disabled'); } - document.getElementById('selectReplaygain').value = obj.data.replaygain; + document.getElementById('selectReplaygain').value = settings.replaygain; var btnnotifyWeb = document.getElementById('btnnotifyWeb'); if (notificationsSupported()) { - if (obj.data.notificationWeb) { - toggleBtn('btnnotifyWeb', obj.data.notificationWeb); + if (settings.notificationWeb) { + toggleBtn('btnnotifyWeb', settings.notificationWeb); Notification.requestPermission(function (permission) { if (!('permission' in Notification)) Notification.permission = permission; @@ -999,7 +1001,7 @@ function parseSettings(obj) { toggleBtn('btnnotifyWeb', 1); } else { toggleBtn('btnnotifyWeb', 0); - obj.data.notificationWeb = true; + settings.notificationWeb = true; } }); } @@ -1011,49 +1013,49 @@ function parseSettings(obj) { toggleBtn('btnnotifyWeb', 0); } - toggleBtn('btnnotifyPage', obj.data.notificationPage); + toggleBtn('btnnotifyPage', settings.notificationPage); var features = ["featStickers", "featSmartpls", "featPlaylists", "featTags"]; for (var j = 0; j < features.length; j++) { var Els = document.getElementsByClassName(features[j]); var ElsLen = Els.length; - var displayEl = obj.data[features[j]] == true ? '' : 'none'; + var displayEl = settings[features[j]] == true ? '' : 'none'; for (var i = 0; i < ElsLen; i++) Els[i].style.display = displayEl; } - if (obj.data.featTags == false) { + if (settings.featTags == false) { app.apps.Browse.active = 'Filesystem'; app.apps.Search.state = '0/filename/'; } - if (obj.data.mixramp == true) + if (settings.mixramp == true) document.getElementsByClassName('mixramp')[0].style.display = ''; else document.getElementsByClassName('mixramp')[0].style.display = 'none'; - if (!obj.data.tags.includes('AlbumArtist') && obj.data.featTags) { - if (obj.data.tags.includes('Artist')) + if (!settings.tags.includes('AlbumArtist') && settings.featTags) { + if (settings.tags.includes('Artist')) app.apps.Browse.tabs.Database.active = 'Artist'; else app.apps.Browse.tabs.Database.active = settings.tags[0]; } - document.getElementById('selectJukeboxMode').value = obj.data.jukeboxMode; - document.getElementById('inputJukeboxQueueLength').value = obj.data.jukeboxQueueLength; - if (obj.data.jukeboxMode == 0 || obj.data.jukeboxMode == 2) { + document.getElementById('selectJukeboxMode').value = settings.jukeboxMode; + document.getElementById('inputJukeboxQueueLength').value = settings.jukeboxQueueLength; + if (settings.jukeboxMode == 0 || settings.jukeboxMode == 2) { document.getElementById('inputJukeboxQueueLength').setAttribute('disabled', 'disabled'); document.getElementById('selectJukeboxPlaylist').setAttribute('disabled', 'disabled'); } - else if (obj.data.jukeboxMode == 1) { + else if (settings.jukeboxMode == 1) { document.getElementById('inputJukeboxQueueLength').removeAttribute('disabled'); document.getElementById('selectJukeboxPlaylist').removeAttribute('disabled'); } - settings = obj.data; - if (obj.data.featPlaylists) { + + if (settings.featPlaylists) { playlistEl = 'selectJukeboxPlaylist'; sendAPI({"cmd": "MPD_API_PLAYLIST_LIST", "data": {"offset": 0, "filter": "-"}}, getAllPlaylists); } else { @@ -1066,29 +1068,42 @@ function parseSettings(obj) { filterCols('colsBrowsePlaylistsDetail'); filterCols('colsBrowseFilesystem'); - settings.mpdstream = 'http://'; - if (settings.mpdhost == '127.0.0.1' || settings.mpdhost == 'localhost') - settings.mpdstream += window.location.hostname; - else - settings.mpdstream += settings.mpdhost; - settings.mpdstream += ':' + settings.streamport + '/'; - - addTagList('BrowseDatabaseByTagDropdown', false); - addTagList('searchqueuetag', true); - addTagList('searchtags', true); - - for (var i = 0; i < obj.data.tags.length; i++) - app.apps.Browse.tabs.Database.views[obj.data.tags[i]] = { "state": "0/-/", "scrollPos": 0 }; - - var syscmdsList = ''; - var syscmdsListLen = obj.data.syscmds.length; - if (syscmdsListLen > 0) { - syscmdsList = ''; - for (var i = 0; i < syscmdsListLen; i++) - syscmdsList += '' + obj.data.syscmds[i] + ''; + if (settings.localplayer) { + if (settings.streamurl == '') { + settings.mpdstream = 'http://'; + if (settings.mpdhost == '127.0.0.1' || settings.mpdhost == 'localhost') + settings.mpdstream += window.location.hostname; + else + settings.mpdstream += settings.mpdhost; + settings.mpdstream += ':' + settings.streamport + '/'; + } + else + settings.mpdstream = settings.streamurl; + document.getElementsByClassName('featLocalplayer')[0].classList.remove('hide'); } - document.getElementById('syscmds').innerHTML = syscmdsList; + else + document.getElementsByClassName('featLocalplayer')[0].classList.add('hide'); + + addTagList('BrowseDatabaseByTagDropdown', 'browsetags'); + addTagList('searchqueuetags', 'searchtags'); + addTagList('searchtags', 'searchtags'); + + for (var i = 0; i < settings.tags.length; i++) + app.apps.Browse.tabs.Database.views[settings.tags[i]] = { "state": "0/-/", "scrollPos": 0 }; + + if (settings.featSyscmds) { + var syscmdsList = ''; + var syscmdsListLen = settings.syscmds.length; + if (syscmdsListLen > 0) { + syscmdsList = ''; + for (var i = 0; i < syscmdsListLen; i++) + syscmdsList += '' + settings.syscmds[i] + ''; + } + document.getElementById('syscmds').innerHTML = syscmdsList; + } + else + document.getElementById('syscmds').innerHTML = ''; setCols('Queue'); setCols('Search'); @@ -1980,17 +1995,14 @@ function parseSmartPlaylist(obj) { var nameEl = document.getElementById('saveSmartPlaylistName'); nameEl.value = obj.data.playlist; nameEl.classList.remove('is-invalid'); - document.getElementById('saveSmartPlaylistType').value = obj.data.Type; + document.getElementById('saveSmartPlaylistType').value = obj.data.type; document.getElementById('saveSmartPlaylistFrm').classList.remove('was-validated'); document.getElementById('saveSmartPlaylistSearch').classList.add('hide'); document.getElementById('saveSmartPlaylistSticker').classList.add('hide'); document.getElementById('saveSmartPlaylistNewest').classList.add('hide'); var tagList = ''; - for (var i = 0; i < settings.tags.length; i++) { - if (settings.tags[i] != 'Track') { - tagList += ''; - } - } + for (var i = 0; i < settings.searchtags.length; i++) + tagList += ''; document.getElementById('selectSaveSmartPlaylistTag').innerHTML = tagList; if (obj.data.type == 'search') { document.getElementById('saveSmartPlaylistSearch').classList.remove('hide'); @@ -2715,7 +2727,7 @@ function selectTag(btnsEl, desc, setTo) { document.getElementById(desc).innerText = aBtn.innerText; } } - +/* function addTagList(x, any) { var tagList = ''; var tagBlacklist = ["Title", "MUSICBRAINZ_TRACKID", "Count", "Disc", "Comment", "Name"]; @@ -2735,6 +2747,19 @@ function addTagList(x, any) { var tagListEl = document.getElementById(x); tagListEl.innerHTML = tagList; } +*/ +function addTagList(el, list) { + var tagList = ''; + if (list == 'searchtags') { + if (settings.featTags == true) + tagList += ''; + else + tagList += ''; + } + for (var i = 0; i < settings[list].length; i++) + tagList += ''; + document.getElementById(el).innerHTML = tagList; +} function gotoTagList() { appGoto(app.current.app, app.current.tab, app.current.view, '0/-/'); diff --git a/src/mpd_client.c b/src/mpd_client.c index 9a86674..de165a1 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -144,13 +144,17 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_SYSCMD: - je = json_scanf(msg.p, msg.len, "{data: {cmd: %Q}}", &p_charbuf1); - if (je == 1) { - int_buf1 = list_get_value(&syscmds, p_charbuf1); - if (int_buf1 > -1) - n = mympd_syscmd(mpd.buf, p_charbuf1, int_buf1); - free(p_charbuf1); - } + if (config.syscmds == true) { + je = json_scanf(msg.p, msg.len, "{data: {cmd: %Q}}", &p_charbuf1); + if (je == 1) { + int_buf1 = list_get_value(&syscmds, p_charbuf1); + if (int_buf1 > -1) + n = mympd_syscmd(mpd.buf, p_charbuf1, int_buf1); + free(p_charbuf1); + } + } + else + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}"); break; case MPD_API_PLAYER_STATE: n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); @@ -719,7 +723,9 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) { void mympd_mpd_features() { struct mpd_pair *pair; char s[2] = ","; - char *str = strdup(config.taglist); + char *taglist = strdup(config.taglist); + char *searchtaglist = strdup(config.searchtaglist); + char *browsetaglist = strdup(config.browsetaglist); char *token; mpd.protocol = mpd_connection_get_server_version(mpd.conn); @@ -769,7 +775,7 @@ void mympd_mpd_features() { else { mpd.feat_tags = true; printf("\nmyMPD enabled tags: "); - token = strtok(str, s); + token = strtok(taglist, s); while (token != NULL) { if (list_get_value(&mpd_tags, token) == 1) { list_push(&mympd_tags, token, 1); @@ -777,9 +783,29 @@ void mympd_mpd_features() { } token = strtok(NULL, s); } - printf("\n"); + printf("\nmyMPD enabled searchtags: "); + token = strtok(searchtaglist, s); + while (token != NULL) { + if (list_get_value(&mpd_tags, token) == 1) { + list_push(&mympd_searchtags, token, 1); + printf("%s ", token); + } + token = strtok(NULL, s); + } + printf("\nmyMPD enabled browsetags: "); + token = strtok(browsetaglist, s); + while (token != NULL) { + if (list_get_value(&mpd_tags, token) == 1) { + list_push(&mympd_browsetags, token, 1); + printf("%s ", token); + } + token = strtok(NULL, s); + } + printf("\n"); } - free(str); + free(taglist); + free(searchtaglist); + free(browsetaglist); } void mympd_idle(struct mg_mgr *s, int timeout) { @@ -1324,8 +1350,8 @@ int mympd_put_settings(char *buffer) { len = json_printf(&out, "{type: settings, data: {" "repeat: %d, single: %d, crossfade: %d, consume: %d, random: %d, " - "mixrampdb: %f, mixrampdelay: %f, mpdhost: %Q, mpdport: %d, passwort_set: %B, featPlaylists: %B, featTags: %B, " - "streamport: %d, coverimage: %Q, featStickers: %B, mixramp: %B, featSmartpls: %B, maxElementsPerPage: %d, " + "mixrampdb: %f, mixrampdelay: %f, mpdhost: %Q, mpdport: %d, passwort_set: %B, featSyscmds: %B, featPlaylists: %B, featTags: %B, " + "localplayer: %B, streamport: %d, streamurl: %Q, coverimage: %Q, featStickers: %B, mixramp: %B, featSmartpls: %B, maxElementsPerPage: %d, " "replaygain: %Q, notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, jukeboxQueueLength: %d, " "tags: [", mpd_status_get_repeat(status), @@ -1338,9 +1364,12 @@ int mympd_put_settings(char *buffer) { config.mpdhost, config.mpdport, config.mpdpass ? "true" : "false", + config.syscmds, mpd.feat_playlists, mpd.feat_tags, - config.streamport, + config.localplayer, + config.streamport, + config.streamurl, config.coverimage, config.stickers, config.mixramp, @@ -1356,6 +1385,7 @@ int mympd_put_settings(char *buffer) { mpd_status_free(status); free(replaygain); + nr = 0; struct node *current = mympd_tags.list; while (current != NULL) { if (nr++) @@ -1363,9 +1393,18 @@ int mympd_put_settings(char *buffer) { len += json_printf(&out, "%Q", current->data); current = current->next; } - len += json_printf(&out, "], syscmds: ["); + len += json_printf(&out, "], searchtags: ["); nr = 0; - current = syscmds.list; + current = mympd_searchtags.list; + while (current != NULL) { + if (nr++) + len += json_printf(&out, ","); + len += json_printf(&out, "%Q", current->data); + current = current->next; + } + len += json_printf(&out, "], browsetags: ["); + nr = 0; + current = mympd_browsetags.list; while (current != NULL) { if (nr++) len += json_printf(&out, ","); @@ -1373,6 +1412,19 @@ int mympd_put_settings(char *buffer) { current = current->next; } len += json_printf(&out, "]"); + + if (config.syscmds == true) { + len += json_printf(&out, ", syscmds: ["); + nr = 0; + current = syscmds.list; + while (current != NULL) { + if (nr++) + len += json_printf(&out, ","); + len += json_printf(&out, "%Q", current->data); + current = current->next; + } + len += json_printf(&out, "]"); + } len += json_printf(&out, ", colsQueue: %s", mympd_state.colsQueue); len += json_printf(&out, ", colsSearch: %s", mympd_state.colsSearch); len += json_printf(&out, ", colsBrowseDatabase: %s", mympd_state.colsBrowseFilesystem); diff --git a/src/mpd_client.h b/src/mpd_client.h index a88c2d6..83e5493 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -168,6 +168,8 @@ struct t_mpd { struct list mpd_tags; struct list mympd_tags; +struct list mympd_searchtags; +struct list mympd_browsetags; struct list syscmds; @@ -181,15 +183,20 @@ typedef struct { const char* sslcert; const char* sslkey; const char* user; - long streamport; const char* coverimage; bool stickers; bool mixramp; const char* taglist; + const char* searchtaglist; + const char* browsetaglist; bool smartpls; const char* varlibdir; const char* etcdir; unsigned long max_elements_per_page; + bool syscmds; + bool localplayer; + long streamport; + const char* streamurl; } t_config; t_config config; diff --git a/src/mympd.c b/src/mympd.c index 1cd2185..7928b94 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -171,6 +171,10 @@ static int inihandler(void* user, const char* section, const char* name, const c p_config->mixramp = false; else if (MATCH("taglist")) p_config->taglist = strdup(value); + else if (MATCH("searchtaglist")) + p_config->searchtaglist = strdup(value); + else if (MATCH("browsetaglist")) + p_config->browsetaglist = strdup(value); else if (MATCH("max_elements_per_page")) { p_config->max_elements_per_page = strtol(value, &crap, 10); if (p_config->max_elements_per_page > MAX_ELEMENTS_PER_PAGE) { @@ -178,6 +182,18 @@ static int inihandler(void* user, const char* section, const char* name, const c p_config->max_elements_per_page = MAX_ELEMENTS_PER_PAGE; } } + else if (MATCH("syscmds")) + if (strcmp(value, "true") == 0) + p_config->syscmds = true; + else + p_config->syscmds = false; + else if (MATCH("localplayer")) + if (strcmp(value, "true") == 0) + p_config->localplayer = true; + else + p_config->localplayer = false; + else if (MATCH("streamurl")) + p_config->streamurl = strdup(value); else return 0; /* unknown section/name, error */ @@ -190,19 +206,22 @@ void read_syscmds() { char dirname[400]; char *cmd; long order; - - snprintf(dirname, 400, "%s/syscmds", config.etcdir); - printf("Reading syscmds: %s\n", dirname); - if ((dir = opendir (dirname)) != NULL) { - while ((ent = readdir(dir)) != NULL) { - if (strncmp(ent->d_name, ".", 1) == 0) - continue; - order = strtol(ent->d_name, &cmd, 10); - if (strcmp(cmd, "") != 0) - list_push(&syscmds, strdup(cmd), order); + if (config.syscmds == true) { + snprintf(dirname, 400, "%s/syscmds", config.etcdir); + printf("Reading syscmds: %s\n", dirname); + if ((dir = opendir (dirname)) != NULL) { + while ((ent = readdir(dir)) != NULL) { + if (strncmp(ent->d_name, ".", 1) == 0) + continue; + order = strtol(ent->d_name, &cmd, 10); + if (strcmp(cmd, "") != 0) + list_push(&syscmds, strdup(cmd), order); + } + closedir(dir); } - closedir(dir); } + else + printf("Syscmds are disabled\n"); } void read_statefiles() { @@ -321,15 +340,20 @@ int main(int argc, char **argv) { config.sslkey = "/etc/mympd/ssl/server.key"; config.user = "mympd"; config.streamport = 8000; + config.streamurl = ""; config.coverimage = "folder.jpg"; config.varlibdir = "/var/lib/mympd"; config.stickers = true; config.mixramp = true; config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer"; + config.searchtaglist = "Artist,Album,AlbumArtist,Title,Genre,Composer,Performer"; + config.browsetaglist = "Artist,Album,AlbumArtist,Genre,Composer,Performer"; config.smartpls = true; config.max_elements_per_page = 100; char *etcdir = strdup(argv[1]); config.etcdir = dirname(etcdir); + config.syscmds = false; + config.localplayer = true; mpd.timeout = 3000; mpd.last_update_sticker_song_id = -1;