1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-06-25 22:23:16 +00:00

First database browse code

This commit is contained in:
jcorporation 2018-06-03 17:36:06 +01:00
parent e37c678a89
commit 7962a8ff54
5 changed files with 305 additions and 35 deletions

View File

@ -182,4 +182,5 @@ main {
#browseFilesystemFilterLetters > button {
min-width:28px;
}
}

View File

@ -209,7 +209,7 @@
<a class="nav-link text-dark" href="#/browse/playlists/0" id="cardBrowseNavPlaylists">Playlists</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" href="#/browse/database/0" id="cardBrowseNavDatabase">Database</a>
<a class="nav-link text-dark" href="#/browse/database/0/" id="cardBrowseNavDatabase">Database</a>
</li>
</ul>
</div>
@ -264,9 +264,65 @@
</div>
<div class="card-body hide" id="cardBrowseDatabase">
<div class="alert alert-warning" role="alert">
Sorry, not implemented yet. Please stay tuned.
<div class="btn-toolbar collapse show" id="browseDatabaseButtons" role="toolbar">
<!--
<div class="btn-group mr-2 pull-right">
<button id="browseDatabaseAddAllSongs" class="btn btn-secondary">Add all</button>
</div>
<div class="btn-group mr-2">
<button id="browseDatabaseFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button>
<div class="dropdown-menu bg-dark px-2" id="browseDatabaseFilterLetters">
</div>
</div>
-->
<div id="browseDatabasePaginationTop" class="btn-group mr-2">
<button onclick="gotoPage('prev',this,event)" id="browseDatabasePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="browseDatabasePaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button>
<div class="dropdown-menu bg-dark px-2" id="browseDatabasePaginationTopPages">
</div>
</div>
<button onclick="gotoPage('next',this,event)" id="browseDatabasePaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
</div>
<div class="table-responsive-md">
<table id="browseDatabaseList" class="table table-hover table-sm">
<col class="tblnum"/>
<col class="tbltitle"/>
<col class="tblaction"/>
<thead>
<tr>
<th></th>
<th>Artist</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div id="browseDatabaseCards" class="card-columns"></div>
<div class="btn-toolbar" id="browseDatabaseButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" onclick="scrollToTop()" title="To top">
<span class="material-icons">keyboard_arrow_up</span>
</button>
</div>
<div id="browseDatabasePaginationBottom" class="btn-group mr-2 dropup">
<button onclick="gotoPage('prev',this,event)" id="browseDatabasePaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="browseDatabasePaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button>
<div class="dropdown-menu bg-dark px-2" id="browseDatabasePaginationBottomPages">
</div>
</div>
<button onclick="gotoPage('next',this,event)" id="browseDatabasePaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
</div>
</div>
<div class="card-body hide" id="cardBrowseFilesystem">

View File

@ -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(
'<tr uri="' + encodeURI(obj.data[item].plist) + '">' +
'<td><span class="material-icons">list</span></td>' +
'<td><a>' + basename(obj.data[item].plist) + '</a></td>' +
'<td>'+d.toUTCString()+'</td><td></td></tr>'
);
var row='<tr uri="' + encodeURI(obj.data[item].plist) + '">' +
'<td><span class="material-icons">list</span></td>' +
'<td><a>' + basename(obj.data[item].plist) + '</a></td>' +
'<td>'+d.toUTCString()+'</td><td></td></tr>';
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='<tr uri="' + encodeURI(obj.data[item].value) + '">' +
'<td><span class="material-icons">album</span></td>' +
'<td><a>' + obj.data[item].value + '</a></td></tr>';
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(
'<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="3">No entries found.</td>' +
'<td></td><td></td></tr>'
);
}
} else if (obj.tagtype == 'Album') {
$('#browseDatabaseList').addClass('hide');
$('#browseDatabaseCards').empty();
$('#browseDatabaseCards').removeClass('hide');
var nrItems=0;
for (var item in obj.data) {
var card='<div class="card" style="width: 18rem;" uri="'+encodeURI(obj.data[item].value)+'">'+
' <img class="card-img-top" src="" alt="Coverimage">'+
' <div class="card-body">'+
' <h5 class="card-title">'+obj.searchstr+'</h5>'+
' <h4 class="card-title">'+obj.data[item].value+'</h4>'+
' <ul class="card-text"></ul>'+
' </div>'+
'</div>';
$('#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+='<li>'+obj.data[item].title+'</li>';
}
album.append(titleList);
break;
case 'search':
$('#panel-heading-search').text(obj.totalEntities + ' Songs found');
if (obj.totalEntities > 0) {

View File

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

View File

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