mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-27 06:33:21 -05:00
Add basic support for playqueue-contents (real reply) and add placeholder
for playqueue-edit
This commit is contained in:
parent
7bb2fef25f
commit
f42bbd37e1
141
src/httpd_dacp.c
141
src/httpd_dacp.c
@ -712,7 +712,7 @@ dacp_reply_ctrlint(struct evhttp_request *req, struct evbuffer *evbuf, char **ur
|
|||||||
dmap_add_char(evbuf, "casu", 1); /* 9, unknown */
|
dmap_add_char(evbuf, "casu", 1); /* 9, unknown */
|
||||||
dmap_add_char(evbuf, "ceSG", 1); /* 9, unknown */
|
dmap_add_char(evbuf, "ceSG", 1); /* 9, unknown */
|
||||||
dmap_add_char(evbuf, "cmrl", 1); /* 9, unknown */
|
dmap_add_char(evbuf, "cmrl", 1); /* 9, unknown */
|
||||||
dmap_add_long(evbuf, "ceSX", 3); /* 16, unknown dacp - announce support for playqueue-contents */
|
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);
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
||||||
}
|
}
|
||||||
@ -1131,8 +1131,17 @@ static void
|
|||||||
dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
|
dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
|
||||||
{
|
{
|
||||||
struct daap_session *s;
|
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;
|
const char *param;
|
||||||
int span;
|
int span;
|
||||||
|
int i;
|
||||||
|
int songlist_length;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* /ctrl-int/1/playqueue-contents?span=50&session-id=... */
|
/* /ctrl-int/1/playqueue-contents?span=50&session-id=... */
|
||||||
@ -1141,6 +1150,8 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
|
|||||||
if (!s)
|
if (!s)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_DACP, "Fetching playqueue contents\n");
|
||||||
|
|
||||||
param = evhttp_find_header(query, "span");
|
param = evhttp_find_header(query, "span");
|
||||||
if (param)
|
if (param)
|
||||||
{
|
{
|
||||||
@ -1149,21 +1160,115 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
|
|||||||
DPRINTF(E_LOG, L_DACP, "Invalid span value in playqueue-contents request\n");
|
DPRINTF(E_LOG, L_DACP, "Invalid span value in playqueue-contents request\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dummy reply */
|
songlist = NULL;
|
||||||
dmap_add_container(evbuf, "ceQR", 138); /* 8, size of contents */
|
i = 0;
|
||||||
dmap_add_int(evbuf, "mstt", 200); /* 12, dmap.status */
|
player_get_status(&status);
|
||||||
dmap_add_int(evbuf, "mtco", span); /* 12, dmap.specifiedtotalcount */
|
/* Get queue and make songlist only if playing or paused */
|
||||||
dmap_add_int(evbuf, "mrco", 0); /* 12, dmap.returnedcount */
|
if ((status.status != PLAY_STOPPED) && (head = player_queue_get()))
|
||||||
dmap_add_char(evbuf, "ceQu", 0); /* 9, unknown dacp */
|
{
|
||||||
dmap_add_container(evbuf, "mlcl", 67); /* 8, size of contents */
|
/* Fast forward to current playlist position */
|
||||||
dmap_add_container(evbuf, "ceQS", 59); /* 8, size of contents */
|
for (ps = head; (ps && i < status.pos_pl); i++)
|
||||||
dmap_add_container(evbuf, "mlit", 51); /* 8, size of contents */
|
ps = ps->pl_next;
|
||||||
dmap_add_string(evbuf, "ceQk", "hist"); /* 12, unknown dacp - either hist or curr (or next?) */
|
/* Make song list for Up Next, begin with first song after playlist position */
|
||||||
dmap_add_int(evbuf, "ceQi", 0xffffff38); /* 12, unknown dacp */
|
// TODO support for shuffle
|
||||||
dmap_add_int(evbuf, "ceQm", 200); /* 12, unknown dacp - status code? */
|
songlist = evbuffer_new();
|
||||||
dmap_add_string(evbuf, "ceQl", "History"); /* X - should be full localised name of hist/curr/next*/
|
while ((ps = ps->pl_next) && (ps != head) && (i - status.pos_pl < span))
|
||||||
dmap_add_char(evbuf, "apsm", 0); /* 9, daap.playlistshufflemode - not part of mlcl container */
|
{
|
||||||
dmap_add_char(evbuf, "aprm", 0); /* 9, daap.playlistrepeatmode - not part of mlcl container */
|
i++;
|
||||||
|
song = evbuffer_new();
|
||||||
|
// TODO Out of memory check (song)
|
||||||
|
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, 0); /* Unknown */
|
||||||
|
dmap_add_raw_uint32(song, 0); /* Unknown */
|
||||||
|
dmap_add_raw_uint32(song, mfi->id);
|
||||||
|
dmap_add_string(song, "ceQn", mfi->title);
|
||||||
|
dmap_add_string(song, "ceQr", mfi->album_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", 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);
|
||||||
|
// TODO Out of memory check (songlist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Playlists are hist, curr and main. Currently we don't support hist. */
|
||||||
|
playlists = evbuffer_new();
|
||||||
|
// TODO Out of memory check (playlists)
|
||||||
|
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", i); /* 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", i); /* 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);
|
||||||
|
// TODO check ret value & memcheck evbuf
|
||||||
|
evbuffer_free(playlists);
|
||||||
|
if (songlist)
|
||||||
|
{
|
||||||
|
ret = evbuffer_add_buffer(evbuf, songlist);
|
||||||
|
evbuffer_free(songlist);
|
||||||
|
}
|
||||||
|
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(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
|
||||||
|
{
|
||||||
|
struct daap_session *s;
|
||||||
|
const char *param;
|
||||||
|
int span;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* /ctrl-int/1/playqueue-edit?command=add&query='dmap.itemid:...'&queuefilter=album:...&sort=album&mode=1&session-id=... */
|
||||||
|
|
||||||
|
s = daap_session_find(req, query, evbuf);
|
||||||
|
if (!s)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
||||||
}
|
}
|
||||||
@ -1672,6 +1777,10 @@ static struct uri_map dacp_handlers[] =
|
|||||||
.regexp = "^/ctrl-int/[[:digit:]]+/playqueue-contents$",
|
.regexp = "^/ctrl-int/[[:digit:]]+/playqueue-contents$",
|
||||||
.handler = dacp_reply_playqueuecontents
|
.handler = dacp_reply_playqueuecontents
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.regexp = "^/ctrl-int/[[:digit:]]+/playqueue-edit$",
|
||||||
|
.handler = dacp_reply_playqueueedit
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.regexp = "^/ctrl-int/[[:digit:]]+/nowplayingartwork$",
|
.regexp = "^/ctrl-int/[[:digit:]]+/nowplayingartwork$",
|
||||||
.handler = dacp_reply_nowplayingartwork
|
.handler = dacp_reply_nowplayingartwork
|
||||||
|
28
src/player.c
28
src/player.c
@ -114,26 +114,6 @@ struct player_command
|
|||||||
int raop_pending;
|
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 */
|
/* Keep in sync with enum raop_devtype */
|
||||||
static const char *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);
|
raop_metadata_send(ps->id, rtptime, offset, startup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Audio sources */
|
/* Audio sources */
|
||||||
/* Thread: httpd (DACP) */
|
/* Thread: httpd (DACP) */
|
||||||
static struct player_source *
|
static struct player_source *
|
||||||
@ -3163,7 +3142,6 @@ sync_command(struct player_command *cmd)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Player API executed in the httpd (DACP) thread */
|
/* Player API executed in the httpd (DACP) thread */
|
||||||
int
|
int
|
||||||
player_get_status(struct player_status *status)
|
player_get_status(struct player_status *status)
|
||||||
@ -3452,6 +3430,12 @@ player_shuffle_set(int enable)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct player_source *
|
||||||
|
player_queue_get(void)
|
||||||
|
{
|
||||||
|
return source_head;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
player_queue_add(struct player_source *ps)
|
player_queue_add(struct player_source *ps)
|
||||||
{
|
{
|
||||||
|
23
src/player.h
23
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 (*spk_enum_cb)(uint64_t id, const char *name, int relvol, struct spk_flags flags, void *arg);
|
||||||
typedef void (*player_status_handler)(void);
|
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
|
int
|
||||||
player_get_current_pos(uint64_t *pos, struct timespec *ts, int commit);
|
player_get_current_pos(uint64_t *pos, struct timespec *ts, int commit);
|
||||||
@ -67,7 +83,6 @@ player_get_status(struct player_status *status);
|
|||||||
int
|
int
|
||||||
player_now_playing(uint32_t *id);
|
player_now_playing(uint32_t *id);
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
player_speaker_enumerate(spk_enum_cb cb, void *arg);
|
player_speaker_enumerate(spk_enum_cb cb, void *arg);
|
||||||
|
|
||||||
@ -115,6 +130,9 @@ player_queue_make_daap(const char *query, const char *sort);
|
|||||||
struct player_source *
|
struct player_source *
|
||||||
player_queue_make_pl(int plid, uint32_t *id);
|
player_queue_make_pl(int plid, uint32_t *id);
|
||||||
|
|
||||||
|
struct player_source *
|
||||||
|
player_queue_get(void);
|
||||||
|
|
||||||
int
|
int
|
||||||
player_queue_add(struct player_source *ps);
|
player_queue_add(struct player_source *ps);
|
||||||
|
|
||||||
@ -124,7 +142,6 @@ player_queue_clear(void);
|
|||||||
void
|
void
|
||||||
player_queue_plid(uint32_t plid);
|
player_queue_plid(uint32_t plid);
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
player_set_update_handler(player_status_handler handler);
|
player_set_update_handler(player_status_handler handler);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user