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

Feat: option to create playlist in "add to playlist"-dialog

Feat: "add all to playlist" in browse filesystem card
Feat: hide pagination if not needed
This commit is contained in:
jcorporation 2018-07-31 21:46:53 +01:00
parent 2bb8cc3021
commit d6a24d4531
4 changed files with 180 additions and 41 deletions

View File

@ -126,7 +126,7 @@
</div>
</div>
</form>
<div id="QueuePaginationTop" class="btn-group mr-2">
<div id="QueuePaginationTop" class="btn-group mr-2 hide">
<button data-href="{'cmd': 'gotoPage', 'options': ['prev']}" id="QueuePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="QueuePaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button>
@ -159,7 +159,7 @@
</tbody>
</table>
</div>
<div class="btn-toolbar" id="QueueButtonsBottom" role="toolbar">
<div class="btn-toolbar hide" id="QueueButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd':'scrollTo','options':[0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span>
@ -208,7 +208,7 @@
<div class="dropdown-menu bg-dark px-2 letters" id="BrowsePlaylistsFilterLetters">
</div>
</div>
<div id="BrowsePlaylistsPaginationTop" class="btn-group mr-2">
<div id="BrowsePlaylistsPaginationTop" class="btn-group mr-2 hide">
<button data-href="{'cmd': 'gotoPage', 'options': ['prev']}" id="BrowsePlaylistsPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="BrowsePlaylistsPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button>
@ -257,7 +257,7 @@
</tbody>
</table>
</div>
<div class="btn-toolbar" id="BrowsePlaylistsButtonsBottom" role="toolbar">
<div class="btn-toolbar hide" id="BrowsePlaylistsButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span>
@ -285,7 +285,7 @@
<div class="dropdown-menu bg-dark px-2 letters" id="BrowseDatabaseFilterLetters">
</div>
</div>
<div id="BrowseDatabasePaginationTop" class="btn-group mr-2">
<div id="BrowseDatabasePaginationTop" class="btn-group mr-2 hide">
<button data-href="{'cmd': 'gotoPage', 'options': ['prev']}" 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">1 / 1</button>
@ -315,7 +315,7 @@
<div id="BrowseDatabaseAlbumCards" class="row hide"></div>
<div class="btn-toolbar" id="BrowseDatabaseButtonsBottom" role="toolbar">
<div class="btn-toolbar hide" id="BrowseDatabaseButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span>
@ -336,15 +336,22 @@
<div class="card-body hide" id="cardBrowseFilesystem">
<div class="btn-toolbar card-toolbar" id="BrowseFilesystemButtons" role="toolbar">
<div class="btn-group mr-2 pull-right">
<button data-href="{'cmd': 'addAllFromBrowse', 'options': []}" id="BrowseFilesystemAddAllSongs" class="btn btn-secondary">Add all</button>
</div>
<div class="btn-group mr-2">
<button id="BrowseFilesystemFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button>
<div class="dropdown-menu bg-dark px-2 letters" id="BrowseFilesystemFilterLetters">
</div>
</div>
<div class="input-group mr-2">
<div class="input-group-prepend">
<button data-href="{'cmd': 'addAllFromBrowse', 'options': []}" id="BrowseFilesystemAddAllSongs" class="btn btn-secondary">Add all</button>
<button id="BrowseFilesystemAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right" type="button" data-toggle="dropdown"></button>
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="BrowseFilesystemAddAllSongsDropdown">
<button type="button" class="btn btn-secondary btn-block">Add all to queue</button>
<button type="button" class="btn btn-secondary btn-block">Add all to playlist</button>
</div>
</div>
</div>
<div id="BrowseFilesystemPaginationTop" class="btn-group mr-2">
<div id="BrowseFilesystemPaginationTop" class="btn-group mr-2 hide">
<button data-href="{'cmd': 'gotoPage', 'options': ['prev']}" id="BrowseFilesystemPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="BrowseFilesystemPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button>
@ -380,7 +387,7 @@
</tbody>
</table>
</div>
<div class="btn-toolbar" id="BrowseFilesystemButtonsBottom" role="toolbar">
<div class="btn-toolbar hide" id="BrowseFilesystemButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span>
@ -423,10 +430,17 @@
</div>
</div>
</form>
<div class="btn-group mr-2 pull-right">
<button id="searchAddAllSongs" class="btn btn-secondary" data-href="{'cmd': 'addAllFromSearch', 'options': []}">Add all</button>
</div>
<div id="AearchPaginationTop" class="btn-group mr-2">
<div class="input-group mr-2">
<div class="input-group-prepend">
<button id="searchAddAllSongs" class="btn btn-secondary rounded-right" data-href="{'cmd': 'addAllFromSearch', 'options': []}">Add all</button>
<button id="searchAddAllSongsBtn" class="btn btn-secondary dropdown-toggle dropdown-toggle-split rounded-right hide" type="button" data-toggle="dropdown"></button>
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchAddAllSongsDropdown">
<button type="button" class="btn btn-secondary btn-block">Add all to queue</button>
<button type="button" class="btn btn-secondary btn-block">Add all to playlist</button>
</div>
</div>
</div>
<div id="SearchPaginationTop" class="btn-group mr-2 hide">
<button data-href="{'cmd': 'gotoPage', 'options': ['prev']}" id="SearchPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="SearchPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button>
@ -458,7 +472,7 @@
</tbody>
</table>
</div>
<div class="btn-toolbar" id="SearchButtonsBottom" role="toolbar">
<div class="btn-toolbar hide" id="SearchButtonsBottom" role="toolbar">
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary" data-href="{'cmd': 'scrollTo', 'options': [0]}" title="To top">
<span class="material-icons">keyboard_arrow_up</span>
@ -510,14 +524,17 @@
<h5 class="modal-title"><span class="material-icons title-icon">playlist_add</span> Add to playlist</h5>
</div>
<div class="modal-body">
<form id="addToPlaylistFrm">
<form class="needs-validation" id="addToPlaylistFrm" novalidate>
<input type="hidden" id="addToPlaylistUri"/>
<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">Playlist</div>
</div>
<select id="addToPlaylistPlaylist" class="form-control custom-select border-secondary">
</select>
<div class="form-group">
<label for="addToPlaylistPlaylist">Playlist</label>
<select id="addToPlaylistPlaylist" class="form-control custom-select"></select>
<div class="invalid-feedback">Please choose playlist.</div>
</div>
<div class="form-group hide" id="addToPlaylistNewPlaylistDiv">
<label for="addToPlaylistNewPlaylist">Create Playlist</label>
<input id="addToPlaylistNewPlaylist" class="form-control"/>
<div class="invalid-feedback">Invalid filename.</div>
</div>
</form>
</div>
@ -543,7 +560,7 @@
</div>
<div class="form-group">
<label for="renamePlaylistTo">To</label>
<input type="text" class="form-control" id="renamePlaylistTo">
<input type="text" class="form-control" id="renamePlaylistTo"/>
<div class="invalid-feedback">Invalid filename.</div>
</div>
</form>

