scanner: Add non-destructive update rescan
New functionality, based on Shrimpkin's patch #145 on SF.
This commit is contained in:
parent
5450ac486e
commit
49aa42d893
@ -25,7 +25,7 @@ minidlnad_SOURCES = minidlna.c upnphttp.c upnpdescgen.c upnpsoap.c \
|
||||
upnpreplyparse.c minixml.c clients.c \
|
||||
getifaddr.c process.c upnpglobalvars.c \
|
||||
options.c minissdp.c uuid.c upnpevents.c \
|
||||
sql.c utils.c metadata.c scanner.c inotify.c \
|
||||
sql.c utils.c metadata.c scanner.c monitor.c \
|
||||
tivo_utils.c tivo_beacon.c tivo_commands.c \
|
||||
playlist.c image_utils.c albumart.c log.c \
|
||||
containers.c avahi.c tagutils/tagutils.c
|
||||
|
20
minidlna.c
20
minidlna.c
@ -90,7 +90,7 @@
|
||||
#include "process.h"
|
||||
#include "upnpevents.h"
|
||||
#include "scanner.h"
|
||||
#include "inotify.h"
|
||||
#include "monitor.h"
|
||||
#include "log.h"
|
||||
#include "tivo_beacon.h"
|
||||
#include "tivo_utils.h"
|
||||
@ -328,12 +328,13 @@ check_db(sqlite3 *db, int new_db, pid_t *scanner_pid)
|
||||
if (ret != 0)
|
||||
{
|
||||
rescan:
|
||||
rescan_db = 0;
|
||||
if (ret < 0)
|
||||
DPRINTF(E_WARN, L_GENERAL, "Creating new database at %s/files.db\n", db_path);
|
||||
else if (ret == 1)
|
||||
DPRINTF(E_WARN, L_GENERAL, "New media_dir detected; rescanning...\n");
|
||||
DPRINTF(E_WARN, L_GENERAL, "New media_dir detected; rebuilding...\n");
|
||||
else if (ret == 2)
|
||||
DPRINTF(E_WARN, L_GENERAL, "Removed media_dir detected; rescanning...\n");
|
||||
DPRINTF(E_WARN, L_GENERAL, "Removed media_dir detected; rebuilding...\n");
|
||||
else
|
||||
DPRINTF(E_WARN, L_GENERAL, "Database version mismatch (%d=>%d); need to recreate...\n",
|
||||
ret, DB_VERSION);
|
||||
@ -346,6 +347,9 @@ rescan:
|
||||
open_db(&db);
|
||||
if (CreateDatabase() != 0)
|
||||
DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database! Exiting...\n");
|
||||
}
|
||||
if (ret || rescan_db)
|
||||
{
|
||||
#if USE_FORK
|
||||
scanning = 1;
|
||||
sqlite3_close(db);
|
||||
@ -834,6 +838,9 @@ init(int argc, char **argv)
|
||||
case 'h':
|
||||
runtime_vars.port = -1; // triggers help display
|
||||
break;
|
||||
case 'r':
|
||||
rescan_db = 1;
|
||||
break;
|
||||
case 'R':
|
||||
snprintf(buf, sizeof(buf), "rm -rf %s/files.db %s/art_cache", db_path, db_path);
|
||||
if (system(buf) != 0)
|
||||
@ -880,9 +887,9 @@ init(int argc, char **argv)
|
||||
"\t\t[-t notify_interval] [-P pid_filename]\n"
|
||||
"\t\t[-s serial] [-m model_number]\n"
|
||||
#ifdef __linux__
|
||||
"\t\t[-w url] [-R] [-L] [-S] [-V] [-h]\n"
|
||||
"\t\t[-w url] [-r] [-R] [-L] [-S] [-V] [-h]\n"
|
||||
#else
|
||||
"\t\t[-w url] [-R] [-L] [-V] [-h]\n"
|
||||
"\t\t[-w url] [-r] [-R] [-L] [-V] [-h]\n"
|
||||
#endif
|
||||
"\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n"
|
||||
"\tDefault pid file is %s.\n"
|
||||
@ -890,7 +897,8 @@ init(int argc, char **argv)
|
||||
"\t-w sets the presentation url. Default is http address on port 80\n"
|
||||
"\t-v enables verbose output\n"
|
||||
"\t-h displays this text\n"
|
||||
"\t-R forces a full rescan\n"
|
||||
"\t-r forces a rescan\n"
|
||||
"\t-R forces a rebuild\n"
|
||||
"\t-L do not create playlists\n"
|
||||
#ifdef __linux__
|
||||
"\t-S changes behaviour for systemd\n"
|
||||
|
@ -17,7 +17,6 @@
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@ -30,6 +29,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#ifdef HAVE_INOTIFY
|
||||
#include <sys/resource.h>
|
||||
#include <poll.h>
|
||||
#ifdef HAVE_SYS_INOTIFY_H
|
||||
@ -38,10 +38,11 @@
|
||||
#include "linux/inotify.h"
|
||||
#include "linux/inotify-syscalls.h"
|
||||
#endif
|
||||
#endif
|
||||
#include "libav.h"
|
||||
|
||||
#include "upnpglobalvars.h"
|
||||
#include "inotify.h"
|
||||
#include "monitor.h"
|
||||
#include "utils.h"
|
||||
#include "sql.h"
|
||||
#include "scanner.h"
|
||||
@ -50,6 +51,9 @@
|
||||
#include "playlist.h"
|
||||
#include "log.h"
|
||||
|
||||
static time_t next_pl_fill = 0;
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
#define EVENT_SIZE ( sizeof (struct inotify_event) )
|
||||
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
|
||||
#define DESIRED_WATCH_LIMIT 65536
|
||||
@ -65,7 +69,6 @@ struct watch
|
||||
|
||||
static struct watch *watches;
|
||||
static struct watch *lastwatch = NULL;
|
||||
static time_t next_pl_fill = 0;
|
||||
|
||||
static char *
|
||||
get_path_from_wd(int wd)
|
||||
@ -244,9 +247,10 @@ inotify_remove_watches(int fd)
|
||||
|
||||
return rm_watches;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
inotify_remove_file(const char * path)
|
||||
int
|
||||
monitor_remove_file(const char * path)
|
||||
{
|
||||
char sql[128];
|
||||
char art_cache[PATH_MAX];
|
||||
@ -323,8 +327,8 @@ inotify_remove_file(const char * path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
inotify_insert_file(char * name, const char * path)
|
||||
int
|
||||
monitor_insert_file(char * name, const char * path)
|
||||
{
|
||||
int len;
|
||||
char * last_dir;
|
||||
@ -406,14 +410,24 @@ inotify_insert_file(char * name, const char * path)
|
||||
if( !ts && is_playlist(path) && (sql_get_int_field(db, "SELECT ID from PLAYLISTS where PATH = '%q'", path) > 0) )
|
||||
{
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "Re-reading modified playlist (%s).\n", path);
|
||||
inotify_remove_file(path);
|
||||
monitor_remove_file(path);
|
||||
next_pl_fill = 1;
|
||||
}
|
||||
else if( ts < st.st_mtime )
|
||||
else if( !ts )
|
||||
{
|
||||
if( ts > 0 )
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "%s is newer than the last db entry.\n", path);
|
||||
inotify_remove_file(path);
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "Adding: %s\n", path);
|
||||
}
|
||||
else if( ts != st.st_mtime )
|
||||
{
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "%s is %s than the last db entry.\n",
|
||||
path, (ts < st.st_mtime) ? "older" : "newer");
|
||||
monitor_remove_file(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( ts == st.st_mtime )
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "%s already exists\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the parentID. If it's not found, create all necessary parents. */
|
||||
@ -478,14 +492,13 @@ inotify_insert_file(char * name, const char * path)
|
||||
return depth;
|
||||
}
|
||||
|
||||
static int
|
||||
inotify_insert_directory(int fd, char *name, const char * path)
|
||||
int
|
||||
monitor_insert_directory(int fd, char *name, const char * path)
|
||||
{
|
||||
DIR * ds;
|
||||
struct dirent * e;
|
||||
char *id, *parent_buf, *esc_name;
|
||||
char path_buf[PATH_MAX];
|
||||
int wd;
|
||||
enum file_types type = TYPE_UNKNOWN;
|
||||
media_types dir_types = ALL_MEDIA;
|
||||
struct media_dir_s* media_path;
|
||||
@ -498,20 +511,25 @@ inotify_insert_directory(int fd, char *name, const char * path)
|
||||
}
|
||||
if( sql_get_int_field(db, "SELECT ID from DETAILS where PATH = '%q'", path) > 0 )
|
||||
{
|
||||
fd = 0;
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "%s already exists\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
parent_buf = strdup(path);
|
||||
id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
||||
" where d.PATH = '%q' and REF_ID is NULL", dirname(parent_buf));
|
||||
" WHERE d.PATH = '%q' and REF_ID is NULL", dirname(parent_buf));
|
||||
if( !id )
|
||||
id = sqlite3_mprintf("%s", BROWSEDIR_ID);
|
||||
insert_directory(name, path, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id));
|
||||
sqlite3_free(id);
|
||||
free(parent_buf);
|
||||
}
|
||||
|
||||
wd = add_watch(fd, path);
|
||||
if( fd > 0 )
|
||||
{
|
||||
#ifdef HAVE_INOTIFY
|
||||
int wd = add_watch(fd, path);
|
||||
if( wd == -1 )
|
||||
{
|
||||
DPRINTF(E_ERROR, L_INOTIFY, "add_watch() failed\n");
|
||||
@ -520,6 +538,8 @@ inotify_insert_directory(int fd, char *name, const char * path)
|
||||
{
|
||||
DPRINTF(E_INFO, L_INOTIFY, "Added watch to %s [%d]\n", path, wd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
media_path = media_dirs;
|
||||
while( media_path )
|
||||
@ -556,13 +576,13 @@ inotify_insert_directory(int fd, char *name, const char * path)
|
||||
}
|
||||
if( type == TYPE_DIR )
|
||||
{
|
||||
inotify_insert_directory(fd, esc_name, path_buf);
|
||||
monitor_insert_directory(fd, esc_name, path_buf);
|
||||
}
|
||||
else if( type == TYPE_FILE )
|
||||
{
|
||||
if( (stat(path_buf, &st) == 0) && (st.st_blocks<<9 >= st.st_size) )
|
||||
{
|
||||
inotify_insert_file(esc_name, path_buf);
|
||||
monitor_insert_file(esc_name, path_buf);
|
||||
}
|
||||
}
|
||||
free(esc_name);
|
||||
@ -572,8 +592,8 @@ inotify_insert_directory(int fd, char *name, const char * path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
inotify_remove_directory(int fd, const char * path)
|
||||
int
|
||||
monitor_remove_directory(int fd, const char * path)
|
||||
{
|
||||
char * sql;
|
||||
char **result;
|
||||
@ -582,7 +602,12 @@ inotify_remove_directory(int fd, const char * path)
|
||||
|
||||
/* Invalidate the scanner cache so we don't insert files into non-existent containers */
|
||||
valid_cache = 0;
|
||||
if( fd > 0 )
|
||||
{
|
||||
#ifdef HAVE_INOTIFY
|
||||
remove_watch(fd, path);
|
||||
#endif
|
||||
}
|
||||
sql = sqlite3_mprintf("SELECT ID from DETAILS where (PATH > '%q/' and PATH <= '%q/%c')"
|
||||
" or PATH = '%q'", path, path, 0xFF, path);
|
||||
if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) )
|
||||
@ -606,6 +631,7 @@ inotify_remove_directory(int fd, const char * path)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
void *
|
||||
start_inotify(void)
|
||||
{
|
||||
@ -681,7 +707,7 @@ start_inotify(void)
|
||||
{
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n",
|
||||
path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created"));
|
||||
inotify_insert_directory(pollfds[0].fd, esc_name, path_buf);
|
||||
monitor_insert_directory(pollfds[0].fd, esc_name, path_buf);
|
||||
}
|
||||
else if ( (event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE)) &&
|
||||
(lstat(path_buf, &st) == 0) )
|
||||
@ -692,9 +718,9 @@ start_inotify(void)
|
||||
(S_ISLNK(st.st_mode) ? "symbolic" : "hard"),
|
||||
path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created"));
|
||||
if( stat(path_buf, &st) == 0 && S_ISDIR(st.st_mode) )
|
||||
inotify_insert_directory(pollfds[0].fd, esc_name, path_buf);
|
||||
monitor_insert_directory(pollfds[0].fd, esc_name, path_buf);
|
||||
else
|
||||
inotify_insert_file(esc_name, path_buf);
|
||||
monitor_insert_file(esc_name, path_buf);
|
||||
}
|
||||
else if( event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO) && st.st_size > 0 )
|
||||
{
|
||||
@ -703,7 +729,7 @@ start_inotify(void)
|
||||
{
|
||||
DPRINTF(E_DEBUG, L_INOTIFY, "The file %s was %s.\n",
|
||||
path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "changed"));
|
||||
inotify_insert_file(esc_name, path_buf);
|
||||
monitor_insert_file(esc_name, path_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -713,9 +739,9 @@ start_inotify(void)
|
||||
(event->mask & IN_ISDIR ? "directory" : "file"),
|
||||
path_buf, (event->mask & IN_MOVED_FROM ? "moved away" : "deleted"));
|
||||
if ( event->mask & IN_ISDIR )
|
||||
inotify_remove_directory(pollfds[0].fd, path_buf);
|
||||
monitor_remove_directory(pollfds[0].fd, path_buf);
|
||||
else
|
||||
inotify_remove_file(path_buf);
|
||||
monitor_remove_file(path_buf);
|
||||
}
|
||||
free(esc_name);
|
||||
}
|
16
monitor.h
Normal file
16
monitor.h
Normal file
@ -0,0 +1,16 @@
|
||||
int
|
||||
monitor_insert_file(char * name, const char * path);
|
||||
|
||||
int
|
||||
monitor_insert_directory(int fd, char *name, const char * path);
|
||||
|
||||
int
|
||||
monitor_remove_file(const char * path);
|
||||
|
||||
int
|
||||
monitor_remove_directory(int fd, const char * path);
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
void *
|
||||
start_inotify();
|
||||
#endif
|
70
scanner.c
70
scanner.c
@ -46,6 +46,7 @@
|
||||
#include "albumart.h"
|
||||
#include "containers.h"
|
||||
#include "log.h"
|
||||
#include "monitor.h"
|
||||
|
||||
#if SCANDIR_CONST
|
||||
typedef const struct dirent scan_filter;
|
||||
@ -837,6 +838,68 @@ _notify_stop(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* rescan functions added by shrimpkin@sourceforge.net */
|
||||
static int
|
||||
cb_orphans(void *args, int argc, char **argv, char **azColName)
|
||||
{
|
||||
const char *path = argv[0];
|
||||
const char *mime = argv[1];
|
||||
|
||||
/* If we can't access the path, remove it */
|
||||
if (access(path, R_OK) != 0)
|
||||
{
|
||||
DPRINTF(E_DEBUG, L_SCANNER, "Removing %s [%s]\n", path, mime ? "file" : "dir");
|
||||
if (mime)
|
||||
monitor_remove_file(path);
|
||||
else
|
||||
monitor_remove_directory(0, path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
start_rescan(void)
|
||||
{
|
||||
struct media_dir_s *media_path;
|
||||
char *esc_name = NULL;
|
||||
char *zErrMsg;
|
||||
const char *sql_files = "SELECT path, mime FROM details WHERE path NOT NULL AND mime IS NOT NULL;";
|
||||
const char *sql_dir = "SELECT path, mime FROM details WHERE path NOT NULL AND mime IS NULL;";
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_INFO, L_SCANNER, "Starting rescan\n");
|
||||
|
||||
/* Find and remove any dead directory links */
|
||||
ret = sqlite3_exec(db, sql_dir, cb_orphans, NULL, &zErrMsg);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
DPRINTF(E_MAXDEBUG, L_SCANNER, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql_dir);
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
|
||||
/* Find and remove any dead file links */
|
||||
ret = sqlite3_exec(db, sql_files, cb_orphans, NULL, &zErrMsg);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
DPRINTF(E_MAXDEBUG, L_SCANNER, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql_files);
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
|
||||
/* Rescan media_paths for new and/or modified files */
|
||||
for (media_path = media_dirs; media_path != NULL; media_path = media_path->next)
|
||||
{
|
||||
char path[MAXPATHLEN], buf[MAXPATHLEN];
|
||||
strncpyt(path, media_path->path, sizeof(path));
|
||||
strncpyt(buf, media_path->path, sizeof(buf));
|
||||
esc_name = escape_tag(basename(buf), 1);
|
||||
monitor_insert_directory(0, esc_name, path);
|
||||
free(esc_name);
|
||||
}
|
||||
DPRINTF(E_INFO, L_SCANNER, "Rescan completed\n");
|
||||
}
|
||||
/* end rescan functions */
|
||||
|
||||
void
|
||||
start_scanner()
|
||||
{
|
||||
@ -845,12 +908,17 @@ start_scanner()
|
||||
|
||||
if (setpriority(PRIO_PROCESS, 0, 15) == -1)
|
||||
DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce scanner thread priority\n");
|
||||
_notify_start();
|
||||
|
||||
setlocale(LC_COLLATE, "");
|
||||
|
||||
av_register_all();
|
||||
av_log_set_level(AV_LOG_PANIC);
|
||||
if( rescan_db )
|
||||
{
|
||||
start_rescan();
|
||||
return;
|
||||
}
|
||||
_notify_start();
|
||||
for( media_path = media_dirs; media_path != NULL; media_path = media_path->next )
|
||||
{
|
||||
int64_t id;
|
||||
|
@ -89,3 +89,4 @@ short int scanning = 0;
|
||||
volatile short int quitting = 0;
|
||||
volatile uint32_t updateID = 0;
|
||||
const char *force_sort_criteria = NULL;
|
||||
short int rescan_db = 0;
|
||||
|
@ -233,5 +233,6 @@ extern short int scanning;
|
||||
extern volatile short int quitting;
|
||||
extern volatile uint32_t updateID;
|
||||
extern const char *force_sort_criteria;
|
||||
extern short int rescan_db;
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user