From 8f311d436069db2ead849ff713f17641d302d07c Mon Sep 17 00:00:00 2001 From: whatdoineed2do/Ray Date: Sat, 8 Jun 2019 15:10:55 +0100 Subject: [PATCH] [library] support forced metadata scan of library, via '.meta-rescan' file --- README.md | 3 +++ src/library.c | 55 +++++++++++++++++++++++++++++++++++++++ src/library.h | 8 ++++++ src/library/filescanner.c | 42 +++++++++++++++++++++++++++--- 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 51389832..0b21c1ba 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,9 @@ Now you can make a cron job that runs this command: When forked-daapd detects a file with filename ending .init-rescan it will perform a bulk scan similar to the startup scan. +Alternatively, you can force a metadata scan of the library even if the +files have not changed by creating a filename ending `.meta-rescan`. + ### Troubleshooting library issues diff --git a/src/library.c b/src/library.c index de038c34..825919e0 100644 --- a/src/library.c +++ b/src/library.c @@ -294,6 +294,49 @@ rescan(void *arg, int *ret) return COMMAND_END; } +static enum command_state +metarescan(void *arg, int *ret) +{ + time_t starttime; + time_t endtime; + int i; + + DPRINTF(E_LOG, L_LIB, "Library meta rescan triggered\n"); + listener_notify(LISTENER_UPDATE); + starttime = time(NULL); + + for (i = 0; sources[i]; i++) + { + if (!sources[i]->disabled && sources[i]->metarescan) + { + DPRINTF(E_INFO, L_LIB, "Meta rescan library source '%s'\n", sources[i]->name); + sources[i]->metarescan(); + } + else + { + DPRINTF(E_INFO, L_LIB, "Library source '%s' is disabled\n", sources[i]->name); + } + } + + purge_cruft(starttime); + + DPRINTF(E_DBG, L_LIB, "Running post library scan jobs\n"); + db_hook_post_scan(); + + endtime = time(NULL); + DPRINTF(E_LOG, L_LIB, "Library meta 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 fullrescan(void *arg, int *ret) { @@ -382,6 +425,18 @@ library_rescan() commands_exec_async(cmdbase, rescan, NULL); } +void +library_metarescan() +{ + if (scanning) + { + DPRINTF(E_INFO, L_LIB, "Scan already running, ignoring request to trigger metadata scan\n"); + return; + } + + scanning = true; // TODO Guard "scanning" with a mutex + commands_exec_async(cmdbase, metarescan, NULL); +} void library_fullrescan() { diff --git a/src/library.h b/src/library.h index 4359b71d..1d2bd8f3 100644 --- a/src/library.h +++ b/src/library.h @@ -61,6 +61,11 @@ struct library_source */ int (*rescan)(void); + /* + * Run a metadata rescan of library even if files not changed (called from the library thread) + */ + int (*metarescan)(void); + /* * Run a full rescan (purge library entries and rescan) (called from the library thread) */ @@ -99,6 +104,9 @@ library_queue_add(const char *path, int position, int *count, int *new_item_id); void library_rescan(); +void +library_metarescan(); + void library_fullrescan(); diff --git a/src/library/filescanner.c b/src/library/filescanner.c index 56896fb4..50c41dc3 100644 --- a/src/library/filescanner.c +++ b/src/library/filescanner.c @@ -76,6 +76,7 @@ #define F_SCAN_RESCAN (1 << 1) #define F_SCAN_FAST (1 << 2) #define F_SCAN_MOVED (1 << 3) +#define F_SCAN_METARESCAN (1 << 4) #define F_SCAN_TYPE_FILE (1 << 0) #define F_SCAN_TYPE_PODCAST (1 << 1) @@ -96,6 +97,7 @@ enum file_type { FILE_CTRL_LASTFM, FILE_CTRL_SPOTIFY, FILE_CTRL_INITSCAN, + FILE_CTRL_METASCAN, // forced scan for meta, preserves existing db records FILE_CTRL_FULLSCAN, }; @@ -366,6 +368,9 @@ file_type_get(const char *path) { if (strcasecmp(ext, ".init-rescan") == 0) return FILE_CTRL_INITSCAN; + if (strcasecmp(ext, ".meta-rescan") == 0) + return FILE_CTRL_METASCAN; + if (strcasecmp(ext, ".full-rescan") == 0) return FILE_CTRL_FULLSCAN; @@ -477,9 +482,12 @@ process_regular_file(const char *file, struct stat *sb, int type, int flags, int // Will return 0 if file is not in library or if file mtime is newer than library timestamp // - note if mtime is 0 then we always scan the file - ret = db_file_ping_bypath(file, sb->st_mtime); - if ((sb->st_mtime != 0) && (ret != 0)) - return; + if (!(flags & F_SCAN_METARESCAN)) + { + ret = db_file_ping_bypath(file, sb->st_mtime); + if ((sb->st_mtime != 0) && (ret != 0)) + return; + } // File is new or modified - (re)scan metadata and update file in library memset(&mfi, 0, sizeof(struct media_file_info)); @@ -624,6 +632,15 @@ process_file(char *file, struct stat *sb, int type, int flags, int dir_id) library_rescan(); break; + case FILE_CTRL_METASCAN: + if (flags & F_SCAN_BULK) + break; + + DPRINTF(E_LOG, L_SCAN, "Meta rescan triggered, found meta-rescan file: %s\n", file); + + library_metarescan(); + break; + case FILE_CTRL_FULLSCAN: if (flags & F_SCAN_BULK) break; @@ -1630,6 +1647,24 @@ filescanner_rescan() return 0; } +static int +filescanner_metarescan() +{ + DPRINTF(E_LOG, L_SCAN, "meta rescan triggered\n"); + + inofd_event_unset(); // Clears all inotify watches + db_watch_clear(); + inofd_event_set(); + bulk_scan(F_SCAN_BULK | F_SCAN_METARESCAN); + + if (!library_is_exiting()) + { + /* Enable inotify */ + event_add(inoev, NULL); + } + return 0; +} + static int filescanner_fullrescan() { @@ -2133,6 +2168,7 @@ struct library_source filescanner = .deinit = filescanner_deinit, .initscan = filescanner_initscan, .rescan = filescanner_rescan, + .metarescan = filescanner_metarescan, .fullrescan = filescanner_fullrescan, .playlist_add = playlist_add, .playlist_remove = playlist_remove,