Feat: jukebox mode - add whole album #57

Feat: jukebox mode - configurable number of tracks in queue #57
This commit is contained in:
jcorporation 2018-09-30 02:03:33 +01:00
parent 0ae38ae2ba
commit fc115a9d1b
5 changed files with 143 additions and 84 deletions

View File

@ -743,16 +743,30 @@
<hr/>
<h4>Jukebox</h4>
<div class="row">
<div class="form-group col-md-6" data-toggle="buttons">
<button data-href='{"cmd": "toggleBtn", "options": ["btnJukebox"]}' id="btnJukebox" type="button" class="btn btn-secondary btn-block" title="Jukebox">
Jukebox
</button>
<div class="form-group input-group col-md-6 border-secondary">
<div class="input-group-prepend">
<div class="input-group-text bg-secondary text-light border-secondary">Mode</div>
</div>
<select id="selectJukeboxMode" class="form-control custom-select border-secondary">
<option value="0">Off</option>
<option value="1">Track</option>
<option value="2">Album</option>
</select>
</div>
<div class="form-group input-group col-md-6 border-secondary">
<select id="jukeboxPlaylist" class="form-control custom-select border-secondary">
<select id="selectJukeboxPlaylist" class="form-control custom-select border-secondary">
</select>
</div>
</div>
<div class="row">
<div class="form-group input-group col-md-6 border-secondary">
<div class="input-group-prepend">
<div class="input-group-text bg-secondary text-light border-secondary">Min. queue length</div>
</div>
<input id="inputJukeboxQueueLength" type="text" class="form-control border-secondary" value="1">
<div class="invalid-feedback">Must be a number and &gt; 0</div>
</div>
</div>
<hr/>
<h4>Notifications</h4>
<div class="row">

View File

@ -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 = '<option></option><option>New Playlist</option>';
else if (playlistEl == 'jukeboxPlaylist')
else if (playlistEl == 'selectJukeboxPlaylist')
playlists = '<option>Database</option>';
}
for (var i = 0; i < nrItems; i++) {
playlists += '<option';
if (playlistEl == 'jukeboxPlaylist' && obj.data[i].uri == settings.jukeboxPlaylist)
if (playlistEl == 'selectJukeboxPlaylist' && obj.data[i].uri == settings.jukeboxPlaylist)
playlists += ' selected';
playlists += '>' + obj.data[i].uri + '</option>';
}
@ -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

View File

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

View File

@ -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;

View File

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