View File

@ -213,10 +213,14 @@ function appRoute() {
else if (app.current.app == 'Browse' && app.current.tab == 'Filesystem') {
sendAPI({"cmd":"MPD_API_GET_FILESYSTEM","data": {"offset": app.current.page, "path": (app.current.search ? app.current.search : "/"), "filter": app.current.filter}}, parseFilesystem);
// Don't add all songs from root
if (app.current.search)
if (app.current.search) {
document.getElementById('BrowseFilesystemAddAllSongs').removeAttribute('disabled');
else
document.getElementById('BrowseFilesystemAddAllSongs').setAttribute('disabled', 'disabled')
document.getElementById('BrowseFilesystemAddAllSongsBtn').removeAttribute('disabled');
}
else {
document.getElementById('BrowseFilesystemAddAllSongs').setAttribute('disabled', 'disabled');
document.getElementById('BrowseFilesystemAddAllSongsBtn').setAttribute('disabled', 'disabled');
}
// Create breadcrumb
var breadcrumbs='<li class="breadcrumb-item"><a data-uri="">root</a></li>';
var pathArray = app.current.search.split('/');
@ -256,6 +260,7 @@ function appRoute() {
} else {
document.getElementById('SearchList').getElementsByTagName('tbody')[0].innerHTML = '';
document.getElementById('searchAddAllSongs').setAttribute('disabled', 'disabled');
document.getElementById('searchAddAllSongsBtn').setAttribute('disabled', 'disabled');
document.getElementById('panel-heading-search').innerText = '';
document.getElementById('SearchList').classList.remove('opacity05');
setPagination(0);
@ -288,14 +293,14 @@ function appInit() {
domCache.volumeBar.value = 0;
domCache.volumeBar.addEventListener('change', function(event) {
sendAPI({"cmd": "MPD_API_SET_VOLUME","data": {"volume": domCache.volumeBar.value}});
sendAPI({"cmd": "MPD_API_SET_VOLUME", "data": {"volume": domCache.volumeBar.value}});
}, false);
domCache.progressBar.value = 0;
domCache.progressBar.addEventListener('change', function(event) {
if (current_song && current_song.currentSongId >= 0) {
var seekVal = Math.ceil(current_song.totalTime * (domCache.progressBar.value / 100));
sendAPI({"cmd": "MPD_API_SET_SEEK", "data": {"songid":current_song.currentSongId,"seek": seekVal}});
sendAPI({"cmd": "MPD_API_SET_SEEK", "data": {"songid": current_song.currentSongId, "seek": seekVal}});
}
}, false);
@ -326,6 +331,17 @@ function appInit() {
streamUrl.classList.remove('is-invalid');
document.getElementById('addStreamFrm').classList.remove('was-validated');
});
document.getElementById('addToPlaylistPlaylist').addEventListener('change',function(event) {
if (this.options[this.selectedIndex].text == 'New Playlist') {
document.getElementById('addToPlaylistNewPlaylistDiv').classList.remove('hide');
document.getElementById('addToPlaylistNewPlaylist').focus();
}
else {
document.getElementById('addToPlaylistNewPlaylistDiv').classList.add('hide');
}
}, false);
addFilterLetter('BrowseFilesystemFilterLetters');
addFilterLetter('BrowseDatabaseFilterLetters');
@ -432,6 +448,28 @@ function appInit() {
}
}, false);
document.getElementById('BrowseFilesystemAddAllSongsDropdown').addEventListener('click', function(event) {
if (event.target.nodeName == 'BUTTON') {
if (event.target.innerText == 'Add all to queue') {
addAllFromBrowse();
}
else if (event.target.innerText == 'Add all to playlist') {
showAddToPlaylist(app.current.search);
}
}
}, false);
document.getElementById('searchAddAllSongsDropdown').addEventListener('click', function(event) {
if (event.target.nodeName == 'BUTTON') {
if (event.target.innerText == 'Add all to queue') {
addAllFromSearch();
}
else if (event.target.innerText == 'Add all to playlist') {
showAddToPlaylist('SEARCH');
}
}
}, false);
document.getElementById('searchtags').addEventListener('click', function(event) {
if (event.target.nodeName == 'BUTTON')
appGoto(app.current.app, app.current.tab, app.current.view, '0/' + event.target.innerText + '/' + app.current.search);
@ -947,10 +985,14 @@ function parseSearch(obj) {
if (app.current.app !== 'Search')
return;
document.getElementById('panel-heading-search').innerHTML = obj.totalEntities + ' Songs found';
if (obj.totalEntities > 0)
if (obj.totalEntities > 0) {
document.getElementById('searchAddAllSongs').removeAttribute('disabled');
else
document.getElementById('searchAddAllSongs').setAttribute('disabled','disabled');
document.getElementById('searchAddAllSongsBtn').removeAttribute('disabled');
}
else {
document.getElementById('searchAddAllSongs').setAttribute('disabled','disabled');
document.getElementById('searchAddAllSongsBtn').setAttribute('disabled','disabled');
}
parseFilesystem(obj);
}
@ -1245,14 +1287,17 @@ function setPagination(number) {
if (number > app.current.page + settings.max_elements_per_page) {
document.getElementById(cat + p[i] + 'Next').removeAttribute('disabled');
document.getElementById(cat + p[i]).classList.remove('hide');
document.getElementById(cat + 'ButtonsBottom').classList.remove('hide');
} else {
document.getElementById(cat + p[i] + 'Next').setAttribute('disabled', 'disabled');
document.getElementById(cat + p[i]).classList.add('hide');
document.getElementById(cat + 'ButtonsBottom').classList.add('hide');
}
if (app.current.page > 0) {
document.getElementById(cat + p[i] + 'Prev').removeAttribute('disabled');
document.getElementById(cat + p[i]).classList.remove('hide');
document.getElementById(cat + 'ButtonsBottom').classList.remove('hide');
} else {
document.getElementById(cat + p[i] + 'Prev').setAttribute('disabled', 'disabled');
@ -1348,7 +1393,7 @@ function playlistClear() {
function getAllPlaylists(obj) {
var nrItems = obj.data.length;
var playlists = '';
var playlists = '<option></option><option>New Playlist</option>';
for (var i = 0; i < nrItems; i++) {
playlists += '<option>' + obj.data[i].uri + '</option>';
}
@ -1359,10 +1404,14 @@ function getAllPlaylists(obj) {
}
}
function showAddToPlaylist(uri) {
modalAddToPlaylist.show();
document.getElementById('addToPlaylistUri').value = uri;
document.getElementById('addToPlaylistPlaylist').innerHTML = '';
document.getElementById('addToPlaylistNewPlaylistDiv').classList.add('hide');
document.getElementById('addToPlaylistFrm').classList.remove('was-validated');
document.getElementById('addToPlaylistNewPlaylist').classList.remove('is-invalid');
sendAPI({"cmd":"MPD_API_GET_PLAYLISTS","data": {"offset": 0, "filter": "-"}}, getAllPlaylists);
}
@ -1370,8 +1419,28 @@ function addToPlaylist() {
var uri = document.getElementById('addToPlaylistUri').value;
var plistEl = document.getElementById('addToPlaylistPlaylist');
var plist = plistEl.options[plistEl.selectedIndex].text;
sendAPI({"cmd": "MPD_API_ADD_TO_PLAYLIST", "data": {"uri": uri, "plist": plist}});
modalAddToPlaylist.hide();
if (plist == 'New Playlist') {
var newPl = document.getElementById('addToPlaylistNewPlaylist').value;
var valid = newPl.replace(/\w/g,'');
if (newPl != '' && valid == '') {
plist = newPl;
} else {
document.getElementById('addToPlaylistNewPlaylist').classList.add('is-invalid');
document.getElementById('addToPlaylistFrm').classList.add('was-validated');
return;
}
}
if (plist != '') {
if (uri != 'SEARCH')
sendAPI({"cmd": "MPD_API_ADD_TO_PLAYLIST", "data": {"uri": uri, "plist": plist}});
else
addAllFromSearchPlist(plist);
modalAddToPlaylist.hide();
}
else {
document.getElementById('addToPlaylistPlaylist').classList.add('is-invalid');
document.getElementById('addToPlaylistFrm').classList.add('was-validated');
}
}
function showRenamePlaylist(from) {
@ -1444,7 +1513,8 @@ function showMenu(el) {
( document.getElementById('BrowsePlaylistsDetailList').getAttribute('data-ro') == 'false' ?
'<div class="dropdown-divider"></div>' +
'<a class="dropdown-item" href="#" data-href="{\'cmd\': \'removeFromPlaylist\', \'options\': [\'' + document.getElementById('BrowsePlaylistsDetailList').getAttribute('data-uri') + '\', \'' +
el.parentNode.parentNode.getAttribute('data-songpos') + '\']}">Remove</a>' : '');
el.parentNode.parentNode.getAttribute('data-songpos') + '\']}">Remove</a>' : '') +
( type != 'plist' ? '<div class="dropdown-divider"></div><a class="dropdown-item" href="#" data-href="{\'cmd\': \'showAddToPlaylist\', \'options\': [\'' + uri + '\']}">Add to playlist</a>' : '');
}
else if (app.current.app == 'Queue') {
menu += '<a class="dropdown-item" href="#" data-href="{\'cmd\': \'delQueueSong\', \'options\': [\'single\',' +
@ -1544,7 +1614,7 @@ function delQueueSong(mode, start, end) {
function delPlaylist(uri) {
sendAPI({"cmd": "MPD_API_RM_PLAYLIST", "data": {"uri": uri}});
document.getElementById('BrowsePlaylistsAllList').querySelector('tr[data-uri=' + encodeURI(uri) + ']').remove();
sendAPI({"cmd":"MPD_API_GET_PLAYLISTS","data": {"offset": app.current.page, "filter": app.current.filter}}, parsePlaylists);
}
function confirmSettings() {
@ -1612,6 +1682,13 @@ function addAllFromSearch() {
}
}
function addAllFromSearchPlist(plist) {
if (app.current.search.length >= 2) {
sendAPI({"cmd":"MPD_API_SEARCH_ADD_PLIST", "data":{ "plist": plist, "filter": app.current.filter, "searchstr": app.current.search}});
showNotification('Added '+ parseInt(document.getElementById('panel-heading-search').innerText) +' songs from search to ' + plist, '', '', 'success');
}
}
function scrollTo(pos) {
document.body.scrollTop = pos; // For Safari
document.documentElement.scrollTop = pos; // For Chrome, Firefox, IE and Opera

View File

@ -59,7 +59,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
unsigned int uint_buf1, uint_buf2, uint_rc;
int je, int_buf, int_rc;
float float_buf;
char *p_charbuf1, *p_charbuf2;
char *p_charbuf1, *p_charbuf2, *p_charbuf3;
struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 };
enum mpd_cmd_ids cmd_id;
@ -403,8 +403,21 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
n = mympd_search_add(mpd.buf, p_charbuf1, p_charbuf2);
free(p_charbuf1);
free(p_charbuf2);
if (n == 0)
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
}
break;
case MPD_API_SEARCH_ADD_PLIST:
je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q, filter:%Q, searchstr:%Q } }", &p_charbuf1, &p_charbuf2, &p_charbuf3);
if (je == 3) {
n = mympd_search_add_plist(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3);
free(p_charbuf1);
free(p_charbuf2);
free(p_charbuf3);
if (n == 0)
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
}
break;
case MPD_API_SEARCH:
je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, mpdtag:%Q, searchstr:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2);
if (je == 3) {
@ -1264,11 +1277,10 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear
return len;
}
int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) {
int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr) {
int len = 0;
struct mpd_song *song;
int len;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
len = 0;
if (mpd_search_add_db_songs(mpd.conn, false) == false) {
RETURN_ERROR_AND_RECOVER("mpd_search_add_db_songs");
@ -1295,6 +1307,37 @@ int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) {
return len;
}
int mympd_search_add_plist(char *buffer, char *plist, char *mpdtagtype, char *searchstr) {
int len = 0;
struct mpd_song *song;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
if (mpd_search_db_songs(mpd.conn, false) == false) {
RETURN_ERROR_AND_RECOVER("mpd_search_add_db_songs");
}
if (mpd_tag_name_parse(mpdtagtype) != MPD_TAG_UNKNOWN) {
if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdtagtype), searchstr) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint");
}
else {
if (mpd_search_add_any_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, searchstr) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint");
}
if (mpd_search_commit(mpd.conn) == false)
RETURN_ERROR_AND_RECOVER("mpd_search_commit");
while((song = mpd_recv_song(mpd.conn)) != NULL) {
// mpd_send_playlist_add(mpd.conn, plist, mpd_song_get_uri(song));
mpd_song_free(song);
}
if (len > MAX_SIZE)
fprintf(stderr,"Buffer truncated\n");
return len;
}
int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) {
struct mpd_song *song;
unsigned long entity_count = 0;

View File

@ -64,6 +64,7 @@
X(MPD_API_RM_ALL) \
X(MPD_API_MOVE_TRACK) \
X(MPD_API_SEARCH_QUEUE) \
X(MPD_API_SEARCH_ADD_PLIST) \
X(MPD_API_SEARCH_ADD) \
X(MPD_API_SEARCH) \
X(MPD_API_SEND_MESSAGE) \
@ -145,6 +146,7 @@ int mympd_put_queue(char *buffer, unsigned int offset);
int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter);
int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr);
int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr);
int mympd_search_add_plist(char *buffer, char *plist, char *mpdtagtype, char *searchstr);
int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr);
int mympd_put_welcome(char *buffer);
int mympd_get_stats(char *buffer);