diff --git a/src/library.c b/src/library.c index 4d4f8428..7d3a20c2 100644 --- a/src/library.c +++ b/src/library.c @@ -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) { @@ -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) { diff --git a/src/library.h b/src/library.h index 5201d951..247bf578 100644 --- a/src/library.h +++ b/src/library.h @@ -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 */ @@ -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);