mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-26 14:13:18 -05:00
Merge branch 'force_rescan'
This commit is contained in:
commit
91ab072e51
6
README
6
README
@ -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.
|
||||
|
@ -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" }
|
||||
|
@ -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
145
src/db.c
@ -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();
|
||||
|
||||
|
13
src/db.h
13
src/db.h
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user