Merge pull request #149 from chme/mpdcommands

Add support for additional mpd commands
This commit is contained in:
ejurgensen 2015-05-21 22:30:49 +02:00
commit d6828b90fc
5 changed files with 217 additions and 42 deletions

View File

@ -1535,6 +1535,14 @@ db_query_start(struct query_params *qp)
ret = db_build_query_browse(qp, "year", "year", &query); ret = db_build_query_browse(qp, "year", "year", &query);
break; break;
case Q_BROWSE_DISCS:
ret = db_build_query_browse(qp, "disc", "disc", &query);
break;
case Q_BROWSE_TRACKS:
ret = db_build_query_browse(qp, "track", "track", &query);
break;
case Q_COUNT_ITEMS: case Q_COUNT_ITEMS:
ret = db_build_query_count_items(qp, &query); ret = db_build_query_count_items(qp, &query);
break; break;

View File

@ -41,6 +41,8 @@ enum query_type {
Q_GROUP_DIRS = Q_F_BROWSE | (1 << 10), Q_GROUP_DIRS = Q_F_BROWSE | (1 << 10),
Q_BROWSE_YEARS = Q_F_BROWSE | (1 << 11), Q_BROWSE_YEARS = Q_F_BROWSE | (1 << 11),
Q_COUNT_ITEMS = (1 << 12), Q_COUNT_ITEMS = (1 << 12),
Q_BROWSE_DISCS = Q_F_BROWSE | (1 << 13),
Q_BROWSE_TRACKS = Q_F_BROWSE | (1 << 14),
}; };
#define ARTWORK_UNKNOWN 0 #define ARTWORK_UNKNOWN 0

209
src/mpd.c
View File

