Merge branch 'remote4'

This commit is contained in:
ejurgensen 2013-11-23 23:11:32 +01:00
commit 795aa50553
9 changed files with 655 additions and 140 deletions

View File

@ -594,27 +594,21 @@ artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *e
need_rescale = 1;
/* Determine width/height -- assuming max_w == max_h */
if ((src->width <= max_w) && (src->height <= max_h))
if ((src->width <= max_w) && (src->height <= max_h)) /* Smaller than target */
{
need_rescale = 0;
target_w = src->width;
target_h = src->height;
}
else if (src->width > src->height)
else if (src->width * max_h > src->height * max_w) /* Wider aspect ratio than target */
{
target_w = max_w;
target_h = (double)max_h * ((double)src->height / (double)src->width);
target_h = (double)max_w * ((double)src->height / (double)src->width);
}
else if (src->height > src->width)
else /* Taller or equal aspect ratio */
{
target_h = max_h;
target_w = (double)max_w * ((double)src->width / (double)src->height);
}
else
{
target_w = max_w;
target_w = (double)max_h * ((double)src->width / (double)src->height);
target_h = max_h;
}

View File

@ -12,6 +12,7 @@ struct dmap_query_field_map;
%%
"dmap.itemname", "f.title", 0
"dmap.itemid", "f.id", 1
"dmap.containeritemid", "f.id", 1
"daap.songalbum", "f.album", 0
"daap.songalbumid", "f.songalbumid", 1
"daap.songartist", "f.album_artist", 0

View File

@ -26,3 +26,5 @@ struct dacp_prop_map;
"dacp.visualizer", dacp_propget_visualizer, NULL
"com.apple.itunes.itms-songid", dacp_propget_itms_songid, NULL
"com.apple.itunes.has-chapter-data", dacp_propget_haschapterdata, NULL
"com.apple.itunes.mediakind", dacp_propget_mediakind, NULL
"com.apple.itunes.extended-media-kind", dacp_propget_extendedmediakind, NULL

View File

