Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement partial library scan (filescanner) #1179

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -3056,6 +3056,18 @@ db_file_ping_bymatch(const char *path, int isdir)
#undef Q_TMPL_NODIR
}

void
db_file_ping_excl_bymatch(const char *path)
{
#define Q_TMPL_DIR "UPDATE files SET db_timestamp = %" PRIi64 " WHERE path NOT LIKE '%q/%%';"
char *query;

query = sqlite3_mprintf(Q_TMPL_DIR, (int64_t)time(NULL), path);
db_query_run(query, 1, 0);

#undef Q_TMPL_DIR
}

char *
db_file_path_byid(int id)
{
Expand Down Expand Up @@ -3639,6 +3651,18 @@ db_pl_ping_bymatch(const char *path, int isdir)
#undef Q_TMPL_NODIR
}

void
db_pl_ping_excl_bymatch(const char *path)
{
#define Q_TMPL_DIR "UPDATE playlists SET db_timestamp = %" PRIi64 " WHERE path NOT LIKE '%q/%%';"
char *query;

query = sqlite3_mprintf(Q_TMPL_DIR, (int64_t)time(NULL), path);
db_query_run(query, 1, 0);

#undef Q_TMPL_DIR
}

void
db_pl_ping_items_bymatch(const char *path, int id)
{
Expand Down Expand Up @@ -4453,6 +4477,18 @@ db_directory_ping_bymatch(char *virtual_path)
#undef Q_TMPL_DIR
}

void
db_directory_ping_excl_bymatch(const char *virtual_path)
{
#define Q_TMPL_DIR "UPDATE directories SET db_timestamp = %" PRIi64 " WHERE virtual_path <> '%q' OR virtual_path NOT LIKE '%q/%%';"
char *query;

query = sqlite3_mprintf(Q_TMPL_DIR, (int64_t)time(NULL), virtual_path, virtual_path);

db_query_run(query, 1, 0);
#undef Q_TMPL_DIR
}

