From 45d789b64e229b103613e189f72caafaee1d9eff Mon Sep 17 00:00:00 2001 From: Andrew DeMaria Date: Sun, 29 Oct 2017 15:31:17 -0600 Subject: [PATCH] Option to disable timestamps during scan I accidentally deleted most of my music directory. The database was still intact. I recovered the music directory by rolling back to a previous ZFS snapshot and performed a reindex. However, libresonic did not mark the deleted files as present. Turns out the file timestamp was unchanged through the ZFS restore, and so libresonic still thought the last indexing effort was still "good". This adds the option to ignore file timestamps when scanning files. This can be helpful in the case of a restore as described above. There might be a better way to do this, as this was really a quick effort on my part to fix my own libresonic. This does not add a UI, just a single property that can be turned on by editing the lilbresonic.properties file. @fxthomas suggested this could instead be a query parameter on the initial issue #359. That would basically move the potential UI to the scan page. That would be fine, but I could imagine there might be cases where people want this setting on all the time. Signed-off-by: Andrew DeMaria --- .../main/java/org/airsonic/player/dao/AlbumDao.java | 6 +++--- .../main/java/org/airsonic/player/dao/ArtistDao.java | 8 ++++---- .../java/org/airsonic/player/dao/MediaFileDao.java | 9 +++++---- .../org/airsonic/player/service/MediaFileService.java | 6 +++++- .../org/airsonic/player/service/SettingsService.java | 10 ++++++++++ 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/AlbumDao.java b/airsonic-main/src/main/java/org/airsonic/player/dao/AlbumDao.java index a4b9ba6c..e3f00892 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/dao/AlbumDao.java +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/AlbumDao.java @@ -334,12 +334,12 @@ public class AlbumDao extends AbstractDao { } public void markNonPresent(Date lastScanned) { - int minId = queryForInt("select min(id) from album where last_scanned != ? and present", 0, lastScanned); - int maxId = queryForInt("select max(id) from album where last_scanned != ? and present", 0, lastScanned); + int minId = queryForInt("select min(id) from album where last_scanned < ? and present", 0, lastScanned); + int maxId = queryForInt("select max(id) from album where last_scanned < ? and present", 0, lastScanned); final int batchSize = 1000; for (int id = minId; id <= maxId; id += batchSize) { - update("update album set present=false where id between ? and ? and last_scanned != ? and present", id, id + batchSize, lastScanned); + update("update album set present=false where id between ? and ? and last_scanned < ? and present", id, id + batchSize, lastScanned); } } diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/ArtistDao.java b/airsonic-main/src/main/java/org/airsonic/player/dao/ArtistDao.java index 8b607062..a45d0480 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/dao/ArtistDao.java +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/ArtistDao.java @@ -156,16 +156,16 @@ public class ArtistDao extends AbstractDao { } public void markPresent(String artistName, Date lastScanned) { - update("update artist set present=?, last_scanned=? where name=?", true, lastScanned, artistName); + update("update artist set present=?, last_scanned = ? where name=?", true, lastScanned, artistName); } public void markNonPresent(Date lastScanned) { - int minId = queryForInt("select min(id) from artist where last_scanned != ? and present", 0, lastScanned); - int maxId = queryForInt("select max(id) from artist where last_scanned != ? and present", 0, lastScanned); + int minId = queryForInt("select min(id) from artist where last_scanned < ? and present", 0, lastScanned); + int maxId = queryForInt("select max(id) from artist where last_scanned < ? and present", 0, lastScanned); final int batchSize = 1000; for (int id = minId; id <= maxId; id += batchSize) { - update("update artist set present=false where id between ? and ? and last_scanned != ? and present", id, id + batchSize, lastScanned); + update("update artist set present=false where id between ? and ? and last_scanned < ? and present", id, id + batchSize, lastScanned); } } diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/MediaFileDao.java b/airsonic-main/src/main/java/org/airsonic/player/dao/MediaFileDao.java index ed9a91e3..ec85de9a 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/dao/MediaFileDao.java +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/MediaFileDao.java @@ -646,17 +646,18 @@ public class MediaFileDao extends AbstractDao { } public void markPresent(String path, Date lastScanned) { - update("update media_file set present=?, last_scanned=? where path=?", true, lastScanned, path); + update("update media_file set present=?, last_scanned = ? where path=?", true, lastScanned, path); } public void markNonPresent(Date lastScanned) { - int minId = queryForInt("select min(id) from media_file where last_scanned != ? and present", 0, lastScanned); - int maxId = queryForInt("select max(id) from media_file where last_scanned != ? and present", 0, lastScanned); + int minId = queryForInt("select min(id) from media_file where last_scanned < ? and present", 0, lastScanned); + int maxId = queryForInt("select max(id) from media_file where last_scanned < ? and present", 0, lastScanned); final int batchSize = 1000; Date childrenLastUpdated = new Date(0L); // Used to force a children rescan if file is later resurrected. for (int id = minId; id <= maxId; id += batchSize) { - update("update media_file set present=false, children_last_updated=? where id between ? and ? and last_scanned != ? and present", + update("update media_file set present=false, children_last_updated=? where id between ? and ? and " + + "last_scanned < ? and present", childrenLastUpdated, id, id + batchSize, lastScanned); } } diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java b/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java index 1298fabb..0984fe55 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java @@ -151,9 +151,13 @@ public class MediaFileService { } private MediaFile checkLastModified(MediaFile mediaFile, boolean useFastCache) { - if (useFastCache || (mediaFile.getVersion() >= MediaFileDao.VERSION && mediaFile.getChanged().getTime() >= FileUtil.lastModified(mediaFile.getFile()))) { + if (useFastCache || (mediaFile.getVersion() >= MediaFileDao.VERSION + && !settingsService.isIgnoreFileTimestamps() + && mediaFile.getChanged().getTime() >= FileUtil.lastModified(mediaFile.getFile()))) { + LOG.debug("Detected unmodified file"); return mediaFile; } + LOG.debug("Updating database file from disk"); mediaFile = createMediaFile(mediaFile.getFile()); mediaFileDao.createOrUpdateMediaFile(mediaFile); return mediaFile; diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java b/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java index 97933905..5ee92d33 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/SettingsService.java @@ -77,6 +77,7 @@ public class SettingsService { private static final String KEY_INDEX_CREATION_INTERVAL = "IndexCreationInterval"; private static final String KEY_INDEX_CREATION_HOUR = "IndexCreationHour"; private static final String KEY_FAST_CACHE_ENABLED = "FastCacheEnabled"; + private static final String KEY_IGNORE_FILE_TIMESTAMPS = "IgnoreFileTimestamps"; private static final String KEY_PODCAST_UPDATE_INTERVAL = "PodcastUpdateInterval"; private static final String KEY_PODCAST_FOLDER = "PodcastFolder"; private static final String KEY_PODCAST_EPISODE_RETENTION_COUNT = "PodcastEpisodeRetentionCount"; @@ -160,6 +161,7 @@ public class SettingsService { private static final int DEFAULT_INDEX_CREATION_INTERVAL = 1; private static final int DEFAULT_INDEX_CREATION_HOUR = 3; private static final boolean DEFAULT_FAST_CACHE_ENABLED = false; + private static final boolean DEFAULT_IGNORE_FILE_TIMESTAMPS = false; private static final int DEFAULT_PODCAST_UPDATE_INTERVAL = 24; private static final String DEFAULT_PODCAST_FOLDER = Util.getDefaultPodcastFolder(); private static final int DEFAULT_PODCAST_EPISODE_RETENTION_COUNT = 10; @@ -537,6 +539,14 @@ public class SettingsService { setBoolean(KEY_FAST_CACHE_ENABLED, enabled); } + public boolean isIgnoreFileTimestamps() { + return getBoolean(KEY_IGNORE_FILE_TIMESTAMPS, DEFAULT_IGNORE_FILE_TIMESTAMPS); + } + + public void setIgnoreFileTimestamps(boolean ignore) { + setBoolean(KEY_IGNORE_FILE_TIMESTAMPS, ignore); + } + /** * Returns the number of hours between Podcast updates, of -1 if automatic updates * are disabled.