Merge branch 'force_rescan'

This commit is contained in:
ejurgensen 2013-12-03 23:13:43 +01:00
commit 91ab072e51
8 changed files with 199 additions and 9 deletions

6
README
View File

@ -257,6 +257,12 @@ be offered until they've been scanned.
Changes to the library are reflected in real time after the initial scan. The
directories are monitored for changes and rescanned on the fly.
If you place a file with the filename ending .force-rescan in your library,
you can trigger a full rescan of your library. This will clear all music and
playlists from forked-daapd's database and initiate a fresh bulk scan. Pairing
and speaker information will be kept. Only use this for troubleshooting, it is
not necessary during normal operation.
Symlinks are supported and dereferenced. This does interact in tricky ways
with the above monitoring and rescanning, so you've been warned. Changes to
symlinks themselves won't be taken into account, or not the way you'd expect.

View File

@ -44,6 +44,14 @@ library {
# a single name which will be used for all music in the compilation dir
compilation_artist = "Various artists"
# There are 5 default playlists: "Library", "Music", "Movies", "TV Shows"
# and "Podcasts". Here you can change the names of these playlists.
# name_library = "Library"
# name_music = "Music"
# name_movies = "Movies"
# name_tvshows = "TV Shows"
# name_podcasts = "Podcasts"
# Artwork file names (without file type extension)
# forked-daapd will look for jpg and png files with these base names
# artwork_basenames = { "artwork", "cover", "Folder" }

View File

@ -63,6 +63,11 @@ static cfg_opt_t sec_library[] =
CFG_STR_LIST("podcasts", NULL, CFGF_NONE),
CFG_STR_LIST("compilations", NULL, CFGF_NONE),
CFG_STR("compilation_artist", NULL, CFGF_NONE),
CFG_STR("name_library", "Library", CFGF_NONE),
CFG_STR("name_music", "Music", CFGF_NONE),
CFG_STR("name_movies", "Movies", CFGF_NONE),
CFG_STR("name_tvshows", "TV Shows", CFGF_NONE),
CFG_STR("name_podcasts", "Podcasts", CFGF_NONE),
CFG_STR_LIST("artwork_basenames", "{artwork,cover,Folder}", CFGF_NONE),
CFG_STR_LIST("filetypes_ignore", "{.db,.ini}", CFGF_NONE),
CFG_BOOL("itunes_overrides", cfg_false, CFGF_NONE),

145
src/db.c
View File

@ -588,6 +588,55 @@ db_analyze(void)
}
}
/* Set names of default playlists according to config */
static void
db_set_cfg_names(void)
{
#define Q_TMPL "UPDATE playlists SET title = '%q' WHERE type = 1 AND special_id = %d;"
char *cfg_item[5] = { "name_library", "name_music", "name_movies", "name_tvshows", "name_podcasts" };
char special_id[5] = { 0, 6, 4, 5, 1 };
cfg_t *lib;
char *query;
char *title;
char *errmsg;
int ret;
int i;
lib = cfg_getsec(cfg, "library");
for (i = 0; i < (sizeof(cfg_item) / sizeof(cfg_item[0])); i++)
{
title = cfg_getstr(lib, cfg_item[i]);
if (!title)
{
DPRINTF(E_LOG, L_DB, "Internal error, unknown config item '%s'\n", cfg_item[i]);
continue;
}
query = sqlite3_mprintf(Q_TMPL, title, special_id[i]);
if (!query)
{
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
return;
}
ret = db_exec(query, &errmsg);
if (ret != SQLITE_OK)
{
DPRINTF(E_LOG, L_DB, "Error setting playlist title, query %s, error: %s\n", query, errmsg);
sqlite3_free(errmsg);
}
else
DPRINTF(E_DBG, L_DB, "Playlist title for config item '%s' set with query '%s'\n", cfg_item[i], query);
sqlite3_free(query);
}
#undef Q_TMPL
}
void
db_hook_post_scan(void)
{
@ -651,6 +700,36 @@ db_purge_cruft(time_t ref)
}
void
db_purge_all(void)
{
char *queries[4] =
{
"DELETE FROM inotify;",
"DELETE FROM playlistitems;",
"DELETE FROM playlists WHERE type <> 1;",
"DELETE FROM files;"
};
char *errmsg;
int i;
int ret;
for (i = 0; i < (sizeof(queries) / sizeof(queries[0])); i++)
{
DPRINTF(E_DBG, L_DB, "Running purge query '%s'\n", queries[i]);
ret = db_exec(queries[i], &errmsg);
if (ret != SQLITE_OK)
{
DPRINTF(E_LOG, L_DB, "Purge query %d error: %s\n", i, errmsg);
sqlite3_free(errmsg);
}
else
DPRINTF(E_DBG, L_DB, "Purged %d rows\n", sqlite3_changes(hdl));
}
}
static int
db_get_count(char *query)
{
@ -1566,12 +1645,13 @@ db_files_get_count(void)
}
int
db_files_get_count_bypathpattern(char *path)
db_files_get_count_bymatch(char *path)
{
#define Q_TMPL "SELECT COUNT(*) FROM files f WHERE f.path LIKE '%%%q';"
char *query;
int count;
query = sqlite3_mprintf("SELECT COUNT(*) FROM files f WHERE f.path LIKE '%%%q';", path);
query = sqlite3_mprintf(Q_TMPL, path);
if (!query)
{
DPRINTF(E_LOG, L_DB, "Out of memory making count query string.\n");
@ -1583,6 +1663,7 @@ db_files_get_count_bypathpattern(char *path)
sqlite3_free(query);
return count;
#undef Q_TMPL
}
void
@ -1659,6 +1740,34 @@ db_file_ping(int id)
#undef Q_TMPL
}
void
db_file_ping_bymatch(char *path)
{
#define Q_TMPL "UPDATE files SET db_timestamp = %" PRIi64 " WHERE path LIKE '%q/%%';"
char *query;
char *errmsg;
int ret;
query = sqlite3_mprintf(Q_TMPL, (int64_t)time(NULL), path);
if (!query)
{
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
return;
}
DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query);
ret = db_exec(query, &errmsg);
if (ret != SQLITE_OK)
DPRINTF(E_LOG, L_DB, "Error pinging files matching %s: %s\n", path, errmsg);
sqlite3_free(errmsg);
sqlite3_free(query);
#undef Q_TMPL
}
char *
db_file_path_byid(int id)
{
@ -1785,7 +1894,7 @@ db_file_id_bypath(char *path)
}
int
db_file_id_bypathpattern(char *path)
db_file_id_bymatch(char *path)
{
#define Q_TMPL "SELECT f.id FROM files f WHERE f.path LIKE '%%%q';"
char *query;
@ -2441,6 +2550,34 @@ db_pl_ping(int id)
#undef Q_TMPL
}
void
db_pl_ping_bymatch(char *path)
{
#define Q_TMPL "UPDATE playlists SET db_timestamp = %" PRIi64 " WHERE path LIKE '%q/%%';"
char *query;
char *errmsg;
int ret;
query = sqlite3_mprintf(Q_TMPL, (int64_t)time(NULL), path);
if (!query)
{
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
return;
}
DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query);
ret = db_exec(query, &errmsg);
if (ret != SQLITE_OK)
DPRINTF(E_LOG, L_DB, "Error pinging playlists matching %s: %s\n", path, errmsg);
sqlite3_free(errmsg);
sqlite3_free(query);
#undef Q_TMPL
}
static int
db_pl_id_bypath(char *path, int *id)
{
@ -4959,6 +5096,8 @@ db_init(void)
db_analyze();
db_set_cfg_names();
files = db_files_get_count();
pls = db_pl_get_count();

View File

@ -295,6 +295,9 @@ db_hook_post_scan(void);
void
db_purge_cruft(time_t ref);
void
db_purge_all(void);
/* Queries */
int
db_query_start(struct query_params *qp);
@ -322,7 +325,7 @@ int
db_files_get_count(void);
int
db_files_get_count_bypathpattern(char *path);
db_files_get_count_bymatch(char *path);
void
db_files_update_songalbumid(void);
@ -333,6 +336,9 @@ db_file_inc_playcount(int id);
void
db_file_ping(int id);
void
db_file_ping_bymatch(char *path);
char *
db_file_path_byid(int id);
@ -340,7 +346,7 @@ int
db_file_id_bypath(char *path);
int
db_file_id_bypathpattern(char *path);
db_file_id_bymatch(char *path);
int
db_file_id_byfilebase(char *filename, char *base);
@ -382,6 +388,9 @@ db_pl_get_count(void);
void
db_pl_ping(int id);
void
db_pl_ping_bymatch(char *path);
struct playlist_info *
db_pl_fetch_bypath(char *path);

View File

@ -90,6 +90,9 @@ static pthread_t tid_scan;
static struct deferred_pl *playlists;
static struct stacked_dir *dirstack;
/* Forward */
static void
bulk_scan(void);
static int
push_dir(struct stacked_dir **s, char *path)
@ -588,6 +591,19 @@ process_file(char *file, time_t mtime, off_t size, int type, int flags)
return;
}
else if (strcmp(ext, ".force-rescan") == 0)
{
if (flags & F_SCAN_BULK)
return;
else
{
DPRINTF(E_LOG, L_SCAN, "Forcing full rescan, found force-rescan file: %s\n", file);
db_purge_all();
bulk_scan();
return;
}
}
}
/* Not any kind of special file, so let's see if it's a media file */
@ -859,6 +875,13 @@ bulk_scan(void)
{
DPRINTF(E_LOG, L_SCAN, "Skipping library directory %s, could not dereference: %s\n", path, strerror(errno));
/* Assume dir is mistakenly not mounted, so just disable everything and update timestamps */
db_file_disable_bymatch(path, "", 0);
db_pl_disable_bymatch(path, "", 0);
db_file_ping_bymatch(path);
db_pl_ping_bymatch(path);
continue;
}

View File

@ -301,13 +301,13 @@ find_track_file(char *location)
entry = location;
DPRINTF(E_SPAM, L_SCAN, "iTunes XML playlist entry is now %s\n", entry);
ret = db_files_get_count_bypathpattern(entry);
ret = db_files_get_count_bymatch(entry);
} while (ptr && (ret > 1));
if (ret > 0)
{
mfi_id = db_file_id_bypathpattern(entry);
mfi_id = db_file_id_bymatch(entry);
DPRINTF(E_DBG, L_SCAN, "Found iTunes XML playlist entry match, id is %d, entry is %s\n", mfi_id, entry);
free(location);

View File

@ -242,13 +242,13 @@ scan_m3u_playlist(char *file, time_t mtime)
entry = buf;
DPRINTF(E_SPAM, L_SCAN, "Playlist entry is now %s\n", entry);
ret = db_files_get_count_bypathpattern(entry);
ret = db_files_get_count_bymatch(entry);
} while (ptr && (ret > 1));
if (ret > 0)
{
mfi_id = db_file_id_bypathpattern(entry);
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);