Made database scan work, added immediate scan option
This commit is contained in:
@@ -30,6 +30,6 @@
|
||||
<message id="msg-error-not-a-directory">Not a directory</message>
|
||||
<message id="msg-btn-apply">Apply</message>
|
||||
<message id="msg-btn-discard">Discard</message>
|
||||
|
||||
<message id="msg-btn-immediate-scan">Scan now!</message>
|
||||
|
||||
</messages>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
${apply-btn class="btn-primary"} ${discard-btn}
|
||||
${apply-btn class="btn-primary"} ${discard-btn} ${immediate-scan-btn class="btn-info"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -295,12 +295,12 @@ Cluster::pointer
|
||||
ClusterType::getCluster(std::string name) const
|
||||
{
|
||||
assert(self());
|
||||
assert(self()->id() != Wt::Dbo::dbo_traits<Release>::invalidId() );
|
||||
assert(self()->id() != Wt::Dbo::dbo_traits<ClusterType>::invalidId() );
|
||||
assert(session());
|
||||
|
||||
return session()->find<Cluster>()
|
||||
.where("name = ?").bind(name)
|
||||
.where("cluster_type_id = ").bind(self()->id());
|
||||
.where("cluster_type_id = ?").bind(self()->id());
|
||||
}
|
||||
|
||||
std::vector<Cluster::pointer>
|
||||
@@ -311,7 +311,7 @@ ClusterType::getClusters() const
|
||||
assert(session());
|
||||
|
||||
Wt::Dbo::collection<Cluster::pointer> res = session()->find<Cluster>()
|
||||
.where("cluster_type_id = ").bind(self()->id())
|
||||
.where("cluster_type_id = ?").bind(self()->id())
|
||||
.orderBy("name");
|
||||
|
||||
return std::vector<Cluster::pointer>(res.begin(), res.end());
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <Wt/Dbo/SqlConnectionPool>
|
||||
|
||||
#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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user