From fc115a9d1b72b5b6be030dfe07d6dec6562c436c Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sun, 30 Sep 2018 02:03:33 +0100 Subject: [PATCH] Feat: jukebox mode - add whole album #57 Feat: jukebox mode - configurable number of tracks in queue #57 --- htdocs/index.html | 24 ++++++-- htdocs/js/mympd.js | 46 ++++++++++----- src/mpd_client.c | 141 ++++++++++++++++++++++++++------------------- src/mpd_client.h | 3 +- src/mympd.c | 13 +++-- 5 files changed, 143 insertions(+), 84 deletions(-) diff --git a/htdocs/index.html b/htdocs/index.html index f608b53..b662f05 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -743,16 +743,30 @@

Jukebox

-
- +
+
+
Mode
+
+
-
+
+
+
+
Min. queue length
+
+ +
Must be a number and > 0
+
+

Notifications

diff --git a/htdocs/js/mympd.js b/htdocs/js/mympd.js index a8edbd0..9e5889d 100644 --- a/htdocs/js/mympd.js +++ b/htdocs/js/mympd.js @@ -808,7 +808,6 @@ function parseSettings(obj) { toggleBtn('btnConsume', obj.data.consume); toggleBtn('btnSingle', obj.data.single); toggleBtn('btnRepeat', obj.data.repeat); - toggleBtn('btnJukebox', obj.data.jukeboxMode); if (obj.data.crossfade != undefined) { document.getElementById('inputCrossfade').removeAttribute('disabled'); @@ -859,26 +858,26 @@ function parseSettings(obj) { var stickerEls = document.getElementsByClassName('stickers'); var stickerElsLen = stickerEls.length; var displayStickers = obj.data.stickers == true ? '' : 'none'; - for (var i = 0; i < stickerElsLen; i++) { + for (var i = 0; i < stickerElsLen; i++) stickerEls[i].style.display = displayStickers; - } var smartplsEls = document.getElementsByClassName('smartpls'); var smartplsElsLen = smartplsEls.length; var displaySmartpls = obj.data.smartpls == true ? '' : 'none'; - for (var i = 0; i < smartplsElsLen; i++) { + for (var i = 0; i < smartplsElsLen; i++) smartplsEls[i].style.display = displaySmartpls; - } - if (obj.data.mixramp == true) { + if (obj.data.mixramp == true) document.getElementsByClassName('mixramp')[0].style.display = ''; - } else { + else document.getElementsByClassName('mixramp')[0].style.display = 'none'; - } + + document.getElementById('selectJukeboxMode').value = obj.data.jukeboxMode; + document.getElementById('inputJukeboxQueueLength').value = obj.data.jukeboxQueueLength; settings = obj.data; - playlistEl = 'jukeboxPlaylist'; + playlistEl = 'selectJukeboxPlaylist'; sendAPI({"cmd": "MPD_API_PLAYLIST_LIST", "data": {"offset": 0, "filter": "-"}}, getAllPlaylists); settings.mpdstream = 'http://'; @@ -1544,12 +1543,12 @@ function getAllPlaylists(obj) { if (obj.offset == 0) { if (playlistEl == 'addToPlaylistPlaylist') playlists = ''; - else if (playlistEl == 'jukeboxPlaylist') + else if (playlistEl == 'selectJukeboxPlaylist') playlists = ''; } for (var i = 0; i < nrItems; i++) { playlists += ''; } @@ -1831,7 +1830,8 @@ function showMenu(el, event) { addMenuItem({"cmd": "replaceQueue", "options": [type, uri, name]}, 'Replace queue') + (x.getAttribute('data-ro') == 'false' ? addMenuItem({"cmd": "removeFromPlaylist", "options": [x.getAttribute('data-uri'), el.parentNode.parentNode.getAttribute('data-songpos')]}, 'Remove') : '') + - addMenuItem({"cmd": "showAddToPlaylist", "options": [uri]}, 'Add to playlist'); + addMenuItem({"cmd": "showAddToPlaylist", "options": [uri]}, 'Add to playlist') + + (uri.indexOf('http') == -1 ? addMenuItem({"cmd": "songDetails", "options": [uri]}, 'Songdetails') : ''); } else if (app.current.app == 'Queue') { menu += addMenuItem({"cmd": "delQueueSong", "options": ["single", el.parentNode.parentNode.getAttribute('data-trackid')]}, 'Remove') + @@ -1999,6 +1999,20 @@ function confirmSettings() { formOK = false; } } + var inputJukeboxQueueLength = document.getElementById('inputJukeboxQueueLength'); + var value = parseInt(inputJukeboxQueueLength.value); + if (!isNaN(value)) { + if (value > 0) { + inputJukeboxQueueLength.value = value; + } else { + inputJukeboxQueueLength.classList.add('is-invalid'); + formOK = false; + } + } + else { + inputJukeboxQueueLength.classList.add('is-invalid'); + formOK = false; + } if (settings.mixramp) { var inputMixrampdb = document.getElementById('inputMixrampdb'); if (!inputMixrampdb.getAttribute('disabled')) { @@ -2026,7 +2040,8 @@ function confirmSettings() { if (formOK == true) { var selectReplaygain = document.getElementById('selectReplaygain'); - var selectJukeboxPlaylist = document.getElementById('jukeboxPlaylist'); + var selectJukeboxPlaylist = document.getElementById('selectJukeboxPlaylist'); + var selectJukeboxMode = document.getElementById('selectJukeboxMode'); sendAPI({"cmd": "MPD_API_SETTINGS_SET", "data": { "consume": (document.getElementById('btnConsume').classList.contains('active') ? 1 : 0), "random": (document.getElementById('btnRandom').classList.contains('active') ? 1 : 0), @@ -2038,8 +2053,9 @@ function confirmSettings() { "mixrampdelay": (settings.mixramp == true ? document.getElementById('inputMixrampdelay').value : settings.mixrampdelay), "notificationWeb": (document.getElementById('btnnotifyWeb').classList.contains('active') ? true : false), "notificationPage": (document.getElementById('btnnotifyPage').classList.contains('active') ? true : false), - "jukeboxMode": (document.getElementById('btnJukebox').classList.contains('active') ? true : false), - "jukeboxPlaylist": selectJukeboxPlaylist.options[selectJukeboxPlaylist.selectedIndex].value + "jukeboxMode": selectJukeboxMode.options[selectJukeboxMode.selectedIndex].value, + "jukeboxPlaylist": selectJukeboxPlaylist.options[selectJukeboxPlaylist.selectedIndex].value, + "jukeboxQueueLength": document.getElementById('inputJukeboxQueueLength').value }}, getSettings); modalSettings.hide(); } else diff --git a/src/mpd_client.c b/src/mpd_client.c index cbf5bc9..139c36b 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -112,23 +112,25 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); break; case MPD_API_SETTINGS_SET: - je = json_scanf(msg.p, msg.len, "{data: {notificationWeb: %B, notificationPage: %B, jukeboxMode: %B, jukeboxPlaylist: %Q}}", + je = json_scanf(msg.p, msg.len, "{data: {notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, jukeboxQueueLength: %d}}", &mympd_state.notificationWeb, &mympd_state.notificationPage, &mympd_state.jukeboxMode, - &mympd_state.jukeboxPlaylist); - if (je == 4) { + &mympd_state.jukeboxPlaylist, + &mympd_state.jukeboxQueueLength); + if (je == 5) { char tmpfile[400]; char statefile[400]; snprintf(tmpfile, 400, "%s/tmp/mympd.state", config.varlibdir ); snprintf(statefile, 400, "%s/mympd.state", config.varlibdir ); - json_fprintf(tmpfile, "{notificationWeb: %B, notificationPage: %B, jukeboxMode: %B, jukeboxPlaylist: %Q}", + json_fprintf(tmpfile, "{notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, jukeboxQueueLength: %d}", mympd_state.notificationWeb, mympd_state.notificationPage, mympd_state.jukeboxMode, - mympd_state.jukeboxPlaylist); + mympd_state.jukeboxPlaylist, + mympd_state.jukeboxQueueLength); rename(tmpfile, statefile); - if (mympd_state.jukeboxMode == true) + if (mympd_state.jukeboxMode > 0) mympd_jukebox(); } @@ -597,7 +599,7 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) { break; case MPD_IDLE_QUEUE: len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); - if (mympd_state.jukeboxMode == true) + if (mympd_state.jukeboxMode > 0) mympd_jukebox(); break; case MPD_IDLE_PLAYER: @@ -783,8 +785,8 @@ void mympd_idle(struct mg_mgr *s, int timeout) { mpd.conn_state = MPD_CONNECTED; mympd_mpd_features(); mympd_smartpls_update_all(); - if (mympd_state.jukeboxMode == true) - mympd_jukebox(); + if (mympd_state.jukeboxMode > 0) + mympd_jukebox(); mpd_send_idle(mpd.conn); break; @@ -976,9 +978,11 @@ char* mympd_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { void mympd_jukebox() { struct mpd_status *status; status = mpd_run_status(mpd.conn); - int queue_length, num_songs, rand_song, i; + int queue_length, num_songs, rand_song, i, j, addSongs; struct mpd_entity *entity; const struct mpd_song *song; + struct mpd_pair *pair; + char *album; if (!status) { printf("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn)); @@ -987,66 +991,86 @@ void mympd_jukebox() { } queue_length = mpd_status_get_queue_length(status); mpd_status_free(status); - if (queue_length > 0) + if (queue_length > mympd_state.jukeboxQueueLength) return; srand((unsigned int)time(NULL)); - - if (strcmp(mympd_state.jukeboxPlaylist, "Database") == 0) { - struct mpd_stats *stats = mpd_run_stats(mpd.conn); - num_songs = mpd_stats_get_number_of_songs(stats); - mpd_stats_free(stats); + + if (mympd_state.jukeboxMode == 2) { + //Add whole album + mpd_search_db_tags(mpd.conn, MPD_TAG_ALBUM); + mpd_search_commit(mpd.conn); + num_songs = 0; + while ((pair = mpd_recv_pair_tag(mpd.conn, MPD_TAG_ALBUM)) != NULL) { + num_songs++; + mpd_return_pair(mpd.conn, pair); + } + mpd_response_finish(mpd.conn); num_songs--; if (num_songs > 0) { rand_song = rand() % num_songs; - mpd_send_list_all(mpd.conn, "/"); + mpd_search_db_tags(mpd.conn, MPD_TAG_ALBUM); + mpd_search_commit(mpd.conn); i = 0; + while ((pair = mpd_recv_pair_tag(mpd.conn, MPD_TAG_ALBUM )) != NULL) { + if (i == rand_song) { + album = strdup(pair->value); + break; + } + i++; + mpd_return_pair(mpd.conn, pair); + } + mpd_return_pair(mpd.conn, pair); + mpd_response_finish(mpd.conn); + printf("Jukebox enabled, adding random album %s: %d/%d\n", album, rand_song, num_songs); + mpd_send_command(mpd.conn, "searchadd", "Album", album, NULL); + mpd_response_finish(mpd.conn); + } + } + else if (mympd_state.jukeboxMode == 1) { + //Add tracks + if (strcmp(mympd_state.jukeboxPlaylist, "Database") == 0) { + struct mpd_stats *stats = mpd_run_stats(mpd.conn); + num_songs = mpd_stats_get_number_of_songs(stats); + mpd_stats_free(stats); + } + else { + mpd_send_list_playlist(mpd.conn, mympd_state.jukeboxPlaylist); + num_songs = 0; while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { - if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { - if (i == rand_song) - break; - i++; + num_songs++; + mpd_entity_free(entity); + } + } + num_songs--; + addSongs = mympd_state.jukeboxQueueLength - queue_length; + if (num_songs > 0) { + for (j = 0; j < addSongs; j++) { + rand_song = rand() % num_songs; + if (strcmp(mympd_state.jukeboxPlaylist, "Database") == 0) + mpd_send_list_all(mpd.conn, "/"); + else + mpd_send_list_playlist(mpd.conn, mympd_state.jukeboxPlaylist); + i = 0; + while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { + if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { + if (i == rand_song) + break; + i++; + } + mpd_entity_free(entity); + } + mpd_response_finish(mpd.conn); + song = mpd_entity_get_song(entity); + if (song != NULL) { + printf("Jukebox enabled, adding random song: %d/%d\n", rand_song, num_songs); + mpd_run_add(mpd.conn, mpd_song_get_uri(song)); } mpd_entity_free(entity); } - mpd_response_finish(mpd.conn); - song = mpd_entity_get_song(entity); - if (song != NULL) { - printf("Jukebox enabled, playing random song from database: %d/%d\n", rand_song, num_songs); - mpd_run_add(mpd.conn, mpd_song_get_uri(song)); - mpd_run_play(mpd.conn); - } - mpd_entity_free(entity); - } - } - else { - mpd_send_list_playlist(mpd.conn, mympd_state.jukeboxPlaylist); - num_songs = 0; - while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { - num_songs++; - mpd_entity_free(entity); - } - num_songs--; - if (num_songs > 0) { - rand_song = rand() % num_songs; - mpd_send_list_playlist(mpd.conn, mympd_state.jukeboxPlaylist); - i = 0; - while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { - if (i == rand_song) - break; - i++; - mpd_entity_free(entity); - } - mpd_response_finish(mpd.conn); - song = mpd_entity_get_song(entity); - if (song != NULL) { - printf("Jukebox enabled, playing random song from %s: %d/%d\n", mympd_state.jukeboxPlaylist, rand_song, num_songs); - mpd_run_add(mpd.conn, mpd_song_get_uri(song)); - mpd_run_play(mpd.conn); - } - mpd_entity_free(entity); } } + mpd_run_play(mpd.conn); } int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length) { @@ -1138,7 +1162,7 @@ int mympd_put_settings(char *buffer) { "repeat: %d, single: %d, crossfade: %d, consume: %d, random: %d, " "mixrampdb: %f, mixrampdelay: %f, mpdhost: %Q, mpdport: %d, passwort_set: %B, " "streamport: %d, coverimage: %Q, stickers: %B, mixramp: %B, smartpls: %B, maxElementsPerPage: %d, " - "replaygain: %Q, notificationWeb: %B, notificationPage: %B, jukeboxMode: %B, jukeboxPlaylist: %Q, " + "replaygain: %Q, notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, jukeboxQueueLength: %d, " "tags: { Artist: %B, Album: %B, AlbumArtist: %B, Title: %B, Track: %B, Genre: %B, Date: %B," "Composer: %B, Performer: %B }" "}}", @@ -1163,6 +1187,7 @@ int mympd_put_settings(char *buffer) { mympd_state.notificationPage, mympd_state.jukeboxMode, mympd_state.jukeboxPlaylist, + mympd_state.jukeboxQueueLength, mpd.tag_artist, mpd.tag_album, mpd.tag_album_artist, diff --git a/src/mpd_client.h b/src/mpd_client.h index 2028baa..fcd816f 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -178,8 +178,9 @@ typedef struct { typedef struct { bool notificationWeb; bool notificationPage; - bool jukeboxMode; + int jukeboxMode; const char* jukeboxPlaylist; + int jukeboxQueueLength; } t_mympd_state; t_mympd_state mympd_state; diff --git a/src/mympd.c b/src/mympd.c index c9de0ae..efe39cc 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -244,22 +244,25 @@ int main(int argc, char **argv) { snprintf(statefile, 400, "%s/mympd.state", config.varlibdir); if (access(statefile, F_OK ) != -1 ) { char *content = json_fread(statefile); - int je = json_scanf(content, strlen(content), "{notificationWeb: %B, notificationPage: %B, jukeboxMode: %B, jukeboxPlaylist: %Q}", + int je = json_scanf(content, strlen(content), "{notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, jukeboxQueueLength: %d}", &mympd_state.notificationWeb, &mympd_state.notificationPage, &mympd_state.jukeboxMode, - &mympd_state.jukeboxPlaylist); - if (je != 4) { + &mympd_state.jukeboxPlaylist, + &mympd_state.jukeboxQueueLength); + if (je != 5) { mympd_state.notificationWeb = false; mympd_state.notificationPage = true; - mympd_state.jukeboxMode = false; + mympd_state.jukeboxMode = 0; mympd_state.jukeboxPlaylist = "Database"; + mympd_state.jukeboxQueueLength = 1; } } else { mympd_state.notificationWeb = false; mympd_state.notificationPage = true; - mympd_state.jukeboxMode = false; + mympd_state.jukeboxMode = 0; mympd_state.jukeboxPlaylist = "Database"; + mympd_state.jukeboxQueueLength = 1; } signal(SIGTERM, signal_handler);