From 986b37ed29d6d520919b7ac72698147c000d8a59 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 30 Mar 2015 01:03:15 +0200 Subject: [PATCH] Adds a playlist folder type, it is needed for sorting since some clients (eg Retune) require that playlist folders are sent before their content. Playlist folders should, however, be sent after the base playlists, so the numbering is changed. At the same time makes the value of the smart playlist type a bit less hardcoded. --- src/db.c | 111 +++++++++++++++++++++++++++++------------------ src/db.h | 6 ++- src/httpd_daap.c | 2 +- 3 files changed, 73 insertions(+), 46 deletions(-) diff --git a/src/db.c b/src/db.c index 0fbc05bd..6af3d52a 100644 --- a/src/db.c +++ b/src/db.c @@ -723,7 +723,7 @@ db_analyze(void) static void db_set_cfg_names(void) { -#define Q_TMPL "UPDATE playlists SET title = '%q' WHERE type = 1 AND special_id = %d;" +#define Q_TMPL "UPDATE playlists SET title = '%q' WHERE type = %d AND special_id = %d;" char *cfg_item[6] = { "name_library", "name_music", "name_movies", "name_tvshows", "name_podcasts", "name_audiobooks" }; char special_id[6] = { 0, 6, 4, 5, 1, 7 }; cfg_t *lib; @@ -745,7 +745,7 @@ db_set_cfg_names(void) continue; } - query = sqlite3_mprintf(Q_TMPL, title, special_id[i]); + query = sqlite3_mprintf(Q_TMPL, title, PL_SMART, special_id[i]); if (!query) { DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); @@ -787,9 +787,9 @@ db_purge_cruft(time_t ref) char *queries[3] = { NULL, NULL, NULL }; char *queries_tmpl[3] = { - "DELETE FROM playlistitems WHERE playlistid IN (SELECT id FROM playlists p WHERE p.type <> 1 AND p.db_timestamp < %" PRIi64 ");", - "DELETE FROM playlists WHERE type <> 1 AND db_timestamp < %" PRIi64 ";", - "DELETE FROM files WHERE db_timestamp < %" PRIi64 ";" + "DELETE FROM playlistitems WHERE playlistid IN (SELECT id FROM playlists p WHERE p.type <> %d AND p.db_timestamp < %" PRIi64 ");", + "DELETE FROM playlists WHERE type <> %d AND db_timestamp < %" PRIi64 ";", + "DELETE FROM files WHERE -1 <> %d AND db_timestamp < %" PRIi64 ";" }; if (sizeof(queries) != sizeof(queries_tmpl)) @@ -800,7 +800,7 @@ db_purge_cruft(time_t ref) for (i = 0; i < (sizeof(queries_tmpl) / sizeof(queries_tmpl[0])); i++) { - queries[i] = sqlite3_mprintf(queries_tmpl[i], (int64_t)ref); + queries[i] = sqlite3_mprintf(queries_tmpl[i], PL_SMART, (int64_t)ref); if (!queries[i]) { DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); @@ -834,15 +834,16 @@ db_purge_cruft(time_t ref) void db_purge_all(void) { +#define Q_TMPL "DELETE FROM playlists WHERE type <> %d;" char *queries[5] = { "DELETE FROM inotify;", "DELETE FROM playlistitems;", - "DELETE FROM playlists WHERE type <> 1;", "DELETE FROM files;", "DELETE FROM groups;", }; char *errmsg; + char *query; int i; int ret; @@ -860,6 +861,28 @@ db_purge_all(void) else DPRINTF(E_DBG, L_DB, "Purged %d rows\n", sqlite3_changes(hdl)); } + + query = sqlite3_mprintf(Q_TMPL, PL_SMART); + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + return; + } + + DPRINTF(E_DBG, L_DB, "Running purge query '%s'\n", query); + + ret = db_exec(query, &errmsg); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Purge query '%s' error: %s\n", query, errmsg); + + sqlite3_free(errmsg); + } + else + DPRINTF(E_DBG, L_DB, "Purged %d rows\n", sqlite3_changes(hdl)); + + sqlite3_free(query); +#undef Q_TMPL } static int @@ -1205,6 +1228,7 @@ db_build_query_plitems(struct query_params *qp, char **q) break; case PL_PLAIN: + case PL_FOLDER: ret = db_build_query_plitems_plain(qp, q); break; @@ -1747,6 +1771,7 @@ db_query_fetch_pl(struct query_params *qp, struct db_playlist_info *dbpli) switch (type) { case PL_PLAIN: + case PL_FOLDER: id = sqlite3_column_int(qp->stmt, 0); nitems = db_pl_count_items(id, 0); nstreams = db_pl_count_items(id, 1); @@ -3030,6 +3055,7 @@ db_pl_fetch_byquery(char *query) switch (pli->type) { case PL_PLAIN: + case PL_FOLDER: pli->items = db_pl_count_items(pli->id, 0); pli->streams = db_pl_count_items(pli->id, 1); break; @@ -4600,27 +4626,27 @@ db_perthread_deinit(void) #define Q_PL1 \ "INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \ - " VALUES(1, 'Library', 1, '1 = 1', 0, '', 0, 0);" + " VALUES(1, 'Library', 2, '1 = 1', 0, '', 0, 0);" #define Q_PL2 \ "INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \ - " VALUES(2, 'Music', 1, 'f.media_kind = 1', 0, '', 0, 6);" + " VALUES(2, 'Music', 2, 'f.media_kind = 1', 0, '', 0, 6);" #define Q_PL3 \ "INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \ - " VALUES(3, 'Movies', 1, 'f.media_kind = 2', 0, '', 0, 4);" + " VALUES(3, 'Movies', 2, 'f.media_kind = 2', 0, '', 0, 4);" #define Q_PL4 \ "INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \ - " VALUES(4, 'TV Shows', 1, 'f.media_kind = 64', 0, '', 0, 5);" + " VALUES(4, 'TV Shows', 2, 'f.media_kind = 64', 0, '', 0, 5);" #define Q_PL5 \ "INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \ - " VALUES(5, 'Podcasts', 1, 'f.media_kind = 4', 0, '', 0, 1);" + " VALUES(5, 'Podcasts', 2, 'f.media_kind = 4', 0, '', 0, 1);" #define Q_PL6 \ "INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \ - " VALUES(6, 'Audiobooks', 1, 'f.media_kind = 8', 0, '', 0, 7);" + " VALUES(6, 'Audiobooks', 2, 'f.media_kind = 8', 0, '', 0, 7);" /* These are the remaining automatically-created iTunes playlists, but * their query is unknown @@ -4628,12 +4654,12 @@ db_perthread_deinit(void) " VALUES(8, 'Purchased', 0, 'media_kind = 1024', 0, '', 0, 8);" */ -#define SCHEMA_VERSION_MAJOR 16 -#define SCHEMA_VERSION_MINOR 01 +#define SCHEMA_VERSION_MAJOR 17 +#define SCHEMA_VERSION_MINOR 00 #define Q_SCVER_MAJOR \ - "INSERT INTO admin (key, value) VALUES ('schema_version_major', '16');" + "INSERT INTO admin (key, value) VALUES ('schema_version_major', '17');" #define Q_SCVER_MINOR \ - "INSERT INTO admin (key, value) VALUES ('schema_version_minor', '01');" + "INSERT INTO admin (key, value) VALUES ('schema_version_minor', '00');" struct db_init_query { char *query; @@ -5735,11 +5761,11 @@ db_upgrade_v16(void) char *title; int id; char *path; - int data_kind; + int type; char virtual_path[PATH_MAX]; int ret; - query = "SELECT id, album_artist, album, title, path, data_kind FROM files;"; + query = "SELECT id, album_artist, album, title, path FROM files;"; DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query); @@ -5757,7 +5783,6 @@ db_upgrade_v16(void) album = (char *)sqlite3_column_text(stmt, 2); title = (char *)sqlite3_column_text(stmt, 3); path = (char *)sqlite3_column_text(stmt, 4); - data_kind = sqlite3_column_int(stmt, 5); if (strncmp(path, "http:", strlen("http:")) == 0) { @@ -5802,25 +5827,20 @@ db_upgrade_v16(void) id = sqlite3_column_int(stmt, 0); title = (char *)sqlite3_column_text(stmt, 1); path = (char *)sqlite3_column_text(stmt, 2); - data_kind = sqlite3_column_int(stmt, 3); + type = sqlite3_column_int(stmt, 3); - if (data_kind == 0) /* Excludes default playlists */ + if (type == PL_PLAIN) /* Excludes default/Smart playlists and playlist folders */ { if (strncmp(path, "spotify:", strlen("spotify:")) == 0) - { - snprintf(virtual_path, PATH_MAX, "/spotify:/%s", title); - } - else - { - snprintf(virtual_path, PATH_MAX, "/file:%s", path); - } + snprintf(virtual_path, PATH_MAX, "/spotify:/%s", title); + else + snprintf(virtual_path, PATH_MAX, "/file:%s", path); uquery = sqlite3_mprintf("UPDATE playlists SET virtual_path = '%q' WHERE id = %d;", virtual_path, id); + ret = sqlite3_exec(hdl, uquery, NULL, NULL, &errmsg); if (ret != SQLITE_OK) - { DPRINTF(E_LOG, L_DB, "Error updating playlists: %s\n", errmsg); - } sqlite3_free(uquery); sqlite3_free(errmsg); @@ -5833,23 +5853,28 @@ db_upgrade_v16(void) return 0; } -/* Upgrade from schema v16.00 to v16.01 */ -/* Expand data model to allow for nested playlists */ +/* Upgrade from schema v16.00 to v17.00 */ +/* Expand data model to allow for nested playlists and change default playlist + * enumeration + */ -#define U_V1601_PL_PARENTID_ADD \ +#define U_V17_PL_PARENTID_ADD \ "ALTER TABLE playlists ADD COLUMN parent_id INTEGER DEFAULT 0;" +#define U_V17_PL_TYPE_CHANGE \ + "UPDATE playlists SET type = 2 WHERE type = 1;" -#define U_V1601_SCVER_MAJOR \ - "UPDATE admin SET value = '16' WHERE key = 'schema_version_major';" -#define U_V1601_SCVER_MINOR \ - "UPDATE admin SET value = '01' WHERE key = 'schema_version_minor';" +#define U_V17_SCVER_MAJOR \ + "UPDATE admin SET value = '17' WHERE key = 'schema_version_major';" +#define U_V17_SCVER_MINOR \ + "UPDATE admin SET value = '00' WHERE key = 'schema_version_minor';" -static const struct db_init_query db_upgrade_v1601_queries[] = +static const struct db_init_query db_upgrade_v17_queries[] = { - { U_V1601_PL_PARENTID_ADD,"expanding table playlists with parent_id column" }, + { U_V17_PL_PARENTID_ADD,"expanding table playlists with parent_id column" }, + { U_V17_PL_TYPE_CHANGE, "changing numbering of default playlists 1 -> 2" }, - { U_V1601_SCVER_MAJOR, "set schema_version_major to 16" }, - { U_V1601_SCVER_MINOR, "set schema_version_minor to 01" }, + { U_V17_SCVER_MAJOR, "set schema_version_major to 17" }, + { U_V17_SCVER_MINOR, "set schema_version_minor to 00" }, }; static int @@ -5931,7 +5956,7 @@ db_upgrade(int db_ver) /* FALLTHROUGH */ case 1600: - ret = db_generic_upgrade(db_upgrade_v1601_queries, sizeof(db_upgrade_v1601_queries) / sizeof(db_upgrade_v1601_queries[0])); + ret = db_generic_upgrade(db_upgrade_v17_queries, sizeof(db_upgrade_v17_queries) / sizeof(db_upgrade_v17_queries[0])); break; diff --git a/src/db.h b/src/db.h index b0c69de8..b653f7f9 100644 --- a/src/db.h +++ b/src/db.h @@ -165,10 +165,12 @@ struct media_file_info { #define mfi_offsetof(field) offsetof(struct media_file_info, field) +/* PL_SMART value must be in sync with type value in Q_PL* in db.c */ enum pl_type { PL_PLAIN = 0, - PL_SMART, - PL_MAX + PL_FOLDER = 1, + PL_SMART = 2, + PL_MAX, }; struct playlist_info { diff --git a/src/httpd_daap.c b/src/httpd_daap.c index df51684f..1facff31 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -1692,7 +1692,7 @@ daap_reply_playlists(struct evhttp_request *req, struct evbuffer *evbuf, char ** if (dfm == &dfm_dmap_mimc) continue; - /* com.apple.itunes.smart-playlist - type = 1 AND id != 1 */ + /* com.apple.itunes.smart-playlist - type = PL_SMART AND id != 1 */ if (dfm == &dfm_dmap_aeSP) { if ((pltype == PL_SMART) && (plid != 1))