From 2d276366440af245004806e217301bf2446c1a90 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 15 Oct 2016 10:06:18 +0200 Subject: [PATCH 01/23] [rng] Add function to shuffle an int array --- src/rng.c | 19 +++++++++++++++++++ src/rng.h | 3 +++ 2 files changed, 22 insertions(+) diff --git a/src/rng.c b/src/rng.c index 5b57e06a..d36b13b3 100644 --- a/src/rng.c +++ b/src/rng.c @@ -144,3 +144,22 @@ shuffle_ptr(struct rng_ctx *ctx, void **values, int len) } } +/* Fisher-Yates shuffling algorithm + * Durstenfeld in-place shuffling variant + */ +void +shuffle_int(struct rng_ctx *ctx, int *values, int len) +{ + int i; + int32_t j; + int tmp; + + for (i = len - 1; i > 0; i--) + { + j = rng_rand_range(ctx, 0, i + 1); + + tmp = values[i]; + values[i] = values[j]; + values[j] = tmp; + } +} diff --git a/src/rng.h b/src/rng.h index b6a2c34f..12d980c8 100644 --- a/src/rng.h +++ b/src/rng.h @@ -21,5 +21,8 @@ rng_rand_range(struct rng_ctx *ctx, int32_t min, int32_t max); void shuffle_ptr(struct rng_ctx *ctx, void **values, int len); +void +shuffle_int(struct rng_ctx *ctx, int *values, int len); + #endif /* !__RNG_H__ */ From faaeb92ce535e5b13536928f013f61493337fc97 Mon Sep 17 00:00:00 2001 From: chme Date: Wed, 26 Oct 2016 20:09:09 +0200 Subject: [PATCH 02/23] [player] Reading media_file_info is not necessary in source_pause --- src/player.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/player.c b/src/player.c index 1ec16a58..8004c51b 100644 --- a/src/player.c +++ b/src/player.c @@ -1026,7 +1026,6 @@ source_pause(uint64_t pos) struct player_source *ps_playing; struct player_source *ps_playnext; struct player_source *ps_temp; - struct media_file_info *mfi; uint64_t seek_frames; int seek_ms; int ret; @@ -1070,33 +1069,14 @@ source_pause(uint64_t pos) if (!cur_streaming->setup_done) { - mfi = db_file_fetch_byid(cur_streaming->id); - if (!mfi) - { - DPRINTF(E_LOG, L_PLAYER, "Couldn't fetch file id %d\n", cur_streaming->id); - - return -1; - } - - if (mfi->disabled) - { - DPRINTF(E_DBG, L_PLAYER, "File id %d is disabled, skipping\n", cur_streaming->id); - - free_mfi(mfi, 0); - return -1; - } - - DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path); + DPRINTF(E_INFO, L_PLAYER, "Opening '%s'\n", cur_streaming->path); ret = stream_setup(cur_streaming, mfi); if (ret < 0) { - DPRINTF(E_LOG, L_PLAYER, "Failed to open '%s' (%s)\n", mfi->title, mfi->path); - free_mfi(mfi, 0); + DPRINTF(E_LOG, L_PLAYER, "Failed to open '%s'\n", cur_streaming->path); return -1; } - - free_mfi(mfi, 0); } /* Seek back to the pause position */ From 0beb68e1f13a1d753116d2faf17de1b0fd64fb4b Mon Sep 17 00:00:00 2001 From: chme Date: Fri, 4 Nov 2016 15:09:24 +0100 Subject: [PATCH 03/23] [db] Upgrade db to v20.00 - New table 'queue' for persisting the queue/current playlist - New entry 'plversion' in admin table for queue version --- src/db_init.c | 36 +++++++++++++++++++++++++++++++ src/db_init.h | 2 +- src/db_upgrade.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/db_init.c b/src/db_init.c index 6eb57490..1d510942 100644 --- a/src/db_init.c +++ b/src/db_init.c @@ -158,6 +158,32 @@ " parent_id INTEGER DEFAULT 0" \ ");" +#define T_QUEUE \ + "CREATE TABLE IF NOT EXISTS queue (" \ + " id INTEGER PRIMARY KEY NOT NULL," \ + " file_id INTEGER NOT NULL," \ + " pos INTEGER NOT NULL," \ + " shuffle_pos INTEGER NOT NULL," \ + " data_kind INTEGER NOT NULL," \ + " media_kind INTEGER NOT NULL," \ + " song_length INTEGER NOT NULL," \ + " path VARCHAR(4096) NOT NULL," \ + " virtual_path VARCHAR(4096) NOT NULL," \ + " title VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " artist VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " album_artist VARCHAR(1024) NOT NULL COLLATE DAAP," \ + " album VARCHAR(1024) NOT NULL COLLATE DAAP," \ + " genre VARCHAR(255) DEFAULT NULL COLLATE DAAP," \ + " songalbumid INTEGER NOT NULL," \ + " time_modified INTEGER DEFAULT 0," \ + " artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " album_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " year INTEGER DEFAULT 0," \ + " track INTEGER DEFAULT 0," \ + " disc INTEGER DEFAULT 0" \ + ");" + #define TRG_GROUPS_INSERT_FILES \ "CREATE TRIGGER update_groups_new_file AFTER INSERT ON files FOR EACH ROW" \ " BEGIN" \ @@ -237,6 +263,7 @@ static const struct db_init_query db_init_table_queries[] = { T_SPEAKERS, "create table speakers" }, { T_INOTIFY, "create table inotify" }, { T_DIRECTORIES, "create table directories" }, + { T_QUEUE, "create table queue" }, { TRG_GROUPS_INSERT_FILES, "create trigger update_groups_new_file" }, { TRG_GROUPS_UPDATE_FILES, "create trigger update_groups_update_file" }, @@ -327,6 +354,12 @@ static const struct db_init_query db_init_table_queries[] = #define I_DIR_PARENT \ "CREATE INDEX IF NOT EXISTS idx_dir_parentid ON directories(parent_id);" +#define I_QUEUE_POS \ + "CREATE INDEX IF NOT EXISTS idx_queue_pos ON queue(pos);" + +#define I_QUEUE_SHUFFLEPOS \ + "CREATE INDEX IF NOT EXISTS idx_queue_shufflepos ON queue(shuffle_pos);" + static const struct db_init_query db_init_index_queries[] = { { I_RESCAN, "create rescan index" }, @@ -357,6 +390,9 @@ static const struct db_init_query db_init_index_queries[] = { I_DIR_VPATH, "create directories disabled_virtualpath index" }, { I_DIR_PARENT, "create directories parentid index" }, + + { I_QUEUE_POS, "create queue pos index" }, + { I_QUEUE_SHUFFLEPOS, "create queue shuffle pos index" }, }; int diff --git a/src/db_init.h b/src/db_init.h index befa6772..33312835 100644 --- a/src/db_init.h +++ b/src/db_init.h @@ -25,7 +25,7 @@ * version of the database? If yes, then it is a minor upgrade, if no, then it * is a major upgrade. In other words minor version upgrades permit downgrading * forked-daapd after the database was upgraded. */ -#define SCHEMA_VERSION_MAJOR 19 +#define SCHEMA_VERSION_MAJOR 20 #define SCHEMA_VERSION_MINOR 00 int diff --git a/src/db_upgrade.c b/src/db_upgrade.c index 32b1c56d..56e4315f 100644 --- a/src/db_upgrade.c +++ b/src/db_upgrade.c @@ -1441,6 +1441,54 @@ db_upgrade_v19(sqlite3 *hdl) return 0; } +/* Upgrade from schema v19.00 to v20.00 */ +/* Create new table queue for persistent playqueue + */ + +#define U_V2000_CREATE_TABLE_QUEUE \ + "CREATE TABLE IF NOT EXISTS queue (" \ + " id INTEGER PRIMARY KEY NOT NULL," \ + " file_id INTEGER NOT NULL," \ + " pos INTEGER NOT NULL," \ + " shuffle_pos INTEGER NOT NULL," \ + " data_kind INTEGER NOT NULL," \ + " media_kind INTEGER NOT NULL," \ + " song_length INTEGER NOT NULL," \ + " path VARCHAR(4096) NOT NULL," \ + " virtual_path VARCHAR(4096) NOT NULL," \ + " title VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " artist VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " album_artist VARCHAR(1024) NOT NULL COLLATE DAAP," \ + " album VARCHAR(1024) NOT NULL COLLATE DAAP," \ + " genre VARCHAR(255) DEFAULT NULL COLLATE DAAP," \ + " songalbumid INTEGER NOT NULL," \ + " time_modified INTEGER DEFAULT 0," \ + " artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " album_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ + " year INTEGER DEFAULT 0," \ + " track INTEGER DEFAULT 0," \ + " disc INTEGER DEFAULT 0" \ + ");" + +#define U_V2000_PLVERSION \ + "INSERT INTO admin (key, value) VALUES ('plversion', '0');" + +#define U_V2000_SCVER_MAJOR \ + "UPDATE admin SET value = '20' WHERE key = 'schema_version_major';" +#define U_V2000_SCVER_MINOR \ + "UPDATE admin SET value = '00' WHERE key = 'schema_version_minor';" + +static const struct db_upgrade_query db_upgrade_v2000_queries[] = + { + { U_V2000_CREATE_TABLE_QUEUE, "create table directories" }, + { U_V2000_PLVERSION, "insert plversion" }, + + { U_V2000_SCVER_MAJOR, "set schema_version_major to 20" }, + { U_V2000_SCVER_MINOR, "set schema_version_minor to 00" }, + }; + + int db_upgrade(sqlite3 *hdl, int db_ver) { @@ -1551,6 +1599,13 @@ db_upgrade(sqlite3 *hdl, int db_ver) if (ret < 0) return -1; + /* FALLTHROUGH */ + + case 1900: + ret = db_generic_upgrade(hdl, db_upgrade_v2000_queries, sizeof(db_upgrade_v2000_queries) / sizeof(db_upgrade_v2000_queries[0])); + if (ret < 0) + return -1; + break; default: From 8ebf2f93071802cd293c6bf34ee15e07bbb4ab05 Mon Sep 17 00:00:00 2001 From: chme Date: Fri, 4 Nov 2016 15:34:00 +0100 Subject: [PATCH 04/23] [db] Add functions to access/modify the queue table --- src/db.c | 1483 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/db.h | 139 +++++ 2 files changed, 1622 insertions(+) diff --git a/src/db.c b/src/db.c index 60340bcc..454d4c15 100644 --- a/src/db.c +++ b/src/db.c @@ -42,10 +42,12 @@ #include "conffile.h" #include "logger.h" #include "cache.h" +#include "listener.h" #include "misc.h" #include "db.h" #include "db_init.h" #include "db_upgrade.h" +#include "rng.h" #define STR(x) ((x) ? (x) : "") @@ -299,6 +301,9 @@ static const char *sort_clause[] = "ORDER BY f.virtual_path ASC", }; +/* Shuffle RNG state */ +struct rng_ctx shuffle_rng; + static char *db_path; static __thread sqlite3 *hdl; @@ -887,6 +892,24 @@ db_transaction_end(void) } } +void +db_transaction_rollback(void) +{ + char *query = "ROLLBACK TRANSACTION;"; + char *errmsg; + int ret; + + DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query); + + ret = db_exec(query, &errmsg); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "SQL error running '%s': %s\n", query, errmsg); + + sqlite3_free(errmsg); + } +} + /* Queries */ static int @@ -4138,6 +4161,1464 @@ db_speaker_clear_all(void) db_query_run("UPDATE speakers SET selected = 0;", 0, 0); } +/* Queue */ + +static int +queue_fetch_byitemid(struct db_queue_enum *queue_enum, uint32_t item_id, struct db_queue_item *queue_item, int keep_item); + + +void +free_queue_item(struct db_queue_item *queue_item, int content_only) +{ + if (queue_item->path) + free(queue_item->path); + if (queue_item->virtual_path) + free(queue_item->virtual_path); + if (queue_item->title) + free(queue_item->title); + if (queue_item->artist) + free(queue_item->artist); + if (queue_item->album_artist) + free(queue_item->album_artist); + if (queue_item->album) + free(queue_item->album); + if (queue_item->genre) + free(queue_item->genre); + if (queue_item->artist_sort) + free(queue_item->artist_sort); + if (queue_item->album_sort) + free(queue_item->album_sort); + if (queue_item->album_artist_sort) + free(queue_item->album_artist_sort); + + if (!content_only && queue_item) + free(queue_item); +} + +/* + * Returns the queue version from the admin table + * + * @return queue version + */ +int +db_queue_get_version() +{ + char *version; + int32_t plversion; + int ret; + + plversion = 0; + version = db_admin_get("plversion"); + if (version) + { + ret = safe_atoi32(version, &plversion); + free(version); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not get playlist version\n"); + return -1; + } + } + + return plversion; +} + +/* + * Increments the version of the queue in the admin table and notifies listener of LISTENER_PLAYLIST + * about the change. + * + * This function must be called after successfully modifying the queue table in order to send + * notification messages to the clients (e. g. dacp or mpd clients). + */ +static void +queue_inc_version_and_notify() +{ + int plversion; + char version[10]; + int ret; + + db_transaction_begin(); + + plversion = db_queue_get_version(); + if (plversion < 0) + plversion = 0; + + plversion++; + ret = snprintf(version, sizeof(version), "%d", plversion); + if (ret >= sizeof(version)) + { + DPRINTF(E_LOG, L_DB, "Error incrementing queue version. Could not convert version to string: %d\n", plversion); + db_transaction_rollback(); + return; + } + + ret = db_admin_update("plversion", version); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Error incrementing queue version. Could not update version in admin table: %d\n", plversion); + db_transaction_rollback(); + return; + } + + db_transaction_end(); + + listener_notify(LISTENER_PLAYLIST); +} + +static int +queue_add_mediafileinfo(struct db_media_file_info *dbmfi, int pos, int shuffle_pos) +{ +#define Q_TMPL "INSERT INTO queue " \ + "(id, file_id, song_length, data_kind, media_kind, " \ + "pos, shuffle_pos, path, virtual_path, title, " \ + "artist, album_artist, album, genre, songalbumid, " \ + "time_modified, artist_sort, album_sort, album_artist_sort, year, " \ + "track, disc)" \ + "VALUES" \ + "(NULL, %s, %s, %s, %s, " \ + "%d, %d, %Q, %Q, %Q, " \ + "%Q, %Q, %Q, %Q, %s, " \ + "%s, %Q, %Q, %Q, %s, " \ + "%s, %s);" + + char *query; + int ret; + + query = sqlite3_mprintf(Q_TMPL, + dbmfi->id, dbmfi->song_length, dbmfi->data_kind, dbmfi->media_kind, + pos, pos, dbmfi->path, dbmfi->virtual_path, dbmfi->title, + dbmfi->artist, dbmfi->album_artist, dbmfi->album, dbmfi->genre, dbmfi->songalbumid, + dbmfi->time_modified, dbmfi->artist_sort, dbmfi->album_sort, dbmfi->album_artist_sort, dbmfi->year, + dbmfi->track, dbmfi->disc); + ret = db_query_run(query, 1, 0); + + return ret; + +#undef Q_TMPL +} + +/* + * Adds the files matching the given query to the queue after the item with the given item id + * + * The files table is queried with the given parameters and all found files are added after the + * item with the given item id to the "normal" queue. They are appended to end of the shuffled queue + * (assuming that the shuffled queue will get reshuffled after adding new items). + * + * The function returns -1 on failure (e. g. error reading from database) and if the given item id + * does not exist. It wraps all database access in a transaction and performs a rollback if an error + * occurs, leaving the queue in a consistent state. + * + * @param qp Query parameters for the files table + * @param item_id Files are added after item with this id + * @return 0 on success, -1 on failure + */ +int +db_queue_add_by_queryafteritemid(struct query_params *qp, uint32_t item_id) +{ + struct db_media_file_info dbmfi; + char *query; + int shuffle_pos; + int pos; + int ret; + + db_transaction_begin(); + + // Position of the first new item + pos = db_queue_get_pos(item_id, 0); + if (pos < 0) + { + DPRINTF(E_LOG, L_DB, "Could not fetch queue item for item-id %d\n", item_id); + db_transaction_rollback(); + return -1; + } + pos++; + + // Shuffle position of the first new item + shuffle_pos = db_queue_get_count(); + if (shuffle_pos < 0) + { + DPRINTF(E_LOG, L_DB, "Could not get count from queue\n"); + db_transaction_rollback(); + return -1; + } + + // Start query for new items from files table + ret = db_query_start(qp); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not start query\n"); + db_transaction_rollback(); + return -1; + } + + DPRINTF(E_DBG, L_DB, "Player queue query returned %d items\n", qp->results); + + // Update pos for all items after the item with item_id + query = sqlite3_mprintf("UPDATE queue SET pos = pos + %d WHERE pos > %d;", qp->results, (pos - 1)); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + // Iterate over new items from files table and insert into queue + while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) + { + ret = queue_add_mediafileinfo(&dbmfi, pos, shuffle_pos); + + if (ret < 0) + { + DPRINTF(E_DBG, L_DB, "Failed to add song with id %s (%s) to queue\n", dbmfi.id, dbmfi.title); + break; + } + + DPRINTF(E_DBG, L_DB, "Added song with id %s (%s) to queue\n", dbmfi.id, dbmfi.title); + shuffle_pos++; + pos++; + } + + db_query_end(qp); + + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Error fetching results\n"); + db_transaction_rollback(); + return -1; + } + + db_transaction_end(); + + queue_inc_version_and_notify(); + + return 0; +} + +/* + * Adds the files matching the given query to the queue + * + * The files table is queried with the given parameters and all found files are added to the end of the + * "normal" queue and the shuffled queue. + * + * The function returns -1 on failure (e. g. error reading from database). It wraps all database access + * in a transaction and performs a rollback if an error occurs, leaving the queue in a consistent state. + * + * @param qp Query parameters for the files table + * @param reshuffle If 1 queue will be reshuffled after adding new items + * @param item_id The base item id, all items after this will be reshuffled + * @return 0 on success, -1 on failure + */ +int +db_queue_add_by_query(struct query_params *qp, char reshuffle, uint32_t item_id) +{ + struct db_media_file_info dbmfi; + int pos; + int ret; + + db_transaction_begin(); + + pos = db_queue_get_count(); + if (pos < 0) + { + DPRINTF(E_LOG, L_DB, "Could not get count from queue\n"); + db_transaction_rollback(); + return -1; + } + + ret = db_query_start(qp); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not start query\n"); + db_transaction_rollback(); + return -1; + } + + DPRINTF(E_DBG, L_DB, "Player queue query returned %d items\n", qp->results); + + while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) + { + ret = queue_add_mediafileinfo(&dbmfi, pos, pos); + + if (ret < 0) + { + DPRINTF(E_DBG, L_DB, "Failed to add song id %s (%s)\n", dbmfi.id, dbmfi.title); + break; + } + + DPRINTF(E_DBG, L_DB, "Added song id %s (%s) to queue\n", dbmfi.id, dbmfi.title); + pos++; + } + + db_query_end(qp); + + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Error fetching results\n"); + db_transaction_rollback(); + return -1; + } + + ret = (int) sqlite3_last_insert_rowid(hdl); + + db_transaction_end(); + + // Reshuffle after adding new items + if (reshuffle) + { + db_queue_reshuffle(item_id); + } + else + { + queue_inc_version_and_notify(); + } + + return ret; +} + +/* + * Adds the items of the stored playlist with the given id to the end of the queue + * + * @param plid Id of the stored playlist + * @param reshuffle If 1 queue will be reshuffled after adding new items + * @param item_id The base item id, all items after this will be reshuffled + * @return 0 on success, -1 on failure + */ +int +db_queue_add_by_playlistid(int plid, char reshuffle, uint32_t item_id) +{ + struct query_params qp; + int ret; + + memset(&qp, 0, sizeof(struct query_params)); + + qp.id = plid; + qp.type = Q_PLITEMS; + qp.offset = 0; + qp.limit = 0; + qp.sort = S_NONE; + qp.idx_type = I_NONE; + + ret = db_queue_add_by_query(&qp, reshuffle, item_id); + + return ret; +} + +/* + * Adds the file with the given id to the queue + * + * @param id Id of the file + * @param reshuffle If 1 queue will be reshuffled after adding new items + * @param item_id The base item id, all items after this will be reshuffled + * @return 0 on success, -1 on failure + */ +int +db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id) +{ + struct query_params qp; + char buf[124]; + int ret; + + memset(&qp, 0, sizeof(struct query_params)); + + qp.id = 0; + qp.type = Q_ITEMS; + qp.offset = 0; + qp.limit = 0; + qp.sort = S_NONE; + snprintf(buf, sizeof(buf), "f.id = %" PRIu32, id); + qp.filter = buf; + + ret = db_queue_add_by_query(&qp, reshuffle, item_id); + + return ret; +} + +static int +queue_enum_start(struct db_queue_enum *queue_enum) +{ +#define Q_TMPL "SELECT * FROM queue WHERE %s ORDER BY %s;" + char *query; + char *orderby; + int ret; + + queue_enum->stmt = NULL; + + if (queue_enum->orderby_shufflepos) + orderby = "shuffle_pos"; + else + orderby = "pos"; + + if (queue_enum->filter) + query = sqlite3_mprintf(Q_TMPL, queue_enum->filter, orderby); + else + query = sqlite3_mprintf(Q_TMPL, "1=1", orderby); + + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + + return -1; + } + + DPRINTF(E_DBG, L_DB, "Starting enum '%s'\n", query); + + ret = db_blocking_prepare_v2(query, -1, &queue_enum->stmt, NULL); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); + + sqlite3_free(query); + return -1; + } + + sqlite3_free(query); + + return 0; + +#undef Q_TMPL +} + +static void +queue_enum_end(struct db_queue_enum *queue_enum) +{ + if (!queue_enum->stmt) + return; + + sqlite3_finalize(queue_enum->stmt); + sqlite3_free(queue_enum->filter); + queue_enum->filter = NULL; + queue_enum->stmt = NULL; +} + +static inline char * +strdup_if(char *str, int cond) +{ + if (str == NULL) + return NULL; + + if (cond) + return strdup(str); + + return str; +} + +static int +queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_item, int keep_item) +{ + int ret; + + memset(queue_item, 0, sizeof(struct db_queue_item)); + + if (!queue_enum->stmt) + { + DPRINTF(E_LOG, L_DB, "Queue enum not started!\n"); + return -1; + } + + ret = db_blocking_step(queue_enum->stmt); + if (ret == SQLITE_DONE) + { + DPRINTF(E_DBG, L_DB, "End of queue enum results\n"); + return 0; + } + else if (ret != SQLITE_ROW) + { + DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl)); + return -1; + } + + queue_item->item_id = (uint32_t)sqlite3_column_int(queue_enum->stmt, 0); + queue_item->file_id = (uint32_t)sqlite3_column_int(queue_enum->stmt, 1); + queue_item->pos = (uint32_t)sqlite3_column_int(queue_enum->stmt, 2); + queue_item->shuffle_pos = (uint32_t)sqlite3_column_int(queue_enum->stmt, 3); + queue_item->data_kind = sqlite3_column_int(queue_enum->stmt, 4); + queue_item->media_kind = sqlite3_column_int(queue_enum->stmt, 5); + queue_item->song_length = (uint32_t)sqlite3_column_int(queue_enum->stmt, 6); + queue_item->path = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 7), keep_item); + queue_item->virtual_path = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 8), keep_item); + queue_item->title = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 9), keep_item); + queue_item->artist = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 10), keep_item); + queue_item->album_artist = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 11), keep_item); + queue_item->album = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 12), keep_item); + queue_item->genre = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 13), keep_item); + queue_item->songalbumid = sqlite3_column_int64(queue_enum->stmt, 14); + queue_item->time_modified = sqlite3_column_int(queue_enum->stmt, 15); + queue_item->artist_sort = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 16), keep_item); + queue_item->album_sort = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 17), keep_item); + queue_item->album_artist_sort = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 18), keep_item); + queue_item->year = sqlite3_column_int(queue_enum->stmt, 19); + queue_item->track = sqlite3_column_int(queue_enum->stmt, 20); + queue_item->disc = sqlite3_column_int(queue_enum->stmt, 21); + + return 0; +} + +int +db_queue_enum_start(struct db_queue_enum *queue_enum) +{ + int ret; + + db_transaction_begin(); + + ret = queue_enum_start(queue_enum); + + if (ret < 0) + db_transaction_rollback(); + + return ret; +} + +void +db_queue_enum_end(struct db_queue_enum *queue_enum) +{ + if (!queue_enum->stmt) + return; + + queue_enum_end(queue_enum); + db_transaction_end(); +} + +int +db_queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_item) +{ + return queue_enum_fetch(queue_enum, queue_item, 0); +} + +int +db_queue_get_pos(uint32_t item_id, char shuffle) +{ +#define Q_TMPL "SELECT pos FROM queue WHERE id = %d;" +#define Q_TMPL_SHUFFLE "SELECT shuffle_pos FROM queue WHERE id = %d;" + + char *query; + int pos; + + if (shuffle) + query = sqlite3_mprintf(Q_TMPL_SHUFFLE, item_id); + else + query = sqlite3_mprintf(Q_TMPL, item_id); + + pos = db_get_count(query); + + sqlite3_free(query); + + return pos; + +#undef Q_TMPL +#undef Q_TMPL_SHUFFLE +} + +int +db_queue_get_pos_byfileid(uint32_t file_id, char shuffle) +{ +#define Q_TMPL "SELECT pos FROM queue WHERE file_id = %d LIMIT 1;" +#define Q_TMPL_SHUFFLE "SELECT shuffle_pos FROM queue WHERE file_id = %d LIMIT 1;" + + char *query; + int pos; + + if (shuffle) + query = sqlite3_mprintf(Q_TMPL_SHUFFLE, file_id); + else + query = sqlite3_mprintf(Q_TMPL, file_id); + + pos = db_get_count(query); + + sqlite3_free(query); + + return pos; + +#undef Q_TMPL +#undef Q_TMPL_SHUFFLE +} + +static int +queue_fetch_byitemid(struct db_queue_enum *queue_enum, uint32_t item_id, struct db_queue_item *queue_item, int keep_item) +{ + int ret; + + memset(queue_enum, 0, sizeof(struct db_queue_enum)); + queue_enum->filter = sqlite3_mprintf("id = %d", item_id); + + ret = queue_enum_start(queue_enum); + if (ret < 0) + { + sqlite3_free(queue_enum->filter); + return -1; + } + + ret = queue_enum_fetch(queue_enum, queue_item, keep_item); + + return ret; +} + +struct db_queue_item * +db_queue_fetch_byitemid(uint32_t item_id) +{ + struct db_queue_item *queue_item; + struct db_queue_enum queue_enum; + int ret; + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + queue_item = calloc(1, sizeof(struct db_queue_item)); + if (!queue_item) + { + DPRINTF(E_LOG, L_DB, "Out of memory for queue_item\n"); + return NULL; + } + + db_transaction_begin(); + ret = queue_fetch_byitemid(&queue_enum, item_id, queue_item, 1); + queue_enum_end(&queue_enum); + db_transaction_end(); + + if (ret < 0) + { + free_queue_item(queue_item, 0); + DPRINTF(E_LOG, L_DB, "Error fetching queue item by item id\n"); + return NULL; + } + + return queue_item; +} + +struct db_queue_item * +db_queue_fetch_byfileid(uint32_t file_id) +{ + struct db_queue_item *queue_item; + struct db_queue_enum queue_enum; + int ret; + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + queue_item = calloc(1, sizeof(struct db_queue_item)); + if (!queue_item) + { + DPRINTF(E_LOG, L_DB, "Out of memory for queue_item\n"); + return NULL; + } + + db_transaction_begin(); + + queue_enum.filter = sqlite3_mprintf("file_id = %d", file_id); + + ret = queue_enum_start(&queue_enum); + if (ret < 0) + { + sqlite3_free(queue_enum.filter); + db_transaction_end(); + free_queue_item(queue_item, 0); + DPRINTF(E_LOG, L_DB, "Error fetching queue item by file id\n"); + return NULL; + } + + ret = queue_enum_fetch(&queue_enum, queue_item, 1); + queue_enum_end(&queue_enum); + sqlite3_free(queue_enum.filter); + db_transaction_end(); + + if (ret < 0) + { + free_queue_item(queue_item, 0); + DPRINTF(E_LOG, L_DB, "Error fetching queue item by file id\n"); + return NULL; + } + + return queue_item; +} + +static int +queue_fetch_bypos(struct db_queue_enum *queue_enum, uint32_t pos, char shuffle, struct db_queue_item *queue_item, int keep_item) +{ + int ret; + + memset(queue_enum, 0, sizeof(struct db_queue_enum)); + if (shuffle) + queue_enum->filter = sqlite3_mprintf("shuffle_pos = %d", pos); + else + queue_enum->filter = sqlite3_mprintf("pos = %d", pos); + + ret = queue_enum_start(queue_enum); + if (ret < 0) + { + sqlite3_free(queue_enum->filter); + return -1; + } + + ret = queue_enum_fetch(queue_enum, queue_item, keep_item); + + return ret; +} + +struct db_queue_item * +db_queue_fetch_bypos(uint32_t pos, char shuffle) +{ + struct db_queue_item *queue_item; + struct db_queue_enum queue_enum; + int ret; + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + queue_item = calloc(1, sizeof(struct db_queue_item)); + if (!queue_item) + { + DPRINTF(E_LOG, L_MAIN, "Out of memory for queue_item\n"); + return NULL; + } + + db_transaction_begin(); + ret = queue_fetch_bypos(&queue_enum, pos, shuffle, queue_item, 1); + queue_enum_end(&queue_enum); + db_transaction_end(); + + if (ret < 0) + { + free_queue_item(queue_item, 0); + DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos id\n"); + return NULL; + } + + return queue_item; +} + +static int +queue_fetch_byposrelativetoitem(struct db_queue_enum *queue_enum, int pos, uint32_t item_id, char shuffle, struct db_queue_item *queue_item, int keep_item) +{ + int pos_absolute; + int ret; + + DPRINTF(E_DBG, L_DB, "Fetch by pos: pos (%d) relative to item with id (%d)\n", pos, item_id); + + pos_absolute = db_queue_get_pos(item_id, shuffle); + if (pos_absolute < 0) + { + return -1; + } + + DPRINTF(E_DBG, L_DB, "Fetch by pos: item (%d) has absolute pos %d\n", item_id, pos_absolute); + + pos_absolute += pos; + + ret = queue_fetch_bypos(queue_enum, pos_absolute, shuffle, queue_item, keep_item); + + DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->item_id, queue_item->pos, queue_item->file_id); + + return ret; +} + +struct db_queue_item * +db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) +{ + struct db_queue_item *queue_item; + struct db_queue_enum queue_enum; + int ret; + + DPRINTF(E_DBG, L_DB, "Fetch by pos: pos (%d) relative to item with id (%d)\n", pos, item_id); + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + queue_item = calloc(1, sizeof(struct db_queue_item)); + if (!queue_item) + { + DPRINTF(E_LOG, L_MAIN, "Out of memory for queue_item\n"); + return NULL; + } + + db_transaction_begin(); + + ret = queue_fetch_byposrelativetoitem(&queue_enum, pos, item_id, shuffle, queue_item, 1); + queue_enum_end(&queue_enum); + + db_transaction_end(); + + if (ret < 0) + { + free_queue_item(queue_item, 0); + DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos relative to item id\n"); + return NULL; + } + + DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->item_id, queue_item->pos, queue_item->file_id); + + return queue_item; +} + +struct db_queue_item * +db_queue_fetch_next(uint32_t item_id, char shuffle) +{ + return db_queue_fetch_byposrelativetoitem(1, item_id, shuffle); +} + +struct db_queue_item * +db_queue_fetch_prev(uint32_t item_id, char shuffle) +{ + return db_queue_fetch_byposrelativetoitem(-1, item_id, shuffle); +} + +/* + * Remove files that are disabled or non existant in the library and repair ordering of + * the queue (shuffle and normal) + */ +int +db_queue_cleanup() +{ +#define Q_TMPL "DELETE FROM queue WHERE NOT file_id IN (SELECT id from files WHERE disabled = 0);" +#define Q_TMPL_UPDATE "UPDATE queue SET pos = %d WHERE id = %d;" +#define Q_TMPL_UPDATE_SHUFFLE "UPDATE queue SET shuffle_pos = %d WHERE id = %d;" + + struct db_queue_enum queue_enum; + struct db_queue_item queue_item; + char *query; + int pos; + int deleted; + int ret; + + db_transaction_begin(); + + ret = db_query_run(Q_TMPL, 0, 0); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + deleted = sqlite3_changes(hdl); + if (deleted <= 0) + { + // Nothing to do + db_transaction_end(); + return 0; + } + + // Update position of normal queue + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + + ret = queue_enum_start(&queue_enum); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + pos = 0; + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0)) + { + if (queue_item.pos != pos) + { + query = sqlite3_mprintf(Q_TMPL_UPDATE, pos, queue_item.item_id); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Failed to update item with item-id: %d\n", queue_item.item_id); + break; + } + } + + pos++; + } + + queue_enum_end(&queue_enum); + + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + // Update position of shuffle queue + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + queue_enum.orderby_shufflepos = 1; + + ret = queue_enum_start(&queue_enum); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + pos = 0; + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0)) + { + if (queue_item.shuffle_pos != pos) + { + query = sqlite3_mprintf(Q_TMPL_UPDATE_SHUFFLE, pos, queue_item.item_id); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Failed to update item with item-id: %d\n", queue_item.item_id); + break; + } + } + + pos++; + } + + queue_enum_end(&queue_enum); + + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + db_transaction_end(); + queue_inc_version_and_notify(); + + return 0; + +#undef Q_TMPL_UPDATE_SHUFFLE +#undef Q_TMPL_UPDATE +#undef Q_TMPL +} + +int +db_queue_clear() +{ + int ret; + + db_transaction_begin(); + ret = db_query_run("DELETE FROM queue;", 0, 0); + + if (ret < 0) + { + db_transaction_rollback(); + } + else + { + db_transaction_end(); + queue_inc_version_and_notify(); + } + return ret; +} + +static int +queue_delete_item(struct db_queue_item *queue_item) +{ + char *query; + int ret; + + // Remove item with the given item_id + query = sqlite3_mprintf("DELETE FROM queue where id = %d;", queue_item->item_id); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + return -1; + } + + // Update pos for all items after the item with given item_id + query = sqlite3_mprintf("UPDATE queue SET pos = pos - 1 WHERE pos > %d;", queue_item->pos); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + return -1; + } + + // Update shuffle_pos for all items after the item with given item_id + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = shuffle_pos - 1 WHERE shuffle_pos > %d;", queue_item->shuffle_pos); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + return -1; + } + + return 0; +} + +int +db_queue_delete_byitemid(uint32_t item_id) +{ + struct db_queue_enum queue_enum; + struct db_queue_item queue_item; + int ret; + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + + db_transaction_begin(); + + ret = queue_fetch_byitemid(&queue_enum, item_id, &queue_item, 0); + + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + if (queue_item.item_id == 0) + { + queue_enum_end(&queue_enum); + db_transaction_end(); + return 0; + } + + ret = queue_delete_item(&queue_item); + + queue_enum_end(&queue_enum); + + if (ret < 0) + { + db_transaction_rollback(); + } + else + { + db_transaction_end(); + queue_inc_version_and_notify(); + } + + return ret; +} + +int +db_queue_delete_bypos(uint32_t pos, int count) +{ + struct db_queue_enum queue_enum; + struct db_queue_item queue_item; + int to_pos; + int ret; + + // Find items with the given position + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + + to_pos = pos + count; + queue_enum.filter = sqlite3_mprintf("pos => %d AND pos < %d", pos, to_pos); + + db_transaction_begin(); + + ret = queue_enum_start(&queue_enum); + if (ret < 0) + { + return -1; + } + + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0)) + { + ret = queue_delete_item(&queue_item); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Failed to delete item with item-id: %d\n", queue_item.item_id); + break; + } + } + + queue_enum_end(&queue_enum); + + if (ret < 0) + { + db_transaction_rollback(); + } + else + { + db_transaction_end(); + queue_inc_version_and_notify(); + } + + return ret; +} + +int +db_queue_delete_byposrelativetoitem(uint32_t pos, uint32_t item_id, char shuffle) +{ + struct db_queue_enum queue_enum; + struct db_queue_item queue_item; + int ret; + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + + db_transaction_begin(); + + ret = queue_fetch_byposrelativetoitem(&queue_enum, pos, item_id, shuffle, &queue_item, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + ret = queue_delete_item(&queue_item); + + queue_enum_end(&queue_enum); + + if (ret < 0) + { + db_transaction_rollback(); + } + else + { + db_transaction_end(); + queue_inc_version_and_notify(); + } + + return ret; +} + +/* + * Moves the queue item with the given id to the given position (zero-based). + * + * @param item_id Queue item id + * @param pos_to target position in the queue (zero-based) + * @return 0 on success, -1 on failure + */ +int +db_queue_move_byitemid(uint32_t item_id, int pos_to) +{ + char *query; + int pos_from; + int ret; + + db_transaction_begin(); + + // Find item with the given item_id + pos_from = db_queue_get_pos(item_id, 0); + if (pos_from < 0) + { + db_transaction_rollback(); + return -1; + } + + // Update pos for all items after the item with given item_id + query = sqlite3_mprintf("UPDATE queue SET pos = pos - 1 WHERE pos > %d;", pos_from); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + // Update pos for all items from the given pos_to + query = sqlite3_mprintf("UPDATE queue SET pos = pos + 1 WHERE pos >= %d;", pos_to); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + // Update item with the given item_id + query = sqlite3_mprintf("UPDATE queue SET pos = %d where id = %d;", pos_to, item_id); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + db_transaction_end(); + queue_inc_version_and_notify(); + + return 0; +} + +/* + * Moves the queue item at the given position to the given position (zero-based). + * + * @param pos_from Position of the queue item to move + * @param pos_to target position in the queue (zero-based) + * @return 0 on success, -1 on failure + */ +int +db_queue_move_bypos(int pos_from, int pos_to) +{ + struct db_queue_item queue_item; + struct db_queue_enum queue_enum; + char *query; + int ret; + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + + db_transaction_begin(); + + // Find item to move + ret = queue_fetch_bypos(&queue_enum, pos_from, 0, &queue_item, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + if (queue_item.item_id == 0) + { + queue_enum_end(&queue_enum); + db_transaction_end(); + return 0; + } + + // Update pos for all items after the item with given position + query = sqlite3_mprintf("UPDATE queue SET pos = pos - 1 WHERE pos > %d;", queue_item.pos); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + // Update pos for all items from the given pos_to + query = sqlite3_mprintf("UPDATE queue SET pos = pos + 1 WHERE pos >= %d;", pos_to); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + // Update item with the given item_id + query = sqlite3_mprintf("UPDATE queue SET pos = %d where id = %d;", pos_to, queue_item.item_id); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + queue_enum_end(&queue_enum); + db_transaction_end(); + queue_inc_version_and_notify(); + + return 0; +} + +/* + * Moves the queue item at the given position to the given target position. The positions + * are relavtive to the given base item (item id). + * + * @param from_pos Relative position of the queue item to the base item + * @param to_offset Target position relative to the base item + * @param item_id The base item id (normaly the now playing item) + * @return 0 on success, -1 on failure + */ +int +db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_t item_id, char shuffle) +{ + struct db_queue_enum queue_enum; + struct db_queue_item queue_item; + char *query; + int pos_move_from; + int pos_move_to; + int ret; + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + + db_transaction_begin(); + + DPRINTF(E_DBG, L_DB, "Move by pos: from %d offset %d relative to item (%d)\n", from_pos, to_offset, item_id); + + // Find item with the given item_id + ret = queue_fetch_byitemid(&queue_enum, item_id, &queue_item, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + DPRINTF(E_DBG, L_DB, "Move by pos: base item (id=%d, pos=%d, file-id=%d)\n", queue_item.item_id, queue_item.pos, queue_item.file_id); + + if (queue_item.item_id == 0) + { + queue_enum_end(&queue_enum); + db_transaction_end(); + return 0; + } + + // Calculate the position of the item to move + if (shuffle) + pos_move_from = queue_item.shuffle_pos + from_pos; + else + pos_move_from = queue_item.pos + from_pos; + + // Calculate the position where to move the item to + if (shuffle) + pos_move_to = queue_item.shuffle_pos + to_offset; + else + pos_move_to = queue_item.pos + to_offset; + + if (pos_move_to < pos_move_from) + { + /* + * Moving an item to a previous position seems to send an offset incremented by one + */ + pos_move_to++; + } + + queue_enum_end(&queue_enum); + + DPRINTF(E_DBG, L_DB, "Move by pos: absolute pos: move from %d to %d\n", pos_move_from, pos_move_to); + + // Find item to move + ret = queue_fetch_bypos(&queue_enum, pos_move_from, shuffle, &queue_item, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + DPRINTF(E_DBG, L_DB, "Move by pos: move item (id=%d, pos=%d, file-id=%d)\n", queue_item.item_id, queue_item.pos, queue_item.file_id); + + if (queue_item.item_id == 0) + { + queue_enum_end(&queue_enum); + db_transaction_end(); + return 0; + } + + // Update pos for all items after the item with given position + if (shuffle) + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = shuffle_pos - 1 WHERE shuffle_pos > %d;", queue_item.shuffle_pos); + else + query = sqlite3_mprintf("UPDATE queue SET pos = pos - 1 WHERE pos > %d;", queue_item.pos); + + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + // Update pos for all items from the given pos_to + if (shuffle) + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = shuffle_pos + 1 WHERE shuffle_pos >= %d;", pos_move_to); + else + query = sqlite3_mprintf("UPDATE queue SET pos = pos + 1 WHERE pos >= %d;", pos_move_to); + + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + // Update item with the given item_id + if (shuffle) + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = %d where id = %d;", pos_move_to, queue_item.item_id); + else + query = sqlite3_mprintf("UPDATE queue SET pos = %d where id = %d;", pos_move_to, queue_item.item_id); + + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + queue_enum_end(&queue_enum); + db_transaction_rollback(); + return -1; + } + + queue_enum_end(&queue_enum); + db_transaction_end(); + queue_inc_version_and_notify(); + + return 0; +} + +/* + * Reshuffles the shuffle queue + * + * If the given item_id is 0, the whole shuffle queue is reshuffled, otherwise the + * queue is reshuffled after the item with the given id (excluding this item). + * + * @param item_id The base item, after this item the queue is reshuffled + * @return 0 on success, -1 on failure + */ +int +db_queue_reshuffle(uint32_t item_id) +{ + char *query; + int pos; + int count; + struct db_queue_item queue_item; + int *shuffle_pos; + int len; + int i; + struct db_queue_enum queue_enum; + int ret; + + db_transaction_begin(); + + DPRINTF(E_DBG, L_DB, "Reshuffle queue after item with item-id: %d\n", item_id); + + // Reset the shuffled order + ret = db_query_run("UPDATE queue SET shuffle_pos = pos;", 0, 0); + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + pos = 0; + if (item_id > 0) + { + pos = db_queue_get_pos(item_id, 0); + if (pos < 0) + { + db_transaction_rollback(); + return -1; + } + + pos++; // Do not reshuffle the base item + } + + count = db_queue_get_count(); + + len = count - pos; + + DPRINTF(E_DBG, L_DB, "Reshuffle %d items off %d total items, starting from pos %d\n", len, count, pos); + + shuffle_pos = malloc(len * sizeof(int)); + for (i = 0; i < len; i++) + { + shuffle_pos[i] = i + pos; + } + + shuffle_int(&shuffle_rng, shuffle_pos, len); + + + + memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + queue_enum.filter = sqlite3_mprintf("pos >= %d", pos); + + ret = queue_enum_start(&queue_enum); + if (ret < 0) + { + sqlite3_free(queue_enum.filter); + db_transaction_rollback(); + return -1; + } + + i = 0; + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0) && (i < len)) + { + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = %d where id = %d;", shuffle_pos[i], queue_item.item_id); + ret = db_query_run(query, 1, 0); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Failed to delete item with item-id: %d\n", queue_item.item_id); + break; + } + + i++; + } + + queue_enum_end(&queue_enum); + + if (ret < 0) + { + db_transaction_rollback(); + return -1; + } + + db_transaction_end(); + queue_inc_version_and_notify(); + + return 0; +} + +int +db_queue_get_count() +{ + return db_get_count("SELECT COUNT(*) FROM queue;"); +} + /* Inotify */ int @@ -5012,6 +6493,8 @@ db_init(void) DPRINTF(E_LOG, L_DB, "Database OK with %d active files and %d active playlists\n", files, pls); + rng_init(&shuffle_rng); + return 0; } diff --git a/src/db.h b/src/db.h index f541ee10..984ad0cf 100644 --- a/src/db.h +++ b/src/db.h @@ -374,6 +374,60 @@ struct directory_enum { sqlite3_stmt *stmt; }; +struct db_queue_item +{ + /* Item-Id is a unique id for this queue item. If the same item appears multiple + times in the queue each corresponding queue item has its own id. */ + uint32_t item_id; + + /* Id of the file/item in the files database */ + uint32_t file_id; + + /* Length of the item in ms */ + uint32_t song_length; + + /* Data type of the item */ + enum data_kind data_kind; + /* Media type of the item */ + enum media_kind media_kind; + + uint32_t seek; + + uint32_t pos; + uint32_t shuffle_pos; + + char *path; + char *virtual_path; + + char *title; + char *artist; + char *album_artist; + char *album; + char *genre; + + int64_t songalbumid; + uint32_t time_modified; + + char *artist_sort; + char *album_sort; + char *album_artist_sort; + + uint32_t year; + uint32_t track; + uint32_t disc; +}; + +struct db_queue_enum +{ + /* 0 = ordered by position, 1 = ordered by position in shuffle queue */ + int orderby_shufflepos; + + char *filter; + + /* Private enum context, keep out */ + sqlite3_stmt *stmt; +}; + char * db_escape_string(const char *str); @@ -392,6 +446,9 @@ free_pli(struct playlist_info *pli, int content_only); void free_di(struct directory_info *di, int content_only); +void +free_queue_item(struct db_queue_item *queue_item, int content_only); + /* Maintenance and DB hygiene */ void db_hook_post_scan(void); @@ -409,6 +466,9 @@ db_transaction_begin(void); void db_transaction_end(void); +void +db_transaction_rollback(void); + /* Queries */ int db_query_start(struct query_params *qp); @@ -646,6 +706,85 @@ db_speaker_get(uint64_t id, int *selected, int *volume); void db_speaker_clear_all(void); +/* Queue */ +int +db_queue_get_version(); + +int +db_queue_add_by_queryafteritemid(struct query_params *qp, uint32_t item_id); + +int +db_queue_add_by_query(struct query_params *qp, char reshuffle, uint32_t item_id); + +int +db_queue_add_by_playlistid(int plid, char reshuffle, uint32_t item_id); + +int +db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id); + +int +db_queue_enum_start(struct db_queue_enum *queue_enum); + +void +db_queue_enum_end(struct db_queue_enum *queue_enum); + +int +db_queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_item); + +struct db_queue_item * +db_queue_fetch_byitemid(uint32_t item_id); + +struct db_queue_item * +db_queue_fetch_byfileid(uint32_t file_id); + +struct db_queue_item * +db_queue_fetch_bypos(uint32_t pos, char shuffle); + +struct db_queue_item * +db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle); + +struct db_queue_item * +db_queue_fetch_next(uint32_t item_id, char shuffle); + +struct db_queue_item * +db_queue_fetch_prev(uint32_t item_id, char shuffle); + +int +db_queue_cleanup(); + +int +db_queue_clear(); + +int +db_queue_delete_byitemid(uint32_t item_id); + +int +db_queue_delete_bypos(uint32_t pos, int count); + +int +db_queue_delete_byposrelativetoitem(uint32_t pos, uint32_t item_id, char shuffle); + +int +db_queue_move_byitemid(uint32_t item_id, int pos_to); + +int +db_queue_move_bypos(int pos_from, int pos_to); + +int +db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_t item_id, char shuffle); + +int +db_queue_reshuffle(uint32_t item_id); + +int +db_queue_get_count(); + +int +db_queue_get_pos(uint32_t item_id, char shuffle); + +int +db_queue_get_pos_byfileid(uint32_t file_id, char shuffle); + /* Inotify */ int db_watch_clear(void); From c504abe45138424ebc5d0af51d1897b38b1c6111 Mon Sep 17 00:00:00 2001 From: chme Date: Tue, 8 Nov 2016 21:27:38 +0100 Subject: [PATCH 05/23] Use queue db table instead of in memory queue struct --- src/db.c | 25 ++ src/db.h | 3 + src/filescanner.c | 17 +- src/httpd_dacp.c | 472 +++++++++++++----------- src/mpd.c | 411 +++++++++++---------- src/player.c | 908 ++++++++++------------------------------------ src/player.h | 63 +--- 7 files changed, 715 insertions(+), 1184 deletions(-) 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); From a023595b6fe95d7bba557f6dc6626e9d04e38ff0 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 20 Aug 2016 09:16:54 +0200 Subject: [PATCH 06/23] [queue] Remove queue.c/.h --- src/Makefile.am | 1 - src/queue.c | 1272 ----------------------------------------------- src/queue.h | 116 ----- 3 files changed, 1389 deletions(-) delete mode 100644 src/queue.c delete mode 100644 src/queue.h diff --git a/src/Makefile.am b/src/Makefile.am index 06d46b44..cc0149d5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -105,7 +105,6 @@ forked_daapd_SOURCES = main.c \ rsp_query.c rsp_query.h \ daap_query.c daap_query.h \ player.c player.h \ - queue.c queue.h \ worker.c worker.h \ outputs.h outputs.c \ outputs/raop.c outputs/streaming.c outputs/dummy.c outputs/fifo.c \ diff --git a/src/queue.c b/src/queue.c deleted file mode 100644 index 718eaa91..00000000 --- a/src/queue.c +++ /dev/null @@ -1,1272 +0,0 @@ -/* - * Copyright (C) 2015 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "queue.h" - -#include -#include -#include -#include - -#include "logger.h" -#include "misc.h" -#include "rng.h" - - -/* - * Internal representation of an item in a queue. It links to the previous and the next item - * in the queue for shuffle on/off. To access the properties use the queueitem_* functions. - */ -struct queue_item -{ - /* Item-Id is a unique id for this queue item. If the same item appears multiple - times in the queue each corresponding queue item has its own id. */ - unsigned int item_id; - - /* Id of the file/item in the files database */ - uint32_t id; - - /* Length of the item in ms */ - unsigned int len_ms; - - /* Data type of the item */ - enum data_kind data_kind; - /* Media type of the item */ - enum media_kind media_kind; - - /* Link to the previous/next item in the queue */ - struct queue_item *next; - struct queue_item *prev; - - /* Link to the previous/next item in the shuffle queue */ - struct queue_item *shuffle_next; - struct queue_item *shuffle_prev; -}; - -/* - * The queue struct references two (double) linked lists of queue_item. One for the play-queue - * and one for the shuffle-queue. - * - * Both linked lists start with the "head" item. The head item is not a media item, instead it is - * an internal item created during initialization of a new queue struct (see queue_new() function). - * The head item is always the first item in the queue and will only be removed when queue is - * destructed. - * - * The linked lists are circular, therefor the last item in a list has the first item (the head item) - * as "next" and the first item in the queue (the head item) has the last item as "prev" linked. - * - * An empty queue (with no media items) will only consist of the head item pointing to itself. - */ -struct queue -{ - /* The queue item id of the last inserted item */ - unsigned int last_inserted_item_id; - - /* The version number of the queue */ - unsigned int version; - - /* Shuffle RNG state */ - struct rng_ctx shuffle_rng; - - /* - * The head item in the queue is not an actual media item, instead it is the - * starting point for the play-queue and the shuffle-queue. It always has the - * item-id 0. The queue is circular, the last item of the queue has the head - * item as "next" and the head item has the last item as "prev". - */ - struct queue_item *head; -}; - - -/* - * Creates and initializes a new queue - */ -struct queue * -queue_new() -{ - struct queue *queue; - - queue = (struct queue *)calloc(1, sizeof(struct queue)); - queue->head = (struct queue_item *)calloc(1, sizeof(struct queue_item)); - - // Create the head item and make the queue circular (head points to itself) - queue->head->next = queue->head; - queue->head->prev = queue->head; - queue->head->shuffle_next = queue->head; - queue->head->shuffle_prev = queue->head; - - rng_init(&queue->shuffle_rng); - - return queue; -} - -/* - * Frees the given item and all linked (next) items - */ -static void -queue_items_free(struct queue_item *item) -{ - struct queue_item *temp; - struct queue_item *next; - - if (!item) - return; - - // Make the queue non-circular - if (item->prev) - item->prev->next = NULL; - - next = item; - while (next) - { - temp = next->next; - free(next); - next = temp; - } -} - -/* - * Frees the given queue and all the items in it - */ -void -queue_free(struct queue *queue) -{ - queue_items_free(queue->head); - free(queue); -} - -/* - * Returns the number of media items in the queue - * - * @param queue The queue - * @return The number of items in the queue - */ -unsigned int -queue_count(struct queue *queue) -{ - struct queue_item *item; - int count; - - count = 0; - - for (item = queue->head->next; item != queue->head; item = item->next) - { - count++; - } - - return count; -} - -/* - * Returns the next item in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - */ -static struct queue_item * -item_next(struct queue_item *item, char shuffle) -{ - if (shuffle) - return item->shuffle_next; - return item->next; -} - -/* - * Returns the previous item in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - */ -static struct queue_item * -item_prev(struct queue_item *item, char shuffle) -{ - if (shuffle) - return item->shuffle_prev; - return item->prev; -} - -/* - * Returns the (0-based) position of the first item with the given dbmfi-id. - * If no item is found for the given id, it returns -1. - */ -int -queueitem_pos(struct queue_item *item, uint32_t id) -{ - struct queue_item *temp; - int pos; - - if (id == 0 || item->id == id) - return 0; - - pos = 1; - for (temp = item->next; (temp != item) && temp->id != id; temp = temp->next) - { - pos++; - } - - if (temp == item) - { - // Item with given (database) id does not exists - return -1; - } - - return pos; -} - -/* - * Returns the id of the item/file in the files database table - */ -uint32_t -queueitem_id(struct queue_item *item) -{ - return item->id; -} - -/* - * Returns the queue-item-id - */ -unsigned int -queueitem_item_id(struct queue_item *item) -{ - return item->item_id; -} - -/* - * Returns the length of the item in milliseconds - */ -unsigned int -queueitem_len(struct queue_item *item) -{ - return item->len_ms; -} - -/* - * Returns the data-kind - */ -enum data_kind -queueitem_data_kind(struct queue_item *item) -{ - return item->data_kind; -} - -/* - * Returns the media-kind - */ -enum media_kind -queueitem_media_kind(struct queue_item *item) -{ - return item->media_kind; -} - -/* - * Returns the item with the given item_id in the queue - * - * @param queue The queue - * @param item_id The unique id of the item in the queue - * @return Item with the given item_id or NULL if not found - */ -static struct queue_item * -queueitem_get_byitemid(struct queue *queue, int item_id) -{ - struct queue_item *item; - - for (item = queue->head->next; item != queue->head && item->item_id != item_id; item = item->next) - { - // Iterate through the queue until the item with item_id is found - } - - if (item == queue->head && item_id != 0) - return NULL; - - return item; -} - -/* - * Returns the item at the given index (0-based) in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - * - * @param queue The queue - * @param index Index of item in the queue (0-based) - * @param shuffle Play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - * @return Item at position in the queue or NULL if not found - */ -static struct queue_item * -queueitem_get_byindex(struct queue *queue, unsigned int index, char shuffle) -{ - struct queue_item *item; - int i; - - i = 0; - for (item = item_next(queue->head, shuffle); item != queue->head && i < index; item = item_next(item, shuffle)) - { - i++; - } - - if (item == queue->head) - return NULL; - - return item; -} - -/* - * Returns the item at the given position relative to the item with the given item_id in the - * play queue (shuffle = 0) or shuffle queue (shuffle = 1). - * - * The item with item_id is at pos == 0. - * - * @param queue The queue - * @param pos The position relative to the item with given queue-item-id - * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue - * @return Item at position in the queue or NULL if not found - */ -static struct queue_item * -queueitem_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle) -{ - struct queue_item *item_base; - struct queue_item *item; - int i; - - item_base = queueitem_get_byitemid(queue, item_id); - - if (!item_base) - return NULL; - - i = 0; - for (item = item_base; i < pos; item = item_next(item, shuffle)) - { - i++; - } - - if (item == queue->head) - return NULL; - - return item; -} - -/* - * Returns the item with the given item_id in the queue - * - * @param queue The queue - * @param item_id The unique id of the item in the queue - * @return Item with the given item_id or NULL if not found - */ -struct queue_item * -queue_get_byitemid(struct queue *queue, unsigned int item_id) -{ - struct queue_item *item; - - item = queueitem_get_byitemid(queue, item_id); - - if (!item) - return NULL; - - return item; -} - -/* - * Returns the item at the given index (0-based) in the play queue (shuffle = 0) or shuffle queue (shuffle = 1) - * - * @param queue The queue - * @param index Position of item in the queue (zero-based) - * @param shuffle Play queue (shuffle = 0) or shuffle queue (shuffle = 1) - * @return Item at index in the queue or NULL if not found - */ -struct queue_item * -queue_get_byindex(struct queue *queue, unsigned int index, char shuffle) -{ - struct queue_item *item; - - item = queueitem_get_byindex(queue, index, shuffle); - - if (!item) - return NULL; - - return item; -} - -/* - * Returns the item at the given position relative to the item with the given item_id in the - * play queue (shuffle = 0) or shuffle queue (shuffle = 1). - * - * The item with item_id is at pos == 0. - * - * @param queue The queue - * @param item_id The unique id of the item in the queue - * @param pos The position relative to the item with given queue-item-id - * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue - * @return Item at position in the queue or NULL if not found - */ -struct queue_item * -queue_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle) -{ - struct queue_item *item; - - item = queueitem_get_bypos(queue, item_id, pos, shuffle); - - if (!item) - return NULL; - - return item; -} - -/* - * Returns the index of the item with the given item-id (unique id in the queue) - * or -1 if the item does not exist. Depending on the given shuffle value, the position - * is either the on in the play-queue (shuffle = 0) or the shuffle-queue (shuffle = 1). - * - * @param queue The queue to search the item - * @param item_id The id of the item in the queue - * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue - * @return Index (0-based) of the item in the given queue or -1 if it does not exist - */ -int -queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle) -{ - struct queue_item *item; - int pos; - - pos = 0; - for (item = item_next(queue->head, shuffle); item != queue->head && item->item_id != item_id; item = item_next(item, shuffle)) - { - pos++; - } - - if (item == queue->head) - // Item not found - return -1; - - return pos; -} - -/* - * Return the next item in the queue for the item with the given item-id. - * - * @param queue The queue - * @param item_id The id of the item in the queue - * @param shuffle If 0 return the next item in the play-queue, if 1 the next item in the shuffle-queue - * @param r_mode Repeat mode - * @param reshuffle If 1 and repeat mode is "repeat all" reshuffles the queue on wrap around - * @return The next item - */ -struct queue_item * -queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode, int reshuffle) -{ - struct queue_item *item; - - item = queueitem_get_byitemid(queue, item_id); - - if (!item) - // Item not found, start playing from the start of the queue - item = queue->head; - - if (r_mode == REPEAT_SONG && item != queue->head) - return item; - - item = item_next(item, shuffle); - - if (item == queue->head && r_mode == REPEAT_ALL) - { - // Repeat all and end of queue reached, return first item in the queue - if (reshuffle) - queue_shuffle(queue, 0); - item = item_next(queue->head, shuffle); - } - - if (item == queue->head) - return NULL; - - return item; -} - -/* - * Return the previous item in the queue for the item with the given item-id. - * - * @param queue The queue - * @param item_id The id of the item in the queue - * @param shuffle If 0 return the next item in the play-queue, if 1 the next item in the shuffle-queue - * @param r_mode Repeat mode - * @return The previous item - */ -struct queue_item * -queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode) -{ - struct queue_item *item; - - item = queueitem_get_byitemid(queue, item_id); - - if (!item) - // Item not found - return NULL; - - if (r_mode == REPEAT_SONG && item != queue->head) - return item; - - item = item_prev(item, shuffle); - - if (item == queue->head && r_mode == REPEAT_ALL) - { - // Repeat all and start of queue reached, return last item in the queue - item = item_prev(queue->head, shuffle); - } - - if (item == queue->head) - return NULL; - - return item; -} - -/* - * Creates a new queue with a copy of the items of the given queue. - * - * The given number of items (count) are copied from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - * starting with the item at the given index (0-based). - * - * If count == 0, all items from the given index up to the end of the queue will be returned. - * - * @param queue The queue - * @param index Index of the first item in the queue - * @param count Maximum number of items to copy (if 0 all remaining items after index) - * @param shuffle If 0 the play-queue, if 1 the shuffle queue - * @return A new queue with the specified items - */ -struct queue * -queue_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle) -{ - struct queue *qi; - struct queue_item *qii; - struct queue_item *item; - int i; - unsigned int qlength; - int qii_size; - - qi = queue_new(); - - qlength = queue_count(queue); - - qii_size = qlength - index; - if (count > 0 && count < qii_size) - qii_size = count; - - if (qii_size <= 0) - { - return qi; - } - - item = queueitem_get_byindex(queue, index, shuffle); - - if (!item) - return NULL; - - i = 0; - for (; item != queue->head && i < qii_size; item = item_next(item, shuffle)) - { - qii = malloc(sizeof(struct queue_item)); - qii->id = item->id; - qii->item_id = item->item_id; - qii->len_ms = item->len_ms; - qii->data_kind = item->data_kind; - qii->media_kind = item->media_kind; - qii->next = qii; - qii->prev = qii; - qii->shuffle_next = qii; - qii->shuffle_prev = qii; - - queue_add(qi, qii); - - // queue_add(...) changes the queue item-id, reset the item-id to the original value - qii->item_id = item->item_id; - - i++; - } - - return qi; -} - -/* - * Creates a new queue with a copy of the items of the given queue. - * - * The given number of items (count) are copied from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - * starting after the item with the given item_id. The item with item_id is excluded, therefor the first item - * is the one after the item with item_id. - * - * If count == 0, all items from the given index up to the end of the queue will be returned. - * - * @param queue The queue - * @param item_id The unique id of the item in the queue - * @param count Maximum number of items to copy (if 0 all remaining items after index) - * @param shuffle If 0 the play-queue, if 1 the shuffle queue - * @return A new queue with the specified items - */ -struct queue * -queue_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle) -{ - int pos; - struct queue *qi; - - pos = queue_index_byitemid(queue, item_id, shuffle); - - if (pos < 0) - pos = 0; - else - pos = pos + 1; // exclude the item with the given item-id - - qi = queue_new_byindex(queue, pos, count, shuffle); - - return qi; -} - -/* - * Adds items to the queue after the given item - * - * @param queue The queue to add the new items - * @param item_new The item(s) to add - * @param item_prev The item to append the new items - */ -static void -queue_add_afteritem(struct queue *queue, struct queue_item *item_new, struct queue_item *item_prev) -{ - struct queue_item *item; - struct queue_item *item_tail; - - if (!item_new) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid new item given to add items\n"); - return; - } - - // Check the item after which the new items will be added - if (!item_prev) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid previous item given to add items\n"); - queue_items_free(item_new); - return; - } - - // Set item-id for all new items - queue->last_inserted_item_id++; - item_new->item_id = queue->last_inserted_item_id; - for (item = item_new->next; item != item_new; item = item->next) - { - queue->last_inserted_item_id++; - item->item_id = queue->last_inserted_item_id; - } - - // Add items into the queue - item_tail = item_new->prev; - - item_tail->next = item_prev->next; - item_tail->shuffle_next = item_prev->shuffle_next; - item_prev->next->prev = item_tail; - item_prev->shuffle_next->shuffle_prev = item_tail; - - item_prev->next = item_new; - item_prev->shuffle_next = item_new; - item_new->prev = item_prev; - item_new->shuffle_prev = item_prev; -} - -/* - * Adds items to the end of the queue - * - * @param queue The queue to add the new items - * @param item The item(s) to add - */ -void -queue_add(struct queue *queue, struct queue_item *item) -{ - queue_add_afteritem(queue, item, queue->head->prev); -} - -/* - * Adds items to the queue after the item with the given item id (id of the item in the queue) - * - * @param queue The queue to add the new items - * @param item The item(s) to add - * @param item_id The item id after which the new items will be inserted - */ -void -queue_add_after(struct queue *queue, struct queue_item *item, unsigned int item_id) -{ - struct queue_item *item_prev; - - // Get the item after which the new items will be added - item_prev = queueitem_get_byitemid(queue, item_id); - queue_add_afteritem(queue, item, item_prev); -} - -static void -queue_move_item_before_item(struct queue *queue, struct queue_item *item, struct queue_item *item_next, char shuffle) -{ - if (!item_next) - { - // If item_next is NULL the item should be inserted at the end of the queue (directly before the head item) - item_next = queue->head; - } - - // Remove item from the queue - if (shuffle) - { - item->shuffle_prev->shuffle_next = item->shuffle_next; - item->shuffle_next->shuffle_prev = item->shuffle_prev; - } - else - { - item->prev->next = item->next; - item->next->prev = item->prev; - } - - // Insert item into the queue before the item at the target postion - if (shuffle) - { - item_next->shuffle_prev->shuffle_next = item; - item->shuffle_prev = item_next->shuffle_prev; - - item_next->shuffle_prev = item; - item->shuffle_next = item_next; - } - else - { - item_next->prev->next = item; - item->prev = item_next->prev; - - item_next->prev = item; - item->next = item_next; - } -} - -/* - * Moves the item at from_pos to to_pos in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - * - * The position arguments are relativ to the item with the given id. At position = 1 is the first item - * after the item with the given id (either in the play-queue or shuffle-queue, depending on the shuffle - * argument). - * - * @param queue The queue to move items - * @param from_pos The position of the first item to be moved - * @param to_pos The position to move the items - * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue - */ -void -queue_move_bypos(struct queue *queue, unsigned int item_id, unsigned int from_pos, unsigned int to_offset, char shuffle) -{ - struct queue_item *item; - struct queue_item *item_next; - - // Get the item to be moved - item = queueitem_get_bypos(queue, item_id, from_pos, shuffle); - if (!item) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid position given to move items\n"); - return; - } - - // Get the item at the target position - item_next = queueitem_get_bypos(queue, item_id, (to_offset + 1), shuffle); - - queue_move_item_before_item(queue, item, item_next, shuffle); -} - -void -queue_move_byindex(struct queue *queue, unsigned int from_pos, unsigned int to_pos, char shuffle) -{ - struct queue_item *item; - struct queue_item *item_next; - - if (from_pos == to_pos) - return; - - // Get the item to be moved - item = queueitem_get_byindex(queue, from_pos, shuffle); - if (!item) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid position given to move items\n"); - return; - } - - // Check if the index of the item to move is lower than the target index - // If that is the case, increment the target position, because the given to_pos - // is based on the queue without the moved item. - if (from_pos < to_pos) - to_pos++; - - // Get the item at the target position - item_next = queueitem_get_byindex(queue, to_pos, shuffle); - - queue_move_item_before_item(queue, item, item_next, shuffle); -} - -/* - * Moves the item with the given item-id to the index to_pos in the play-queue (shuffle = 0) - * or shuffle-queue (shuffle = 1) - * - * @param queue The queue to move item - * @param item_id The item-id of the to be moved - * @param to_pos The index to move the item - * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue - */ -void -queue_move_byitemid(struct queue *queue, unsigned int item_id, unsigned int to_pos, char shuffle) -{ - struct queue_item *item; - struct queue_item *item_next; - int from_pos; - - // Get the item to be moved - item = queueitem_get_byitemid(queue, item_id); - if (!item) - { - DPRINTF(E_LOG, L_PLAYER, "Item with item-id %d does not exist in the queue\n", item_id); - return; - } - - from_pos = queue_index_byitemid(queue, item_id, shuffle); - - if (from_pos == to_pos) - { - DPRINTF(E_DBG, L_PLAYER, "Ignore moving item %d from index %d to %d\n", item_id, from_pos, to_pos); - return; - } - - // Check if the index of the item to move is lower than the target index - // If that is the case, increment the target position, because the given to_pos - // is based on the queue without the moved item. - if (from_pos < to_pos) - to_pos++; - - // Get the item at the target position - item_next = queueitem_get_byindex(queue, to_pos, shuffle); - - queue_move_item_before_item(queue, item, item_next, shuffle); -} - -/* - * Removes the item from the queue and frees it - */ -static void -queue_remove_item(struct queue_item *item) -{ - struct queue_item *item_next; - struct queue_item *item_prev; - - item_next = item->next; - item_prev = item->prev; - - item_prev->next = item_next; - item_next->prev = item_prev; - - item_next = item->shuffle_next; - item_prev = item->shuffle_prev; - - item_prev->shuffle_next = item_next; - item_next->shuffle_prev = item_prev; - - item->next = NULL; - item->prev = NULL; - item->shuffle_next = NULL; - item->shuffle_prev = NULL; - - free(item); -} - -/* - * Removes the item with the given item-id from the queue - */ -void -queue_remove_byitemid(struct queue *queue, unsigned int item_id) -{ - struct queue_item *item; - - // Do not remove the head item - if (item_id <= 0) - return; - - // Get the item after which the items will be removed from the queue - item = queueitem_get_byitemid(queue, item_id); - if (!item) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid item-id given to remove items\n"); - return; - } - - queue_remove_item(item); -} - -/* - * Remove item at index from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - * - * @param queue The queue - * @param index The index of the item to be removed (0-based) - * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue - */ -void -queue_remove_byindex(struct queue *queue, unsigned int index, char shuffle) -{ - struct queue_item *item; - - // Get the item after which the items will be removed from the queue - item = queueitem_get_byindex(queue, index, shuffle); - if (!item) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid position given to remove items\n"); - return; - } - - queue_remove_item(item); -} - -/* - * Removes the item at pos from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) - * - * The position argument is relativ to the item with the given id. At position = 1 is the first item - * after the item with the given id (either in the play-queue or shuffle-queue, depending on the shuffle - * argument). - * - * @param queue The queue to add the new items - * @param item_id The unique id of the item in the queue - * @param pos The position of the first item to be removed - * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue - */ -void -queue_remove_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle) -{ - struct queue_item *item; - - // Get the item after which the items will be removed from the queue - item = queueitem_get_bypos(queue, item_id, pos, shuffle); - if (!item) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid position given to remove items\n"); - return; - } - - queue_remove_item(item); -} - -/* - * Removes all items from the queue - * - * @param queue The queue to clear - */ -void -queue_clear(struct queue *queue) -{ - struct queue_item *item; - - // Check if the queue is already empty - if (queue->head->next == queue->head) - return; - - // Remove the head item from the shuffle-queue - item = queue->head->shuffle_next; - item->shuffle_prev = queue->head->shuffle_prev; - queue->head->shuffle_prev->shuffle_next = item; - - // Remove the head item from the play-queue - item = queue->head->next; - item->prev = queue->head->prev; - queue->head->prev->next = item; - - // item now points to the first item in the play-queue (excluding the head item) - queue_items_free(item); - - // Make the queue circular again - queue->head->next = queue->head; - queue->head->prev = queue->head; - queue->head->shuffle_next = queue->head; - queue->head->shuffle_prev = queue->head; -} - -/* - * Resets the shuffle-queue to be identical to the play-queue and returns the item - * with the given item_id. - * - * If no item was found with the given item_id, it returns the head item. - */ -static struct queue_item * -queue_reset_and_find(struct queue *queue, unsigned int item_id) -{ - struct queue_item *item; - struct queue_item *temp; - - item = queue->head; - - item->shuffle_next = item->next; - item->shuffle_prev = item->prev; - - for (temp = item->next; temp != queue->head; temp = temp->next) - { - temp->shuffle_next = temp->next; - temp->shuffle_prev = temp->prev; - - if (temp->item_id == item_id) - item = temp; - } - - return item; -} - -/* - * Shuffles the queue - * - * If the item_id > 0, only the items in the queue after the item (excluding it) - * with the given id are shuffled. - * - * @param queue The queue to shuffle - * @param item_id 0 to shuffle the whole queue or the item-id after which the queue gets shuffled - */ -void -queue_shuffle(struct queue *queue, unsigned int item_id) -{ - struct queue_item *temp; - struct queue_item *item; - struct queue_item **item_array; - int nitems; - int i; - - item = queue_reset_and_find(queue, item_id); - - // Count items to reshuffle - nitems = 0; - for (temp = item->next; temp != queue->head; temp = temp->next) - { - nitems++; - } - - // Do not reshuffle queue with one item - if (nitems < 2) - return; - - // Construct array for number of items in queue - item_array = (struct queue_item **)malloc(nitems * sizeof(struct queue_item *)); - if (!item_array) - { - DPRINTF(E_LOG, L_PLAYER, "Could not allocate memory for shuffle array\n"); - return; - } - - // Fill array with items in queue - i = 0; - for (temp = item->next; temp != queue->head; temp = temp->next) - { - item_array[i] = temp; - i++; - } - - // Shuffle item array - shuffle_ptr(&queue->shuffle_rng, (void **)item_array, nitems); - - // Update shuffle-next/-prev for shuffled items - for (i = 0; i < nitems; i++) - { - temp = item_array[i]; - - if (i > 0) - temp->shuffle_prev = item_array[i - 1]; - else - temp->shuffle_prev = NULL; - - if (i < (nitems - 1)) - temp->shuffle_next = item_array[i + 1]; - else - temp->shuffle_next = NULL; - } - - // Insert shuffled items after item with given item_id - item->shuffle_next = item_array[0]; - item_array[0]->shuffle_prev = item; - - queue->head->shuffle_prev = item_array[nitems - 1]; - item_array[nitems - 1]->shuffle_next = queue->head; - - free(item_array); -} - -/* - * Creates a new queue item for the given media file - * - * @param dbmfi media file info - * @return The new queue item or NULL if an error occured - */ -static struct queue_item * -queue_item_new(struct db_media_file_info *dbmfi) -{ - struct queue_item *item; - uint32_t id; - uint32_t len_ms; - uint32_t data_kind; - uint32_t media_kind; - int ret; - - ret = safe_atou32(dbmfi->id, &id); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid song id in query result!\n"); - return NULL; - } - - ret = safe_atou32(dbmfi->song_length, &len_ms); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid song length in query result!\n"); - return NULL; - } - - ret = safe_atou32(dbmfi->data_kind, &data_kind); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid data kind in query result!\n"); - return NULL; - } - - ret = safe_atou32(dbmfi->media_kind, &media_kind); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid media kind in query result!\n"); - return NULL; - } - - item = (struct queue_item *) calloc(1, sizeof(struct queue_item)); - if (!item) - { - DPRINTF(E_LOG, L_PLAYER, "Out of memory for struct queue_item\n"); - return NULL; - } - - item->id = id; - item->len_ms = len_ms; - item->data_kind = data_kind; - item->media_kind = media_kind; - - return item; -} - -struct queue_item * -queueitem_make_byquery(struct query_params *qp) -{ - struct db_media_file_info dbmfi; - struct queue_item *item_head; - struct queue_item *item_tail; - struct queue_item *item_temp; - int ret; - - ret = db_query_start(qp); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Could not start query\n"); - return NULL; - } - - DPRINTF(E_DBG, L_PLAYER, "Player queue query returned %d items\n", qp->results); - - item_head = NULL; - item_tail = NULL; - while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) - { - item_temp = queue_item_new(&dbmfi); - if (!item_temp) - { - DPRINTF(E_LOG, L_PLAYER, "Error creating new queue_item for id '%s'\n", dbmfi.id); - continue; - } - - if (!item_head) - item_head = item_temp; - - if (item_tail) - { - item_tail->next = item_temp; - item_temp->prev = item_tail; - item_tail->shuffle_next = item_temp; - item_temp->shuffle_prev = item_tail; - } - - item_tail = item_temp; - - DPRINTF(E_DBG, L_PLAYER, "Added song id %s (%s)\n", dbmfi.id, dbmfi.title); - } - - db_query_end(qp); - - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Error fetching results\n"); - queue_items_free(item_tail); - return NULL; - } - - if (!item_head || !item_tail) - { - DPRINTF(E_INFO, L_PLAYER, "No item found to add to queue\n"); - return NULL; - } - - item_head->prev = item_tail; - item_tail->next = item_head; - item_head->shuffle_prev = item_tail; - item_tail->shuffle_next = item_head; - - return item_head; -} - -/* - * Makes a list of queue-items for the given playlist id (plid) - * - * @param plid Id of the playlist - * @return List of items for all playlist items - */ -struct queue_item * -queueitem_make_byplid(int plid) -{ - struct query_params qp; - struct queue_item *item; - - memset(&qp, 0, sizeof(struct query_params)); - - qp.id = plid; - qp.type = Q_PLITEMS; - qp.offset = 0; - qp.limit = 0; - qp.sort = S_NONE; - qp.idx_type = I_NONE; - - item = queueitem_make_byquery(&qp); - - return item; -} - -/* - * Makes a queue-item for the item/file with the given id - * - * @param id Id of the item/file in the db - * @return List of items containing only the item with the given id - */ -struct queue_item * -queueitem_make_byid(uint32_t id) -{ - struct query_params qp; - struct queue_item *item; - char buf[124]; - - memset(&qp, 0, sizeof(struct query_params)); - - qp.id = 0; - qp.type = Q_ITEMS; - qp.offset = 0; - qp.limit = 0; - qp.sort = S_NONE; - snprintf(buf, sizeof(buf), "f.id = %" PRIu32, id); - qp.filter = buf; - - item = queueitem_make_byquery(&qp); - - return item; -} diff --git a/src/queue.h b/src/queue.h deleted file mode 100644 index 556aec91..00000000 --- a/src/queue.h +++ /dev/null @@ -1,116 +0,0 @@ - -#ifndef SRC_QUEUE_H_ -#define SRC_QUEUE_H_ - - -#include "db.h" - -enum repeat_mode { - REPEAT_OFF = 0, - REPEAT_SONG = 1, - REPEAT_ALL = 2, -}; - - -/* - * Internal representation of a queue - */ -struct queue; - -/* - * Internal representation of a list of queue items - */ -struct queue_item; - - -struct queue * -queue_new(); - -void -queue_free(struct queue *queue); - -unsigned int -queue_count(struct queue *queue); - -int -queueitem_pos(struct queue_item *item, uint32_t id); - -uint32_t -queueitem_id(struct queue_item *item); - -unsigned int -queueitem_item_id(struct queue_item *item); - -unsigned int -queueitem_len(struct queue_item *item); - -enum data_kind -queueitem_data_kind(struct queue_item *item); - -enum media_kind -queueitem_media_kind(struct queue_item *item); - -struct queue_item * -queue_get_byitemid(struct queue *queue, unsigned int item_id); - -struct queue_item * -queue_get_byindex(struct queue *queue, unsigned int index, char shuffle); - -struct queue_item * -queue_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle); - -int -queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle); - -struct queue_item * -queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode, int reshuffle); - -struct queue_item * -queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode); - -struct queue * -queue_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle); - -struct queue * -queue_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle); - -void -queue_add(struct queue *queue, struct queue_item *item); - -void -queue_add_after(struct queue *queue, struct queue_item *item, unsigned int item_id); - -void -queue_move_bypos(struct queue *queue, unsigned int item_id, unsigned int from_pos, unsigned int to_offset, char shuffle); - -void -queue_move_byindex(struct queue *queue, unsigned int from_pos, unsigned int to_pos, char shuffle); - -void -queue_move_byitemid(struct queue *queue, unsigned int item_id, unsigned int to_pos, char shuffle); - -void -queue_remove_byitemid(struct queue *queue, unsigned int item_id); - -void -queue_remove_byindex(struct queue *queue, unsigned int index, char shuffle); - -void -queue_remove_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle); - -void -queue_clear(struct queue *queue); - -void -queue_shuffle(struct queue *queue, unsigned int item_id); - -struct queue_item * -queueitem_make_byquery(struct query_params *qp); - -struct queue_item * -queueitem_make_byplid(int plid); - -struct queue_item * -queueitem_make_byid(uint32_t id); - -#endif /* SRC_QUEUE_H_ */ From e08b4f3b01b039502eb78d99af5223d19bd3ab6e Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 12 Nov 2016 08:52:48 +0100 Subject: [PATCH 07/23] [rng] Cleanup: remove unused function 'shuffle_ptr' --- src/rng.c | 20 -------------------- src/rng.h | 3 --- 2 files changed, 23 deletions(-) diff --git a/src/rng.c b/src/rng.c index d36b13b3..00d0e5ce 100644 --- a/src/rng.c +++ b/src/rng.c @@ -124,26 +124,6 @@ rng_rand_range(struct rng_ctx *ctx, int32_t min, int32_t max) return min + res; } -/* Fisher-Yates shuffling algorithm - * Durstenfeld in-place shuffling variant - */ -void -shuffle_ptr(struct rng_ctx *ctx, void **values, int len) -{ - int i; - int32_t j; - void *tmp; - - for (i = len - 1; i > 0; i--) - { - j = rng_rand_range(ctx, 0, i + 1); - - tmp = values[i]; - values[i] = values[j]; - values[j] = tmp; - } -} - /* Fisher-Yates shuffling algorithm * Durstenfeld in-place shuffling variant */ diff --git a/src/rng.h b/src/rng.h index 12d980c8..200f4a52 100644 --- a/src/rng.h +++ b/src/rng.h @@ -18,9 +18,6 @@ rng_rand(struct rng_ctx *ctx); int32_t rng_rand_range(struct rng_ctx *ctx, int32_t min, int32_t max); -void -shuffle_ptr(struct rng_ctx *ctx, void **values, int len); - void shuffle_int(struct rng_ctx *ctx, int *values, int len); From a0590ce548181713fb43a21b5209cf5508a214ad Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 12 Nov 2016 13:23:41 +0100 Subject: [PATCH 08/23] [db/player] Icy metadata update --- src/db.c | 44 +++++++++++++++++++++++--------------------- src/db.h | 6 +++--- src/player.c | 4 ++-- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/db.c b/src/db.c index f6b627d4..d6298b42 100644 --- a/src/db.c +++ b/src/db.c @@ -2734,27 +2734,6 @@ db_file_update(struct media_file_info *mfi) #undef Q_TMPL } -void -db_file_update_icy(int id, char *artist, char *album) -{ -#define Q_TMPL "UPDATE files SET artist = TRIM(%Q), album = TRIM(%Q) WHERE id = %d;" - char *query; - - if (id == 0) - return; - - query = sqlite3_mprintf(Q_TMPL, artist, album, id); - if (!query) - { - DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); - - return; - } - - db_query_run(query, 1, 0); -#undef Q_TMPL -} - void db_file_save_seek(int id, uint32_t seek) { @@ -4290,6 +4269,29 @@ queue_inc_version_and_notify() listener_notify(LISTENER_PLAYLIST); } +void +db_queue_update_icymetadata(int id, char *artist, char *album) +{ +#define Q_TMPL "UPDATE queue SET artist = TRIM(%Q), album = TRIM(%Q) WHERE id = %d;" + char *query; + + if (id == 0) + return; + + query = sqlite3_mprintf(Q_TMPL, artist, album, id); + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + + return; + } + + db_query_run(query, 1, 0); + queue_inc_version_and_notify(); + +#undef Q_TMPL +} + static int queue_add_mediafileinfo(struct db_media_file_info *dbmfi, int pos, int shuffle_pos) { diff --git a/src/db.h b/src/db.h index 63652917..8c1e94c8 100644 --- a/src/db.h +++ b/src/db.h @@ -561,9 +561,6 @@ db_file_add(struct media_file_info *mfi); int db_file_update(struct media_file_info *mfi); -void -db_file_update_icy(int id, char *artist, char *album); - void db_file_save_seek(int id, uint32_t seek); @@ -713,6 +710,9 @@ db_speaker_clear_all(void); int db_queue_get_version(); +void +db_queue_update_icymetadata(int id, char *artist, char *album); + int db_queue_add_by_queryafteritemid(struct query_params *qp, uint32_t item_id); diff --git a/src/player.c b/src/player.c index ca3b8f63..88f3f0c0 100644 --- a/src/player.c +++ b/src/player.c @@ -520,7 +520,7 @@ update_icy_cb(void *arg) { struct http_icy_metadata *metadata = arg; - db_file_update_icy(metadata->id, metadata->artist, metadata->title); + db_queue_update_icymetadata(metadata->id, metadata->artist, metadata->title); http_icy_metadata_free(metadata, 1); } @@ -579,7 +579,7 @@ metadata_check_icy(void) if (metadata->title[0] == '\0') goto no_update; - metadata->id = cur_streaming->id; + metadata->id = cur_streaming->item_id; /* Defer the database update to the worker thread */ worker_execute(update_icy_cb, metadata, sizeof(struct http_icy_metadata), 0); From 6c66d39d912d504d52d7356e4be8a733541303fb Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 12 Nov 2016 18:45:54 +0100 Subject: [PATCH 09/23] [player] Fix segfault if player reaches end of queue, fix repeat single --- src/db.c | 31 +++++++++++++++++++++++++++++++ src/player.c | 19 +++++++++---------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/db.c b/src/db.c index d6298b42..e49c699b 100644 --- a/src/db.c +++ b/src/db.c @@ -4805,6 +4805,12 @@ db_queue_fetch_byitemid(uint32_t item_id) DPRINTF(E_LOG, L_DB, "Error fetching queue item by item id\n"); return NULL; } + else if (queue_item->item_id == 0) + { + // No item found + free_queue_item(queue_item, 0); + return NULL; + } return queue_item; } @@ -4849,6 +4855,12 @@ db_queue_fetch_byfileid(uint32_t file_id) DPRINTF(E_LOG, L_DB, "Error fetching queue item by file id\n"); return NULL; } + else if (queue_item->item_id == 0) + { + // No item found + free_queue_item(queue_item, 0); + return NULL; + } return queue_item; } @@ -4902,6 +4914,12 @@ db_queue_fetch_bypos(uint32_t pos, char shuffle) DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos id\n"); return NULL; } + else if (queue_item->item_id == 0) + { + // No item found + free_queue_item(queue_item, 0); + return NULL; + } return queue_item; } @@ -4961,6 +4979,12 @@ db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos relative to item id\n"); return NULL; } + else if (queue_item->item_id == 0) + { + // No item found + free_queue_item(queue_item, 0); + return NULL; + } DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->item_id, queue_item->pos, queue_item->file_id); @@ -5257,6 +5281,13 @@ db_queue_delete_byposrelativetoitem(uint32_t pos, uint32_t item_id, char shuffle db_transaction_rollback(); return -1; } + else if (queue_item.item_id == 0) + { + // No item found + queue_enum_end(&queue_enum); + db_transaction_end(); + return 0; + } ret = queue_delete_item(&queue_item); diff --git a/src/player.c b/src/player.c index 88f3f0c0..252e5472 100644 --- a/src/player.c +++ b/src/player.c @@ -1321,9 +1321,8 @@ source_next() else { queue_item = db_queue_fetch_next(cur_streaming->item_id, shuffle); - if (!!queue_item && repeat == REPEAT_ALL) + if (!queue_item && repeat == REPEAT_ALL) { - free_queue_item(queue_item, 0); if (shuffle) { db_queue_reshuffle(0); @@ -1336,11 +1335,16 @@ source_next() return NULL; } } - - ps = source_new(queue_item); - free_queue_item(queue_item, 0); } + if (!queue_item) + { + DPRINTF(E_DBG, L_PLAYER, "Reached end of queue\n"); + return NULL; + } + + ps = source_new(queue_item); + free_queue_item(queue_item, 0); return ps; } @@ -1412,11 +1416,6 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) DPRINTF(E_DBG, L_PLAYER, "New file\n"); 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) { From cef4fedfb479b40f39ff0cdb27a6e2b0f2d031b0 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 13 Nov 2016 12:24:25 +0100 Subject: [PATCH 10/23] Remove unused structs and player arguments --- src/httpd_dacp.c | 12 ++++++------ src/mpd.c | 16 ++++++++-------- src/player.c | 50 ++---------------------------------------------- src/player.h | 2 +- 4 files changed, 17 insertions(+), 63 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 742163ab..6b2f2936 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -622,7 +622,7 @@ seek_timer_cb(int fd, short what, void *arg) return; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) DPRINTF(E_LOG, L_DACP, "Player returned an error for start after seek\n"); } @@ -1287,7 +1287,7 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u free_queue_item(queue_item, 0); } else - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { @@ -1338,7 +1338,7 @@ dacp_reply_playpause(struct evhttp_request *req, struct evbuffer *evbuf, char ** } else { - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after pause\n"); @@ -1371,7 +1371,7 @@ dacp_reply_nextitem(struct evhttp_request *req, struct evbuffer *evbuf, char **u return; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after nextitem\n"); @@ -1403,7 +1403,7 @@ dacp_reply_previtem(struct evhttp_request *req, struct evbuffer *evbuf, char **u return; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after previtem\n"); @@ -1875,7 +1875,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, } else { - ret = player_playback_start(NULL); + ret = player_playback_start(); } if (ret < 0) diff --git a/src/mpd.c b/src/mpd.c index 491ad8e5..4ee6cbbb 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1063,7 +1063,7 @@ mpd_command_next(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_UNKNOWN; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { ret = asprintf(errmsg, "Player returned an error for start after nextitem"); @@ -1111,7 +1111,7 @@ mpd_command_pause(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) if (pause == 1) ret = player_playback_pause(); else - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { @@ -1179,7 +1179,7 @@ mpd_command_play(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) free_queue_item(queue_item, 0); } else - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { @@ -1242,7 +1242,7 @@ mpd_command_playid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) free_queue_item(queue_item, 0); } else - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { @@ -1274,7 +1274,7 @@ mpd_command_previous(struct evbuffer *evbuf, int argc, char **argv, char **errms return ACK_ERROR_UNKNOWN; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { ret = asprintf(errmsg, "Player returned an error for start after previtem"); @@ -1331,7 +1331,7 @@ mpd_command_seek(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_UNKNOWN; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { ret = asprintf(errmsg, "Player returned an error for start after seekcur"); @@ -1397,7 +1397,7 @@ mpd_command_seekid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_UNKNOWN; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { ret = asprintf(errmsg, "Player returned an error for start after seekcur"); @@ -1442,7 +1442,7 @@ mpd_command_seekcur(struct evbuffer *evbuf, int argc, char **argv, char **errmsg return ACK_ERROR_UNKNOWN; } - ret = player_playback_start(NULL); + ret = player_playback_start(); if (ret < 0) { ret = asprintf(errmsg, "Player returned an error for start after seekcur"); diff --git a/src/player.c b/src/player.c index 252e5472..6814ab1e 100644 --- a/src/player.c +++ b/src/player.c @@ -137,44 +137,6 @@ struct spk_enum void *arg; }; -struct playback_start_param -{ - uint32_t id; - int pos; - - uint32_t *id_ptr; -}; - -struct playerqueue_get_param -{ - int pos; - int count; - - struct queue *queue; -}; - -struct playerqueue_add_param -{ - struct queue_item *items; - int pos; - - uint32_t *item_id_ptr; -}; - -struct playerqueue_move_param -{ - uint32_t item_id; - int from_pos; - int to_pos; - int count; -}; - -struct playerqueue_remove_param -{ - int from_pos; - int count; -}; - struct icy_artwork { uint32_t id; @@ -212,11 +174,6 @@ union player_arg uint32_t id; int intval; struct icy_artwork icy; - struct playback_start_param playback_start_param; - struct playerqueue_get_param queue_get_param; - struct playerqueue_add_param queue_add_param; - struct playerqueue_move_param queue_move_param; - struct playerqueue_remove_param queue_remove_param; }; struct event_base *evbase_player; @@ -3156,14 +3113,11 @@ player_get_icy_artwork_url(uint32_t id) * @return 0 if successful, -1 if an error occurred */ int -player_playback_start(uint32_t *id) +player_playback_start() { - union player_arg cmdarg; int ret; - cmdarg.playback_start_param.id_ptr = id; - - ret = commands_exec_sync(cmdbase, playback_start, playback_start_bh, &cmdarg); + ret = commands_exec_sync(cmdbase, playback_start, playback_start_bh, NULL); return ret; } diff --git a/src/player.h b/src/player.h index 45de3593..faec7da3 100644 --- a/src/player.h +++ b/src/player.h @@ -94,7 +94,7 @@ int player_speaker_set(uint64_t *ids); int -player_playback_start(uint32_t *id); +player_playback_start(); int player_playback_start_byitem(struct db_queue_item *queue_item); From 6c7df96371620cb54b331d0780c0087901be7628 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Dec 2016 06:37:38 +0100 Subject: [PATCH 11/23] [db] Remove forward declaration of queue_fetch_byitemid --- src/db.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/db.c b/src/db.c index e49c699b..b37664de 100644 --- a/src/db.c +++ b/src/db.c @@ -4167,10 +4167,6 @@ db_speaker_clear_all(void) /* Queue */ -static int -queue_fetch_byitemid(struct db_queue_enum *queue_enum, uint32_t item_id, struct db_queue_item *queue_item, int keep_item); - - void free_queue_item(struct db_queue_item *queue_item, int content_only) { From b5bf1928ffd5fd4458f310e4c8509dc700d4c4c1 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Dec 2016 07:00:28 +0100 Subject: [PATCH 12/23] [db] Rename "plversion" to "queue_version", add missing init query for queue_version --- src/db.c | 28 ++++++++++++++-------------- src/db_init.c | 4 ++++ src/db_upgrade.c | 6 +++--- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/db.c b/src/db.c index b37664de..75862bcc 100644 --- a/src/db.c +++ b/src/db.c @@ -4204,14 +4204,14 @@ int db_queue_get_version() { char *version; - int32_t plversion; + int32_t queue_version; int ret; - plversion = 0; - version = db_admin_get("plversion"); + queue_version = 0; + version = db_admin_get("queue_version"); if (version) { - ret = safe_atoi32(version, &plversion); + ret = safe_atoi32(version, &queue_version); free(version); if (ret < 0) { @@ -4220,7 +4220,7 @@ db_queue_get_version() } } - return plversion; + return queue_version; } /* @@ -4233,29 +4233,29 @@ db_queue_get_version() static void queue_inc_version_and_notify() { - int plversion; + int queue_version; char version[10]; int ret; db_transaction_begin(); - plversion = db_queue_get_version(); - if (plversion < 0) - plversion = 0; + queue_version = db_queue_get_version(); + if (queue_version < 0) + queue_version = 0; - plversion++; - ret = snprintf(version, sizeof(version), "%d", plversion); + queue_version++; + ret = snprintf(version, sizeof(version), "%d", queue_version); if (ret >= sizeof(version)) { - DPRINTF(E_LOG, L_DB, "Error incrementing queue version. Could not convert version to string: %d\n", plversion); + DPRINTF(E_LOG, L_DB, "Error incrementing queue version. Could not convert version to string: %d\n", queue_version); db_transaction_rollback(); return; } - ret = db_admin_update("plversion", version); + ret = db_admin_update("queue_version", version); if (ret < 0) { - DPRINTF(E_LOG, L_DB, "Error incrementing queue version. Could not update version in admin table: %d\n", plversion); + DPRINTF(E_LOG, L_DB, "Error incrementing queue version. Could not update version in admin table: %d\n", queue_version); db_transaction_rollback(); return; } diff --git a/src/db_init.c b/src/db_init.c index 1d510942..b92dd822 100644 --- a/src/db_init.c +++ b/src/db_init.c @@ -242,6 +242,9 @@ "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id)" \ " VALUES (4, '/spotify:', 0, 4294967296, 1);" +#define Q_QUEUE_VERSION \ + "INSERT INTO admin (key, value) VALUES ('queue_version', '0');" + #define Q_SCVER_MAJOR \ "INSERT INTO admin (key, value) VALUES ('schema_version_major', '%d');" #define Q_SCVER_MINOR \ @@ -278,6 +281,7 @@ static const struct db_init_query db_init_table_queries[] = { Q_DIR2, "create default base directory '/file:'" }, { Q_DIR3, "create default base directory '/http:'" }, { Q_DIR4, "create default base directory '/spotify:'" }, + { Q_QUEUE_VERSION, "initialize queue version" }, }; diff --git a/src/db_upgrade.c b/src/db_upgrade.c index 56e4315f..799a7dc6 100644 --- a/src/db_upgrade.c +++ b/src/db_upgrade.c @@ -1471,8 +1471,8 @@ db_upgrade_v19(sqlite3 *hdl) " disc INTEGER DEFAULT 0" \ ");" -#define U_V2000_PLVERSION \ - "INSERT INTO admin (key, value) VALUES ('plversion', '0');" +#define U_V2000_QUEUE_VERSION \ + "INSERT INTO admin (key, value) VALUES ('queue_version', '0');" #define U_V2000_SCVER_MAJOR \ "UPDATE admin SET value = '20' WHERE key = 'schema_version_major';" @@ -1482,7 +1482,7 @@ db_upgrade_v19(sqlite3 *hdl) static const struct db_upgrade_query db_upgrade_v2000_queries[] = { { U_V2000_CREATE_TABLE_QUEUE, "create table directories" }, - { U_V2000_PLVERSION, "insert plversion" }, + { U_V2000_QUEUE_VERSION, "insert plversion" }, { U_V2000_SCVER_MAJOR, "set schema_version_major to 20" }, { U_V2000_SCVER_MINOR, "set schema_version_minor to 00" }, From 0251f3c51433820c4f8a0d856ecc55dbff3f635a Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Dec 2016 07:07:01 +0100 Subject: [PATCH 13/23] [db] Rename "queue_add_mediafileinfo" to "queue_add_file" --- src/db.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db.c b/src/db.c index 75862bcc..0b4afec1 100644 --- a/src/db.c +++ b/src/db.c @@ -4289,7 +4289,7 @@ db_queue_update_icymetadata(int id, char *artist, char *album) } static int -queue_add_mediafileinfo(struct db_media_file_info *dbmfi, int pos, int shuffle_pos) +queue_add_file(struct db_media_file_info *dbmfi, int pos, int shuffle_pos) { #define Q_TMPL "INSERT INTO queue " \ "(id, file_id, song_length, data_kind, media_kind, " \ @@ -4388,7 +4388,7 @@ db_queue_add_by_queryafteritemid(struct query_params *qp, uint32_t item_id) // Iterate over new items from files table and insert into queue while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) { - ret = queue_add_mediafileinfo(&dbmfi, pos, shuffle_pos); + ret = queue_add_file(&dbmfi, pos, shuffle_pos); if (ret < 0) { @@ -4460,7 +4460,7 @@ db_queue_add_by_query(struct query_params *qp, char reshuffle, uint32_t item_id) while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) { - ret = queue_add_mediafileinfo(&dbmfi, pos, pos); + ret = queue_add_file(&dbmfi, pos, pos); if (ret < 0) { From eb896a1085577fd6db7637799b8b19eec9a4f381 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Dec 2016 07:41:12 +0100 Subject: [PATCH 14/23] [db] Fix loglevel --- src/db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db.c b/src/db.c index 0b4afec1..db09d03e 100644 --- a/src/db.c +++ b/src/db.c @@ -4392,7 +4392,7 @@ db_queue_add_by_queryafteritemid(struct query_params *qp, uint32_t item_id) if (ret < 0) { - DPRINTF(E_DBG, L_DB, "Failed to add song with id %s (%s) to queue\n", dbmfi.id, dbmfi.title); + DPRINTF(E_LOG, L_DB, "Failed to add song with id %s (%s) to queue\n", dbmfi.id, dbmfi.title); break; } From 6c6f02fdfad3fa385df0427c4397709bd9cc7952 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Dec 2016 08:03:19 +0100 Subject: [PATCH 15/23] Rename "queue_item.item_id" to "queue_item.id" --- src/db.c | 58 ++++++++++++++++++++++++------------------------ src/db.h | 4 ++-- src/httpd_dacp.c | 4 ++-- src/mpd.c | 14 ++++++------ src/player.c | 4 ++-- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/db.c b/src/db.c index db09d03e..33fac21d 100644 --- a/src/db.c +++ b/src/db.c @@ -4650,7 +4650,7 @@ queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_i return -1; } - queue_item->item_id = (uint32_t)sqlite3_column_int(queue_enum->stmt, 0); + queue_item->id = (uint32_t)sqlite3_column_int(queue_enum->stmt, 0); queue_item->file_id = (uint32_t)sqlite3_column_int(queue_enum->stmt, 1); queue_item->pos = (uint32_t)sqlite3_column_int(queue_enum->stmt, 2); queue_item->shuffle_pos = (uint32_t)sqlite3_column_int(queue_enum->stmt, 3); @@ -4801,7 +4801,7 @@ db_queue_fetch_byitemid(uint32_t item_id) DPRINTF(E_LOG, L_DB, "Error fetching queue item by item id\n"); return NULL; } - else if (queue_item->item_id == 0) + else if (queue_item->id == 0) { // No item found free_queue_item(queue_item, 0); @@ -4851,7 +4851,7 @@ db_queue_fetch_byfileid(uint32_t file_id) DPRINTF(E_LOG, L_DB, "Error fetching queue item by file id\n"); return NULL; } - else if (queue_item->item_id == 0) + else if (queue_item->id == 0) { // No item found free_queue_item(queue_item, 0); @@ -4910,7 +4910,7 @@ db_queue_fetch_bypos(uint32_t pos, char shuffle) DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos id\n"); return NULL; } - else if (queue_item->item_id == 0) + else if (queue_item->id == 0) { // No item found free_queue_item(queue_item, 0); @@ -4940,7 +4940,7 @@ queue_fetch_byposrelativetoitem(struct db_queue_enum *queue_enum, int pos, uint3 ret = queue_fetch_bypos(queue_enum, pos_absolute, shuffle, queue_item, keep_item); - DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->item_id, queue_item->pos, queue_item->file_id); + DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->id, queue_item->pos, queue_item->file_id); return ret; } @@ -4975,14 +4975,14 @@ db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos relative to item id\n"); return NULL; } - else if (queue_item->item_id == 0) + else if (queue_item->id == 0) { // No item found free_queue_item(queue_item, 0); return NULL; } - DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->item_id, queue_item->pos, queue_item->file_id); + DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->id, queue_item->pos, queue_item->file_id); return queue_item; } @@ -5045,15 +5045,15 @@ db_queue_cleanup() } pos = 0; - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0)) + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0)) { if (queue_item.pos != pos) { - query = sqlite3_mprintf(Q_TMPL_UPDATE, pos, queue_item.item_id); + query = sqlite3_mprintf(Q_TMPL_UPDATE, pos, queue_item.id); ret = db_query_run(query, 1, 0); if (ret < 0) { - DPRINTF(E_LOG, L_DB, "Failed to update item with item-id: %d\n", queue_item.item_id); + DPRINTF(E_LOG, L_DB, "Failed to update item with item-id: %d\n", queue_item.id); break; } } @@ -5081,15 +5081,15 @@ db_queue_cleanup() } pos = 0; - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0)) + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0)) { if (queue_item.shuffle_pos != pos) { - query = sqlite3_mprintf(Q_TMPL_UPDATE_SHUFFLE, pos, queue_item.item_id); + query = sqlite3_mprintf(Q_TMPL_UPDATE_SHUFFLE, pos, queue_item.id); ret = db_query_run(query, 1, 0); if (ret < 0) { - DPRINTF(E_LOG, L_DB, "Failed to update item with item-id: %d\n", queue_item.item_id); + DPRINTF(E_LOG, L_DB, "Failed to update item with item-id: %d\n", queue_item.id); break; } } @@ -5142,7 +5142,7 @@ queue_delete_item(struct db_queue_item *queue_item) int ret; // Remove item with the given item_id - query = sqlite3_mprintf("DELETE FROM queue where id = %d;", queue_item->item_id); + query = sqlite3_mprintf("DELETE FROM queue where id = %d;", queue_item->id); ret = db_query_run(query, 1, 0); if (ret < 0) { @@ -5188,7 +5188,7 @@ db_queue_delete_byitemid(uint32_t item_id) return -1; } - if (queue_item.item_id == 0) + if (queue_item.id == 0) { queue_enum_end(&queue_enum); db_transaction_end(); @@ -5234,12 +5234,12 @@ db_queue_delete_bypos(uint32_t pos, int count) return -1; } - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0)) + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0)) { ret = queue_delete_item(&queue_item); if (ret < 0) { - DPRINTF(E_LOG, L_DB, "Failed to delete item with item-id: %d\n", queue_item.item_id); + DPRINTF(E_LOG, L_DB, "Failed to delete item with item-id: %d\n", queue_item.id); break; } } @@ -5277,7 +5277,7 @@ db_queue_delete_byposrelativetoitem(uint32_t pos, uint32_t item_id, char shuffle db_transaction_rollback(); return -1; } - else if (queue_item.item_id == 0) + else if (queue_item.id == 0) { // No item found queue_enum_end(&queue_enum); @@ -5387,7 +5387,7 @@ db_queue_move_bypos(int pos_from, int pos_to) return -1; } - if (queue_item.item_id == 0) + if (queue_item.id == 0) { queue_enum_end(&queue_enum); db_transaction_end(); @@ -5415,7 +5415,7 @@ db_queue_move_bypos(int pos_from, int pos_to) } // Update item with the given item_id - query = sqlite3_mprintf("UPDATE queue SET pos = %d where id = %d;", pos_to, queue_item.item_id); + query = sqlite3_mprintf("UPDATE queue SET pos = %d where id = %d;", pos_to, queue_item.id); ret = db_query_run(query, 1, 0); if (ret < 0) { @@ -5465,9 +5465,9 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ return -1; } - DPRINTF(E_DBG, L_DB, "Move by pos: base item (id=%d, pos=%d, file-id=%d)\n", queue_item.item_id, queue_item.pos, queue_item.file_id); + DPRINTF(E_DBG, L_DB, "Move by pos: base item (id=%d, pos=%d, file-id=%d)\n", queue_item.id, queue_item.pos, queue_item.file_id); - if (queue_item.item_id == 0) + if (queue_item.id == 0) { queue_enum_end(&queue_enum); db_transaction_end(); @@ -5507,9 +5507,9 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ return -1; } - DPRINTF(E_DBG, L_DB, "Move by pos: move item (id=%d, pos=%d, file-id=%d)\n", queue_item.item_id, queue_item.pos, queue_item.file_id); + DPRINTF(E_DBG, L_DB, "Move by pos: move item (id=%d, pos=%d, file-id=%d)\n", queue_item.id, queue_item.pos, queue_item.file_id); - if (queue_item.item_id == 0) + if (queue_item.id == 0) { queue_enum_end(&queue_enum); db_transaction_end(); @@ -5546,9 +5546,9 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ // Update item with the given item_id if (shuffle) - query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = %d where id = %d;", pos_move_to, queue_item.item_id); + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = %d where id = %d;", pos_move_to, queue_item.id); else - query = sqlite3_mprintf("UPDATE queue SET pos = %d where id = %d;", pos_move_to, queue_item.item_id); + query = sqlite3_mprintf("UPDATE queue SET pos = %d where id = %d;", pos_move_to, queue_item.id); ret = db_query_run(query, 1, 0); if (ret < 0) @@ -5640,13 +5640,13 @@ db_queue_reshuffle(uint32_t item_id) } i = 0; - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.item_id > 0) && (i < len)) + while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0) && (i < len)) { - query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = %d where id = %d;", shuffle_pos[i], queue_item.item_id); + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = %d where id = %d;", shuffle_pos[i], queue_item.id); ret = db_query_run(query, 1, 0); if (ret < 0) { - DPRINTF(E_LOG, L_DB, "Failed to delete item with item-id: %d\n", queue_item.item_id); + DPRINTF(E_LOG, L_DB, "Failed to delete item with item-id: %d\n", queue_item.id); break; } diff --git a/src/db.h b/src/db.h index 8c1e94c8..bb607f41 100644 --- a/src/db.h +++ b/src/db.h @@ -376,9 +376,9 @@ struct directory_enum { struct db_queue_item { - /* Item-Id is a unique id for this queue item. If the same item appears multiple + /* A unique id for this queue item. If the same item appears multiple times in the queue each corresponding queue item has its own id. */ - uint32_t item_id; + uint32_t id; /* Id of the file/item in the files database */ uint32_t file_id; diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 6b2f2936..02eb4fd4 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1635,9 +1635,9 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, 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) + while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) { - if (status.item_id == 0 || status.item_id == queue_item.item_id) + if (status.item_id == 0 || status.item_id == queue_item.id) count = 1; else if (count > 0) { diff --git a/src/mpd.c b/src/mpd.c index 4ee6cbbb..74f335df 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -436,7 +436,7 @@ mpd_add_db_queue_item(struct evbuffer *evbuf, struct db_queue_item *queue_item) queue_item->genre, queue_item->disc, queue_item->pos, - queue_item->item_id); + queue_item->id); return ret; } @@ -766,7 +766,7 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) evbuffer_add_printf(evbuf, "nextsong: %d\n" "nextsongid: %d\n", - next_item->item_id, + next_item->id, next_item->pos); free_queue_item(next_item, 0); @@ -1828,7 +1828,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err return ACK_ERROR_ARG; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0) + while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) { ret = mpd_add_db_queue_item(evbuf, &queue_item); if (ret < 0) @@ -1897,7 +1897,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e return ACK_ERROR_ARG; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0) + while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) { ret = mpd_add_db_queue_item(evbuf, &queue_item); if (ret < 0) @@ -1945,7 +1945,7 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm return ACK_ERROR_ARG; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0) + while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) { ret = mpd_add_db_queue_item(evbuf, &queue_item); if (ret < 0) @@ -1991,13 +1991,13 @@ mpd_command_plchangesposid(struct evbuffer *evbuf, int argc, char **argv, char * return ACK_ERROR_ARG; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.item_id > 0) + while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) { evbuffer_add_printf(evbuf, "cpos: %d\n" "Id: %d\n", queue_item.pos, - queue_item.item_id); + queue_item.id); } db_queue_enum_end(&queue_enum); diff --git a/src/player.c b/src/player.c index 6814ab1e..95ceb880 100644 --- a/src/player.c +++ b/src/player.c @@ -927,7 +927,7 @@ source_new(struct db_queue_item *queue_item) } ps->id = queue_item->file_id; - ps->item_id = queue_item->item_id; + ps->item_id = queue_item->id; ps->data_kind = queue_item->data_kind; ps->media_kind = queue_item->media_kind; ps->len_ms = queue_item->song_length; @@ -2311,7 +2311,7 @@ playback_start_item(void *arg, int *retval) 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); + DPRINTF(E_DBG, L_PLAYER, "Start playback of '%s' (id=%d, item-id=%d)\n", queue_item->path, queue_item->file_id, queue_item->id); source_stop(); ps = source_new(queue_item); From fdd85ccf66792f368d39f1e7e1efe21c50a32b03 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Dec 2016 20:40:54 +0100 Subject: [PATCH 16/23] [raop/dmap] Build raop metadata from queue item --- src/dmap_common.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ src/dmap_common.h | 3 ++ src/outputs/raop.c | 73 ++++++++++++-------------------------------- src/player.c | 2 +- 4 files changed, 98 insertions(+), 55 deletions(-) diff --git a/src/dmap_common.c b/src/dmap_common.c index 495a7bfb..d79675ef 100644 --- a/src/dmap_common.c +++ b/src/dmap_common.c @@ -559,3 +559,78 @@ dmap_encode_file_metadata(struct evbuffer *songlist, struct evbuffer *song, stru return 0; } + +int +dmap_encode_queue_metadata(struct evbuffer *songlist, struct evbuffer *song, struct db_queue_item *queue_item) +{ + int32_t val; + int want_mikd; + int want_asdk; + int want_ased; + int ret; + + dmap_add_int(song, "miid", queue_item->file_id); + dmap_add_string(song, "minm", queue_item->title); + dmap_add_long(song, "mper", queue_item->file_id); + dmap_add_int(song, "mcti", queue_item->file_id); + dmap_add_string(song, "asal", queue_item->album); + dmap_add_long(song, "asai", queue_item->songalbumid); + dmap_add_string(song, "asaa", queue_item->album_artist); + dmap_add_string(song, "asar", queue_item->artist); + dmap_add_int(song, "asdm", queue_item->time_modified); + dmap_add_short(song, "asdn", queue_item->disc); + dmap_add_string(song, "asgn", queue_item->genre); + dmap_add_int(song, "astm", queue_item->song_length); + dmap_add_short(song, "astn", queue_item->track); + dmap_add_short(song, "asyr", queue_item->year); + dmap_add_int(song, "aeMK", queue_item->media_kind); + dmap_add_char(song, "aeMk", queue_item->media_kind); + + dmap_add_string(song, "asfm", "wav"); + dmap_add_short(song, "asbr", 1411); + dmap_add_string(song, "asdt", "wav audio file"); + + want_mikd = 1;/* Will be prepended to the list *//* item kind */ + want_asdk = 1;/* Will be prepended to the list *//* data kind */ + want_ased = 1;/* Extradata not in media_file_info but flag for reply */ + + /* Required for artwork in iTunes, set songartworkcount (asac) = 1 */ + if (want_ased) + { + dmap_add_short(song, "ased", 1); + dmap_add_short(song, "asac", 1); + } + + val = 0; + if (want_mikd) + val += 9; + if (want_asdk) + val += 9; + + dmap_add_container(songlist, "mlit", evbuffer_get_length(song) + val); + + /* Prepend mikd & asdk if needed */ + if (want_mikd) + { + /* dmap.itemkind must come first */ + val = 2; /* music by default */ + dmap_add_char(songlist, "mikd", val); + } + if (want_asdk) + { + ret = queue_item->data_kind; + if (ret < 0) + val = 0; + dmap_add_char(songlist, "asdk", val); + } + + ret = evbuffer_add_buffer(songlist, song); + if (ret < 0) + { + DPRINTF(E_LOG, L_DAAP, "Could not add song to song list\n"); + + return -1; + } + + return 0; +} diff --git a/src/dmap_common.h b/src/dmap_common.h index 620ab37a..a595558d 100644 --- a/src/dmap_common.h +++ b/src/dmap_common.h @@ -84,4 +84,7 @@ dmap_send_error(struct evhttp_request *req, const char *container, const char *e int dmap_encode_file_metadata(struct evbuffer *songlist, struct evbuffer *song, struct db_media_file_info *dbmfi, const struct dmap_field **meta, int nmeta, int sort_tags, int force_wav); +int +dmap_encode_queue_metadata(struct evbuffer *songlist, struct evbuffer *song, struct db_queue_item *queue_item); + #endif /* !__DMAP_HELPERS_H__ */ diff --git a/src/outputs/raop.c b/src/outputs/raop.c index d7c7496b..c0249b41 100644 --- a/src/outputs/raop.c +++ b/src/outputs/raop.c @@ -821,12 +821,9 @@ raop_metadata_prune(uint64_t rtptime) static void * raop_metadata_prepare(int id) { - struct query_params qp; - struct db_media_file_info dbmfi; - char filter[32]; + struct db_queue_item *queue_item; struct raop_metadata *rmd; struct evbuffer *tmp; - uint64_t duration; int ret; rmd = (struct raop_metadata *)malloc(sizeof(struct raop_metadata)); @@ -839,6 +836,14 @@ raop_metadata_prepare(int id) memset(rmd, 0, sizeof(struct raop_metadata)); + queue_item = db_queue_fetch_byitemid(id); + if (!queue_item) + { + DPRINTF(E_LOG, L_RAOP, "Out of memory for queue item\n"); + + goto out_rmd; + } + /* Get artwork */ rmd->artwork = evbuffer_new(); if (!rmd->artwork) @@ -848,7 +853,7 @@ raop_metadata_prepare(int id) goto skip_artwork; } - ret = artwork_get_item(rmd->artwork, id, 600, 600); + ret = artwork_get_item(rmd->artwork, queue_item->file_id, 600, 600); if (ret < 0) { DPRINTF(E_INFO, L_RAOP, "Failed to retrieve artwork for file id %d; no artwork will be sent\n", id); @@ -861,44 +866,13 @@ raop_metadata_prepare(int id) skip_artwork: - /* Get dbmfi */ - memset(&qp, 0, sizeof(struct query_params)); - qp.type = Q_ITEMS; - qp.idx_type = I_NONE; - qp.sort = S_NONE; - qp.filter = filter; - - ret = snprintf(filter, sizeof(filter), "id = %d", id); - if ((ret < 0) || (ret >= sizeof(filter))) - { - DPRINTF(E_LOG, L_RAOP, "Could not build filter for file id %d; metadata will not be sent\n", id); - - goto out_rmd; - } - - ret = db_query_start(&qp); - if (ret < 0) - { - DPRINTF(E_LOG, L_RAOP, "Couldn't start query; no metadata will be sent\n"); - - goto out_rmd; - } - - ret = db_query_fetch_file(&qp, &dbmfi); - if (ret < 0) - { - DPRINTF(E_LOG, L_RAOP, "Couldn't fetch file id %d; metadata will not be sent\n", id); - - goto out_query; - } - /* Turn it into DAAP metadata */ tmp = evbuffer_new(); if (!tmp) { DPRINTF(E_LOG, L_RAOP, "Out of memory for temporary metadata evbuffer; metadata will not be sent\n"); - goto out_query; + goto out_qi; } rmd->metadata = evbuffer_new(); @@ -907,10 +881,10 @@ raop_metadata_prepare(int id) DPRINTF(E_LOG, L_RAOP, "Out of memory for metadata evbuffer; metadata will not be sent\n"); evbuffer_free(tmp); - goto out_query; + goto out_qi; } - ret = dmap_encode_file_metadata(rmd->metadata, tmp, &dbmfi, NULL, 0, 0, 1); + ret = dmap_encode_queue_metadata(rmd->metadata, tmp, queue_item); evbuffer_free(tmp); if (ret < 0) { @@ -919,27 +893,18 @@ raop_metadata_prepare(int id) goto out_metadata; } - /* Progress */ - ret = safe_atou64(dbmfi.song_length, &duration); - if (ret < 0) - { - DPRINTF(E_LOG, L_RAOP, "Failed to convert song_length to integer; no metadata will be sent\n"); - - goto out_metadata; - } - - db_query_end(&qp); - - /* raop_metadata_send() will add rtptime to these */ + /* Progress - raop_metadata_send() will add rtptime to these */ rmd->start = 0; - rmd->end = (duration * 44100UL) / 1000UL; + rmd->end = (queue_item->song_length * 44100UL) / 1000UL; + + free_queue_item(queue_item, 0); return rmd; out_metadata: evbuffer_free(rmd->metadata); - out_query: - db_query_end(&qp); + out_qi: + free_queue_item(queue_item, 0); out_rmd: free(rmd); diff --git a/src/player.c b/src/player.c index 95ceb880..7c52ee70 100644 --- a/src/player.c +++ b/src/player.c @@ -502,7 +502,7 @@ metadata_trigger(int startup) memset(&pmd, 0, sizeof(struct player_metadata)); - pmd.id = cur_streaming->id; + pmd.id = cur_streaming->item_id; pmd.startup = startup; if (cur_streaming->stream_start && cur_streaming->output_start) From d431ace5c4dee4c6c0a72609132d02cf87043663 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 4 Dec 2016 07:28:06 +0100 Subject: [PATCH 17/23] [db] Use a minor version update for the new queue table (v19.01 instead of v20.00) --- src/db_init.h | 4 ++-- src/db_upgrade.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/db_init.h b/src/db_init.h index 33312835..88287f18 100644 --- a/src/db_init.h +++ b/src/db_init.h @@ -25,8 +25,8 @@ * version of the database? If yes, then it is a minor upgrade, if no, then it * is a major upgrade. In other words minor version upgrades permit downgrading * forked-daapd after the database was upgraded. */ -#define SCHEMA_VERSION_MAJOR 20 -#define SCHEMA_VERSION_MINOR 00 +#define SCHEMA_VERSION_MAJOR 19 +#define SCHEMA_VERSION_MINOR 01 int db_init_indices(sqlite3 *hdl); diff --git a/src/db_upgrade.c b/src/db_upgrade.c index 799a7dc6..6745409a 100644 --- a/src/db_upgrade.c +++ b/src/db_upgrade.c @@ -1479,13 +1479,13 @@ db_upgrade_v19(sqlite3 *hdl) #define U_V2000_SCVER_MINOR \ "UPDATE admin SET value = '00' WHERE key = 'schema_version_minor';" -static const struct db_upgrade_query db_upgrade_v2000_queries[] = +static const struct db_upgrade_query db_upgrade_v1901_queries[] = { { U_V2000_CREATE_TABLE_QUEUE, "create table directories" }, - { U_V2000_QUEUE_VERSION, "insert plversion" }, + { U_V2000_QUEUE_VERSION, "insert queue version" }, - { U_V2000_SCVER_MAJOR, "set schema_version_major to 20" }, - { U_V2000_SCVER_MINOR, "set schema_version_minor to 00" }, + { U_V2000_SCVER_MAJOR, "set schema_version_major to 19" }, + { U_V2000_SCVER_MINOR, "set schema_version_minor to 01" }, }; @@ -1602,7 +1602,7 @@ db_upgrade(sqlite3 *hdl, int db_ver) /* FALLTHROUGH */ case 1900: - ret = db_generic_upgrade(hdl, db_upgrade_v2000_queries, sizeof(db_upgrade_v2000_queries) / sizeof(db_upgrade_v2000_queries[0])); + ret = db_generic_upgrade(hdl, db_upgrade_v1901_queries, sizeof(db_upgrade_v1901_queries) / sizeof(db_upgrade_v1901_queries[0])); if (ret < 0) return -1; From b9117f9abdc7f1e8e328b4c6f941b902f2dd5fb9 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 4 Dec 2016 07:42:38 +0100 Subject: [PATCH 18/23] [db] Remove unnecessary setting of query param fields to 0 --- src/db.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/db.c b/src/db.c index 33fac21d..2b38d89b 100644 --- a/src/db.c +++ b/src/db.c @@ -4516,10 +4516,6 @@ db_queue_add_by_playlistid(int plid, char reshuffle, uint32_t item_id) qp.id = plid; qp.type = Q_PLITEMS; - qp.offset = 0; - qp.limit = 0; - qp.sort = S_NONE; - qp.idx_type = I_NONE; ret = db_queue_add_by_query(&qp, reshuffle, item_id); @@ -4543,11 +4539,7 @@ db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id) memset(&qp, 0, sizeof(struct query_params)); - qp.id = 0; qp.type = Q_ITEMS; - qp.offset = 0; - qp.limit = 0; - qp.sort = S_NONE; snprintf(buf, sizeof(buf), "f.id = %" PRIu32, id); qp.filter = buf; From fb55960b0a10731c75cc0accd22c53f875181809 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 4 Dec 2016 08:18:33 +0100 Subject: [PATCH 19/23] [db] Remove db_queue_enum struct and use generic query_params --- src/db.c | 281 ++++++++++++++++++++++------------------------- src/db.h | 19 +--- src/httpd_dacp.c | 14 +-- src/mpd.c | 62 +++++------ 4 files changed, 177 insertions(+), 199 deletions(-) diff --git a/src/db.c b/src/db.c index 2b38d89b..de7ad4ac 100644 --- a/src/db.c +++ b/src/db.c @@ -299,6 +299,8 @@ static const char *sort_clause[] = "ORDER BY f.disc ASC", "ORDER BY f.track ASC", "ORDER BY f.virtual_path ASC", + "ORDER BY pos ASC", + "ORDER BY shuffle_pos ASC", }; /* Shuffle RNG state */ @@ -4549,22 +4551,22 @@ db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id) } static int -queue_enum_start(struct db_queue_enum *queue_enum) +queue_enum_start(struct query_params *query_params) { #define Q_TMPL "SELECT * FROM queue WHERE %s ORDER BY %s;" char *query; - char *orderby; + const char *orderby; int ret; - queue_enum->stmt = NULL; + query_params->stmt = NULL; - if (queue_enum->orderby_shufflepos) - orderby = "shuffle_pos"; + if (query_params) + orderby = sort_clause[query_params->sort]; else - orderby = "pos"; + orderby = sort_clause[S_POS]; - if (queue_enum->filter) - query = sqlite3_mprintf(Q_TMPL, queue_enum->filter, orderby); + if (query_params->filter) + query = sqlite3_mprintf(Q_TMPL, query_params->filter, orderby); else query = sqlite3_mprintf(Q_TMPL, "1=1", orderby); @@ -4577,7 +4579,7 @@ queue_enum_start(struct db_queue_enum *queue_enum) DPRINTF(E_DBG, L_DB, "Starting enum '%s'\n", query); - ret = db_blocking_prepare_v2(query, -1, &queue_enum->stmt, NULL); + ret = db_blocking_prepare_v2(query, -1, &query_params->stmt, NULL); if (ret != SQLITE_OK) { DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); @@ -4593,18 +4595,6 @@ queue_enum_start(struct db_queue_enum *queue_enum) #undef Q_TMPL } -static void -queue_enum_end(struct db_queue_enum *queue_enum) -{ - if (!queue_enum->stmt) - return; - - sqlite3_finalize(queue_enum->stmt); - sqlite3_free(queue_enum->filter); - queue_enum->filter = NULL; - queue_enum->stmt = NULL; -} - static inline char * strdup_if(char *str, int cond) { @@ -4618,19 +4608,19 @@ strdup_if(char *str, int cond) } static int -queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_item, int keep_item) +queue_enum_fetch(struct query_params *query_params, struct db_queue_item *queue_item, int keep_item) { int ret; memset(queue_item, 0, sizeof(struct db_queue_item)); - if (!queue_enum->stmt) + if (!query_params->stmt) { DPRINTF(E_LOG, L_DB, "Queue enum not started!\n"); return -1; } - ret = db_blocking_step(queue_enum->stmt); + ret = db_blocking_step(query_params->stmt); if (ret == SQLITE_DONE) { DPRINTF(E_DBG, L_DB, "End of queue enum results\n"); @@ -4642,40 +4632,40 @@ queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_i return -1; } - queue_item->id = (uint32_t)sqlite3_column_int(queue_enum->stmt, 0); - queue_item->file_id = (uint32_t)sqlite3_column_int(queue_enum->stmt, 1); - queue_item->pos = (uint32_t)sqlite3_column_int(queue_enum->stmt, 2); - queue_item->shuffle_pos = (uint32_t)sqlite3_column_int(queue_enum->stmt, 3); - queue_item->data_kind = sqlite3_column_int(queue_enum->stmt, 4); - queue_item->media_kind = sqlite3_column_int(queue_enum->stmt, 5); - queue_item->song_length = (uint32_t)sqlite3_column_int(queue_enum->stmt, 6); - queue_item->path = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 7), keep_item); - queue_item->virtual_path = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 8), keep_item); - queue_item->title = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 9), keep_item); - queue_item->artist = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 10), keep_item); - queue_item->album_artist = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 11), keep_item); - queue_item->album = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 12), keep_item); - queue_item->genre = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 13), keep_item); - queue_item->songalbumid = sqlite3_column_int64(queue_enum->stmt, 14); - queue_item->time_modified = sqlite3_column_int(queue_enum->stmt, 15); - queue_item->artist_sort = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 16), keep_item); - queue_item->album_sort = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 17), keep_item); - queue_item->album_artist_sort = strdup_if((char *)sqlite3_column_text(queue_enum->stmt, 18), keep_item); - queue_item->year = sqlite3_column_int(queue_enum->stmt, 19); - queue_item->track = sqlite3_column_int(queue_enum->stmt, 20); - queue_item->disc = sqlite3_column_int(queue_enum->stmt, 21); + queue_item->id = (uint32_t)sqlite3_column_int(query_params->stmt, 0); + queue_item->file_id = (uint32_t)sqlite3_column_int(query_params->stmt, 1); + queue_item->pos = (uint32_t)sqlite3_column_int(query_params->stmt, 2); + queue_item->shuffle_pos = (uint32_t)sqlite3_column_int(query_params->stmt, 3); + queue_item->data_kind = sqlite3_column_int(query_params->stmt, 4); + queue_item->media_kind = sqlite3_column_int(query_params->stmt, 5); + queue_item->song_length = (uint32_t)sqlite3_column_int(query_params->stmt, 6); + queue_item->path = strdup_if((char *)sqlite3_column_text(query_params->stmt, 7), keep_item); + queue_item->virtual_path = strdup_if((char *)sqlite3_column_text(query_params->stmt, 8), keep_item); + queue_item->title = strdup_if((char *)sqlite3_column_text(query_params->stmt, 9), keep_item); + queue_item->artist = strdup_if((char *)sqlite3_column_text(query_params->stmt, 10), keep_item); + queue_item->album_artist = strdup_if((char *)sqlite3_column_text(query_params->stmt, 11), keep_item); + queue_item->album = strdup_if((char *)sqlite3_column_text(query_params->stmt, 12), keep_item); + queue_item->genre = strdup_if((char *)sqlite3_column_text(query_params->stmt, 13), keep_item); + queue_item->songalbumid = sqlite3_column_int64(query_params->stmt, 14); + queue_item->time_modified = sqlite3_column_int(query_params->stmt, 15); + queue_item->artist_sort = strdup_if((char *)sqlite3_column_text(query_params->stmt, 16), keep_item); + queue_item->album_sort = strdup_if((char *)sqlite3_column_text(query_params->stmt, 17), keep_item); + queue_item->album_artist_sort = strdup_if((char *)sqlite3_column_text(query_params->stmt, 18), keep_item); + queue_item->year = sqlite3_column_int(query_params->stmt, 19); + queue_item->track = sqlite3_column_int(query_params->stmt, 20); + queue_item->disc = sqlite3_column_int(query_params->stmt, 21); return 0; } int -db_queue_enum_start(struct db_queue_enum *queue_enum) +db_queue_enum_start(struct query_params *query_params) { int ret; db_transaction_begin(); - ret = queue_enum_start(queue_enum); + ret = queue_enum_start(query_params); if (ret < 0) db_transaction_rollback(); @@ -4684,19 +4674,16 @@ db_queue_enum_start(struct db_queue_enum *queue_enum) } void -db_queue_enum_end(struct db_queue_enum *queue_enum) +db_queue_enum_end(struct query_params *query_params) { - if (!queue_enum->stmt) - return; - - queue_enum_end(queue_enum); + db_query_end(query_params); db_transaction_end(); } int -db_queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_item) +db_queue_enum_fetch(struct query_params *query_params, struct db_queue_item *queue_item) { - return queue_enum_fetch(queue_enum, queue_item, 0); + return queue_enum_fetch(query_params, queue_item, 0); } int @@ -4748,21 +4735,21 @@ db_queue_get_pos_byfileid(uint32_t file_id, char shuffle) } static int -queue_fetch_byitemid(struct db_queue_enum *queue_enum, uint32_t item_id, struct db_queue_item *queue_item, int keep_item) +queue_fetch_byitemid(struct query_params *query_params, uint32_t item_id, struct db_queue_item *queue_item, int keep_item) { int ret; - memset(queue_enum, 0, sizeof(struct db_queue_enum)); - queue_enum->filter = sqlite3_mprintf("id = %d", item_id); + memset(query_params, 0, sizeof(struct query_params)); + query_params->filter = sqlite3_mprintf("id = %d", item_id); - ret = queue_enum_start(queue_enum); + ret = queue_enum_start(query_params); if (ret < 0) { - sqlite3_free(queue_enum->filter); + sqlite3_free(query_params->filter); return -1; } - ret = queue_enum_fetch(queue_enum, queue_item, keep_item); + ret = queue_enum_fetch(query_params, queue_item, keep_item); return ret; } @@ -4771,10 +4758,10 @@ struct db_queue_item * db_queue_fetch_byitemid(uint32_t item_id) { struct db_queue_item *queue_item; - struct db_queue_enum queue_enum; + struct query_params query_params; int ret; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); queue_item = calloc(1, sizeof(struct db_queue_item)); if (!queue_item) { @@ -4783,8 +4770,8 @@ db_queue_fetch_byitemid(uint32_t item_id) } db_transaction_begin(); - ret = queue_fetch_byitemid(&queue_enum, item_id, queue_item, 1); - queue_enum_end(&queue_enum); + ret = queue_fetch_byitemid(&query_params, item_id, queue_item, 1); + db_query_end(&query_params); db_transaction_end(); if (ret < 0) @@ -4807,10 +4794,10 @@ struct db_queue_item * db_queue_fetch_byfileid(uint32_t file_id) { struct db_queue_item *queue_item; - struct db_queue_enum queue_enum; + struct query_params query_params; int ret; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); queue_item = calloc(1, sizeof(struct db_queue_item)); if (!queue_item) { @@ -4820,21 +4807,21 @@ db_queue_fetch_byfileid(uint32_t file_id) db_transaction_begin(); - queue_enum.filter = sqlite3_mprintf("file_id = %d", file_id); + query_params.filter = sqlite3_mprintf("file_id = %d", file_id); - ret = queue_enum_start(&queue_enum); + ret = queue_enum_start(&query_params); if (ret < 0) { - sqlite3_free(queue_enum.filter); + sqlite3_free(query_params.filter); db_transaction_end(); free_queue_item(queue_item, 0); DPRINTF(E_LOG, L_DB, "Error fetching queue item by file id\n"); return NULL; } - ret = queue_enum_fetch(&queue_enum, queue_item, 1); - queue_enum_end(&queue_enum); - sqlite3_free(queue_enum.filter); + ret = queue_enum_fetch(&query_params, queue_item, 1); + db_query_end(&query_params); + sqlite3_free(query_params.filter); db_transaction_end(); if (ret < 0) @@ -4854,24 +4841,24 @@ db_queue_fetch_byfileid(uint32_t file_id) } static int -queue_fetch_bypos(struct db_queue_enum *queue_enum, uint32_t pos, char shuffle, struct db_queue_item *queue_item, int keep_item) +queue_fetch_bypos(struct query_params *query_params, uint32_t pos, char shuffle, struct db_queue_item *queue_item, int keep_item) { int ret; - memset(queue_enum, 0, sizeof(struct db_queue_enum)); + memset(query_params, 0, sizeof(struct query_params)); if (shuffle) - queue_enum->filter = sqlite3_mprintf("shuffle_pos = %d", pos); + query_params->filter = sqlite3_mprintf("shuffle_pos = %d", pos); else - queue_enum->filter = sqlite3_mprintf("pos = %d", pos); + query_params->filter = sqlite3_mprintf("pos = %d", pos); - ret = queue_enum_start(queue_enum); + ret = queue_enum_start(query_params); if (ret < 0) { - sqlite3_free(queue_enum->filter); + sqlite3_free(query_params->filter); return -1; } - ret = queue_enum_fetch(queue_enum, queue_item, keep_item); + ret = queue_enum_fetch(query_params, queue_item, keep_item); return ret; } @@ -4880,10 +4867,10 @@ struct db_queue_item * db_queue_fetch_bypos(uint32_t pos, char shuffle) { struct db_queue_item *queue_item; - struct db_queue_enum queue_enum; + struct query_params query_params; int ret; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); queue_item = calloc(1, sizeof(struct db_queue_item)); if (!queue_item) { @@ -4892,8 +4879,8 @@ db_queue_fetch_bypos(uint32_t pos, char shuffle) } db_transaction_begin(); - ret = queue_fetch_bypos(&queue_enum, pos, shuffle, queue_item, 1); - queue_enum_end(&queue_enum); + ret = queue_fetch_bypos(&query_params, pos, shuffle, queue_item, 1); + db_query_end(&query_params); db_transaction_end(); if (ret < 0) @@ -4913,7 +4900,7 @@ db_queue_fetch_bypos(uint32_t pos, char shuffle) } static int -queue_fetch_byposrelativetoitem(struct db_queue_enum *queue_enum, int pos, uint32_t item_id, char shuffle, struct db_queue_item *queue_item, int keep_item) +queue_fetch_byposrelativetoitem(struct query_params *query_params, int pos, uint32_t item_id, char shuffle, struct db_queue_item *queue_item, int keep_item) { int pos_absolute; int ret; @@ -4930,7 +4917,7 @@ queue_fetch_byposrelativetoitem(struct db_queue_enum *queue_enum, int pos, uint3 pos_absolute += pos; - ret = queue_fetch_bypos(queue_enum, pos_absolute, shuffle, queue_item, keep_item); + ret = queue_fetch_bypos(query_params, pos_absolute, shuffle, queue_item, keep_item); DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->id, queue_item->pos, queue_item->file_id); @@ -4941,12 +4928,12 @@ struct db_queue_item * db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) { struct db_queue_item *queue_item; - struct db_queue_enum queue_enum; + struct query_params query_params; int ret; DPRINTF(E_DBG, L_DB, "Fetch by pos: pos (%d) relative to item with id (%d)\n", pos, item_id); - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); queue_item = calloc(1, sizeof(struct db_queue_item)); if (!queue_item) { @@ -4956,8 +4943,8 @@ db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) db_transaction_begin(); - ret = queue_fetch_byposrelativetoitem(&queue_enum, pos, item_id, shuffle, queue_item, 1); - queue_enum_end(&queue_enum); + ret = queue_fetch_byposrelativetoitem(&query_params, pos, item_id, shuffle, queue_item, 1); + db_query_end(&query_params); db_transaction_end(); @@ -5002,7 +4989,7 @@ db_queue_cleanup() #define Q_TMPL_UPDATE "UPDATE queue SET pos = %d WHERE id = %d;" #define Q_TMPL_UPDATE_SHUFFLE "UPDATE queue SET shuffle_pos = %d WHERE id = %d;" - struct db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; char *query; int pos; @@ -5027,9 +5014,9 @@ db_queue_cleanup() } // Update position of normal queue - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); - ret = queue_enum_start(&queue_enum); + ret = queue_enum_start(&query_params); if (ret < 0) { db_transaction_rollback(); @@ -5037,7 +5024,7 @@ db_queue_cleanup() } pos = 0; - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0)) + while ((ret = queue_enum_fetch(&query_params, &queue_item, 0)) == 0 && (queue_item.id > 0)) { if (queue_item.pos != pos) { @@ -5053,7 +5040,7 @@ db_queue_cleanup() pos++; } - queue_enum_end(&queue_enum); + db_query_end(&query_params); if (ret < 0) { @@ -5062,10 +5049,10 @@ db_queue_cleanup() } // Update position of shuffle queue - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); - queue_enum.orderby_shufflepos = 1; + memset(&query_params, 0, sizeof(struct query_params)); + query_params.sort = S_SHUFFLE_POS; - ret = queue_enum_start(&queue_enum); + ret = queue_enum_start(&query_params); if (ret < 0) { db_transaction_rollback(); @@ -5073,7 +5060,7 @@ db_queue_cleanup() } pos = 0; - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0)) + while ((ret = queue_enum_fetch(&query_params, &queue_item, 0)) == 0 && (queue_item.id > 0)) { if (queue_item.shuffle_pos != pos) { @@ -5089,7 +5076,7 @@ db_queue_cleanup() pos++; } - queue_enum_end(&queue_enum); + db_query_end(&query_params); if (ret < 0) { @@ -5163,33 +5150,33 @@ queue_delete_item(struct db_queue_item *queue_item) int db_queue_delete_byitemid(uint32_t item_id) { - struct db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; int ret; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); db_transaction_begin(); - ret = queue_fetch_byitemid(&queue_enum, item_id, &queue_item, 0); + ret = queue_fetch_byitemid(&query_params, item_id, &queue_item, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } if (queue_item.id == 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_end(); return 0; } ret = queue_delete_item(&queue_item); - queue_enum_end(&queue_enum); + db_query_end(&query_params); if (ret < 0) { @@ -5207,26 +5194,26 @@ db_queue_delete_byitemid(uint32_t item_id) int db_queue_delete_bypos(uint32_t pos, int count) { - struct db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; int to_pos; int ret; // Find items with the given position - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); to_pos = pos + count; - queue_enum.filter = sqlite3_mprintf("pos => %d AND pos < %d", pos, to_pos); + query_params.filter = sqlite3_mprintf("pos => %d AND pos < %d", pos, to_pos); db_transaction_begin(); - ret = queue_enum_start(&queue_enum); + ret = queue_enum_start(&query_params); if (ret < 0) { return -1; } - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0)) + while ((ret = queue_enum_fetch(&query_params, &queue_item, 0)) == 0 && (queue_item.id > 0)) { ret = queue_delete_item(&queue_item); if (ret < 0) @@ -5236,7 +5223,7 @@ db_queue_delete_bypos(uint32_t pos, int count) } } - queue_enum_end(&queue_enum); + db_query_end(&query_params); if (ret < 0) { @@ -5254,32 +5241,32 @@ db_queue_delete_bypos(uint32_t pos, int count) int db_queue_delete_byposrelativetoitem(uint32_t pos, uint32_t item_id, char shuffle) { - struct db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; int ret; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); db_transaction_begin(); - ret = queue_fetch_byposrelativetoitem(&queue_enum, pos, item_id, shuffle, &queue_item, 0); + ret = queue_fetch_byposrelativetoitem(&query_params, pos, item_id, shuffle, &queue_item, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } else if (queue_item.id == 0) { // No item found - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_end(); return 0; } ret = queue_delete_item(&queue_item); - queue_enum_end(&queue_enum); + db_query_end(&query_params); if (ret < 0) { @@ -5362,26 +5349,26 @@ int db_queue_move_bypos(int pos_from, int pos_to) { struct db_queue_item queue_item; - struct db_queue_enum queue_enum; + struct query_params query_params; char *query; int ret; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); db_transaction_begin(); // Find item to move - ret = queue_fetch_bypos(&queue_enum, pos_from, 0, &queue_item, 0); + ret = queue_fetch_bypos(&query_params, pos_from, 0, &queue_item, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } if (queue_item.id == 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_end(); return 0; } @@ -5391,7 +5378,7 @@ db_queue_move_bypos(int pos_from, int pos_to) ret = db_query_run(query, 1, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5401,7 +5388,7 @@ db_queue_move_bypos(int pos_from, int pos_to) ret = db_query_run(query, 1, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5411,12 +5398,12 @@ db_queue_move_bypos(int pos_from, int pos_to) ret = db_query_run(query, 1, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_end(); queue_inc_version_and_notify(); @@ -5435,24 +5422,24 @@ db_queue_move_bypos(int pos_from, int pos_to) int db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_t item_id, char shuffle) { - struct db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; char *query; int pos_move_from; int pos_move_to; int ret; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); db_transaction_begin(); DPRINTF(E_DBG, L_DB, "Move by pos: from %d offset %d relative to item (%d)\n", from_pos, to_offset, item_id); // Find item with the given item_id - ret = queue_fetch_byitemid(&queue_enum, item_id, &queue_item, 0); + ret = queue_fetch_byitemid(&query_params, item_id, &queue_item, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5461,7 +5448,7 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ if (queue_item.id == 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_end(); return 0; } @@ -5486,15 +5473,15 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ pos_move_to++; } - queue_enum_end(&queue_enum); + db_query_end(&query_params); DPRINTF(E_DBG, L_DB, "Move by pos: absolute pos: move from %d to %d\n", pos_move_from, pos_move_to); // Find item to move - ret = queue_fetch_bypos(&queue_enum, pos_move_from, shuffle, &queue_item, 0); + ret = queue_fetch_bypos(&query_params, pos_move_from, shuffle, &queue_item, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5503,7 +5490,7 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ if (queue_item.id == 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_end(); return 0; } @@ -5517,7 +5504,7 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ ret = db_query_run(query, 1, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5531,7 +5518,7 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ ret = db_query_run(query, 1, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5545,12 +5532,12 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ ret = db_query_run(query, 1, 0); if (ret < 0) { - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_rollback(); return -1; } - queue_enum_end(&queue_enum); + db_query_end(&query_params); db_transaction_end(); queue_inc_version_and_notify(); @@ -5576,7 +5563,7 @@ db_queue_reshuffle(uint32_t item_id) int *shuffle_pos; int len; int i; - struct db_queue_enum queue_enum; + struct query_params query_params; int ret; db_transaction_begin(); @@ -5620,19 +5607,19 @@ db_queue_reshuffle(uint32_t item_id) - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); - queue_enum.filter = sqlite3_mprintf("pos >= %d", pos); + memset(&query_params, 0, sizeof(struct query_params)); + query_params.filter = sqlite3_mprintf("pos >= %d", pos); - ret = queue_enum_start(&queue_enum); + ret = queue_enum_start(&query_params); if (ret < 0) { - sqlite3_free(queue_enum.filter); + sqlite3_free(query_params.filter); db_transaction_rollback(); return -1; } i = 0; - while ((ret = queue_enum_fetch(&queue_enum, &queue_item, 0)) == 0 && (queue_item.id > 0) && (i < len)) + while ((ret = queue_enum_fetch(&query_params, &queue_item, 0)) == 0 && (queue_item.id > 0) && (i < len)) { query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = %d where id = %d;", shuffle_pos[i], queue_item.id); ret = db_query_run(query, 1, 0); @@ -5645,7 +5632,7 @@ db_queue_reshuffle(uint32_t item_id) i++; } - queue_enum_end(&queue_enum); + db_query_end(&query_params); if (ret < 0) { diff --git a/src/db.h b/src/db.h index bb607f41..600e3b38 100644 --- a/src/db.h +++ b/src/db.h @@ -28,6 +28,8 @@ enum sort_type { S_DISC, S_TRACK, S_VPATH, + S_POS, + S_SHUFFLE_POS, }; #define Q_F_BROWSE (1 << 15) @@ -417,17 +419,6 @@ struct db_queue_item uint32_t disc; }; -struct db_queue_enum -{ - /* 0 = ordered by position, 1 = ordered by position in shuffle queue */ - int orderby_shufflepos; - - char *filter; - - /* Private enum context, keep out */ - sqlite3_stmt *stmt; -}; - char * db_escape_string(const char *str); @@ -726,13 +717,13 @@ int db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id); int -db_queue_enum_start(struct db_queue_enum *queue_enum); +db_queue_enum_start(struct query_params *query_params); void -db_queue_enum_end(struct db_queue_enum *queue_enum); +db_queue_enum_end(struct query_params *query_params); int -db_queue_enum_fetch(struct db_queue_enum *queue_enum, struct db_queue_item *queue_item); +db_queue_enum_fetch(struct query_params *query_params, struct db_queue_item *queue_item); struct db_queue_item * db_queue_fetch_byitemid(uint32_t item_id); diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 02eb4fd4..6897d98f 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1568,7 +1568,7 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, int count; int ret; int start_index; - struct db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; /* /ctrl-int/1/playqueue-contents?span=50&session-id=... */ @@ -1629,13 +1629,13 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, { player_get_status(&status); - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); if (status.shuffle) - queue_enum.orderby_shufflepos = 1; - ret = db_queue_enum_start(&queue_enum); + query_params.sort = S_SHUFFLE_POS; + ret = db_queue_enum_start(&query_params); count = 0; //FIXME [queue] Check count value - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) + while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0) { if (status.item_id == 0 || status.item_id == queue_item.id) count = 1; @@ -1654,8 +1654,8 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, } } - db_queue_enum_end(&queue_enum); - sqlite3_free(queue_enum.filter); + db_queue_enum_end(&query_params); + sqlite3_free(query_params.filter); } /* Playlists are hist, curr and main. */ diff --git a/src/mpd.c b/src/mpd.c index 74f335df..333fd2bb 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1794,7 +1794,7 @@ 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 db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; uint32_t songid; int ret; @@ -1813,22 +1813,22 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err } } - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); if (songid > 0) - queue_enum.filter = sqlite3_mprintf("id = %d", songid); + query_params.filter = sqlite3_mprintf("id = %d", songid); - ret = db_queue_enum_start(&queue_enum); + ret = db_queue_enum_start(&query_params); if (ret < 0) { - sqlite3_free(queue_enum.filter); + sqlite3_free(query_params.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; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) + while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0) { ret = mpd_add_db_queue_item(evbuf, &queue_item); if (ret < 0) @@ -1837,14 +1837,14 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); - db_queue_enum_end(&queue_enum); - sqlite3_free(queue_enum.filter); + db_queue_enum_end(&query_params); + sqlite3_free(query_params.filter); return ACK_ERROR_UNKNOWN; } } - db_queue_enum_end(&queue_enum); - sqlite3_free(queue_enum.filter); + db_queue_enum_end(&query_params); + sqlite3_free(query_params.filter); return 0; } @@ -1860,7 +1860,7 @@ 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 db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; int start_pos; int end_pos; @@ -1868,7 +1868,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e start_pos = 0; end_pos = 0; - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); if (argc > 1) { @@ -1884,20 +1884,20 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e 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]); else - queue_enum.filter = sqlite3_mprintf("pos >= %d AND pos < %d", start_pos, end_pos); + query_params.filter = sqlite3_mprintf("pos >= %d AND pos < %d", start_pos, end_pos); } - ret = db_queue_enum_start(&queue_enum); + ret = db_queue_enum_start(&query_params); if (ret < 0) { - sqlite3_free(queue_enum.filter); + sqlite3_free(query_params.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; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) + while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0) { ret = mpd_add_db_queue_item(evbuf, &queue_item); if (ret < 0) @@ -1907,14 +1907,14 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); - db_queue_enum_end(&queue_enum); - sqlite3_free(queue_enum.filter); + db_queue_enum_end(&query_params); + sqlite3_free(query_params.filter); return ACK_ERROR_UNKNOWN; } } - db_queue_enum_end(&queue_enum); - sqlite3_free(queue_enum.filter); + db_queue_enum_end(&query_params); + sqlite3_free(query_params.filter); return 0; } @@ -1926,7 +1926,7 @@ 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 db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; int ret; @@ -1934,9 +1934,9 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm * 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. */ - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); - ret = db_queue_enum_start(&queue_enum); + ret = db_queue_enum_start(&query_params); if (ret < 0) { ret = asprintf(errmsg, "Failed to start queue enum for command plchanges"); @@ -1945,7 +1945,7 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm return ACK_ERROR_ARG; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) + while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0) { ret = mpd_add_db_queue_item(evbuf, &queue_item); if (ret < 0) @@ -1955,12 +1955,12 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); - db_queue_enum_end(&queue_enum); + db_queue_enum_end(&query_params); return ACK_ERROR_UNKNOWN; } } - db_queue_enum_end(&queue_enum); + db_queue_enum_end(&query_params); return 0; } @@ -1972,7 +1972,7 @@ 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 db_queue_enum queue_enum; + struct query_params query_params; struct db_queue_item queue_item; int ret; @@ -1980,9 +1980,9 @@ mpd_command_plchangesposid(struct evbuffer *evbuf, int argc, char **argv, char * * 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. */ - memset(&queue_enum, 0, sizeof(struct db_queue_enum)); + memset(&query_params, 0, sizeof(struct query_params)); - ret = db_queue_enum_start(&queue_enum); + ret = db_queue_enum_start(&query_params); if (ret < 0) { ret = asprintf(errmsg, "Failed to start queue enum for command plchangesposid"); @@ -1991,7 +1991,7 @@ mpd_command_plchangesposid(struct evbuffer *evbuf, int argc, char **argv, char * return ACK_ERROR_ARG; } - while ((ret = db_queue_enum_fetch(&queue_enum, &queue_item)) == 0 && queue_item.id > 0) + while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0) { evbuffer_add_printf(evbuf, "cpos: %d\n" @@ -2000,7 +2000,7 @@ mpd_command_plchangesposid(struct evbuffer *evbuf, int argc, char **argv, char * queue_item.id); } - db_queue_enum_end(&queue_enum); + db_queue_enum_end(&query_params); return 0; } From 09c2e7ca768bcfb68b857b3c84755691404e51bb Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 4 Dec 2016 09:05:11 +0100 Subject: [PATCH 20/23] [db] Add missing free of query_param.filter --- src/db.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/db.c b/src/db.c index de7ad4ac..589afd39 100644 --- a/src/db.c +++ b/src/db.c @@ -4750,7 +4750,7 @@ queue_fetch_byitemid(struct query_params *query_params, uint32_t item_id, struct } ret = queue_enum_fetch(query_params, queue_item, keep_item); - + sqlite3_free(query_params->filter); return ret; } @@ -4859,7 +4859,7 @@ queue_fetch_bypos(struct query_params *query_params, uint32_t pos, char shuffle, } ret = queue_enum_fetch(query_params, queue_item, keep_item); - + sqlite3_free(query_params->filter); return ret; } @@ -5224,6 +5224,7 @@ db_queue_delete_bypos(uint32_t pos, int count) } db_query_end(&query_params); + sqlite3_free(query_params.filter); if (ret < 0) { @@ -5633,6 +5634,7 @@ db_queue_reshuffle(uint32_t item_id) } db_query_end(&query_params); + sqlite3_free(query_params.filter); if (ret < 0) { From edc609b531b2cab9e96d3fb81a29927103b5cdf1 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 4 Dec 2016 09:30:21 +0100 Subject: [PATCH 21/23] [db] Queue fetch functions should end the query after start and fetch --- src/db.c | 99 +++++++++++++++++--------------------------------------- 1 file changed, 29 insertions(+), 70 deletions(-) diff --git a/src/db.c b/src/db.c index 589afd39..c2da17ac 100644 --- a/src/db.c +++ b/src/db.c @@ -4735,22 +4735,24 @@ db_queue_get_pos_byfileid(uint32_t file_id, char shuffle) } static int -queue_fetch_byitemid(struct query_params *query_params, uint32_t item_id, struct db_queue_item *queue_item, int keep_item) +queue_fetch_byitemid(uint32_t item_id, struct db_queue_item *queue_item, int with_metadata) { + struct query_params query_params; int ret; - memset(query_params, 0, sizeof(struct query_params)); - query_params->filter = sqlite3_mprintf("id = %d", item_id); + memset(&query_params, 0, sizeof(struct query_params)); + query_params.filter = sqlite3_mprintf("id = %d", item_id); - ret = queue_enum_start(query_params); + ret = queue_enum_start(&query_params); if (ret < 0) { - sqlite3_free(query_params->filter); + sqlite3_free(query_params.filter); return -1; } - ret = queue_enum_fetch(query_params, queue_item, keep_item); - sqlite3_free(query_params->filter); + ret = queue_enum_fetch(&query_params, queue_item, with_metadata); + db_query_end(&query_params); + sqlite3_free(query_params.filter); return ret; } @@ -4758,10 +4760,8 @@ struct db_queue_item * db_queue_fetch_byitemid(uint32_t item_id) { struct db_queue_item *queue_item; - struct query_params query_params; int ret; - memset(&query_params, 0, sizeof(struct query_params)); queue_item = calloc(1, sizeof(struct db_queue_item)); if (!queue_item) { @@ -4770,8 +4770,7 @@ db_queue_fetch_byitemid(uint32_t item_id) } db_transaction_begin(); - ret = queue_fetch_byitemid(&query_params, item_id, queue_item, 1); - db_query_end(&query_params); + ret = queue_fetch_byitemid(item_id, queue_item, 1); db_transaction_end(); if (ret < 0) @@ -4841,25 +4840,27 @@ db_queue_fetch_byfileid(uint32_t file_id) } static int -queue_fetch_bypos(struct query_params *query_params, uint32_t pos, char shuffle, struct db_queue_item *queue_item, int keep_item) +queue_fetch_bypos(uint32_t pos, char shuffle, struct db_queue_item *queue_item, int with_metadata) { + struct query_params query_params; int ret; - memset(query_params, 0, sizeof(struct query_params)); + memset(&query_params, 0, sizeof(struct query_params)); if (shuffle) - query_params->filter = sqlite3_mprintf("shuffle_pos = %d", pos); + query_params.filter = sqlite3_mprintf("shuffle_pos = %d", pos); else - query_params->filter = sqlite3_mprintf("pos = %d", pos); + query_params.filter = sqlite3_mprintf("pos = %d", pos); - ret = queue_enum_start(query_params); + ret = queue_enum_start(&query_params); if (ret < 0) { - sqlite3_free(query_params->filter); + sqlite3_free(query_params.filter); return -1; } - ret = queue_enum_fetch(query_params, queue_item, keep_item); - sqlite3_free(query_params->filter); + ret = queue_enum_fetch(&query_params, queue_item, with_metadata); + db_query_end(&query_params); + sqlite3_free(query_params.filter); return ret; } @@ -4867,10 +4868,8 @@ struct db_queue_item * db_queue_fetch_bypos(uint32_t pos, char shuffle) { struct db_queue_item *queue_item; - struct query_params query_params; int ret; - memset(&query_params, 0, sizeof(struct query_params)); queue_item = calloc(1, sizeof(struct db_queue_item)); if (!queue_item) { @@ -4879,8 +4878,7 @@ db_queue_fetch_bypos(uint32_t pos, char shuffle) } db_transaction_begin(); - ret = queue_fetch_bypos(&query_params, pos, shuffle, queue_item, 1); - db_query_end(&query_params); + ret = queue_fetch_bypos(pos, shuffle, queue_item, 1); db_transaction_end(); if (ret < 0) @@ -4900,7 +4898,7 @@ db_queue_fetch_bypos(uint32_t pos, char shuffle) } static int -queue_fetch_byposrelativetoitem(struct query_params *query_params, int pos, uint32_t item_id, char shuffle, struct db_queue_item *queue_item, int keep_item) +queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle, struct db_queue_item *queue_item, int with_metadata) { int pos_absolute; int ret; @@ -4917,7 +4915,7 @@ queue_fetch_byposrelativetoitem(struct query_params *query_params, int pos, uint pos_absolute += pos; - ret = queue_fetch_bypos(query_params, pos_absolute, shuffle, queue_item, keep_item); + ret = queue_fetch_bypos(pos_absolute, shuffle, queue_item, with_metadata); DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->id, queue_item->pos, queue_item->file_id); @@ -4928,12 +4926,10 @@ struct db_queue_item * db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) { struct db_queue_item *queue_item; - struct query_params query_params; int ret; DPRINTF(E_DBG, L_DB, "Fetch by pos: pos (%d) relative to item with id (%d)\n", pos, item_id); - memset(&query_params, 0, sizeof(struct query_params)); queue_item = calloc(1, sizeof(struct db_queue_item)); if (!queue_item) { @@ -4943,8 +4939,7 @@ db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) db_transaction_begin(); - ret = queue_fetch_byposrelativetoitem(&query_params, pos, item_id, shuffle, queue_item, 1); - db_query_end(&query_params); + ret = queue_fetch_byposrelativetoitem(pos, item_id, shuffle, queue_item, 1); db_transaction_end(); @@ -5150,34 +5145,27 @@ queue_delete_item(struct db_queue_item *queue_item) int db_queue_delete_byitemid(uint32_t item_id) { - struct query_params query_params; struct db_queue_item queue_item; int ret; - memset(&query_params, 0, sizeof(struct query_params)); - db_transaction_begin(); - ret = queue_fetch_byitemid(&query_params, item_id, &queue_item, 0); + ret = queue_fetch_byitemid(item_id, &queue_item, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } if (queue_item.id == 0) { - db_query_end(&query_params); db_transaction_end(); return 0; } ret = queue_delete_item(&queue_item); - db_query_end(&query_params); - if (ret < 0) { db_transaction_rollback(); @@ -5242,33 +5230,26 @@ db_queue_delete_bypos(uint32_t pos, int count) int db_queue_delete_byposrelativetoitem(uint32_t pos, uint32_t item_id, char shuffle) { - struct query_params query_params; struct db_queue_item queue_item; int ret; - memset(&query_params, 0, sizeof(struct query_params)); - db_transaction_begin(); - ret = queue_fetch_byposrelativetoitem(&query_params, pos, item_id, shuffle, &queue_item, 0); + ret = queue_fetch_byposrelativetoitem(pos, item_id, shuffle, &queue_item, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } else if (queue_item.id == 0) { // No item found - db_query_end(&query_params); db_transaction_end(); return 0; } ret = queue_delete_item(&queue_item); - db_query_end(&query_params); - if (ret < 0) { db_transaction_rollback(); @@ -5350,26 +5331,21 @@ int db_queue_move_bypos(int pos_from, int pos_to) { struct db_queue_item queue_item; - struct query_params query_params; char *query; int ret; - memset(&query_params, 0, sizeof(struct query_params)); - db_transaction_begin(); // Find item to move - ret = queue_fetch_bypos(&query_params, pos_from, 0, &queue_item, 0); + ret = queue_fetch_bypos(pos_from, 0, &queue_item, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } if (queue_item.id == 0) { - db_query_end(&query_params); db_transaction_end(); return 0; } @@ -5379,7 +5355,6 @@ db_queue_move_bypos(int pos_from, int pos_to) ret = db_query_run(query, 1, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5389,7 +5364,6 @@ db_queue_move_bypos(int pos_from, int pos_to) ret = db_query_run(query, 1, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5399,12 +5373,10 @@ db_queue_move_bypos(int pos_from, int pos_to) ret = db_query_run(query, 1, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } - db_query_end(&query_params); db_transaction_end(); queue_inc_version_and_notify(); @@ -5423,24 +5395,20 @@ db_queue_move_bypos(int pos_from, int pos_to) int db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_t item_id, char shuffle) { - struct query_params query_params; struct db_queue_item queue_item; char *query; int pos_move_from; int pos_move_to; int ret; - memset(&query_params, 0, sizeof(struct query_params)); - db_transaction_begin(); DPRINTF(E_DBG, L_DB, "Move by pos: from %d offset %d relative to item (%d)\n", from_pos, to_offset, item_id); // Find item with the given item_id - ret = queue_fetch_byitemid(&query_params, item_id, &queue_item, 0); + ret = queue_fetch_byitemid(item_id, &queue_item, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5449,7 +5417,6 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ if (queue_item.id == 0) { - db_query_end(&query_params); db_transaction_end(); return 0; } @@ -5474,15 +5441,12 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ pos_move_to++; } - db_query_end(&query_params); - DPRINTF(E_DBG, L_DB, "Move by pos: absolute pos: move from %d to %d\n", pos_move_from, pos_move_to); // Find item to move - ret = queue_fetch_bypos(&query_params, pos_move_from, shuffle, &queue_item, 0); + ret = queue_fetch_bypos(pos_move_from, shuffle, &queue_item, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5491,7 +5455,6 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ if (queue_item.id == 0) { - db_query_end(&query_params); db_transaction_end(); return 0; } @@ -5505,7 +5468,6 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ ret = db_query_run(query, 1, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5519,7 +5481,6 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ ret = db_query_run(query, 1, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } @@ -5533,12 +5494,10 @@ db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_ ret = db_query_run(query, 1, 0); if (ret < 0) { - db_query_end(&query_params); db_transaction_rollback(); return -1; } - db_query_end(&query_params); db_transaction_end(); queue_inc_version_and_notify(); From d339f5bcd2cfaffb2e9e63fc856c0c1c7b7e9bfb Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 4 Dec 2016 11:34:19 +0100 Subject: [PATCH 22/23] [db] fixup --- src/db.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db.c b/src/db.c index c2da17ac..7f82a9da 100644 --- a/src/db.c +++ b/src/db.c @@ -4553,14 +4553,14 @@ db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id) static int queue_enum_start(struct query_params *query_params) { -#define Q_TMPL "SELECT * FROM queue WHERE %s ORDER BY %s;" +#define Q_TMPL "SELECT * FROM queue WHERE %s %s;" char *query; const char *orderby; int ret; query_params->stmt = NULL; - if (query_params) + if (query_params->sort) orderby = sort_clause[query_params->sort]; else orderby = sort_clause[S_POS]; From 2d1e5f6d6087e506f89e90ac6cf3293d7fdfc9a7 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 4 Dec 2016 17:48:35 +0100 Subject: [PATCH 23/23] [player/db] Remove function "db_file_get_seekpos" --- src/db.c | 25 ------------------------- src/db.h | 3 --- src/player.c | 15 +++++++++++---- 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/src/db.c b/src/db.c index 7f82a9da..62592ef7 100644 --- a/src/db.c +++ b/src/db.c @@ -2024,31 +2024,6 @@ 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 600e3b38..47e212f6 100644 --- a/src/db.h +++ b/src/db.h @@ -498,9 +498,6 @@ 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/player.c b/src/player.c index 7c52ee70..17ea5172 100644 --- a/src/player.c +++ b/src/player.c @@ -2280,6 +2280,7 @@ static enum command_state playback_start_item(void *arg, int *retval) { struct db_queue_item *queue_item = arg; + struct media_file_info *mfi; struct output_device *device; struct player_source *ps; int seek_ms; @@ -2316,16 +2317,22 @@ playback_start_item(void *arg, int *retval) ps = source_new(queue_item); if (!ps) - { + { playback_abort(); *retval = -1; return COMMAND_END; } + seek_ms = 0; if (queue_item->file_id > 0) - seek_ms = db_file_get_seekpos(queue_item->file_id); - else - seek_ms = 0; + { + mfi = db_file_fetch_byid(queue_item->file_id); + if (mfi) + { + seek_ms = mfi->seek; + free_mfi(mfi, 0); + } + } ret = source_open(ps, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, seek_ms); if (ret < 0)