Merge pull request #149 from chme/mpdcommands
Add support for additional mpd commands
This commit is contained in:
commit
d6828b90fc
8
src/db.c
8
src/db.c
|
@ -1535,6 +1535,14 @@ db_query_start(struct query_params *qp)
|
|||
ret = db_build_query_browse(qp, "year", "year", &query);
|
||||
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:
|
||||
ret = db_build_query_count_items(qp, &query);
|
||||
break;
|
||||
|
|
2
src/db.h
2
src/db.h
|
@ -41,6 +41,8 @@ enum query_type {
|
|||
Q_GROUP_DIRS = Q_F_BROWSE | (1 << 10),
|
||||
Q_BROWSE_YEARS = Q_F_BROWSE | (1 << 11),
|
||||
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
|
||||
|
|
209
src/mpd.c
209
src/mpd.c
|
@ -397,11 +397,13 @@ mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, int pos_p
|
|||
ret = evbuffer_add_printf(evbuf,
|
||||
"Pos: %d\n",
|
||||
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;
|
||||
}
|
||||
|
@ -454,7 +456,6 @@ mpd_add_mediainfo_byid(struct evbuffer *evbuf, int id, int pos_pl)
|
|||
* MUSICBRAINZ_ARTISTID: c5c2ea1c-4bde-4f4d-bd0b-47b200bf99d6
|
||||
* MUSICBRAINZ_ALBUMID: 812f4b87-8ad9-41bd-be79-38151f17a2b4
|
||||
* MUSICBRAINZ_TRACKID: fde95c39-ee51-48f6-a7f9-b5631c2ed156
|
||||
* Id: 1
|
||||
*
|
||||
* @param evbuf the response event buffer
|
||||
* @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"
|
||||
"Date: %s\n"
|
||||
"Genre: %s\n"
|
||||
"Disc: %s\n"
|
||||
"Id: %s\n",
|
||||
"Disc: %s\n",
|
||||
(dbmfi->virtual_path + 1),
|
||||
modified,
|
||||
(songlength / 1000),
|
||||
|
@ -509,8 +509,7 @@ mpd_add_db_media_file_info(struct evbuffer *evbuf, struct db_media_file_info *db
|
|||
dbmfi->track,
|
||||
dbmfi->year,
|
||||
dbmfi->genre,
|
||||
dbmfi->disc,
|
||||
dbmfi->id);
|
||||
dbmfi->disc);
|
||||
|
||||
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]);
|
||||
if (ret < 0)
|
||||
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));
|
||||
|
@ -1925,6 +1924,7 @@ mpd_get_query_params_find(int argc, char **argv, struct query_params *qp)
|
|||
int start_pos;
|
||||
int end_pos;
|
||||
int i;
|
||||
uint32_t num;
|
||||
int ret;
|
||||
|
||||
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]);
|
||||
}
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2163,6 +2221,24 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||
qp.sort = S_YEAR;
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
@ -2321,6 +2397,7 @@ mpd_get_query_params_search(int argc, char **argv, struct query_params *qp)
|
|||
int start_pos;
|
||||
int end_pos;
|
||||
int i;
|
||||
uint32_t num;
|
||||
int ret;
|
||||
|
||||
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]);
|
||||
}
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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'
|
||||
* 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
|
||||
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
|
||||
{
|
||||
|
@ -3201,11 +3364,11 @@ static struct command mpd_handlers[] =
|
|||
.mpdcommand = "search",
|
||||
.handler = mpd_command_search
|
||||
},
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "searchadd",
|
||||
.handler = mpd_command_searchadd
|
||||
},
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "searchaddpl",
|
||||
.handler = mpd_command_searchaddpl
|
||||
|
@ -3247,12 +3410,10 @@ static struct command mpd_handlers[] =
|
|||
/*
|
||||
* Stickers
|
||||
*/
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "sticker",
|
||||
.handler = mpd_command_sticker
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
* Connection settings
|
||||
|
@ -3309,10 +3470,9 @@ static struct command mpd_handlers[] =
|
|||
.mpdcommand = "commands",
|
||||
.handler = mpd_command_commands
|
||||
},
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "notcommands",
|
||||
.handler = mpd_command_notcommands
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
{
|
||||
.mpdcommand = "tagtypes",
|
||||
|
@ -3320,8 +3480,9 @@ static struct command mpd_handlers[] =
|
|||
},
|
||||
{
|
||||
.mpdcommand = "urlhandlers",
|
||||
.handler = mpd_command_urlhandlers
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "decoders",
|
||||
.handler = mpd_command_decoders
|
||||
|
@ -3331,28 +3492,26 @@ static struct command mpd_handlers[] =
|
|||
/*
|
||||
* Client to client
|
||||
*/
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "subscribe",
|
||||
.handler = mpd_command_subscribe
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
{
|
||||
.mpdcommand = "unsubscribe",
|
||||
.handler = mpd_command_unsubscribe
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
{
|
||||
.mpdcommand = "channels",
|
||||
.handler = mpd_command_channels
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
{
|
||||
.mpdcommand = "readmessages",
|
||||
.handler = mpd_command_readmessages
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
{
|
||||
.mpdcommand = "sendmessage",
|
||||
.handler = mpd_command_sendmessage
|
||||
.handler = mpd_command_ignore
|
||||
},
|
||||
*/
|
||||
|
||||
/*
|
||||
* NULL command to terminate loop
|
||||
|
@ -3733,7 +3892,7 @@ int mpd_init(void)
|
|||
}
|
||||
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);
|
||||
if (ret < 0)
|
||||
|
|
35
src/player.c
35
src/player.c
|
@ -759,8 +759,8 @@ metadata_check_icy(void)
|
|||
|
||||
/* Audio sources */
|
||||
/* Thread: httpd (DACP) */
|
||||
static struct player_source *
|
||||
player_queue_make(struct query_params *qp, const char *sort)
|
||||
struct player_source *
|
||||
player_queue_make(struct query_params *qp)
|
||||
{
|
||||
struct db_media_file_info dbmfi;
|
||||
struct player_source *q_head;
|
||||
|
@ -770,18 +770,6 @@ player_queue_make(struct query_params *qp, const char *sort)
|
|||
uint32_t song_length;
|
||||
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);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
@ -960,6 +948,7 @@ player_queue_make_daap(struct player_source **head, const char *query, const cha
|
|||
qp.offset = 0;
|
||||
qp.limit = 0;
|
||||
qp.sort = S_NONE;
|
||||
qp.idx_type = I_NONE;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
free(qp.filter);
|
||||
|
@ -1092,7 +1091,9 @@ player_queue_make_pl(int plid, uint32_t *id)
|
|||
else
|
||||
return NULL;
|
||||
|
||||
ps = player_queue_make(&qp, NULL);
|
||||
qp.idx_type = I_NONE;
|
||||
|
||||
ps = player_queue_make(&qp);
|
||||
|
||||
if (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");
|
||||
}
|
||||
|
||||
ps = player_queue_make(&qp, NULL);
|
||||
ps = player_queue_make(&qp);
|
||||
|
||||
sqlite3_free(qp.filter);
|
||||
return ps;
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "db.h"
|
||||
|
||||
/* AirTunes v2 packet interval in ns */
|
||||
/* (352 samples/packet * 1e9 ns/s) / 44100 samples/s = 7981859 ns/packet */
|
||||
# define AIRTUNES_V2_STREAM_PERIOD 7981859
|
||||
|
@ -182,6 +184,9 @@ player_repeat_set(enum repeat_mode mode);
|
|||
int
|
||||
player_shuffle_set(int enable);
|
||||
|
||||
struct player_source *
|
||||
player_queue_make(struct query_params *qp);
|
||||
|
||||
int
|
||||
player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk);
|
||||
|
||||
|
|
Loading…
Reference in New Issue