diff --git a/src/db.c b/src/db.c index 454d4c15..f6b627d4 100644 --- a/src/db.c +++ b/src/db.c @@ -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) { diff --git a/src/db.h b/src/db.h index 984ad0cf..63652917 100644 --- a/src/db.h +++ b/src/db.h @@ -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); diff --git a/src/filescanner.c b/src/filescanner.c index e973ab91..d8e93e2d 100644 --- a/src/filescanner.c +++ b/src/filescanner.c @@ -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 diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 46b321eb..742163ab 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -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); diff --git a/src/mpd.c b/src/mpd.c index 874d8ff0..491ad8e5 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010 Julien BLACHE + * Copyright (C) 2016 Christian Meffert * * 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; } diff --git a/src/player.c b/src/player.c index 8004c51b..ca3b8f63 100644 --- a/src/player.c +++ b/src/player.c @@ -97,6 +97,7 @@ struct player_source enum data_kind data_kind; enum media_kind media_kind; + char *path; /* Start time of the media item as rtp-time The stream-start is the rtp-time the media item did or would have @@ -278,9 +279,6 @@ static struct evbuffer *audio_buf; static uint8_t rawbuf[STOB(AIRTUNES_V2_PACKET_SAMPLES)]; -/* Play queue */ -static struct queue *queue; - /* Play history */ static struct player_history *history; @@ -475,9 +473,6 @@ pb_timer_stop(void) static void playback_abort(void); -static enum command_state -playerqueue_clear(void *arg, int *retval); - static void player_metadata_send(struct player_metadata *pmd); @@ -645,14 +640,14 @@ history_add(uint32_t id, uint32_t item_id) * Initializes the given player source for playback */ static int -stream_setup(struct player_source *ps, struct media_file_info *mfi) +stream_setup(struct player_source *ps) { char *url; int ret; - if (!ps || !mfi) + if (!ps) { - DPRINTF(E_LOG, L_PLAYER, "No player source and/or media info given to stream_setup\n"); + DPRINTF(E_LOG, L_PLAYER, "No player source given to stream_setup\n"); return -1; } @@ -666,39 +661,39 @@ stream_setup(struct player_source *ps, struct media_file_info *mfi) switch (ps->data_kind) { case DATA_KIND_FILE: - ps->xcode = transcode_setup(mfi->data_kind, mfi->path, mfi->song_length, XCODE_PCM16_NOHEADER, NULL); + ps->xcode = transcode_setup(ps->data_kind, ps->path, ps->len_ms, XCODE_PCM16_NOHEADER, NULL); ret = ps->xcode ? 0 : -1; break; case DATA_KIND_HTTP: - ret = http_stream_setup(&url, mfi->path); + ret = http_stream_setup(&url, ps->path); if (ret < 0) break; - free(mfi->path); - mfi->path = url; + free(ps->path); + ps->path = url; - ps->xcode = transcode_setup(mfi->data_kind, mfi->path, mfi->song_length, XCODE_PCM16_NOHEADER, NULL); + ps->xcode = transcode_setup(ps->data_kind, ps->path, ps->len_ms, XCODE_PCM16_NOHEADER, NULL); ret = ps->xcode ? 0 : -1; break; case DATA_KIND_SPOTIFY: #ifdef HAVE_SPOTIFY_H - ret = spotify_playback_setup(mfi->path); + ret = spotify_playback_setup(ps->path); #else - DPRINTF(E_LOG, L_PLAYER, "Player source has data kind 'spotify' (%d), but forked-daapd is compiled without spotify support - cannot setup source '%s' (%s)\n", - ps->data_kind, mfi->title, mfi->path); + DPRINTF(E_LOG, L_PLAYER, "Player source has data kind 'spotify' (%d), but forked-daapd is compiled without spotify support - cannot setup source '%s'\n", + ps->data_kind, ps->path); ret = -1; #endif break; case DATA_KIND_PIPE: - ret = pipe_setup(mfi->path); + ret = pipe_setup(ps->path); break; default: - DPRINTF(E_LOG, L_PLAYER, "Unknown data kind (%d) for player source - cannot setup source '%s' (%s)\n", - ps->data_kind, mfi->title, mfi->path); + DPRINTF(E_LOG, L_PLAYER, "Unknown data kind (%d) for player source - cannot setup source '%s'\n", + ps->data_kind, ps->path); ret = -1; } @@ -963,7 +958,7 @@ source_now_playing() * Creates a new player source for the given queue item */ static struct player_source * -source_new(struct queue_item *item) +source_new(struct db_queue_item *queue_item) { struct player_source *ps; @@ -974,16 +969,26 @@ source_new(struct queue_item *item) return NULL; } - ps->id = queueitem_id(item); - ps->item_id = queueitem_item_id(item); - ps->data_kind = queueitem_data_kind(item); - ps->media_kind = queueitem_media_kind(item); - ps->len_ms = queueitem_len(item); + ps->id = queue_item->file_id; + ps->item_id = queue_item->item_id; + ps->data_kind = queue_item->data_kind; + ps->media_kind = queue_item->media_kind; + ps->len_ms = queue_item->song_length; ps->play_next = NULL; + ps->path = strdup(queue_item->path); return ps; } +static void +source_free(struct player_source *ps) +{ + if (ps->path) + free(ps->path); + + free(ps); +} + /* * Stops playback for the current streaming source and frees all * player sources (starting from the playing source). Sets current streaming @@ -1006,7 +1011,7 @@ source_stop() ps_playing = ps_playing->play_next; ps_temp->play_next = NULL; - free(ps_temp); + source_free(ps_temp); } cur_playing = NULL; @@ -1060,7 +1065,7 @@ source_pause(uint64_t pos) ps_playnext = ps_playnext->play_next; ps_temp->play_next = NULL; - free(ps_temp); + source_free(ps_temp); } ps_playing->play_next = NULL; @@ -1071,7 +1076,7 @@ source_pause(uint64_t pos) { DPRINTF(E_INFO, L_PLAYER, "Opening '%s'\n", cur_streaming->path); - ret = stream_setup(cur_streaming, mfi); + ret = stream_setup(cur_streaming); if (ret < 0) { DPRINTF(E_LOG, L_PLAYER, "Failed to open '%s'\n", cur_streaming->path); @@ -1132,66 +1137,38 @@ source_play() } /* - * Initializes playback of the given queue item (but does not start playback) + * Opens the given player source for playback (but does not start playback) * - * A new source is created for the given queue item and is set as the current - * streaming source. If a streaming source already existed (and reached eof) - * the new source is appended as the play-next item to it. + * The given source is appended to the current streaming source (if one exists) and + * becomes the new current streaming source. * * Stream-start and output-start values are set to the given start position. */ static int -source_open(struct queue_item *qii, uint64_t start_pos, int seek) +source_open(struct player_source *ps, uint64_t start_pos, int seek_ms) { - struct player_source *ps; - struct media_file_info *mfi; - uint32_t id; int ret; + DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (id=%d, item-id=%d)\n", ps->path, ps->id, ps->item_id); + if (cur_streaming && cur_streaming->end == 0) { - DPRINTF(E_LOG, L_PLAYER, "Current streaming source not at eof %d\n", cur_streaming->id); + DPRINTF(E_LOG, L_PLAYER, "Current streaming source not at eof '%s' (id=%d, item-id=%d)\n", + cur_streaming->path, cur_streaming->id, cur_streaming->item_id); return -1; } - id = queueitem_id(qii); - mfi = db_file_fetch_byid(id); - if (!mfi) - { - DPRINTF(E_LOG, L_PLAYER, "Couldn't fetch file id %d\n", id); - - return -1; - } - - if (mfi->disabled) - { - DPRINTF(E_DBG, L_PLAYER, "File id %d is disabled, skipping\n", id); - - free_mfi(mfi, 0); - return -1; - } - - DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path); - - ps = source_new(qii); - if (!ps) - return -1; - - ret = stream_setup(ps, mfi); + ret = stream_setup(ps); if (ret < 0) { - DPRINTF(E_LOG, L_PLAYER, "Failed to open '%s' (%s)\n", mfi->title, mfi->path); - free(ps); - free_mfi(mfi, 0); + DPRINTF(E_LOG, L_PLAYER, "Failed to open '%s' (id=%d, item-id=%d)\n", ps->path, ps->id, ps->item_id); return -1; } /* If a streaming source exists, append the new source as play-next and set it as the new streaming source */ if (cur_streaming) - { cur_streaming->play_next = ps; - } cur_streaming = ps; @@ -1199,11 +1176,13 @@ source_open(struct queue_item *qii, uint64_t start_pos, int seek) cur_streaming->output_start = cur_streaming->stream_start; cur_streaming->end = 0; - /* Seek to the saved seek position */ - if (seek && mfi->seek) - source_seek(mfi->seek); + // Seek to the given seek position + if (seek_ms) + { + DPRINTF(E_INFO, L_PLAYER, "Seek to %d ms for '%s' (id=%d, item-id=%d)\n", seek_ms, ps->path, ps->id, ps->item_id); + source_seek(seek_ms); + } - free_mfi(mfi, 0); return ret; } @@ -1296,7 +1275,7 @@ source_check(void) ps = cur_playing; cur_playing = cur_playing->play_next; - free(ps); + source_free(ps); } if (i > 0) @@ -1311,13 +1290,92 @@ source_check(void) return pos; } +/* + * Returns the next player source based on the current streaming source and repeat mode + * + * If repeat mode is repeat all, shuffle is active and the current streaming source is the + * last item in the queue, the queue is reshuffled prior to returning the first item of the + * queue. + */ +static struct player_source * +source_next() +{ + struct player_source *ps = NULL; + struct db_queue_item *queue_item; + + if (!cur_streaming) + { + DPRINTF(E_LOG, L_PLAYER, "source_next() called with no current streaming source available\n"); + return NULL; + } + + if (repeat == REPEAT_SONG) + { + queue_item = db_queue_fetch_byitemid(cur_streaming->item_id); + if (!queue_item) + { + DPRINTF(E_LOG, L_PLAYER, "Error fetching item from queue '%s' (id=%d, item-id=%d)\n", cur_streaming->path, cur_streaming->id, cur_streaming->item_id); + return NULL; + } + } + else + { + queue_item = db_queue_fetch_next(cur_streaming->item_id, shuffle); + if (!!queue_item && repeat == REPEAT_ALL) + { + free_queue_item(queue_item, 0); + if (shuffle) + { + db_queue_reshuffle(0); + } + + queue_item = db_queue_fetch_bypos(0, shuffle); + if (!queue_item) + { + DPRINTF(E_LOG, L_PLAYER, "Error fetching item from queue '%s' (id=%d, item-id=%d)\n", cur_streaming->path, cur_streaming->id, cur_streaming->item_id); + return NULL; + } + } + + ps = source_new(queue_item); + free_queue_item(queue_item, 0); + } + + return ps; +} + +/* + * Returns the previous player source based on the current streaming source + */ +static struct player_source * +source_prev() +{ + struct player_source *ps = NULL; + struct db_queue_item *queue_item; + + if (!cur_streaming) + { + DPRINTF(E_LOG, L_PLAYER, "source_prev() called with no current streaming source available\n"); + return NULL; + } + + queue_item = db_queue_fetch_prev(cur_streaming->item_id, shuffle); + if (!queue_item) + return NULL; + + ps = source_new(queue_item); + free_queue_item(queue_item, 0); + + return ps; +} + static int source_read(uint8_t *buf, int len, uint64_t rtptime) { int ret; int nbytes; char *silence_buf; - struct queue_item *item; + struct player_source *ps; if (!cur_streaming) return 0; @@ -1353,17 +1411,22 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) DPRINTF(E_DBG, L_PLAYER, "New file\n"); - item = queue_next(queue, cur_streaming->item_id, shuffle, repeat, 1); + ps = source_next(); + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Error fetching next item from queue %d\n", cur_streaming->id); + return -1; + } if (ret < 0) { DPRINTF(E_LOG, L_PLAYER, "Error reading source %d\n", cur_streaming->id); - queue_remove_byitemid(queue, cur_streaming->item_id); + db_queue_delete_byitemid(cur_streaming->item_id); } - if (item) + if (ps) { - ret = source_open(item, cur_streaming->end + 1, 0); + ret = source_open(ps, cur_streaming->end + 1, 0); if (ret < 0) return -1; @@ -2013,8 +2076,6 @@ device_restart_cb(struct output_device *device, struct output_session *session, static void playback_abort(void) { - int ret; - outputs_playback_stop(); pb_timer_stop(); @@ -2024,7 +2085,7 @@ playback_abort(void) evbuffer_drain(audio_buf, evbuffer_get_length(audio_buf)); if (!clear_queue_on_stop_disabled) - playerqueue_clear(NULL, &ret); + db_queue_clear(); status_update(PLAY_STOPPED); @@ -2039,7 +2100,6 @@ get_status(void *arg, int *retval) struct timespec ts; struct player_source *ps; struct player_status *status; - struct queue_item *item_next; uint64_t pos; int ret; @@ -2053,8 +2113,6 @@ get_status(void *arg, int *retval) status->volume = master_volume; status->plid = cur_plid; - status->plversion = cur_plversion; - status->playlistlength = queue_count(queue); switch (player_state) { @@ -2075,8 +2133,6 @@ get_status(void *arg, int *retval) status->pos_ms = (pos * 1000) / 44100; status->len_ms = cur_streaming->len_ms; - status->pos_pl = queue_index_byitemid(queue, cur_streaming->item_id, 0); - break; case PLAY_PLAYING: @@ -2116,21 +2172,6 @@ get_status(void *arg, int *retval) status->id = ps->id; status->item_id = ps->item_id; - status->pos_pl = queue_index_byitemid(queue, ps->item_id, 0); - - item_next = queue_next(queue, ps->item_id, shuffle, repeat, 0); - if (item_next) - { - status->next_id = queueitem_id(item_next); - status->next_item_id = queueitem_item_id(item_next); - status->next_pos_pl = queue_index_byitemid(queue, status->next_item_id, 0); - } - else - { - //TODO [queue/mpd] Check how mpd sets the next-id/-pos if the last song is playing - status->next_id = 0; - status->next_pos_pl = 0; - } break; } @@ -2280,30 +2321,16 @@ playback_start_bh(void *arg, int *retval) } static enum command_state -playback_start_item(union player_arg *cmdarg, int *retval, struct queue_item *qii) +playback_start_item(void *arg, int *retval) { - uint32_t *dbmfi_id; + struct db_queue_item *queue_item = arg; struct output_device *device; - struct player_source *ps_playing; - struct queue_item *item; + struct player_source *ps; + int seek_ms; int ret; - dbmfi_id = cmdarg->playback_start_param.id_ptr; - - ps_playing = source_now_playing(); - if (player_state == PLAY_PLAYING) { - /* - * If player is already playing a song, only return current playing song id - * and do not change player state (ignores given arguments for playing a - * specified song by pos or id). - */ - if (dbmfi_id && ps_playing) - { - *dbmfi_id = ps_playing->id; - } - status_update(player_state); *retval = 1; // Value greater 0 will prevent execution of the bottom half function @@ -2313,22 +2340,38 @@ playback_start_item(union player_arg *cmdarg, int *retval, struct queue_item *qi // Update global playback position pb_pos = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - 88200; - item = NULL; - if (qii) + if (player_state == PLAY_STOPPED && !queue_item) { - item = qii; - } - else if (!cur_streaming) - { - if (shuffle) - queue_shuffle(queue, 0); - item = queue_next(queue, 0, shuffle, repeat, 0); + *retval = -1; + return COMMAND_END; } - if (item) + if (!queue_item) { + // Resume playback of current source + ps = source_now_playing(); + DPRINTF(E_DBG, L_PLAYER, "Resume playback of '%s' (id=%d, item-id=%d)\n", ps->path, ps->id, ps->item_id); + } + else + { + // Start playback for given queue item + DPRINTF(E_DBG, L_PLAYER, "Start playback of '%s' (id=%d, item-id=%d)\n", queue_item->path, queue_item->file_id, queue_item->item_id); source_stop(); - ret = source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 1); + + ps = source_new(queue_item); + if (!ps) + { + playback_abort(); + *retval = -1; + return COMMAND_END; + } + + if (queue_item->file_id > 0) + seek_ms = db_file_get_seekpos(queue_item->file_id); + else + seek_ms = 0; + + ret = source_open(ps, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, seek_ms); if (ret < 0) { playback_abort(); @@ -2346,9 +2389,6 @@ playback_start_item(union player_arg *cmdarg, int *retval, struct queue_item *qi } - if (dbmfi_id) - *dbmfi_id = cur_streaming->id; - metadata_trigger(1); /* Start sessions on selected devices */ @@ -2413,59 +2453,22 @@ playback_start_item(union player_arg *cmdarg, int *retval, struct queue_item *qi static enum command_state playback_start(void *arg, int *retval) { - return playback_start_item(arg, retval, NULL); -} + struct db_queue_item *queue_item = NULL; + enum command_state cmd_state; -static enum command_state -playback_start_byitemid(void *arg, int *retval) + if (player_state == PLAY_STOPPED) { - union player_arg *cmdarg = arg; - int item_id; - struct queue_item *qii; - - item_id = cmdarg->playback_start_param.id; - - qii = queue_get_byitemid(queue, item_id); - - return playback_start_item(cmdarg, retval, qii); -} - -static enum command_state -playback_start_byindex(void *arg, int *retval) + // Start playback of first item in queue + queue_item = db_queue_fetch_bypos(0, shuffle); + if (!queue_item) { - union player_arg *cmdarg = arg; - int pos; - struct queue_item *qii; - - pos = cmdarg->playback_start_param.pos; - - qii = queue_get_byindex(queue, pos, 0); - - return playback_start_item(cmdarg, retval, qii); + *retval = -1; + return COMMAND_END; } - -static enum command_state -playback_start_bypos(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - int offset; - struct player_source *ps_playing; - struct queue_item *qii; - - offset = cmdarg->playback_start_param.pos; - - ps_playing = source_now_playing(); - - if (ps_playing) - { - qii = queue_get_bypos(queue, ps_playing->item_id, offset, shuffle); - } - else - { - qii = queue_get_byindex(queue, offset, shuffle); } - return playback_start_item(cmdarg, retval, qii); + cmd_state = playback_start_item(queue_item, retval); + return cmd_state; } static enum command_state @@ -2473,7 +2476,7 @@ playback_prev_bh(void *arg, int *retval) { int ret; int pos_sec; - struct queue_item *item; + struct player_source *ps; /* * The upper half is playback_pause, therefor the current playing item is @@ -2501,8 +2504,8 @@ playback_prev_bh(void *arg, int *retval) DPRINTF(E_DBG, L_PLAYER, "Skipping song played %d sec\n", pos_sec); if (pos_sec < 3) { - item = queue_prev(queue, cur_streaming->item_id, shuffle, repeat); - if (!item) + ps = source_prev(); + if (!ps) { playback_abort(); *retval = -1; @@ -2511,7 +2514,7 @@ playback_prev_bh(void *arg, int *retval) source_stop(); - ret = source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 0); + ret = source_open(ps, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 0); if (ret < 0) { playback_abort(); @@ -2551,8 +2554,8 @@ playback_prev_bh(void *arg, int *retval) static enum command_state playback_next_bh(void *arg, int *retval) { + struct player_source *ps; int ret; - struct queue_item *item; /* * The upper half is playback_pause, therefor the current playing item is @@ -2569,8 +2572,8 @@ playback_next_bh(void *arg, int *retval) if (cur_streaming->output_start > cur_streaming->stream_start) history_add(cur_streaming->id, cur_streaming->item_id); - item = queue_next(queue, cur_streaming->item_id, shuffle, repeat, 0); - if (!item) + ps = source_next(); + if (!ps) { playback_abort(); *retval = -1; @@ -2579,7 +2582,7 @@ playback_next_bh(void *arg, int *retval) source_stop(); - ret = source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 0); + ret = source_open(ps, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 0); if (ret < 0) { playback_abort(); @@ -3044,7 +3047,7 @@ shuffle_set(void *arg, int *retval) if (!shuffle) { cur_id = cur_streaming ? cur_streaming->item_id : 0; - queue_shuffle(queue, cur_id); + db_queue_reshuffle(cur_id); } /* FALLTHROUGH*/ case 0: @@ -3063,276 +3066,6 @@ shuffle_set(void *arg, int *retval) return COMMAND_END; } -static enum command_state -playerqueue_get_bypos(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - int count; - struct queue *qi; - struct player_source *ps; - int item_id; - - count = cmdarg->queue_get_param.count; - - ps = source_now_playing(); - - item_id = 0; - if (ps) - { - item_id = ps->item_id; - } - - qi = queue_new_bypos(queue, item_id, count, shuffle); - - cmdarg->queue_get_param.queue = qi; - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_get_byindex(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - int pos; - int count; - struct queue *qi; - - pos = cmdarg->queue_get_param.pos; - count = cmdarg->queue_get_param.count; - - qi = queue_new_byindex(queue, pos, count, 0); - cmdarg->queue_get_param.queue = qi; - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_add(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - struct queue_item *items; - uint32_t cur_id; - uint32_t *item_id; - - items = cmdarg->queue_add_param.items; - item_id = cmdarg->queue_add_param.item_id_ptr; - - queue_add(queue, items); - - if (shuffle) - { - cur_id = cur_streaming ? cur_streaming->item_id : 0; - queue_shuffle(queue, cur_id); - } - - if (item_id) - *item_id = queueitem_item_id(items); - - cur_plid = 0; - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_add_next(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - struct queue_item *items; - uint32_t cur_id; - - items = cmdarg->queue_add_param.items; - - cur_id = cur_streaming ? cur_streaming->item_id : 0; - - queue_add_after(queue, items, cur_id); - - if (shuffle) - queue_shuffle(queue, cur_id); - - cur_plid = 0; - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_move_bypos(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - struct player_source *ps_playing; - uint32_t item_id; - - DPRINTF(E_DBG, L_PLAYER, "Moving song from position %d to be the next song after %d\n", - cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos); - - ps_playing = source_now_playing(); - - if (!ps_playing) - { - DPRINTF(E_DBG, L_PLAYER, "No playing item found for move by pos\n"); - item_id = 0; - } - else - item_id = ps_playing->item_id; - - queue_move_bypos(queue, item_id, cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos, shuffle); - - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_move_byindex(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - - DPRINTF(E_DBG, L_PLAYER, "Moving song from index %d to be the next song after %d\n", - cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos); - - queue_move_byindex(queue, cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos, 0); - - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_move_byitemid(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - - DPRINTF(E_DBG, L_PLAYER, "Moving song with item-id %d to be the next song after index %d\n", - cmdarg->queue_move_param.item_id, cmdarg->queue_move_param.to_pos); - - queue_move_byitemid(queue, cmdarg->queue_move_param.item_id, cmdarg->queue_move_param.to_pos, 0); - - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_remove_bypos(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - int pos; - struct player_source *ps_playing; - uint32_t item_id; - - pos = cmdarg->intval; - if (pos < 1) - { - DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid position %d\n", pos); - *retval = -1; - return COMMAND_END; - } - - ps_playing = source_now_playing(); - - if (!ps_playing) - { - DPRINTF(E_DBG, L_PLAYER, "No playing item for remove by pos\n"); - item_id = 0; - } - else - item_id = ps_playing->item_id; - - DPRINTF(E_DBG, L_PLAYER, "Removing item from position %d\n", pos); - queue_remove_bypos(queue, item_id, pos, shuffle); - - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_remove_byindex(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - int pos; - int count; - int i; - - pos = cmdarg->queue_remove_param.from_pos; - count = cmdarg->queue_remove_param.count; - - DPRINTF(E_DBG, L_PLAYER, "Removing %d items starting from position %d\n", count, pos); - - for (i = 0; i < count; i++) - queue_remove_byindex(queue, pos, 0); - - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -static enum command_state -playerqueue_remove_byitemid(void *arg, int *retval) -{ - union player_arg *cmdarg = arg; - uint32_t id; - - id = cmdarg->id; - if (id < 1) - { - DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid id %d\n", id); - *retval = -1; - return COMMAND_END; - } - - DPRINTF(E_DBG, L_PLAYER, "Removing item with id %d\n", id); - queue_remove_byitemid(queue, id); - - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - -/* - * Removes all media items from the queue - */ -static enum command_state -playerqueue_clear(void *arg, int *retval) -{ - queue_clear(queue); - - cur_plid = 0; - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - *retval = 0; - return COMMAND_END; -} - /* * Removes all items from the history */ @@ -3341,7 +3074,7 @@ playerqueue_clear_history(void *arg, int *retval) { memset(history, 0, sizeof(struct player_history)); - cur_plversion++; + cur_plversion++; // TODO [db_queue] need to update db queue version listener_notify(LISTENER_PLAYLIST); @@ -3447,66 +3180,11 @@ player_playback_start(uint32_t *id) * @return 0 if successful, -1 if an error occurred */ int -player_playback_start_byindex(int index, uint32_t *id) +player_playback_start_byitem(struct db_queue_item *queue_item) { - union player_arg cmdarg; int ret; - cmdarg.playback_start_param.pos = index; - cmdarg.playback_start_param.id_ptr = id; - - ret = commands_exec_sync(cmdbase, playback_start_byindex, playback_start_bh, &cmdarg); - return ret; -} - -/* - * Starts playback with the media item at the given position in the UpNext-queue. - * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue - * (shuffle on) after the current playing item (starting with position 0). - * - * If shuffle is set, the queue is reshuffled prior to starting playback. - * - * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. - * - * @param pos the position in the UpNext-queue (zero-based) - * @param *id if not NULL, will be set to the playing item dbmfi-id - * @return 0 if successful, -1 if an error occurred - */ -int -player_playback_start_bypos(int pos, uint32_t *id) -{ - union player_arg cmdarg; - int ret; - - cmdarg.playback_start_param.pos = pos; - cmdarg.playback_start_param.id_ptr = id; - - ret = commands_exec_sync(cmdbase, playback_start_bypos, playback_start_bh, &cmdarg); - return ret; -} - -/* - * Starts playback with the media item with the given (queueitem) item-id in queue - * - * If shuffle is set, the queue is reshuffled prior to starting playback. - * - * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. - * - * @param item_id The queue-item-id - * @param *id if not NULL, will be set to the playing item dbmfi-id - * @return 0 if successful, -1 if an error occurred - */ -int -player_playback_start_byitemid(uint32_t item_id, uint32_t *id) -{ - union player_arg cmdarg; - int ret; - - cmdarg.playback_start_param.id = item_id; - cmdarg.playback_start_param.id_ptr = id; - ret = commands_exec_sync(cmdbase, playback_start_byitemid, playback_start_bh, &cmdarg); - return ret; - + ret = commands_exec_sync(cmdbase, playback_start_item, playback_start_bh, queue_item); return ret; } @@ -3648,202 +3326,6 @@ player_shuffle_set(int enable) return ret; } -/* - * Returns the queue info for max "count" media items in the UpNext-queue - * - * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue - * (shuffle on) after the current playing item (starting with position 0). - * - * @param count max number of media items to return - * @return queue info - */ -struct queue * -player_queue_get_bypos(int count) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_get_param.pos = -1; - cmdarg.queue_get_param.count = count; - cmdarg.queue_get_param.queue = NULL; - - ret = commands_exec_sync(cmdbase, playerqueue_get_bypos, NULL, &cmdarg); - - if (ret != 0) - return NULL; - - return cmdarg.queue_get_param.queue; -} - -/* - * Returns the queue info for max "count" media items starting with the item at the given - * index in the play-queue - * - * @param index Index of the play-queue for the first item - * @param count max number of media items to return - * @return queue info - */ -struct queue * -player_queue_get_byindex(int index, int count) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_get_param.pos = index; - cmdarg.queue_get_param.count = count; - cmdarg.queue_get_param.queue = NULL; - - ret = commands_exec_sync(cmdbase, playerqueue_get_byindex, NULL, &cmdarg); - - if (ret != 0) - return NULL; - - return cmdarg.queue_get_param.queue; -} - -/* - * Appends the given media items to the queue - */ -int -player_queue_add(struct queue_item *items, uint32_t *item_id) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_add_param.items = items; - cmdarg.queue_add_param.item_id_ptr = item_id; - - ret = commands_exec_sync(cmdbase, playerqueue_add, NULL, &cmdarg); - return ret; -} - -/* - * Adds the given media items directly after the current playing/streaming media item - */ -int -player_queue_add_next(struct queue_item *items) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_add_param.items = items; - - ret = commands_exec_sync(cmdbase, playerqueue_add_next, NULL, &cmdarg); - return ret; -} - -/* - * Moves the media item at 'pos_from' to 'pos_to' in the UpNext-queue. - * - * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue - * (shuffle on) after the current playing item (starting with position 0). - */ -int -player_queue_move_bypos(int pos_from, int pos_to) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_move_param.from_pos = pos_from; - cmdarg.queue_move_param.to_pos = pos_to; - - ret = commands_exec_sync(cmdbase, playerqueue_move_bypos, NULL, &cmdarg); - return ret; -} - -int -player_queue_move_byindex(int pos_from, int pos_to) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_move_param.from_pos = pos_from; - cmdarg.queue_move_param.to_pos = pos_to; - - ret = commands_exec_sync(cmdbase, playerqueue_move_byindex, NULL, &cmdarg); - return ret; -} - -int -player_queue_move_byitemid(uint32_t item_id, int pos_to) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_move_param.item_id = item_id; - cmdarg.queue_move_param.to_pos = pos_to; - - ret = commands_exec_sync(cmdbase, playerqueue_move_byitemid, NULL, &cmdarg); - return ret; -} - -/* - * Removes the media item at the given position from the UpNext-queue - * - * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue - * (shuffle on) after the current playing item (starting with position 0). - * - * @param pos Position in the UpNext-queue (0-based) - * @return 0 on success, -1 on failure - */ -int -player_queue_remove_bypos(int pos) -{ - union player_arg cmdarg; - int ret; - - cmdarg.intval = pos; - - ret = commands_exec_sync(cmdbase, playerqueue_remove_bypos, NULL, &cmdarg); - return ret; -} - -/* - * Removes the media item at the given position from the UpNext-queue - * - * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue - * (shuffle on) after the current playing item (starting with position 0). - * - * @param pos Position in the UpNext-queue (0-based) - * @return 0 on success, -1 on failure - */ -int -player_queue_remove_byindex(int pos, int count) -{ - union player_arg cmdarg; - int ret; - - cmdarg.queue_remove_param.from_pos = pos; - cmdarg.queue_remove_param.count = count; - - ret = commands_exec_sync(cmdbase, playerqueue_remove_byindex, NULL, &cmdarg); - return ret; -} - -/* - * Removes the item with the given (queueitem) item id from the queue - * - * @param id Id of the queue item to remove - * @return 0 on success, -1 on failure - */ -int -player_queue_remove_byitemid(uint32_t id) -{ - union player_arg cmdarg; - int ret; - - cmdarg.id = id; - - ret = commands_exec_sync(cmdbase, playerqueue_remove_byitemid, NULL, &cmdarg); - return ret; -} - -void -player_queue_clear(void) -{ - commands_exec_sync(cmdbase, playerqueue_clear, NULL, NULL); -} - void player_queue_clear_history() { @@ -3974,7 +3456,6 @@ player_init(void) repeat = REPEAT_OFF; shuffle = 0; - queue = queue_new(); history = (struct player_history *)calloc(1, sizeof(struct player_history)); /* @@ -4106,7 +3587,6 @@ player_deinit(void) return; } - queue_free(queue); free(history); pb_timer_stop(); diff --git a/src/player.h b/src/player.h index d0b4040b..45de3593 100644 --- a/src/player.h +++ b/src/player.h @@ -5,7 +5,6 @@ #include #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);