Reloading the recommendation engine once scan is complete
This commit is contained in:
@@ -47,14 +47,14 @@
|
|||||||
<div class="form-horizontal">
|
<div class="form-horizontal">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="${id:similarity-engine-type}">
|
<label class="control-label col-sm-2" for="${id:recommendation-engine-type}">
|
||||||
${tr:Lms.Admin.Database.similarity-engine-type}
|
${tr:Lms.Admin.Database.recommendation-engine-type}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
${similarity-engine-type}
|
${recommendation-engine-type}
|
||||||
</div>
|
</div>
|
||||||
<div class="help-block col-sm-5">
|
<div class="help-block col-sm-5">
|
||||||
${similarity-engine-type-info}
|
${recommendation-engine-type-info}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ createEngine(Database::Session& session)
|
|||||||
|
|
||||||
Engine::Engine(Database::Session& session)
|
Engine::Engine(Database::Session& session)
|
||||||
{
|
{
|
||||||
reloadSettings(session);
|
reload(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Engine::reloadSettings(Database::Session& session)
|
Engine::reload(Database::Session& session)
|
||||||
{
|
{
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
@@ -50,56 +50,57 @@ Engine::reloadSettings(Database::Session& session)
|
|||||||
return ScanSettings::get(session)->getRecommendationEngineType();
|
return ScanSettings::get(session)->getRecommendationEngineType();
|
||||||
}()};
|
}()};
|
||||||
|
|
||||||
|
// TODO: just replace the classifier once it is ready
|
||||||
clearClassifiers();
|
clearClassifiers();
|
||||||
|
|
||||||
switch (engineType)
|
switch (engineType)
|
||||||
{
|
{
|
||||||
case ScanSettings::RecommendationEngineType::Features:
|
case ScanSettings::RecommendationEngineType::Features:
|
||||||
// _classifiers.emplace(0, createFeaturesClassifier()); // higher priority
|
// addClassifier(createFeaturesClassifier(), 0); // higher priority
|
||||||
// [[fallthrough]];
|
// [[fallthrough]];
|
||||||
|
|
||||||
case ScanSettings::RecommendationEngineType::Clusters:
|
case ScanSettings::RecommendationEngineType::Clusters:
|
||||||
_classifiers.emplace(1, createClustersClassifier(session)); // lower priority
|
addClassifier(createClustersClassifier(session), 1); // lower priority
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Database::IdType>
|
std::vector<Database::IdType>
|
||||||
Engine::getSimilarTracksFromTrackList(Database::Session& /*session*/, Database::IdType /*trackListId*/, std::size_t /*maxCount*/)
|
Engine::getSimilarTracksFromTrackList(Database::Session& session, Database::IdType trackListId, std::size_t maxCount)
|
||||||
{
|
{
|
||||||
#if 0
|
const std::unordered_set<Database::IdType> trackIds {[&]() -> std::unordered_set<Database::IdType>
|
||||||
auto engineType {getEngineType(session)};
|
|
||||||
auto somSearcher {_somAddon.getSearcher()};
|
|
||||||
|
|
||||||
std::set<Database::IdType> trackIds;
|
|
||||||
{
|
{
|
||||||
auto transaction {session.createSharedTransaction()};
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)};
|
Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)};
|
||||||
if (trackList)
|
if (trackList)
|
||||||
{
|
{
|
||||||
const std::vector<Database::IdType> orderedTrackIds {trackList->getTrackIds()};
|
const std::vector<Database::IdType> orderedTrackIds {trackList->getTrackIds()};
|
||||||
trackIds = std::set<Database::IdType> {std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)};
|
return std::unordered_set<Database::IdType> {std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return {};
|
||||||
|
}()};
|
||||||
|
|
||||||
if (trackIds.empty())
|
if (trackIds.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (engineType == Database::ScanSettings::SimilarityEngineType::Features
|
std::shared_lock lock {_mutex};
|
||||||
&& somSearcher
|
|
||||||
&& std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return somSearcher->isTrackClassified(trackId); } ))
|
for (const auto& [priority, classifier] : _classifiers)
|
||||||
{
|
{
|
||||||
return somSearcher->getSimilarTracks(trackIds, maxCount);
|
if (std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return classifier->isTrackClassified(trackId); } ))
|
||||||
|
return classifier->getSimilarTracksFromTrackList(session, trackListId, maxCount);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return ClusterEngine::getSimilarTracksFromTrackList(session, trackListId, maxCount);
|
|
||||||
#endif
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Database::IdType>
|
std::vector<Database::IdType>
|
||||||
Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set<Database::IdType>& trackIds, std::size_t maxCount)
|
Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set<Database::IdType>& trackIds, std::size_t maxCount)
|
||||||
{
|
{
|
||||||
|
std::shared_lock lock {_mutex};
|
||||||
|
|
||||||
for (const auto& [priority, classifier] : _classifiers)
|
for (const auto& [priority, classifier] : _classifiers)
|
||||||
{
|
{
|
||||||
if (std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return classifier->isTrackClassified(trackId); } ))
|
if (std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return classifier->isTrackClassified(trackId); } ))
|
||||||
@@ -112,6 +113,8 @@ Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set<
|
|||||||
std::vector<Database::IdType>
|
std::vector<Database::IdType>
|
||||||
Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std::size_t maxCount)
|
Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std::size_t maxCount)
|
||||||
{
|
{
|
||||||
|
std::shared_lock lock {_mutex};
|
||||||
|
|
||||||
for (const auto& [priority, classifier] : _classifiers)
|
for (const auto& [priority, classifier] : _classifiers)
|
||||||
{
|
{
|
||||||
if (classifier->isReleaseClassified(releaseId))
|
if (classifier->isReleaseClassified(releaseId))
|
||||||
@@ -124,6 +127,8 @@ Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releas
|
|||||||
std::vector<Database::IdType>
|
std::vector<Database::IdType>
|
||||||
Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistId, std::size_t maxCount)
|
Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistId, std::size_t maxCount)
|
||||||
{
|
{
|
||||||
|
std::shared_lock lock {_mutex};
|
||||||
|
|
||||||
for (const auto& [priority, classifier] : _classifiers)
|
for (const auto& [priority, classifier] : _classifiers)
|
||||||
{
|
{
|
||||||
if (classifier->isArtistClassified(artistId))
|
if (classifier->isArtistClassified(artistId))
|
||||||
@@ -136,12 +141,14 @@ Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistI
|
|||||||
void
|
void
|
||||||
Engine::clearClassifiers()
|
Engine::clearClassifiers()
|
||||||
{
|
{
|
||||||
|
std::unique_lock lock {_mutex};
|
||||||
_classifiers.clear();
|
_classifiers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Engine::addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority)
|
Engine::addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority)
|
||||||
{
|
{
|
||||||
|
std::unique_lock lock {_mutex};
|
||||||
_classifiers.emplace(priority, std::move(classifier));
|
_classifiers.emplace(priority, std::move(classifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
#include "recommendation/IEngine.hpp"
|
#include "recommendation/IEngine.hpp"
|
||||||
#include "recommendation/IClassifier.hpp"
|
#include "recommendation/IClassifier.hpp"
|
||||||
@@ -38,7 +39,7 @@ namespace Recommendation
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void reloadSettings(Database::Session& session) override;
|
void reload(Database::Session& session) override;
|
||||||
|
|
||||||
// Closest results first
|
// Closest results first
|
||||||
std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) override;
|
std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) override;
|
||||||
@@ -49,6 +50,7 @@ namespace Recommendation
|
|||||||
void clearClassifiers();
|
void clearClassifiers();
|
||||||
void addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority);
|
void addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority);
|
||||||
|
|
||||||
|
std::shared_mutex _mutex;
|
||||||
std::map<unsigned, std::unique_ptr<IClassifier>> _classifiers;
|
std::map<unsigned, std::unique_ptr<IClassifier>> _classifiers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Recommendation
|
|||||||
public:
|
public:
|
||||||
virtual ~IEngine() = default;
|
virtual ~IEngine() = default;
|
||||||
|
|
||||||
virtual void reloadSettings(Database::Session& session) = 0;
|
virtual void reload(Database::Session& session) = 0;
|
||||||
|
|
||||||
// Closest results first
|
// Closest results first
|
||||||
virtual std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) = 0;
|
virtual std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) = 0;
|
||||||
|
|||||||
@@ -427,6 +427,10 @@ MediaScanner::scan(boost::system::error_code err)
|
|||||||
addon->preScanComplete();
|
addon->preScanComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Optimizing db...";
|
||||||
|
_dbSession.optimize();
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Optimize db done!";
|
||||||
|
|
||||||
if (_running)
|
if (_running)
|
||||||
{
|
{
|
||||||
stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC();
|
stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC();
|
||||||
@@ -448,10 +452,6 @@ MediaScanner::scan(boost::system::error_code err)
|
|||||||
_curState = State::NotScheduled;
|
_curState = State::NotScheduled;
|
||||||
_inProgressScanStats.reset();
|
_inProgressScanStats.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(DBUPDATER, INFO) << "Optimizing db...";
|
|
||||||
_dbSession.optimize();
|
|
||||||
LMS_LOG(DBUPDATER, INFO) << "Optimize db done!";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
+17
-1
@@ -145,7 +145,23 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
{
|
{
|
||||||
Database::Session session {database};
|
Database::Session session {database};
|
||||||
ServiceProvider<Recommendation::IEngine>::assign(Recommendation::createEngine(session));
|
Recommendation::IEngine& recommendationEngine {ServiceProvider<Recommendation::IEngine>::assign(Recommendation::createEngine(session))};
|
||||||
|
|
||||||
|
mediaScanner.scanComplete().connect([&]()
|
||||||
|
{
|
||||||
|
auto status = mediaScanner.getStatus();
|
||||||
|
|
||||||
|
if (status.lastCompleteScanStats->nbChanges() > 0)
|
||||||
|
{
|
||||||
|
LMS_LOG(MAIN, INFO) << "Scanner changed some files, reloading the recommendation engine...";
|
||||||
|
Database::Session session {database};
|
||||||
|
recommendationEngine.reload(session);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LMS_LOG(MAIN, INFO) << "Scanner did not change files, not reloading the recommendation engine...";
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
CoverArt::IGrabber& coverArtGrabber {ServiceProvider<CoverArt::IGrabber>::assign(CoverArt::createGrabber(argv[0]))};
|
CoverArt::IGrabber& coverArtGrabber {ServiceProvider<CoverArt::IGrabber>::assign(CoverArt::createGrabber(argv[0]))};
|
||||||
|
|||||||
Reference in New Issue
Block a user