Use queue db table instead of in memory queue struct

This commit is contained in:
chme 2016-11-08 21:27:38 +01:00
parent 8ebf2f9307
commit c504abe451
7 changed files with 715 additions and 1184 deletions

View File

@ -2022,6 +2022,31 @@ db_files_get_count_bymatch(char *path)
#undef Q_TMPL
}
int
db_file_get_seekpos(uint32_t id)
{
#define Q_TMPL "SELECT seek FROM files f WHERE f.id = %d;"
char *query;
int seek_ms;
query = sqlite3_mprintf(Q_TMPL, id);
if (!query)
{
DPRINTF(E_LOG, L_DB, "Out of memory making seekpos query string.\n");
return -1;
}
seek_ms = db_get_count(query);
sqlite3_free(query);
if (seek_ms < 0)
seek_ms = 0;
return seek_ms;
#undef Q_TMPL
}
void
db_files_update_songartistid(void)
{

View File

@ -507,6 +507,9 @@ db_files_get_album_count(void);
int
db_files_get_count_bymatch(char *path);
int
db_file_get_seekpos(uint32_t id);
void
db_files_update_songartistid(void);

View File

@ -1227,6 +1227,7 @@ bulk_scan(int flags)
DPRINTF(E_DBG, L_SCAN, "Purging old database content\n");
db_purge_cruft(start);
db_queue_cleanup();
cache_artwork_purge_cruft(start);
DPRINTF(E_LOG, L_SCAN, "Bulk library scan completed in %.f sec\n", difftime(end, start));
@ -1244,6 +1245,7 @@ bulk_scan(int flags)
static void *
filescanner(void *arg)
{
int clear_queue_on_stop_disabled;
int ret;
#if defined(__linux__)
struct sched_param param;
@ -1284,6 +1286,19 @@ filescanner(void *arg)
pthread_exit(NULL);
}
// Only clear the queue if enabled (default) in config
clear_queue_on_stop_disabled = cfg_getbool(cfg_getsec(cfg, "mpd"), "clear_queue_on_stop_disable");
if (!clear_queue_on_stop_disabled)
{
ret = db_queue_clear();
if (ret < 0)
{
DPRINTF(E_LOG, L_SCAN, "Error: could not clear queue from DB\n");
pthread_exit(NULL);
}
}
/* Recompute all songartistids and songalbumids, in case the SQLite DB got transferred
* to a different host; the hash is not portable.
* It will also rebuild the groups we just cleared.
@ -1937,7 +1952,7 @@ filescanner_fullrescan(void *arg, int *retval)
DPRINTF(E_LOG, L_SCAN, "Full rescan triggered\n");
player_playback_stop();
player_queue_clear();
db_queue_clear();
inofd_event_unset(); // Clears all inotify watches
db_purge_all(); // Clears files, playlists, playlistitems, inotify and groups

View File

@ -51,7 +51,6 @@
#include "db.h"
#include "daap_query.h"
#include "player.h"
#include "queue.h"
#include "listener.h"
/* httpd event base, from httpd.c */
@ -76,7 +75,7 @@ struct dacp_update_request {
struct dacp_update_request *next;
};
typedef void (*dacp_propget)(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
typedef void (*dacp_propget)(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
typedef void (*dacp_propset)(const char *value, struct evkeyvalq *query);
struct dacp_prop_map {
@ -88,40 +87,40 @@ struct dacp_prop_map {
/* Forward - properties getters */
static void
dacp_propget_volume(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_volume(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_volumecontrollable(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_volumecontrollable(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_playerstate(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_playerstate(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_shufflestate(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_shufflestate(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_availableshufflestates(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_availableshufflestates(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_repeatstate(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_repeatstate(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_availablerepeatstates(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_availablerepeatstates(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_playingtime(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_playingtime(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_fullscreenenabled(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_fullscreenenabled(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_fullscreen(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_fullscreen(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_visualizerenabled(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_visualizerenabled(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_visualizer(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_visualizer(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_itms_songid(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_itms_songid(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_haschapterdata(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_haschapterdata(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_mediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_mediakind(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
static void
dacp_propget_extendedmediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi);
dacp_propget_extendedmediakind(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item);
/* Forward - properties setters */
static void
@ -165,16 +164,25 @@ static struct media_file_info dummy_mfi =
.album = "(unknown album)",
.genre = "(unknown genre)",
};
static struct db_queue_item dummy_queue_item =
{
.file_id = 9999999,
.title = "(unknown title)",
.artist = "(unknown artist)",
.album = "(unknown album)",
.genre = "(unknown genre)",
};
/* DACP helpers */
static void
dacp_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
uint32_t id;
int64_t songalbumid;
int pos_pl;
if ((status->status == PLAY_STOPPED) || !mfi)
if ((status->status == PLAY_STOPPED) || !queue_item)
return;
/* Send bogus id's if playing internet radio, because clients like
@ -183,44 +191,46 @@ dacp_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct med
* FIXME: Giving the client invalid ids on purpose is hardly ideal, but the
* clients don't seem to use these ids for anything other than rating.
*/
if (mfi->data_kind == DATA_KIND_HTTP)
if (queue_item->data_kind == DATA_KIND_HTTP)
{
id = djb_hash(mfi->album, strlen(mfi->album));
id = djb_hash(queue_item->album, strlen(queue_item->album));
songalbumid = (int64_t)id;
}
else
{
id = status->id;
songalbumid = mfi->songalbumid;
songalbumid = queue_item->songalbumid;
}
pos_pl = db_queue_get_pos(status->item_id, 0);
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, pos_pl);
dmap_add_raw_uint32(evbuf, id);
dmap_add_string(evbuf, "cann", mfi->title);
dmap_add_string(evbuf, "cana", mfi->artist);
dmap_add_string(evbuf, "canl", mfi->album);
dmap_add_string(evbuf, "cang", mfi->genre);
dmap_add_string(evbuf, "cann", queue_item->title);
dmap_add_string(evbuf, "cana", queue_item->artist);
dmap_add_string(evbuf, "canl", queue_item->album);
dmap_add_string(evbuf, "cang", queue_item->genre);
dmap_add_long(evbuf, "asai", songalbumid);
dmap_add_int(evbuf, "cmmk", 1);
}
static void
dacp_playingtime(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_playingtime(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
if ((status->status == PLAY_STOPPED) || !mfi)
if ((status->status == PLAY_STOPPED) || !queue_item)
return;
if (mfi->song_length)
dmap_add_int(evbuf, "cant", mfi->song_length - status->pos_ms); /* Remaining time in ms */
if (queue_item->song_length)
dmap_add_int(evbuf, "cant", queue_item->song_length - status->pos_ms); /* Remaining time in ms */
else
dmap_add_int(evbuf, "cant", 0); /* Unknown remaining time */
dmap_add_int(evbuf, "cast", mfi->song_length); /* Song length in ms */
dmap_add_int(evbuf, "cast", queue_item->song_length); /* Song length in ms */
}
@ -229,7 +239,7 @@ static int
make_playstatusupdate(struct evbuffer *evbuf)
{
struct player_status status;
struct media_file_info *mfi;
struct db_queue_item *queue_item = NULL;
struct evbuffer *psu;
int ret;
@ -245,16 +255,14 @@ make_playstatusupdate(struct evbuffer *evbuf)
if (status.status != PLAY_STOPPED)
{
mfi = db_file_fetch_byid(status.id);
if (!mfi)
queue_item = db_queue_fetch_byitemid(status.item_id);
if (!queue_item)
{
DPRINTF(E_LOG, L_DACP, "Could not fetch file id %d\n", status.id);
DPRINTF(E_LOG, L_DACP, "Could not fetch item id %d (file id %d)\n", status.item_id, status.id);
mfi = &dummy_mfi;
queue_item = &dummy_queue_item;
}
}
else
mfi = NULL;
dmap_add_int(psu, "mstt", 200); /* 12 */
@ -271,19 +279,19 @@ make_playstatusupdate(struct evbuffer *evbuf)
dmap_add_char(psu, "cafe", 0); /* 9 */ /* dacp.fullscreenenabled */
dmap_add_char(psu, "cave", 0); /* 9 */ /* dacp.visualizerenabled */
if (mfi)
if (queue_item)
{
dacp_nowplaying(psu, &status, mfi);
dacp_nowplaying(psu, &status, queue_item);
dmap_add_int(psu, "casa", 1); /* 12 */ /* unknown */
dmap_add_int(psu, "astm", mfi->song_length);
dmap_add_int(psu, "astm", queue_item->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);
dacp_playingtime(psu, &status, queue_item);
if (mfi != &dummy_mfi)
free_mfi(mfi, 0);
if (queue_item != &dummy_queue_item)
free_queue_item(queue_item, 0);
}
dmap_add_char(psu, "casu", 1); /* 9 */ /* unknown */
@ -448,103 +456,103 @@ update_fail_cb(struct evhttp_connection *evcon, void *arg)
/* Properties getters */
static void
dacp_propget_volume(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_volume(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dmap_add_int(evbuf, "cmvo", status->volume);
}
static void
dacp_propget_volumecontrollable(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_volumecontrollable(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dmap_add_char(evbuf, "cavc", 1);
}
static void
dacp_propget_playerstate(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_playerstate(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dmap_add_char(evbuf, "caps", status->status);
}
static void
dacp_propget_shufflestate(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_shufflestate(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dmap_add_char(evbuf, "cash", status->shuffle);
}
static void
dacp_propget_availableshufflestates(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_availableshufflestates(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dmap_add_int(evbuf, "caas", 2);
}
static void
dacp_propget_repeatstate(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_repeatstate(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dmap_add_char(evbuf, "carp", status->repeat);
}
static void
dacp_propget_availablerepeatstates(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_availablerepeatstates(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dmap_add_int(evbuf, "caar", 6);
}
static void
dacp_propget_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dacp_nowplaying(evbuf, status, mfi);
dacp_nowplaying(evbuf, status, queue_item);
}
static void
dacp_propget_playingtime(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_playingtime(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
dacp_playingtime(evbuf, status, mfi);
dacp_playingtime(evbuf, status, queue_item);
}
static void
dacp_propget_fullscreenenabled(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_fullscreenenabled(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
static void
dacp_propget_fullscreen(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_fullscreen(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
static void
dacp_propget_visualizerenabled(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_visualizerenabled(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
static void
dacp_propget_visualizer(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_visualizer(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
static void
dacp_propget_itms_songid(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_itms_songid(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
static void
dacp_propget_haschapterdata(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_haschapterdata(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
static void
dacp_propget_mediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_mediakind(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
static void
dacp_propget_extendedmediakind(struct evbuffer *evbuf, struct player_status *status, struct media_file_info *mfi)
dacp_propget_extendedmediakind(struct evbuffer *evbuf, struct player_status *status, struct db_queue_item *queue_item)
{
// TODO
}
@ -845,20 +853,19 @@ find_first_song_id(const char *query)
static int
dacp_queueitem_make(struct queue_item **head, const char *query, const char *queuefilter, const char *sort, int quirk)
dacp_queueitem_add(const char *query, const char *queuefilter, const char *sort, int quirk, int mode)
{
struct media_file_info *mfi;
struct query_params qp;
struct queue_item *items;
int64_t albumid;
int64_t artistid;
int plid;
int id;
int idx;
int ret;
int len;
char *s;
char buf[1024];
struct player_status status;
if (query)
{
@ -965,36 +972,34 @@ dacp_queueitem_make(struct queue_item **head, const char *query, const char *que
qp.sort = S_ARTIST;
}
items = queueitem_make_byquery(&qp);
player_get_status(&status);
if (mode == 3)
ret = db_queue_add_by_queryafteritemid(&qp, status.item_id);
else
ret = db_queue_add_by_query(&qp, status.shuffle, status.item_id);
if (qp.filter)
free(qp.filter);
if (items)
*head = items;
else
if (ret < 0)
return -1;
// Get the position (0-based) of the first item
idx = queueitem_pos(items, id);
return idx;
return id;
}
static void
dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
struct player_status status;
struct queue_item *items;
const char *sort;
const char *cuequery;
const char *param;
uint32_t id;
uint32_t item_id;
uint32_t pos;
int clear;
struct db_queue_item *queue_item = NULL;
struct player_history *history;
int hist;
int ret;
/* /cue?command=play&query=...&sort=...&index=N */
@ -1009,18 +1014,16 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
{
player_playback_stop();
player_queue_clear();
db_queue_clear();
}
}
player_get_status(&status);
cuequery = evhttp_find_header(query, "query");
if (cuequery)
{
sort = evhttp_find_header(query, "sort");
ret = dacp_queueitem_make(&items, cuequery, NULL, sort, 0);
ret = dacp_queueitem_add(cuequery, NULL, sort, 0, 0);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not build song queue\n");
@ -1028,11 +1031,11 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
dmap_send_error(req, "cacr", "Could not build song queue");
return;
}
player_queue_add(items, NULL);
}
else
{
player_get_status(&status);
if (status.status != PLAY_STOPPED)
player_playback_stop();
}
@ -1041,7 +1044,6 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
if (param)
dacp_propset_shufflestate(param, NULL);
id = 0;
item_id = 0;
pos = 0;
param = evhttp_find_header(query, "index");
@ -1053,7 +1055,6 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
}
/* If selection was from Up Next queue or history queue (command will be playnow), then index is relative */
hist = 0;
if ((param = evhttp_find_header(query, "command")) && (strcmp(param, "playnow") == 0))
{
/* If mode parameter is -1 or 1, the index is relative to the history queue, otherwise to the Up Next queue */
@ -1061,12 +1062,20 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
if (param && ((strcmp(param, "-1") == 0) || (strcmp(param, "1") == 0)))
{
/* Play from history queue */
hist = 1;
history = player_history_get();
if (history->count > pos)
{
pos = (history->start_index + history->count - pos - 1) % MAX_HISTORY_COUNT;
item_id = history->item_id[pos];
queue_item = db_queue_fetch_byitemid(item_id);
if (!queue_item)
{
DPRINTF(E_LOG, L_DACP, "Could not start playback from history\n");
dmap_send_error(req, "cacr", "Playback failed to start");
return;
}
}
else
{
@ -1079,19 +1088,22 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
else
{
/* Play from Up Next queue */
pos += status.pos_pl;
if (status.status == PLAY_STOPPED && pos > 0)
pos--;
queue_item = db_queue_fetch_byposrelativetoitem(pos, status.item_id, status.shuffle);
if (!queue_item)
{
DPRINTF(E_LOG, L_DACP, "Could not fetch item from queue: pos=%d, now playing=%d\n", pos, status.item_id);
dmap_send_error(req, "cacr", "Playback failed to start");
return;
}
}
}
/* If playing from history queue, the pos holds the id of the item to play */
if (hist)
ret = player_playback_start_byitemid(item_id, &id);
else
ret = player_playback_start_bypos(pos, &id);
ret = player_playback_start_byitem(queue_item);
free_queue_item(queue_item, 0);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not start playback\n");
@ -1100,9 +1112,11 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u
return;
}
player_get_status(&status);
dmap_add_container(evbuf, "cacr", 24); /* 8 + len */
dmap_add_int(evbuf, "mstt", 200); /* 12 */
dmap_add_int(evbuf, "miid", id); /* 12 */
dmap_add_int(evbuf, "miid", status.id);/* 12 */
httpd_send_reply(req, HTTP_OK, "OK", evbuf, 0);
}
@ -1114,7 +1128,7 @@ dacp_reply_cue_clear(struct evhttp_request *req, struct evbuffer *evbuf, char **
player_playback_stop();
player_queue_clear();
db_queue_clear();
dmap_add_container(evbuf, "cacr", 24); /* 8 + len */
dmap_add_int(evbuf, "mstt", 200); /* 12 */
@ -1159,13 +1173,12 @@ static void
dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
struct player_status status;
struct queue_item *items;
struct daap_session *s;
const char *param;
const char *shuffle;
uint32_t plid;
uint32_t id;
int pos;
struct db_queue_item *queue_item = NULL;
int ret;
/* /ctrl-int/1/playspec?database-spec='dmap.persistentid:0x1'&container-spec='dmap.persistentid:0x5'&container-item-spec='dmap.containeritemid:0x9'
@ -1241,40 +1254,41 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u
DPRINTF(E_DBG, L_DACP, "Playspec request for playlist %d, start song id %d%s\n", plid, id, (shuffle) ? ", shuffle" : "");
items = NULL;
if (plid > 0)
items = queueitem_make_byplid(plid);
else if (id > 0)
items = queueitem_make_byid(id);
player_get_status(&status);
if (!items)
if (status.status != PLAY_STOPPED)
player_playback_stop();
db_queue_clear();
if (plid > 0)
ret = db_queue_add_by_playlistid(plid, status.shuffle, status.item_id);
else if (id > 0)
ret = db_queue_add_by_fileid(id, status.shuffle, status.item_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not build song queue from playlist %d\n", plid);
goto out_fail;
}
pos = queueitem_pos(items, id);
if (pos < 0)
{
DPRINTF(E_DBG, L_DACP, "No item with %d found in queue\n", id);
pos = 0;
}
DPRINTF(E_DBG, L_DACP, "Playspec start song index is %d\n", pos);
player_get_status(&status);
if (status.status != PLAY_STOPPED)
player_playback_stop();
player_queue_clear();
player_queue_add(items, NULL);
player_queue_plid(plid);
if (shuffle)
dacp_propset_shufflestate(shuffle, NULL);
ret = player_playback_start_bypos(pos, NULL);
if (id > 0)
queue_item = db_queue_fetch_byfileid(id);
if (queue_item)
{
ret = player_playback_start_byitem(queue_item);
free_queue_item(queue_item, 0);
}
else
ret = player_playback_start(NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not start playback\n");
@ -1500,6 +1514,44 @@ playqueuecontents_add_source(struct evbuffer *songlist, uint32_t source_id, int
return 0;
}
static int
playqueuecontents_add_queue_item(struct evbuffer *songlist, struct db_queue_item *queue_item, int pos_in_queue, uint32_t plid)
{
struct evbuffer *song;
int ret;
song = evbuffer_new();
if (!song)
{
DPRINTF(E_LOG, L_DACP, "Could not allocate song evbuffer for playqueue-contents\n");
return -1;
}
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, queue_item->file_id);
dmap_add_string(song, "ceQn", queue_item->title);
dmap_add_string(song, "ceQr", queue_item->artist);
dmap_add_string(song, "ceQa", queue_item->album);
dmap_add_string(song, "ceQg", queue_item->genre);
dmap_add_long(song, "asai", queue_item->songalbumid);
dmap_add_int(song, "cmmk", queue_item->media_kind);
dmap_add_int(song, "casa", 1); /* Unknown */
dmap_add_int(song, "astm", queue_item->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_get_length(song));
ret = evbuffer_add_buffer(songlist, song);
evbuffer_free(song);
return ret;
}
static void
dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, char **uri,
struct evkeyvalq *query)
@ -1509,17 +1561,15 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
struct evbuffer *playlists;
struct player_status status;
struct player_history *history;
struct queue *queue;
struct queue_item *item;
const char *param;
size_t songlist_length;
size_t playlist_length;
int span;
int count;
int i;
int n;
int ret;
int start_index;
struct db_queue_enum queue_enum;
struct db_queue_item queue_item;
/* /ctrl-int/1/playqueue-contents?span=50&session-id=... */
@ -1538,7 +1588,7 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
DPRINTF(E_LOG, L_DACP, "Invalid span value in playqueue-contents request\n");
}
n = 0; // count of songs in songlist
count = 0; // count of songs in songlist
songlist = evbuffer_new();
if (!songlist)
{
@ -1548,8 +1598,6 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
return;
}
player_get_status(&status);
/*
* 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.
@ -1565,9 +1613,9 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
{
start_index = (history->start_index + history->count - abs(span)) % MAX_HISTORY_COUNT;
}
for (n = 0; n < history->count && n < abs(span); n++)
for (count = 0; count < history->count && count < abs(span); count++)
{
ret = playqueuecontents_add_source(songlist, history->id[(start_index + n) % MAX_HISTORY_COUNT], (n + 1), status.plid);
ret = playqueuecontents_add_source(songlist, history->id[(start_index + count) % MAX_HISTORY_COUNT], (count + 1), status.plid);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not add song to songlist for playqueue-contents\n");
@ -1579,15 +1627,21 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
}
else
{
queue = player_queue_get_bypos(abs(span));
if (queue)
player_get_status(&status);
memset(&queue_enum, 0, sizeof(struct db_queue_enum));
if (status.shuffle)
queue_enum.orderby_shufflepos = 1;
ret = db_queue_enum_start(&queue_enum);
count = 0; //FIXME [queue] Check count value
while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0)
{
i = 0;
count = queue_count(queue);
for (n = 0; (n < count) && (n < abs(span)); n++)
if (status.item_id == 0 || status.item_id == queue_item.item_id)
count = 1;
else if (count > 0)
{
item = queue_get_byindex(queue, n, 0);
ret = playqueuecontents_add_source(songlist, queueitem_id(item), (n + i + 1), status.plid);
ret = playqueuecontents_add_queue_item(songlist, &queue_item, count, status.plid);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not add song to songlist for playqueue-contents\n");
@ -1595,9 +1649,13 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
dmap_send_error(req, "ceQR", "Out of memory");
return;
}
count++;
}
queue_free(queue);
}
db_queue_enum_end(&queue_enum);
sqlite3_free(queue_enum.filter);
}
/* Playlists are hist, curr and main. */
@ -1628,7 +1686,7 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
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_int(playlists, "ceQm", count); /* 12 */
dmap_add_string(playlists, "ceQl", "Up Next"); /* 15 = 8 + 7 */
dmap_add_string(playlists, "ceQh", "from Music"); /* 18 = 8 + 10 */
@ -1640,10 +1698,10 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf,
/* Final construction of reply */
playlist_length = evbuffer_get_length(playlists);
dmap_add_container(evbuf, "ceQR", 79 + playlist_length + 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_int(evbuf, "mstt", 200); /* 12, dmap.status */
dmap_add_int(evbuf, "mtco", abs(span)); /* 12 */
dmap_add_int(evbuf, "mrco", count); /* 12 */
dmap_add_char(evbuf, "ceQu", 0); /* 9 */
dmap_add_container(evbuf, "mlcl", 8 + playlist_length + songlist_length); /* 8 */
dmap_add_container(evbuf, "ceQS", playlist_length); /* 8 */
ret = evbuffer_add_buffer(evbuf, playlists);
@ -1691,7 +1749,7 @@ dacp_reply_playqueueedit_clear(struct evhttp_request *req, struct evbuffer *evbu
if (strcmp(param,"0x68697374") == 0)
player_queue_clear_history();
else
player_queue_clear();
db_queue_clear();
dmap_add_container(evbuf, "cacr", 24); /* 8 + len */
dmap_add_int(evbuf, "mstt", 200); /* 12 */
@ -1712,7 +1770,6 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf,
//?command=add&query='dmap.itemid:2'&query-modifier=containers&sort=name&mode=2&session-id=100
// -> mode 2: stop playblack, clear playqueue, add shuffled songs from playlist=itemid to playqueue
struct queue_item *items;
const char *editquery;
const char *queuefilter;
const char *querymodifier;
@ -1724,6 +1781,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf,
int plid;
int ret;
int quirkyquery;
struct db_queue_item *queue_item;
mode = 1;
@ -1743,67 +1801,54 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf,
if ((mode == 1) || (mode == 2))
{
player_playback_stop();
player_queue_clear();
db_queue_clear();
}
editquery = evhttp_find_header(query, "query");
if (editquery)
if (!editquery)
{
sort = evhttp_find_header(query, "sort");
DPRINTF(E_LOG, L_DACP, "Could not add song queue, DACP query missing\n");
// 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";
}
dmap_send_error(req, "cacr", "Invalid request");
return;
}
// only use queryfilter if mode is not equal 0 (add to up next), 3 (play next) or 5 (add to up next)
queuefilter = (mode == 0 || mode == 3 || mode == 5) ? NULL : evhttp_find_header(query, "queuefilter");
sort = evhttp_find_header(query, "sort");
querymodifier = evhttp_find_header(query, "query-modifier");
if (!querymodifier || (strcmp(querymodifier, "containers") != 0))
{
quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)"));
ret = dacp_queueitem_make(&items, editquery, queuefilter, sort, quirkyquery);
}
else
{
// Modify the query: Take the id from the editquery and use it as a queuefilter playlist id
ret = safe_atoi32(strchr(editquery, ':') + 1, &plid);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Invalid playlist id in request: %s\n", editquery);
// 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";
}
dmap_send_error(req, "cacr", "Invalid request");
return;
}
snprintf(modifiedquery, sizeof(modifiedquery), "playlist:%d", plid);
ret = dacp_queueitem_make(&items, NULL, modifiedquery, sort, 0);
}
// only use queryfilter if mode is not equal 0 (add to up next), 3 (play next) or 5 (add to up next)
queuefilter = (mode == 0 || mode == 3 || mode == 5) ? NULL : evhttp_find_header(query, "queuefilter");
querymodifier = evhttp_find_header(query, "query-modifier");
if (!querymodifier || (strcmp(querymodifier, "containers") != 0))
{
quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)"));
ret = dacp_queueitem_add(editquery, queuefilter, sort, quirkyquery, mode);
}
else
{
// Modify the query: Take the id from the editquery and use it as a queuefilter playlist id
ret = safe_atoi32(strchr(editquery, ':') + 1, &plid);
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not build song queue\n");
{
DPRINTF(E_LOG, L_DACP, "Invalid playlist id in request: %s\n", editquery);
dmap_send_error(req, "cacr", "Invalid request");
return;
}
idx = ret;
if (mode == 3)
{
player_queue_add_next(items);
}
else
{
player_queue_add(items, NULL);
}
snprintf(modifiedquery, sizeof(modifiedquery), "playlist:%d", plid);
ret = dacp_queueitem_add(NULL, modifiedquery, sort, 0, mode);
}
else
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not add song queue, DACP query missing\n");
DPRINTF(E_LOG, L_DACP, "Could not build song queue\n");
dmap_send_error(req, "cacr", "Invalid request");
return;
@ -1812,11 +1857,27 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf,
if (mode == 2)
{
player_shuffle_set(1);
idx = 0;
ret = 0;
}
idx = 0;
queue_item = NULL;
if (ret > 0)
{
queue_item = db_queue_fetch_byfileid(ret);
}
DPRINTF(E_DBG, L_DACP, "Song queue built, playback starting at index %" PRIu32 "\n", idx);
ret = player_playback_start_bypos(idx, NULL);
if (queue_item)
{
ret = player_playback_start_byitem(queue_item);
free_queue_item(queue_item, 0);
}
else
{
ret = player_playback_start(NULL);
}
if (ret < 0)
{
DPRINTF(E_LOG, L_DACP, "Could not start playback\n");
@ -1840,6 +1901,7 @@ dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf
* 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).
*/
struct player_status status;
int ret;
const char *param;
@ -1867,7 +1929,8 @@ dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf
return;
}
player_queue_move_bypos(src, dst);
player_get_status(&status);
db_queue_move_byposrelativetoitem(src, dst, status.item_id, status.shuffle);
}
/* 204 No Content is the canonical reply */
@ -1882,6 +1945,7 @@ dacp_reply_playqueueedit_remove(struct evhttp_request *req, struct evbuffer *evb
* Exampe request (removes song at position 1 in the playqueue):
* ?command=remove&items=1&session-id=100
*/
struct player_status status;
int ret;
const char *param;
@ -1899,7 +1963,9 @@ dacp_reply_playqueueedit_remove(struct evhttp_request *req, struct evbuffer *evb
return;
}
player_queue_remove_bypos(item_index);
player_get_status(&status);
db_queue_delete_byposrelativetoitem(item_index, status.item_id, status.shuffle);
}
/* 204 No Content is the canonical reply */
@ -2150,7 +2216,7 @@ dacp_reply_getproperty(struct evhttp_request *req, struct evbuffer *evbuf, char
struct player_status status;
struct daap_session *s;
const struct dacp_prop_map *dpm;
struct media_file_info *mfi;
struct db_queue_item *queue_item = NULL;
struct evbuffer *proplist;
const char *param;
char *ptr;
@ -2194,17 +2260,15 @@ dacp_reply_getproperty(struct evhttp_request *req, struct evbuffer *evbuf, char
if (status.status != PLAY_STOPPED)
{
mfi = db_file_fetch_byid(status.id);
if (!mfi)
queue_item = db_queue_fetch_byitemid(status.item_id);
if (!queue_item)
{
DPRINTF(E_LOG, L_DACP, "Could not fetch file id %d\n", status.id);
DPRINTF(E_LOG, L_DACP, "Could not fetch queue_item for item-id %d\n", status.item_id);
dmap_send_error(req, "cmgt", "Server error");
goto out_free_proplist;
}
}
else
mfi = NULL;
prop = strtok_r(propstr, ",", &ptr);
while (prop)
@ -2213,7 +2277,7 @@ dacp_reply_getproperty(struct evhttp_request *req, struct evbuffer *evbuf, char
if (dpm)
{
if (dpm->propget)
dpm->propget(proplist, &status, mfi);
dpm->propget(proplist, &status, queue_item);
else
DPRINTF(E_WARN, L_DACP, "No getter method for DACP property %s\n", prop);
}
@ -2225,8 +2289,8 @@ dacp_reply_getproperty(struct evhttp_request *req, struct evbuffer *evbuf, char
free(propstr);
if (mfi)
free_mfi(mfi, 0);
if (queue_item)
free_queue_item(queue_item, 0);
len = evbuffer_get_length(proplist);
dmap_add_container(evbuf, "cmgt", 12 + len);

411
src/mpd.c
View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2010 Julien BLACHE <jb@jblache.org>
* Copyright (C) 2016 Christian Meffert <christian.meffert@googlemail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -61,7 +61,6 @@
#include "artwork.h"
#include "player.h"
#include "queue.h"
#include "filescanner.h"
#include "commands.h"
@ -396,18 +395,16 @@ mpd_parse_args(char *args, int *argc, char **argv)
* Id: 1
*
* @param evbuf the response event buffer
* @param mfi media information
* @param item_id queue-item id
* @param pos_pl position in the playqueue, if -1 the position is ignored
* @param queue_item queue item information
* @return the number of bytes added if successful, or -1 if an error occurred.
*/
static int
mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, unsigned int item_id, int pos_pl)
mpd_add_db_queue_item(struct evbuffer *evbuf, struct db_queue_item *queue_item)
{
char modified[32];
int ret;
mpd_time(modified, sizeof(modified), mfi->time_modified);
mpd_time(modified, sizeof(modified), queue_item->time_modified);
ret = evbuffer_add_printf(evbuf,
"file: %s\n"
@ -422,65 +419,28 @@ mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, unsigned
"Track: %d\n"
"Date: %d\n"
"Genre: %s\n"
"Disc: %d\n",
(mfi->virtual_path + 1),
"Disc: %d\n"
"Pos: %d\n"
"Id: %d\n",
(queue_item->virtual_path + 1),
modified,
(mfi->song_length / 1000),
mfi->artist,
mfi->album_artist,
mfi->artist_sort,
mfi->album_artist_sort,
mfi->album,
mfi->title,
mfi->track,
mfi->year,
mfi->genre,
mfi->disc);
if (ret >= 0 && pos_pl >= 0)
{
ret = evbuffer_add_printf(evbuf,
"Pos: %d\n",
pos_pl);
if (ret >= 0)
{
ret = evbuffer_add_printf(evbuf,
"Id: %d\n",
item_id);
}
}
(queue_item->song_length / 1000),
queue_item->artist,
queue_item->album_artist,
queue_item->artist_sort,
queue_item->album_artist_sort,
queue_item->album,
queue_item->title,
queue_item->track,
queue_item->year,
queue_item->genre,
queue_item->disc,
queue_item->pos,
queue_item->item_id);
return ret;
}
static int
mpd_add_mediainfo_byid(struct evbuffer *evbuf, int id, unsigned int item_id, int pos_pl)
{
struct media_file_info *mfi;
int ret;
mfi = db_file_fetch_byid(id);
if (!mfi)
{
DPRINTF(E_LOG, L_MPD, "Error fetching file by id: %d\n", id);
return -1;
}
ret = mpd_add_mediainfo(evbuf, mfi, item_id, pos_pl);
if (ret < 0)
{
DPRINTF(E_LOG, L_MPD, "Error adding media info for file with id: %d\n", id);
free_mfi(mfi, 0);
return -1;
}
free_mfi(mfi, 0);
return 0;
}
/*
* Adds the informations (path, id, tags, etc.) for the given song to the given buffer.
*
@ -568,6 +528,7 @@ mpd_command_currentsong(struct evbuffer *evbuf, int argc, char **argv, char **er
{
struct player_status status;
struct db_queue_item *queue_item;
int ret;
player_get_status(&status);
@ -578,7 +539,20 @@ mpd_command_currentsong(struct evbuffer *evbuf, int argc, char **argv, char **er
return 0;
}
ret = mpd_add_mediainfo_byid(evbuf, status.id, status.item_id, status.pos_pl);
queue_item = db_queue_fetch_byitemid(status.item_id);
if (!queue_item)
{
ret = asprintf(errmsg, "Error adding queue item info for file with id: %d", status.item_id);
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
ret = mpd_add_db_queue_item(evbuf, queue_item);
free_queue_item(queue_item, 0);
if (ret < 0)
{
ret = asprintf(errmsg, "Error adding media info for file with id: %d", status.id);
@ -717,7 +691,11 @@ static int
mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct player_status status;
int queue_length;
int queue_version;
char *state;
int pos_pl;
struct db_queue_item *next_item;
player_get_status(&status);
@ -736,6 +714,9 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
break;
}
queue_version = db_queue_get_version();
queue_length = db_queue_get_count();
evbuffer_add_printf(evbuf,
"volume: %d\n"
"repeat: %d\n"
@ -751,12 +732,14 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
status.shuffle,
(status.repeat == REPEAT_SONG ? 1 : 0),
0 /* consume: not supported by forked-daapd, always return 'off' */,
status.plversion,
status.playlistlength,
queue_version,
queue_length,
state);
if (status.status != PLAY_STOPPED)
{
pos_pl = db_queue_get_pos(status.item_id, 0);
evbuffer_add_printf(evbuf,
"song: %d\n"
"songid: %d\n"
@ -764,7 +747,7 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
"elapsed: %#.3f\n"
"bitrate: 128\n"
"audio: 44100:16:2\n",
status.pos_pl,
pos_pl,
status.item_id,
(status.pos_ms / 1000), (status.len_ms / 1000),
(status.pos_ms / 1000.0));
@ -777,11 +760,17 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
if (status.status != PLAY_STOPPED)
{
next_item = db_queue_fetch_next(status.item_id, status.shuffle);
if (next_item)
{
evbuffer_add_printf(evbuf,
"nextsong: %d\n"
"nextsongid: %d\n",
status.next_pos_pl,
status.next_item_id);
next_item->item_id,
next_item->pos);
free_queue_item(next_item, 0);
}
}
return 0;
@ -1145,10 +1134,9 @@ mpd_command_play(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
int songpos;
struct player_status status;
struct db_queue_item *queue_item;
int ret;
player_get_status(&status);
songpos = 0;
if (argc > 1)
{
@ -1162,6 +1150,8 @@ mpd_command_play(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
}
}
player_get_status(&status);
if (status.status == PLAY_PLAYING && songpos < 0)
{
DPRINTF(E_DBG, L_MPD, "Ignoring play command with parameter '%s', player is already playing.\n", argv[1]);
@ -1175,7 +1165,19 @@ mpd_command_play(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
}
if (songpos > 0)
ret = player_playback_start_byindex(songpos, NULL);
{
queue_item = db_queue_fetch_bypos(songpos, 0);
if (!queue_item)
{
ret = asprintf(errmsg, "Failed to start playback");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
ret = player_playback_start_byitem(queue_item);
free_queue_item(queue_item, 0);
}
else
ret = player_playback_start(NULL);
@ -1200,6 +1202,7 @@ mpd_command_playid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
uint32_t id;
struct player_status status;
struct db_queue_item *queue_item;
int ret;
player_get_status(&status);
@ -1225,7 +1228,19 @@ mpd_command_playid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
}
if (id > 0)
ret = player_playback_start_byitemid(id, NULL);
{
queue_item = db_queue_fetch_byitemid(id);
if (!queue_item)
{
ret = asprintf(errmsg, "Failed to start playback");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
ret = player_playback_start_byitem(queue_item);
free_queue_item(queue_item, 0);
}
else
ret = player_playback_start(NULL);
@ -1279,7 +1294,6 @@ mpd_command_previous(struct evbuffer *evbuf, int argc, char **argv, char **errms
static int
mpd_command_seek(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct player_status status;
uint32_t songpos;
float seek_target_sec;
int seek_target_msec;
@ -1303,14 +1317,6 @@ mpd_command_seek(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
}
//TODO Allow seeking in songs not currently playing
player_get_status(&status);
if (status.pos_pl != songpos)
{
ret = asprintf(errmsg, "Given song is not the current playing one, seeking is not supported");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
seek_target_sec = strtof(argv[2], NULL);
seek_target_msec = seek_target_sec * 1000;
@ -1470,11 +1476,12 @@ mpd_command_stop(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return 0;
}
static struct queue_item *
mpd_queueitem_make(char *path, int recursive)
static int
mpd_queue_add(char *path, int recursive)
{
struct query_params qp;
struct queue_item *items;
struct player_status status;
int ret;
memset(&qp, 0, sizeof(struct query_params));
@ -1495,10 +1502,12 @@ mpd_queueitem_make(char *path, int recursive)
DPRINTF(E_DBG, L_PLAYER, "Out of memory\n");
}
items = queueitem_make_byquery(&qp);
player_get_status(&status);
ret = db_queue_add_by_query(&qp, status.shuffle, status.item_id);
sqlite3_free(qp.filter);
return items;
return ret;
}
/*
@ -1509,7 +1518,6 @@ mpd_queueitem_make(char *path, int recursive)
static int
mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct queue_item *items;
int ret;
if (argc < 2)
@ -1520,9 +1528,9 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return ACK_ERROR_ARG;
}
items = mpd_queueitem_make(argv[1], 1);
ret = mpd_queue_add(argv[1], 1);
if (!items)
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to add song '%s' to playlist", argv[1]);
if (ret < 0)
@ -1530,8 +1538,6 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return ACK_ERROR_UNKNOWN;
}
player_queue_add(items, NULL);
return 0;
}
@ -1544,8 +1550,6 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
static int
mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct queue_item *items;
uint32_t item_id;
int ret;
if (argc < 2)
@ -1562,9 +1566,9 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
DPRINTF(E_LOG, L_MPD, "Adding at a specified position not supported for 'addid', adding songs at end of queue.\n");
}
items = mpd_queueitem_make(argv[1], 0);
ret = mpd_queue_add(argv[1], 0);
if (!items)
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to add song '%s' to playlist", argv[1]);
if (ret < 0)
@ -1572,12 +1576,9 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return ACK_ERROR_UNKNOWN;
}
player_queue_add(items, &item_id);
evbuffer_add_printf(evbuf,
"Id: %d\n",
item_id);
ret); // mpd_queue_add returns the item_id of the last inserted queue item
return 0;
}
@ -1597,7 +1598,7 @@ mpd_command_clear(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
DPRINTF(E_DBG, L_MPD, "Failed to stop playback\n");
}
player_queue_clear();
db_queue_clear();
return 0;
}
@ -1616,10 +1617,10 @@ mpd_command_delete(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
int count;
int ret;
// If argv[1] is ommited clear the whole queue except the current playing one
// If argv[1] is ommited clear the whole queue
if (argc < 2)
{
player_queue_clear();
db_queue_clear();
return 0;
}
@ -1635,7 +1636,7 @@ mpd_command_delete(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
count = end_pos - start_pos;
ret = player_queue_remove_byindex(start_pos, count);
ret = db_queue_delete_bypos(start_pos, count);
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to remove %d songs starting at position %d", count, start_pos);
@ -1674,7 +1675,7 @@ mpd_command_deleteid(struct evbuffer *evbuf, int argc, char **argv, char **errms
return ACK_ERROR_ARG;
}
ret = player_queue_remove_byitemid(songid);
ret = db_queue_delete_byitemid(songid);
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to remove song with id '%s'", argv[1]);
@ -1726,7 +1727,7 @@ mpd_command_move(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return ACK_ERROR_ARG;
}
ret = player_queue_move_byindex(start_pos, to_pos);
ret = db_queue_move_bypos(start_pos, to_pos);
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to move song at position %d to %d", start_pos, to_pos);
@ -1771,7 +1772,7 @@ mpd_command_moveid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
return ACK_ERROR_ARG;
}
ret = player_queue_move_byitemid(songid, to_pos);
ret = db_queue_move_byitemid(songid, to_pos);
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to move song with id '%s' to index '%s'", argv[1], argv[2]);
@ -1793,12 +1794,9 @@ mpd_command_moveid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
static int
mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct queue *queue;
struct queue_item *item;
struct db_queue_enum queue_enum;
struct db_queue_item queue_item;
uint32_t songid;
int pos_pl;
int count;
int i;
int ret;
songid = 0;
@ -1815,39 +1813,38 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err
}
}
// Get the whole queue (start_pos = 0, end_pos = -1)
queue = player_queue_get_byindex(0, 0);
memset(&queue_enum, 0, sizeof(struct db_queue_enum));
if (!queue)
if (songid > 0)
queue_enum.filter = sqlite3_mprintf("id = %d", songid);
ret = db_queue_enum_start(&queue_enum);
if (ret < 0)
{
// Queue is emtpy
return 0;
sqlite3_free(queue_enum.filter);
ret = asprintf(errmsg, "Failed to start queue enum for command playlistid: '%s'", argv[1]);
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
pos_pl = 0;
count = queue_count(queue);
for (i = 0; i < count; i++)
while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0)
{
item = queue_get_byindex(queue, i, 0);
if (songid == 0 || songid == queueitem_item_id(item))
{
ret = mpd_add_mediainfo_byid(evbuf, queueitem_id(item), queueitem_item_id(item), pos_pl);
ret = mpd_add_db_queue_item(evbuf, &queue_item);
if (ret < 0)
{
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queueitem_id(item));
queue_free(queue);
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue_item.file_id);
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
db_queue_enum_end(&queue_enum);
sqlite3_free(queue_enum.filter);
return ACK_ERROR_UNKNOWN;
}
}
pos_pl++;
}
queue_free(queue);
db_queue_enum_end(&queue_enum);
sqlite3_free(queue_enum.filter);
return 0;
}
@ -1863,17 +1860,15 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err
static int
mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct queue *queue;
struct queue_item *item;
struct db_queue_enum queue_enum;
struct db_queue_item queue_item;
int start_pos;
int end_pos;
int count;
int pos_pl;
int i;
int ret;
start_pos = 0;
end_pos = 0;
memset(&queue_enum, 0, sizeof(struct db_queue_enum));
if (argc > 1)
{
@ -1885,46 +1880,41 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
}
count = end_pos - start_pos;
if (start_pos < 0)
{
DPRINTF(E_DBG, L_MPD, "Command 'playlistinfo' called with pos < 0 (arg = '%s'), ignore arguments and return whole queue\n", argv[1]);
start_pos = 0;
count = 0;
else
queue_enum.filter = sqlite3_mprintf("pos >= %d AND pos < %d", start_pos, end_pos);
}
queue = player_queue_get_byindex(start_pos, count);
if (!queue)
ret = db_queue_enum_start(&queue_enum);
if (ret < 0)
{
// Queue is emtpy
return 0;
sqlite3_free(queue_enum.filter);
ret = asprintf(errmsg, "Failed to start queue enum for command playlistinfo: '%s'", argv[1]);
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
pos_pl = start_pos;
count = queue_count(queue);
for (i = 0; i < count; i++)
while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0)
{
item = queue_get_byindex(queue, i, 0);
ret = mpd_add_mediainfo_byid(evbuf, queueitem_id(item), queueitem_item_id(item), pos_pl);
ret = mpd_add_db_queue_item(evbuf, &queue_item);
if (ret < 0)
{
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queueitem_id(item));
queue_free(queue);
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue_item.file_id);
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
db_queue_enum_end(&queue_enum);
sqlite3_free(queue_enum.filter);
return ACK_ERROR_UNKNOWN;
}
pos_pl++;
}
queue_free(queue);
db_queue_enum_end(&queue_enum);
sqlite3_free(queue_enum.filter);
return 0;
}
@ -1936,46 +1926,42 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e
static int
mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct queue *queue;
struct queue_item *item;
int pos_pl;
int count;
int i;
struct db_queue_enum queue_enum;
struct db_queue_item queue_item;
int ret;
/*
* forked-daapd does not keep track of changes in the queue based on the playlist version,
* therefor plchanges returns all songs in the queue as changed ignoring the given version.
*/
queue = player_queue_get_byindex(0, 0);
memset(&queue_enum, 0, sizeof(struct db_queue_enum));
if (!queue)
ret = db_queue_enum_start(&queue_enum);
if (ret < 0)
{
// Queue is emtpy
return 0;
ret = asprintf(errmsg, "Failed to start queue enum for command plchanges");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
pos_pl = 0;
count = queue_count(queue);
for (i = 0; i < count; i++)
while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0)
{
item = queue_get_byindex(queue, i, 0);
ret = mpd_add_mediainfo_byid(evbuf, queueitem_id(item), queueitem_item_id(item), pos_pl);
ret = mpd_add_db_queue_item(evbuf, &queue_item);
if (ret < 0)
{
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queueitem_id(item));
queue_free(queue);
ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue_item.file_id);
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
db_queue_enum_end(&queue_enum);
return ACK_ERROR_UNKNOWN;
}
pos_pl++;
}
queue_free(queue);
db_queue_enum_end(&queue_enum);
return 0;
}
@ -1986,36 +1972,36 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm
static int
mpd_command_plchangesposid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct queue *queue;
struct queue_item *item;
int count;
int i;
struct db_queue_enum queue_enum;
struct db_queue_item queue_item;
int ret;
/*
* forked-daapd does not keep track of changes in the queue based on the playlist version,
* therefor plchangesposid returns all songs in the queue as changed ignoring the given version.
*/
queue = player_queue_get_byindex(0, 0);
memset(&queue_enum, 0, sizeof(struct db_queue_enum));
if (!queue)
ret = db_queue_enum_start(&queue_enum);
if (ret < 0)
{
// Queue is emtpy
return 0;
ret = asprintf(errmsg, "Failed to start queue enum for command plchangesposid");
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_ARG;
}
count = queue_count(queue);
for (i = 0; i < count; i++)
while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0)
{
item = queue_get_byindex(queue, i, 0);
evbuffer_add_printf(evbuf,
"cpos: %d\n"
"Id: %d\n",
i,
queueitem_item_id(item));
queue_item.pos,
queue_item.item_id);
}
queue_free(queue);
db_queue_enum_end(&queue_enum);
return 0;
}
@ -2239,7 +2225,7 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
char path[PATH_MAX];
struct playlist_info *pli;
struct queue_item *items;
struct player_status status;
int ret;
if (argc < 2)
@ -2274,20 +2260,18 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
//TODO If a second parameter is given only add the specified range of songs to the playqueue
items = queueitem_make_byplid(pli->id);
player_get_status(&status);
if (!items)
ret = db_queue_add_by_playlistid(pli->id, status.shuffle, status.item_id);
free_pli(pli, 0);
if (ret < 0)
{
free_pli(pli, 0);
ret = asprintf(errmsg, "Failed to add song '%s' to playlist", argv[1]);
if (ret < 0)
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
return ACK_ERROR_UNKNOWN;
}
player_queue_add(items, NULL);
return 0;
}
@ -2436,6 +2420,7 @@ mpd_command_count(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
if (ret < 0)
{
db_query_end(&qp);
sqlite3_free(qp.filter);
ret = asprintf(errmsg, "Could not start query");
if (ret < 0)
@ -2447,6 +2432,7 @@ mpd_command_count(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
if (ret < 0)
{
db_query_end(&qp);
sqlite3_free(qp.filter);
ret = asprintf(errmsg, "Could not fetch query count");
if (ret < 0)
@ -2461,6 +2447,7 @@ mpd_command_count(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
(fci.length / 1000));
db_query_end(&qp);
sqlite3_free(qp.filter);
return 0;
}
@ -2492,6 +2479,7 @@ mpd_command_find(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
if (ret < 0)
{
db_query_end(&qp);
sqlite3_free(qp.filter);
ret = asprintf(errmsg, "Could not start query");
if (ret < 0)
@ -2509,6 +2497,7 @@ mpd_command_find(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
}
db_query_end(&qp);
sqlite3_free(qp.filter);
return 0;
}
@ -2517,7 +2506,7 @@ static int
mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct query_params qp;
struct queue_item *items;
struct player_status status;
int ret;
if (argc < 3 || ((argc - 1) % 2) != 0)
@ -2536,9 +2525,11 @@ mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg
mpd_get_query_params_find(argc - 1, argv + 1, &qp);
items = queueitem_make_byquery(&qp);
player_get_status(&status);
if (!items)
ret = db_queue_add_by_query(&qp, status.shuffle, status.item_id);
sqlite3_free(qp.filter);
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to add songs to playlist");
if (ret < 0)
@ -2546,8 +2537,6 @@ mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg
return ACK_ERROR_UNKNOWN;
}
player_queue_add(items, NULL);
return 0;
}
@ -2639,6 +2628,7 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
if (ret < 0)
{
db_query_end(&qp);
sqlite3_free(qp.filter);
ret = asprintf(errmsg, "Could not start query");
if (ret < 0)
@ -2682,6 +2672,7 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
}
db_query_end(&qp);
sqlite3_free(qp.filter);
return 0;
}
@ -3121,6 +3112,7 @@ mpd_command_search(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
if (ret < 0)
{
db_query_end(&qp);
sqlite3_free(qp.filter);
ret = asprintf(errmsg, "Could not start query");
if (ret < 0)
@ -3138,6 +3130,7 @@ mpd_command_search(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
}
db_query_end(&qp);
sqlite3_free(qp.filter);
return 0;
}
@ -3146,7 +3139,7 @@ static int
mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
{
struct query_params qp;
struct queue_item *items;
struct player_status status;
int ret;
if (argc < 3 || ((argc - 1) % 2) != 0)
@ -3165,9 +3158,11 @@ mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errm
mpd_get_query_params_search(argc - 1, argv + 1, &qp);
items = queueitem_make_byquery(&qp);
player_get_status(&status);
if (!items)
ret = db_queue_add_by_query(&qp, status.shuffle, status.item_id);
sqlite3_free(qp.filter);
if (ret < 0)
{
ret = asprintf(errmsg, "Failed to add songs to playlist");
if (ret < 0)
@ -3175,8 +3170,6 @@ mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errm
return ACK_ERROR_UNKNOWN;
}
player_queue_add(items, NULL);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,6 @@
#include <stdint.h>
#include "db.h"
#include "queue.h"
/* AirTunes v2 packet interval in ns */
/* (352 samples/packet * 1e9 ns/s) / 44100 samples/s = 7981859 ns/packet */
@ -28,6 +27,12 @@ enum play_status {
PLAY_PLAYING = 4,
};
enum repeat_mode {
REPEAT_OFF = 0,
REPEAT_SONG = 1,
REPEAT_ALL = 2,
};
struct spk_flags {
unsigned selected:1;
unsigned has_password:1;
@ -44,13 +49,6 @@ struct player_status {
/* Playlist id */
uint32_t plid;
/* Playlist version
After startup plversion is 0 and gets incremented after each change of the playlist
(e. g. after adding/moving/removing items). It is used by mpd clients to recognize if
they need to update the current playlist. */
uint32_t plversion;
/* Playlist length */
uint32_t playlistlength;
/* Id of the playing file/item in the files database */
uint32_t id;
/* Item-Id of the playing file/item in the queue */
@ -59,14 +57,6 @@ struct player_status {
uint32_t pos_ms;
/* Length in ms of playing item */
uint32_t len_ms;
/* Playlist position of playing item*/
int pos_pl;
/* Item id of next item in playlist */
uint32_t next_id;
/* Item-Id of the next file/item in the queue */
uint32_t next_item_id;
/* Playlist position of next item */
int next_pos_pl;
};
typedef void (*spk_enum_cb)(uint64_t id, const char *name, int relvol, int absvol, struct spk_flags flags, void *arg);
@ -107,13 +97,7 @@ int
player_playback_start(uint32_t *id);
int
player_playback_start_byindex(int pos, uint32_t *id);
int
player_playback_start_bypos(int pos, uint32_t *id);
int
player_playback_start_byitemid(uint32_t item_id, uint32_t *id);
player_playback_start_byitem(struct db_queue_item *queue_item);
int
player_playback_stop(void);
@ -147,39 +131,6 @@ int
player_shuffle_set(int enable);
struct queue *
player_queue_get_bypos(int count);
struct queue *
player_queue_get_byindex(int pos, int count);
int
player_queue_add(struct queue_item *items, uint32_t *item_id);
int
player_queue_add_next(struct queue_item *items);
int
player_queue_move_bypos(int ps_pos_from, int ps_pos_to);
int
player_queue_move_byindex(int pos_from, int pos_to);
int
player_queue_move_byitemid(uint32_t item_id, int pos_to);
int
player_queue_remove_bypos(int pos);
int
player_queue_remove_byindex(int pos, int count);
int
player_queue_remove_byitemid(uint32_t id);
void
player_queue_clear(void);
void
player_queue_clear_history(void);