diff --git a/approot/admin-database.xml b/approot/admin-database.xml index e5c8ec6f..6e2b08ec 100644 --- a/approot/admin-database.xml +++ b/approot/admin-database.xml @@ -47,14 +47,14 @@
-
diff --git a/src/libs/recommendation/impl/Engine.cpp b/src/libs/recommendation/impl/Engine.cpp index a5cf2a63..d53d8645 100644 --- a/src/libs/recommendation/impl/Engine.cpp +++ b/src/libs/recommendation/impl/Engine.cpp @@ -36,11 +36,11 @@ createEngine(Database::Session& session) Engine::Engine(Database::Session& session) { - reloadSettings(session); + reload(session); } void -Engine::reloadSettings(Database::Session& session) +Engine::reload(Database::Session& session) { using namespace Database; @@ -50,56 +50,57 @@ Engine::reloadSettings(Database::Session& session) return ScanSettings::get(session)->getRecommendationEngineType(); }()}; + // TODO: just replace the classifier once it is ready clearClassifiers(); switch (engineType) { case ScanSettings::RecommendationEngineType::Features: -// _classifiers.emplace(0, createFeaturesClassifier()); // higher priority +// addClassifier(createFeaturesClassifier(), 0); // higher priority // [[fallthrough]]; case ScanSettings::RecommendationEngineType::Clusters: - _classifiers.emplace(1, createClustersClassifier(session)); // lower priority + addClassifier(createClustersClassifier(session), 1); // lower priority break; } } std::vector -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 - auto engineType {getEngineType(session)}; - auto somSearcher {_somAddon.getSearcher()}; - - std::set trackIds; + const std::unordered_set trackIds {[&]() -> std::unordered_set { auto transaction {session.createSharedTransaction()}; + Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)}; if (trackList) { const std::vector orderedTrackIds {trackList->getTrackIds()}; - trackIds = std::set {std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)}; + return std::unordered_set {std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)}; } - } + + return {}; + }()}; if (trackIds.empty()) return {}; - if (engineType == Database::ScanSettings::SimilarityEngineType::Features - && somSearcher - && std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return somSearcher->isTrackClassified(trackId); } )) + std::shared_lock lock {_mutex}; + + 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 {}; } std::vector Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set& trackIds, std::size_t maxCount) { + std::shared_lock lock {_mutex}; + for (const auto& [priority, classifier] : _classifiers) { 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 Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std::size_t maxCount) { + std::shared_lock lock {_mutex}; + for (const auto& [priority, classifier] : _classifiers) { if (classifier->isReleaseClassified(releaseId)) @@ -124,6 +127,8 @@ Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releas std::vector Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistId, std::size_t maxCount) { + std::shared_lock lock {_mutex}; + for (const auto& [priority, classifier] : _classifiers) { if (classifier->isArtistClassified(artistId)) @@ -136,12 +141,14 @@ Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistI void Engine::clearClassifiers() { + std::unique_lock lock {_mutex}; _classifiers.clear(); } void Engine::addClassifier(std::unique_ptr classifier, unsigned priority) { + std::unique_lock lock {_mutex}; _classifiers.emplace(priority, std::move(classifier)); } diff --git a/src/libs/recommendation/impl/Engine.hpp b/src/libs/recommendation/impl/Engine.hpp index 2fdace41..17b9787c 100644 --- a/src/libs/recommendation/impl/Engine.hpp +++ b/src/libs/recommendation/impl/Engine.hpp @@ -20,6 +20,7 @@ #pragma once #include +#include #include "recommendation/IEngine.hpp" #include "recommendation/IClassifier.hpp" @@ -38,7 +39,7 @@ namespace Recommendation private: - void reloadSettings(Database::Session& session) override; + void reload(Database::Session& session) override; // Closest results first std::vector getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) override; @@ -49,6 +50,7 @@ namespace Recommendation void clearClassifiers(); void addClassifier(std::unique_ptr classifier, unsigned priority); + std::shared_mutex _mutex; std::map> _classifiers; }; diff --git a/src/libs/recommendation/include/recommendation/IEngine.hpp b/src/libs/recommendation/include/recommendation/IEngine.hpp index 91f10c50..4561f19d 100644 --- a/src/libs/recommendation/include/recommendation/IEngine.hpp +++ b/src/libs/recommendation/include/recommendation/IEngine.hpp @@ -36,7 +36,7 @@ namespace Recommendation public: virtual ~IEngine() = default; - virtual void reloadSettings(Database::Session& session) = 0; + virtual void reload(Database::Session& session) = 0; // Closest results first virtual std::vector getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) = 0; diff --git a/src/libs/scanner/impl/MediaScanner.cpp b/src/libs/scanner/impl/MediaScanner.cpp index a1a88753..6a606773 100644 --- a/src/libs/scanner/impl/MediaScanner.cpp +++ b/src/libs/scanner/impl/MediaScanner.cpp @@ -427,6 +427,10 @@ MediaScanner::scan(boost::system::error_code err) addon->preScanComplete(); } + LMS_LOG(DBUPDATER, INFO) << "Optimizing db..."; + _dbSession.optimize(); + LMS_LOG(DBUPDATER, INFO) << "Optimize db done!"; + if (_running) { stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC(); @@ -448,10 +452,6 @@ MediaScanner::scan(boost::system::error_code err) _curState = State::NotScheduled; _inProgressScanStats.reset(); } - - LMS_LOG(DBUPDATER, INFO) << "Optimizing db..."; - _dbSession.optimize(); - LMS_LOG(DBUPDATER, INFO) << "Optimize db done!"; } void diff --git a/src/lms/main.cpp b/src/lms/main.cpp index f2a71fa0..b82a7917 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -145,7 +145,23 @@ int main(int argc, char* argv[]) { Database::Session session {database}; - ServiceProvider::assign(Recommendation::createEngine(session)); + Recommendation::IEngine& recommendationEngine {ServiceProvider::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::assign(CoverArt::createGrabber(argv[0]))};