mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-13 16:03:23 -05:00
Merge pull request #20 from chme/playqueue-edit
Added support for move, remove and add-next
This commit is contained in:
commit
e005733700
@ -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
|
||||
|
108
src/httpd_dacp.c
108
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;
|
||||
@ -1348,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");
|
||||
@ -1382,7 +1395,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
|
||||
{
|
||||
@ -1412,6 +1432,83 @@ 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 *param;
|
||||
int src;
|
||||
int dst;
|
||||
|
||||
param = evhttp_find_header(query, "edit-params");
|
||||
if (param)
|
||||
{
|
||||
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");
|
||||
|
||||
dmap_send_error(req, "cacr", "Invalid request");
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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_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 *param;
|
||||
int item_index;
|
||||
|
||||
param = evhttp_find_header(query, "items");
|
||||
if (param)
|
||||
{
|
||||
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");
|
||||
|
||||
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)
|
||||
{
|
||||
@ -1454,6 +1551,11 @@ 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
|
||||
?command=remove&items=1&session-id=100
|
||||
-> remove song on position 1 from the playqueue
|
||||
*/
|
||||
|
||||
s = daap_session_find(req, query, evbuf);
|
||||
@ -1475,6 +1577,10 @@ 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 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);
|
||||
|
200
src/player.c
200
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,148 @@ queue_add(struct player_command *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
struct player_source *ps_dst;
|
||||
int i;
|
||||
|
||||
int pos_max = MAX(cmd->arg.ps_pos[0], cmd->arg.ps_pos[1]);
|
||||
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_LOG, L_PLAYER, "Current playing/streaming song not found\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ps_src = NULL;
|
||||
ps_dst = NULL;
|
||||
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_remove(struct player_command *cmd)
|
||||
{
|
||||
struct player_source *ps_src = NULL;
|
||||
|
||||
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_LOG, 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)
|
||||
{
|
||||
@ -3829,6 +3972,63 @@ 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -145,6 +145,15 @@ 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);
|
||||
|
||||
int
|
||||
player_queue_remove(int ps_pos_remove);
|
||||
|
||||
void
|
||||
player_queue_clear(void);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user