mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-26 14:13:18 -05:00
Merge branch 'remote4'
This commit is contained in:
commit
795aa50553
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -10,19 +10,21 @@
|
||||
%omit-struct-type
|
||||
struct dacp_prop_map;
|
||||
%%
|
||||
"dmcp.volume", dacp_propget_volume, dacp_propset_volume
|
||||
"dacp.playerstate", dacp_propget_playerstate, NULL
|
||||
"dacp.nowplaying", dacp_propget_nowplaying, NULL
|
||||
"dacp.playingtime", dacp_propget_playingtime, dacp_propset_playingtime
|
||||
"dacp.volumecontrollable", dacp_propget_volumecontrollable, NULL
|
||||
"dacp.availableshufflestates", dacp_propget_availableshufflestates, NULL
|
||||
"dacp.availablerepeatstates", dacp_propget_availablerepeatstates, NULL
|
||||
"dacp.shufflestate", dacp_propget_shufflestate, dacp_propset_shufflestate
|
||||
"dacp.repeatstate", dacp_propget_repeatstate, dacp_propset_repeatstate
|
||||
"dacp.userrating", NULL, dacp_propset_userrating
|
||||
"dacp.fullscreenenabled", dacp_propget_fullscreenenabled, NULL
|
||||
"dacp.fullscreen", dacp_propget_fullscreen, NULL
|
||||
"dacp.visualizerenabled", dacp_propget_visualizerenabled, NULL
|
||||
"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
|
||||
"dmcp.volume", dacp_propget_volume, dacp_propset_volume
|
||||
"dacp.playerstate", dacp_propget_playerstate, NULL
|
||||
"dacp.nowplaying", dacp_propget_nowplaying, NULL
|
||||
"dacp.playingtime", dacp_propget_playingtime, dacp_propset_playingtime
|
||||
"dacp.volumecontrollable", dacp_propget_volumecontrollable, NULL
|
||||
"dacp.availableshufflestates", dacp_propget_availableshufflestates, NULL
|
||||
"dacp.availablerepeatstates", dacp_propget_availablerepeatstates, NULL
|
||||
"dacp.shufflestate", dacp_propget_shufflestate, dacp_propset_shufflestate
|
||||
"dacp.repeatstate", dacp_propget_repeatstate, dacp_propset_repeatstate
|
||||
"dacp.userrating", NULL, dacp_propset_userrating
|
||||
"dacp.fullscreenenabled", dacp_propget_fullscreenenabled, NULL
|
||||
"dacp.fullscreen", dacp_propget_fullscreen, NULL
|
||||
"dacp.visualizerenabled", dacp_propget_visualizerenabled, NULL
|
||||
"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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
462
src/httpd_dacp.c
462
src/httpd_dacp.c
@ -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_int(psu, "caas", 2); /* 12 */ /* available shuffle states */
|
||||
dmap_add_int(psu, "caar", 6); /* 12 */ /* available repeat states */
|
||||
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
|
||||
|
184
src/player.c
184
src/player.c
@ -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);
|
||||
|
||||
free(qp.filter);
|
||||
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)
|
||||
{
|
||||
|
28
src/player.h
28
src/player.h
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user