From 2db46260ab99f37b827ac37408c38a47a2845986 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 4 Mar 2018 15:19:44 +0100 Subject: [PATCH] Made database scan work, added immediate scan option --- approot/messages.xml | 2 +- approot/settings.xml | 2 +- src/database/Track.cpp | 6 +- src/main/main.cpp | 3 +- src/scanner/MediaScanner.cpp | 111 ++++++++++++++++++------------- src/scanner/MediaScanner.hpp | 15 ++--- src/ui/LmsApplication.cpp | 12 +++- src/ui/LmsApplication.hpp | 12 +++- src/ui/settings/DatabaseView.cpp | 10 ++- 9 files changed, 105 insertions(+), 68 deletions(-) diff --git a/approot/messages.xml b/approot/messages.xml index 6bf0010a..b3e1146e 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -30,6 +30,6 @@ Not a directory Apply Discard - +Scan now! diff --git a/approot/settings.xml b/approot/settings.xml index 75f4c661..f5107fcc 100644 --- a/approot/settings.xml +++ b/approot/settings.xml @@ -45,7 +45,7 @@
- ${apply-btn class="btn-primary"} ${discard-btn} + ${apply-btn class="btn-primary"} ${discard-btn} ${immediate-scan-btn class="btn-info"}
diff --git a/src/database/Track.cpp b/src/database/Track.cpp index d913980f..d66327f0 100644 --- a/src/database/Track.cpp +++ b/src/database/Track.cpp @@ -295,12 +295,12 @@ Cluster::pointer ClusterType::getCluster(std::string name) const { assert(self()); - assert(self()->id() != Wt::Dbo::dbo_traits::invalidId() ); + assert(self()->id() != Wt::Dbo::dbo_traits::invalidId() ); assert(session()); return session()->find() .where("name = ?").bind(name) - .where("cluster_type_id = ").bind(self()->id()); + .where("cluster_type_id = ?").bind(self()->id()); } std::vector @@ -311,7 +311,7 @@ ClusterType::getClusters() const assert(session()); Wt::Dbo::collection res = session()->find() - .where("cluster_type_id = ").bind(self()->id()) + .where("cluster_type_id = ?").bind(self()->id()) .orderBy("name"); return std::vector(res.begin(), res.end()); diff --git a/src/main/main.cpp b/src/main/main.cpp index 2901d976..4715640c 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -129,7 +129,7 @@ int main(int argc, char* argv[]) // bind entry point server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create, - _1, boost::ref(*connectionPool))); + _1, boost::ref(*connectionPool), boost::ref(scanner))); // Start LMS_LOG(MAIN, INFO) << "Starting Media scanner..."; @@ -149,6 +149,7 @@ int main(int argc, char* argv[]) LMS_LOG(MAIN, INFO) << "Stopping database updater..."; scanner.stop(); + LMS_LOG(MAIN, INFO) << "Clean stop!"; res = EXIT_SUCCESS; } catch( libconfig::FileIOException& e) diff --git a/src/scanner/MediaScanner.cpp b/src/scanner/MediaScanner.cpp index 7f1239d8..2e56944f 100644 --- a/src/scanner/MediaScanner.cpp +++ b/src/scanner/MediaScanner.cpp @@ -155,7 +155,7 @@ MediaScanner::start(void) _running = true; // post some jobs in the io_service - processNextJob(); + scheduleScan(); _ioService.start(); } @@ -171,50 +171,63 @@ MediaScanner::stop(void) } void -MediaScanner::processNextJob(void) +MediaScanner::scheduleImmediateScan() { - if (Setting::getBool(_db.getSession(), "manual_scan_requested", false)) + _ioService.post([=]() { - LMS_LOG(DBUPDATER, INFO) << "Manual scan requested!"; + LMS_LOG(DBUPDATER, INFO) << "Schedule immediate scan"; scheduleScan( boost::posix_time::seconds(0) ); - } - else + }); +} + +void +MediaScanner::reschedule() +{ + _ioService.post([=]() { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - boost::posix_time::time_duration startTime = getUpdateStartTime(_db.getSession()); + LMS_LOG(DBUPDATER, INFO) << "Rescheduling scan"; + scheduleScan(); + }); +} - boost::gregorian::date nextScanDate; +void +MediaScanner::scheduleScan() +{ + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + boost::posix_time::time_duration startTime = getUpdateStartTime(_db.getSession()); - switch ( getUpdatePeriod(_db.getSession()) ) - { - case UpdatePeriod::Daily: - if (now.time_of_day() < startTime) - nextScanDate = now.date(); - else - nextScanDate = getNextDay(now.date()); - break; + boost::gregorian::date nextScanDate; - case UpdatePeriod::Weekly: - if (now.time_of_day() < startTime && now.date().day_of_week() == 1) - nextScanDate = now.date(); - else - nextScanDate = getNextMonday(now.date()); - break; + switch ( getUpdatePeriod(_db.getSession()) ) + { + case UpdatePeriod::Daily: + if (now.time_of_day() < startTime) + nextScanDate = now.date(); + else + nextScanDate = getNextDay(now.date()); + break; - case UpdatePeriod::Monthly: - if (now.time_of_day() < startTime && now.date().day() == 1) - nextScanDate = now.date(); - else - nextScanDate = getNextFirstOfMonth(now.date()); - break; + case UpdatePeriod::Weekly: + if (now.time_of_day() < startTime && now.date().day_of_week() == 1) + nextScanDate = now.date(); + else + nextScanDate = getNextMonday(now.date()); + break; - case UpdatePeriod::Never: - break; - } + case UpdatePeriod::Monthly: + if (now.time_of_day() < startTime && now.date().day() == 1) + nextScanDate = now.date(); + else + nextScanDate = getNextFirstOfMonth(now.date()); + break; - if (!nextScanDate.is_special()) - scheduleScan( boost::posix_time::ptime (nextScanDate, startTime) ); + case UpdatePeriod::Never: + LMS_LOG(DBUPDATER, INFO) << "Auto scan disabled!"; + break; } + + if (!nextScanDate.is_special()) + scheduleScan( boost::posix_time::ptime (nextScanDate, startTime) ); } void @@ -222,7 +235,7 @@ MediaScanner::scheduleScan( boost::posix_time::time_duration duration) { LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan in " << duration; _scheduleTimer.expires_from_now(duration); - _scheduleTimer.async_wait( boost::bind( &MediaScanner::process, this, boost::asio::placeholders::error) ); + _scheduleTimer.async_wait( boost::bind( &MediaScanner::scan, this, boost::asio::placeholders::error) ); } void @@ -230,11 +243,11 @@ MediaScanner::scheduleScan( boost::posix_time::ptime time) { LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan at " << time; _scheduleTimer.expires_at(time); - _scheduleTimer.async_wait( boost::bind( &MediaScanner::process, this, boost::asio::placeholders::error) ); + _scheduleTimer.async_wait( boost::bind( &MediaScanner::scan, this, boost::asio::placeholders::error) ); } void -MediaScanner::process(boost::system::error_code err) +MediaScanner::scan(boost::system::error_code err) { if (err) return; @@ -250,9 +263,9 @@ MediaScanner::process(boost::system::error_code err) if (!_running) break; - LMS_LOG(DBUPDATER, INFO) << "Processing root directory '" << rootDirectory << "'..."; - processRootDirectory(rootDirectory, stats); - LMS_LOG(DBUPDATER, INFO) << "Processing root directory '" << rootDirectory << "' DONE"; + LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory << "'..."; + scanRootDirectory(rootDirectory, stats); + LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory << "' DONE"; } if (_running) @@ -269,9 +282,8 @@ MediaScanner::process(boost::system::error_code err) if (_running) { Setting::setTime(_db.getSession(), "last_scan", now); - Setting::setBool(_db.getSession(), "manual_scan_requested", false); - processNextJob(); + scheduleScan(); scanComplete().emit(stats); } @@ -373,12 +385,19 @@ MediaScanner::getClusters( const MetaData::Clusters& clustersNames) for (auto clusterNames : clustersNames) { ClusterType::pointer clusterType = ClusterType::getByName(_db.getSession(), clusterNames.first); + bool newType = !clusterType; + if (!clusterType) clusterType = ClusterType::create(_db.getSession(), clusterNames.first); + std::cout << "Cluster type = " << clusterType.id() << "\n"; for (auto clusterName : clusterNames.second) { - Cluster::pointer cluster = clusterType->getCluster(clusterName); + Cluster::pointer cluster; + + if (!newType) + cluster = clusterType->getCluster(clusterName); + if (!cluster) cluster = Cluster::create(_db.getSession(), clusterType, clusterName); @@ -390,7 +409,7 @@ MediaScanner::getClusters( const MetaData::Clusters& clustersNames) } void -MediaScanner::processAudioFile( const boost::filesystem::path& file, Stats& stats) +MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats) { boost::posix_time::ptime lastWriteTime (boost::posix_time::from_time_t( boost::filesystem::last_write_time( file ) ) ); @@ -601,7 +620,7 @@ MediaScanner::processAudioFile( const boost::filesystem::path& file, Stats& stat } void -MediaScanner::processRootDirectory(boost::filesystem::path rootDirectory, Stats& stats) +MediaScanner::scanRootDirectory(boost::filesystem::path rootDirectory, Stats& stats) { boost::system::error_code ec; @@ -619,7 +638,7 @@ MediaScanner::processRootDirectory(boost::filesystem::path rootDirectory, Stats& if (boost::filesystem::is_regular(path)) { if (isFileSupported(path, _fileExtensions)) - processAudioFile(path, stats ); + scanAudioFile(path, stats ); } } } diff --git a/src/scanner/MediaScanner.hpp b/src/scanner/MediaScanner.hpp index 6d319f0b..902dc29b 100644 --- a/src/scanner/MediaScanner.hpp +++ b/src/scanner/MediaScanner.hpp @@ -53,6 +53,9 @@ class MediaScanner void stop(); void restart(); + void scheduleImmediateScan(); + void reschedule(); + struct Stats { std::size_t nbNoChange = 0; // no change since last scan @@ -82,14 +85,14 @@ class MediaScanner private: // Job handling - void processNextJob(); + void scheduleScan(); void scheduleScan(boost::posix_time::time_duration duration); void scheduleScan(boost::posix_time::ptime time); // Update database (scheduled callback) - void process(boost::system::error_code ec); + void scan(boost::system::error_code ec); - void processRootDirectory( boost::filesystem::path rootDirectory, Stats& stats); + void scanRootDirectory( boost::filesystem::path rootDirectory, Stats& stats); // Helpers Database::Artist::pointer getArtist( const boost::filesystem::path& file, const std::string& name, const std::string& MBID); @@ -100,11 +103,7 @@ class MediaScanner // Audio void checkAudioFiles( Stats& stats ); void checkDuplicatedAudioFiles( Stats& stats ); - void processAudioFile( const boost::filesystem::path& file, Stats& stats); - - // Video - void checkVideoFiles( Stats& stats ); - void processVideoFile( const boost::filesystem::path& file, Stats& stats); + void scanAudioFile( const boost::filesystem::path& file, Stats& stats); bool _running; Wt::WIOService _ioService; diff --git a/src/ui/LmsApplication.cpp b/src/ui/LmsApplication.cpp index dcfd42ff..3e935f67 100644 --- a/src/ui/LmsApplication.cpp +++ b/src/ui/LmsApplication.cpp @@ -46,13 +46,13 @@ namespace skeletons { namespace UserInterface { Wt::WApplication* -LmsApplication::create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool) +LmsApplication::create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner) { /* * You could read information from the environment to decide whether * the user has permission to start a new application */ - return new LmsApplication(env, connectionPool); + return new LmsApplication(env, connectionPool, scanner); } LmsApplication* @@ -67,9 +67,10 @@ LmsApplication::instance() * constructor so it is typically also an argument for your custom * application constructor. */ -LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool) +LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner) : Wt::WApplication(env), _db(connectionPool), + _scanner(scanner), _imageResource(nullptr), _transcodeResource(nullptr) { @@ -141,6 +142,11 @@ TranscodeResource* SessionTranscodeResource() return LmsApplication::instance()->getTranscodeResource(); } +Scanner::MediaScanner& MediaScanner() +{ + return LmsApplication::instance()->getMediaScanner(); +} + void LmsApplication::goHome() { diff --git a/src/ui/LmsApplication.hpp b/src/ui/LmsApplication.hpp index de460e4e..e36fcbee 100644 --- a/src/ui/LmsApplication.hpp +++ b/src/ui/LmsApplication.hpp @@ -26,6 +26,8 @@ #include #include "database/DatabaseHandler.hpp" +#include "scanner/MediaScanner.hpp" + #include "resource/ImageResource.hpp" #include "resource/TranscodeResource.hpp" @@ -34,26 +36,29 @@ namespace UserInterface { class LmsApplication : public Wt::WApplication { public: - static Wt::WApplication *create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool); + static Wt::WApplication *create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner); static LmsApplication* instance(); - LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool); - // Session application data ImageResource* getImageResource() { return _imageResource; } TranscodeResource* getTranscodeResource() { return _transcodeResource; } Database::Handler& getDbHandler() { return _db;} + Scanner::MediaScanner& getMediaScanner() { return _scanner; } + // Utils void goHome(); private: + LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner); + void handleAuthEvent(void); void createFirstConnectionUI(); void createLmsUI(); Database::Handler _db; + Scanner::MediaScanner& _scanner; ImageResource* _imageResource; TranscodeResource* _transcodeResource; }; @@ -70,6 +75,7 @@ Database::User::pointer CurrentUser(); ImageResource *SessionImageResource(); TranscodeResource *SessionTranscodeResource(); +Scanner::MediaScanner& MediaScanner(); } // namespace UserInterface diff --git a/src/ui/settings/DatabaseView.cpp b/src/ui/settings/DatabaseView.cpp index 355b3b25..712794c8 100644 --- a/src/ui/settings/DatabaseView.cpp +++ b/src/ui/settings/DatabaseView.cpp @@ -258,13 +258,19 @@ DatabaseView::DatabaseView(Wt::WContainerWidget *parent) Wt::WPushButton *saveBtn = new Wt::WPushButton(Wt::WString::tr("msg-btn-apply")); bindWidget("apply-btn", saveBtn); - saveBtn->setStyleClass("btn-primary"); Wt::WPushButton *discardBtn = new Wt::WPushButton(Wt::WString::tr("msg-btn-discard")); bindWidget("discard-btn", discardBtn); + Wt::WPushButton *immScanBtn = new Wt::WPushButton(Wt::WString::tr("msg-btn-immediate-scan")); + bindWidget("immediate-scan-btn", immScanBtn); + saveBtn->clicked().connect(this, &DatabaseView::processSave); discardBtn->clicked().connect(this, &DatabaseView::processDiscard); + immScanBtn->clicked().connect(std::bind([=] () + { + MediaScanner().scheduleImmediateScan(); + })); updateView(_model); } @@ -285,7 +291,7 @@ DatabaseView::processSave() if (_model->validate()) { _model->saveData(); -// _sigChanged.emit(); + MediaScanner().reschedule(); } // Udate the view: Delete any validation message in the view, etc. updateView(_model);