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 @@
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);