Can now interrupt/relaunch scan + scan force from the web interface. ref #32
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,16 +21,17 @@
|
||||
|
||||
#include <Wt/WTemplate.h>
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class DatabaseStatus : public Wt::WTemplate
|
||||
namespace UserInterface
|
||||
{
|
||||
|
||||
class ScannerController : public Wt::WTemplate
|
||||
{
|
||||
public:
|
||||
DatabaseStatus();
|
||||
ScannerController();
|
||||
|
||||
private:
|
||||
void refreshContents();
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace DatabaseStatus
|
||||
|
||||
Reference in New Issue
Block a user