[db/filescanner] Add index on fname, change playlist matching (wip)

This commit is contained in:
ejurgensen 2017-10-14 09:12:52 +02:00
parent 45bd2d6cc8
commit 4d47307a02
6 changed files with 143 additions and 60 deletions

View File

@ -684,9 +684,10 @@ db_purge_cruft(time_t ref)
int i; int i;
int ret; int ret;
char *query; char *query;
char *queries_tmpl[3] = char *queries_tmpl[4] =
{ {
"DELETE FROM playlistitems WHERE playlistid IN (SELECT id FROM playlists p WHERE p.type <> %d AND p.db_timestamp < %" PRIi64 ");", "DELETE FROM playlistitems WHERE playlistid IN (SELECT p.id FROM playlists p WHERE p.type <> %d AND p.db_timestamp < %" PRIi64 ");",
"DELETE FROM playlistitems WHERE filepath IN (SELECT f.path FROM files f WHERE -1 <> %d AND f.db_timestamp < %" PRIi64 ");",
"DELETE FROM playlists WHERE type <> %d AND db_timestamp < %" PRIi64 ";", "DELETE FROM playlists WHERE type <> %d AND db_timestamp < %" PRIi64 ";",
"DELETE FROM files WHERE -1 <> %d AND db_timestamp < %" PRIi64 ";", "DELETE FROM files WHERE -1 <> %d AND db_timestamp < %" PRIi64 ";",
}; };
@ -2959,6 +2960,21 @@ db_pl_add(struct playlist_info *pli, int *id)
#undef QADD_TMPL #undef QADD_TMPL
} }
int
db_pl_add_item_byfile(int plid, const char *filename)
{
#define Q_TMPL "INSERT INTO playlistitems (playlistid, filepath) SELECT %d, path FROM files WHERE fname='%q' GROUP BY fname HAVING COUNT(*)=1;"
char *query;
int ret;
query = sqlite3_mprintf(Q_TMPL, plid, filename);
ret = db_query_run(query, 1, 0);
return ((ret < 0) ? -1 : sqlite3_changes(hdl));
#undef Q_TMPL
}
int int
db_pl_add_item_bypath(int plid, const char *path) db_pl_add_item_bypath(int plid, const char *path)
{ {

View File

@ -593,6 +593,9 @@ db_pl_fetch_bytitlepath(const char *title, const char *path);
int int
db_pl_add(struct playlist_info *pli, int *id); db_pl_add(struct playlist_info *pli, int *id);
int
db_pl_add_item_byfile(int plid, const char *filename);
int int
db_pl_add_item_bypath(int plid, const char *path); db_pl_add_item_bypath(int plid, const char *path);

View File

@ -293,6 +293,9 @@ static const struct db_init_query db_init_table_queries[] =
#define I_RESCAN \ #define I_RESCAN \
"CREATE INDEX IF NOT EXISTS idx_rescan ON files(path, db_timestamp);" "CREATE INDEX IF NOT EXISTS idx_rescan ON files(path, db_timestamp);"
#define I_FNAME \
"CREATE INDEX IF NOT EXISTS idx_fname ON files(disabled, fname);"
#define I_SONGARTISTID \ #define I_SONGARTISTID \
"CREATE INDEX IF NOT EXISTS idx_sari ON files(songartistid);" "CREATE INDEX IF NOT EXISTS idx_sari ON files(songartistid);"
@ -370,6 +373,7 @@ static const struct db_init_query db_init_table_queries[] =
static const struct db_init_query db_init_index_queries[] = static const struct db_init_query db_init_index_queries[] =
{ {
{ I_RESCAN, "create rescan index" }, { I_RESCAN, "create rescan index" },
{ I_FNAME, "create filename index" },
{ I_SONGARTISTID, "create songartistid index" }, { I_SONGARTISTID, "create songartistid index" },
{ I_SONGALBUMID, "create songalbumid index" }, { I_SONGALBUMID, "create songalbumid index" },
{ I_STATEMKINDSARI, "create state/mkind/sari index" }, { I_STATEMKINDSARI, "create state/mkind/sari index" },

View File

@ -26,7 +26,7 @@
* is a major upgrade. In other words minor version upgrades permit downgrading * is a major upgrade. In other words minor version upgrades permit downgrading
* forked-daapd after the database was upgraded. */ * forked-daapd after the database was upgraded. */
#define SCHEMA_VERSION_MAJOR 19 #define SCHEMA_VERSION_MAJOR 19
#define SCHEMA_VERSION_MINOR 04 #define SCHEMA_VERSION_MINOR 05
int int
db_init_indices(sqlite3 *hdl); db_init_indices(sqlite3 *hdl);

View File

@ -1564,6 +1564,16 @@ static const struct db_upgrade_query db_upgrade_v1904_queries[] =
}; };
#define U_V1905_SCVER_MINOR \
"UPDATE admin SET value = '05' WHERE key = 'schema_version_minor';"
// Purpose of this upgrade is to reset the indeces, so that I_FNAME gets added
static const struct db_upgrade_query db_upgrade_v1905_queries[] =
{
{ U_V1905_SCVER_MINOR, "set schema_version_minor to 05" },
};
int int
db_upgrade(sqlite3 *hdl, int db_ver) db_upgrade(sqlite3 *hdl, int db_ver)
{ {
@ -1702,6 +1712,13 @@ db_upgrade(sqlite3 *hdl, int db_ver)
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */
case 1904:
ret = db_generic_upgrade(hdl, db_upgrade_v1905_queries, sizeof(db_upgrade_v1905_queries) / sizeof(db_upgrade_v1905_queries[0]));
if (ret < 0)
return -1;
break; break;
default: default:

View File

@ -73,13 +73,32 @@ extinf_get(char *string, struct media_file_info *mfi, int *extinf)
} }
static int static int
process_url(const char *path, time_t mtime, int extinf, struct media_file_info *mfi, char **filename) parent_dir(const char **current, const char *path)
{
const char *ptr;
if (*current)
ptr = *current;
else
ptr = strrchr(path, '/');
if (!ptr || (ptr == path))
return -1;
for (ptr--; (ptr > path) && (*ptr != '/'); ptr--)
;
*current = ptr;
return 0;
}
static int
process_url(int pl_id, const char *path, time_t mtime, int extinf, struct media_file_info *mfi)
{ {
char virtual_path[PATH_MAX]; char virtual_path[PATH_MAX];
int ret; int ret;
*filename = strdup(path);
if (extinf) if (extinf)
DPRINTF(E_INFO, L_SCAN, "Playlist has EXTINF metadata, artist is '%s', title is '%s'\n", mfi->artist, mfi->title); DPRINTF(E_INFO, L_SCAN, "Playlist has EXTINF metadata, artist is '%s', title is '%s'\n", mfi->artist, mfi->title);
@ -107,67 +126,99 @@ process_url(const char *path, time_t mtime, int extinf, struct media_file_info *
library_add_media(mfi); library_add_media(mfi);
return 0; return db_pl_add_item_bypath(pl_id, path);
} }
static int static int
process_regular_file(char **filename, char *path) process_regular_file(int pl_id, char *path)
{ {
struct query_params qp;
char filter[PATH_MAX];
const char *a;
const char *b;
char *dbpath;
char *winner;
int score;
int i; int i;
int mfi_id;
char *ptr;
char *entry;
int ret; int ret;
/* Playlist might be from Windows so we change backslash to forward slash */ // Playlist might be from Windows so we change backslash to forward slash
for (i = 0; i < strlen(path); i++) for (i = 0; i < strlen(path); i++)
{ {
if (path[i] == '\\') if (path[i] == '\\')
path[i] = '/'; path[i] = '/';
} }
/* Now search for the library item where the path has closest match to playlist item */ ret = snprintf(filter, sizeof(filter), "(f.fname = '%s')", filename_from_path(path)); // TODO make case insensitive?
/* Succes is when we find an unambiguous match, or when we no longer can expand the */ if (ret < 0 || ret >= sizeof(filter))
/* the path to refine our search. */
entry = NULL;
do
{ {
ptr = strrchr(path, '/'); DPRINTF(E_LOG, L_SCAN, "Playlist contains bad filename: '%s'\n", path);
if (entry)
*(entry - 1) = '/';
if (ptr)
{
*ptr = '\0';
entry = ptr + 1;
}
else
entry = path;
DPRINTF(E_SPAM, L_SCAN, "Playlist entry is now %s\n", entry);
ret = db_files_get_count_bymatch(entry);
}
while (ptr && (ret > 1));
if (ret > 0)
{
mfi_id = db_file_id_bymatch(entry);
DPRINTF(E_DBG, L_SCAN, "Found playlist entry match, id is %d, entry is %s\n", mfi_id, entry);
*filename = db_file_path_byid(mfi_id);
if (!(*filename))
{
DPRINTF(E_LOG, L_SCAN, "Playlist entry %s matches file id %d, but file path is missing.\n", entry, mfi_id);
return -1; return -1;
} }
}
else // Fast path - only works if there are not multiple items in the lib with the
// same filename
/* ret = db_pl_add_item_byfile(pl_id, filename_from_path(path));
if (ret == 1)
return 0;
DPRINTF(E_DBG, L_SCAN, "Fast path adding '%s' to playlist did not work (ret=%d), now searching\n", path, ret);
*/
memset(&qp, 0, sizeof(struct query_params));
qp.type = Q_BROWSE_PATH;
qp.sort = S_NONE;
qp.filter = filter;
ret = db_query_start(&qp);
if (ret < 0)
{ {
DPRINTF(E_DBG, L_SCAN, "No match for playlist entry %s\n", entry); db_query_end(&qp);
return -1; return -1;
} }
winner = NULL;
score = 0;
while (((ret = db_query_fetch_string(&qp, &dbpath)) == 0) && (dbpath))
{
if (qp.results == 1)
{
DPRINTF(E_DBG, L_SCAN, "Adding '%s' to playlist %d (fast path)\n", dbpath, pl_id);
winner = strdup(dbpath);
break;
}
for (i = 0, a = NULL, b = NULL; (parent_dir(&a, path) == 0) && (parent_dir(&b, dbpath) == 0) && (strcasecmp(a, b) == 0); i++)
;
DPRINTF(E_DBG, L_SCAN, "Comparison of '%s' and '%s' gave score %d\n", dbpath, path, i);
if (i > score)
{
free(winner);
winner = strdup(dbpath);
score = ret;
}
else if (i == score)
{
free(winner);
winner = NULL;
}
}
db_query_end(&qp);
if (!winner)
{
DPRINTF(E_LOG, L_SCAN, "No file in the library matches playlist entry '%s'\n", path);
return -1;
}
db_pl_add_item_bypath(pl_id, winner);
free(winner);
return 0; return 0;
} }
@ -189,7 +240,6 @@ scan_playlist(char *file, time_t mtime, int dir_id)
int counter; int counter;
int ret; int ret;
char virtual_path[PATH_MAX]; char virtual_path[PATH_MAX];
char *plitem_path;
ptr = strrchr(file, '.'); ptr = strrchr(file, '.');
if (!ptr) if (!ptr)
@ -309,14 +359,10 @@ scan_playlist(char *file, time_t mtime, int dir_id)
/* Check if line is an URL, will be added to library, otherwise it should already be there */ /* Check if line is an URL, will be added to library, otherwise it should already be there */
if (strncasecmp(path, "http://", 7) == 0) if (strncasecmp(path, "http://", 7) == 0)
ret = process_url(path, sb.st_mtime, extinf, &mfi, &plitem_path); ret = process_url(pl_id, path, sb.st_mtime, extinf, &mfi);
else else
ret = process_regular_file(&plitem_path, path); ret = process_regular_file(pl_id, path);
if (ret < 0)
continue;
ret = db_pl_add_item_bypath(pl_id, plitem_path);
if (ret == 0) if (ret == 0)
{ {
counter++; counter++;
@ -327,13 +373,10 @@ scan_playlist(char *file, time_t mtime, int dir_id)
db_transaction_begin(); db_transaction_begin();
} }
} }
else
DPRINTF(E_WARN, L_SCAN, "Could not add '%s' to playlist\n", plitem_path);
/* Clean up in preparation for next item */ /* Clean up in preparation for next item */
extinf = 0; extinf = 0;
free_mfi(&mfi, 1); free_mfi(&mfi, 1);
free(plitem_path);
} }
db_transaction_end(); db_transaction_end();