[mpd] add support for commands 'listplaylists', 'listplaylist',

'listplaylistinfo', 'ping'; add support for additional tags
This commit is contained in:
chme 2015-02-23 18:43:38 +01:00
parent 4a1bff500a
commit f41e7014e4
3 changed files with 336 additions and 45 deletions

View File

@ -280,6 +280,7 @@ static const char *sort_clause[] =
"ORDER BY f.album_sort ASC, f.disc ASC, f.track ASC", "ORDER BY f.album_sort ASC, f.disc ASC, f.track ASC",
"ORDER BY f.album_artist_sort ASC", "ORDER BY f.album_artist_sort ASC",
"ORDER BY f.type DESC, f.special_id ASC, f.title ASC", "ORDER BY f.type DESC, f.special_id ASC, f.title ASC",
"ORDER BY f.year ASC",
}; };
static char *db_path; static char *db_path;
@ -1546,6 +1547,10 @@ db_query_start(struct query_params *qp)
ret = db_build_query_browse(qp, "composer", "composer_sort", &query); ret = db_build_query_browse(qp, "composer", "composer_sort", &query);
break; break;
case Q_BROWSE_YEARS:
ret = db_build_query_browse(qp, "year", "year", &query);
break;
default: default:
DPRINTF(E_LOG, L_DB, "Unknown query type\n"); DPRINTF(E_LOG, L_DB, "Unknown query type\n");
return -1; return -1;

View File

@ -22,6 +22,7 @@ enum sort_type {
S_ALBUM, S_ALBUM,
S_ARTIST, S_ARTIST,
S_PLAYLIST, S_PLAYLIST,
S_YEAR,
}; };
#define Q_F_BROWSE (1 << 15) #define Q_F_BROWSE (1 << 15)
@ -38,6 +39,7 @@ enum query_type {
Q_GROUP_ARTISTS = (1 << 8), Q_GROUP_ARTISTS = (1 << 8),
Q_GROUP_ITEMS = (1 << 9), Q_GROUP_ITEMS = (1 << 9),
Q_GROUP_DIRS = Q_F_BROWSE | (1 << 10), Q_GROUP_DIRS = Q_F_BROWSE | (1 << 10),
Q_BROWSE_YEARS = Q_F_BROWSE | (1 << 11),
}; };
#define ARTWORK_UNKNOWN 0 #define ARTWORK_UNKNOWN 0

342
src/mpd.c
View File

