mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 08:15:02 -05:00
Merge pull request #119 from chme/mpdprotocol
[mpd] some fixes and support for new commands
This commit is contained in:
commit
34d815a130
5
src/db.c
5
src/db.c
@ -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;
|
||||||
|
2
src/db.h
2
src/db.h
@ -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
|
||||||
|
386
src/mpd.c
386
src/mpd.c
@ -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;
|
||||||
@ -885,7 +929,7 @@ mpd_command_next(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after nextitem\n");
|
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after nextitem\n");
|
||||||
ret = asprintf(errmsg, "Player returned an error for start after nextitem\n");
|
ret = asprintf(errmsg, "Player returned an error for start after nextitem");
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1079,7 +1123,7 @@ mpd_command_previous(struct evbuffer *evbuf, int argc, char **argv, char **errms
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after previtem\n");
|
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after previtem\n");
|
||||||
ret = asprintf(errmsg, "Player returned an error for start after previtem\n");
|
ret = asprintf(errmsg, "Player returned an error for start after previtem");
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1124,7 +1168,7 @@ mpd_command_seek(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (songpos != 0)
|
if (songpos != 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Given song is not the current playing one, seeking is not supported\n");
|
DPRINTF(E_LOG, L_MPD, "Given song is not the current playing one, seeking is not supported\n");
|
||||||
ret = asprintf(errmsg, "Given song is not the current playing one, seeking is not supported\n");
|
ret = asprintf(errmsg, "Given song is not the current playing one, seeking is not supported");
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1138,7 +1182,7 @@ mpd_command_seek(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_MPD, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
DPRINTF(E_DBG, L_MPD, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
||||||
ret = asprintf(errmsg, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
ret = asprintf(errmsg, "Failed to seek current song to time %d msec", seek_target_msec);
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1148,7 +1192,7 @@ mpd_command_seek(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after seekcur\n");
|
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after seekcur\n");
|
||||||
ret = asprintf(errmsg, "Player returned an error for start after seekcur\n");
|
ret = asprintf(errmsg, "Player returned an error for start after seekcur");
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1195,7 +1239,7 @@ mpd_command_seekid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (status.id != id)
|
if (status.id != id)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Given song is not the current playing one, seeking is not supported\n");
|
DPRINTF(E_LOG, L_MPD, "Given song is not the current playing one, seeking is not supported\n");
|
||||||
ret = asprintf(errmsg, "Given song is not the current playing one, seeking is not supported\n");
|
ret = asprintf(errmsg, "Given song is not the current playing one, seeking is not supported");
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1209,7 +1253,7 @@ mpd_command_seekid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_MPD, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
DPRINTF(E_DBG, L_MPD, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
||||||
ret = asprintf(errmsg, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
ret = asprintf(errmsg, "Failed to seek current song to time %d msec", seek_target_msec);
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1219,7 +1263,7 @@ mpd_command_seekid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after seekcur\n");
|
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after seekcur\n");
|
||||||
ret = asprintf(errmsg, "Player returned an error for start after seekcur\n");
|
ret = asprintf(errmsg, "Player returned an error for start after seekcur");
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1257,7 +1301,7 @@ mpd_command_seekcur(struct evbuffer *evbuf, int argc, char **argv, char **errmsg
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_MPD, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
DPRINTF(E_DBG, L_MPD, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
||||||
ret = asprintf(errmsg, "Failed to seek current song to time %d msec\n", seek_target_msec);
|
ret = asprintf(errmsg, "Failed to seek current song to time %d msec", seek_target_msec);
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1267,7 +1311,7 @@ mpd_command_seekcur(struct evbuffer *evbuf, int argc, char **argv, char **errmsg
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after seekcur\n");
|
DPRINTF(E_LOG, L_MPD, "Player returned an error for start after seekcur\n");
|
||||||
ret = asprintf(errmsg, "Player returned an error for start after seekcur\n");
|
ret = asprintf(errmsg, "Player returned an error for start after seekcur");
|
||||||
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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1324,7 +1368,7 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (!ps)
|
if (!ps)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_MPD, "Failed to add song '%s' to playlist\n", argv[1]);
|
DPRINTF(E_DBG, L_MPD, "Failed to add song '%s' to playlist\n", argv[1]);
|
||||||
ret = asprintf(errmsg, "Failed to add song '%s' to playlist\n", argv[1]);
|
ret = asprintf(errmsg, "Failed to add song '%s' to playlist", 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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1373,7 +1417,7 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (!ps)
|
if (!ps)
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_MPD, "Failed to add song '%s' to playlist\n", argv[1]);
|
DPRINTF(E_DBG, L_MPD, "Failed to add song '%s' to playlist\n", argv[1]);
|
||||||
ret = asprintf(errmsg, "Failed to add song '%s' to playlist\n", argv[1]);
|
ret = asprintf(errmsg, "Failed to add song '%s' to playlist", 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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1567,7 +1611,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Error adding media info for file with id: %d\n", queue->queue[i]);
|
DPRINTF(E_LOG, L_MPD, "Error adding media info for file with id: %d\n", queue->queue[i]);
|
||||||
ret = asprintf(errmsg, "Error adding media info for file with id: %d\n", queue->queue[i]);
|
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i]);
|
||||||
|
|
||||||
queue_free(queue);
|
queue_free(queue);
|
||||||
|
|
||||||
@ -1634,7 +1678,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Error adding media info for file with id: %d\n", queue->queue[i]);
|
DPRINTF(E_LOG, L_MPD, "Error adding media info for file with id: %d\n", queue->queue[i]);
|
||||||
ret = asprintf(errmsg, "Error adding media info for file with id: %d\n", queue->queue[i]);
|
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i]);
|
||||||
|
|
||||||
queue_free(queue);
|
queue_free(queue);
|
||||||
|
|
||||||
@ -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.
|
||||||
@ -1701,7 +1957,7 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
free_pli(pli, 0);
|
free_pli(pli, 0);
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_MPD, "Failed to add song '%s' to playlist\n", argv[1]);
|
DPRINTF(E_DBG, L_MPD, "Failed to add song '%s' to playlist\n", argv[1]);
|
||||||
ret = asprintf(errmsg, "Failed to add song '%s' to playlist\n", argv[1]);
|
ret = asprintf(errmsg, "Failed to add song '%s' to playlist", 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_UNKNOWN;
|
return ACK_ERROR_UNKNOWN;
|
||||||
@ -1779,6 +2035,11 @@ 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 (i == 0 && argc == 1)
|
||||||
|
{
|
||||||
|
// Special case: a single token is allowed if listing albums for an artist
|
||||||
|
c1 = sqlite3_mprintf("(f.album_artist = '%q')", argv[i]);
|
||||||
|
}
|
||||||
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]);
|
||||||
@ -1866,9 +2127,13 @@ 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))
|
||||||
|
{
|
||||||
|
if (argc != 3 || (0 != strcasecmp(argv[1], "album")))
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Missing argument(s) for command 'list'\n");
|
DPRINTF(E_LOG, L_MPD, "Missing argument(s) for command 'list'\n");
|
||||||
ret = asprintf(errmsg, "Missing argument(s) for command 'list'");
|
ret = asprintf(errmsg, "Missing argument(s) for command 'list'");
|
||||||
@ -1876,6 +2141,7 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
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_ARG;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
memset(&qp, 0, sizeof(struct query_params));
|
memset(&qp, 0, sizeof(struct query_params));
|
||||||
|
|
||||||
@ -1892,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]);
|
||||||
@ -1917,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,
|
||||||
@ -1924,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);
|
||||||
|
|
||||||
@ -1941,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
|
||||||
@ -1976,7 +2262,7 @@ mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MPD, "Could not start query for path '%s'\n", argv[1]);
|
DPRINTF(E_LOG, L_MPD, "Could not start query for path '%s'\n", argv[1]);
|
||||||
ret = asprintf(errmsg, "Could not start query for path '%s'\n", argv[1]);
|
ret = asprintf(errmsg, "Could not start query 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");
|
||||||
|
|
||||||
@ -1988,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)
|
||||||
{
|
{
|
||||||
@ -2629,7 +2921,7 @@ static struct command mpd_handlers[] =
|
|||||||
.handler = mpd_command_idle
|
.handler = mpd_command_idle
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.mpdcommand = "idle",
|
.mpdcommand = "noidle",
|
||||||
.handler = mpd_command_noidle
|
.handler = mpd_command_noidle
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -2786,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
|
||||||
@ -2831,7 +3125,6 @@ static struct command mpd_handlers[] =
|
|||||||
/*
|
/*
|
||||||
* Stored playlists
|
* Stored playlists
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
{
|
{
|
||||||
.mpdcommand = "listplaylist",
|
.mpdcommand = "listplaylist",
|
||||||
.handler = mpd_command_listplaylist
|
.handler = mpd_command_listplaylist
|
||||||
@ -2843,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
|
||||||
@ -2998,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
|
||||||
|
11
src/player.c
11
src/player.c
@ -1028,7 +1028,6 @@ player_queue_make_mpd(char *path, int recursive)
|
|||||||
{
|
{
|
||||||
struct query_params qp;
|
struct query_params qp;
|
||||||
struct player_source *ps;
|
struct player_source *ps;
|
||||||
int ret;
|
|
||||||
|
|
||||||
memset(&qp, 0, sizeof(struct query_params));
|
memset(&qp, 0, sizeof(struct query_params));
|
||||||
|
|
||||||
@ -1038,20 +1037,20 @@ player_queue_make_mpd(char *path, int recursive)
|
|||||||
|
|
||||||
if (recursive)
|
if (recursive)
|
||||||
{
|
{
|
||||||
ret = asprintf(&(qp.filter), "f.virtual_path LIKE '/%s%%'", path);
|
qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q%%'", path);
|
||||||
if (ret < 0)
|
if (!qp.filter)
|
||||||
DPRINTF(E_DBG, L_PLAYER, "Out of memory\n");
|
DPRINTF(E_DBG, L_PLAYER, "Out of memory\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = asprintf(&(qp.filter), "f.virtual_path LIKE '/%s'", path);
|
qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q'", path);
|
||||||
if (ret < 0)
|
if (!qp.filter)
|
||||||
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, NULL);
|
||||||
|
|
||||||
free(qp.filter);
|
sqlite3_free(qp.filter);
|
||||||
return ps;
|
return ps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user