From c3c2c421d24fb7d49fcfe9087062e46915212c13 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 19 Apr 2014 08:09:32 +0200 Subject: [PATCH 1/6] added support for the playqueueedit move command --- src/httpd_dacp.c | 57 +++++++++++++++++++++++++++++++- src/player.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ src/player.h | 3 ++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index ccccbf57..d80b0d89 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1336,7 +1336,12 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, return; } } - + //?command=add&query='dmap.itemid:156'&sort=album&mode=3&session-id=100 + // -> mode=3: add to playqueue position 0 (play next) + //?command=add&query='dmap.itemid:158'&sort=album&mode=0&session-id=100 + // -> mode=0: add to end of playqueue + //?command=add&query='dmap.itemid:306'&queuefilter=album:6525753023700533274&sort=album&mode=1&session-id=100 + // -> mode 1: stop playblack, clear playqueue, add songs to playqueue if ((mode == 1) || (mode == 2)) { player_playback_stop(); @@ -1412,6 +1417,51 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); } +static void +dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) +{ + /* + * Handles the move command. + * Exampe request: + * playqueue-edit?command=move&edit-params='edit-param.move-pair:3,0'&session-id=100 + * + * The 'edit-param.move-pair' param contains the index of the song in the playqueue to be moved (index 3 in the example) + * and the index of the song after which it should be inserted (index 0 in the exampe, the now playing song). + */ + int ret; + + const char *editparams; + int src; + int dst; + + editparams = evhttp_find_header(query, "edit-params"); + if (editparams) + { + ret = safe_atoi32(strchr(editparams, ':') + 1, &src); + if (ret < 0) + { + DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-from value in playqueue-edit request\n"); + + dmap_send_error(req, "cacr", "Invalid request"); + return; + } + + ret = safe_atoi32(strchr(editparams, ',') + 1, &dst); + if (ret < 0) + { + DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-to value in playqueue-edit request\n"); + + dmap_send_error(req, "cacr", "Invalid request"); + return; + } + + player_queue_move(src, dst); + } + + /* 204 No Content is the canonical reply */ + evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); +} + static void dacp_reply_playqueueedit(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) { @@ -1454,6 +1504,9 @@ dacp_reply_playqueueedit(struct evhttp_request *req, struct evbuffer *evbuf, cha User selected track (Audiobooks): ?command=add&query='dmap.itemid:...'&mode=1&session-id=... -> clear queue, play itemid and the rest of album tracks + + ?command=move&edit-params='edit-param.move-pair:3,0'&session-id=100 + -> move song from playqueue position 3 to be played after song at position 0 */ s = daap_session_find(req, query, evbuf); @@ -1475,6 +1528,8 @@ dacp_reply_playqueueedit(struct evhttp_request *req, struct evbuffer *evbuf, cha dacp_reply_cue_play(req, evbuf, uri, query); else if (strcmp(param, "add") == 0) dacp_reply_playqueueedit_add(req, evbuf, uri, query); + else if (strcmp(param, "move") == 0) + dacp_reply_playqueueedit_move(req, evbuf, uri, query); else { DPRINTF(E_LOG, L_DACP, "Unknown playqueue-edit command %s\n", param); diff --git a/src/player.c b/src/player.c index e3fad788..e93557e1 100644 --- a/src/player.c +++ b/src/player.c @@ -116,6 +116,7 @@ struct player_command enum repeat_mode mode; uint32_t id; int intval; + int ps_pos[2]; } arg; int ret; @@ -3356,6 +3357,70 @@ queue_add(struct player_command *cmd) return 0; } +static int queue_move(struct player_command *cmd) +{ + struct player_source *ps_src = NULL; + struct player_source *ps_dst = NULL; + + int pos_max = MAX(cmd->arg.ps_pos[0], cmd->arg.ps_pos[1]); + DPRINTF(E_LOG, L_PLAYER, "Move song from position %d to be next song after %d\n", cmd->arg.ps_pos[0], + cmd->arg.ps_pos[1]); + + struct player_source *ps_tmp = cur_playing ? cur_playing : cur_streaming; + if (!ps_tmp) + { + DPRINTF(E_DBG, L_PLAYER, "Current playing/streaming song not found\n"); + return 0; + } + + int i = 0; + for (i = 0; i <= pos_max; ++i) + { + if (i == cmd->arg.ps_pos[0]) + ps_src = ps_tmp; + if (i == cmd->arg.ps_pos[1]) + ps_dst = ps_tmp; + + ps_tmp = shuffle ? ps_tmp->shuffle_next : ps_tmp->pl_next; + } + + if (ps_src && ps_dst) + { + struct player_source *ps_src_prev; + struct player_source *ps_src_next; + struct player_source *ps_dst_next; + + if (shuffle) + { + ps_src_prev = ps_src->shuffle_prev; + ps_src_next = ps_src->shuffle_next; + ps_src_prev->shuffle_next = ps_src_next; + ps_src_next->shuffle_prev = ps_src_prev; + + ps_dst_next = ps_dst->shuffle_next; + ps_dst->shuffle_next = ps_src; + ps_src->shuffle_prev = ps_dst; + ps_dst_next->shuffle_prev = ps_src; + ps_src->shuffle_next = ps_dst_next; + } + else + { + ps_src_prev = ps_src->pl_prev; + ps_src_next = ps_src->pl_next; + ps_src_prev->pl_next = ps_src_next; + ps_src_next->pl_prev = ps_src_prev; + + ps_dst_next = ps_dst->pl_next; + ps_dst->pl_next = ps_src; + ps_src->pl_prev = ps_dst; + ps_dst_next->pl_prev = ps_src; + ps_src->pl_next = ps_dst_next; + } + } + + return 0; +} + static int queue_clear(struct player_command *cmd) { @@ -3829,6 +3894,26 @@ player_queue_add(struct player_source *ps) return ret; } +int +player_queue_move(int ps_pos_from, int ps_pos_to) +{ + struct player_command cmd; + int ret; + + command_init(&cmd); + + cmd.func = queue_move; + cmd.func_bh = NULL; + cmd.arg.ps_pos[0] = ps_pos_from; + cmd.arg.ps_pos[1] = ps_pos_to; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + void player_queue_clear(void) { diff --git a/src/player.h b/src/player.h index 0c62e674..2c8c3bda 100644 --- a/src/player.h +++ b/src/player.h @@ -145,6 +145,9 @@ player_queue_get(void); int player_queue_add(struct player_source *ps); +int +player_queue_move(int ps_pos_from, int ps_pos_to); + void player_queue_clear(void); From 475a2f4e8f019c70fd938abcecb199451551f8b8 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 19 Apr 2014 08:35:07 +0200 Subject: [PATCH 2/6] added support for the playqueueedit remove command --- src/httpd_dacp.c | 36 ++++++++++++++++++++++++++++++++ src/player.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ src/player.h | 3 +++ 3 files changed, 93 insertions(+) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index d80b0d89..ac4bdfd9 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1462,6 +1462,38 @@ dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); } +static void +dacp_reply_playqueueedit_remove(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) +{ + /* + * Handles the remove command. + * Exampe request (removes song at position 1 in the playqueue): + * ?command=remove&items=1&session-id=100 + */ + int ret; + + const char *itemsparam; + int item_index; + + itemsparam = evhttp_find_header(query, "items"); + if (itemsparam) + { + ret = safe_atoi32(itemsparam, &item_index); + if (ret < 0) + { + DPRINTF(E_LOG, L_DACP, "Invalid edit-params remove item value in playqueue-edit request\n"); + + dmap_send_error(req, "cacr", "Invalid request"); + return; + } + + player_queue_remove(item_index); + } + + /* 204 No Content is the canonical reply */ + evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf); +} + static void dacp_reply_playqueueedit(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) { @@ -1507,6 +1539,8 @@ dacp_reply_playqueueedit(struct evhttp_request *req, struct evbuffer *evbuf, cha ?command=move&edit-params='edit-param.move-pair:3,0'&session-id=100 -> move song from playqueue position 3 to be played after song at position 0 + ?command=remove&items=1&session-id=100 + -> remove song on position 1 from the playqueue */ s = daap_session_find(req, query, evbuf); @@ -1530,6 +1564,8 @@ dacp_reply_playqueueedit(struct evhttp_request *req, struct evbuffer *evbuf, cha dacp_reply_playqueueedit_add(req, evbuf, uri, query); else if (strcmp(param, "move") == 0) dacp_reply_playqueueedit_move(req, evbuf, uri, query); + else if (strcmp(param, "remove") == 0) + dacp_reply_playqueueedit_remove(req, evbuf, uri, query); else { DPRINTF(E_LOG, L_DACP, "Unknown playqueue-edit command %s\n", param); diff --git a/src/player.c b/src/player.c index e93557e1..7a12f558 100644 --- a/src/player.c +++ b/src/player.c @@ -3421,6 +3421,42 @@ static int queue_move(struct player_command *cmd) return 0; } +static int queue_remove(struct player_command *cmd) +{ + struct player_source *ps_src = NULL; + + DPRINTF(E_LOG, L_PLAYER, "Remove song from position %d\n", cmd->arg.ps_pos[0]); + + struct player_source *ps_tmp = cur_playing ? cur_playing : cur_streaming; + if (!ps_tmp) + { + DPRINTF(E_DBG, L_PLAYER, "Current playing/streaming song not found\n"); + return 0; + } + + int i = 0; + for (i = 0; i <= cmd->arg.ps_pos[0]; ++i) + { + if (i == cmd->arg.ps_pos[0]) + ps_src = ps_tmp; + + ps_tmp = shuffle ? ps_tmp->shuffle_next : ps_tmp->pl_next; + } + + if (ps_src) + { + ps_src->shuffle_prev->shuffle_next = ps_src->shuffle_next; + ps_src->shuffle_next->shuffle_prev = ps_src->shuffle_prev; + + ps_src->pl_prev->pl_next = ps_src->pl_next; + ps_src->pl_next->pl_prev = ps_src->pl_prev; + + source_free(ps_src); + } + + return 0; +} + static int queue_clear(struct player_command *cmd) { @@ -3914,6 +3950,24 @@ player_queue_move(int ps_pos_from, int ps_pos_to) return ret; } +int player_queue_remove(int ps_pos_remove) +{ + struct player_command cmd; + int ret; + + command_init(&cmd); + + cmd.func = queue_remove; + cmd.func_bh = NULL; + cmd.arg.ps_pos[0] = ps_pos_remove; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + void player_queue_clear(void) { diff --git a/src/player.h b/src/player.h index 2c8c3bda..8b9f7aa5 100644 --- a/src/player.h +++ b/src/player.h @@ -148,6 +148,9 @@ player_queue_add(struct player_source *ps); int player_queue_move(int ps_pos_from, int ps_pos_to); +int +player_queue_remove(int ps_pos_remove); + void player_queue_clear(void); From 32c3b57fcdcc2357492e20c0ae258f843ac01423 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 19 Apr 2014 09:12:58 +0200 Subject: [PATCH 3/6] added support for the playqueueedit add next command --- src/httpd_dacp.c | 23 ++++++++++++------ src/player.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++-- src/player.h | 3 +++ 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index ac4bdfd9..81f5eef0 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1311,6 +1311,13 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, static void dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) { + //?command=add&query='dmap.itemid:156'&sort=album&mode=3&session-id=100 + // -> mode=3: add to playqueue position 0 (play next) + //?command=add&query='dmap.itemid:158'&sort=album&mode=0&session-id=100 + // -> mode=0: add to end of playqueue + //?command=add&query='dmap.itemid:306'&queuefilter=album:6525753023700533274&sort=album&mode=1&session-id=100 + // -> mode 1: stop playblack, clear playqueue, add songs to playqueue + struct player_source *ps; const char *editquery; const char *queuefilter; @@ -1336,12 +1343,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, return; } } - //?command=add&query='dmap.itemid:156'&sort=album&mode=3&session-id=100 - // -> mode=3: add to playqueue position 0 (play next) - //?command=add&query='dmap.itemid:158'&sort=album&mode=0&session-id=100 - // -> mode=0: add to end of playqueue - //?command=add&query='dmap.itemid:306'&queuefilter=album:6525753023700533274&sort=album&mode=1&session-id=100 - // -> mode 1: stop playblack, clear playqueue, add songs to playqueue + if ((mode == 1) || (mode == 2)) { player_playback_stop(); @@ -1387,7 +1389,14 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, idx = ret; - player_queue_add(ps); + if (mode == 3) + { + player_queue_add_next(ps); + } + else + { + player_queue_add(ps); + } } else { diff --git a/src/player.c b/src/player.c index 7a12f558..07a2cbab 100644 --- a/src/player.c +++ b/src/player.c @@ -3357,7 +3357,46 @@ queue_add(struct player_command *cmd) return 0; } -static int queue_move(struct player_command *cmd) +static int +queue_add_next(struct player_command *cmd) +{ + struct player_source *ps; + struct player_source *ps_shuffle; + struct player_source *ps_playing; + struct player_source *ps_tail; + + ps = cmd->arg.ps; + + ps_shuffle = source_shuffle(ps); + if (!ps_shuffle) + ps_shuffle = ps; + + if (source_head) + { + ps_playing = cur_playing ? cur_playing : cur_streaming; + ps_tail = ps->pl_prev; + + ps_tail->pl_next = ps_playing->pl_next; + ps_tail->shuffle_next = ps_playing->shuffle_next; + ps_playing->pl_next = ps; + ps_playing->shuffle_next = ps; + ps->pl_prev = ps_playing; + ps->shuffle_prev = ps_playing; + } + else + { + source_head = ps; + shuffle_head = ps_shuffle; + } + + if (cur_plid != 0) + cur_plid = 0; + + return 0; +} + +static int +queue_move(struct player_command *cmd) { struct player_source *ps_src = NULL; struct player_source *ps_dst = NULL; @@ -3421,7 +3460,8 @@ static int queue_move(struct player_command *cmd) return 0; } -static int queue_remove(struct player_command *cmd) +static int +queue_remove(struct player_command *cmd) { struct player_source *ps_src = NULL; @@ -3930,6 +3970,25 @@ player_queue_add(struct player_source *ps) return ret; } +int +player_queue_add_next(struct player_source *ps) +{ + struct player_command cmd; + int ret; + + command_init(&cmd); + + cmd.func = queue_add_next; + cmd.func_bh = NULL; + cmd.arg.ps = ps; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + int player_queue_move(int ps_pos_from, int ps_pos_to) { diff --git a/src/player.h b/src/player.h index 8b9f7aa5..71e5510f 100644 --- a/src/player.h +++ b/src/player.h @@ -145,6 +145,9 @@ player_queue_get(void); int player_queue_add(struct player_source *ps); +int +player_queue_add_next(struct player_source *ps); + int player_queue_move(int ps_pos_from, int ps_pos_to); From 01cc83f81988c15c9a401a082aa0fda618ce382a Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 20 Apr 2014 06:43:01 +0200 Subject: [PATCH 4/6] respect coding convention and changed log level --- src/httpd_dacp.c | 18 +++++++++--------- src/player.c | 18 ++++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 81f5eef0..2aa6b057 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1439,14 +1439,14 @@ dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf */ int ret; - const char *editparams; + const char *param; int src; int dst; - editparams = evhttp_find_header(query, "edit-params"); - if (editparams) + param = evhttp_find_header(query, "edit-params"); + if (param) { - ret = safe_atoi32(strchr(editparams, ':') + 1, &src); + ret = safe_atoi32(strchr(param, ':') + 1, &src); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-from value in playqueue-edit request\n"); @@ -1455,7 +1455,7 @@ dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf return; } - ret = safe_atoi32(strchr(editparams, ',') + 1, &dst); + ret = safe_atoi32(strchr(param, ',') + 1, &dst); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-to value in playqueue-edit request\n"); @@ -1481,13 +1481,13 @@ dacp_reply_playqueueedit_remove(struct evhttp_request *req, struct evbuffer *evb */ int ret; - const char *itemsparam; + const char *param; int item_index; - itemsparam = evhttp_find_header(query, "items"); - if (itemsparam) + param = evhttp_find_header(query, "items"); + if (param) { - ret = safe_atoi32(itemsparam, &item_index); + ret = safe_atoi32(param, &item_index); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Invalid edit-params remove item value in playqueue-edit request\n"); diff --git a/src/player.c b/src/player.c index 07a2cbab..315f65ec 100644 --- a/src/player.c +++ b/src/player.c @@ -3398,22 +3398,24 @@ queue_add_next(struct player_command *cmd) static int queue_move(struct player_command *cmd) { - struct player_source *ps_src = NULL; - struct player_source *ps_dst = NULL; + struct player_source *ps_src; + struct player_source *ps_dst; + int i; int pos_max = MAX(cmd->arg.ps_pos[0], cmd->arg.ps_pos[1]); - DPRINTF(E_LOG, L_PLAYER, "Move song from position %d to be next song after %d\n", cmd->arg.ps_pos[0], + DPRINTF(E_DBG, L_PLAYER, "Move song from position %d to be next song after %d\n", cmd->arg.ps_pos[0], cmd->arg.ps_pos[1]); struct player_source *ps_tmp = cur_playing ? cur_playing : cur_streaming; if (!ps_tmp) { - DPRINTF(E_DBG, L_PLAYER, "Current playing/streaming song not found\n"); + DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming song not found\n"); return 0; } - int i = 0; - for (i = 0; i <= pos_max; ++i) + ps_src = NULL; + ps_dst = NULL; + for (i = 0; i <= pos_max; i++) { if (i == cmd->arg.ps_pos[0]) ps_src = ps_tmp; @@ -3465,12 +3467,12 @@ queue_remove(struct player_command *cmd) { struct player_source *ps_src = NULL; - DPRINTF(E_LOG, L_PLAYER, "Remove song from position %d\n", cmd->arg.ps_pos[0]); + DPRINTF(E_DBG, L_PLAYER, "Remove song from position %d\n", cmd->arg.ps_pos[0]); struct player_source *ps_tmp = cur_playing ? cur_playing : cur_streaming; if (!ps_tmp) { - DPRINTF(E_DBG, L_PLAYER, "Current playing/streaming song not found\n"); + DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming song not found\n"); return 0; } From 2391838ea1fd9b722bb6ff3c8600e83cdd68ab10 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 20 Apr 2014 07:33:28 +0200 Subject: [PATCH 5/6] announce support of playlist editing by setting "msed" to 1 --- src/httpd_daap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpd_daap.c b/src/httpd_daap.c index 82a1fcc5..fde12e16 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -788,7 +788,7 @@ daap_reply_server_info(struct evhttp_request *req, struct evbuffer *evbuf, char // dmap_add_int(content, "ppro", ); // dpap.protocolversion - dmap_add_char(content, "msed", 0); // dmap.supportsedit? - we don't support playlist editing + dmap_add_char(content, "msed", 1); // dmap.supportsedit? dmap_add_char(content, "mslr", 1); // dmap.loginrequired dmap_add_int(content, "mstm", DAAP_SESSION_TIMEOUT_CAPABILITY); // dmap.timeoutinterval From 4c5abdac5ef5c15c802d9a9ef544f5c400283783 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 20 Apr 2014 11:03:49 +0200 Subject: [PATCH 6/6] add songs for album or artist ordered by album --- src/httpd_dacp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 2aa6b057..12029b81 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1355,6 +1355,12 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, { sort = evhttp_find_header(query, "sort"); + // if sort param is missing and an album or artist is added to the queue, set sort to "album" + if (!sort && (strstr(editquery, "daap.songalbumid:") || strstr(editquery, "daap.songartistid:"))) + { + sort = "album"; + } + queuefilter = evhttp_find_header(query, "queuefilter"); querymodifier = evhttp_find_header(query, "query-modifier");