@ -397,11 +397,13 @@ mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, int pos_p
ret = evbuffer_add_printf(evbuf, ret = evbuffer_add_printf(evbuf,
"Pos: %d\n", "Pos: %d\n",
pos_pl); pos_pl);
//TODO mpd does not return the persistent id of a file but instead a unique id in the current playlist
ret = evbuffer_add_printf(evbuf,
"Id: %d\n",
mfi->id);
} }
ret = evbuffer_add_printf(evbuf,
"Id: %d\n",
mfi->id);
return ret; return ret;
} }
@ -454,7 +456,6 @@ mpd_add_mediainfo_byid(struct evbuffer *evbuf, int id, int pos_pl)
* MUSICBRAINZ_ARTISTID: c5c2ea1c-4bde-4f4d-bd0b-47b200bf99d6 * MUSICBRAINZ_ARTISTID: c5c2ea1c-4bde-4f4d-bd0b-47b200bf99d6
* MUSICBRAINZ_ALBUMID: 812f4b87-8ad9-41bd-be79-38151f17a2b4 * MUSICBRAINZ_ALBUMID: 812f4b87-8ad9-41bd-be79-38151f17a2b4
* MUSICBRAINZ_TRACKID: fde95c39-ee51-48f6-a7f9-b5631c2ed156 * MUSICBRAINZ_TRACKID: fde95c39-ee51-48f6-a7f9-b5631c2ed156
* Id: 1
* *
* @param evbuf the response event buffer * @param evbuf the response event buffer
* @param mfi media information * @param mfi media information
@ -495,8 +496,7 @@ mpd_add_db_media_file_info(struct evbuffer *evbuf, struct db_media_file_info *db
"Track: %s\n" "Track: %s\n"
"Date: %s\n" "Date: %s\n"
"Genre: %s\n" "Genre: %s\n"
"Disc: %s\n" "Disc: %s\n",
"Id: %s\n",
(dbmfi->virtual_path + 1), (dbmfi->virtual_path + 1),
modified, modified,
(songlength / 1000), (songlength / 1000),
@ -509,8 +509,7 @@ mpd_add_db_media_file_info(struct evbuffer *evbuf, struct db_media_file_info *db
dbmfi->track, dbmfi->track,
dbmfi->year, dbmfi->year,
dbmfi->genre, dbmfi->genre,
dbmfi->disc, dbmfi->disc);
dbmfi->id);
return ret; return ret;
} }
@ -1760,7 +1759,7 @@ mpd_command_listplaylistinfo(struct evbuffer *evbuf, int argc, char **argv, char
ret = asprintf(errmsg, "Playlist not found for path '%s'", argv[1]); ret = asprintf(errmsg, "Playlist not found for path '%s'", argv[1]);
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n"); DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG; return ACK_ERROR_NO_EXIST;
} }
memset(&qp, 0, sizeof(struct query_params)); memset(&qp, 0, sizeof(struct query_params));
@ -1925,6 +1924,7 @@ mpd_get_query_params_find(int argc, char **argv, struct query_params *qp)
int start_pos; int start_pos;
int end_pos; int end_pos;
int i; int i;
uint32_t num;
int ret; int ret;
c1 = NULL; c1 = NULL;
@ -1978,6 +1978,26 @@ mpd_get_query_params_find(int argc, char **argv, struct query_params *qp)
{ {
c1 = sqlite3_mprintf("(f.title = '%q')", argv[i + 1]); c1 = sqlite3_mprintf("(f.title = '%q')", argv[i + 1]);
} }
else if (0 == strcasecmp(argv[i], "genre"))
{
c1 = sqlite3_mprintf("(f.genre = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "disc"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
DPRINTF(E_WARN, L_MPD, "Disc parameter '%s' is not an integer and will be ignored\n", argv[i + 1]);
else
c1 = sqlite3_mprintf("(f.disc = %d)", num);
}
else if (0 == strcasecmp(argv[i], "track"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
DPRINTF(E_WARN, L_MPD, "Track parameter '%s' is not an integer and will be ignored\n", argv[i + 1]);
else
c1 = sqlite3_mprintf("(f.track = %d)", num);
}
else if (i == 0 && argc == 1) else if (i == 0 && argc == 1)
{ {
// Special case: a single token is allowed if listing albums for an artist // Special case: a single token is allowed if listing albums for an artist
@ -2113,6 +2133,44 @@ mpd_command_find(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
static int static int
mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{ {
struct query_params qp;
struct player_source *ps;
int ret;
if (argc < 3 || ((argc - 1) % 2) != 0)
{
ret = asprintf(errmsg, "Missing argument(s) for command 'findadd'");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_ITEMS;
qp.sort = S_NAME;
qp.idx_type = I_NONE;
mpd_get_query_params_find(argc - 1, argv + 1, &qp);
ps = player_queue_make(&qp);
if (!ps)
{
ret = asprintf(errmsg, "Failed to add songs to playlist");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
player_queue_add(ps);
ret = player_playback_start(NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_MPD, "Could not start playback\n");
}
return 0; return 0;
} }
@ -2163,6 +2221,24 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
qp.sort = S_YEAR; qp.sort = S_YEAR;
type = "Date: "; type = "Date: ";
} }
else if (0 == strcasecmp(argv[1], "genre"))
{
qp.type = Q_BROWSE_GENRES;
qp.sort = S_NONE;
type = "Genre: ";
}
else if (0 == strcasecmp(argv[1], "disc"))
{
qp.type = Q_BROWSE_DISCS;
qp.sort = S_NONE;
type = "Disc: ";
}
else if (0 == strcasecmp(argv[1], "track"))
{
qp.type = Q_BROWSE_TRACKS;
qp.sort = S_NONE;
type = "Track: ";
}
else else
{ {
DPRINTF(E_WARN, L_MPD, "Unsupported type argument for command 'list': %s\n", argv[1]); DPRINTF(E_WARN, L_MPD, "Unsupported type argument for command 'list': %s\n", argv[1]);
@ -2187,7 +2263,7 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return ACK_ERROR_UNKNOWN; return ACK_ERROR_UNKNOWN;
} }
if (qp.type == Q_BROWSE_YEARS) if (qp.type & Q_F_BROWSE)
{ {
while (((ret = db_query_fetch_string_sort(&qp, &browse_item, &sort_item)) == 0) && (browse_item)) while (((ret = db_query_fetch_string_sort(&qp, &browse_item, &sort_item)) == 0) && (browse_item))
{ {
@ -2321,6 +2397,7 @@ mpd_get_query_params_search(int argc, char **argv, struct query_params *qp)
int start_pos; int start_pos;
int end_pos; int end_pos;
int i; int i;
uint32_t num;
int ret; int ret;
c1 = NULL; c1 = NULL;
@ -2374,6 +2451,26 @@ mpd_get_query_params_search(int argc, char **argv, struct query_params *qp)
{ {
c1 = sqlite3_mprintf("(f.title LIKE '%%%q%%')", argv[i + 1]); c1 = sqlite3_mprintf("(f.title LIKE '%%%q%%')", argv[i + 1]);
} }
else if (0 == strcasecmp(argv[i], "genre"))
{
c1 = sqlite3_mprintf("(f.genre LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "disc"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
DPRINTF(E_WARN, L_MPD, "Disc parameter '%s' is not an integer and will be ignored\n", argv[i + 1]);
else
c1 = sqlite3_mprintf("(f.disc = %d)", num);
}
else if (0 == strcasecmp(argv[i], "track"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
DPRINTF(E_WARN, L_MPD, "Track parameter '%s' is not an integer and will be ignored\n", argv[i + 1]);
else
c1 = sqlite3_mprintf("(f.track = %d)", num);
}
else else
{ {
DPRINTF(E_WARN, L_MPD, "Parameter '%s' is not supported by forked-daapd and will be ignored\n", argv[i]); DPRINTF(E_WARN, L_MPD, "Parameter '%s' is not supported by forked-daapd and will be ignored\n", argv[i]);
@ -2461,6 +2558,50 @@ mpd_command_search(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return 0; return 0;
} }
static int
mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct query_params qp;
struct player_source *ps;
int ret;
if (argc < 3 || ((argc - 1) % 2) != 0)
{
ret = asprintf(errmsg, "Missing argument(s) for command 'search'");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_ITEMS;
qp.sort = S_NAME;
qp.idx_type = I_NONE;
mpd_get_query_params_search(argc - 1, argv + 1, &qp);
ps = player_queue_make(&qp);
if (!ps)
{
ret = asprintf(errmsg, "Failed to add songs to playlist");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
player_queue_add(ps);
ret = player_playback_start(NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_MPD, "Could not start playback\n");
}
return 0;
}
/* /*
* Command handler function for 'update' * Command handler function for 'update'
* Initiates an init-rescan (scans for new files) * Initiates an init-rescan (scans for new files)
@ -2867,6 +3008,28 @@ mpd_command_ignore(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
static int static int
mpd_command_commands(struct evbuffer *evbuf, int argc, char **argv, char **errmsg); mpd_command_commands(struct evbuffer *evbuf, int argc, char **argv, char **errmsg);
/*
* Command handler function for 'tagtypes'
* Returns a lists with supported tags in the form:
* tagtype: Artist
*/
static int
mpd_command_tagtypes(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
evbuffer_add_printf(evbuf,
"tagtype: Artist\n"
"tagtype: AlbumArtist\n"
"tagtype: ArtistSort\n"
"tagtype: AlbumArtistSort\n"
"tagtype: Album\n"
"tagtype: Title\n"
"tagtype: Track\n"
"tagtype: Genre\n"
"tagtype: Disc\n");
return 0;
}
struct command struct command
{ {
@ -3201,11 +3364,11 @@ static struct command mpd_handlers[] =
.mpdcommand = "search", .mpdcommand = "search",
.handler = mpd_command_search .handler = mpd_command_search
}, },
/*
{ {
.mpdcommand = "searchadd", .mpdcommand = "searchadd",
.handler = mpd_command_searchadd .handler = mpd_command_searchadd
}, },
/*
{ {
.mpdcommand = "searchaddpl", .mpdcommand = "searchaddpl",
.handler = mpd_command_searchaddpl .handler = mpd_command_searchaddpl
@ -3247,12 +3410,10 @@ static struct command mpd_handlers[] =
/* /*
* Stickers * Stickers
*/ */
/*
{ {
.mpdcommand = "sticker", .mpdcommand = "sticker",
.handler = mpd_command_sticker .handler = mpd_command_ignore
}, },
*/
/* /*
* Connection settings * Connection settings
@ -3309,10 +3470,9 @@ static struct command mpd_handlers[] =
.mpdcommand = "commands", .mpdcommand = "commands",
.handler = mpd_command_commands .handler = mpd_command_commands
}, },
/*
{ {
.mpdcommand = "notcommands", .mpdcommand = "notcommands",
.handler = mpd_command_notcommands .handler = mpd_command_ignore
}, },
{ {
.mpdcommand = "tagtypes", .mpdcommand = "tagtypes",
@ -3320,8 +3480,9 @@ static struct command mpd_handlers[] =
}, },
{ {
.mpdcommand = "urlhandlers", .mpdcommand = "urlhandlers",
.handler = mpd_command_urlhandlers .handler = mpd_command_ignore
}, },
/*
{ {
.mpdcommand = "decoders", .mpdcommand = "decoders",
.handler = mpd_command_decoders .handler = mpd_command_decoders
@ -3331,28 +3492,26 @@ static struct command mpd_handlers[] =
/* /*
* Client to client * Client to client
*/ */
/*
{ {
.mpdcommand = "subscribe", .mpdcommand = "subscribe",
.handler = mpd_command_subscribe .handler = mpd_command_ignore
}, },
{ {
.mpdcommand = "unsubscribe", .mpdcommand = "unsubscribe",
.handler = mpd_command_unsubscribe .handler = mpd_command_ignore
}, },
{ {
.mpdcommand = "channels", .mpdcommand = "channels",
.handler = mpd_command_channels .handler = mpd_command_ignore
}, },
{ {
.mpdcommand = "readmessages", .mpdcommand = "readmessages",
.handler = mpd_command_readmessages .handler = mpd_command_ignore
}, },
{ {
.mpdcommand = "sendmessage", .mpdcommand = "sendmessage",
.handler = mpd_command_sendmessage .handler = mpd_command_ignore
}, },
*/
/* /*
* NULL command to terminate loop * NULL command to terminate loop
@ -3733,7 +3892,7 @@ int mpd_init(void)
} }
evconnlistener_set_error_cb(listener, mpd_accept_error_cb); evconnlistener_set_error_cb(listener, mpd_accept_error_cb);
DPRINTF(E_INFO, L_MPD, "cache thread init\n"); DPRINTF(E_INFO, L_MPD, "mpd thread init\n");
ret = pthread_create(&tid_mpd, NULL, mpd, NULL); ret = pthread_create(&tid_mpd, NULL, mpd, NULL);
if (ret < 0) if (ret < 0)

View File

@ -759,8 +759,8 @@ metadata_check_icy(void)
/* Audio sources */ /* Audio sources */
/* Thread: httpd (DACP) */ /* Thread: httpd (DACP) */
static struct player_source * struct player_source *
player_queue_make(struct query_params *qp, const char *sort) player_queue_make(struct query_params *qp)
{ {
struct db_media_file_info dbmfi; struct db_media_file_info dbmfi;
struct player_source *q_head; struct player_source *q_head;
@ -770,18 +770,6 @@ player_queue_make(struct query_params *qp, const char *sort)
uint32_t song_length; uint32_t song_length;
int ret; int ret;
qp->idx_type = I_NONE;
if (sort)
{
if (strcmp(sort, "name") == 0)
qp->sort = S_NAME;
else if (strcmp(sort, "album") == 0)
qp->sort = S_ALBUM;
else if (strcmp(sort, "artist") == 0)
qp->sort = S_ARTIST;
}
ret = db_query_start(qp); ret = db_query_start(qp);
if (ret < 0) if (ret < 0)
{ {
@ -960,6 +948,7 @@ player_queue_make_daap(struct player_source **head, const char *query, const cha
qp.offset = 0; qp.offset = 0;
qp.limit = 0; qp.limit = 0;
qp.sort = S_NONE; qp.sort = S_NONE;
qp.idx_type = I_NONE;
if (quirk) if (quirk)
{ {
@ -1040,7 +1029,17 @@ player_queue_make_daap(struct player_source **head, const char *query, const cha
qp.filter = daap_query_parse_sql(query); qp.filter = daap_query_parse_sql(query);
} }
ps = player_queue_make(&qp, sort); if (sort)
{
if (strcmp(sort, "name") == 0)
qp.sort = S_NAME;
else if (strcmp(sort, "album") == 0)
qp.sort = S_ALBUM;
else if (strcmp(sort, "artist") == 0)
qp.sort = S_ARTIST;
}
ps = player_queue_make(&qp);
if (qp.filter) if (qp.filter)
free(qp.filter); free(qp.filter);
@ -1092,7 +1091,9 @@ player_queue_make_pl(int plid, uint32_t *id)
else else
return NULL; return NULL;
ps = player_queue_make(&qp, NULL); qp.idx_type = I_NONE;
ps = player_queue_make(&qp);
if (qp.filter) if (qp.filter)
free(qp.filter); free(qp.filter);
@ -1144,7 +1145,7 @@ player_queue_make_mpd(char *path, int recursive)
DPRINTF(E_DBG, L_PLAYER, "Out of memory\n"); DPRINTF(E_DBG, L_PLAYER, "Out of memory\n");
} }
ps = player_queue_make(&qp, NULL); ps = player_queue_make(&qp);
sqlite3_free(qp.filter); sqlite3_free(qp.filter);
return ps; return ps;

View File

@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include "db.h"
/* AirTunes v2 packet interval in ns */ /* AirTunes v2 packet interval in ns */
/* (352 samples/packet * 1e9 ns/s) / 44100 samples/s = 7981859 ns/packet */ /* (352 samples/packet * 1e9 ns/s) / 44100 samples/s = 7981859 ns/packet */
# define AIRTUNES_V2_STREAM_PERIOD 7981859 # define AIRTUNES_V2_STREAM_PERIOD 7981859
@ -182,6 +184,9 @@ player_repeat_set(enum repeat_mode mode);
int int
player_shuffle_set(int enable); player_shuffle_set(int enable);
struct player_source *
player_queue_make(struct query_params *qp);
int int
player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk); player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk);