Can now interrupt/relaunch scan + scan force from the web interface. ref #32

This commit is contained in:
emeric
2020-07-25 14:58:02 +02:00
parent 36ccd62a84
commit d0826daa41
13 changed files with 197 additions and 154 deletions
+2 -37
View File
@@ -60,7 +60,7 @@
</div>
<div class="form-group">
<div class="col-lg-offset-3 col-lg-9">
${apply-btn class="btn-primary"} ${discard-btn} ${immediate-scan-btn class="btn-info"}
${apply-btn class="btn-primary"} ${discard-btn}
</div>
</div>
</form>
@@ -69,42 +69,7 @@
<br/>
<div class="row">
<div class="col-lg-8">
<div class="panel panel-info">
<div class="panel-heading">${tr:Lms.Admin.Database.Status.status}</div>
<div class="panel-body">
${status}
</div>
</div>
</div>
</div>
</message>
<message id="Lms.Admin.Database.Status.template">
<div class="form-horizontal">
<div class="form-group">
<label class="col-lg-3 control-label">
${tr:Lms.Admin.Database.Status.status}
</label>
<div class="col-lg-9">
<div class="well well-sm">
${status}
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">
${tr:Lms.Admin.Database.Status.last-scan}
</label>
<div class="col-lg-9">
<div class="well well-sm">
${last-scan}
</div>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-3 col-lg-9">
${btn-report class="btn"}
</div>
${scanner-controller}
</div>
</div>
</message>
+44
View File
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<messages xmlns:if="Wt.WTemplate.conditions">
<message id="Lms.Admin.ScannerController.template">
<div class="panel panel-info">
<div class="panel-heading">${tr:Lms.Admin.ScannerController.scanner}</div>
<div class="panel-body">
<div class="form-horizontal">
<div class="form-group">
<label class="col-lg-3 control-label">
${tr:Lms.Admin.ScannerController.status}
</label>
<div class="col-lg-9">
<div class="well well-sm">
${status}
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">
${tr:Lms.Admin.ScannerController.last-scan}
</label>
<div class="col-lg-9">
<div class="well well-sm">
${last-scan}
</div>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-3 col-lg-9">
${btn-report class="btn btn-xs"}
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-3 col-lg-9">
${btn-action}
</div>
</div>
</div>
</div>
</div>
</message>
</messages>
+19 -16
View File
@@ -54,22 +54,25 @@
<message id="Lms.Admin.Database.update-start-time">Update start time</message>
<message id="Lms.Admin.Database.weekly">Weekly</message>
<message id="Lms.Admin.Database.Status.bad-duration">Cannot get track duration</message>
<message id="Lms.Admin.Database.Status.cannot-parse-file">Cannot parse file</message>
<message id="Lms.Admin.Database.Status.cannot-read-file">Cannot read file</message>
<message id="Lms.Admin.Database.Status.duplicates-header">{1} duplicate files:</message>
<message id="Lms.Admin.Database.Status.errors-header">{1} errors:</message>
<message id="Lms.Admin.Database.Status.get-report">Get report</message>
<message id="Lms.Admin.Database.Status.last-scan">Last scan</message>
<message id="Lms.Admin.Database.Status.last-scan-not-available">Not available</message>
<message id="Lms.Admin.Database.Status.last-scan-status">Scanned {1} files in {2} on {3} ({4} errors, {5} duplicates)</message>
<message id="Lms.Admin.Database.Status.no-audio-track">No audio track</message>
<message id="Lms.Admin.Database.Status.same-hash">Duplicated file hash</message>
<message id="Lms.Admin.Database.Status.same-mbid">Duplicated MBID</message>
<message id="Lms.Admin.Database.Status.status">Status</message>
<message id="Lms.Admin.Database.Status.status-not-scheduled">Not scheduled</message>
<message id="Lms.Admin.Database.Status.status-scheduled">Scheduled on {1}</message>
<message id="Lms.Admin.Database.Status.status-in-progress">Scanning {1} of {2} files ({3} %)</message>
<message id="Lms.Admin.ScannerController.bad-duration">Cannot get track duration</message>
<message id="Lms.Admin.ScannerController.cannot-parse-file">Cannot parse file</message>
<message id="Lms.Admin.ScannerController.cannot-read-file">Cannot read file</message>
<message id="Lms.Admin.ScannerController.duplicates-header">{1} duplicate files:</message>
<message id="Lms.Admin.ScannerController.errors-header">{1} errors:</message>
<message id="Lms.Admin.ScannerController.force-scan-now">Force scan now</message>
<message id="Lms.Admin.ScannerController.get-report">Get report</message>
<message id="Lms.Admin.ScannerController.last-scan">Last scan</message>
<message id="Lms.Admin.ScannerController.last-scan-not-available">Not available</message>
<message id="Lms.Admin.ScannerController.last-scan-status">Scanned {1} files in {2} on {3} ({4} errors, {5} duplicates)</message>
<message id="Lms.Admin.ScannerController.no-audio-track">No audio track</message>
<message id="Lms.Admin.ScannerController.same-hash">Duplicated file hash</message>
<message id="Lms.Admin.ScannerController.same-mbid">Duplicated MBID</message>
<message id="Lms.Admin.ScannerController.scan-now">Scan now</message>
<message id="Lms.Admin.ScannerController.scanner">Scanner</message>
<message id="Lms.Admin.ScannerController.status">Status</message>
<message id="Lms.Admin.ScannerController.status-not-scheduled">Not scheduled</message>
<message id="Lms.Admin.ScannerController.status-scheduled">Scheduled on {1}</message>
<message id="Lms.Admin.ScannerController.status-in-progress">Scanning {1} of {2} files ({3} %)</message>
<!--Users-->
<message id="Lms.Admin.Users.add">New user</message>
+20 -17
View File
@@ -54,22 +54,25 @@
<message id="Lms.Admin.Database.update-start-time">Heure de départ de la mise à jour</message>
<message id="Lms.Admin.Database.weekly">Toutes les semaines</message>
<message id="Lms.Admin.Database.Status.bad-duration">Impossible de récupérer la durée de la piste</message>
<message id="Lms.Admin.Database.Status.cannot-parse-file">Impossible d'analyser le fichier</message>
<message id="Lms.Admin.Database.Status.cannot-read-file">Impossible de lire le fichier</message>
<message id="Lms.Admin.Database.Status.duplicates-header">{1} fichiers dupliqués :</message>
<message id="Lms.Admin.Database.Status.errors-header">{1} erreurs :</message>
<message id="Lms.Admin.Database.Status.get-report">Rapport</message>
<message id="Lms.Admin.Database.Status.last-scan">Dernier scan</message>
<message id="Lms.Admin.Database.Status.last-scan-not-available">Non disponible</message>
<message id="Lms.Admin.Database.Status.last-scan-status">{1} fichiers scannés en {2} le {3} ({4} erreurs, {5} duplicatas)</message>
<message id="Lms.Admin.Database.Status.no-audio-track">Pas de piste audio</message>
<message id="Lms.Admin.Database.Status.same-hash">Hash dupliqué</message>
<message id="Lms.Admin.Database.Status.same-mbid">MBID dupliqué</message>
<message id="Lms.Admin.Database.Status.status">Statut</message>
<message id="Lms.Admin.Database.Status.status-not-scheduled">Non planifié</message>
<message id="Lms.Admin.Database.Status.status-scheduled">Planifié le {1}</message>
<message id="Lms.Admin.Database.Status.status-in-progress">Scan de {1} fichiers sur {2} ({3} %)</message>
<message id="Lms.Admin.ScannerController.bad-duration">Impossible de récupérer la durée de la piste</message>
<message id="Lms.Admin.ScannerController.cannot-parse-file">Impossible d'analyser le fichier</message>
<message id="Lms.Admin.ScannerController.cannot-read-file">Impossible de lire le fichier</message>
<message id="Lms.Admin.ScannerController.duplicates-header">{1} fichiers dupliqués :</message>
<message id="Lms.Admin.ScannerController.errors-header">{1} erreurs :</message>
<message id="Lms.Admin.ScannerController.force-scan-now">Lancer un scan forcé</message>
<message id="Lms.Admin.ScannerController.get-report">Rapport</message>
<message id="Lms.Admin.ScannerController.last-scan">Dernier scan</message>
<message id="Lms.Admin.ScannerController.last-scan-not-available">Non disponible</message>
<message id="Lms.Admin.ScannerController.last-scan-status">{1} fichiers scannés en {2} le {3} ({4} erreurs, {5} duplicatas)</message>
<message id="Lms.Admin.ScannerController.no-audio-track">Pas de piste audio</message>
<message id="Lms.Admin.ScannerController.same-hash">Hash dupliqué</message>
<message id="Lms.Admin.ScannerController.same-mbid">MBID dupliqué</message>
<message id="Lms.Admin.ScannerController.scan-now">Lancer un scan</message>
<message id="Lms.Admin.ScannerController.scanner">Scanner</message>
<message id="Lms.Admin.ScannerController.status">Statut</message>
<message id="Lms.Admin.ScannerController.status-not-scheduled">Non planifié</message>
<message id="Lms.Admin.ScannerController.status-scheduled">Planifié le {1}</message>
<message id="Lms.Admin.ScannerController.status-in-progress">Scan de {1} fichiers sur {2} ({3} %)</message>
<!--Users-->
<message id="Lms.Admin.Users.add">Ajouter</message>
@@ -132,7 +135,7 @@
<message id="Lms.Explore.Artists.linktype-releaseartist">Artistes d'album</message>
<!--Explore:Release-->
<message id="Lms.Explore.ReleaseInfo.similar-releases">Albums similaires</message>
<message id="Lms.Explore.Release.similar-releases">Albums similaires</message>
<message id="Lms.Explore.Release.copyright">Copyright</message>
<message id="Lms.Explore.Release.disc">Disque {1}</message>
+41 -36
View File
@@ -258,48 +258,50 @@ MediaScanner::MediaScanner(Database::Db& db)
MediaScanner::~MediaScanner()
{
if (_running)
stop();
}
void
MediaScanner::restart(void)
MediaScanner::start()
{
stop();
start();
}
void
MediaScanner::start(void)
{
_running = true;
scheduleNextScan();
_ioService.start();
}
void
MediaScanner::stop(void)
MediaScanner::stop()
{
_running = false;
_abortScan = true;
_scheduleTimer.cancel();
_ioService.stop();
}
void
MediaScanner::requestImmediateScan()
MediaScanner::abortScan()
{
_abortScan = true;
_scheduleTimer.cancel();
_ioService.stop();
_abortScan = false;
_ioService.start();
}
void
MediaScanner::requestImmediateScan(bool force)
{
abortScan();
_ioService.post([=]()
{
scheduleScan();
scheduleScan(force);
});
}
void
MediaScanner::requestReschedule()
MediaScanner::requestReload()
{
_ioService.post([=]()
{
@@ -308,7 +310,7 @@ MediaScanner::requestReschedule()
}
MediaScanner::Status
MediaScanner::getStatus()
MediaScanner::getStatus() const
{
Status res;
@@ -365,7 +367,7 @@ MediaScanner::scheduleNextScan()
if (nextScanDate.isValid())
{
nextScanDateTime = Wt::WDateTime {nextScanDate, _startTime};
scheduleScan(nextScanDateTime);
scheduleScan(false, nextScanDateTime);
}
{
@@ -384,7 +386,7 @@ MediaScanner::countAllFiles(ScanStats& stats)
exploreFilesRecursive(_mediaDirectory, [&](std::error_code ec, const std::filesystem::path& path)
{
if (!_running)
if (_abortScan)
return false;
if (!ec && isFileSupported(path, _fileExtensions))
@@ -398,13 +400,21 @@ MediaScanner::countAllFiles(ScanStats& stats)
}
void
MediaScanner::scheduleScan(const Wt::WDateTime& dateTime)
MediaScanner::scheduleScan(bool force, const Wt::WDateTime& dateTime)
{
auto cb {[=](boost::system::error_code ec)
{
if (ec)
return;
scan(force);
}};
if (dateTime.isNull())
{
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan right now";
_scheduleTimer.expires_from_now(std::chrono::seconds(0));
_scheduleTimer.async_wait(std::bind(&MediaScanner::scan, this, std::placeholders::_1));
_scheduleTimer.expires_from_now(std::chrono::seconds {0});
_scheduleTimer.async_wait(cb);
}
else
{
@@ -413,16 +423,13 @@ MediaScanner::scheduleScan(const Wt::WDateTime& dateTime)
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan at " << std::string(std::ctime(&t));
_scheduleTimer.expires_at(timePoint);
_scheduleTimer.async_wait(std::bind(&MediaScanner::scan, this, std::placeholders::_1));
_scheduleTimer.async_wait(cb);
}
}
void
MediaScanner::scan(boost::system::error_code err)
MediaScanner::scan(bool forceScan)
{
if (err)
return;
scanStarted().emit();
{
@@ -438,8 +445,6 @@ MediaScanner::scan(boost::system::error_code err)
refreshScanSettings();
bool forceScan {false};
LMS_LOG(DBUPDATER, DEBUG) << "Counting files in media directory '" << _mediaDirectory.string() << "'...";
countAllFiles(stats);
LMS_LOG(DBUPDATER, DEBUG) << "-> Nb files = " << stats.filesToScan;
@@ -454,17 +459,17 @@ MediaScanner::scan(boost::system::error_code err)
removeOrphanEntries();
if (_running)
if (!_abortScan)
checkDuplicatedAudioFiles(stats);
// Now update all the track features if needed
fetchTrackFeatures(stats);
LMS_LOG(DBUPDATER, INFO) << "Scan " << (_running ? "complete" : "aborted") << ". Changes = " << stats.nbChanges() << " (added = " << stats.additions << ", removed = " << stats.deletions << ", updated = " << stats.updates << "), Not changed = " << stats.skips << ", Scanned = " << stats.scans << " (errors = " << stats.errors.size() << "), features fetched = " << stats.featuresFetched << "/" << stats.featuresToFetch <<", duplicates = " << stats.duplicates.size();
LMS_LOG(DBUPDATER, INFO) << "Scan " << (_abortScan ? "aborted" : "complete") << ". Changes = " << stats.nbChanges() << " (added = " << stats.additions << ", removed = " << stats.deletions << ", updated = " << stats.updates << "), Not changed = " << stats.skips << ", Scanned = " << stats.scans << " (errors = " << stats.errors.size() << "), features fetched = " << stats.featuresFetched << "/" << stats.featuresToFetch <<", duplicates = " << stats.duplicates.size();
_dbSession.optimize();
if (_running)
if (!_abortScan)
{
stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC();
{
@@ -546,7 +551,7 @@ MediaScanner::fetchTrackFeatures(ScanStats& stats)
for (const TrackInfo& trackToFetch : tracksToFetch)
{
if (!_running)
if (_abortScan)
return;
if (fetchTrackFeatures(trackToFetch.id, trackToFetch.mbid))
@@ -774,7 +779,7 @@ MediaScanner::scanMediaDirectory(const std::filesystem::path& mediaDirectory, bo
{
exploreFilesRecursive(mediaDirectory, [&](std::error_code ec, const std::filesystem::path& path)
{
if (!_running)
if (_abortScan)
return false;
if (ec)
@@ -861,7 +866,7 @@ MediaScanner::removeMissingTracks(ScanStats& stats)
for (const auto& [trackId, trackPath] : trackPaths)
{
if (!_running)
if (_abortScan)
return;
if (!checkFile(trackPath, _mediaDirectory, _fileExtensions))
+11 -9
View File
@@ -52,12 +52,10 @@ class MediaScanner : public IMediaScanner
void start() override;
void stop() override;
void restart() override;
void requestReload() override;
void requestImmediateScan(bool force) override;
void requestImmediateScan() override;
void requestReschedule() override ;
Status getStatus() override;
Status getStatus() const override;
Wt::Signal<>& scanStarted() override { return _sigScanStarted; }
Wt::Signal<>& scanComplete() override { return _sigScanComplete; }
@@ -68,10 +66,12 @@ class MediaScanner : public IMediaScanner
// Job handling
void scheduleNextScan();
void scheduleScan(const Wt::WDateTime& dateTime = {});
void scheduleScan(bool force, const Wt::WDateTime& dateTime = {});
void abortScan();
// Update database (scheduled callback)
void scan(boost::system::error_code ec);
void scan(bool force);
void scanMediaDirectory( const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats);
bool fetchTrackFeatures(Database::IdType trackId, const UUID& MBID);
@@ -89,7 +89,9 @@ class MediaScanner : public IMediaScanner
void notifyInProgressIfNeeded(const ScanStats& stats);
void notifyInProgress(const ScanStats& stats);
bool _running {};
std::mutex _scanInProgress {};
std::atomic<bool> _abortScan {};
Wt::WIOService _ioService;
boost::asio::system_timer _scheduleTimer {_ioService};
Wt::Signal<> _sigScanStarted;
@@ -100,7 +102,7 @@ class MediaScanner : public IMediaScanner
Database::Session _dbSession;
std::unique_ptr<MetaData::IParser> _metadataParser;
std::shared_mutex _statusMutex;
mutable std::shared_mutex _statusMutex;
State _curState {State::NotScheduled};
std::optional<ScanStats> _lastCompleteScanStats;
std::optional<ScanProgressStats> _inProgressScanStats;
@@ -40,11 +40,10 @@ class IMediaScanner
virtual void start() = 0;
virtual void stop() = 0;
virtual void restart() = 0;
// Async requests
virtual void requestImmediateScan() = 0;
virtual void requestReschedule() = 0;
virtual void requestReload() = 0;
virtual void requestImmediateScan(bool force) = 0;
enum class State
@@ -62,7 +61,7 @@ class IMediaScanner
std::optional<ScanProgressStats> inProgressScanStats;
};
virtual Status getStatus() = 0;
virtual Status getStatus() const = 0;
// Called just after scan start
virtual Wt::Signal<>& scanStarted() = 0;
+1 -1
View File
@@ -54,7 +54,7 @@ namespace API::Subsonic::Scan
Response
handleStartScan(RequestContext& context)
{
ServiceProvider<IMediaScanner>::get()->requestImmediateScan();
ServiceProvider<IMediaScanner>::get()->requestImmediateScan(false);
Response response {Response::createOkResponse(context)};
response.addNode("scanStatus", createStatusResponseNode());
+1 -1
View File
@@ -10,7 +10,7 @@ add_executable(lms
ui/SettingsView.cpp
ui/TrackStringUtils.cpp
ui/admin/DatabaseSettingsView.cpp
ui/admin/DatabaseStatus.cpp
ui/admin/ScannerController.cpp
ui/admin/InitWizardView.cpp
ui/admin/UserView.cpp
ui/admin/UsersView.cpp
+1
View File
@@ -123,6 +123,7 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env,
// Add a resource bundle
messageResourceBundle().use(appRoot() + "admin-database");
messageResourceBundle().use(appRoot() + "admin-initwizard");
messageResourceBundle().use(appRoot() + "admin-scannercontroller");
messageResourceBundle().use(appRoot() + "admin-user");
messageResourceBundle().use(appRoot() + "admin-users");
messageResourceBundle().use(appRoot() + "artist");
+4 -4
View File
@@ -34,7 +34,7 @@
#include "common/Validators.hpp"
#include "common/ValueStringModel.hpp"
#include "DatabaseStatus.hpp"
#include "ScannerController.hpp"
#include "LmsApplication.hpp"
namespace UserInterface {
@@ -220,7 +220,7 @@ DatabaseSettingsView::refreshView()
Wt::WPushButton *discardBtn = t->bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")));
Wt::WPushButton *immScanBtn = t->bindWidget("immediate-scan-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.Admin.Database.immediate-scan")));
t->bindNew<DatabaseStatus>("status");
t->bindNew<ScannerController>("scanner-controller");
saveBtn->clicked().connect([=] ()
{
@@ -230,7 +230,7 @@ DatabaseSettingsView::refreshView()
{
model->saveData();
ServiceProvider<Scanner::IMediaScanner>::get()->requestReschedule();
ServiceProvider<Scanner::IMediaScanner>::get()->requestReload();
LmsApp->notifyMsg(MsgType::Success, Wt::WString::tr("Lms.Admin.Database.settings-saved"));
}
@@ -247,7 +247,7 @@ DatabaseSettingsView::refreshView()
immScanBtn->clicked().connect([=] ()
{
ServiceProvider<Scanner::IMediaScanner>::get()->requestImmediateScan();
ServiceProvider<Scanner::IMediaScanner>::get()->requestImmediateScan(false);
});
t->updateView(model.get());
@@ -17,14 +17,16 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DatabaseStatus.hpp"
#include "ScannerController.hpp"
#include <iomanip>
#include <Wt/Http/Response.h>
#include <Wt/WDateTime.h>
#include <Wt/WPopupMenu.h>
#include <Wt/WPushButton.h>
#include <Wt/WResource.h>
#include <Wt/WSplitButton.h>
#include "utils/Service.hpp"
#include "LmsApplication.hpp"
@@ -66,7 +68,7 @@ class ReportResource : public Wt::WResource
void handleRequest(const Wt::Http::Request&, Wt::Http::Response& response)
{
response.out() << Wt::WString::tr("Lms.Admin.Database.Status.errors-header").arg(_stats.errors.size()).toUTF8() << std::endl;
response.out() << Wt::WString::tr("Lms.Admin.ScannerController.errors-header").arg(_stats.errors.size()).toUTF8() << std::endl;
for (const auto& error : _stats.errors)
{
@@ -78,7 +80,7 @@ class ReportResource : public Wt::WResource
response.out() << std::endl;
response.out() << Wt::WString::tr("Lms.Admin.Database.Status.duplicates-header").arg(_stats.duplicates.size()).toUTF8() << std::endl;
response.out() << Wt::WString::tr("Lms.Admin.ScannerController.duplicates-header").arg(_stats.duplicates.size()).toUTF8() << std::endl;
for (const auto& duplicate : _stats.duplicates)
response.out() << duplicate.file.string() << " - " << duplicateReasonToWString(duplicate.reason).toUTF8() << std::endl;
@@ -90,10 +92,10 @@ class ReportResource : public Wt::WResource
{
switch (error)
{
case Scanner::ScanErrorType::CannotReadFile: return Wt::WString::tr("Lms.Admin.Database.Status.cannot-read-file");
case Scanner::ScanErrorType::CannotParseFile: return Wt::WString::tr("Lms.Admin.Database.Status.cannot-parse-file");
case Scanner::ScanErrorType::NoAudioTrack: return Wt::WString::tr("Lms.Admin.Database.Status.no-audio-track");
case Scanner::ScanErrorType::BadDuration: return Wt::WString::tr("Lms.Admin.Database.Status.bad-duration");
case Scanner::ScanErrorType::CannotReadFile: return Wt::WString::tr("Lms.Admin.ScannerController.cannot-read-file");
case Scanner::ScanErrorType::CannotParseFile: return Wt::WString::tr("Lms.Admin.ScannerController.cannot-parse-file");
case Scanner::ScanErrorType::NoAudioTrack: return Wt::WString::tr("Lms.Admin.ScannerController.no-audio-track");
case Scanner::ScanErrorType::BadDuration: return Wt::WString::tr("Lms.Admin.ScannerController.bad-duration");
}
return "?";
}
@@ -102,8 +104,8 @@ class ReportResource : public Wt::WResource
{
switch (reason)
{
case Scanner::DuplicateReason::SameHash: return Wt::WString::tr("Lms.Admin.Database.Status.same-hash");
case Scanner::DuplicateReason::SameMBID: return Wt::WString::tr("Lms.Admin.Database.Status.same-mbid");
case Scanner::DuplicateReason::SameHash: return Wt::WString::tr("Lms.Admin.ScannerController.same-hash");
case Scanner::DuplicateReason::SameMBID: return Wt::WString::tr("Lms.Admin.ScannerController.same-mbid");
}
return "?";
}
@@ -112,8 +114,8 @@ class ReportResource : public Wt::WResource
};
DatabaseStatus::DatabaseStatus()
: WTemplate {Wt::WString::tr("Lms.Admin.Database.Status.template")}
ScannerController::ScannerController()
: WTemplate {Wt::WString::tr("Lms.Admin.ScannerController.template")}
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
@@ -133,16 +135,34 @@ DatabaseStatus::DatabaseStatus()
}
void
DatabaseStatus::refreshContents()
ScannerController::refreshContents()
{
using namespace Scanner;
Wt::WPushButton* reportBtn {bindNew<Wt::WPushButton>("btn-report", Wt::WString::tr("Lms.Admin.Database.Status.get-report"))};
Wt::WPushButton* reportBtn {bindNew<Wt::WPushButton>("btn-report", Wt::WString::tr("Lms.Admin.ScannerController.get-report"))};
Wt::WSplitButton* actionBtn {bindNew<Wt::WSplitButton>("btn-action")};
actionBtn->actionButton()->addStyleClass("btn-primary");
actionBtn->actionButton()->setText(Wt::WString::tr("Lms.Admin.ScannerController.scan-now"));
actionBtn->actionButton()->clicked().connect([]
{
ServiceProvider<Scanner::IMediaScanner>::get()->requestImmediateScan(false);
});
auto popup = std::make_unique<Wt::WPopupMenu>();
popup->addItem(Wt::WString::tr("Lms.Admin.ScannerController.force-scan-now"));
popup->itemSelected().connect([]
{
ServiceProvider<Scanner::IMediaScanner>::get()->requestImmediateScan(true);
});
actionBtn->dropDownButton()->setMenu(std::move(popup));
actionBtn->dropDownButton()->addStyleClass("btn-primary");
const IMediaScanner::Status status {ServiceProvider<IMediaScanner>::get()->getStatus()};
if (status.lastCompleteScanStats)
{
bindString("last-scan", Wt::WString::tr("Lms.Admin.Database.Status.last-scan-status")
bindString("last-scan", Wt::WString::tr("Lms.Admin.ScannerController.last-scan-status")
.arg(status.lastCompleteScanStats->nbFiles())
.arg(durationToString(status.lastCompleteScanStats->startTime, status.lastCompleteScanStats->stopTime))
.arg(status.lastCompleteScanStats->stopTime.toString())
@@ -156,7 +176,7 @@ DatabaseStatus::refreshContents()
}
else
{
bindString("last-scan", Wt::WString::tr("Lms.Admin.Database.Status.last-scan-not-available"));
bindString("last-scan", Wt::WString::tr("Lms.Admin.ScannerController.last-scan-not-available"));
reportBtn->setEnabled(false);
}
@@ -164,16 +184,16 @@ DatabaseStatus::refreshContents()
switch (status.currentState)
{
case IMediaScanner::State::NotScheduled:
bindString("status", Wt::WString::tr("Lms.Admin.Database.Status.status-not-scheduled"));
bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-not-scheduled"));
break;
case IMediaScanner::State::Scheduled:
bindString("status", Wt::WString::tr("Lms.Admin.Database.Status.status-scheduled")
bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-scheduled")
.arg(status.nextScheduledScan.toString()));
break;
case IMediaScanner::State::InProgress:
{
std::ostringstream oss;
bindString("status", Wt::WString::tr("Lms.Admin.Database.Status.status-in-progress")
bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-in-progress")
.arg(status.inProgressScanStats->processedFiles)
.arg(status.inProgressScanStats->filesToScan)
.arg(status.inProgressScanStats->progress()));
@@ -21,12 +21,13 @@
#include <Wt/WTemplate.h>
namespace UserInterface {
namespace UserInterface
{
class DatabaseStatus : public Wt::WTemplate
class ScannerController : public Wt::WTemplate
{
public:
DatabaseStatus();
ScannerController();
private:
void refreshContents();