void
db_directory_disable_bymatch(const char *path, enum strip_type strip, uint32_t cookie)
{
Expand Down Expand Up @@ -6494,8 +6530,8 @@ db_watch_cookie_known(uint32_t cookie)
int
db_watch_enum_start(struct watch_enum *we)
{
#define Q_MATCH_TMPL "SELECT wd FROM inotify WHERE path LIKE '%q/%%';"
#define Q_COOKIE_TMPL "SELECT wd FROM inotify WHERE cookie = %" PRIi64 ";"
#define Q_MATCH_TMPL "SELECT wd,path FROM inotify WHERE path LIKE '%q/%%';"
#define Q_COOKIE_TMPL "SELECT wd,path FROM inotify WHERE cookie = %" PRIi64 ";"
sqlite3_stmt *stmt;
char *query;
int ret;
Expand Down Expand Up @@ -6580,6 +6616,40 @@ db_watch_enum_fetchwd(struct watch_enum *we, uint32_t *wd)
return 0;
}

int
db_watch_enum_fetch(struct watch_enum *we, struct watch_info *wi)
{
int ret;

wi->wd = 0;
wi->cookie = 0;

if (!we->stmt)
{
DPRINTF(E_LOG, L_DB, "Watch enum not started!\n");
return -1;
}

ret = db_blocking_step(we->stmt);
if (ret == SQLITE_DONE)
{
DPRINTF(E_INFO, L_DB, "End of watch 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;
}

wi->wd = (uint32_t)sqlite3_column_int(we->stmt, 0);
if (wi->path)
snprintf(wi->path, PATH_MAX, (char*)sqlite3_column_text(we->stmt, 1));

return 0;
}



#ifdef DB_PROFILE
static int
Expand Down
14 changes: 13 additions & 1 deletion src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,9 @@ db_file_ping_bypath(const char *path, time_t mtime_max);
void
db_file_ping_bymatch(const char *path, int isdir);

void
db_file_ping_excl_bymatch(const char *path);

char *
db_file_path_byid(int id);

Expand Down Expand Up @@ -746,6 +749,9 @@ db_pl_ping(int id);
void
db_pl_ping_bymatch(const char *path, int isdir);

void
db_pl_ping_excl_bymatch(const char *path);

void
db_pl_ping_items_bymatch(const char *path, int id);

Expand Down Expand Up @@ -827,6 +833,9 @@ db_directory_update(struct directory_info *di);
void
db_directory_ping_bymatch(char *virtual_path);

void
db_directory_ping_excl_bymatch(const char *virtual_path);

void
db_directory_disable_bymatch(const char *path, enum strip_type strip, uint32_t cookie);

Expand Down Expand Up @@ -1017,7 +1026,10 @@ int
db_watch_enum_fetchwd(struct watch_enum *we, uint32_t *wd);

int
db_backup(void);
db_watch_enum_fetch(struct watch_enum *we, struct watch_info *wi);

int
db_backup();

int
db_perthread_init(void);
Expand Down
8 changes: 7 additions & 1 deletion src/httpd_jsonapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1230,10 +1230,16 @@ static int
jsonapi_reply_update(struct httpd_request *hreq)
{
const char *param;
const char *param_path;

param = httpd_query_value_find(hreq->query, "scan_kind");
param_path = httpd_query_value_find(hreq->query, "path");

if (param_path)
library_rescan_path(param_path);
else
library_rescan(db_scan_kind_enum(param));

library_rescan(db_scan_kind_enum(param));
return HTTP_NOCONTENT;
}

Expand Down
88 changes: 88 additions & 0 deletions src/library.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,81 @@ rescan(void *arg, int *ret)
return COMMAND_END;
}

static enum command_state
rescan_path(void *arg, int *ret)
{
time_t starttime;
time_t endtime;
int i;
char *path = (char *)arg;
char virtual_path[PATH_MAX];
struct stat st;
int ret1;
char *ptr;

// drop any trailing '/' on path
ptr = path + strlen(path)-1;
while (ptr > path)
{
if (*ptr != '/')
break;

*ptr = '\0';
--ptr;
}

listener_notify(LISTENER_UPDATE);
starttime = time(NULL);

DPRINTF(E_LOG, L_LIB, "Library partial rescan triggered: '%s'\n", path);
ret1 = lstat(path, &st);
if (ret1 < 0 || (ret1 == 0 && (st.st_mode & S_IFMT) != S_IFDIR))
{
DPRINTF(E_LOG, L_LIB, "Partial rescan on '%s' is not a valid directory\n", path);
goto out;
}

// protecting everything else other than the request path
db_file_ping_excl_bymatch(path);
db_pl_ping_excl_bymatch(path);
ret1 = snprintf(virtual_path, sizeof(virtual_path), "/file:%s", path);
if ((ret1 < 0) || (ret1 >= sizeof(virtual_path)))
DPRINTF(E_LOG, L_SCAN, "Virtual path exceeds PATH_MAX (/file:%s)\n", path);
else
db_directory_ping_excl_bymatch(virtual_path);

for (i = 0; sources[i]; i++)
{
if (!sources[i]->disabled && sources[i]->rescan_path && sources[i]->scan_kind == SCAN_KIND_FILES)
{
DPRINTF(E_INFO, L_LIB, "Rescan partial library source '%s'\n", db_scan_kind_label(sources[i]->scan_kind));
sources[i]->rescan_path(path);
}
else
{
DPRINTF(E_INFO, L_LIB, "Library partial source '%s' is disabled\n", db_scan_kind_label(sources[i]->scan_kind));
}
}

purge_cruft(starttime, SCAN_KIND_FILES);

DPRINTF(E_DBG, L_LIB, "Running post library partial scan jobs\n");
db_hook_post_scan();

out:
endtime = time(NULL);
DPRINTF(E_LOG, L_LIB, "Library partial rescan completed in %.f sec (%d changes)\n", difftime(endtime, starttime), deferred_update_notifications);
scanning = false;

if (handle_deferred_update_notifications())
listener_notify(LISTENER_UPDATE | LISTENER_DATABASE);
else
listener_notify(LISTENER_UPDATE);

*ret = 0;
return COMMAND_END;
}

static enum command_state
metarescan(void *arg, int *ret)
{
Expand Down Expand Up @@ -760,6 +835,19 @@ library_rescan(enum scan_kind scan_kind)
commands_exec_async(cmdbase, rescan, param);
}

void
library_rescan_path(const char *path)
{
if (scanning)
{
DPRINTF(E_INFO, L_LIB, "Scan already running, ignoring request to trigger a new init path scan\n");
return;
}

scanning = true; // TODO Guard "scanning" with a mutex
commands_exec_async(cmdbase, rescan_path, (void*)strdup(path));
}

void
library_metarescan(enum scan_kind scan_kind)
{
Expand Down
8 changes: 8 additions & 0 deletions src/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ struct library_source
*/
int (*write_metadata)(struct media_file_info *mfi);

/*
* Run rescan (called from the library thread)
*/
int (*rescan_path)(const char *path);

/*
* Add an item to the library
*/
Expand Down Expand Up @@ -183,6 +188,9 @@ library_rescan(enum scan_kind library_source);
/*
* Same as library_rescan but also updates unmodified tracks and playlists
*/
void
library_rescan_path(const char *path);

void
library_metarescan(enum scan_kind library_source);

Expand Down
Loading
Loading