mirror of
https://github.com/owntone/owntone-server.git
synced 2025-03-20 04:24:20 -04:00
Added support for playqueue-contents with negativ span value (returns the previously played songs)
This commit is contained in:
parent
20539c0c08
commit
5f307c7ce1
184
src/httpd_dacp.c
184
src/httpd_dacp.c
@ -1131,23 +1131,71 @@ next_ps(struct player_source *ps, char shuffle)
|
|||||||
return ps->pl_next;
|
return ps->pl_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
|
playqueuecontents_add_source(struct evbuffer *songlist, uint32_t source_id, int pos_in_queue, uint32_t plid)
|
||||||
|
{
|
||||||
|
struct evbuffer *song;
|
||||||
|
struct media_file_info *mfi;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
song = evbuffer_new();
|
||||||
|
if (!song)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_DACP, "Could not allocate song evbuffer for playqueue-contents\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mfi = db_file_fetch_byid(source_id);
|
||||||
|
dmap_add_container(song, "ceQs", 16);
|
||||||
|
dmap_add_raw_uint32(song, 1); /* Database */
|
||||||
|
dmap_add_raw_uint32(song, 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", pos_in_queue);
|
||||||
|
|
||||||
|
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");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void 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 *songlist;
|
||||||
struct evbuffer *playlists;
|
struct evbuffer *playlists;
|
||||||
struct media_file_info *mfi;
|
|
||||||
struct player_source *ps;
|
struct player_source *ps;
|
||||||
struct player_source *head;
|
struct player_source *head;
|
||||||
struct player_status status;
|
struct player_status status;
|
||||||
|
struct player_history *history;
|
||||||
const char *param;
|
const char *param;
|
||||||
int span;
|
int span;
|
||||||
int i;
|
int i;
|
||||||
int n;
|
int n;
|
||||||
int songlist_length;
|
int songlist_length;
|
||||||
int ret;
|
int ret;
|
||||||
|
int start_index;
|
||||||
|
|
||||||
/* /ctrl-int/1/playqueue-contents?span=50&session-id=... */
|
/* /ctrl-int/1/playqueue-contents?span=50&session-id=... */
|
||||||
|
|
||||||
@ -1160,80 +1208,84 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
|
|||||||
span = 50; /* Default */
|
span = 50; /* Default */
|
||||||
param = evhttp_find_header(query, "span");
|
param = evhttp_find_header(query, "span");
|
||||||
if (param)
|
if (param)
|
||||||
{
|
{
|
||||||
ret = safe_atoi32(param, &span);
|
ret = safe_atoi32(param, &span);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
songlist = NULL;
|
songlist = NULL;
|
||||||
i = 0;
|
i = 0;
|
||||||
n = 0;
|
n = 0; // count of songs in songlist
|
||||||
player_get_status(&status);
|
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 */
|
/* Get queue and make songlist only if playing or paused */
|
||||||
songlist = evbuffer_new();
|
if (status.status != PLAY_STOPPED)
|
||||||
if (!songlist)
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the span parameter is negativ make song list for Previously Played,
|
||||||
|
* otherwise make song list for Up Next and begin with first song after playlist position.
|
||||||
|
*/
|
||||||
|
if (span < 0)
|
||||||
|
{
|
||||||
|
history = player_history_get();
|
||||||
|
if (abs(span) > history->count)
|
||||||
|
{
|
||||||
|
start_index = history->start_index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start_index = (history->start_index + history->count - abs(span)) % MAX_HISTORY_COUNT;
|
||||||
|
}
|
||||||
|
for (i = 0; i < history->count && i < abs(span); i++)
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
ret = playqueuecontents_add_source(songlist, history->buffer[(start_index + i) % MAX_HISTORY_COUNT], (n + i + 1), status.plid);
|
||||||
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_DACP, "Could not allocate songlist evbuffer for playqueue-contents\n");
|
DPRINTF(E_LOG, L_DACP, "Could not add song to songlist for playqueue-contents\n");
|
||||||
|
|
||||||
dmap_send_error(req, "ceQR", "Out of memory");
|
dmap_send_error(req, "ceQR", "Out of memory");
|
||||||
return;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Fast forward to song currently being played */
|
||||||
|
head = player_queue_get();
|
||||||
|
if (head)
|
||||||
|
{
|
||||||
|
ps = head;
|
||||||
|
while ((ps->id != status.id) && (ps = next_ps(ps, status.shuffle)) && (ps != head))
|
||||||
|
i++;
|
||||||
|
|
||||||
|
while ((n < abs(span)) && (ps = next_ps(ps, status.shuffle)) && (ps != head))
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
|
||||||
|
ret = playqueuecontents_add_source(songlist, ps->id, (n + i + 1), status.plid);
|
||||||
|
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();
|
playlists = evbuffer_new();
|
||||||
if (!playlists)
|
if (!playlists)
|
||||||
{
|
{
|
||||||
|
71
src/player.c
71
src/player.c
@ -204,6 +204,8 @@ static struct player_source *cur_streaming;
|
|||||||
static uint32_t cur_plid;
|
static uint32_t cur_plid;
|
||||||
static struct evbuffer *audio_buf;
|
static struct evbuffer *audio_buf;
|
||||||
|
|
||||||
|
/* Play history */
|
||||||
|
static struct player_history *history;
|
||||||
|
|
||||||
/* Command helpers */
|
/* Command helpers */
|
||||||
static void
|
static void
|
||||||
@ -1434,6 +1436,28 @@ source_check(void)
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct player_history *
|
||||||
|
player_history_get(void)
|
||||||
|
{
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the song with the given id to the list of previously played songs
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
history_add(uint32_t id)
|
||||||
|
{
|
||||||
|
unsigned int next_index = (history->start_index + history->count) % MAX_HISTORY_COUNT;
|
||||||
|
if (next_index == history->start_index && history->count > 0)
|
||||||
|
history->start_index = (history->start_index + 1) % MAX_HISTORY_COUNT;
|
||||||
|
|
||||||
|
history->buffer[next_index] = id;
|
||||||
|
|
||||||
|
if (history->count < MAX_HISTORY_COUNT)
|
||||||
|
history->count++;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
source_read(uint8_t *buf, int len, uint64_t rtptime)
|
source_read(uint8_t *buf, int len, uint64_t rtptime)
|
||||||
{
|
{
|
||||||
@ -1447,17 +1471,20 @@ source_read(uint8_t *buf, int len, uint64_t rtptime)
|
|||||||
nbytes = 0;
|
nbytes = 0;
|
||||||
new = 0;
|
new = 0;
|
||||||
while (nbytes < len)
|
while (nbytes < len)
|
||||||
|
{
|
||||||
|
if (new)
|
||||||
{
|
{
|
||||||
if (new)
|
DPRINTF(E_DBG, L_PLAYER, "New file\n");
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_PLAYER, "New file\n");
|
|
||||||
|
|
||||||
new = 0;
|
new = 0;
|
||||||
|
|
||||||
ret = source_next(0);
|
// add song to the played history
|
||||||
if (ret < 0)
|
history_add(cur_streaming->id);
|
||||||
return -1;
|
|
||||||
}
|
ret = source_next(0);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(audio_buf) == 0)
|
if (EVBUFFER_LENGTH(audio_buf) == 0)
|
||||||
{
|
{
|
||||||
@ -2268,9 +2295,15 @@ playback_stop(struct player_command *cmd)
|
|||||||
pb_timer_fd = -1;
|
pb_timer_fd = -1;
|
||||||
|
|
||||||
if (cur_playing)
|
if (cur_playing)
|
||||||
|
{
|
||||||
|
history_add(cur_playing->id);
|
||||||
source_stop(cur_playing);
|
source_stop(cur_playing);
|
||||||
else
|
}
|
||||||
|
else if (cur_streaming)
|
||||||
|
{
|
||||||
|
history_add(cur_streaming->id);
|
||||||
source_stop(cur_streaming);
|
source_stop(cur_streaming);
|
||||||
|
}
|
||||||
|
|
||||||
cur_playing = NULL;
|
cur_playing = NULL;
|
||||||
cur_streaming = NULL;
|
cur_streaming = NULL;
|
||||||
@ -2589,9 +2622,15 @@ playback_prev_bh(struct player_command *cmd)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (cur_playing)
|
if (cur_playing)
|
||||||
|
{
|
||||||
|
history_add(cur_playing->id);
|
||||||
source_stop(cur_playing);
|
source_stop(cur_playing);
|
||||||
else
|
}
|
||||||
|
else if (cur_streaming)
|
||||||
|
{
|
||||||
|
history_add(cur_streaming->id);
|
||||||
source_stop(cur_streaming);
|
source_stop(cur_streaming);
|
||||||
|
}
|
||||||
|
|
||||||
ret = source_prev();
|
ret = source_prev();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -2621,9 +2660,15 @@ playback_next_bh(struct player_command *cmd)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (cur_playing)
|
if (cur_playing)
|
||||||
|
{
|
||||||
|
history_add(cur_playing->id);
|
||||||
source_stop(cur_playing);
|
source_stop(cur_playing);
|
||||||
else
|
}
|
||||||
|
else if (cur_streaming)
|
||||||
|
{
|
||||||
|
history_add(cur_streaming->id);
|
||||||
source_stop(cur_streaming);
|
source_stop(cur_streaming);
|
||||||
|
}
|
||||||
|
|
||||||
ret = source_next(1);
|
ret = source_next(1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -4429,6 +4474,8 @@ player_init(void)
|
|||||||
|
|
||||||
update_handler = NULL;
|
update_handler = NULL;
|
||||||
|
|
||||||
|
history = (struct player_history *) calloc(1, sizeof(struct player_history));
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
/*
|
/*
|
||||||
* Determine if the resolution of the system timer is > or < the size
|
* Determine if the resolution of the system timer is > or < the size
|
||||||
@ -4620,6 +4667,8 @@ player_deinit(void)
|
|||||||
if (source_head)
|
if (source_head)
|
||||||
queue_clear(NULL);
|
queue_clear(NULL);
|
||||||
|
|
||||||
|
free(history);
|
||||||
|
|
||||||
evbuffer_free(audio_buf);
|
evbuffer_free(audio_buf);
|
||||||
|
|
||||||
laudio_deinit();
|
laudio_deinit();
|
||||||
|
19
src/player.h
19
src/player.h
@ -21,6 +21,9 @@
|
|||||||
#define STOB(s) ((s) * 4)
|
#define STOB(s) ((s) * 4)
|
||||||
#define BTOS(b) ((b) / 4)
|
#define BTOS(b) ((b) / 4)
|
||||||
|
|
||||||
|
/* Maximum number of previously played songs that are remembered */
|
||||||
|
#define MAX_HISTORY_COUNT 20
|
||||||
|
|
||||||
enum play_status {
|
enum play_status {
|
||||||
PLAY_STOPPED = 2,
|
PLAY_STOPPED = 2,
|
||||||
PLAY_PAUSED = 3,
|
PLAY_PAUSED = 3,
|
||||||
@ -84,6 +87,19 @@ struct player_source
|
|||||||
struct player_source *play_next;
|
struct player_source *play_next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct player_history
|
||||||
|
{
|
||||||
|
/* Buffer index of the oldest remembered song */
|
||||||
|
unsigned int start_index;
|
||||||
|
|
||||||
|
/* Count of song ids in the buffer */
|
||||||
|
unsigned int count;
|
||||||
|
|
||||||
|
/* Circular buffer of song ids previously played by forked-daapd */
|
||||||
|
uint32_t buffer[MAX_HISTORY_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@ -160,6 +176,9 @@ player_queue_clear(void);
|
|||||||
void
|
void
|
||||||
player_queue_plid(uint32_t plid);
|
player_queue_plid(uint32_t plid);
|
||||||
|
|
||||||
|
struct player_history *
|
||||||
|
player_history_get(void);
|
||||||
|
|
||||||
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