No need to full rescan when changing some settings

This commit is contained in:
emeric
2025-04-07 22:48:10 +02:00
parent 15c5f0b89b
commit 75cbbde598
41 changed files with 331 additions and 163 deletions
+106 -61
View File
@@ -28,6 +28,7 @@
#include "core/ITraceLogger.hpp"
#include "database/MediaLibrary.hpp"
#include "database/ScanSettings.hpp"
#include "database/Session.hpp"
#include "scanners/ArtistInfoFileScanner.hpp"
#include "scanners/AudioFileScanner.hpp"
@@ -56,6 +57,9 @@ namespace lms::scanner
namespace
{
static constexpr std::string_view currentSettingsName{ "" };
static constexpr std::string_view lastScanSettingsName{ "last_scan" };
Wt::WDate getNextMonday(Wt::WDate current)
{
do
@@ -75,6 +79,60 @@ namespace lms::scanner
return current;
}
std::optional<ScannerSettings> readScannerSettings(db::Session& session, std::string_view name)
{
std::optional<ScannerSettings> settings;
auto transaction{ session.createReadTransaction() };
const ScanSettings::pointer scanSettings{ ScanSettings::get(session, name) };
if (!scanSettings)
return settings;
settings.emplace();
settings->audioScanVersion = scanSettings->getAudioScanVersion();
settings->startTime = scanSettings->getUpdateStartTime();
settings->updatePeriod = scanSettings->getUpdatePeriod();
MediaLibrary::find(session, [&](const MediaLibrary::pointer& mediaLibrary) {
MediaLibraryInfo info;
info.firstScan = mediaLibrary->isEmpty();
info.id = mediaLibrary->getId();
info.rootDirectory = mediaLibrary->getPath().lexically_normal();
settings->mediaLibraries.push_back(info);
});
{
const auto& tags{ scanSettings->getExtraTagsToScan() };
std::transform(std::cbegin(tags), std::cend(tags), std::back_inserter(settings->extraTags), [](std::string_view tag) { return std::string{ tag }; });
}
settings->artistTagDelimiters = scanSettings->getArtistTagDelimiters();
settings->defaultTagDelimiters = scanSettings->getDefaultTagDelimiters();
settings->skipSingleReleasePlayLists = scanSettings->getSkipSingleReleasePlayLists();
settings->allowArtistMBIDFallback = scanSettings->getAllowMBIDArtistMerge();
// TODO, store this in DB + expose in UI
settings->skipDuplicateTrackMBID = core::Service<core::IConfig>::get()->getBool("scanner-skip-duplicate-mbid", false);
return settings;
}
void writeScannerSettings(db::Session& session, std::string_view name, const ScannerSettings& settings)
{
auto transaction{ session.createWriteTransaction() };
ScanSettings::pointer scanSettings{ ScanSettings::get(session, name) };
if (!scanSettings)
scanSettings = session.create<ScanSettings>(name);
scanSettings.modify()->setAllowMBIDArtistMerge(settings.allowArtistMBIDFallback);
scanSettings.modify()->setSkipSingleReleasePlayLists(settings.skipSingleReleasePlayLists);
// TODO add more fields
}
} // namespace
std::unique_ptr<IScannerService> createScannerService(Db& db)
@@ -283,26 +341,7 @@ namespace lms::scanner
ScanStats& stats{ scanContext.stats };
stats.startTime = Wt::WDateTime::currentDateTime();
std::size_t stepIndex{};
for (auto& scanStep : _scanSteps)
{
LMS_SCOPED_TRACE_OVERVIEW("Scanner", scanStep->getStepName());
LMS_LOG(DBUPDATER, DEBUG, "Starting scan step '" << scanStep->getStepName() << "'");
scanContext.currentStepStats = ScanStepStats{
.startTime = Wt::WDateTime::currentDateTime(),
.stepCount = _scanSteps.size(),
.stepIndex = stepIndex++,
.currentStep = scanStep->getStep(),
.totalElems = 0,
.processedElems = 0
};
notifyInProgress(scanContext.currentStepStats);
scanStep->process(scanContext);
notifyInProgress(scanContext.currentStepStats);
LMS_LOG(DBUPDATER, DEBUG, "Completed scan step '" << scanStep->getStepName() << "'");
}
processScanSteps(scanContext);
{
std::unique_lock lock{ _statusMutex };
@@ -322,6 +361,10 @@ namespace lms::scanner
_lastCompleteScanStats = stats;
}
// save current settings as last scan settings to compare during next scans if something changed
writeScannerSettings(_db.getTLSSession(), lastScanSettingsName, _settings);
_lastScanSettings = _settings;
LMS_LOG(DBUPDATER, DEBUG, "Scan not aborted, scheduling next scan!");
scheduleNextScan();
@@ -333,16 +376,54 @@ namespace lms::scanner
}
}
void ScannerService::processScanSteps(ScanContext& context)
{
std::size_t stepIndex{};
for (auto& scanStep : _scanSteps)
{
context.currentStepStats = ScanStepStats{
.startTime = Wt::WDateTime::currentDateTime(),
.stepCount = _scanSteps.size(),
.stepIndex = stepIndex++,
.currentStep = scanStep->getStep(),
.totalElems = 0,
.processedElems = 0
};
if (_abortScan)
break;
if (!scanStep->needProcess(context))
{
LMS_LOG(DBUPDATER, DEBUG, "Skipping scan step '" << scanStep->getStepName() << "'");
continue;
}
{
LMS_SCOPED_TRACE_OVERVIEW("Scanner", scanStep->getStepName());
LMS_LOG(DBUPDATER, DEBUG, "Starting scan step '" << scanStep->getStepName() << "'");
notifyInProgress(context.currentStepStats);
scanStep->process(context);
notifyInProgress(context.currentStepStats);
LMS_LOG(DBUPDATER, DEBUG, "Completed scan step '" << scanStep->getStepName() << "'");
}
}
}
void ScannerService::refreshScanSettings()
{
ScannerSettings newSettings{ readSettings() };
if (_settings == newSettings)
std::optional<ScannerSettings> newSettings{ readScannerSettings(_db.getTLSSession(), currentSettingsName) };
assert(newSettings.has_value());
if (_settings == *newSettings)
return;
LMS_LOG(DBUPDATER, DEBUG, "Scanner settings updated");
LMS_LOG(DBUPDATER, DEBUG, "Using scan settings version " << newSettings.scanVersion);
LMS_LOG(DBUPDATER, DEBUG, "Using audio scan settings version " << newSettings->audioScanVersion);
_settings = std::move(newSettings);
_settings = std::move(*newSettings);
if (!_lastScanSettings)
_lastScanSettings = readScannerSettings(_db.getTLSSession(), lastScanSettingsName);
auto cbFunc{ [this](const ScanStepStats& stats) {
notifyInProgressIfNeeded(stats);
@@ -360,6 +441,7 @@ namespace lms::scanner
ScanStepBase::InitParams params{
.settings = _settings,
.lastScanSettings = _lastScanSettings.has_value() ? &(_lastScanSettings.value()) : nullptr,
.progressCallback = cbFunc,
.abortScan = _abortScan,
.db = _db,
@@ -384,43 +466,6 @@ namespace lms::scanner
_scanSteps.emplace_back(std::make_unique<ScanStepCheckForDuplicatedFiles>(params));
}
ScannerSettings ScannerService::readSettings()
{
ScannerSettings newSettings;
newSettings.skipDuplicateTrackMBID = core::Service<core::IConfig>::get()->getBool("scanner-skip-duplicate-mbid", false);
{
auto transaction{ _db.getTLSSession().createReadTransaction() };
const ScanSettings::pointer scanSettings{ ScanSettings::get(_db.getTLSSession()) };
newSettings.scanVersion = scanSettings->getScanVersion();
newSettings.startTime = scanSettings->getUpdateStartTime();
newSettings.updatePeriod = scanSettings->getUpdatePeriod();
MediaLibrary::find(_db.getTLSSession(), [&](const MediaLibrary::pointer& mediaLibrary) {
MediaLibraryInfo info;
info.firstScan = mediaLibrary->isEmpty();
info.id = mediaLibrary->getId();
info.rootDirectory = mediaLibrary->getPath().lexically_normal();
newSettings.mediaLibraries.push_back(info);
});
{
const auto& tags{ scanSettings->getExtraTagsToScan() };
std::transform(std::cbegin(tags), std::cend(tags), std::back_inserter(newSettings.extraTags), [](std::string_view tag) { return std::string{ tag }; });
}
newSettings.artistTagDelimiters = scanSettings->getArtistTagDelimiters();
newSettings.defaultTagDelimiters = scanSettings->getDefaultTagDelimiters();
newSettings.skipSingleReleasePlayLists = scanSettings->getSkipSingleReleasePlayLists();
}
return newSettings;
}
void ScannerService::notifyInProgress(const ScanStepStats& stepStats)
{
{