@ -167,6 +167,20 @@ dmap_add_literal(struct evbuffer *evbuf, char *tag, char *str, int len)
evbuffer_add(evbuf, str, len);
}
void
dmap_add_raw_uint32(struct evbuffer *evbuf, uint32_t val)
{
unsigned char buf[4];
/* Value */
buf[0] = (val >> 24) & 0xff;
buf[1] = (val >> 16) & 0xff;
buf[2] = (val >> 8) & 0xff;
buf[3] = val & 0xff;
evbuffer_add(evbuf, buf, sizeof(buf));
}
void
dmap_add_string(struct evbuffer *evbuf, char *tag, const char *str)
{
@ -427,7 +441,7 @@ dmap_encode_file_metadata(struct evbuffer *songlist, struct evbuffer *song, stru
continue;
}
DPRINTF(E_DBG, L_DAAP, "Investigating %s\n", df->desc);
DPRINTF(E_SPAM, L_DAAP, "Investigating %s\n", df->desc);
strval = (char **) ((char *)dbmfi + dfm->mfi_offset);
@ -476,7 +490,7 @@ dmap_encode_file_metadata(struct evbuffer *songlist, struct evbuffer *song, stru
dmap_add_field(song, df, *strval, val);
DPRINTF(E_DBG, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
DPRINTF(E_SPAM, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
}
if (sort_tags)

View File

@ -67,6 +67,9 @@ dmap_add_char(struct evbuffer *evbuf, char *tag, char val);
void
dmap_add_literal(struct evbuffer *evbuf, char *tag, char *str, int len);
void
dmap_add_raw_uint32(struct evbuffer *evbuf, uint32_t val);
void
dmap_add_string(struct evbuffer *evbuf, char *tag, const char *str);

View File

@ -438,11 +438,9 @@ daap_sort_build(struct sort_ctx *ctx, char *str)
return 0;
}
static int
daap_sort_finalize(struct sort_ctx *ctx, struct evbuffer *evbuf)
static void
daap_sort_finalize(struct sort_ctx *ctx)
{
int ret;
/* Add current entry, if any */
if (ctx->mshc != -1)
{
@ -461,13 +459,6 @@ daap_sort_finalize(struct sort_ctx *ctx, struct evbuffer *evbuf)
dmap_add_short(ctx->headerlist, "mshc", '0'); /* 10 */
dmap_add_int(ctx->headerlist, "mshi", ctx->mshi); /* 12 */
dmap_add_int(ctx->headerlist, "mshn", ctx->misc_mshn); /* 12 */
dmap_add_container(evbuf, "mshl", EVBUFFER_LENGTH(ctx->headerlist));
ret = evbuffer_add_buffer(evbuf, ctx->headerlist);
if (ret < 0)
return -1;
return 0;
}
@ -1174,14 +1165,17 @@ daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf,
/* Add header to evbuf, add songlist to evbuf */
if (sort_headers)
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(songlist) + EVBUFFER_LENGTH(sctx->headerlist) + 53);
{
daap_sort_finalize(sctx);
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(songlist) + EVBUFFER_LENGTH(sctx->headerlist) + 61);
}
else
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(songlist) + 53);
dmap_add_int(evbuf, "mstt", 200); /* 12 */
dmap_add_char(evbuf, "muty", 0); /* 9 */
dmap_add_int(evbuf, "mtco", qp.results); /* 12 */
dmap_add_int(evbuf, "mrco", nsongs); /* 12 */
dmap_add_container(evbuf, "mlcl", EVBUFFER_LENGTH(songlist));
dmap_add_container(evbuf, "mlcl", EVBUFFER_LENGTH(songlist)); /* 8 */
db_query_end(&qp);
@ -1201,7 +1195,8 @@ daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf,
if (sort_headers)
{
ret = daap_sort_finalize(sctx, evbuf);
dmap_add_container(evbuf, "mshl", EVBUFFER_LENGTH(sctx->headerlist)); /* 8 */
ret = evbuffer_add_buffer(evbuf, sctx->headerlist);
daap_sort_context_free(sctx);
if (ret < 0)
@ -1412,7 +1407,7 @@ daap_reply_playlists(struct evhttp_request *req, struct evbuffer *evbuf, char **
dmap_add_field(playlist, df, *strval, 0);
DPRINTF(E_DBG, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
DPRINTF(E_SPAM, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
}
/* Item count (mimc) */
@ -1658,7 +1653,7 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
dmap_add_field(group, df, *strval, 0);
DPRINTF(E_DBG, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
DPRINTF(E_SPAM, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
}
if (sort_headers)
@ -1730,7 +1725,10 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
/* Add header to evbuf, add grouplist to evbuf */
if (sort_headers)
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(grouplist) + EVBUFFER_LENGTH(sctx->headerlist) + 53);
{
daap_sort_finalize(sctx);
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(grouplist) + EVBUFFER_LENGTH(sctx->headerlist) + 61);
}
else
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(grouplist) + 53);
@ -1738,7 +1736,7 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
dmap_add_char(evbuf, "muty", 0); /* 9 */
dmap_add_int(evbuf, "mtco", qp.results); /* 12 */
dmap_add_int(evbuf,"mrco", ngrp); /* 12 */
dmap_add_container(evbuf, "mlcl", EVBUFFER_LENGTH(grouplist));
dmap_add_container(evbuf, "mlcl", EVBUFFER_LENGTH(grouplist)); /* 8 */
db_query_end(&qp);
@ -1758,12 +1756,13 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
if (sort_headers)
{
ret = daap_sort_finalize(sctx, evbuf);
dmap_add_container(evbuf, "mshl", EVBUFFER_LENGTH(sctx->headerlist)); /* 8 */
ret = evbuffer_add_buffer(evbuf, sctx->headerlist);
daap_sort_context_free(sctx);
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Could not add sort headers to DAAP browse reply\n");
DPRINTF(E_LOG, L_DAAP, "Could not add sort headers to DAAP groups reply\n");
dmap_send_error(req, tag, "Out of memory");
return;
@ -1936,7 +1935,10 @@ daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
}
if (sort_headers)
dmap_add_container(evbuf, "abro", EVBUFFER_LENGTH(itemlist) + EVBUFFER_LENGTH(sctx->headerlist) + 44);
{
daap_sort_finalize(sctx);
dmap_add_container(evbuf, "abro", EVBUFFER_LENGTH(itemlist) + EVBUFFER_LENGTH(sctx->headerlist) + 52);
}
else
dmap_add_container(evbuf, "abro", EVBUFFER_LENGTH(itemlist) + 44);
@ -1944,7 +1946,7 @@ daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
dmap_add_int(evbuf, "mtco", qp.results); /* 12 */
dmap_add_int(evbuf, "mrco", nitems); /* 12 */
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(itemlist));
dmap_add_container(evbuf, tag, EVBUFFER_LENGTH(itemlist)); /* 8 */
db_query_end(&qp);
@ -1964,7 +1966,8 @@ daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
if (sort_headers)
{
ret = daap_sort_finalize(sctx, evbuf);
dmap_add_container(evbuf, "mshl", EVBUFFER_LENGTH(sctx->headerlist)); /* 8 */
ret = evbuffer_add_buffer(evbuf, sctx->headerlist);
daap_sort_context_free(sctx);
if (ret < 0)

View File

@ -115,6 +115,10 @@ static void
dacp_propget_itms_songid(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
static void
dacp_propget_haschapterdata(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
static void
dacp_propget_mediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
static void
dacp_propget_extendedmediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
/* Forward - properties setters */
static void
@ -154,31 +158,14 @@ static int seek_target;
static void
dacp_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
{
char canp[16];
if ((status->status == PLAY_STOPPED) || !mfi)
return;
memset(canp, 0, sizeof(canp));
canp[3] = 1; /* 0-3 database ID */
canp[4] = (status->plid >> 24) & 0xff;
canp[5] = (status->plid >> 16) & 0xff;
canp[6] = (status->plid >> 8) & 0xff;
canp[7] = status->plid & 0xff;
canp[8] = (status->pos_pl >> 24) & 0xff; /* 8-11 position in playlist */
canp[9] = (status->pos_pl >> 16) & 0xff;
canp[10] = (status->pos_pl >> 8) & 0xff;
canp[11] = status->pos_pl & 0xff;
canp[12] = (status->id >> 24) & 0xff; /* 12-15 track ID */
canp[13] = (status->id >> 16) & 0xff;
canp[14] = (status->id >> 8) & 0xff;
canp[15] = status->id & 0xff;
dmap_add_literal(evbuf, "canp", canp, sizeof(canp));
dmap_add_container(evbuf, "canp", 16);
dmap_add_raw_uint32(evbuf, 1); /* Database */
dmap_add_raw_uint32(evbuf, status->plid);
dmap_add_raw_uint32(evbuf, status->pos_pl);
dmap_add_raw_uint32(evbuf, status->id);
dmap_add_string(evbuf, "cann", mfi->title);
dmap_add_string(evbuf, "cana", mfi->artist);
@ -236,22 +223,34 @@ make_playstatusupdate(struct evbuffer *evbuf)
dmap_add_int(psu, "cmsr", current_rev); /* 12 */
dmap_add_char(psu, "cavc", 1); /* 9 */ /* volume controllable */
dmap_add_char(psu, "caps", status.status); /* 9 */ /* play status, 2 = stopped, 3 = paused, 4 = playing */
dmap_add_char(psu, "cash", status.shuffle); /* 9 */ /* shuffle, true/false */
dmap_add_char(psu, "carp", status.repeat); /* 9 */ /* repeat, 0 = off, 1 = repeat song, 2 = repeat (playlist) */
dmap_add_char(psu, "cafs", 0); /* 9 */ /* dacp.fullscreen */
dmap_add_char(psu, "cavs", 0); /* 9 */ /* dacp.visualizer */
dmap_add_char(psu, "cavc", 1); /* 9 */ /* volume controllable */
dmap_add_int(psu, "caas", 2); /* 12 */ /* available shuffle states */
dmap_add_int(psu, "caar", 6); /* 12 */ /* available repeat states */
dmap_add_char(psu, "cafe", 0); /* 9 */ /* dacp.fullscreenenabled */
dmap_add_char(psu, "cave", 0); /* 9 */ /* dacp.visualizerenabled */
if (mfi)
{
dacp_nowplaying(psu, &status, mfi);
dmap_add_int(psu, "casa", 1); /* 12 */ /* unknown */
dmap_add_int(psu, "astm", mfi->song_length);
dmap_add_char(psu, "casc", 1); /* Maybe an indication of extra data? */
dmap_add_char(psu, "caks", 6); /* Unknown */
dacp_playingtime(psu, &status, mfi);
free_mfi(mfi, 0);
}
dmap_add_char(psu, "casu", 1); /* 9 */ /* unknown */
dmap_add_char(psu, "ceQu", 0); /* 9 */ /* unknown */
dmap_add_container(evbuf, "cmst", EVBUFFER_LENGTH(psu)); /* 8 + len */
ret = evbuffer_add_buffer(evbuf, psu);
@ -481,6 +480,17 @@ dacp_propget_haschapterdata(struct evbuffer *evbuf, struct player_status *status
// TODO
}
static void
dacp_propget_mediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
{
// TODO
}
static void
dacp_propget_extendedmediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
{
// TODO
}
/* Properties setters */
static void
@ -674,27 +684,30 @@ dacp_propset_userrating(const char *value, struct evkeyvalq *query)
static void
dacp_reply_ctrlint(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
dmap_add_container(evbuf, "caci", 127); /* 8 + len */
dmap_add_int(evbuf, "mstt", 200); /* 12 */
dmap_add_char(evbuf, "muty", 0); /* 9 */
dmap_add_int(evbuf, "mtco", 1); /* 12 */
dmap_add_int(evbuf, "mrco", 1); /* 12 */
dmap_add_container(evbuf, "mlcl", 125); /* 8 + len */
dmap_add_container(evbuf, "mlit", 117); /* 8 + len */
dmap_add_int(evbuf, "miid", 1); /* 12 */ /* Database ID */
dmap_add_char(evbuf, "cmik", 1); /* 9 */
/* /ctrl-int */
/* If tags are added or removed container sizes should be adjusted too */
dmap_add_container(evbuf, "caci", 194); /* 8, unknown dacp container - size of content */
dmap_add_int(evbuf, "mstt", 200); /* 12, dmap.status */
dmap_add_char(evbuf, "muty", 0); /* 9, dmap.updatetype */
dmap_add_int(evbuf, "mtco", 1); /* 12, dmap.specifiedtotalcount */
dmap_add_int(evbuf, "mrco", 1); /* 12, dmap.returnedcount */
dmap_add_container(evbuf, "mlcl", 141); /* 8, dmap.listing - size of content */
dmap_add_container(evbuf, "mlit", 133); /* 8, dmap.listingitem - size of content */
dmap_add_int(evbuf, "miid", 1); /* 12, dmap.itemid - database ID */
dmap_add_char(evbuf, "cmik", 1); /* 9, unknown */
dmap_add_int(evbuf, "cmpr", (2 << 16 | 1)); /* 12 */
dmap_add_int(evbuf, "capr", (2 << 16 | 2)); /* 12 */
dmap_add_int(evbuf, "cmpr", (2 << 16 | 2)); /* 12, dmcp.protocolversion */
dmap_add_int(evbuf, "capr", (2 << 16 | 5)); /* 12, dacp.protocolversion */
dmap_add_char(evbuf, "cmsp", 1); /* 9 */
dmap_add_char(evbuf, "aeFR", 0x64); /* 9 */
dmap_add_char(evbuf, "cmsv", 1); /* 9 */
dmap_add_char(evbuf, "cass", 1); /* 9 */
dmap_add_char(evbuf, "caov", 1); /* 9 */
dmap_add_char(evbuf, "casu", 1); /* 9 */
dmap_add_char(evbuf, "ceSG", 1); /* 9 */
dmap_add_char(evbuf, "cmrl", 1); /* 9 */
dmap_add_char(evbuf, "cmsp", 1); /* 9, unknown */
dmap_add_char(evbuf, "aeFR", 0x64); /* 9, unknown */
dmap_add_char(evbuf, "cmsv", 1); /* 9, unknown */
dmap_add_char(evbuf, "cass", 1); /* 9, unknown */
dmap_add_char(evbuf, "caov", 1); /* 9, unknown */
dmap_add_char(evbuf, "casu", 1); /* 9, unknown */
dmap_add_char(evbuf, "ceSG", 1); /* 9, unknown */
dmap_add_char(evbuf, "cmrl", 1); /* 9, unknown */
dmap_add_long(evbuf, "ceSX", (1 << 1 | 1)); /* 16, unknown dacp - lowest bit announces support for playqueue-contents/-edit */
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
}
@ -732,8 +745,8 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
{
sort = evhttp_find_header(query, "sort");
ps = player_queue_make_daap(cuequery, sort);
if (!ps)
ret = player_queue_make_daap(&ps, cuequery, NULL, sort, 0);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not build song queue\n");
@ -764,6 +777,10 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
DPRINTF(E_LOG, L_DACP, "Invalid index (%s) in cue request\n", param);
}
/* If selection was from Up Next queue (command will be playnow), then index is relative */
if ((param = evhttp_find_header(query, "command")) && (strcmp(param, "playnow") == 0))
id += status.pos_pl;
ret = player_playback_start(&id);
if (ret < 0)
{
@ -1109,6 +1126,343 @@ dacp_reply_playresume(struct evhttp_request *req, struct evbuffer *evbuf, char *
evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf);
}
static struct player_source *
next_ps(struct player_source *ps, char shuffle)
{
if (shuffle)
return ps->shuffle_next;
else
return ps->pl_next;
}
static void
dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
struct daap_session *s;
struct evbuffer *song;
struct evbuffer *songlist;
struct evbuffer *playlists;
struct media_file_info *mfi;
struct player_source *ps;
struct player_source *head;
struct player_status status;
const char *param;
int span;
int i;
int n;
int songlist_length;
int ret;
/* /ctrl-int/1/playqueue-contents?span=50&session-id=... */
s = daap_session_find(req, query, evbuf);
if (!s)
return;
DPRINTF(E_DBG, L_DACP, "Fetching playqueue contents\n");
span = 50; /* Default */
param = evhttp_find_header(query, "span");
if (param)
{
ret = safe_atoi32(param, &span);
if (ret < 0)
DPRINTF(E_LOG, L_DACP, "Invalid span value in playqueue-contents request\n");
}
songlist = NULL;
i = 0;
n = 0;
player_get_status(&status);
/* Get queue and make songlist only if playing or paused */
if ((status.status != PLAY_STOPPED) && (head = player_queue_get()))
{
/* Fast forward to song currently being played */
ps = head;
while ((ps->id != status.id) && (ps = next_ps(ps, status.shuffle)) && (ps != head))
i++;
/* Make song list for Up Next, begin with first song after playlist position */
songlist = evbuffer_new();
if (!songlist)
{
DPRINTF(E_LOG, L_DACP, "Could not allocate songlist evbuffer for playqueue-contents\n");
dmap_send_error(req, "ceQR", "Out of memory");
return;
}
while ((n < abs(span)) && (ps = next_ps(ps, status.shuffle)) && (ps != head))
{
n++;
song = evbuffer_new();
if (!song)
{
DPRINTF(E_LOG, L_DACP, "Could not allocate song evbuffer for playqueue-contents\n");
dmap_send_error(req, "ceQR", "Out of memory");
return;
}
mfi = db_file_fetch_byid(ps->id);
dmap_add_container(song, "ceQs", 16);
dmap_add_raw_uint32(song, 1); /* Database */
dmap_add_raw_uint32(song, status.plid);
dmap_add_raw_uint32(song, 0); /* Should perhaps be playlist index? */
dmap_add_raw_uint32(song, mfi->id);
dmap_add_string(song, "ceQn", mfi->title);
dmap_add_string(song, "ceQr", mfi->artist);
dmap_add_string(song, "ceQa", mfi->album);
dmap_add_string(song, "ceQg", mfi->genre);
dmap_add_long(song, "asai", mfi->songalbumid);
dmap_add_int(song, "cmmk", mfi->media_kind);
dmap_add_int(song, "casa", 1); /* Unknown */
dmap_add_int(song, "astm", mfi->song_length);
dmap_add_char(song, "casc", 1); /* Maybe an indication of extra data? */
dmap_add_char(song, "caks", 6); /* Unknown */
dmap_add_int(song, "ceQI", n + i + 1);
dmap_add_container(songlist, "mlit", EVBUFFER_LENGTH(song));
ret = evbuffer_add_buffer(songlist, song);
evbuffer_free(song);
if (mfi)
free_mfi(mfi, 0);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not add song to songlist for playqueue-contents\n");
dmap_send_error(req, "ceQR", "Out of memory");
return;
}
}
}
/* Playlists are hist, curr and main. Currently we don't support hist. */
playlists = evbuffer_new();
if (!playlists)
{
DPRINTF(E_LOG, L_DACP, "Could not allocate playlists evbuffer for playqueue-contents\n");
if (songlist)
evbuffer_free(songlist);
dmap_send_error(req, "ceQR", "Out of memory");
return;
}
dmap_add_container(playlists, "mlit", 61);
dmap_add_string(playlists, "ceQk", "hist"); /* 12 */
dmap_add_int(playlists, "ceQi", -200); /* 12 */
dmap_add_int(playlists, "ceQm", 200); /* 12 */
dmap_add_string(playlists, "ceQl", "Previously Played"); /* 25 = 8 + 17 */
if (songlist)
{
dmap_add_container(playlists, "mlit", 36);
dmap_add_string(playlists, "ceQk", "curr"); /* 12 */
dmap_add_int(playlists, "ceQi", 0); /* 12 */
dmap_add_int(playlists, "ceQm", 1); /* 12 */
dmap_add_container(playlists, "mlit", 69);
dmap_add_string(playlists, "ceQk", "main"); /* 12 */
dmap_add_int(playlists, "ceQi", 1); /* 12 */
dmap_add_int(playlists, "ceQm", n); /* 12 */
dmap_add_string(playlists, "ceQl", "Up Next"); /* 15 = 8 + 7 */
dmap_add_string(playlists, "ceQh", "from Music"); /* 18 = 8 + 10 */
songlist_length = EVBUFFER_LENGTH(songlist);
}
else
songlist_length = 0;
/* Final construction of reply */
dmap_add_container(evbuf, "ceQR", 79 + EVBUFFER_LENGTH(playlists) + songlist_length); /* size of entire container */
dmap_add_int(evbuf, "mstt", 200); /* 12, dmap.status */
dmap_add_int(evbuf, "mtco", abs(span)); /* 12 */
dmap_add_int(evbuf, "mrco", n); /* 12 */
dmap_add_char(evbuf, "ceQu", 0); /* 9 */
dmap_add_container(evbuf, "mlcl", 8 + EVBUFFER_LENGTH(playlists) + songlist_length); /* 8 */
dmap_add_container(evbuf, "ceQS", EVBUFFER_LENGTH(playlists)); /* 8 */
ret = evbuffer_add_buffer(evbuf, playlists);
evbuffer_free(playlists);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not add playlists to evbuffer for playqueue-contents\n");
if (songlist)
evbuffer_free(songlist);
dmap_send_error(req, "ceQR", "Out of memory");
return;
}
if (songlist)
{
ret = evbuffer_add_buffer(evbuf, songlist);
evbuffer_free(songlist);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not add songlist to evbuffer for playqueue-contents\n");
dmap_send_error(req, "ceQR", "Out of memory");
return;
}
}
dmap_add_char(evbuf, "apsm", status.shuffle); /* 9, daap.playlistshufflemode - not part of mlcl container */
dmap_add_char(evbuf, "aprm", status.repeat); /* 9, daap.playlistrepeatmode - not part of mlcl container */
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
}
static void
dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
struct player_source *ps;
const char *editquery;
const char *queuefilter;
const char *sort;
const char *param;
uint32_t idx;
int mode;
int ret;
int quirkyquery;
param = evhttp_find_header(query, "mode");
if (param)
{
ret = safe_atoi32(param, &mode);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Invalid mode value in playqueue-edit request\n");
dmap_send_error(req, "cacr", "Invalid request");
return;
}
}
if ((mode == 1) || (mode == 2))
{
player_playback_stop();
player_queue_clear();
}
editquery = evhttp_find_header(query, "query");
if (editquery)
{
/* This query kind needs special treatment */
quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:");
queuefilter = evhttp_find_header(query, "queuefilter");
sort = evhttp_find_header(query, "sort");
ret = player_queue_make_daap(&ps, editquery, queuefilter, sort, quirkyquery);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not build song queue\n");
dmap_send_error(req, "cacr", "Invalid request");
return;
}
idx = ret;
player_queue_add(ps);
}
else
{
DPRINTF(E_LOG, L_DACP, "Could not add song queue, DACP query missing\n");
dmap_send_error(req, "cacr", "Invalid request");
return;
}
if (mode == 2)
{
player_shuffle_set(1);
idx = 0;
}
DPRINTF(E_DBG, L_DACP, "Song queue built, playback starting at index %" PRIu32 "\n", idx);
ret = player_playback_start(&idx);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not start playback\n");
dmap_send_error(req, "cacr", "Playback failed to start");
return;
}
dmap_add_container(evbuf, "cacr", 24); /* 8 + len */
dmap_add_int(evbuf, "mstt", 200); /* 12 */
dmap_add_int(evbuf, "miid", ps->id); /* 12 */
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
}
static void
dacp_reply_playqueueedit(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
struct daap_session *s;
const char *param;
/* Variations of /ctrl-int/1/playqueue-edit and expected behaviour
User selected play (album or artist tab):
?command=add&query='...'&sort=album&mode=1&session-id=...
-> clear queue, play query results
User selected track (album tab):
?command=add&query='dmap.itemid:...'&queuefilter=album:...&sort=album&mode=1&session-id=...
-> clear queue, play itemid and the rest of album
User selected track (song tab):
?command=add&query='dmap.itemid:...'&queuefilter=playlist:...&sort=name&mode=1&session-id=...
-> clear queue, play itemid and the rest of playlist
User selected track (playlist tab):
?command=add&query='dmap.containeritemid:...'&queuefilter=playlist:...&sort=physical&mode=1&session-id=...
-> clear queue, play containeritemid and the rest of playlist
User selected shuffle (artist tab):
?command=add&query='...'&sort=album&mode=2&session-id=...
-> clear queue, play shuffled query results
User selected add item to queue:
?command=add&query='...'&sort=album&mode=0&session-id=...
-> add query results to queue
User selected play next song (album tab)
?command=add&query='daap.songalbumid:...'&sort=album&mode=3&session-id=...
-> replace queue from after current song with query results
User selected track in queue:
?command=playnow&index=...&session-id=...
-> play index
And the quirky query - no sort and no queuefilter:
User selected track (artist tab):
?command=add&query='dmap.itemid:...'&mode=1&session-id=...
-> clear queue, play itemid and the rest of artist tracks
*/
s = daap_session_find(req, query, evbuf);
if (!s)
return;
param = evhttp_find_header(query, "command");
if (!param)
{
DPRINTF(E_LOG, L_DACP, "No command in playqueue-edit request\n");
dmap_send_error(req, "cmst", "Invalid request");
return;
}
if (strcmp(param, "clear") == 0)
dacp_reply_cue_clear(req, evbuf, uri, query);
else if (strcmp(param, "playnow") == 0)
dacp_reply_cue_play(req, evbuf, uri, query);
else if (strcmp(param, "add") == 0)
dacp_reply_playqueueedit_add(req, evbuf, uri, query);
else
{
DPRINTF(E_LOG, L_DACP, "Unknown playqueue-edit command %s\n", param);
dmap_send_error(req, "cmst", "Invalid request");
return;
}
}
static void
dacp_reply_playstatusupdate(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
@ -1609,6 +1963,14 @@ static struct uri_map dacp_handlers[] =
.regexp = "^/ctrl-int/[[:digit:]]+/playstatusupdate$",
.handler = dacp_reply_playstatusupdate
},
{
.regexp = "^/ctrl-int/[[:digit:]]+/playqueue-contents$",
.handler = dacp_reply_playqueuecontents
},
{
.regexp = "^/ctrl-int/[[:digit:]]+/playqueue-edit$",
.handler = dacp_reply_playqueueedit
},
{
.regexp = "^/ctrl-int/[[:digit:]]+/nowplayingartwork$",
.handler = dacp_reply_nowplayingartwork

View File

@ -114,26 +114,6 @@ struct player_command
int raop_pending;
};
struct player_source
{
uint32_t id;
uint64_t stream_start;
uint64_t output_start;
uint64_t end;
struct transcode_ctx *ctx;
struct player_source *pl_next;
struct player_source *pl_prev;
struct player_source *shuffle_next;
struct player_source *shuffle_prev;
struct player_source *play_next;
};
/* Keep in sync with enum raop_devtype */
static const char *raop_devtype[] =
{
@ -560,7 +540,6 @@ metadata_send(struct player_source *ps, int startup)
raop_metadata_send(ps->id, rtptime, offset, startup);
}
/* Audio sources */
/* Thread: httpd (DACP) */
static struct player_source *
@ -574,7 +553,6 @@ player_queue_make(struct query_params *qp, const char *sort)
int ret;
qp->idx_type = I_NONE;
qp->sort = S_NONE;
if (sort)
{
@ -656,32 +634,165 @@ player_queue_make(struct query_params *qp, const char *sort)
return q_head;
}
/* Thread: httpd (DACP) */
struct player_source *
player_queue_make_daap(const char *query, const char *sort)
static int
fetch_first_query_match(const char *query, struct db_media_file_info *dbmfi)
{
struct query_params qp;
struct player_source *ps;
uint32_t id;
int ret;
memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_ITEMS;
qp.idx_type = I_FIRST;
qp.sort = S_NONE;
qp.offset = 0;
qp.limit = 0;
qp.limit = 1;
qp.filter = daap_query_parse_sql(query);
if (!qp.filter)
{
DPRINTF(E_LOG, L_PLAYER, "Improper DAAP query!\n");
return NULL;
return -1;
}
ret = db_query_start(&qp);
if (ret < 0)
{
DPRINTF(E_LOG, L_PLAYER, "Could not start query\n");
goto no_query_start;
}
if (((ret = db_query_fetch_file(&qp, dbmfi)) == 0) && (dbmfi->id))
{
ret = safe_atou32(dbmfi->id, &id);
if (ret < 0)
{
DPRINTF(E_LOG, L_PLAYER, "Invalid song id in query result!\n");
goto no_result;
}
DPRINTF(E_DBG, L_PLAYER, "Found index song\n");
ret = 1;
}
else
{
DPRINTF(E_LOG, L_PLAYER, "No song matches query (num %d): %s\n", qp.results, qp.filter);
goto no_result;
}
no_result:
db_query_end(&qp);
no_query_start:
if (qp.filter)
free(qp.filter);
if (ret == 1)
return 0;
else
return -1;
}
/* Thread: httpd (DACP) */
int
player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk)
{
struct query_params qp;
struct player_source *ps;
struct db_media_file_info dbmfi;
uint32_t id;
int64_t albumid;
int plid;
int idx;
int ret;
char buf[200];
/* If query doesn't give even a single result give up */
ret = fetch_first_query_match(query, &dbmfi);
if (ret < 0)
return -1;
memset(&qp, 0, sizeof(struct query_params));
qp.offset = 0;
qp.limit = 0;
qp.sort = S_NONE;
id = 0;
if (queuefilter)
{
safe_atou32(dbmfi.id, &id);
if ((strlen(queuefilter) > 6) && (strncmp(queuefilter, "album:", 6) == 0))
{
qp.type = Q_ITEMS;
ret = safe_atoi64(strchr(queuefilter, ':') + 1, &albumid);
if (ret < 0)
{
DPRINTF(E_LOG, L_PLAYER, "Invalid album id in queuefilter: %s\n", queuefilter);
return -1;
}
snprintf(buf, sizeof(buf), "f.songalbumid = %" PRIi64, albumid);
qp.filter = strdup(buf);
}
else if ((strlen(queuefilter) > 9) && (strncmp(queuefilter, "playlist:", 9) == 0))
{
qp.type = Q_PLITEMS;
ret = safe_atoi32(strchr(queuefilter, ':') + 1, &plid);
if (ret < 0)
{
DPRINTF(E_LOG, L_PLAYER, "Invalid playlist id in queuefilter: %s\n", queuefilter);
return -1;
}
qp.id = plid;
qp.filter = strdup("1 = 1");
}
else
{
DPRINTF(E_LOG, L_PLAYER, "Unknown queuefilter: %s\n", queuefilter);
return -1;
}
}
else if (quirk && dbmfi.album_artist)
{
safe_atou32(dbmfi.id, &id);
qp.sort = S_ALBUM;
qp.type = Q_ITEMS;
snprintf(buf, sizeof(buf), "f.album_artist = \"%s\"", dbmfi.album_artist);
qp.filter = strdup(buf);
}
else
{
id = 0;
qp.type = Q_ITEMS;
qp.filter = daap_query_parse_sql(query);
}
ps = player_queue_make(&qp, sort);
if (qp.filter)
free(qp.filter);
return ps;
if (ps)
*head = ps;
else
return -1;
idx = 0;
while (id && ps && ps->pl_next && (ps->id != id) && (ps->pl_next != *head))
{
idx++;
ps = ps->pl_next;
}
return idx;
}
struct player_source *
@ -698,6 +809,7 @@ player_queue_make_pl(int plid, uint32_t *id)
qp.type = Q_PLITEMS;
qp.offset = 0;
qp.limit = 0;
qp.sort = S_NONE;
ps = player_queue_make(&qp, NULL);
@ -3163,7 +3275,6 @@ sync_command(struct player_command *cmd)
return ret;
}
/* Player API executed in the httpd (DACP) thread */
int
player_get_status(struct player_status *status)
@ -3452,6 +3563,15 @@ player_shuffle_set(int enable)
return ret;
}
struct player_source *
player_queue_get(void)
{
if (shuffle)
return shuffle_head;
else
return source_head;
}
int
player_queue_add(struct player_source *ps)
{

View File

@ -55,8 +55,24 @@ struct player_status {
typedef void (*spk_enum_cb)(uint64_t id, const char *name, int relvol, struct spk_flags flags, void *arg);
typedef void (*player_status_handler)(void);
struct player_source;
struct player_source
{
uint32_t id;
uint64_t stream_start;
uint64_t output_start;
uint64_t end;
struct transcode_ctx *ctx;
struct player_source *pl_next;
struct player_source *pl_prev;
struct player_source *shuffle_next;
struct player_source *shuffle_prev;
struct player_source *play_next;
};
int
player_get_current_pos(uint64_t *pos, struct timespec *ts, int commit);
@ -67,7 +83,6 @@ player_get_status(struct player_status *status);
int
player_now_playing(uint32_t *id);
void
player_speaker_enumerate(spk_enum_cb cb, void *arg);
@ -108,13 +123,15 @@ player_repeat_set(enum repeat_mode mode);
int
player_shuffle_set(int enable);
struct player_source *
player_queue_make_daap(const char *query, const char *sort);
int
player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk);
struct player_source *
player_queue_make_pl(int plid, uint32_t *id);
struct player_source *
player_queue_get(void);
int
player_queue_add(struct player_source *ps);
@ -124,7 +141,6 @@ player_queue_clear(void);
void
player_queue_plid(uint32_t plid);
void
player_set_update_handler(player_status_handler handler);