diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index 586872b..df14adc 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -182,4 +182,5 @@ main { #browseFilesystemFilterLetters > button { min-width:28px; -} \ No newline at end of file +} + diff --git a/htdocs/index.html b/htdocs/index.html index 9c59068..fa0e58c 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -209,7 +209,7 @@ Playlists @@ -264,9 +264,65 @@
-
diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 7ba9443..685d2f8 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -101,19 +101,23 @@ var app = $.sammy(function() { $('#cardBrowse').removeClass('hide'); $('#cardBrowsePlaylists').removeClass('hide'); $('#cardBrowseNavPlaylists').addClass('active'); - $('#browsePlaylistsList').find("tr:gt(0)").remove(); socket.send('MPD_API_GET_PLAYLISTS,'+pagination); }); - this.get(/\#\/browse\/database\/(\d+)/, function() { + this.get(/\#\/browse\/database\/(\d+)\/(.*)/, function() { prepare(); - browsepath = this.params['splat'][1]; pagination = parseInt(this.params['splat'][0]); + var artist = this.params['splat'][1]; current_app = 'browseDatabase'; $('#navBrowse').addClass('active'); $('#cardBrowse').removeClass('hide'); $('#cardBrowseDatabase').removeClass('hide'); $('#cardBrowseNavDatabase').addClass('active'); + if (artist == "") { + socket.send('MPD_API_GET_ARTISTS,'+pagination); + } else { + socket.send('MPD_API_GET_ARTISTALBUMS,'+pagination+',' + decodeURI(artist)); + } }); this.get(/\#\/browse\/filesystem\/(\d+)\/(\w|\!)\/(.*)/, function() { @@ -382,15 +386,19 @@ function webSocketConnect() { if(current_app !== 'browsePlaylists') break; var nrItems=0; + var tr=document.getElementById(current_app+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); for (var item in obj.data) { nrItems++; var d = new Date(obj.data[item].last_modified * 1000); - $('#'+current_app+'List > tbody').append( - '' + - 'list' + - '' + basename(obj.data[item].plist) + '' + - ''+d.toUTCString()+'' - ); + var row='' + + 'list' + + '' + basename(obj.data[item].plist) + '' + + ''+d.toUTCString()+''; + if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + else { $('#'+current_app+'List > tbody').append(row); } + } + for (var i=tr.length;i>nrItems;i--) { + $(tr[tr.length-1]).remove(); } setPagination(obj.totalEntities); if ( isTouch ) { @@ -427,6 +435,70 @@ function webSocketConnect() { ); } break; + + case 'listDBtags': + if(current_app !== 'browseDatabase') + break; + if (obj.tagtype == 'AlbumArtist') { + $('#browseDatabaseCards').addClass('hide'); + $('#browseDatabaseList').removeClass('hide'); + var nrItems=0; + var tr=document.getElementById(current_app+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); + for (var item in obj.data) { + nrItems++; + var row='' + + 'album' + + '' + obj.data[item].value + ''; + if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } + else { $('#'+current_app+'List > tbody').append(row); } + + } + for (var i=tr.length;i>nrItems;i--) { + $(tr[tr.length-1]).remove(); + } + setPagination(obj.totalEntities); + $('#'+current_app+'List > tbody > tr').on({ + click: function() { + pagination = 0; + app.setLocation('#/browse/database/'+pagination+'/'+$(this).attr('uri')); + } + }); + if (nrItems == 0) { + $('#'+current_app+'List > tbody').append( + 'error_outline' + + 'No entries found.' + + '' + ); + } + } else if (obj.tagtype == 'Album') { + $('#browseDatabaseList').addClass('hide'); + $('#browseDatabaseCards').empty(); + $('#browseDatabaseCards').removeClass('hide'); + var nrItems=0; + for (var item in obj.data) { + var card='
'+ + ' Coverimage'+ + '
'+ + '
'+obj.searchstr+'
'+ + '

'+obj.data[item].value+'

'+ + '
    '+ + '
    '+ + '
    '; + $('#browseDatabaseCards').append(card); + socket.send('MPD_API_GET_ARTISTALBUMTITLES,' + obj.searchstr + ','+obj.data[item].value); + } + setPagination(obj.totalEntities); + } + break; + case 'listTitles': + var album=$('div[uri="'+encodeURI(obj.album)+'"] > div > ul'); + $('div[uri="'+encodeURI(obj.album)+'"] > img').attr('src','/library/'+obj.data[0].uri.replace(/\/[^\/]+$/,'\/'+coverImageFile)); + var titleList=''; + for (var item in obj.data) { + titleList+='
  • '+obj.data[item].title+'
  • '; + } + album.append(titleList); + break; case 'search': $('#panel-heading-search').text(obj.totalEntities + ' Songs found'); if (obj.totalEntities > 0) { diff --git a/src/mpd_client.c b/src/mpd_client.c index 75089fb..367470f 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -70,7 +70,7 @@ int callback_mpd(struct mg_connection *c) char *p_charbuf2 = NULL; char *searchstr = NULL; - fprintf(stdout,"%s\n",c->content); + fprintf(stdout,"Got request: %s:%d\n",c->content,cmd_id); if(cmd_id == -1) return MG_TRUE; @@ -171,24 +171,51 @@ int callback_mpd(struct mg_connection *c) if(sscanf(c->content, "MPD_API_GET_QUEUE,%u", &uint_buf)) n = mpd_put_queue(mpd.buf, uint_buf); break; + case MPD_API_GET_ARTISTS: + if(sscanf(c->content, "MPD_API_GET_ARTISTS,%u", &uint_buf)) + n = mympd_put_db_tag(mpd.buf, uint_buf, "AlbumArtist","",""); + break; + case MPD_API_GET_ARTISTALBUMS: + p_charbuf = strdup(c->content); + if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMS")) + goto out_artistalbum; + uint_buf = strtoul(strtok(NULL, ","), NULL, 10); + + if((token = strtok(NULL, ",")) == NULL) { + goto out_artistalbum; + } else { + searchstr = strdup(token); + } + n = mympd_put_db_tag(mpd.buf, uint_buf, "Album", "AlbumArtist", searchstr); + free(searchstr); +out_artistalbum: + free(p_charbuf); + break; + case MPD_API_GET_ARTISTALBUMTITLES: + p_charbuf = strdup(c->content); + if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMTITLES")) + goto out_artistalbumtitle; + + if((token = strtok(NULL, ",")) == NULL) { + goto out_artistalbumtitle; + } else { + searchstr = strdup(token); + } + if((token = strtok(NULL, ",")) == NULL) { + goto out_artistalbumtitle; + } else { + p_charbuf2 = strdup(token); + } + n = mympd_put_songs_in_album(mpd.buf, searchstr, p_charbuf2); + free(searchstr); +out_artistalbumtitle: + free(p_charbuf); + break; case MPD_API_GET_PLAYLISTS: if(sscanf(c->content, "MPD_API_GET_PLAYLISTS,%u", &uint_buf)) n = mpd_put_playlists(mpd.buf, uint_buf); break; case MPD_API_GET_BROWSE: -/* p_charbuf = strdup(c->content); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_BROWSE")) - goto out_browse; - - uint_buf = strtoul(strtok(NULL, ","), NULL, 10); - if((token = strtok(NULL, ",")) == NULL) - goto out_browse; - - free(p_charbuf); - p_charbuf = strdup(c->content); - n = mpd_put_browse(mpd.buf, get_arg2(p_charbuf), uint_buf); -out_browse: - free(p_charbuf);*/ p_charbuf = strdup(c->content); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_BROWSE")) goto out_browse; @@ -417,8 +444,10 @@ out_set_replaygain: mpd.conn_state = MPD_FAILURE; } - if(n > 0) + if(n > 0) { + fprintf(stdout,"Send response:\n %s\n",mpd.buf); mg_websocket_write(c, 1, mpd.buf, n); + } return MG_TRUE; } @@ -933,6 +962,116 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) return cur - buffer; } +int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr) +{ + char *cur = buffer; + const char *end = buffer + MAX_SIZE; + struct mpd_pair *pair; + unsigned long entity_count = 0; + unsigned long entities_returned = 0; + + if(mpd_search_db_tags(mpd.conn, mpd_tag_name_parse(mpdtagtype)) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_db_tags"); + + if (mpd_tag_name_parse(mpdsearchtagtype) != MPD_TAG_UNKNOWN) { + if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdsearchtagtype), searchstr) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); + } + + if(mpd_search_commit(mpd.conn) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_commit"); + else { + cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"listDBtags\",\"data\":[ "); + while((pair = mpd_recv_pair_tag(mpd.conn, mpd_tag_name_parse(mpdtagtype))) != NULL) { + entity_count ++; + if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + entities_returned ++; + cur += json_emit_raw_str(cur, end - cur, "{\"type\":"); + cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); + cur += json_emit_raw_str(cur, end - cur, ",\"value\":"); + cur += json_emit_quoted_str(cur, end - cur, pair->value); + cur += json_emit_raw_str(cur, end - cur, "},"); + } + mpd_return_pair(mpd.conn, pair); + } + + /* remove last ',' */ + cur--; + + cur += json_emit_raw_str(cur, end - cur, "]"); + cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); + cur += json_emit_int(cur, end - cur, offset); + cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); + cur += json_emit_int(cur, end - cur, entities_returned); + cur += json_emit_raw_str(cur, end - cur, ",\"tagtype\":"); + cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); + cur += json_emit_raw_str(cur, end - cur, ",\"searchtagtype\":"); + cur += json_emit_quoted_str(cur, end - cur, mpdsearchtagtype); + cur += json_emit_raw_str(cur, end - cur, ",\"searchstr\":"); + cur += json_emit_quoted_str(cur, end - cur, searchstr); + cur += json_emit_raw_str(cur, end - cur, "}"); + } + return cur - buffer; +} + +int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) +{ + char *cur = buffer; + const char *end = buffer + MAX_SIZE; + struct mpd_song *song; + unsigned long entity_count = 0; + unsigned long entities_returned = 0; + + if(mpd_search_db_songs(mpd.conn, false) == false) { + RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); + } + + if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, albumartist) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); + + if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); + + if(mpd_search_commit(mpd.conn) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_commit"); + else { + cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"listTitles\",\"data\":[ "); + + while((song = mpd_recv_song(mpd.conn)) != NULL) { + entity_count ++; + if(entity_count <= MAX_ELEMENTS_PER_PAGE) { + entities_returned ++; + cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); + cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); + cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); + cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); + cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); + cur += json_emit_raw_str(cur, end - cur, "},"); + } + mpd_song_free(song); + } + + /* remove last ',' */ + cur--; + + cur += json_emit_raw_str(cur, end - cur, "]"); + cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); + cur += json_emit_int(cur, end - cur, entities_returned); + cur += json_emit_raw_str(cur, end - cur, ",\"albumartist\":"); + cur += json_emit_quoted_str(cur, end - cur, albumartist); + cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); + cur += json_emit_quoted_str(cur, end - cur, album); + cur += json_emit_raw_str(cur, end - cur, "}"); + } + return cur - buffer; +} + int mpd_put_playlists(char *buffer, unsigned int offset) { char *cur = buffer; @@ -959,12 +1098,9 @@ int mpd_put_playlists(char *buffer, unsigned int offset) mpd_playlist_free(pl); } - if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) { - fprintf(stderr, "MPD mpd_send_list_playlists: %s\n", mpd_connection_get_error_message(mpd.conn)); - mpd.conn_state = MPD_FAILURE; - return 0; - } - + if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) + RETURN_ERROR_AND_RECOVER("mpd_send_list_playlists"); + /* remove last ',' */ cur--; diff --git a/src/mpd_client.h b/src/mpd_client.h index 4a13d86..9b8330e 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -80,7 +80,10 @@ X(MPD_API_SET_MIXRAMPDELAY) \ X(MPD_API_GET_PLAYLISTS) \ X(MPD_API_RM_PLAYLIST) \ - X(MPD_API_SET_REPLAYGAIN) + X(MPD_API_SET_REPLAYGAIN) \ + X(MPD_API_GET_ARTISTALBUMS) \ + X(MPD_API_GET_ARTISTALBUMTITLES) \ + X(MPD_API_GET_ARTISTS) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) @@ -136,6 +139,8 @@ int mpd_search_add(char *buffer, char *mpdtagtype, char *searchstr); int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); int mympd_get_stats(char *buffer); int mympd_put_settings(char *buffer); +int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr); +int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album); void mpd_disconnect(); #endif