diff --git a/src/libs/scanner/CMakeLists.txt b/src/libs/scanner/CMakeLists.txt
index 4f5fa30b..5f1467b8 100644
--- a/src/libs/scanner/CMakeLists.txt
+++ b/src/libs/scanner/CMakeLists.txt
@@ -1,8 +1,8 @@
add_library(lmsscanner SHARED
impl/AcousticBrainzUtils.cpp
- impl/MediaScanner.cpp
- impl/MediaScannerStats.cpp
+ impl/Scanner.cpp
+ impl/ScannerStats.cpp
)
target_include_directories(lmsscanner INTERFACE
diff --git a/src/libs/scanner/impl/MediaScanner.cpp b/src/libs/scanner/impl/Scanner.cpp
similarity index 94%
rename from src/libs/scanner/impl/MediaScanner.cpp
rename to src/libs/scanner/impl/Scanner.cpp
index 5f3660f2..5c1b61b5 100644
--- a/src/libs/scanner/impl/MediaScanner.cpp
+++ b/src/libs/scanner/impl/Scanner.cpp
@@ -17,7 +17,7 @@
* along with LMS. If not, see .
*/
-#include "MediaScanner.hpp"
+#include "Scanner.hpp"
#include
#include
@@ -242,13 +242,13 @@ getOrCreateClusters(Session& session, const MetaData::Clusters& clustersNames)
namespace Scanner {
-std::unique_ptr
-createMediaScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine)
+std::unique_ptr
+createScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine)
{
- return std::make_unique(db, recommendationEngine);
+ return std::make_unique(db, recommendationEngine);
}
-MediaScanner::MediaScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine)
+Scanner::Scanner(Database::Db& db, Recommendation::IEngine& recommendationEngine)
: _recommendationEngine {recommendationEngine}
, _dbSession {db}
{
@@ -262,14 +262,14 @@ MediaScanner::MediaScanner(Database::Db& db, Recommendation::IEngine& recommenda
start();
}
-MediaScanner::~MediaScanner()
+Scanner::~Scanner()
{
- LMS_LOG(DBUPDATER, INFO) << "Shutting down MediaScanner...";
+ LMS_LOG(DBUPDATER, INFO) << "Shutting down Scanner...";
stop();
}
void
-MediaScanner::start()
+Scanner::start()
{
std::scoped_lock lock {_controlMutex};
@@ -290,7 +290,7 @@ MediaScanner::start()
}
void
-MediaScanner::stop()
+Scanner::stop()
{
std::scoped_lock lock {_controlMutex};
@@ -301,7 +301,7 @@ MediaScanner::stop()
}
void
-MediaScanner::abortScan()
+Scanner::abortScan()
{
LMS_LOG(DBUPDATER, DEBUG) << "Aborting scan...";
std::scoped_lock lock {_controlMutex};
@@ -319,7 +319,7 @@ MediaScanner::abortScan()
}
void
-MediaScanner::requestImmediateScan(bool force)
+Scanner::requestImmediateScan(bool force)
{
abortScan();
_ioService.post([=]()
@@ -332,7 +332,7 @@ MediaScanner::requestImmediateScan(bool force)
}
void
-MediaScanner::requestReload()
+Scanner::requestReload()
{
abortScan();
_ioService.post([=]()
@@ -344,8 +344,8 @@ MediaScanner::requestReload()
});
}
-MediaScanner::Status
-MediaScanner::getStatus() const
+Scanner::Status
+Scanner::getStatus() const
{
Status res;
@@ -360,7 +360,7 @@ MediaScanner::getStatus() const
}
void
-MediaScanner::scheduleNextScan()
+Scanner::scheduleNextScan()
{
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan";
@@ -411,11 +411,11 @@ MediaScanner::scheduleNextScan()
_nextScheduledScan = nextScanDateTime;
}
- _sigScheduled.emit(_nextScheduledScan);
+ _events.scanScheduled.emit(_nextScheduledScan);
}
void
-MediaScanner::countAllFiles(ScanStats& stats)
+Scanner::countAllFiles(ScanStats& stats)
{
ScanStepStats stepStats{stats.startTime, ScanProgressStep::DiscoveringFiles};
@@ -440,7 +440,7 @@ MediaScanner::countAllFiles(ScanStats& stats)
}
void
-MediaScanner::scheduleScan(bool force, const Wt::WDateTime& dateTime)
+Scanner::scheduleScan(bool force, const Wt::WDateTime& dateTime)
{
auto cb {[=](boost::system::error_code ec)
{
@@ -469,9 +469,9 @@ MediaScanner::scheduleScan(bool force, const Wt::WDateTime& dateTime)
}
void
-MediaScanner::scan(bool forceScan)
+Scanner::scan(bool forceScan)
{
- scanStarted().emit();
+ _events.scanStarted.emit();
{
std::unique_lock lock {_statusMutex};
@@ -517,14 +517,14 @@ MediaScanner::scan(bool forceScan)
{
std::unique_lock lock {_statusMutex};
- _lastCompleteScanStats = std::move(stats);
+ _lastCompleteScanStats = stats;
_currentScanStepStats.reset();
}
LMS_LOG(DBUPDATER, DEBUG) << "Scan not aborted, scheduling next scan!";
scheduleNextScan();
- scanComplete().emit();
+ _events.scanComplete.emit(stats);
}
else
{
@@ -538,7 +538,7 @@ MediaScanner::scan(bool forceScan)
}
bool
-MediaScanner::fetchTrackFeatures(Database::IdType trackId, const UUID& MBID)
+Scanner::fetchTrackFeatures(Database::IdType trackId, const UUID& MBID)
{
std::map features;
@@ -564,7 +564,7 @@ MediaScanner::fetchTrackFeatures(Database::IdType trackId, const UUID& MBID)
}
void
-MediaScanner::fetchTrackFeatures(ScanStats& stats)
+Scanner::fetchTrackFeatures(ScanStats& stats)
{
if (_recommendationEngineType != ScanSettings::RecommendationEngineType::Features)
return;
@@ -614,7 +614,7 @@ MediaScanner::fetchTrackFeatures(ScanStats& stats)
}
void
-MediaScanner::refreshScanSettings()
+Scanner::refreshScanSettings()
{
auto transaction {_dbSession.createSharedTransaction()};
@@ -646,7 +646,7 @@ MediaScanner::refreshScanSettings()
}
void
-MediaScanner::notifyInProgress(const ScanStepStats& stepStats)
+Scanner::notifyInProgress(const ScanStepStats& stepStats)
{
{
std::unique_lock lock {_statusMutex};
@@ -654,12 +654,12 @@ MediaScanner::notifyInProgress(const ScanStepStats& stepStats)
}
const std::chrono::system_clock::time_point now {std::chrono::system_clock::now()};
- _sigScanInProgress(stepStats);
+ _events.scanInProgress(stepStats);
_lastScanInProgressEmit = now;
}
void
-MediaScanner::notifyInProgressIfNeeded(const ScanStepStats& stepStats)
+Scanner::notifyInProgressIfNeeded(const ScanStepStats& stepStats)
{
std::chrono::system_clock::time_point now {std::chrono::system_clock::now()};
@@ -668,7 +668,7 @@ MediaScanner::notifyInProgressIfNeeded(const ScanStepStats& stepStats)
}
void
-MediaScanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanStats& stats)
+Scanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanStats& stats)
{
Wt::WDateTime lastWriteTime;
try
@@ -831,7 +831,7 @@ MediaScanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, S
}
void
-MediaScanner::scanMediaDirectory(const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats)
+Scanner::scanMediaDirectory(const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats)
{
ScanStepStats stepStats{stats.startTime, ScanProgressStep::ScanningFiles};
stepStats.totalElems = stats.filesScanned;
@@ -899,7 +899,7 @@ checkFile(const std::filesystem::path& p, const std::filesystem::path& mediaDire
}
void
-MediaScanner::removeMissingTracks(ScanStats& stats)
+Scanner::removeMissingTracks(ScanStats& stats)
{
static constexpr std::size_t batchSize {50};
@@ -966,7 +966,7 @@ MediaScanner::removeMissingTracks(ScanStats& stats)
}
void
-MediaScanner::removeOrphanEntries()
+Scanner::removeOrphanEntries()
{
LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan clusters...";
{
@@ -1009,7 +1009,7 @@ MediaScanner::removeOrphanEntries()
}
void
-MediaScanner::checkDuplicatedAudioFiles(ScanStats& stats)
+Scanner::checkDuplicatedAudioFiles(ScanStats& stats)
{
LMS_LOG(DBUPDATER, INFO) << "Checking duplicated audio files";
@@ -1029,7 +1029,7 @@ MediaScanner::checkDuplicatedAudioFiles(ScanStats& stats)
}
void
-MediaScanner::reloadSimilarityEngine(ScanStats& stats)
+Scanner::reloadSimilarityEngine(ScanStats& stats)
{
ScanStepStats stepStats {stats.startTime, ScanProgressStep::ReloadingSimilarityEngine};
diff --git a/src/libs/scanner/impl/MediaScanner.hpp b/src/libs/scanner/impl/Scanner.hpp
similarity index 78%
rename from src/libs/scanner/impl/MediaScanner.hpp
rename to src/libs/scanner/impl/Scanner.hpp
index 6a78b86c..43a8b964 100644
--- a/src/libs/scanner/impl/MediaScanner.hpp
+++ b/src/libs/scanner/impl/Scanner.hpp
@@ -33,7 +33,7 @@
#include "database/ScanSettings.hpp"
#include "database/Session.hpp"
#include "metadata/IParser.hpp"
-#include "scanner/IMediaScanner.hpp"
+#include "scanner/IScanner.hpp"
class UUID;
@@ -44,29 +44,24 @@ namespace Recommendation
namespace Scanner {
-class MediaScanner : public IMediaScanner
+class Scanner : public IScanner
{
public:
- MediaScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine);
- ~MediaScanner();
+ Scanner(Database::Db& db, Recommendation::IEngine& recommendationEngine);
+ ~Scanner();
- MediaScanner(const MediaScanner&) = delete;
- MediaScanner(MediaScanner&&) = delete;
- MediaScanner& operator=(const MediaScanner&) = delete;
- MediaScanner& operator=(MediaScanner&&) = delete;
+ Scanner(const Scanner&) = delete;
+ Scanner(Scanner&&) = delete;
+ Scanner& operator=(const Scanner&) = delete;
+ Scanner& operator=(Scanner&&) = delete;
void requestReload() override;
void requestImmediateScan(bool force) override;
- Status getStatus() const override;
-
- Wt::Signal<>& scanStarted() override { return _sigScanStarted; }
- Wt::Signal<>& scanComplete() override { return _sigScanComplete; }
- Wt::Signal& scanInProgress() override { return _sigScanInProgress; }
- Wt::Signal& scheduled() override { return _sigScheduled; }
+ Status getStatus() const override;
+ Events& getEvents() override { return _events; }
private:
-
void start();
void stop();
@@ -102,11 +97,8 @@ class MediaScanner : public IMediaScanner
std::atomic _abortScan {};
Wt::WIOService _ioService;
boost::asio::system_timer _scheduleTimer {_ioService};
- Wt::Signal<> _sigScanStarted;
- Wt::Signal<> _sigScanComplete;
- Wt::Signal _sigScanInProgress;
+ Events _events;
std::chrono::system_clock::time_point _lastScanInProgressEmit {};
- Wt::Signal _sigScheduled;
Database::Session _dbSession;
std::unique_ptr _metadataParser;
@@ -124,7 +116,7 @@ class MediaScanner : public IMediaScanner
std::filesystem::path _mediaDirectory;
Database::ScanSettings::RecommendationEngineType _recommendationEngineType;
-}; // class MediaScanner
+}; // class Scanner
} // Scanner
diff --git a/src/libs/scanner/impl/MediaScannerStats.cpp b/src/libs/scanner/impl/ScannerStats.cpp
similarity index 96%
rename from src/libs/scanner/impl/MediaScannerStats.cpp
rename to src/libs/scanner/impl/ScannerStats.cpp
index 17450156..afc12f99 100644
--- a/src/libs/scanner/impl/MediaScannerStats.cpp
+++ b/src/libs/scanner/impl/ScannerStats.cpp
@@ -17,7 +17,7 @@
* along with LMS. If not, see .
*/
-#include "scanner/MediaScannerStats.hpp"
+#include "scanner/ScannerStats.hpp"
namespace Scanner {
diff --git a/src/libs/scanner/include/scanner/IMediaScanner.hpp b/src/libs/scanner/include/scanner/IMediaScanner.hpp
deleted file mode 100644
index ec8f64d5..00000000
--- a/src/libs/scanner/include/scanner/IMediaScanner.hpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2013 Emeric Poupon
- *
- * This file is part of LMS.
- *
- * LMS is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * LMS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with LMS. If not, see .
- */
-
-#pragma once
-
-#include
-
-#include
-#include
-
-#include "MediaScannerStats.hpp"
-
-namespace Database
-{
- class Db;
-}
-
-namespace Recommendation
-{
- class IEngine;
-}
-
-namespace Scanner {
-
-class IMediaScanner
-{
- public:
- virtual ~IMediaScanner() = default;
-
- // Async requests
- virtual void requestReload() = 0;
- virtual void requestImmediateScan(bool force) = 0;
-
- enum class State
- {
- NotScheduled,
- Scheduled,
- InProgress,
- };
-
- struct Status
- {
- State currentState {State::NotScheduled};
- Wt::WDateTime nextScheduledScan;
- std::optional lastCompleteScanStats;
- std::optional currentScanStepStats;
- };
-
- virtual Status getStatus() const = 0;
-
- // Called just after scan start
- virtual Wt::Signal<>& scanStarted() = 0;
-
- // Called just after scan complete
- virtual Wt::Signal<>& scanComplete() = 0;
-
- // Called during scan in progress
- virtual Wt::Signal& scanInProgress() = 0;
-
- // Called after a schedule
- virtual Wt::Signal& scheduled() = 0;
-
-};
-
-std::unique_ptr createMediaScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine);
-
-} // Scanner
-
diff --git a/src/libs/scanner/include/scanner/IScanner.hpp b/src/libs/scanner/include/scanner/IScanner.hpp
new file mode 100644
index 00000000..018cca98
--- /dev/null
+++ b/src/libs/scanner/include/scanner/IScanner.hpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 Emeric Poupon
+ *
+ * This file is part of LMS.
+ *
+ * LMS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LMS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LMS. If not, see .
+ */
+
+#pragma once
+
+#include
+
+#include "ScannerEvents.hpp"
+#include "ScannerStats.hpp"
+
+namespace Database
+{
+ class Db;
+}
+
+namespace Recommendation
+{
+ class IEngine;
+}
+
+namespace Scanner
+{
+
+ class IScanner
+ {
+ public:
+ virtual ~IScanner() = default;
+
+ // Async requests
+ virtual void requestReload() = 0;
+ virtual void requestImmediateScan(bool force) = 0;
+
+ enum class State
+ {
+ NotScheduled,
+ Scheduled,
+ InProgress,
+ };
+
+ struct Status
+ {
+ State currentState {State::NotScheduled};
+ Wt::WDateTime nextScheduledScan;
+ std::optional lastCompleteScanStats;
+ std::optional currentScanStepStats;
+ };
+
+ virtual Status getStatus() const = 0;
+
+ virtual Events& getEvents() = 0;
+ };
+
+ std::unique_ptr createScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine);
+
+} // Scanner
+
diff --git a/src/libs/scanner/include/scanner/ScannerEvents.hpp b/src/libs/scanner/include/scanner/ScannerEvents.hpp
new file mode 100644
index 00000000..c380bba6
--- /dev/null
+++ b/src/libs/scanner/include/scanner/ScannerEvents.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 Emeric Poupon
+ *
+ * This file is part of LMS.
+ *
+ * LMS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LMS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LMS. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "ScannerStats.hpp"
+
+namespace Scanner
+{
+
+ struct Events
+ {
+ // Called just after scan start
+ Wt::Signal<> scanStarted;
+
+ // Called just after scan complete (true if changes have been made)
+ Wt::Signal scanComplete;
+
+ // Called during scan in progress
+ Wt::Signal scanInProgress;
+
+ // Called after a schedule
+ Wt::Signal scanScheduled;
+ };
+
+} // ns Scanner
+
diff --git a/src/libs/scanner/include/scanner/MediaScannerStats.hpp b/src/libs/scanner/include/scanner/ScannerStats.hpp
similarity index 100%
rename from src/libs/scanner/include/scanner/MediaScannerStats.hpp
rename to src/libs/scanner/include/scanner/ScannerStats.hpp
diff --git a/src/libs/subsonic/impl/Scan.cpp b/src/libs/subsonic/impl/Scan.cpp
index ff32b960..9d57fb17 100644
--- a/src/libs/subsonic/impl/Scan.cpp
+++ b/src/libs/subsonic/impl/Scan.cpp
@@ -19,7 +19,7 @@
#include "Scan.hpp"
-#include "scanner/IMediaScanner.hpp"
+#include "scanner/IScanner.hpp"
#include "utils/Service.hpp"
namespace API::Subsonic::Scan
@@ -32,10 +32,10 @@ namespace API::Subsonic::Scan
{
Response::Node statusResponse;
- const IMediaScanner::Status scanStatus {Service::get()->getStatus()};
+ const IScanner::Status scanStatus {Service::get()->getStatus()};
- statusResponse.setAttribute("scanning", scanStatus.currentState == IMediaScanner::State::InProgress);
- if (scanStatus.currentState == IMediaScanner::State::InProgress)
+ statusResponse.setAttribute("scanning", scanStatus.currentState == IScanner::State::InProgress);
+ if (scanStatus.currentState == IScanner::State::InProgress)
{
std::size_t count{};
@@ -61,7 +61,7 @@ namespace API::Subsonic::Scan
Response
handleStartScan(RequestContext& context)
{
- Service::get()->requestImmediateScan(false);
+ Service::get()->requestImmediateScan(false);
Response response {Response::createOkResponse(context)};
response.addNode("scanStatus", createStatusResponseNode());
diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt
index ba77ba17..800c9e8d 100644
--- a/src/lms/CMakeLists.txt
+++ b/src/lms/CMakeLists.txt
@@ -32,8 +32,8 @@ add_executable(lms
ui/explore/TracksView.cpp
ui/resource/AudioFileResource.cpp
ui/resource/AudioTranscodeResource.cpp
+ ui/resource/CoverResource.cpp
ui/resource/DownloadResource.cpp
- ui/resource/ImageResource.cpp
)
target_include_directories(lms PRIVATE
diff --git a/src/lms/main.cpp b/src/lms/main.cpp
index 202b71d7..f190371f 100644
--- a/src/lms/main.cpp
+++ b/src/lms/main.cpp
@@ -31,7 +31,7 @@
#include "cover/ICoverArtGrabber.hpp"
#include "database/Db.hpp"
#include "database/Session.hpp"
-#include "scanner/IMediaScanner.hpp"
+#include "scanner/IScanner.hpp"
#include "recommendation/IEngine.hpp"
#include "subsonic/SubsonicResource.hpp"
#include "ui/LmsApplication.hpp"
@@ -119,6 +119,47 @@ generateWtConfig(std::string execPath)
}
+static
+void
+proxyScannerEventsToApplication(Scanner::IScanner& scanner, Wt::WServer& server)
+{
+ scanner.getEvents().scanStarted.connect([&]
+ {
+ server.postAll([]
+ {
+ LmsApp->getScannerEvents().scanStarted.emit();
+ LmsApp->triggerUpdate();
+ });
+ });
+
+ scanner.getEvents().scanComplete.connect([&] (const Scanner::ScanStats& stats)
+ {
+ server.postAll([&]
+ {
+ LmsApp->getScannerEvents().scanComplete.emit(stats);
+ LmsApp->triggerUpdate();
+ });
+ });
+
+ scanner.getEvents().scanInProgress.connect([&] (const Scanner::ScanStepStats& stats)
+ {
+ server.postAll([&]
+ {
+ LmsApp->getScannerEvents().scanInProgress.emit(stats);
+ LmsApp->triggerUpdate();
+ });
+ });
+
+ scanner.getEvents().scanScheduled.connect([&] (const Wt::WDateTime dateTime)
+ {
+ server.postAll([&]
+ {
+ LmsApp->getScannerEvents().scanScheduled.emit(dateTime);
+ LmsApp->triggerUpdate();
+ });
+ });
+}
+
int main(int argc, char* argv[])
{
std::filesystem::path configFilePath {"/etc/lms.conf"};
@@ -184,9 +225,9 @@ int main(int argc, char* argv[])
config->getULong("cover-max-file-size", 10) * 1000 * 1000,
config->getULong("cover-jpeg-quality", 75))};
Service recommendationEngineService {Recommendation::createEngine(database)};
- Service mediaScannerService {Scanner::createMediaScanner(database, *recommendationEngineService)};
+ Service scannerService {Scanner::createScanner(database, *recommendationEngineService)};
- mediaScannerService->scanComplete().connect([&]()
+ scannerService->getEvents().scanComplete.connect([&]
{
// Flush cover cache even if no changes:
// covers may be external files that changed and we don't keep track of them
@@ -201,8 +242,12 @@ int main(int argc, char* argv[])
// bind UI entry point
server.addEntryPoint(Wt::EntryPointType::Application,
- std::bind(UserInterface::LmsApplication::create,
- std::placeholders::_1, std::ref(database), std::ref(appGroups)));
+ [&](const Wt::WEnvironment &env)
+ {
+ return UserInterface::LmsApplication::create(env, database, appGroups);
+ });
+
+ proxyScannerEventsToApplication(*scannerService, server);
LMS_LOG(MAIN, INFO) << "Starting server...";
server.start();
diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp
index a38c3da1..2c81e9c7 100644
--- a/src/lms/ui/LmsApplication.cpp
+++ b/src/lms/ui/LmsApplication.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -34,8 +35,6 @@
#include "database/Release.hpp"
#include "database/Session.hpp"
#include "database/User.hpp"
-#include "explore/Explore.hpp"
-#include "explore/Filters.hpp"
#include "utils/Logger.hpp"
#include "utils/Service.hpp"
#include "utils/String.hpp"
@@ -44,10 +43,12 @@
#include "admin/DatabaseSettingsView.hpp"
#include "admin/UserView.hpp"
#include "admin/UsersView.hpp"
+#include "explore/Explore.hpp"
+#include "explore/Filters.hpp"
#include "resource/AudioFileResource.hpp"
#include "resource/AudioTranscodeResource.hpp"
#include "resource/DownloadResource.hpp"
-#include "resource/ImageResource.hpp"
+#include "resource/CoverResource.hpp"
#include "Auth.hpp"
#include "LmsApplicationException.hpp"
#include "LmsTheme.hpp"
@@ -227,6 +228,8 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env,
}
}
+LmsApplication::~LmsApplication() = default;
+
void
LmsApplication::finalize()
{
@@ -440,9 +443,7 @@ LmsApplication::handleUserLoggedIn(Database::IdType userId, bool strongAuth)
void
LmsApplication::createHome()
{
- _audioFileResource = std::make_shared();
- _audioTranscodeResource = std::make_shared();
- _imageResource = std::make_shared();
+ _coverResource = std::make_shared();
declareJavaScriptFunction("onLoadCover", "function(id) { id.className += \" Lms-cover-loaded\"}");
doJavaScript("$('body').tooltip({ selector: '[data-toggle=\"tooltip\"]'})");
@@ -532,59 +533,15 @@ LmsApplication::createHome()
_mediaPlayer->loadTrack(trackId, play, replayGain);
});
- _playQueue->trackUnselected.connect([this] ()
+ _playQueue->trackUnselected.connect([this]
{
_mediaPlayer->stop();
});
- // Events from MediaScanner
+ if (isUserAdmin())
{
- const std::string sessionId {LmsApp->sessionId()};
-
- Service::get()->scanStarted().connect(this, [=]
+ _scannerEvents.scanComplete.connect([=] (const Scanner::ScanStats& stats)
{
- Wt::WServer::instance()->post(sessionId, [=]
- {
- _events.dbScanStarted.emit();
- triggerUpdate();
- });
- });
-
- Service::get()->scanComplete().connect(this, [=]
- {
- Wt::WServer::instance()->post(sessionId, [this]
- {
- _events.dbScanned.emit();
- triggerUpdate();
- });
- });
-
- Service::get()->scanInProgress().connect(this, [=] (Scanner::ScanStepStats stepStats)
- {
- Wt::WServer::instance()->post(sessionId, [=]
- {
- _events.dbScanInProgress.emit(stepStats);
- triggerUpdate();
- });
- });
-
- Service::get()->scheduled().connect(this, [=] (Wt::WDateTime dateTime)
- {
- Wt::WServer::instance()->post(sessionId, [=]
- {
- _events.dbScanScheduled.emit(dateTime);
- triggerUpdate();
- });
- });
-
- }
-
- _events.dbScanned.connect([=] ()
- {
- if (isUserAdmin())
- {
- const auto& stats {*Service::get()->getStatus().lastCompleteScanStats};
-
notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-complete")
.arg(static_cast(stats.nbFiles()))
.arg(static_cast(stats.additions))
@@ -592,8 +549,8 @@ LmsApplication::createHome()
.arg(static_cast(stats.deletions))
.arg(static_cast(stats.duplicates.size()))
.arg(static_cast(stats.errors.size())));
- }
- });
+ });
+ }
// Events from Application group
_events.appOpen.connect([=]
@@ -632,14 +589,14 @@ LmsApplication::notify(const Wt::WEvent& event)
}
}
-static std::string msgTypeToString(MsgType type)
+static std::string msgTypeToString(LmsApplication::MsgType type)
{
switch(type)
{
- case MsgType::Success: return "success";
- case MsgType::Info: return "info";
- case MsgType::Warning: return "warning";
- case MsgType::Danger: return "danger";
+ case LmsApplication::MsgType::Success: return "success";
+ case LmsApplication::MsgType::Info: return "info";
+ case LmsApplication::MsgType::Warning: return "warning";
+ case LmsApplication::MsgType::Danger: return "danger";
}
return "";
}
diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp
index 757dc126..9718c534 100644
--- a/src/lms/ui/LmsApplication.hpp
+++ b/src/lms/ui/LmsApplication.hpp
@@ -22,13 +22,18 @@
#include
#include
-#include
-#include "scanner/IMediaScanner.hpp"
+#include "scanner/ScannerEvents.hpp"
#include "LmsApplicationGroup.hpp"
-namespace Database {
+namespace Wt
+{
+ class WPopupMenu;
+}
+
+namespace Database
+{
class Artist;
class Cluster;
class Db;
@@ -39,10 +44,8 @@ namespace Database {
namespace UserInterface {
-class AudioTranscodeResource;
-class AudioFileResource;
class Auth;
-class ImageResource;
+class CoverResource;
class LmsApplicationException;
class MediaPlayer;
class PlayQueue;
@@ -53,35 +56,19 @@ struct Events
// Events relative to group
Wt::Signal appOpen;
Wt::Signal appClosed;
-
- // Database events
- Wt::Signal<> dbScanStarted;
- Wt::Signal<> dbScanned;
- Wt::Signal dbScanInProgress;
- Wt::Signal dbScanScheduled;
-};
-
-// Used to classify the message sent to the user
-enum class MsgType
-{
- Success,
- Info,
- Warning,
- Danger,
};
class LmsApplication : public Wt::WApplication
{
public:
LmsApplication(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups);
+ ~LmsApplication();
static std::unique_ptr create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups);
static LmsApplication* instance();
// Session application data
- std::shared_ptr getImageResource() { return _imageResource; }
- std::shared_ptr getAudioTranscodeResource() { return _audioTranscodeResource; }
- std::shared_ptr getAudioFileResource() { return _audioFileResource; }
+ std::shared_ptr getCoverResource() { return _coverResource; }
Database::Session& getDbSession(); // always thread safe
Wt::Dbo::ptr getUser();
@@ -91,10 +78,20 @@ class LmsApplication : public Wt::WApplication
std::string getUserLoginName(); // user must be logged in prior this call
Events& getEvents() { return _events; }
+ Scanner::Events& getScannerEvents() { return _scannerEvents; }
// Utils
void post(std::function func);
- void notifyMsg(MsgType type, const Wt::WString& message, std::chrono::milliseconds duration = std::chrono::milliseconds(4000));
+
+ // Used to classify the message sent to the user
+ enum class MsgType
+ {
+ Success,
+ Info,
+ Warning,
+ Danger,
+ };
+ void notifyMsg(MsgType type, const Wt::WString& message, std::chrono::milliseconds duration = std::chrono::milliseconds {4000});
static Wt::WLink createArtistLink(Wt::Dbo::ptr artist);
static std::unique_ptr createArtistAnchor(Wt::Dbo::ptr artist, bool addText = true);
@@ -129,19 +126,18 @@ class LmsApplication : public Wt::WApplication
Wt::Signal<> _preQuit;
LmsApplicationGroupContainer& _appGroups;
Events _events;
+ Scanner::Events _scannerEvents;
std::optional _userId;
std::optional _userAuthStrong;
- std::shared_ptr _audioTranscodeResource;
- std::shared_ptr _audioFileResource;
- std::shared_ptr _imageResource;
+ std::shared_ptr _coverResource;
MediaPlayer* _mediaPlayer {};
PlayQueue* _playQueue {};
- std::unique_ptr _popupMenu {};
+ std::unique_ptr _popupMenu;
};
// Helper to get session instance
-#define LmsApp LmsApplication::instance()
+#define LmsApp ::UserInterface::LmsApplication::instance()
} // namespace UserInterface
diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp
index abebac64..cb771f2b 100644
--- a/src/lms/ui/MediaPlayer.cpp
+++ b/src/lms/ui/MediaPlayer.cpp
@@ -33,7 +33,7 @@
#include "database/Types.hpp"
#include "database/User.hpp"
-#include "resource/ImageResource.hpp"
+#include "resource/CoverResource.hpp"
#include "resource/AudioTranscodeResource.hpp"
#include "resource/AudioFileResource.hpp"
@@ -192,14 +192,17 @@ static MediaPlayer::Settings settingsfromJSString(const std::string& strSettings
}
MediaPlayer::MediaPlayer()
-: Wt::WTemplate {Wt::WString::tr("Lms.MediaPlayer.template")},
- playbackEnded {this, "playbackEnded"},
- playPrevious {this, "playPrevious"},
- playNext {this, "playNext"},
- _settingsLoaded {this, "settingsLoaded"}
+: Wt::WTemplate {Wt::WString::tr("Lms.MediaPlayer.template")}
+, playbackEnded {this, "playbackEnded"}
+, playPrevious {this, "playPrevious"}
+, playNext {this, "playNext"}
+, _settingsLoaded {this, "settingsLoaded"}
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
+ _audioTranscodeResource = std::make_unique();
+ _audioFileResource = std::make_unique();
+
_title = bindNew("title");
_artist = bindNew("artist");
_release = bindNew("release");
@@ -240,8 +243,8 @@ MediaPlayer::loadTrack(Database::IdType trackId, bool play, float replayGain)
if (!track)
return;
- const std::string transcodeResource {LmsApp->getAudioTranscodeResource()->getUrl(trackId)};
- const std::string nativeResource {LmsApp->getAudioFileResource()->getUrl(trackId)};
+ const std::string transcodeResource {_audioTranscodeResource->getUrl(trackId)};
+ const std::string nativeResource {_audioFileResource->getUrl(trackId)};
const auto artists {track->getArtists({Database::TrackArtistLinkType::Artist})};
@@ -255,8 +258,8 @@ MediaPlayer::loadTrack(Database::IdType trackId, bool play, float replayGain)
<< " artist: \"" << (!artists.empty() ? StringUtils::jsEscape(artists.front()->getName()) : "") << "\","
<< " release: \"" << (track->getRelease() ? StringUtils::jsEscape(track->getRelease()->getName()) : "") << "\","
<< " artwork: ["
- << " { src: \"" << LmsApp->getImageResource()->getTrackUrl(trackId, ImageResource::Size::Small) << "\", sizes: \"128x128\", type: \"image/jpeg\" },"
- << " { src: \"" << LmsApp->getImageResource()->getTrackUrl(trackId, ImageResource::Size::Large) << "\", sizes: \"512x512\", type: \"image/jpeg\" },"
+ << " { src: \"" << LmsApp->getCoverResource()->getTrackUrl(trackId, CoverResource::Size::Small) << "\", sizes: \"128x128\", type: \"image/jpeg\" },"
+ << " { src: \"" << LmsApp->getCoverResource()->getTrackUrl(trackId, CoverResource::Size::Large) << "\", sizes: \"512x512\", type: \"image/jpeg\" },"
<< " ]"
<< "};";
oss << "LMS.mediaplayer.loadTrack(params, " << (play ? "true" : "false") << ")"; // true to autoplay
diff --git a/src/lms/ui/MediaPlayer.hpp b/src/lms/ui/MediaPlayer.hpp
index 58f7b432..af2374b9 100644
--- a/src/lms/ui/MediaPlayer.hpp
+++ b/src/lms/ui/MediaPlayer.hpp
@@ -29,6 +29,9 @@
namespace UserInterface {
+class AudioFileResource;
+class AudioTranscodeResource;
+
class MediaPlayer : public Wt::WTemplate
{
public:
@@ -105,6 +108,8 @@ class MediaPlayer : public Wt::WTemplate
Wt::Signal<> settingsLoaded;
private:
+ std::unique_ptr _audioFileResource;
+ std::unique_ptr _audioTranscodeResource;
std::optional _trackIdLoaded;
std::optional _settings;
diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp
index 7b8e89cb..c1bd31f1 100644
--- a/src/lms/ui/PlayQueue.cpp
+++ b/src/lms/ui/PlayQueue.cpp
@@ -34,7 +34,7 @@
#include "utils/String.hpp"
#include "common/LoadingIndicator.hpp"
-#include "resource/ImageResource.hpp"
+#include "resource/CoverResource.hpp"
#include "LmsApplication.hpp"
#include "MediaPlayer.hpp"
#include "TrackStringUtils.hpp"
@@ -394,10 +394,10 @@ PlayQueue::processTracks(PlayQueueAction action, const std::vector 0)
- LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", nbAddedTracks).arg(nbAddedTracks), std::chrono::milliseconds(2000));
+ LmsApp->notifyMsg(LmsApplication::MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", nbAddedTracks).arg(nbAddedTracks), std::chrono::milliseconds(2000));
if (isFull())
- LmsApp->notifyMsg(MsgType::Warning, Wt::WString::tr("Lms.PlayQueue.playqueue-full"), std::chrono::milliseconds(2000));
+ LmsApp->notifyMsg(LmsApplication::MsgType::Warning, Wt::WString::tr("Lms.PlayQueue.playqueue-full"), std::chrono::milliseconds(2000));
}
@@ -443,7 +443,7 @@ PlayQueue::addSome()
{
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
auto cover = std::make_unique();
- cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Large));
+ cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large));
cover->setStyleClass("Lms-cover");
cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)");
anchor->setImage(std::move(cover));
@@ -452,7 +452,7 @@ PlayQueue::addSome()
else
{
auto cover = entry->bindNew("cover");
- cover->setImageLink(LmsApp->getImageResource()->getTrackUrl(track.id(), ImageResource::Size::Large));
+ cover->setImageLink(LmsApp->getCoverResource()->getTrackUrl(track.id(), CoverResource::Size::Large));
cover->setStyleClass("Lms-cover");
cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)");
}
diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp
index 33f50856..1d5531b4 100644
--- a/src/lms/ui/SettingsView.cpp
+++ b/src/lms/ui/SettingsView.cpp
@@ -514,7 +514,7 @@ SettingsView::refreshView()
if (LmsApp->getUser()->isDemo())
{
- LmsApp->notifyMsg(MsgType::Warning, Wt::WString::tr("Lms.Settings.demo-cannot-save"));
+ LmsApp->notifyMsg(LmsApplication::MsgType::Warning, Wt::WString::tr("Lms.Settings.demo-cannot-save"));
return;
}
}
@@ -524,7 +524,7 @@ SettingsView::refreshView()
if (model->validate())
{
model->saveData();
- LmsApp->notifyMsg(MsgType::Success, Wt::WString::tr("Lms.Settings.settings-saved"));
+ LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Settings.settings-saved"));
}
// Udate the view: Delete any validation message in the view, etc.
diff --git a/src/lms/ui/admin/DatabaseSettingsView.cpp b/src/lms/ui/admin/DatabaseSettingsView.cpp
index af11f6ea..aa35e0ae 100644
--- a/src/lms/ui/admin/DatabaseSettingsView.cpp
+++ b/src/lms/ui/admin/DatabaseSettingsView.cpp
@@ -29,6 +29,7 @@
#include "database/Cluster.hpp"
#include "database/ScanSettings.hpp"
#include "database/Session.hpp"
+#include "scanner/IScanner.hpp"
#include "utils/Logger.hpp"
#include "utils/Service.hpp"
#include "utils/String.hpp"
@@ -222,8 +223,8 @@ DatabaseSettingsView::refreshView()
{
model->saveData();
- Service::get()->requestImmediateScan(false);
- LmsApp->notifyMsg(MsgType::Success, Wt::WString::tr("Lms.Admin.Database.settings-saved"));
+ Service::get()->requestImmediateScan(false);
+ LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Admin.Database.settings-saved"));
}
// Udate the view: Delete any validation message in the view, etc.
@@ -239,7 +240,7 @@ DatabaseSettingsView::refreshView()
immScanBtn->clicked().connect([=]
{
- Service::get()->requestImmediateScan(false);
+ Service::get()->requestImmediateScan(false);
});
t->updateView(model.get());
diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp
index 8f39e2b0..39b5776f 100644
--- a/src/lms/ui/admin/InitWizardView.cpp
+++ b/src/lms/ui/admin/InitWizardView.cpp
@@ -202,7 +202,7 @@ InitWizardView::InitWizardView()
if (model->validate())
{
model->saveData();
- LmsApp->notifyMsg(MsgType::Success, Wt::WString::tr("Lms.Admin.InitWizard.done"));
+ LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Admin.InitWizard.done"));
saveButton->setEnabled(false);
}
diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp
index 87f9ae9b..d19b4974 100644
--- a/src/lms/ui/admin/ScannerController.cpp
+++ b/src/lms/ui/admin/ScannerController.cpp
@@ -30,6 +30,7 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
+#include "scanner/IScanner.hpp"
#include "utils/Service.hpp"
#include "LmsApplication.hpp"
@@ -139,13 +140,13 @@ ScannerController::ScannerController()
auto onDbEvent = [&]() { refreshContents(); };
- LmsApp->getEvents().dbScanStarted.connect(this, []
+ LmsApp->getScannerEvents().scanStarted.connect(this, []
{
- LmsApp->notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-launched"));
+ LmsApp->notifyMsg(LmsApplication::MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-launched"));
});
- LmsApp->getEvents().dbScanned.connect(this, onDbEvent);
- LmsApp->getEvents().dbScanInProgress.connect(this, onDbEvent);
- LmsApp->getEvents().dbScanScheduled.connect(this, onDbEvent);
+ LmsApp->getScannerEvents().scanComplete.connect(this, onDbEvent);
+ LmsApp->getScannerEvents().scanInProgress.connect(this, onDbEvent);
+ LmsApp->getScannerEvents().scanScheduled.connect(this, onDbEvent);
refreshContents();
}
@@ -162,20 +163,20 @@ ScannerController::refreshContents()
actionBtn->actionButton()->setText(Wt::WString::tr("Lms.Admin.ScannerController.scan-now"));
actionBtn->actionButton()->clicked().connect([]
{
- Service::get()->requestImmediateScan(false);
+ Service::get()->requestImmediateScan(false);
});
auto popup = std::make_unique();
popup->addItem(Wt::WString::tr("Lms.Admin.ScannerController.force-scan-now"));
popup->itemSelected().connect([]
{
- Service::get()->requestImmediateScan(true);
+ Service::get()->requestImmediateScan(true);
});
actionBtn->dropDownButton()->setMenu(std::move(popup));
actionBtn->dropDownButton()->addStyleClass("btn-primary");
- const IMediaScanner::Status status {Service::get()->getStatus()};
+ const IScanner::Status status {Service::get()->getStatus()};
if (status.lastCompleteScanStats)
{
bindString("last-scan", Wt::WString::tr("Lms.Admin.ScannerController.last-scan-status")
@@ -199,16 +200,16 @@ ScannerController::refreshContents()
switch (status.currentState)
{
- case IMediaScanner::State::NotScheduled:
+ case IScanner::State::NotScheduled:
bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-not-scheduled"));
bindEmpty("step-status");
break;
- case IMediaScanner::State::Scheduled:
+ case IScanner::State::Scheduled:
bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-scheduled")
.arg(status.nextScheduledScan.toString()));
bindEmpty("step-status");
break;
- case IMediaScanner::State::InProgress:
+ case IScanner::State::InProgress:
bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-in-progress")
.arg(static_cast(status.currentScanStepStats->currentStep) + 1)
.arg(Scanner::ScanProgressStepCount));
diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp
index 33cfb6fb..4ec962cb 100644
--- a/src/lms/ui/admin/UserView.cpp
+++ b/src/lms/ui/admin/UserView.cpp
@@ -316,7 +316,7 @@ UserView::refreshView()
if (model->validate())
{
model->saveData();
- LmsApp->notifyMsg(MsgType::Success, Wt::WString::tr(userId ? "Lms.Admin.User.user-updated" : "Lms.Admin.User.user-created"));
+ LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr(userId ? "Lms.Admin.User.user-updated" : "Lms.Admin.User.user-created"));
LmsApp->setInternalPath("/admin/users", true);
}
else
diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp
index 70f4242d..79dbfc9f 100644
--- a/src/lms/ui/explore/ArtistView.cpp
+++ b/src/lms/ui/explore/ArtistView.cpp
@@ -21,6 +21,7 @@
#include
#include
+#include
#include
#include
diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp
index bdf7430e..25f34307 100644
--- a/src/lms/ui/explore/ArtistsView.cpp
+++ b/src/lms/ui/explore/ArtistsView.cpp
@@ -73,9 +73,10 @@ Artists::Artists(Filters* filters)
_linkType->changed().connect([this] { refreshView(); });
refreshArtistLinkTypes();
- LmsApp->getEvents().dbScanned.connect(this, [this]
+ LmsApp->getScannerEvents().scanComplete.connect(this, [this](const Scanner::ScanStats& stats)
{
- refreshArtistLinkTypes();
+ if (stats.nbChanges())
+ refreshArtistLinkTypes();
});
_container = bindNew("artists");
diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp
index 06c34da9..0796ab01 100644
--- a/src/lms/ui/explore/Filters.cpp
+++ b/src/lms/ui/explore/Filters.cpp
@@ -150,7 +150,7 @@ Filters::add(Database::IdType clusterId)
_sigUpdated.emit();
});
- LmsApp->notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Explore.filter-added"), std::chrono::seconds {2});
+ LmsApp->notifyMsg(LmsApplication::MsgType::Info, Wt::WString::tr("Lms.Explore.filter-added"), std::chrono::seconds {2});
_sigUpdated.emit();
}
diff --git a/src/lms/ui/explore/ReleaseListHelpers.cpp b/src/lms/ui/explore/ReleaseListHelpers.cpp
index b77945dc..fc29d3c1 100644
--- a/src/lms/ui/explore/ReleaseListHelpers.cpp
+++ b/src/lms/ui/explore/ReleaseListHelpers.cpp
@@ -25,7 +25,7 @@
#include "database/Artist.hpp"
#include "database/Release.hpp"
-#include "resource/ImageResource.hpp"
+#include "resource/CoverResource.hpp"
#include "LmsApplication.hpp"
@@ -44,7 +44,7 @@ namespace UserInterface::ReleaseListHelpers
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
auto cover = std::make_unique();
- cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Large));
+ cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large));
cover->setStyleClass("Lms-cover");
cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)");
anchor->setImage(std::move(cover));
diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp
index 79e64497..79617838 100644
--- a/src/lms/ui/explore/ReleasePopup.cpp
+++ b/src/lms/ui/explore/ReleasePopup.cpp
@@ -19,6 +19,8 @@
#include "ReleasePopup.hpp"
+#include
+
#include "database/Release.hpp"
#include "database/Session.hpp"
#include "database/User.hpp"
diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp
index d2dcacac..6c393eac 100644
--- a/src/lms/ui/explore/ReleaseView.cpp
+++ b/src/lms/ui/explore/ReleaseView.cpp
@@ -34,7 +34,7 @@
#include "utils/String.hpp"
#include "resource/DownloadResource.hpp"
-#include "resource/ImageResource.hpp"
+#include "resource/CoverResource.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"
#include "LmsApplicationException.hpp"
@@ -127,7 +127,7 @@ Release::refreshView()
}
{
- Wt::WImage* cover {bindNew("cover", Wt::WLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Large)))};
+ Wt::WImage* cover {bindNew("cover", Wt::WLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large)))};
cover->setStyleClass("Lms-cover-large");
cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)");
}
diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp
index afc4d3cf..84ac60d0 100644
--- a/src/lms/ui/explore/ReleasesView.cpp
+++ b/src/lms/ui/explore/ReleasesView.cpp
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include "database/Release.hpp"
@@ -34,7 +35,6 @@
#include "utils/String.hpp"
#include "common/LoadingIndicator.hpp"
-#include "resource/ImageResource.hpp"
#include "ReleaseListHelpers.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"
diff --git a/src/lms/ui/explore/SearchView.cpp b/src/lms/ui/explore/SearchView.cpp
index e62c5aff..61897b63 100644
--- a/src/lms/ui/explore/SearchView.cpp
+++ b/src/lms/ui/explore/SearchView.cpp
@@ -27,7 +27,6 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
-#include "resource/ImageResource.hpp"
#include "ArtistListHelpers.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"
diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp
index 7d241b3c..0fc280fb 100644
--- a/src/lms/ui/explore/TrackListHelpers.cpp
+++ b/src/lms/ui/explore/TrackListHelpers.cpp
@@ -28,7 +28,7 @@
#include "database/Release.hpp"
#include "database/Track.hpp"
#include "resource/DownloadResource.hpp"
-#include "resource/ImageResource.hpp"
+#include "resource/CoverResource.hpp"
#include "LmsApplication.hpp"
#include "MediaPlayer.hpp"
#include "TrackPopup.hpp"
@@ -71,9 +71,9 @@ namespace UserInterface::TrackListHelpers
entry->setCondition("if-has-release", true);
entry->bindWidget("release", LmsApplication::createReleaseAnchor(track->getRelease()));
{
- Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
- auto cover = std::make_unique();
- cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Large));
+ Wt::WAnchor* anchor {entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false))};
+ auto cover {std::make_unique()};
+ cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large));
cover->setStyleClass("Lms-cover");
cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)");
anchor->setImage(std::move(cover));
@@ -81,8 +81,8 @@ namespace UserInterface::TrackListHelpers
}
else
{
- auto cover = entry->bindNew("cover");
- cover->setImageLink(LmsApp->getImageResource()->getTrackUrl(trackId, ImageResource::Size::Large));
+ auto* cover {entry->bindNew("cover")};
+ cover->setImageLink(LmsApp->getCoverResource()->getTrackUrl(trackId, CoverResource::Size::Large));
cover->setStyleClass("Lms-cover");
cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)");
}
diff --git a/src/lms/ui/explore/TrackPopup.cpp b/src/lms/ui/explore/TrackPopup.cpp
index f1943355..ff1bbd5f 100644
--- a/src/lms/ui/explore/TrackPopup.cpp
+++ b/src/lms/ui/explore/TrackPopup.cpp
@@ -19,6 +19,8 @@
#include "TrackPopup.hpp"
+#include
+
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "database/User.hpp"
diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp
index 909b0768..6093b45d 100644
--- a/src/lms/ui/explore/TracksView.cpp
+++ b/src/lms/ui/explore/TracksView.cpp
@@ -20,8 +20,9 @@
#include "TracksView.hpp"
#include
-#include
#include
+#include
+#include
#include
#include "database/Artist.hpp"
@@ -34,7 +35,6 @@
#include "utils/String.hpp"
#include "common/LoadingIndicator.hpp"
-#include "resource/ImageResource.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"
#include "MediaPlayer.hpp"
diff --git a/src/lms/ui/resource/ImageResource.cpp b/src/lms/ui/resource/CoverResource.cpp
similarity index 86%
rename from src/lms/ui/resource/ImageResource.cpp
rename to src/lms/ui/resource/CoverResource.cpp
index 21edbb67..8adf85d8 100644
--- a/src/lms/ui/resource/ImageResource.cpp
+++ b/src/lms/ui/resource/CoverResource.cpp
@@ -17,7 +17,7 @@
* along with LMS. If not, see .
*/
-#include "ImageResource.hpp"
+#include "CoverResource.hpp"
#include
#include
@@ -35,25 +35,34 @@
namespace UserInterface {
-ImageResource::~ImageResource()
+CoverResource::CoverResource()
+{
+ LmsApp->getScannerEvents().scanComplete.connect(this, [this](const Scanner::ScanStats& stats)
+ {
+ if (stats.nbChanges())
+ setChanged();
+ });
+}
+
+CoverResource::~CoverResource()
{
beingDeleted();
}
std::string
-ImageResource::getReleaseUrl(Database::IdType releaseId, Size size) const
+CoverResource::getReleaseUrl(Database::IdType releaseId, Size size) const
{
return url() + "&releaseid=" + std::to_string(releaseId) + "&size=" + std::to_string(static_cast(size));
}
std::string
-ImageResource::getTrackUrl(Database::IdType trackId, Size size) const
+CoverResource::getTrackUrl(Database::IdType trackId, Size size) const
{
return url() + "&trackid=" + std::to_string(trackId) + "&size=" + std::to_string(static_cast(size));
}
void
-ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response)
+CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response)
{
// Retrieve parameters
const std::string *trackIdStr = request.getParameter("trackid");
diff --git a/src/lms/ui/resource/ImageResource.hpp b/src/lms/ui/resource/CoverResource.hpp
similarity index 93%
rename from src/lms/ui/resource/ImageResource.hpp
rename to src/lms/ui/resource/CoverResource.hpp
index 76c574aa..f95a555d 100644
--- a/src/lms/ui/resource/ImageResource.hpp
+++ b/src/lms/ui/resource/CoverResource.hpp
@@ -25,12 +25,13 @@
namespace UserInterface
{
- class ImageResource : public Wt::WResource
+ class CoverResource : public Wt::WResource
{
public:
static const std::size_t maxSize {512};
- ~ImageResource();
+ CoverResource();
+ ~CoverResource();
enum class Size : std::size_t
{