@ -169,6 +169,17 @@ exit_cb(int fd, short what, void *arg)
event_add(g_exitev, NULL); event_add(g_exitev, NULL);
} }
static void
mpd_time(char *buffer, size_t bufferlen, time_t t)
{
struct tm tm;
const struct tm *tm2 = gmtime_r(&t, &tm);
if (tm2 == NULL)
return;
strftime(buffer, bufferlen, "%FT%TZ", tm2);
}
/* /*
* Parses a rage argument of the form START:END (the END item is not included in the range) * Parses a rage argument of the form START:END (the END item is not included in the range)
* into its start and end position. * into its start and end position.
@ -348,43 +359,50 @@ mpd_parse_args(char *args, int *argc, char **argv)
static int static int
mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, int pos_pl) mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, int pos_pl)
{ {
char modified[32];
int ret; int ret;
if (pos_pl < 0) mpd_time(modified, sizeof(modified), mfi->time_modified);
{
ret = evbuffer_add_printf(evbuf, ret = evbuffer_add_printf(evbuf,
"file: %s\n" "file: %s\n"
"Last-Modified: %s\n"
"Time: %d\n" "Time: %d\n"
"Artist: %s\n" "Artist: %s\n"
"AlbumArtist: %s\n"
"ArtistSort: %s\n"
"AlbumArtistSort: %s\n"
"Album: %s\n" "Album: %s\n"
"Title: %s\n" "Title: %s\n"
"Id: %d\n", "Track: %d\n"
"Date: %d\n"
"Genre: %s\n"
"Disc: %d\n",
(mfi->virtual_path + 1), (mfi->virtual_path + 1),
modified,
(mfi->song_length / 1000), (mfi->song_length / 1000),
mfi->artist, mfi->artist,
mfi->album_artist,
mfi->artist_sort,
mfi->album_artist_sort,
mfi->album, mfi->album,
mfi->title, mfi->title,
mfi->id); mfi->track,
} mfi->year,
else mfi->genre,
mfi->disc);
if (pos_pl >= 0)
{ {
ret = evbuffer_add_printf(evbuf, ret = evbuffer_add_printf(evbuf,
"file: %s\n" "Pos: %d\n",
"Time: %d\n" pos_pl);
"Artist: %s\n"
"Album: %s\n"
"Title: %s\n"
"Pos: %d\n"
"Id: %d\n",
(mfi->virtual_path + 1),
(mfi->song_length / 1000),
mfi->artist,
mfi->album,
mfi->title,
pos_pl,
mfi->id);
} }
ret = evbuffer_add_printf(evbuf,
"Id: %d\n",
mfi->id);
return ret; return ret;
} }
@ -445,9 +463,19 @@ mpd_add_mediainfo_byid(struct evbuffer *evbuf, int id, int pos_pl)
static int static int
mpd_add_db_media_file_info(struct evbuffer *evbuf, struct db_media_file_info *dbmfi) mpd_add_db_media_file_info(struct evbuffer *evbuf, struct db_media_file_info *dbmfi)
{ {
char modified[32];
uint32_t time_modified;
uint32_t songlength; uint32_t songlength;
int ret; int ret;
if (safe_atou32(dbmfi->time_modified, &time_modified) != 0)
{
DPRINTF(E_LOG, L_MPD, "Error converting time modified to uint32_t: %s\n", dbmfi->time_modified);
return -1;
}
mpd_time(modified, sizeof(modified), time_modified);
if (safe_atou32(dbmfi->song_length, &songlength) != 0) if (safe_atou32(dbmfi->song_length, &songlength) != 0)
{ {
DPRINTF(E_LOG, L_MPD, "Error converting song length to uint32_t: %s\n", dbmfi->song_length); DPRINTF(E_LOG, L_MPD, "Error converting song length to uint32_t: %s\n", dbmfi->song_length);
@ -456,16 +484,32 @@ mpd_add_db_media_file_info(struct evbuffer *evbuf, struct db_media_file_info *db
ret = evbuffer_add_printf(evbuf, ret = evbuffer_add_printf(evbuf,
"file: %s\n" "file: %s\n"
"Last-Modified: %s\n"
"Time: %d\n" "Time: %d\n"
"Artist: %s\n" "Artist: %s\n"
"AlbumArtist: %s\n"
"ArtistSort: %s\n"
"AlbumArtistSort: %s\n"
"Album: %s\n" "Album: %s\n"
"Title: %s\n" "Title: %s\n"
"Track: %s\n"
"Date: %s\n"
"Genre: %s\n"
"Disc: %s\n"
"Id: %s\n", "Id: %s\n",
(dbmfi->virtual_path + 1), (dbmfi->virtual_path + 1),
modified,
(songlength / 1000), (songlength / 1000),
dbmfi->artist, dbmfi->artist,
dbmfi->album_artist,
dbmfi->artist_sort,
dbmfi->album_artist_sort,
dbmfi->album, dbmfi->album,
dbmfi->title, dbmfi->title,
dbmfi->track,
dbmfi->year,
dbmfi->genre,
dbmfi->disc,
dbmfi->id); dbmfi->id);
return ret; return ret;
@ -1651,6 +1695,218 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e
return 0; return 0;
} }
static int
mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
return 0;
}
/*
* Command handler function for 'listplaylist'
* Lists all songs in the playlist given by virtual-path in argv[1].
*/
static int
mpd_command_listplaylist(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
char path[PATH_MAX];
struct playlist_info *pli;
struct query_params qp;
struct db_media_file_info dbmfi;
int ret;
if (argc < 2)
{
DPRINTF(E_LOG, L_MPD, "Missing argument for command 'load'\n");
ret = asprintf(errmsg, "Missing argument for command 'load'");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
if (strncmp(argv[1], "/", 1) == 0)
{
ret = snprintf(path, sizeof(path), "%s", argv[1]);
}
else
{
ret = snprintf(path, sizeof(path), "/%s", argv[1]);
}
pli = db_pl_fetch_byvirtualpath(path);
if (!pli)
{
DPRINTF(E_LOG, L_MPD, "Playlist not found for path '%s'\n", argv[1]);
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;
}
memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_PLITEMS;
qp.idx_type = I_NONE;
qp.id = pli->id;
ret = db_query_start(&qp);
if (ret < 0)
{
db_query_end(&qp);
free_pli(pli, 0);
DPRINTF(E_LOG, L_MPD, "Could not start query\n");
ret = asprintf(errmsg, "Could not start query");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
{
evbuffer_add_printf(evbuf,
"file: %s\n",
(dbmfi.virtual_path + 1));
}
db_query_end(&qp);
free_pli(pli, 0);
return 0;
}
/*
* Command handler function for 'listplaylistinfo'
* Lists all songs in the playlist given by virtual-path in argv[1] with metadata.
*/
static int
mpd_command_listplaylistinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
char path[PATH_MAX];
struct playlist_info *pli;
struct query_params qp;
struct db_media_file_info dbmfi;
int ret;
if (argc < 2)
{
DPRINTF(E_LOG, L_MPD, "Missing argument for command 'load'\n");
ret = asprintf(errmsg, "Missing argument for command 'load'");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
if (strncmp(argv[1], "/", 1) == 0)
{
ret = snprintf(path, sizeof(path), "%s", argv[1]);
}
else
{
ret = snprintf(path, sizeof(path), "/%s", argv[1]);
}
pli = db_pl_fetch_byvirtualpath(path);
if (!pli)
{
DPRINTF(E_LOG, L_MPD, "Playlist not found for path '%s'\n", argv[1]);
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;
}
memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_PLITEMS;
qp.idx_type = I_NONE;
qp.id = pli->id;
ret = db_query_start(&qp);
if (ret < 0)
{
db_query_end(&qp);
free_pli(pli, 0);
DPRINTF(E_LOG, L_MPD, "Could not start query\n");
ret = asprintf(errmsg, "Could not start query");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
{
ret = mpd_add_db_media_file_info(evbuf, &dbmfi);
if (ret < 0)
{
DPRINTF(E_LOG, L_MPD, "Error adding song to the evbuffer, song id: %s\n", dbmfi.id);
}
}
db_query_end(&qp);
free_pli(pli, 0);
return 0;
}
/*
* Command handler function for 'listplaylists'
* Lists all playlists with their last modified date.
*/
static int
mpd_command_listplaylists(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct query_params qp;
struct db_playlist_info dbpli;
char modified[32];
uint32_t time_modified;
int ret;
memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_PL;
qp.sort = S_PLAYLIST;
qp.idx_type = I_NONE;
qp.filter = "(f.type = 0)";
ret = db_query_start(&qp);
if (ret < 0)
{
db_query_end(&qp);
DPRINTF(E_LOG, L_MPD, "Could not start query\n");
ret = asprintf(errmsg, "Could not start query");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
while (((ret = db_query_fetch_pl(&qp, &dbpli)) == 0) && (dbpli.id))
{
if (safe_atou32(dbpli.db_timestamp, &time_modified) != 0)
{
DPRINTF(E_LOG, L_MPD, "Error converting time modified to uint32_t: %s\n", dbpli.db_timestamp);
return -1;
}
mpd_time(modified, sizeof(modified), time_modified);
evbuffer_add_printf(evbuf,
"playlist: %s\n"
"Last-Modified: %s\n",
(dbpli.virtual_path + 1),
modified);
}
db_query_end(&qp);
return 0;
}
/* /*
* Command handler function for 'load' * Command handler function for 'load'
* Adds the playlist given by virtual-path in argv[1] to the queue. * Adds the playlist given by virtual-path in argv[1] to the queue.
@ -1871,6 +2127,8 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
struct query_params qp; struct query_params qp;
struct db_group_info dbgri; struct db_group_info dbgri;
char *type; char *type;
char *browse_item;
char *sort_item;
int ret; int ret;
if (argc < 2 || ((argc % 2) != 0)) if (argc < 2 || ((argc % 2) != 0))
@ -1900,6 +2158,12 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
qp.sort = S_ALBUM; qp.sort = S_ALBUM;
type = "Album: "; type = "Album: ";
} }
else if (0 == strcasecmp(argv[1], "date"))
{
qp.type = Q_BROWSE_YEARS;
qp.sort = S_YEAR;
type = "Date: ";
}
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]);
@ -1925,6 +2189,18 @@ 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)
{
while (((ret = db_query_fetch_string_sort(&qp, &browse_item, &sort_item)) == 0) && (browse_item))
{
evbuffer_add_printf(evbuf,
"%s%s\n",
type,
browse_item);
}
}
else
{
while ((ret = db_query_fetch_group(&qp, &dbgri)) == 0) while ((ret = db_query_fetch_group(&qp, &dbgri)) == 0)
{ {
evbuffer_add_printf(evbuf, evbuffer_add_printf(evbuf,
@ -1932,6 +2208,7 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
type, type,
dbgri.itemname); dbgri.itemname);
} }
}
db_query_end(&qp); db_query_end(&qp);
@ -1949,6 +2226,7 @@ mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
char parent[PATH_MAX]; char parent[PATH_MAX];
struct filelist_info *fi; struct filelist_info *fi;
struct media_file_info *mfi; struct media_file_info *mfi;
char modified[32];
int ret; int ret;
if (argc < 2 || strlen(argv[1]) == 0 if (argc < 2 || strlen(argv[1]) == 0
@ -1996,17 +2274,23 @@ mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{ {
if (fi->type == F_DIR) if (fi->type == F_DIR)
{ {
mpd_time(modified, sizeof(modified), fi->time_modified);
evbuffer_add_printf(evbuf, evbuffer_add_printf(evbuf,
"directory: %s\n" "directory: %s\n"
"Last-Modified: 2014-07-11T14:13:56Z\n", //TODO Send correct last modified timestamp "Last-Modified: %s\n",
(fi->virtual_path + 1)); (fi->virtual_path + 1),
modified);
} }
else if (fi->type == F_PLAYLIST) else if (fi->type == F_PLAYLIST)
{ {
mpd_time(modified, sizeof(modified), fi->time_modified);
evbuffer_add_printf(evbuf, evbuffer_add_printf(evbuf,
"playlist: %s\n" "playlist: %s\n"
"Last-Modified: 2014-07-11T14:13:56Z\n", //TODO Send correct last modified timestamp "Last-Modified: %s\n",
(fi->virtual_path + 1)); (fi->virtual_path + 1),
modified);
} }
else if (fi->type == F_FILE) else if (fi->type == F_FILE)
{ {
@ -2794,10 +3078,12 @@ static struct command mpd_handlers[] =
.mpdcommand = "playlistsearch", .mpdcommand = "playlistsearch",
.handler = mpd_command_playlistsearch .handler = mpd_command_playlistsearch
}, },
*/
{ {
.mpdcommand = "plchanges", .mpdcommand = "plchanges",
.handler = mpd_command_plchanges .handler = mpd_command_plchanges
}, },
/*
{ {
.mpdcommand = "plchangesposid", .mpdcommand = "plchangesposid",
.handler = mpd_command_plchangesposid .handler = mpd_command_plchangesposid
@ -2839,7 +3125,6 @@ static struct command mpd_handlers[] =
/* /*
* Stored playlists * Stored playlists
*/ */
/*
{ {
.mpdcommand = "listplaylist", .mpdcommand = "listplaylist",
.handler = mpd_command_listplaylist .handler = mpd_command_listplaylist
@ -2851,8 +3136,7 @@ static struct command mpd_handlers[] =
{ {
.mpdcommand = "listplaylists", .mpdcommand = "listplaylists",
.handler = mpd_command_listplaylists .handler = mpd_command_listplaylists
},load },
*/
{ {
.mpdcommand = "load", .mpdcommand = "load",
.handler = mpd_command_load .handler = mpd_command_load
@ -3006,11 +3290,11 @@ static struct command mpd_handlers[] =
.mpdcommand = "password", .mpdcommand = "password",
.handler = mpd_command_password .handler = mpd_command_password
}, },
*/
{ {
.mpdcommand = "ping", .mpdcommand = "ping",
.handler = mpd_command_ping .handler = mpd_command_ignore
}, },
*/
/* /*
* Audio output devices * Audio output devices