diff --git a/src/libs/core/include/core/SystemPaths.hpp b/src/libs/core/include/core/SystemPaths.hpp
new file mode 100644
index 00000000..7a6e0e46
--- /dev/null
+++ b/src/libs/core/include/core/SystemPaths.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 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
+
+namespace lms::core
+{
+ static inline const std::filesystem::path sysconfDirectory{ "/etc" };
+}
\ No newline at end of file
diff --git a/src/lms/main.cpp b/src/lms/main.cpp
index 63cb1309..f2586a48 100644
--- a/src/lms/main.cpp
+++ b/src/lms/main.cpp
@@ -30,6 +30,7 @@
#include "core/ITraceLogger.hpp"
#include "core/Service.hpp"
#include "core/String.hpp"
+#include "core/SystemPaths.hpp"
#include "core/WtLogger.hpp"
#include "database/Db.hpp"
#include "database/Session.hpp"
@@ -50,180 +51,195 @@
namespace lms
{
- std::size_t getThreadCount()
+ namespace
{
- const unsigned long configHttpServerThreadCount{ core::Service::get()->getULong("http-server-thread-count", 0) };
-
- // Reserve at least 2 threads since we still have some blocking IO (for example when reading from ffmpeg)
- return configHttpServerThreadCount ? configHttpServerThreadCount : std::max(2, std::thread::hardware_concurrency());
- }
-
- core::logging::Severity getLogMinSeverity()
- {
- std::string_view minSeverity{ core::Service::get()->getString("log-min-severity", "info") };
-
- if (minSeverity == "debug")
- return core::logging::Severity::DEBUG;
- else if (minSeverity == "info")
- return core::logging::Severity::INFO;
- else if (minSeverity == "warning")
- return core::logging::Severity::WARNING;
- else if (minSeverity == "error")
- return core::logging::Severity::ERROR;
- else if (minSeverity == "fatal")
- return core::logging::Severity::FATAL;
-
- throw core::LmsException{ "Invalid config value for 'log-min-severity'" };
- }
-
- std::optional getTracingLevel()
- {
- std::string_view tracingLevel{ core::Service::get()->getString("tracing-level", "disabled") };
-
- if (tracingLevel == "disabled")
- return std::nullopt;
- else if (tracingLevel == "overview")
- return core::tracing::Level::Overview;
- else if (tracingLevel == "detailed")
- return core::tracing::Level::Detailed;
-
- throw core::LmsException{ "Invalid config value for 'tracing-level'" };
- }
-
- std::vector generateWtConfig(std::string execPath, core::logging::Severity minSeverity)
- {
- std::vector args;
-
- const std::filesystem::path wtConfigPath{ core::Service::get()->getPath("working-dir") / "wt_config.xml" };
- const std::filesystem::path wtLogFilePath{ core::Service::get()->getPath("log-file", "/var/log/lms.log") };
- const std::filesystem::path wtAccessLogFilePath{ core::Service::get()->getPath("access-log-file", "/var/log/lms.access.log") };
- const std::filesystem::path wtResourcesPath{ core::Service::get()->getPath("wt-resources", "/usr/share/Wt/resources") };
-
- args.push_back(execPath);
- args.push_back("--config=" + wtConfigPath.string());
- args.push_back("--docroot=" + std::string{ core::Service::get()->getString("docroot") });
- args.push_back("--approot=" + std::string{ core::Service::get()->getString("approot") });
- args.push_back("--deploy-path=" + std::string{ core::Service::get()->getString("deploy-path", "/") });
- if (!wtResourcesPath.empty())
- args.push_back("--resources-dir=" + wtResourcesPath.string());
-
- if (core::Service::get()->getBool("tls-enable", false))
+ std::size_t getThreadCount()
{
- args.push_back("--https-port=" + std::to_string(core::Service::get()->getULong("listen-port", 5082)));
- args.push_back("--https-address=" + std::string{ core::Service::get()->getString("listen-addr", "0.0.0.0") });
- args.push_back("--ssl-certificate=" + std::string{ core::Service::get()->getString("tls-cert") });
- args.push_back("--ssl-private-key=" + std::string{ core::Service::get()->getString("tls-key") });
- args.push_back("--ssl-tmp-dh=" + std::string{ core::Service::get()->getString("tls-dh") });
- }
- else
- {
- args.push_back("--http-port=" + std::to_string(core::Service::get()->getULong("listen-port", 5082)));
- args.push_back("--http-address=" + std::string{ core::Service::get()->getString("listen-addr", "0.0.0.0") });
+ const unsigned long configHttpServerThreadCount{ core::Service::get()->getULong("http-server-thread-count", 0) };
+
+ // Reserve at least 2 threads since we still have some blocking IO (for example when reading from ffmpeg)
+ return configHttpServerThreadCount ? configHttpServerThreadCount : std::max(2, std::thread::hardware_concurrency());
}
- if (!wtAccessLogFilePath.empty())
- args.push_back("--accesslog=" + wtAccessLogFilePath.string());
-
- args.push_back("--threads=" + std::to_string(getThreadCount()));
-
- // Generate the wt_config.xml file
- boost::property_tree::ptree pt;
-
- pt.put("server.application-settings..location", "*");
- pt.put("server.application-settings.log-file", wtLogFilePath.string());
-
- // log-config
- pt.put("server.application-settings.log-config", core::logging::WtLogger::computeLogConfig(minSeverity));
- pt.put("server.application-settings.behind-reverse-proxy", core::Service::get()->getBool("behind-reverse-proxy", false));
-
+ core::logging::Severity getLogMinSeverity()
{
- boost::property_tree::ptree viewport;
- viewport.put(".name", "viewport");
- viewport.put(".content", "width=device-width, initial-scale=1, user-scalable=no");
- pt.add_child("server.application-settings.head-matter.meta", viewport);
- }
- {
- boost::property_tree::ptree themeColor;
- themeColor.put(".name", "theme-color");
- themeColor.put(".content", "#303030");
- pt.add_child("server.application-settings.head-matter.meta", themeColor);
+ std::string_view minSeverity{ core::Service::get()->getString("log-min-severity", "info") };
+
+ if (minSeverity == "debug")
+ return core::logging::Severity::DEBUG;
+ else if (minSeverity == "info")
+ return core::logging::Severity::INFO;
+ else if (minSeverity == "warning")
+ return core::logging::Severity::WARNING;
+ else if (minSeverity == "error")
+ return core::logging::Severity::ERROR;
+ else if (minSeverity == "fatal")
+ return core::logging::Severity::FATAL;
+
+ throw core::LmsException{ "Invalid config value for 'log-min-severity'" };
}
+ std::optional getTracingLevel()
{
- std::ofstream oss{ wtConfigPath.string().c_str(), std::ios::out };
- if (!oss)
- throw core::LmsException{ "Can't open '" + wtConfigPath.string() + "' for writing!" };
+ std::string_view tracingLevel{ core::Service::get()->getString("tracing-level", "disabled") };
- boost::property_tree::xml_parser::write_xml(oss, pt);
+ if (tracingLevel == "disabled")
+ return std::nullopt;
+ else if (tracingLevel == "overview")
+ return core::tracing::Level::Overview;
+ else if (tracingLevel == "detailed")
+ return core::tracing::Level::Detailed;
- if (!oss)
- throw core::LmsException{ "Can't write in file '" + wtConfigPath.string() + "', no space left?" };
+ throw core::LmsException{ "Invalid config value for 'tracing-level'" };
}
- return args;
- }
+ std::vector generateWtConfig(std::string execPath, core::logging::Severity minSeverity)
+ {
+ std::vector args;
- void proxyScannerEventsToApplication(scanner::IScannerService& scanner, Wt::WServer& server)
- {
- auto postAll{ [](Wt::WServer& server, std::function cb) {
- server.postAll([cb = std::move(cb)] {
- // may be nullptr, see https://redmine.webtoolkit.eu/issues/8202
- if (LmsApp)
- cb();
- });
- } };
+ const std::filesystem::path wtConfigPath{ core::Service::get()->getPath("working-dir") / "wt_config.xml" };
+ const std::filesystem::path wtLogFilePath{ core::Service::get()->getPath("log-file", "/var/log/lms.log") };
+ const std::filesystem::path wtAccessLogFilePath{ core::Service::get()->getPath("access-log-file", "/var/log/lms.access.log") };
+ const std::filesystem::path wtResourcesPath{ core::Service::get()->getPath("wt-resources", "/usr/share/Wt/resources") };
- scanner.getEvents().scanAborted.connect([&] {
- postAll(server, [] {
- LmsApp->getScannerEvents().scanAborted.emit();
- LmsApp->triggerUpdate();
- });
- });
+ args.push_back(execPath);
+ args.push_back("--config=" + wtConfigPath.string());
+ args.push_back("--docroot=" + std::string{ core::Service::get()->getString("docroot") });
+ args.push_back("--approot=" + std::string{ core::Service::get()->getString("approot") });
+ args.push_back("--deploy-path=" + std::string{ core::Service::get()->getString("deploy-path", "/") });
+ if (!wtResourcesPath.empty())
+ args.push_back("--resources-dir=" + wtResourcesPath.string());
- scanner.getEvents().scanStarted.connect([&] {
- postAll(server, [] {
- LmsApp->getScannerEvents().scanStarted.emit();
- LmsApp->triggerUpdate();
- });
- });
+ if (core::Service::get()->getBool("tls-enable", false))
+ {
+ args.push_back("--https-port=" + std::to_string(core::Service::get()->getULong("listen-port", 5082)));
+ args.push_back("--https-address=" + std::string{ core::Service::get()->getString("listen-addr", "0.0.0.0") });
+ args.push_back("--ssl-certificate=" + std::string{ core::Service::get()->getString("tls-cert") });
+ args.push_back("--ssl-private-key=" + std::string{ core::Service::get()->getString("tls-key") });
+ args.push_back("--ssl-tmp-dh=" + std::string{ core::Service::get()->getString("tls-dh") });
+ }
+ else
+ {
+ args.push_back("--http-port=" + std::to_string(core::Service::get()->getULong("listen-port", 5082)));
+ args.push_back("--http-address=" + std::string{ core::Service::get()->getString("listen-addr", "0.0.0.0") });
+ }
- scanner.getEvents().scanComplete.connect([&](const scanner::ScanStats& stats) {
- postAll(server, [=] {
- LmsApp->getScannerEvents().scanComplete.emit(stats);
- LmsApp->triggerUpdate();
- });
- });
+ if (!wtAccessLogFilePath.empty())
+ args.push_back("--accesslog=" + wtAccessLogFilePath.string());
- scanner.getEvents().scanInProgress.connect([&](const scanner::ScanStepStats& stats) {
- postAll(server, [=] {
- LmsApp->getScannerEvents().scanInProgress.emit(stats);
- LmsApp->triggerUpdate();
- });
- });
+ args.push_back("--threads=" + std::to_string(getThreadCount()));
- scanner.getEvents().scanScheduled.connect([&](const Wt::WDateTime dateTime) {
- postAll(server, [=] {
- LmsApp->getScannerEvents().scanScheduled.emit(dateTime);
- LmsApp->triggerUpdate();
+ // Generate the wt_config.xml file
+ boost::property_tree::ptree pt;
+
+ pt.put("server.application-settings..location", "*");
+ pt.put("server.application-settings.log-file", wtLogFilePath.string());
+
+ // log-config
+ pt.put("server.application-settings.log-config", core::logging::WtLogger::computeLogConfig(minSeverity));
+ pt.put("server.application-settings.behind-reverse-proxy", core::Service::get()->getBool("behind-reverse-proxy", false));
+
+ {
+ boost::property_tree::ptree viewport;
+ viewport.put(".name", "viewport");
+ viewport.put(".content", "width=device-width, initial-scale=1, user-scalable=no");
+ pt.add_child("server.application-settings.head-matter.meta", viewport);
+ }
+ {
+ boost::property_tree::ptree themeColor;
+ themeColor.put(".name", "theme-color");
+ themeColor.put(".content", "#303030");
+ pt.add_child("server.application-settings.head-matter.meta", themeColor);
+ }
+
+ {
+ std::ofstream oss{ wtConfigPath.string().c_str(), std::ios::out };
+ if (!oss)
+ throw core::LmsException{ "Can't open '" + wtConfigPath.string() + "' for writing!" };
+
+ boost::property_tree::xml_parser::write_xml(oss, pt);
+
+ if (!oss)
+ throw core::LmsException{ "Can't write in file '" + wtConfigPath.string() + "', no space left?" };
+ }
+
+ return args;
+ }
+
+ void proxyScannerEventsToApplication(scanner::IScannerService& scanner, Wt::WServer& server)
+ {
+ auto postAll{ [](Wt::WServer& server, std::function cb) {
+ server.postAll([cb = std::move(cb)] {
+ // may be nullptr, see https://redmine.webtoolkit.eu/issues/8202
+ if (LmsApp)
+ cb();
+ });
+ } };
+
+ scanner.getEvents().scanAborted.connect([&] {
+ postAll(server, [] {
+ LmsApp->getScannerEvents().scanAborted.emit();
+ LmsApp->triggerUpdate();
+ });
});
- });
- }
+
+ scanner.getEvents().scanStarted.connect([&] {
+ postAll(server, [] {
+ LmsApp->getScannerEvents().scanStarted.emit();
+ LmsApp->triggerUpdate();
+ });
+ });
+
+ scanner.getEvents().scanComplete.connect([&](const scanner::ScanStats& stats) {
+ postAll(server, [=] {
+ LmsApp->getScannerEvents().scanComplete.emit(stats);
+ LmsApp->triggerUpdate();
+ });
+ });
+
+ scanner.getEvents().scanInProgress.connect([&](const scanner::ScanStepStats& stats) {
+ postAll(server, [=] {
+ LmsApp->getScannerEvents().scanInProgress.emit(stats);
+ LmsApp->triggerUpdate();
+ });
+ });
+
+ scanner.getEvents().scanScheduled.connect([&](const Wt::WDateTime dateTime) {
+ postAll(server, [=] {
+ LmsApp->getScannerEvents().scanScheduled.emit(dateTime);
+ LmsApp->triggerUpdate();
+ });
+ });
+ }
+ } // namespace
int main(int argc, char* argv[])
{
- std::filesystem::path configFilePath{ "/etc/lms.conf" };
+ std::filesystem::path configFilePath{ core::sysconfDirectory / "lms.conf" };
int res{ EXIT_FAILURE };
assert(argc > 0);
assert(argv[0] != NULL);
+ auto displayUsage{ [&](std::ostream& os) {
+ os << "Usage:\t" << argv[0] << "\t[conf_file]\n\n"
+ << "Options:\n"
+ << "\tconf_file:\t path to the LMS configuration file (defaults to " << configFilePath << ")\n\n";
+ } };
+
if (argc == 2)
- configFilePath = std::string(argv[1], 0, 256);
+ {
+ const std::string_view arg{ argv[1] };
+ if (arg == "-h" || arg == "--help")
+ {
+ displayUsage(std::cout);
+ return EXIT_SUCCESS;
+ }
+ configFilePath = std::string(arg, 0, 256);
+ }
else if (argc > 2)
{
- std::cerr << "Usage:\t" << argv[0] << "\t[conf_file]\n\n"
- << "Options:\n"
- << "\tconf_file:\t path to the LMS configuration file (defaults to " << configFilePath << ")\n\n";
+ displayUsage(std::cerr);
return EXIT_FAILURE;
}
diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp
index ce5f5320..dee31376 100644
--- a/src/tools/cover/LmsCover.cpp
+++ b/src/tools/cover/LmsCover.cpp
@@ -28,6 +28,7 @@
#include "core/ILogger.hpp"
#include "core/Service.hpp"
#include "core/StreamLogger.hpp"
+#include "core/SystemPaths.hpp"
#include "database/Db.hpp"
#include "database/Release.hpp"
#include "database/Session.hpp"
@@ -66,7 +67,7 @@ int main(int argc, char* argv[])
core::Service logger{ std::make_unique(std::cout) };
po::options_description desc{ "Allowed options" };
- desc.add_options()("help,h", "print usage message")("conf,c", po::value()->default_value("/etc/lms.conf"), "LMS config file")("default-cover,d", po::value(), "Default cover path")("tracks,t", "dump covers for tracks")("size,s", po::value()->default_value(512), "Requested cover size")("quality,q", po::value()->default_value(75), "JPEG quality (1-100)");
+ desc.add_options()("help,h", "print usage message")("conf,c", po::value()->default_value(core::sysconfDirectory / "lms.conf"), "LMS config file")("default-cover,d", po::value(), "Default cover path")("tracks,t", "dump covers for tracks")("size,s", po::value()->default_value(512), "Requested cover size")("quality,q", po::value()->default_value(75), "JPEG quality (1-100)");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
diff --git a/src/tools/db-generator/LmsDbGenerator.cpp b/src/tools/db-generator/LmsDbGenerator.cpp
index e41882ed..b046e04b 100644
--- a/src/tools/db-generator/LmsDbGenerator.cpp
+++ b/src/tools/db-generator/LmsDbGenerator.cpp
@@ -32,6 +32,7 @@
#include "core/Random.hpp"
#include "core/Service.hpp"
#include "core/StreamLogger.hpp"
+#include "core/SystemPaths.hpp"
#include "database/Artist.hpp"
#include "database/Cluster.hpp"
#include "database/Db.hpp"
@@ -177,7 +178,7 @@ int main(int argc, char* argv[])
const GeneratorParameters defaultParams;
program_options::options_description options{ "Options" };
- options.add_options()("conf,c", program_options::value()->default_value("/etc/lms.conf"), "lms config file")("media-library-count", program_options::value()->default_value(defaultParams.mediaLibraryCount), "Number of media libraries to use")("release-count-per-batch", program_options::value()->default_value(defaultParams.releaseCountPerBatch), "Number of releases to generate before committing transaction")("release-count", program_options::value()->default_value(defaultParams.releaseCount), "Number of releases to generate")("track-count-per-release", program_options::value()->default_value(defaultParams.trackCountPerRelease), "Number of tracks per release")("compilation-ratio", program_options::value()->default_value(defaultParams.compilationRatio), "Compilation ratio (compilation means all tracks have a different artist)")("track-path", program_options::value()->required(), "Path of a valid track file, that will be used for all generated tracks")("genre-count", program_options::value()->default_value(defaultParams.genreCount), "Number of genres to generate")("genre-count-per-track", program_options::value()->default_value(defaultParams.genreCountPerTrack), "Number of genres to assign to each track")("mood-count", program_options::value()->default_value(defaultParams.moodCount), "Number of moods to generate")("mood-count-per-track", program_options::value()->default_value(defaultParams.moodCountPerTrack), "Number of moods to assign to each track")("help,h", "produce help message");
+ options.add_options()("conf,c", program_options::value()->default_value(core::sysconfDirectory / "lms.conf"), "lms config file")("media-library-count", program_options::value()->default_value(defaultParams.mediaLibraryCount), "Number of media libraries to use")("release-count-per-batch", program_options::value()->default_value(defaultParams.releaseCountPerBatch), "Number of releases to generate before committing transaction")("release-count", program_options::value()->default_value(defaultParams.releaseCount), "Number of releases to generate")("track-count-per-release", program_options::value()->default_value(defaultParams.trackCountPerRelease), "Number of tracks per release")("compilation-ratio", program_options::value()->default_value(defaultParams.compilationRatio), "Compilation ratio (compilation means all tracks have a different artist)")("track-path", program_options::value()->required(), "Path of a valid track file, that will be used for all generated tracks")("genre-count", program_options::value()->default_value(defaultParams.genreCount), "Number of genres to generate")("genre-count-per-track", program_options::value()->default_value(defaultParams.genreCountPerTrack), "Number of genres to assign to each track")("mood-count", program_options::value()->default_value(defaultParams.moodCount), "Number of moods to generate")("mood-count-per-track", program_options::value()->default_value(defaultParams.moodCountPerTrack), "Number of moods to assign to each track")("help,h", "produce help message");
program_options::variables_map vm;
program_options::store(program_options::parse_command_line(argc, argv, options), vm);
diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp
index 578de0ba..0dd76576 100644
--- a/src/tools/recommendation/LmsRecommendation.cpp
+++ b/src/tools/recommendation/LmsRecommendation.cpp
@@ -27,6 +27,7 @@
#include "core/IConfig.hpp"
#include "core/Service.hpp"
#include "core/StreamLogger.hpp"
+#include "core/SystemPaths.hpp"
#include "database/Artist.hpp"
#include "database/Cluster.hpp"
#include "database/Db.hpp"
@@ -132,7 +133,7 @@ int main(int argc, char* argv[])
core::Service logger{ std::make_unique(std::cout) };
po::options_description desc{ "Allowed options" };
- desc.add_options()("help,h", "print usage message")("conf,c", po::value()->default_value("/etc/lms.conf"), "LMS config file")("artists,a", "Display recommendation for artists")("releases,r", "Display recommendation for releases")("tracks,t", "Display recommendation for tracks")("max,m", po::value()->default_value(3), "Max similarity result count");
+ desc.add_options()("help,h", "print usage message")("conf,c", po::value()->default_value(core::sysconfDirectory / "lms.conf"), "LMS config file")("artists,a", "Display recommendation for artists")("releases,r", "Display recommendation for releases")("tracks,t", "Display recommendation for tracks")("max,m", po::value()->default_value(3), "Max similarity result count");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);