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
@@ -32,6 +32,7 @@ namespace lms::scanner
virtual ScanStep getStep() const = 0;
virtual core::LiteralString getStepName() const = 0;
virtual bool needProcess(const ScanContext& context) const = 0;
virtual void process(ScanContext& context) = 0;
};
} // namespace lms::scanner
@@ -74,6 +74,12 @@ namespace lms::scanner
}
} // namespace
bool ScanStepArtistReconciliation::needProcess([[maybe_unused]] const ScanContext& context) const
{
// Since this step is very fast in case there is nothing to do, no need to skip if nothing has changed
return true;
}
void ScanStepArtistReconciliation::process(ScanContext& context)
{
// Reconcile artist links
@@ -99,7 +105,7 @@ namespace lms::scanner
db::Session& session{ _db.getTLSSession() };
std::vector<db::ArtistInfo::pointer> artistInfo;
while (true)
while (!_abortScan)
{
artistInfo.clear();
{
@@ -133,7 +139,7 @@ namespace lms::scanner
db::Session& session{ _db.getTLSSession() };
std::vector<db::ArtistInfo::pointer> artistInfo;
while (true)
while (!_abortScan)
{
artistInfo.clear();
{
@@ -166,7 +172,7 @@ namespace lms::scanner
db::Session& session{ _db.getTLSSession() };
std::vector<db::TrackArtistLink::pointer> links;
while (true)
while (!_abortScan)
{
links.clear();
{
@@ -200,7 +206,7 @@ namespace lms::scanner
db::Session& session{ _db.getTLSSession() };
std::vector<db::TrackArtistLink::pointer> links;
while (true)
while (!_abortScan)
{
links.clear();
{
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::ReconciliateArtists; }
core::LiteralString getStepName() const override { return "Artist reconciliation"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
void updateLinksForArtistNameNoLongerMatch(ScanContext& context);
@@ -254,14 +254,16 @@ namespace lms::scanner
{
}
bool ScanStepAssociateArtistImages::needProcess(const ScanContext& context) const
{
if (context.stats.nbChanges() > 0)
return true;
return false;
}
void ScanStepAssociateArtistImages::process(ScanContext& context)
{
if (_abortScan)
return;
if (context.stats.nbChanges() == 0)
return;
auto& session{ _db.getTLSSession() };
{
@@ -37,6 +37,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::AssociateArtistImages; }
core::LiteralString getStepName() const override { return "Associate artist images"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
const std::vector<std::string> _artistFileNames;
@@ -138,14 +138,16 @@ namespace lms::scanner
}
} // namespace
bool ScanStepAssociateExternalLyrics::needProcess(const ScanContext& context) const
{
if (context.stats.nbChanges() > 0)
return true;
return false;
}
void ScanStepAssociateExternalLyrics::process(ScanContext& context)
{
if (_abortScan)
return;
if (context.stats.nbChanges() == 0)
return;
auto& session{ _db.getTLSSession() };
{
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::AssociateExternalLyrics; }
core::LiteralString getStepName() const override { return "Associate external lyrics"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
};
} // namespace lms::scanner
@@ -212,14 +212,19 @@ namespace lms::scanner
}
} // namespace
bool ScanStepAssociatePlayListTracks::needProcess(const ScanContext& context) const
{
if (context.stats.nbChanges() > 0)
return true;
if (getLastScanSettings() && getLastScanSettings()->skipSingleReleasePlayLists != _settings.skipSingleReleasePlayLists)
return true;
return false;
}
void ScanStepAssociatePlayListTracks::process(ScanContext& context)
{
if (_abortScan)
return;
if (context.stats.nbChanges() == 0)
return;
auto& session{ _db.getTLSSession() };
{
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::AssociatePlayListTracks; }
core::LiteralString getStepName() const override { return "Associate playlist tracks"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
};
} // namespace lms::scanner
@@ -199,14 +199,16 @@ namespace lms::scanner
{
}
bool ScanStepAssociateReleaseImages::needProcess(const ScanContext& context) const
{
if (context.stats.nbChanges() > 0)
return true;
return false;
}
void ScanStepAssociateReleaseImages::process(ScanContext& context)
{
if (_abortScan)
return;
if (context.stats.nbChanges() == 0)
return;
auto& session{ _db.getTLSSession() };
{
@@ -37,6 +37,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::AssociateReleaseImages; }
core::LiteralString getStepName() const override { return "Associate release images"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
const std::vector<std::string> _releaseFileNames;
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScanStepBase.hpp"
namespace lms::scanner
{
ScanStepBase::ScanStepBase(InitParams& initParams)
: _settings{ initParams.settings }
, _progressCallback{ initParams.progressCallback }
, _abortScan{ initParams.abortScan }
, _db{ initParams.db }
, _fileScanners(std::cbegin(initParams.fileScanners), std::cend(initParams.fileScanners))
, _lastScanSettings{ initParams.lastScanSettings }
{
}
ScanStepBase::~ScanStepBase() = default;
} // namespace lms::scanner
@@ -44,28 +44,27 @@ namespace lms::scanner
struct InitParams
{
const ScannerSettings& settings;
const ScannerSettings* lastScanSettings{};
ProgressCallback progressCallback;
bool& abortScan;
db::Db& db;
std::span<IFileScanner*> fileScanners;
};
ScanStepBase(InitParams& initParams)
: _settings{ initParams.settings }
, _progressCallback{ initParams.progressCallback }
, _abortScan{ initParams.abortScan }
, _db{ initParams.db }
, _fileScanners(std::cbegin(initParams.fileScanners), std::cend(initParams.fileScanners))
{
}
~ScanStepBase() override = default;
ScanStepBase(InitParams& initParams);
~ScanStepBase() override;
ScanStepBase(const ScanStepBase&) = delete;
ScanStepBase& operator=(const ScanStepBase&) = delete;
protected:
const ScannerSettings* getLastScanSettings() const { return _lastScanSettings; }
const ScannerSettings& _settings;
ProgressCallback _progressCallback;
bool& _abortScan;
db::Db& _db;
std::vector<IFileScanner*> _fileScanners;
private:
const ScannerSettings* _lastScanSettings{};
};
} // namespace lms::scanner
@@ -26,13 +26,16 @@
namespace lms::scanner
{
bool ScanStepCheckForDuplicatedFiles::needProcess([[maybe_unused]] const ScanContext& context) const
{
// Always check for everything
return true;
}
void ScanStepCheckForDuplicatedFiles::process(ScanContext& context)
{
using namespace db;
if (_abortScan)
return;
Session& session{ _db.getTLSSession() };
auto transaction{ session.createReadTransaction() };
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
core::LiteralString getStepName() const override { return "Check for duplicated files"; }
ScanStep getStep() const override { return ScanStep::CheckForDuplicatedFiles; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
};
} // namespace lms::scanner
@@ -41,11 +41,14 @@ namespace lms::scanner
constexpr std::size_t batchSize = 100;
}
bool ScanStepCheckForRemovedFiles::needProcess([[maybe_unused]] const ScanContext& context) const
{
// always check for removed files
return true;
}
void ScanStepCheckForRemovedFiles::process(ScanContext& context)
{
if (_abortScan)
return;
db::Session& session{ _db.getTLSSession() };
{
@@ -34,6 +34,7 @@ namespace lms::scanner
private:
core::LiteralString getStepName() const override { return "Check for removed files"; }
ScanStep getStep() const override { return ScanStep::CheckForRemovedFiles; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
template<typename Object>
@@ -24,10 +24,14 @@
namespace lms::scanner
{
void ScanStepCompact::process(ScanContext& context)
bool ScanStepCompact::needProcess(const ScanContext& context) const
{
// Don't auto compact as it may be too annoying to block the whole application for very large databases
if (context.scanOptions.compact)
_db.getTLSSession().vacuum();
return context.scanOptions.compact;
}
void ScanStepCompact::process([[maybe_unused]] ScanContext& context)
{
_db.getTLSSession().vacuum();
}
} // namespace lms::scanner
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::Compact; }
core::LiteralString getStepName() const override { return "Compact"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
};
} // namespace lms::scanner
@@ -25,13 +25,18 @@
namespace lms::scanner
{
bool ScanStepComputeClusterStats::needProcess(const ScanContext& context) const
{
if (context.stats.nbChanges() > 0)
return true;
return false;
}
void ScanStepComputeClusterStats::process(ScanContext& context)
{
using namespace db;
if (context.stats.nbChanges() == 0)
return;
Session& dbSession{ _db.getTLSSession() };
const std::size_t clusterCount{ [&] {
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::ComputeClusterStats; }
core::LiteralString getStepName() const override { return "Compute cluster stats"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
};
} // namespace lms::scanner
@@ -28,6 +28,12 @@
namespace lms::scanner
{
bool ScanStepDiscoverFiles::needProcess([[maybe_unused]] const ScanContext& context) const
{
// always discover files
return true;
}
void ScanStepDiscoverFiles::process(ScanContext& context)
{
context.stats.totalFileCount = 0;
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::DiscoverFiles; }
core::LiteralString getStepName() const override { return "Discover files"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
};
} // namespace lms::scanner
@@ -25,32 +25,38 @@
namespace lms::scanner
{
bool ScanStepOptimize::needProcess(const ScanContext& context) const
{
if (context.scanOptions.forceOptimize)
return true;
if (context.stats.nbChanges() > (context.stats.nbFiles() / 10))
return true;
return false;
}
void ScanStepOptimize::process(ScanContext& context)
{
ScanStats& stats{ context.stats };
LMS_LOG(DBUPDATER, INFO, "Database analyze started");
if (context.scanOptions.forceOptimize || (stats.nbChanges() > (stats.nbFiles() / 10)))
auto& session{ _db.getTLSSession() };
std::vector<std::string> entries;
session.retrieveEntriesToAnalyze(entries);
context.currentStepStats.totalElems = entries.size();
_progressCallback(context.currentStepStats);
for (const std::string& entry : entries)
{
LMS_LOG(DBUPDATER, INFO, "Database analyze started");
if (_abortScan)
break;
auto& session{ _db.getTLSSession() };
std::vector<std::string> entries;
session.retrieveEntriesToAnalyze(entries);
context.currentStepStats.totalElems = entries.size();
_db.getTLSSession().analyzeEntry(entry);
context.currentStepStats.processedElems++;
_progressCallback(context.currentStepStats);
for (const std::string& entry : entries)
{
if (_abortScan)
break;
_db.getTLSSession().analyzeEntry(entry);
context.currentStepStats.processedElems++;
_progressCallback(context.currentStepStats);
}
LMS_LOG(DBUPDATER, INFO, "Database analyze complete");
}
LMS_LOG(DBUPDATER, INFO, "Database analyze complete");
}
} // namespace lms::scanner
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::Optimize; }
core::LiteralString getStepName() const override { return "Optimize"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
};
} // namespace lms::scanner
@@ -31,6 +31,12 @@
namespace lms::scanner
{
bool ScanStepRemoveOrphanedDbEntries::needProcess([[maybe_unused]] const ScanContext& context) const
{
// fast enough when there is nothing to do
return true;
}
void ScanStepRemoveOrphanedDbEntries::process(ScanContext& context)
{
removeOrphanedClusters(context);
@@ -31,6 +31,7 @@ namespace lms::scanner
private:
core::LiteralString getStepName() const override { return "Remove orphaned DB entries"; }
ScanStep getStep() const override { return ScanStep::RemoveOrphanedDbEntries; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
void removeOrphanedClusters(ScanContext& context);
@@ -64,6 +64,12 @@ namespace lms::scanner
LMS_LOG(DBUPDATER, INFO, "Using " << _fileScanQueue.getThreadCount() << " thread(s) for scanning file metadata");
}
bool ScanStepScanFiles::needProcess([[maybe_unused]] const ScanContext& context) const
{
// Always need to scan files
return true;
}
void ScanStepScanFiles::process(ScanContext& context)
{
context.currentStepStats.totalElems = context.stats.totalFileCount;
@@ -38,6 +38,7 @@ namespace lms::scanner
private:
ScanStep getStep() const override { return ScanStep::ScanFiles; }
core::LiteralString getStepName() const override { return "Scan files"; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
void process(ScanContext& context, const MediaLibraryInfo& mediaLibrary);
void processFileScanResults(ScanContext& context, std::span<std::unique_ptr<IFileScanOperation>> scanOperations);
@@ -29,6 +29,11 @@
namespace lms::scanner
{
bool ScanStepUpdateLibraryFields::needProcess([[maybe_unused]] const ScanContext& context) const
{
// Fast enough when nothing to do
return true;
}
void ScanStepUpdateLibraryFields::process(ScanContext& context)
{
@@ -33,6 +33,7 @@ namespace lms::scanner
private:
core::LiteralString getStepName() const override { return "Update Library fields"; }
ScanStep getStep() const override { return ScanStep::UpdateLibraryFields; }
bool needProcess(const ScanContext& context) const override;
void process(ScanContext& context) override;
void processDirectories(ScanContext& context);