[mpd] Implement 'playlistfind' and 'playlistsearch'

This commit is contained in:
chme 2017-12-15 18:19:20 +01:00
parent 6f4f7c5b16
commit a1372c692e
2 changed files with 322 additions and 238 deletions

View File

@ -4450,7 +4450,7 @@ db_queue_add_item(struct db_queue_item *queue_item, char reshuffle, uint32_t ite
static int
queue_enum_start(struct query_params *qp)
{
#define Q_TMPL "SELECT * FROM queue WHERE %s %s;"
#define Q_TMPL "SELECT * FROM queue f WHERE %s %s;"
sqlite3_stmt *stmt;
char *query;
const char *orderby;

558
src/mpd.c
View File

@ -594,6 +594,239 @@ mpd_add_db_media_file_info(struct evbuffer *evbuf, struct db_media_file_info *db
return ret;
}
static int
mpd_get_query_params_find(int argc, char **argv, struct query_params *qp)
{
char *c1;
char *c2;
int start_pos;
int end_pos;
int i;
uint32_t num;
int ret;
c1 = NULL;
c2 = NULL;
for (i = 0; i < argc; i += 2)
{
if (0 == strcasecmp(argv[i], "any"))
{
c1 = db_mprintf("(f.artist LIKE '%%%q%%' OR f.album LIKE '%%%q%%' OR f.title LIKE '%%%q%%')", argv[i + 1], argv[i + 1], argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "file"))
{
c1 = db_mprintf("(f.virtual_path = '/%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "base"))
{
c1 = db_mprintf("(f.virtual_path LIKE '/%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "modified-since"))
{
DPRINTF(E_WARN, L_MPD, "Special parameter 'modified-since' is not supported by forked-daapd and will be ignored\n");
}
else if (0 == strcasecmp(argv[i], "window"))
{
ret = mpd_pars_range_arg(argv[i + 1], &start_pos, &end_pos);
if (ret == 0)
{
qp->idx_type = I_SUB;
qp->limit = end_pos - start_pos;
qp->offset = start_pos;
}
else
{
DPRINTF(E_LOG, L_MPD, "Window argument doesn't convert to integer or range: '%s'\n", argv[i + 1]);
}
}
else if (0 == strcasecmp(argv[i], "artist"))
{
c1 = db_mprintf("(f.artist = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "albumartist"))
{
c1 = db_mprintf("(f.album_artist = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "album"))
{
c1 = db_mprintf("(f.album = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "title"))
{
c1 = db_mprintf("(f.title = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "genre"))
{
c1 = db_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 = db_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 = db_mprintf("(f.track = %d)", num);
}
else if (0 == strcasecmp(argv[i], "date"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
c1 = db_mprintf("(f.year = 0 OR f.year IS NULL)");
else
c1 = db_mprintf("(f.year = %d)", num);
}
else if (i == 0 && argc == 1)
{
// Special case: a single token is allowed if listing albums for an artist
c1 = db_mprintf("(f.album_artist = '%q')", argv[i]);
}
else
{
DPRINTF(E_WARN, L_MPD, "Parameter '%s' is not supported by forked-daapd and will be ignored\n", argv[i]);
}
if (c1)
{
if (qp->filter)
c2 = db_mprintf("%s AND %s", qp->filter, c1);
else
c2 = db_mprintf("%s", c1);
free(qp->filter);
qp->filter = c2;
c2 = NULL;
free(c1);
c1 = NULL;
}
}
return 0;
}
static int
mpd_get_query_params_search(int argc, char **argv, struct query_params *qp)
{
char *c1;
char *c2;
int start_pos;
int end_pos;
int i;
uint32_t num;
int ret;
c1 = NULL;
c2 = NULL;
for (i = 0; i < argc; i += 2)
{
if (0 == strcasecmp(argv[i], "any"))
{
c1 = db_mprintf("(f.artist LIKE '%%%q%%' OR f.album LIKE '%%%q%%' OR f.title LIKE '%%%q%%')", argv[i + 1], argv[i + 1], argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "file"))
{
c1 = db_mprintf("(f.virtual_path LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "base"))
{
c1 = db_mprintf("(f.virtual_path LIKE '/%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "modified-since"))
{
DPRINTF(E_WARN, L_MPD, "Special parameter 'modified-since' is not supported by forked-daapd and will be ignored\n");
}
else if (0 == strcasecmp(argv[i], "window"))
{
ret = mpd_pars_range_arg(argv[i + 1], &start_pos, &end_pos);
if (ret == 0)
{
qp->idx_type = I_SUB;
qp->limit = end_pos - start_pos;
qp->offset = start_pos;
}
else
{
DPRINTF(E_LOG, L_MPD, "Window argument doesn't convert to integer or range: '%s'\n", argv[i + 1]);
}
}
else if (0 == strcasecmp(argv[i], "artist"))
{
c1 = db_mprintf("(f.artist LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "albumartist"))
{
c1 = db_mprintf("(f.album_artist LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "album"))
{
c1 = db_mprintf("(f.album LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "title"))
{
c1 = db_mprintf("(f.title LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "genre"))
{
c1 = db_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 = db_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 = db_mprintf("(f.track = %d)", num);
}
else if (0 == strcasecmp(argv[i], "date"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
c1 = db_mprintf("(f.year = 0 OR f.year IS NULL)");
else
c1 = db_mprintf("(f.year = %d)", num);
}
else
{
DPRINTF(E_WARN, L_MPD, "Parameter '%s' is not supported by forked-daapd and will be ignored\n", argv[i]);
}
if (c1)
{
if (qp->filter)
c2 = db_mprintf("%s AND %s", qp->filter, c1);
else
c2 = db_mprintf("%s", c1);
free(qp->filter);
qp->filter = c2;
c2 = NULL;
free(c1);
c1 = NULL;
}
}
return 0;
}
/*
* Command handler function for 'currentsong'
*/
@ -1900,6 +2133,94 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e
return 0;
}
static int
mpd_command_playlistfind(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, struct mpd_client_ctx *ctx)
{
struct query_params query_params;
struct db_queue_item queue_item;
int ret;
memset(&query_params, 0, sizeof(struct query_params));
if (argc < 3 || ((argc - 1) % 2) != 0)
{
*errmsg = safe_asprintf("Missing argument(s) for command 'playlistfind'");
return ACK_ERROR_ARG;
}
mpd_get_query_params_find(argc - 1, argv + 1, &query_params);
ret = db_queue_enum_start(&query_params);
if (ret < 0)
{
free(query_params.filter);
*errmsg = safe_asprintf("Failed to start queue enum for command playlistinfo: '%s'", argv[1]);
return ACK_ERROR_ARG;
}
while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0)
{
ret = mpd_add_db_queue_item(evbuf, &queue_item);
if (ret < 0)
{
*errmsg = safe_asprintf("Error adding media info for file with id: %d", queue_item.file_id);
db_queue_enum_end(&query_params);
free(query_params.filter);
return ACK_ERROR_UNKNOWN;
}
}
db_queue_enum_end(&query_params);
free(query_params.filter);
return 0;
}
static int
mpd_command_playlistsearch(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, struct mpd_client_ctx *ctx)
{
struct query_params query_params;
struct db_queue_item queue_item;
int ret;
memset(&query_params, 0, sizeof(struct query_params));
if (argc < 3 || ((argc - 1) % 2) != 0)
{
*errmsg = safe_asprintf("Missing argument(s) for command 'playlistfind'");
return ACK_ERROR_ARG;
}
mpd_get_query_params_search(argc - 1, argv + 1, &query_params);
ret = db_queue_enum_start(&query_params);
if (ret < 0)
{
free(query_params.filter);
*errmsg = safe_asprintf("Failed to start queue enum for command playlistinfo: '%s'", argv[1]);
return ACK_ERROR_ARG;
}
while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0)
{
ret = mpd_add_db_queue_item(evbuf, &queue_item);
if (ret < 0)
{
*errmsg = safe_asprintf("Error adding media info for file with id: %d", queue_item.file_id);
db_queue_enum_end(&query_params);
free(query_params.filter);
return ACK_ERROR_UNKNOWN;
}
}
db_queue_enum_end(&query_params);
free(query_params.filter);
return 0;
}
static int
plchanges_build_queryparams(struct query_params *query_params, int argc, char **argv, char **errmsg)
{
@ -2398,125 +2719,6 @@ mpd_command_save(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, s
return 0;
}
static int
mpd_get_query_params_find(int argc, char **argv, struct query_params *qp)
{
char *c1;
char *c2;
int start_pos;
int end_pos;
int i;
uint32_t num;
int ret;
c1 = NULL;
c2 = NULL;
for (i = 0; i < argc; i += 2)
{
if (0 == strcasecmp(argv[i], "any"))
{
c1 = db_mprintf("(f.artist LIKE '%%%q%%' OR f.album LIKE '%%%q%%' OR f.title LIKE '%%%q%%')", argv[i + 1], argv[i + 1], argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "file"))
{
c1 = db_mprintf("(f.virtual_path = '/%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "base"))
{
c1 = db_mprintf("(f.virtual_path LIKE '/%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "modified-since"))
{
DPRINTF(E_WARN, L_MPD, "Special parameter 'modified-since' is not supported by forked-daapd and will be ignored\n");
}
else if (0 == strcasecmp(argv[i], "window"))
{
ret = mpd_pars_range_arg(argv[i + 1], &start_pos, &end_pos);
if (ret == 0)
{
qp->idx_type = I_SUB;
qp->limit = end_pos - start_pos;
qp->offset = start_pos;
}
else
{
DPRINTF(E_LOG, L_MPD, "Window argument doesn't convert to integer or range: '%s'\n", argv[i + 1]);
}
}
else if (0 == strcasecmp(argv[i], "artist"))
{
c1 = db_mprintf("(f.artist = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "albumartist"))
{
c1 = db_mprintf("(f.album_artist = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "album"))
{
c1 = db_mprintf("(f.album = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "title"))
{
c1 = db_mprintf("(f.title = '%q')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "genre"))
{
c1 = db_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 = db_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 = db_mprintf("(f.track = %d)", num);
}
else if (0 == strcasecmp(argv[i], "date"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
c1 = db_mprintf("(f.year = 0 OR f.year IS NULL)");
else
c1 = db_mprintf("(f.year = %d)", num);
}
else if (i == 0 && argc == 1)
{
// Special case: a single token is allowed if listing albums for an artist
c1 = db_mprintf("(f.album_artist = '%q')", argv[i]);
}
else
{
DPRINTF(E_WARN, L_MPD, "Parameter '%s' is not supported by forked-daapd and will be ignored\n", argv[i]);
}
if (c1)
{
if (qp->filter)
c2 = db_mprintf("%s AND %s", qp->filter, c1);
else
c2 = db_mprintf("%s", c1);
free(qp->filter);
qp->filter = c2;
c2 = NULL;
free(c1);
c1 = NULL;
}
}
return 0;
}
static int
mpd_command_count(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, struct mpd_client_ctx *ctx)
{
@ -3034,120 +3236,6 @@ mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg,
return ret;
}
static int
mpd_get_query_params_search(int argc, char **argv, struct query_params *qp)
{
char *c1;
char *c2;
int start_pos;
int end_pos;
int i;
uint32_t num;
int ret;
c1 = NULL;
c2 = NULL;
for (i = 0; i < argc; i += 2)
{
if (0 == strcasecmp(argv[i], "any"))
{
c1 = db_mprintf("(f.artist LIKE '%%%q%%' OR f.album LIKE '%%%q%%' OR f.title LIKE '%%%q%%')", argv[i + 1], argv[i + 1], argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "file"))
{
c1 = db_mprintf("(f.virtual_path LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "base"))
{
c1 = db_mprintf("(f.virtual_path LIKE '/%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "modified-since"))
{
DPRINTF(E_WARN, L_MPD, "Special parameter 'modified-since' is not supported by forked-daapd and will be ignored\n");
}
else if (0 == strcasecmp(argv[i], "window"))
{
ret = mpd_pars_range_arg(argv[i + 1], &start_pos, &end_pos);
if (ret == 0)
{
qp->idx_type = I_SUB;
qp->limit = end_pos - start_pos;
qp->offset = start_pos;
}
else
{
DPRINTF(E_LOG, L_MPD, "Window argument doesn't convert to integer or range: '%s'\n", argv[i + 1]);
}
}
else if (0 == strcasecmp(argv[i], "artist"))
{
c1 = db_mprintf("(f.artist LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "albumartist"))
{
c1 = db_mprintf("(f.album_artist LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "album"))
{
c1 = db_mprintf("(f.album LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "title"))
{
c1 = db_mprintf("(f.title LIKE '%%%q%%')", argv[i + 1]);
}
else if (0 == strcasecmp(argv[i], "genre"))
{
c1 = db_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 = db_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 = db_mprintf("(f.track = %d)", num);
}
else if (0 == strcasecmp(argv[i], "date"))
{
ret = safe_atou32(argv[i + 1], &num);
if (ret < 0)
c1 = db_mprintf("(f.year = 0 OR f.year IS NULL)");
else
c1 = db_mprintf("(f.year = %d)", num);
}
else
{
DPRINTF(E_WARN, L_MPD, "Parameter '%s' is not supported by forked-daapd and will be ignored\n", argv[i]);
}
if (c1)
{
if (qp->filter)
c2 = db_mprintf("%s AND %s", qp->filter, c1);
else
c2 = db_mprintf("%s", c1);
free(qp->filter);
qp->filter = c2;
c2 = NULL;
free(c1);
c1 = NULL;
}
}
return 0;
}
/*
* Command handler function for 'search'
* Lists any song that matches the given list of arguments. Arguments are pairs of TYPE and WHAT, where
@ -4394,12 +4482,10 @@ static struct mpd_command mpd_handlers[] =
.mpdcommand = "playlist",
.handler = mpd_command_playlistinfo
},
/*
{
.mpdcommand = "playlistfind",
.handler = mpd_command_playlistfind
},
*/
{
.mpdcommand = "playlistid",
.handler = mpd_command_playlistid
@ -4408,12 +4494,10 @@ static struct mpd_command mpd_handlers[] =
.mpdcommand = "playlistinfo",
.handler = mpd_command_playlistinfo
},
/*
{
.mpdcommand = "playlistsearch",
.handler = mpd_command_playlistsearch
},
*/
{
.mpdcommand = "plchanges",
.handler = mpd_command_plchanges