From b4e515f82553521d84a771bef22a8976b6036830 Mon Sep 17 00:00:00 2001 From: emeric Date: Tue, 14 Nov 2023 13:47:08 +0100 Subject: [PATCH 01/18] Made scanner abort faster when it has to clean a lot of stuff --- src/libs/services/database/impl/Cluster.cpp | 2 +- .../include/services/database/Cluster.hpp | 2 +- src/libs/services/database/test/Cluster.cpp | 24 ++--- .../impl/ScanStepCheckDuplicatedDbFiles.cpp | 46 +++++----- .../impl/ScanStepComputeClusterStats.cpp | 5 +- .../scanner/impl/ScanStepDiscoverFiles.cpp | 35 ++++--- .../impl/ScanStepRemoveOrphanDbFiles.cpp | 92 ++++++++++--------- 7 files changed, 107 insertions(+), 99 deletions(-) diff --git a/src/libs/services/database/impl/Cluster.cpp b/src/libs/services/database/impl/Cluster.cpp index f93e8de2..cef0c615 100644 --- a/src/libs/services/database/impl/Cluster.cpp +++ b/src/libs/services/database/impl/Cluster.cpp @@ -106,7 +106,7 @@ namespace Database return Utils::execQuery(query, params.range); } - RangeResults Cluster::findOrphans(Session& session, std::optional range) + RangeResults Cluster::findOrphanIds(Session& session, std::optional range) { session.checkSharedLocked(); auto query{ session.getDboSession().query("SELECT DISTINCT c.id FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)") }; diff --git a/src/libs/services/database/include/services/database/Cluster.hpp b/src/libs/services/database/include/services/database/Cluster.hpp index 7220d647..c5215c01 100644 --- a/src/libs/services/database/include/services/database/Cluster.hpp +++ b/src/libs/services/database/include/services/database/Cluster.hpp @@ -65,7 +65,7 @@ namespace Database { static RangeResults find(Session& session, const FindParameters& params); static void find(Session& session, const FindParameters& params, std::function _func); static pointer find(Session& session, ClusterId id); - static RangeResults findOrphans(Session& session, std::optional range = std::nullopt); + static RangeResults findOrphanIds(Session& session, std::optional range = std::nullopt); // May be very slow static std::size_t computeTrackCount(Session& session, ClusterId id); diff --git a/src/libs/services/database/test/Cluster.cpp b/src/libs/services/database/test/Cluster.cpp index aeea9be6..cf768e00 100644 --- a/src/libs/services/database/test/Cluster.cpp +++ b/src/libs/services/database/test/Cluster.cpp @@ -55,7 +55,7 @@ TEST_F(DatabaseFixture, Cluster) } { - const auto clusters{ Cluster::findOrphans(session) }; + const auto clusters{ Cluster::findOrphanIds(session) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster.getId()); } @@ -91,7 +91,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); + EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); auto clusterTypes{ ClusterType::findOrphans(session) }; ASSERT_EQ(clusterTypes.results.size(), 1); EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); @@ -102,7 +102,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) { auto transaction{ session.createSharedTransaction() }; - auto clusters{ Cluster::findOrphans(session) }; + auto clusters{ Cluster::findOrphanIds(session) }; EXPECT_EQ(clusters.results.size(), 2); EXPECT_TRUE(track->getClusters().empty()); EXPECT_TRUE(track->getClusterIds().empty()); @@ -127,7 +127,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) { auto transaction{ session.createSharedTransaction() }; - auto clusters{ Cluster::findOrphans(session) }; + auto clusters{ Cluster::findOrphanIds(session) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster2.getId()); @@ -225,7 +225,7 @@ TEST_F(DatabaseFixture, Cluster_multiTracks) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); + EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_EQ(Cluster::computeTrackCount(session, cluster.getId()), tracks.size()); @@ -244,7 +244,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); + EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); } ScopedClusterType clusterType{ session, "MyClusterType" }; @@ -253,7 +253,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) { auto transaction{ session.createSharedTransaction() }; - ASSERT_EQ(Cluster::findOrphans(session).results.size(), 2); + ASSERT_EQ(Cluster::findOrphanIds(session).results.size(), 2); EXPECT_TRUE(Release::find(session, Release::FindParameters{}.setClusters({ unusedCluster.getId() })).results.empty()); EXPECT_EQ(Release::find(session, Release::FindParameters{}).results.size(), 1); EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 0); @@ -271,7 +271,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) auto transaction{ session.createSharedTransaction() }; { - auto clusters{ Cluster::findOrphans(session) }; + auto clusters{ Cluster::findOrphanIds(session) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), unusedCluster.getId()); } @@ -330,7 +330,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { auto transaction{ session.createSharedTransaction() }; EXPECT_TRUE(ClusterType::findOrphans(session).results.empty()); - EXPECT_EQ(Cluster::findOrphans(session).results.size(), 2); + EXPECT_EQ(Cluster::findOrphanIds(session).results.size(), 2); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } @@ -390,7 +390,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); + EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } @@ -430,7 +430,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); + EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } @@ -541,7 +541,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); + EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_TRUE(ClusterType::findOrphans(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); diff --git a/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp b/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp index e1893a8a..306167be 100644 --- a/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp @@ -26,30 +26,32 @@ namespace Scanner { - void - ScanStepCheckDuplicatedDbFiles::process(ScanContext& context) - { - using namespace Database; + void ScanStepCheckDuplicatedDbFiles::process(ScanContext& context) + { + using namespace Database; - if (_abortScan) - return; + if (_abortScan) + return; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - const RangeResults tracks = Track::findIdsTrackMBIDDuplicates(session); - for (const TrackId trackId : tracks.results) - { - const Track::pointer track {Track::find(session, trackId)}; - if (auto trackMBID {track->getTrackMBID()}) - { - LMS_LOG(DBUPDATER, INFO) << "Found duplicated track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); - context.stats.duplicates.emplace_back(ScanDuplicate {track->getId(), DuplicateReason::SameTrackMBID}); - context.currentStepStats.processedElems++; - _progressCallback(context.currentStepStats); - } - } + const RangeResults tracks = Track::findIdsTrackMBIDDuplicates(session); + for (const TrackId trackId : tracks.results) + { + if (_abortScan) + break; - LMS_LOG(DBUPDATER, DEBUG) << "Found " << context.currentStepStats.processedElems << " duplicated audio files"; - } + const Track::pointer track{ Track::find(session, trackId) }; + if (auto trackMBID{ track->getTrackMBID() }) + { + LMS_LOG(DBUPDATER, INFO) << "Found duplicated track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); + context.stats.duplicates.emplace_back(ScanDuplicate{ track->getId(), DuplicateReason::SameTrackMBID }); + context.currentStepStats.processedElems++; + _progressCallback(context.currentStepStats); + } + } + + LMS_LOG(DBUPDATER, DEBUG) << "Found " << context.currentStepStats.processedElems << " duplicated audio files"; + } } diff --git a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp index 38399599..ee7f55e7 100644 --- a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp +++ b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp @@ -57,6 +57,9 @@ namespace Scanner for (const ClusterId clusterId : clusterIds) { + if (_abortScan) + break; + std::size_t trackCount; std::size_t releaseCount; @@ -81,6 +84,6 @@ namespace Scanner return true; }); - LMS_LOG(DBUPDATER, DEBUG) << "Recomputed stats for " << clusterCount << " clusters!"; + LMS_LOG(DBUPDATER, DEBUG) << "Recomputed stats for " << context.currentStepStats.processedElems << " clusters!"; } } diff --git a/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp b/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp index 02f3dcfa..929ff07e 100644 --- a/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp @@ -23,26 +23,25 @@ namespace Scanner { - void - ScanStepDiscoverFiles::process(ScanContext& context) - { - context.stats.filesScanned = 0; - PathUtils::exploreFilesRecursive(context.directory, [&](std::error_code ec, const std::filesystem::path& path) - { - if (_abortScan) - return false; + void ScanStepDiscoverFiles::process(ScanContext& context) + { + context.stats.filesScanned = 0; + PathUtils::exploreFilesRecursive(context.directory, [&](std::error_code ec, const std::filesystem::path& path) + { + if (_abortScan) + return false; - if (!ec && PathUtils::hasFileAnyExtension(path, _settings.supportedExtensions)) - { - context.currentStepStats.processedElems++; - _progressCallback(context.currentStepStats); - } + if (!ec && PathUtils::hasFileAnyExtension(path, _settings.supportedExtensions)) + { + context.currentStepStats.processedElems++; + _progressCallback(context.currentStepStats); + } - return true; - }, &excludeDirFileName); + return true; + }, &excludeDirFileName); - context.stats.filesScanned = context.currentStepStats.processedElems; + context.stats.filesScanned = context.currentStepStats.processedElems; - LMS_LOG(DBUPDATER, DEBUG) << "Discovered " << context.stats.filesScanned << " files in '" << context.directory << "'"; - } + LMS_LOG(DBUPDATER, DEBUG) << "Discovered " << context.stats.filesScanned << " files in '" << context.directory << "'"; + } } diff --git a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp index 1ae6781d..097c9906 100644 --- a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp @@ -30,6 +30,49 @@ namespace Scanner { + using namespace Database; + + namespace + { + constexpr std::size_t batchSize = 100; + + template + void removeOrphanEntries(Session& session, bool& abortScan) + { + using IdType = typename T::IdType; + + RangeResults entries; + while (!abortScan) + { + { + LMS_LOG(DBUPDATER, DEBUG) << "FIND"; + auto transaction{ session.createSharedTransaction() }; + entries = T::findOrphanIds(session, Range{ 0, batchSize }); + LMS_LOG(DBUPDATER, DEBUG) << "FIND DONE"; + }; + + { + LMS_LOG(DBUPDATER, DEBUG) << "REMOVE"; + + auto transaction{ session.createUniqueTransaction() }; + for (const IdType objectId : entries.results) + { + if (abortScan) + break; + + typename T::pointer entry{ T::find(session, objectId) }; + + LMS_LOG(DBUPDATER, DEBUG) << "Removing '" << entry->getName() << "'"; + entry.remove(); + } + } + + if (!entries.moreResults) + break; + } + } + } + void ScanStepRemoveOrphanDbFiles::process(ScanContext& context) { removeOrphanTracks(context); @@ -45,7 +88,6 @@ namespace Scanner if (_abortScan) return; - static constexpr std::size_t batchSize{ 50 }; Session& session{ _db.getTLSSession() }; LMS_LOG(DBUPDATER, DEBUG) << "Checking tracks to be removed..."; @@ -85,7 +127,7 @@ namespace Scanner if (!tracksToRemove.empty()) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createUniqueTransaction() }; for (const TrackId trackId : tracksToRemove) { @@ -109,57 +151,20 @@ namespace Scanner void ScanStepRemoveOrphanDbFiles::removeOrphanClusters() { - using namespace Database; - LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan clusters..."; - Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; - - // Now process orphan Cluster (no track) - auto clusterIds{ Cluster::findOrphans(session) }; - for (ClusterId clusterId : clusterIds.results) - { - Cluster::pointer cluster{ Cluster::find(session, clusterId) }; - LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan cluster '" << cluster->getName() << "'"; - cluster.remove(); - } + removeOrphanEntries(_db.getTLSSession(), _abortScan); } void ScanStepRemoveOrphanDbFiles::removeOrphanArtists() { - using namespace Database; - LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan artists..."; - - Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; - - auto artistIds{ Artist::findOrphanIds(session) }; - for (const ArtistId artistId : artistIds.results) - { - Artist::pointer artist{ Artist::find(session, artistId) }; - LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan artist '" << artist->getName() << "'"; - artist.remove(); - } + removeOrphanEntries(_db.getTLSSession(), _abortScan); } void ScanStepRemoveOrphanDbFiles::removeOrphanReleases() { - using namespace Database; - LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan releases..."; - - // TODO, by batch - Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; - - auto releases{ Release::findOrphanIds(session) }; - for (const ReleaseId releaseId : releases.results) - { - Release::pointer release{ Release::find(session, releaseId) }; - LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan release '" << release->getName() << "'"; - release.remove(); - } + removeOrphanEntries(_db.getTLSSession(), _abortScan); } bool ScanStepRemoveOrphanDbFiles::checkFile(const std::filesystem::path& p) @@ -168,8 +173,7 @@ namespace Scanner { // For each track, make sure the the file still exists // and still belongs to a media directory - if (!std::filesystem::exists(p) - || !std::filesystem::is_regular_file(p)) + if (!std::filesystem::exists(p) || !std::filesystem::is_regular_file(p)) { LMS_LOG(DBUPDATER, INFO) << "Removing '" << p.string() << "': missing"; return false; From d7451e5393f353ac641aa34c9209e6d65e825760 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 17 Nov 2023 08:35:33 +0100 Subject: [PATCH 02/18] Added a param to set the release batch size --- src/tools/db-generator/LmsDbGenerator.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/tools/db-generator/LmsDbGenerator.cpp b/src/tools/db-generator/LmsDbGenerator.cpp index f4700fe3..efd2b2a9 100644 --- a/src/tools/db-generator/LmsDbGenerator.cpp +++ b/src/tools/db-generator/LmsDbGenerator.cpp @@ -45,6 +45,7 @@ namespace { struct GeneratorParameters { + std::size_t releaseCountPerBatch{ 1000 }; std::size_t releaseCount{ 100 }; std::size_t trackCountPerRelease{ 10 }; float compilationRatio{ 0.1 }; @@ -111,17 +112,22 @@ namespace void generate(const GeneratorParameters& params, GenerationContext& context) { - for (std::size_t i{}; i < params.releaseCount; ++i) - { - if ((i % 1000) == 0) - std::cout << "Generating album #" << i << std::endl; + std::size_t remainingCount{ params.releaseCount }; - generateRelease(params, context); + while(remainingCount > 0) + { + auto transaction{ context.session.createUniqueTransaction() }; + std::cout << "Generating album #" << params.releaseCount - remainingCount << " / " << params.releaseCount << std::endl; + + for (std::size_t i{}; i < params.releaseCountPerBatch && remainingCount-- > 0; ++i) + generateRelease(params, context); } } void prepareContext(const GeneratorParameters& params, GenerationContext& context) { + auto transaction{ context.session.createUniqueTransaction() }; + // create some random genres/moods { Database::ClusterType::pointer genre{ Database::ClusterType::find(context.session, "GENRE") }; @@ -157,6 +163,7 @@ int main(int argc, char* argv[]) po::options_description options{ "Options" }; options.add_options() ("conf,c", po::value()->default_value("/etc/lms.conf"), "lms config file") + ("release-count-per-batch", po::value()->default_value(defaultParams.releaseCountPerBatch), "Number of releases to generate before committing transaction") ("release-count", po::value()->default_value(defaultParams.releaseCount), "Number of releases to generate") ("track-count-per-release", po::value()->default_value(defaultParams.trackCountPerRelease), "Number of tracks per release") ("compilation-ratio", po::value()->default_value(defaultParams.compilationRatio), "Compilation ratio (compilation means all tracks have a different artist)") @@ -180,6 +187,7 @@ int main(int argc, char* argv[]) po::notify(vm); GeneratorParameters genParams; + genParams.releaseCountPerBatch = vm["release-count-per-batch"].as(); genParams.releaseCount = vm["release-count"].as(); genParams.trackCountPerRelease = vm["track-count-per-release"].as(); genParams.compilationRatio = vm["compilation-ratio"].as(); @@ -191,7 +199,6 @@ int main(int argc, char* argv[]) Service config{ createConfig(vm["conf"].as()) }; Database::Db db{ config->getPath("working-dir") / "lms.db" }; Database::Session session{ db }; - auto transaction{ session.createUniqueTransaction() }; std::cout << "Starting generation..." << std::endl; GenerationContext genContext{ session }; From 60bb6170e23f921a27e76a7f0c24e0bfcb8d8e3a Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 17 Nov 2023 08:36:20 +0100 Subject: [PATCH 03/18] Added a param to show the underlying db queries --- conf/lms.conf | 2 ++ src/libs/services/database/impl/Db.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/conf/lms.conf b/conf/lms.conf index 02985108..36d07f83 100644 --- a/conf/lms.conf +++ b/conf/lms.conf @@ -12,6 +12,8 @@ log-file = ""; access-log-file = ""; # Logger configuration, see log-config in https://webtoolkit.eu/wt/doc/reference/html/overview.html#config_general log-config = "* -debug -info:WebRequest"; +# Output db queries on stdout +db-show-queries = false; # Listen port/addr of the web server listen-port = 5082; diff --git a/src/libs/services/database/impl/Db.cpp b/src/libs/services/database/impl/Db.cpp index c2179165..4a83b580 100644 --- a/src/libs/services/database/impl/Db.cpp +++ b/src/libs/services/database/impl/Db.cpp @@ -24,6 +24,8 @@ #include "services/database/Session.hpp" #include "services/database/User.hpp" +#include "utils/IConfig.hpp" +#include "utils/Service.hpp" #include "utils/Logger.hpp" namespace Database @@ -87,7 +89,8 @@ namespace Database LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string(); auto connection{ std::make_unique(dbPath.string()) }; - // connection->setProperty("show-queries", "true"); + if (Service::get()->getBool("db-show-queries", false)) + connection->setProperty("show-queries", "true"); auto connectionPool{ std::make_unique(std::move(connection), connectionCount) }; connectionPool->setTimeout(std::chrono::seconds{ 10 }); From 0cc73ca022f0bb6d8a7c606122167d84c55117d0 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 17 Nov 2023 08:44:41 +0100 Subject: [PATCH 04/18] Config service may not be here (unit tests) --- src/libs/services/database/impl/Db.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/services/database/impl/Db.cpp b/src/libs/services/database/impl/Db.cpp index 4a83b580..2f67b77a 100644 --- a/src/libs/services/database/impl/Db.cpp +++ b/src/libs/services/database/impl/Db.cpp @@ -89,8 +89,8 @@ namespace Database LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string(); auto connection{ std::make_unique(dbPath.string()) }; - if (Service::get()->getBool("db-show-queries", false)) - connection->setProperty("show-queries", "true"); + if (IConfig * config{ Service::get() })// may not be here on testU + connection->setProperty("show-queries", config->getBool("db-show-queries", false) ? "true" : "false"); auto connectionPool{ std::make_unique(std::move(connection), connectionCount) }; connectionPool->setTimeout(std::chrono::seconds{ 10 }); From f2515edc8ba5ba05afff62bed1e099f98e2dda67 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 17 Nov 2023 08:57:08 +0100 Subject: [PATCH 05/18] Optimized the way we count albums for artists --- src/libs/services/database/impl/Release.cpp | 30 +++++++------------ .../include/services/database/Release.hpp | 1 + src/libs/services/database/test/Release.cpp | 3 +- src/libs/subsonic/impl/responses/Artist.cpp | 4 +-- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/libs/services/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp index f3091eb6..0f557462 100644 --- a/src/libs/services/database/impl/Release.cpp +++ b/src/libs/services/database/impl/Release.cpp @@ -39,7 +39,7 @@ namespace Database template Wt::Dbo::Query createQuery(Session& session, std::string_view itemToSelect, const Release::FindParameters& params) { - auto query{ session.getDboSession().query("SELECT DISTINCT " + std::string{ itemToSelect } + " from release r") }; + auto query{ session.getDboSession().query("SELECT " + std::string{ itemToSelect } + " from release r") }; if (params.sortMethod == ReleaseSortMethod::LastWritten || params.sortMethod == ReleaseSortMethod::Date @@ -183,21 +183,6 @@ namespace Database return query; } - - template - Wt::Dbo::Query createQuery(Session& session, const Release::FindParameters& params) - { - std::string_view itemToSelect; - - if constexpr (std::is_same_v) - itemToSelect = "r.id"; - else if constexpr (std::is_same_v>) - itemToSelect = "r"; - else - static_assert("Unhandled type"); - - return createQuery(session, itemToSelect, params); - } } Release::Release(const std::string& name, const std::optional& MBID) @@ -283,7 +268,7 @@ namespace Database { session.checkSharedLocked(); - auto query{ createQuery>(session, params) }; + auto query{ createQuery>(session, "DISTINCT r", params) }; return Utils::execQuery(query, params.range); } @@ -291,7 +276,7 @@ namespace Database { session.checkSharedLocked(); - auto query{ createQuery>(session, params) }; + auto query{ createQuery>(session, "DISTINCT r", params) }; Utils::execQuery(query, params.range, func); } @@ -299,10 +284,17 @@ namespace Database { session.checkSharedLocked(); - auto query{ createQuery(session, params) }; + auto query{ createQuery(session, "DISTINCT r.id", params) }; return Utils::execQuery(query, params.range); } + std::size_t Release::getCount(Session& session, const FindParameters& params) + { + session.checkSharedLocked(); + + return createQuery(session, "COUNT(DISTINCT r.id)", params).resultValue(); + } + std::size_t Release::getDiscCount() const { assert(session()); diff --git a/src/libs/services/database/include/services/database/Release.hpp b/src/libs/services/database/include/services/database/Release.hpp index 92bf6ffd..2000e5e0 100644 --- a/src/libs/services/database/include/services/database/Release.hpp +++ b/src/libs/services/database/include/services/database/Release.hpp @@ -91,6 +91,7 @@ namespace Database static RangeResults find(Session& session, const FindParameters& parameters); static void find(Session& session, const FindParameters& parameters, std::function func); static RangeResults findIds(Session& session, const FindParameters& parameters); + static std::size_t getCount(Session& session, const FindParameters& parameters); static RangeResults findOrphanIds(Session& session, std::optional range = std::nullopt); // not track related static RangeResults findIdsOrderedByArtist(Session& session, std::optional range = std::nullopt); diff --git a/src/libs/services/database/test/Release.cpp b/src/libs/services/database/test/Release.cpp index 520c5530..1162a89a 100644 --- a/src/libs/services/database/test/Release.cpp +++ b/src/libs/services/database/test/Release.cpp @@ -27,6 +27,7 @@ TEST_F(DatabaseFixture, Release) auto transaction{ session.createSharedTransaction() }; EXPECT_EQ(Release::getCount(session), 0); + EXPECT_EQ(Release::getCount(session, Release::FindParameters{}), 0); EXPECT_FALSE(Release::exists(session, 0)); EXPECT_FALSE(Release::exists(session, 1)); } @@ -37,6 +38,7 @@ TEST_F(DatabaseFixture, Release) auto transaction{ session.createSharedTransaction() }; EXPECT_EQ(Release::getCount(session), 1); + EXPECT_EQ(Release::getCount(session, Release::FindParameters{}), 1); EXPECT_TRUE(Release::exists(session, release.getId())); { @@ -616,7 +618,6 @@ TEST_F(DatabaseFixture, Release_sortMethod) } } - TEST_F(DatabaseFixture, Release_meanBitrate) { ScopedRelease release1{ session, "MyRelease1" }; diff --git a/src/libs/subsonic/impl/responses/Artist.cpp b/src/libs/subsonic/impl/responses/Artist.cpp index bd7c6cf2..54635316 100644 --- a/src/libs/subsonic/impl/responses/Artist.cpp +++ b/src/libs/subsonic/impl/responses/Artist.cpp @@ -82,8 +82,8 @@ namespace API::Subsonic if (id3) { - const auto releases{ Release::find(context.dbSession, Release::FindParameters {}.setArtist(artist->getId())) }; - artistNode.setAttribute("albumCount", releases.results.size()); + const std::size_t count{ Release::getCount(context.dbSession, Release::FindParameters {}.setArtist(artist->getId())) }; + artistNode.setAttribute("albumCount", count); } if (const Wt::WDateTime dateTime{ Service::get()->getStarredDateTime(user->getId(), artist->getId()) }; dateTime.isValid()) From 575fec5727b29951a87578e338b1c4bbcac77941 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 17 Nov 2023 20:43:04 +0100 Subject: [PATCH 06/18] Removed some useless intermediate queries --- .../services/database/impl/StarredArtist.cpp | 11 +++ .../services/database/impl/StarredRelease.cpp | 11 +++ .../services/database/impl/StarredTrack.cpp | 20 +++++ .../services/database/StarredArtist.hpp | 1 + .../services/database/StarredRelease.hpp | 1 + .../services/database/StarredTrack.hpp | 2 + .../services/database/test/StarredArtist.cpp | 23 ++++++ .../services/database/test/StarredRelease.cpp | 12 +++ .../services/database/test/StarredTrack.cpp | 74 +++++++++++-------- .../feedback/impl/FeedbackService.impl.hpp | 12 +-- .../listenbrainz/FeedbacksSynchronizer.cpp | 4 +- 11 files changed, 127 insertions(+), 44 deletions(-) diff --git a/src/libs/services/database/impl/StarredArtist.cpp b/src/libs/services/database/impl/StarredArtist.cpp index 0427c80b..0508cef8 100644 --- a/src/libs/services/database/impl/StarredArtist.cpp +++ b/src/libs/services/database/impl/StarredArtist.cpp @@ -53,6 +53,17 @@ namespace Database return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } + StarredArtist::pointer StarredArtist::find(Session& session, ArtistId artistId, UserId userId) + { + session.checkSharedLocked(); + return session.getDboSession().query>("SELECT s_a from starred_artist s_a") + .join("user u ON u.id = s_a.user_id") + .where("s_a.artist_id = ?").bind(artistId) + .where("s_a.user_id = ?").bind(userId) + .where("s_a.backend = u.feedback_backend") + .resultValue(); + } + StarredArtist::pointer StarredArtist::find(Session& session, ArtistId artistId, UserId userId, FeedbackBackend backend) { session.checkSharedLocked(); diff --git a/src/libs/services/database/impl/StarredRelease.cpp b/src/libs/services/database/impl/StarredRelease.cpp index 7645d3f9..39c83274 100644 --- a/src/libs/services/database/impl/StarredRelease.cpp +++ b/src/libs/services/database/impl/StarredRelease.cpp @@ -53,6 +53,17 @@ namespace Database return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } + StarredRelease::pointer StarredRelease::find(Session& session, ReleaseId releaseId, UserId userId) + { + session.checkSharedLocked(); + return session.getDboSession().query>("SELECT s_r from starred_release s_r") + .join("user u ON u.id = s_r.user_id") + .where("s_r.release_id = ?").bind(releaseId) + .where("s_r.user_id = ?").bind(userId) + .where("s_r.backend = u.feedback_backend") + .resultValue(); + } + StarredRelease::pointer StarredRelease::find(Session& session, ReleaseId releaseId, UserId userId, FeedbackBackend backend) { session.checkSharedLocked(); diff --git a/src/libs/services/database/impl/StarredTrack.cpp b/src/libs/services/database/impl/StarredTrack.cpp index 78112ded..1a8252e3 100644 --- a/src/libs/services/database/impl/StarredTrack.cpp +++ b/src/libs/services/database/impl/StarredTrack.cpp @@ -53,6 +53,17 @@ namespace Database return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } + StarredTrack::pointer StarredTrack::find(Session& session, TrackId trackId, UserId userId) + { + session.checkSharedLocked(); + return session.getDboSession().query>("SELECT s_t from starred_track s_t") + .join("user u ON u.id = s_t.user_id") + .where("s_t.track_id = ?").bind(trackId) + .where("s_t.user_id = ?").bind(userId) + .where("s_t.backend = u.feedback_backend") + .resultValue(); + } + StarredTrack::pointer StarredTrack::find(Session& session, TrackId trackId, UserId userId, FeedbackBackend backend) { session.checkSharedLocked(); @@ -63,6 +74,15 @@ namespace Database .resultValue(); } + bool StarredTrack::exists(Session& session, TrackId trackId, UserId userId, FeedbackBackend backend) + { + return session.getDboSession().query("SELECT 1 from starred_track") + .where("track_id = ?").bind(trackId) + .where("user_id = ?").bind(userId) + .where("backend = ?").bind(backend) + .resultValue() == 1; + } + RangeResults StarredTrack::find(Session& session, const FindParameters& params) { session.checkSharedLocked(); diff --git a/src/libs/services/database/include/services/database/StarredArtist.hpp b/src/libs/services/database/include/services/database/StarredArtist.hpp index 1d02eaf5..6d53225c 100644 --- a/src/libs/services/database/include/services/database/StarredArtist.hpp +++ b/src/libs/services/database/include/services/database/StarredArtist.hpp @@ -42,6 +42,7 @@ namespace Database // Search utility static std::size_t getCount(Session& session); static pointer find(Session& session, StarredArtistId id); + static pointer find(Session& session, ArtistId artistId, UserId userId); // current backend static pointer find(Session& session, ArtistId artistId, UserId userId, FeedbackBackend backend); // Accessors diff --git a/src/libs/services/database/include/services/database/StarredRelease.hpp b/src/libs/services/database/include/services/database/StarredRelease.hpp index 23d4116e..f42866d4 100644 --- a/src/libs/services/database/include/services/database/StarredRelease.hpp +++ b/src/libs/services/database/include/services/database/StarredRelease.hpp @@ -42,6 +42,7 @@ namespace Database // Search utility static std::size_t getCount(Session& session); static pointer find(Session& session, StarredReleaseId id); + static pointer find(Session& session, ReleaseId releaseId, UserId userId); // current feedback backend static pointer find(Session& session, ReleaseId releaseId, UserId userId, FeedbackBackend backend); // Accessors diff --git a/src/libs/services/database/include/services/database/StarredTrack.hpp b/src/libs/services/database/include/services/database/StarredTrack.hpp index 5db11aaa..fa85f41d 100644 --- a/src/libs/services/database/include/services/database/StarredTrack.hpp +++ b/src/libs/services/database/include/services/database/StarredTrack.hpp @@ -55,7 +55,9 @@ namespace Database // Search utility static std::size_t getCount(Session& session); static pointer find(Session& session, StarredTrackId id); + static pointer find(Session& session, TrackId trackId, UserId userId); // current feedback backend static pointer find(Session& session, TrackId trackId, UserId userId, FeedbackBackend backend); + static bool exists(Session& session, TrackId trackId, UserId userId, FeedbackBackend backend); static RangeResults find(Session& session, const FindParameters& findParams); // Accessors diff --git a/src/libs/services/database/test/StarredArtist.cpp b/src/libs/services/database/test/StarredArtist.cpp index 8be5df9b..55e7d9da 100644 --- a/src/libs/services/database/test/StarredArtist.cpp +++ b/src/libs/services/database/test/StarredArtist.cpp @@ -62,6 +62,29 @@ TEST_F(DatabaseFixture, StarredArtist) artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal)); EXPECT_EQ(artists.results.size(), 0); } + + { + auto transaction{ session.createUniqueTransaction() }; + user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz); + } + + { + auto transaction{ session.createSharedTransaction() }; + + auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) }; + EXPECT_EQ(gotArtist, Artist::pointer{}); + } + + { + auto transaction{ session.createUniqueTransaction() }; + user.get().modify()->setFeedbackBackend(FeedbackBackend::Internal); + } + + { + auto transaction{ session.createUniqueTransaction() }; + auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) }; + EXPECT_EQ(gotArtist->getId(), starredArtist->getId()); + } } TEST_F(DatabaseFixture, StarredArtist_PendingDestroy) diff --git a/src/libs/services/database/test/StarredRelease.cpp b/src/libs/services/database/test/StarredRelease.cpp index e595a416..91b3871e 100644 --- a/src/libs/services/database/test/StarredRelease.cpp +++ b/src/libs/services/database/test/StarredRelease.cpp @@ -62,6 +62,18 @@ TEST_F(DatabaseFixture, StarredRelease) releases = Release::find(session, Release::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal)); EXPECT_EQ(releases.results.size(), 0); } + + { + auto transaction{ session.createUniqueTransaction() }; + user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz); + } + + { + auto transaction{ session.createSharedTransaction() }; + + auto gotRelease{ StarredRelease::find(session, release->getId(), user->getId()) }; + EXPECT_EQ(gotRelease, StarredRelease::pointer{}); + } } TEST_F(DatabaseFixture, Starredrelease_PendingDestroy) diff --git a/src/libs/services/database/test/StarredTrack.cpp b/src/libs/services/database/test/StarredTrack.cpp index a471215e..cd7f45f2 100644 --- a/src/libs/services/database/test/StarredTrack.cpp +++ b/src/libs/services/database/test/StarredTrack.cpp @@ -26,98 +26,110 @@ using ScopedStarredTrack = ScopedEntity; TEST_F(DatabaseFixture, StarredTrack) { - ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - ScopedUser user2 {session, "MyUser2"}; + ScopedTrack track{ session, "MyTrack" }; + ScopedUser user{ session, "MyUser" }; + ScopedUser user2{ session, "MyUser2" }; { - auto transaction {session.createSharedTransaction()}; + auto transaction{ session.createSharedTransaction() }; - auto starredTrack {StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal)}; + auto starredTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_FALSE(starredTrack); EXPECT_EQ(StarredTrack::getCount(session), 0); - auto tracks {Track::findIds(session, Track::FindParameters {})}; + auto tracks{ Track::findIds(session, Track::FindParameters {}) }; EXPECT_EQ(tracks.results.size(), 1); } - ScopedStarredTrack starredTrack {session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal}; + ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction {session.createSharedTransaction()}; + auto transaction{ session.createSharedTransaction() }; - auto gotTrack {StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal)}; + auto gotTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_EQ(gotTrack->getId(), starredTrack->getId()); EXPECT_EQ(StarredTrack::getCount(session), 1); } { - auto transaction {session.createSharedTransaction()}; + auto transaction{ session.createSharedTransaction() }; - auto tracks {Track::findIds(session, Track::FindParameters {})}; + auto tracks{ Track::findIds(session, Track::FindParameters {}) }; EXPECT_EQ(tracks.results.size(), 1); - tracks = Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)); + tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal)); EXPECT_EQ(tracks.results.size(), 1); - tracks = Track::findIds(session, Track::FindParameters {}.setStarringUser(user2.getId(), FeedbackBackend::Internal)); + tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal)); EXPECT_EQ(tracks.results.size(), 0); } + + { + auto transaction{ session.createUniqueTransaction() }; + user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz); + } + + { + auto transaction{ session.createSharedTransaction() }; + + auto gotRelease{ StarredTrack::find(session, track->getId(), user->getId()) }; + EXPECT_EQ(gotRelease, StarredTrack::pointer{}); + } } TEST_F(DatabaseFixture, Starredtrack_PendingDestroy) { - ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - ScopedStarredTrack starredTrack {session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal}; + ScopedTrack track{ session, "MyTrack" }; + ScopedUser user{ session, "MyUser" }; + ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; - auto tracks {Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal))}; + auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(tracks.results.size(), 1); starredTrack.get().modify()->setSyncState(SyncState::PendingRemove); - tracks = Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)); + tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal)); EXPECT_EQ(tracks.results.size(), 0); } } TEST_F(DatabaseFixture, StarredTrack_dateTime) { - ScopedTrack track1 {session, "MyTrack1"}; - ScopedTrack track2 {session, "MyTrack2"}; - ScopedUser user {session, "MyUser"}; + ScopedTrack track1{ session, "MyTrack1" }; + ScopedTrack track2{ session, "MyTrack2" }; + ScopedUser user{ session, "MyUser" }; - ScopedStarredTrack starredTrack1 {session, track1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal}; - ScopedStarredTrack starredTrack2 {session, track2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal}; + ScopedStarredTrack starredTrack1{ session, track1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; + ScopedStarredTrack starredTrack2{ session, track2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; - const Wt::WDateTime dateTime {Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1}}; + const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} }; { - auto transaction {session.createSharedTransaction()}; + auto transaction{ session.createSharedTransaction() }; - auto tracks {Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal))}; + auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(tracks.results.size(), 2); } { - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; starredTrack1.get().modify()->setDateTime(dateTime); starredTrack2.get().modify()->setDateTime(dateTime.addSecs(-1)); - auto tracks {Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc))}; + auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc)) }; ASSERT_EQ(tracks.results.size(), 2); EXPECT_EQ(tracks.results[0], starredTrack1->getTrack()->getId()); EXPECT_EQ(tracks.results[1], starredTrack2->getTrack()->getId()); } { - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; starredTrack1.get().modify()->setDateTime(dateTime); starredTrack2.get().modify()->setDateTime(dateTime.addSecs(1)); - auto tracks {Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc))}; + auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc)) }; ASSERT_EQ(tracks.results.size(), 2); EXPECT_EQ(tracks.results[0], starredTrack2->getTrack()->getId()); EXPECT_EQ(tracks.results[1], starredTrack1->getTrack()->getId()); diff --git a/src/libs/services/feedback/impl/FeedbackService.impl.hpp b/src/libs/services/feedback/impl/FeedbackService.impl.hpp index f16be52c..b9220ffd 100644 --- a/src/libs/services/feedback/impl/FeedbackService.impl.hpp +++ b/src/libs/services/feedback/impl/FeedbackService.impl.hpp @@ -82,28 +82,20 @@ namespace Feedback template bool FeedbackService::isStarred(UserId userId, ObjIdType objId) { - const auto backend{ getUserFeedbackBackend(userId) }; - if (!backend) - return false; - Session& session{ _db.getTLSSession() }; auto transaction{ session.createSharedTransaction() }; - typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId, *backend) }; + typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId) }; return starredObj && (starredObj->getSyncState() != SyncState::PendingRemove); } template Wt::WDateTime FeedbackService::getStarredDateTime(UserId userId, ObjIdType objId) { - const auto backend{ getUserFeedbackBackend(userId) }; - if (!backend) - return {}; - Session& session{ _db.getTLSSession() }; auto transaction{ session.createSharedTransaction() }; - typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId, *backend) }; + typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId) }; if (starredObj && (starredObj->getSyncState() != SyncState::PendingRemove)) return starredObj->getDateTime(); diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp index 1cd2c64a..6a9e91fa 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp @@ -451,9 +451,7 @@ namespace Feedback::ListenBrainz } trackId = tracks.front()->getId(); - - const StarredTrack::pointer starredTrack{ StarredTrack::find(session, trackId, context.userId, Database::FeedbackBackend::ListenBrainz) }; - needImport = !starredTrack; + needImport = !StarredTrack::exists(session, trackId, context.userId, Database::FeedbackBackend::ListenBrainz); // don't update starred date time // no need to update state if it was found as not synchronized From 17a2a969e2321c28af08f86d46830f6d59196d58 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 19 Nov 2023 16:17:27 +0100 Subject: [PATCH 07/18] Reduced exclusive lock durations --- .../services/auth/impl/AuthServiceBase.cpp | 4 +- .../services/auth/impl/AuthTokenService.cpp | 6 +- .../impl/internal/InternalPasswordService.cpp | 4 +- src/libs/services/cover/impl/CoverService.cpp | 4 +- src/libs/services/database/CMakeLists.txt | 1 + src/libs/services/database/impl/Artist.cpp | 20 +- src/libs/services/database/impl/AuthToken.cpp | 4 +- src/libs/services/database/impl/Cluster.cpp | 28 +-- src/libs/services/database/impl/Listen.cpp | 30 +-- src/libs/services/database/impl/Migration.cpp | 6 +- src/libs/services/database/impl/Release.cpp | 22 +- .../services/database/impl/ScanSettings.cpp | 6 +- src/libs/services/database/impl/Session.cpp | 62 +++--- .../services/database/impl/StarredArtist.cpp | 8 +- .../services/database/impl/StarredRelease.cpp | 8 +- .../services/database/impl/StarredTrack.cpp | 10 +- src/libs/services/database/impl/Track.cpp | 30 +-- .../database/impl/TrackArtistLink.cpp | 12 +- .../services/database/impl/TrackBookmark.cpp | 8 +- .../services/database/impl/TrackFeatures.cpp | 8 +- src/libs/services/database/impl/TrackList.cpp | 10 +- .../database/impl/TransactionChecker.cpp | 109 ++++++++++ src/libs/services/database/impl/User.cpp | 6 +- .../include/services/database/Object.hpp | 5 +- .../include/services/database/Session.hpp | 34 +-- .../services/database/TransactionChecker.hpp | 53 +++++ src/libs/services/database/test/Artist.cpp | 72 +++---- src/libs/services/database/test/Cluster.cpp | 118 +++++------ src/libs/services/database/test/Common.cpp | 4 +- src/libs/services/database/test/Common.hpp | 8 +- .../services/database/test/DatabaseTest.cpp | 14 +- src/libs/services/database/test/Listen.cpp | 194 +++++++++--------- src/libs/services/database/test/Release.cpp | 112 +++++----- .../services/database/test/StarredArtist.cpp | 22 +- .../services/database/test/StarredRelease.cpp | 18 +- .../services/database/test/StarredTrack.cpp | 18 +- src/libs/services/database/test/Track.cpp | 24 +-- .../services/database/test/TrackBookmark.cpp | 8 +- .../services/database/test/TrackFeatures.cpp | 4 +- src/libs/services/database/test/TrackList.cpp | 40 ++-- .../feedback/impl/FeedbackService.cpp | 8 +- .../feedback/impl/FeedbackService.impl.hpp | 8 +- .../impl/internal/InternalBackend.cpp | 4 +- .../listenbrainz/FeedbacksSynchronizer.cpp | 12 +- .../impl/listenbrainz/ListenBrainzBackend.cpp | 4 +- .../feedback/impl/listenbrainz/Utils.cpp | 2 +- .../impl/PlaylistGeneratorService.cpp | 2 +- .../impl/RecommendationService.cpp | 2 +- .../impl/clusters/ClustersEngine.cpp | 8 +- .../impl/features/FeaturesEngine.cpp | 14 +- .../ConsecutiveArtists.cpp | 2 +- .../ConsecutiveReleases.cpp | 2 +- .../impl/ScanStepCheckDuplicatedDbFiles.cpp | 2 +- .../impl/ScanStepComputeClusterStats.cpp | 8 +- .../impl/ScanStepRemoveOrphanDbFiles.cpp | 12 +- .../scanner/impl/ScanStepScanFiles.cpp | 4 +- .../services/scanner/impl/ScannerService.cpp | 2 +- .../scrobbling/impl/ScrobblingService.cpp | 18 +- .../impl/internal/InternalBackend.cpp | 2 +- .../impl/listenbrainz/ListenBrainzBackend.cpp | 2 +- .../impl/listenbrainz/ListensSynchronizer.cpp | 10 +- .../scrobbling/impl/listenbrainz/Utils.cpp | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 4 +- .../impl/entrypoints/AlbumSongLists.cpp | 8 +- .../subsonic/impl/entrypoints/Bookmarks.cpp | 6 +- .../subsonic/impl/entrypoints/Browsing.cpp | 26 +-- .../impl/entrypoints/MediaRetrieval.cpp | 4 +- .../subsonic/impl/entrypoints/Playlists.cpp | 10 +- .../subsonic/impl/entrypoints/Searching.cpp | 2 +- .../impl/entrypoints/UserManagement.cpp | 14 +- src/lms/main.cpp | 4 +- src/lms/ui/Auth.cpp | 4 +- src/lms/ui/LmsApplication.cpp | 8 +- src/lms/ui/MediaPlayer.cpp | 2 +- src/lms/ui/PlayQueue.cpp | 44 ++-- src/lms/ui/SettingsView.cpp | 6 +- src/lms/ui/Utils.cpp | 6 +- src/lms/ui/admin/DatabaseSettingsView.cpp | 4 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/ScannerController.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 14 +- src/lms/ui/admin/UsersView.cpp | 4 +- src/lms/ui/explore/ArtistCollector.cpp | 8 +- src/lms/ui/explore/ArtistListHelpers.cpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 8 +- src/lms/ui/explore/ArtistsView.cpp | 2 +- src/lms/ui/explore/Filters.cpp | 6 +- src/lms/ui/explore/PlayQueueController.cpp | 8 +- src/lms/ui/explore/ReleaseCollector.cpp | 8 +- src/lms/ui/explore/ReleaseView.cpp | 8 +- src/lms/ui/explore/ReleasesView.cpp | 2 +- src/lms/ui/explore/SearchView.cpp | 6 +- src/lms/ui/explore/TrackCollector.cpp | 8 +- src/lms/ui/explore/TrackListHelpers.cpp | 4 +- src/lms/ui/explore/TrackListView.cpp | 6 +- src/lms/ui/explore/TrackListsView.cpp | 2 +- src/lms/ui/explore/TracksView.cpp | 2 +- src/lms/ui/resource/AudioFileResource.cpp | 2 +- .../ui/resource/AudioTranscodingResource.cpp | 2 +- src/lms/ui/resource/DownloadResource.cpp | 16 +- src/tools/cover/LmsCover.cpp | 2 +- src/tools/db-generator/LmsDbGenerator.cpp | 4 +- .../recommendation/LmsRecommendation.cpp | 12 +- .../LmsSimilarityParameters.cpp | 10 +- 104 files changed, 881 insertions(+), 705 deletions(-) create mode 100644 src/libs/services/database/impl/TransactionChecker.cpp create mode 100644 src/libs/services/database/include/services/database/TransactionChecker.hpp diff --git a/src/libs/services/auth/impl/AuthServiceBase.cpp b/src/libs/services/auth/impl/AuthServiceBase.cpp index 133ce8f5..1dc01a72 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.cpp +++ b/src/libs/services/auth/impl/AuthServiceBase.cpp @@ -36,7 +36,7 @@ namespace Auth AuthServiceBase::getOrCreateUser(std::string_view loginName) { Session& session {getDbSession()}; - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; User::pointer user {User::find(session, loginName)}; if (!user) @@ -56,7 +56,7 @@ namespace Auth AuthServiceBase::onUserAuthenticated(UserId userId) { Session& session {getDbSession()}; - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; User::pointer user {User::find(session, userId)}; if (user) diff --git a/src/libs/services/auth/impl/AuthTokenService.cpp b/src/libs/services/auth/impl/AuthTokenService.cpp index 7decbc14..95d33343 100644 --- a/src/libs/services/auth/impl/AuthTokenService.cpp +++ b/src/libs/services/auth/impl/AuthTokenService.cpp @@ -54,7 +54,7 @@ namespace Auth Database::Session& session {getDbSession()}; - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; Database::User::pointer user {Database::User::find(session, userId)}; if (!user) @@ -76,7 +76,7 @@ namespace Auth const std::string secretHash {sha1Function.compute(std::string {secret}, {})}; Database::Session& session {getDbSession()}; - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; Database::AuthToken::pointer authToken {Database::AuthToken::find(session, secretHash)}; if (!authToken) @@ -131,7 +131,7 @@ namespace Auth { Database::Session& session {getDbSession()}; - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; Database::User::pointer user {Database::User::find(session, userId)}; if (!user) diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp index ebc55158..9d078b02 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp @@ -49,7 +49,7 @@ namespace Auth Database::User::PasswordHash passwordHash; { Database::Session& session {getDbSession()}; - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; const Database::User::pointer user {Database::User::find(session, loginName)}; if (!user) @@ -100,7 +100,7 @@ namespace Auth const Database::User::PasswordHash passwordHash {hashPassword(newPassword)}; Database::Session& session {getDbSession()}; - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; Database::User::pointer user {Database::User::find(session, userId)}; if (!user) diff --git a/src/libs/services/cover/impl/CoverService.cpp b/src/libs/services/cover/impl/CoverService.cpp index 773e41f9..30708f6b 100644 --- a/src/libs/services/cover/impl/CoverService.cpp +++ b/src/libs/services/cover/impl/CoverService.cpp @@ -51,7 +51,7 @@ namespace Cover { std::optional res; - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; const Database::Track::pointer track{ Database::Track::find(dbSession, trackId) }; if (!track) @@ -372,7 +372,7 @@ namespace Cover { std::optional res; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; // get a track in this release, consider the release is in a single directory const auto tracks{ Track::find(session, Track::FindParameters {}.setRelease(releaseId).setRange(Range{ 0, 1 }).setSortMethod(TrackSortMethod::Release)) }; diff --git a/src/libs/services/database/CMakeLists.txt b/src/libs/services/database/CMakeLists.txt index dc1a11f5..1d147d23 100644 --- a/src/libs/services/database/CMakeLists.txt +++ b/src/libs/services/database/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(lmsdatabase SHARED impl/SqlQuery.cpp impl/Track.cpp impl/TrackBookmark.cpp + impl/TransactionChecker.cpp impl/Types.cpp impl/User.cpp impl/Utils.cpp diff --git a/src/libs/services/database/impl/Artist.cpp b/src/libs/services/database/impl/Artist.cpp index 5f8db6eb..393c2736 100644 --- a/src/libs/services/database/impl/Artist.cpp +++ b/src/libs/services/database/impl/Artist.cpp @@ -38,7 +38,7 @@ namespace Database template Wt::Dbo::Query createQuery(Session& session, std::string_view itemToSelect, const Artist::FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT DISTINCT " + std::string{ itemToSelect } + " FROM artist a") }; if (params.sortMethod == ArtistSortMethod::LastWritten @@ -175,14 +175,14 @@ namespace Database std::size_t Artist::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM artist"); } std::vector Artist::find(Session& session, const std::string& name) { - session.checkSharedLocked(); + session.checkReadTransaction(); Wt::Dbo::collection> res = session.getDboSession().find() .where("name = ?").bind(std::string{ name, 0, _maxNameLength }) @@ -193,33 +193,33 @@ namespace Database Artist::pointer Artist::find(Session& session, const UUID& mbid) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("mbid = ?").bind(std::string{ mbid.getAsString() }).resultValue(); } Artist::pointer Artist::find(Session& session, ArtistId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } bool Artist::exists(Session& session, ArtistId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT 1 FROM artist").where("id = ?").bind(id).resultValue() == 1; } RangeResults Artist::findOrphanIds(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT DISTINCT a.id FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)") }; return Utils::execQuery(query, range); } RangeResults Artist::findIds(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery(session, params) }; return Utils::execQuery(query, params.range); @@ -227,7 +227,7 @@ namespace Database RangeResults Artist::find(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery>(session, params) }; return Utils::execQuery(query, params.range); @@ -235,7 +235,7 @@ namespace Database void Artist::find(Session& session, const FindParameters& params, std::function func) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery>(session, params) }; Utils::execQuery(query, params.range, func); diff --git a/src/libs/services/database/impl/AuthToken.cpp b/src/libs/services/database/impl/AuthToken.cpp index b2702d00..7093a2b9 100644 --- a/src/libs/services/database/impl/AuthToken.cpp +++ b/src/libs/services/database/impl/AuthToken.cpp @@ -44,7 +44,7 @@ namespace Database void AuthToken::removeExpiredTokens(Session& session, const Wt::WDateTime& now) { - session.checkUniqueLocked(); + session.checkWriteTransaction(); session.getDboSession().execute("DELETE FROM auth_token WHERE expiry < ?").bind(now); } @@ -52,7 +52,7 @@ namespace Database AuthToken::pointer AuthToken::find(Session& session, std::string_view value) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("value = ?").bind(value) diff --git a/src/libs/services/database/impl/Cluster.cpp b/src/libs/services/database/impl/Cluster.cpp index f93e8de2..75dfabc8 100644 --- a/src/libs/services/database/impl/Cluster.cpp +++ b/src/libs/services/database/impl/Cluster.cpp @@ -35,7 +35,7 @@ namespace Database template Wt::Dbo::Query createQuery(Session& session, std::string_view itemToSelect, const Cluster::FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT DISTINCT " + std::string{ itemToSelect } + " FROM cluster c") }; @@ -85,14 +85,14 @@ namespace Database std::size_t Cluster::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM cluster"); } RangeResults Cluster::findIds(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery(session, params) }; return Utils::execQuery(query, params.range); @@ -100,7 +100,7 @@ namespace Database RangeResults Cluster::find(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery>(session, params) }; return Utils::execQuery(query, params.range); @@ -108,7 +108,7 @@ namespace Database RangeResults Cluster::findOrphans(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT DISTINCT c.id FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)") }; return Utils::execQuery(query, range); @@ -116,14 +116,14 @@ namespace Database Cluster::pointer Cluster::find(Session& session, ClusterId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } std::size_t Cluster::computeTrackCount(Session& session, ClusterId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(t.id) FROM track t INNER JOIN track_cluster t_c ON t_c.track_id = t.id") .where("t_c.cluster_id = ?").bind(id).resultValue(); @@ -131,7 +131,7 @@ namespace Database std::size_t Cluster::computeReleaseCount(Session& session, ClusterId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(DISTINCT r.id) FROM release r INNER JOIN track t on t.release_id = r.id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") .where("t_c.cluster_id = ?").bind(id).resultValue(); @@ -164,7 +164,7 @@ namespace Database std::size_t ClusterType::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM cluster_type"); } @@ -172,7 +172,7 @@ namespace Database RangeResults ClusterType::findOrphans(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query( "SELECT c_t.id from cluster_type c_t" @@ -184,7 +184,7 @@ namespace Database RangeResults ClusterType::findUsed(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query( "SELECT DISTINCT c_t.id from cluster_type c_t") @@ -195,21 +195,21 @@ namespace Database ClusterType::pointer ClusterType::find(Session& session, std::string_view name) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("name = ?").bind(std::string{ name }).resultValue(); } ClusterType::pointer ClusterType::find(Session& session, ClusterTypeId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } RangeResults ClusterType::find(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT id from cluster_type") }; diff --git a/src/libs/services/database/impl/Listen.cpp b/src/libs/services/database/impl/Listen.cpp index 4f645a04..60108db8 100644 --- a/src/libs/services/database/impl/Listen.cpp +++ b/src/libs/services/database/impl/Listen.cpp @@ -140,25 +140,25 @@ namespace Database Listen::pointer Listen::create(Session& session, ObjectPtr user, ObjectPtr track, ScrobblingBackend backend, const Wt::WDateTime& dateTime) { - session.checkUniqueLocked(); + session.checkWriteTransaction(); return session.getDboSession().add(std::unique_ptr {new Listen{ user, track, backend, dateTime }}); } std::size_t Listen::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM listen"); } Listen::pointer Listen::find(Session& session, ListenId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } RangeResults Listen::find(Session& session, const FindParameters& parameters) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT id FROM listen") .orderBy("date_time") }; @@ -177,7 +177,7 @@ namespace Database Listen::pointer Listen::find(Session& session, UserId userId, TrackId trackId, ScrobblingBackend backend, const Wt::WDateTime& dateTime) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("user_id = ?").bind(userId) @@ -189,7 +189,7 @@ namespace Database RangeResults Listen::getTopArtists(Session& session, UserId userId, ScrobblingBackend backend, const std::vector& clusterIds, std::optional linkType, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createArtistsQuery(session.getDboSession(), userId, backend, clusterIds, linkType) }; auto collection{ query @@ -201,7 +201,7 @@ namespace Database RangeResults Listen::getTopReleases(Session& session, UserId userId, ScrobblingBackend backend, const std::vector& clusterIds, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createReleasesQuery(session.getDboSession(), userId, backend, clusterIds) .orderBy("COUNT(r.id) DESC") .groupBy("r.id") }; @@ -211,7 +211,7 @@ namespace Database RangeResults Listen::getTopTracks(Session& session, UserId userId, ScrobblingBackend backend, const std::vector& clusterIds, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createTracksQuery(session.getDboSession(), userId, backend, clusterIds) .orderBy("COUNT(t.id) DESC") .groupBy("t.id") }; @@ -221,7 +221,7 @@ namespace Database RangeResults Listen::getRecentArtists(Session& session, UserId userId, ScrobblingBackend backend, const std::vector& clusterIds, std::optional linkType, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createArtistsQuery(session.getDboSession(), userId, backend, clusterIds, linkType) .groupBy("a.id").having("l.date_time = MAX(l.date_time)") .orderBy("l.date_time DESC") }; @@ -231,7 +231,7 @@ namespace Database RangeResults Listen::getRecentReleases(Session& session, UserId userId, ScrobblingBackend backend, const std::vector& clusterIds, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createReleasesQuery(session.getDboSession(), userId, backend, clusterIds) .groupBy("r.id").having("l.date_time = MAX(l.date_time)") .orderBy("l.date_time DESC") }; @@ -241,7 +241,7 @@ namespace Database RangeResults Listen::getRecentTracks(Session& session, UserId userId, ScrobblingBackend backend, const std::vector& clusterIds, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createTracksQuery(session.getDboSession(), userId, backend, clusterIds) .groupBy("t.id").having("l.date_time = MAX(l.date_time)") .orderBy("l.date_time DESC") }; @@ -251,7 +251,7 @@ namespace Database std::size_t Listen::getCount(Session& session, UserId userId, ScrobblingBackend backend, TrackId trackId) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) from listen l") .where("l.track_id = ?").bind(trackId) @@ -262,7 +262,7 @@ namespace Database std::size_t Listen::getCount(Session& session, UserId userId, ScrobblingBackend backend, ReleaseId releaseId) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT IFNULL(MIN(count_result), 0)" " FROM (" @@ -279,7 +279,7 @@ namespace Database Listen::pointer Listen::getMostRecentListen(Session& session, UserId userId, ScrobblingBackend backend, ReleaseId releaseId) { - session.checkSharedLocked(); + session.checkReadTransaction(); // TODO not pending remove? return session.getDboSession().query>("SELECT l from listen l") @@ -294,7 +294,7 @@ namespace Database Listen::pointer Listen::getMostRecentListen(Session& session, UserId userId, ScrobblingBackend backend, TrackId trackId) { - session.checkSharedLocked(); + session.checkReadTransaction(); // TODO not pending remove? return session.getDboSession().query>("SELECT l from listen l") .where("l.track_id = ?").bind(trackId) diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index 93fb283a..fbf5d577 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -32,7 +32,7 @@ namespace Database { VersionInfo::pointer VersionInfo::getOrCreate(Session& session) { - session.checkUniqueLocked(); + session.checkWriteTransaction(); pointer versionInfo{ session.getDboSession().find() }; if (!versionInfo) @@ -43,7 +43,7 @@ namespace Database VersionInfo::pointer VersionInfo::get(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find(); } @@ -276,7 +276,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( }; { - auto uniqueTransaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; Version version; try diff --git a/src/libs/services/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp index 0f557462..37c87efb 100644 --- a/src/libs/services/database/impl/Release.cpp +++ b/src/libs/services/database/impl/Release.cpp @@ -198,7 +198,7 @@ namespace Database std::vector Release::find(Session& session, const std::string& name) { - session.checkUniqueLocked(); + session.checkWriteTransaction(); auto res{ session.getDboSession() .find() @@ -210,7 +210,7 @@ namespace Database Release::pointer Release::find(Session& session, const UUID& mbid) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession() .find() @@ -220,7 +220,7 @@ namespace Database Release::pointer Release::find(Session& session, ReleaseId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession() .find() @@ -230,20 +230,20 @@ namespace Database bool Release::exists(Session& session, ReleaseId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT 1 FROM release").where("id = ?").bind(id).resultValue() == 1; } std::size_t Release::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM release"); } RangeResults Release::findIdsOrderedByArtist(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); // TODO merge with find auto query{ session.getDboSession().query( @@ -258,7 +258,7 @@ namespace Database RangeResults Release::findOrphanIds(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("select r.id from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL") }; return Utils::execQuery(query, range); @@ -266,7 +266,7 @@ namespace Database RangeResults Release::find(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery>(session, "DISTINCT r", params) }; return Utils::execQuery(query, params.range); @@ -274,7 +274,7 @@ namespace Database void Release::find(Session& session, const FindParameters& params, std::function func) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery>(session, "DISTINCT r", params) }; Utils::execQuery(query, params.range, func); @@ -282,7 +282,7 @@ namespace Database RangeResults Release::findIds(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery(session, "DISTINCT r.id", params) }; return Utils::execQuery(query, params.range); @@ -290,7 +290,7 @@ namespace Database std::size_t Release::getCount(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); return createQuery(session, "COUNT(DISTINCT r.id)", params).resultValue(); } diff --git a/src/libs/services/database/impl/ScanSettings.cpp b/src/libs/services/database/impl/ScanSettings.cpp index 45ff9f38..e89757cc 100644 --- a/src/libs/services/database/impl/ScanSettings.cpp +++ b/src/libs/services/database/impl/ScanSettings.cpp @@ -45,7 +45,7 @@ namespace Database { void ScanSettings::init(Session& session) { - session.checkUniqueLocked(); + session.checkWriteTransaction(); pointer settings {get(session)}; if (settings) @@ -58,7 +58,7 @@ ScanSettings::init(Session& session) ScanSettings::pointer ScanSettings::get(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().resultValue(); } @@ -109,7 +109,7 @@ std::set getNames(It begin, It end) void ScanSettings::setClusterTypes(Session& session, const std::set& clusterTypeNames) { - session.checkUniqueLocked(); + session.checkWriteTransaction(); bool needRescan {}; diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 13589cc1..126c0c19 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -39,6 +39,7 @@ #include "services/database/TrackArtistLink.hpp" #include "services/database/TrackList.hpp" #include "services/database/TrackFeatures.hpp" +#include "services/database/TransactionChecker.hpp" #include "services/database/User.hpp" #include "EnumSetTraits.hpp" #include "Migration.hpp" @@ -46,6 +47,29 @@ namespace Database { + WriteTransaction::WriteTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) + : _lock{ mutex }, + _transaction{ session } + { + TransactionChecker::pushWriteTransaction(_transaction.session()); + } + + WriteTransaction::~WriteTransaction() + { + TransactionChecker::popWriteTransaction(_transaction.session()); + } + + ReadTransaction::ReadTransaction(Wt::Dbo::Session& session) + : _transaction{ session } + { + TransactionChecker::pushReadTransaction(_transaction.session()); + } + + ReadTransaction::~ReadTransaction() + { + TransactionChecker::popReadTransaction(_transaction.session()); + } + Session::Session(Db& db) : _db{ db } { @@ -71,36 +95,14 @@ namespace Database _session.mapClass("user"); } - UniqueTransaction::UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) - : _lock{ mutex }, - _transaction{ session } + WriteTransaction Session::createWriteTransaction() { + return WriteTransaction{ _db.getMutex(), _session }; } - SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) - : _lock{ mutex }, - _transaction{ session } + ReadTransaction Session::createReadTransaction() { - } - - void Session::checkUniqueLocked() - { - assert(_db.getMutex().isUniqueLocked()); - } - - void Session::checkSharedLocked() - { - assert(_db.getMutex().isSharedLocked()); - } - - UniqueTransaction Session::createUniqueTransaction() - { - return UniqueTransaction{ _db.getMutex(), _session }; - } - - SharedTransaction Session::createSharedTransaction() - { - return SharedTransaction{ _db.getMutex(), _session }; + return ReadTransaction{ _session }; } void Session::prepareTables() @@ -127,7 +129,7 @@ namespace Database // Indexes { - auto uniqueTransaction{ createUniqueTransaction() }; + auto transaction{ createWriteTransaction() }; _session.execute("CREATE INDEX IF NOT EXISTS artist_name_idx ON artist(name)"); _session.execute("CREATE INDEX IF NOT EXISTS artist_sort_name_nocase_idx ON artist(sort_name COLLATE NOCASE)"); _session.execute("CREATE INDEX IF NOT EXISTS artist_mbid_idx ON artist(mbid)"); @@ -172,7 +174,7 @@ namespace Database // Initial settings tables { - auto uniqueTransaction{ createUniqueTransaction() }; + auto uniqueTransaction{ createWriteTransaction() }; ScanSettings::init(*this); } @@ -182,7 +184,7 @@ namespace Database { LMS_LOG(DB, INFO) << "Analyzing database..."; { - auto uniqueTransaction{ createUniqueTransaction() }; + auto transaction{ createWriteTransaction() }; _session.execute("ANALYZE"); } LMS_LOG(DB, INFO) << "Database Analyze complete"; @@ -192,7 +194,7 @@ namespace Database { LMS_LOG(DB, INFO) << "Optimizing database..."; { - auto uniqueTransaction{ createUniqueTransaction() }; + auto transaction{ createWriteTransaction() }; _session.execute("PRAGMA optimize"); } LMS_LOG(DB, INFO) << "Database optimizing complete"; diff --git a/src/libs/services/database/impl/StarredArtist.cpp b/src/libs/services/database/impl/StarredArtist.cpp index 0508cef8..33395538 100644 --- a/src/libs/services/database/impl/StarredArtist.cpp +++ b/src/libs/services/database/impl/StarredArtist.cpp @@ -43,19 +43,19 @@ namespace Database std::size_t StarredArtist::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM starred_artist"); } StarredArtist::pointer StarredArtist::find(Session& session, StarredArtistId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } StarredArtist::pointer StarredArtist::find(Session& session, ArtistId artistId, UserId userId) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query>("SELECT s_a from starred_artist s_a") .join("user u ON u.id = s_a.user_id") .where("s_a.artist_id = ?").bind(artistId) @@ -66,7 +66,7 @@ namespace Database StarredArtist::pointer StarredArtist::find(Session& session, ArtistId artistId, UserId userId, FeedbackBackend backend) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("artist_id = ?").bind(artistId) .where("user_id = ?").bind(userId) diff --git a/src/libs/services/database/impl/StarredRelease.cpp b/src/libs/services/database/impl/StarredRelease.cpp index 39c83274..ab6f1900 100644 --- a/src/libs/services/database/impl/StarredRelease.cpp +++ b/src/libs/services/database/impl/StarredRelease.cpp @@ -43,19 +43,19 @@ namespace Database std::size_t StarredRelease::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM starred_release"); } StarredRelease::pointer StarredRelease::find(Session& session, StarredReleaseId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } StarredRelease::pointer StarredRelease::find(Session& session, ReleaseId releaseId, UserId userId) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query>("SELECT s_r from starred_release s_r") .join("user u ON u.id = s_r.user_id") .where("s_r.release_id = ?").bind(releaseId) @@ -66,7 +66,7 @@ namespace Database StarredRelease::pointer StarredRelease::find(Session& session, ReleaseId releaseId, UserId userId, FeedbackBackend backend) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("release_id = ?").bind(releaseId) .where("user_id = ?").bind(userId) diff --git a/src/libs/services/database/impl/StarredTrack.cpp b/src/libs/services/database/impl/StarredTrack.cpp index 1a8252e3..f4b139c4 100644 --- a/src/libs/services/database/impl/StarredTrack.cpp +++ b/src/libs/services/database/impl/StarredTrack.cpp @@ -43,19 +43,19 @@ namespace Database std::size_t StarredTrack::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM starred_track"); } StarredTrack::pointer StarredTrack::find(Session& session, StarredTrackId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } StarredTrack::pointer StarredTrack::find(Session& session, TrackId trackId, UserId userId) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query>("SELECT s_t from starred_track s_t") .join("user u ON u.id = s_t.user_id") .where("s_t.track_id = ?").bind(trackId) @@ -66,7 +66,7 @@ namespace Database StarredTrack::pointer StarredTrack::find(Session& session, TrackId trackId, UserId userId, FeedbackBackend backend) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("track_id = ?").bind(trackId) .where("user_id = ?").bind(userId) @@ -85,7 +85,7 @@ namespace Database RangeResults StarredTrack::find(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT DISTINCT s_t.id FROM starred_track s_t") }; diff --git a/src/libs/services/database/impl/Track.cpp b/src/libs/services/database/impl/Track.cpp index 0be23942..58be0924 100644 --- a/src/libs/services/database/impl/Track.cpp +++ b/src/libs/services/database/impl/Track.cpp @@ -42,7 +42,7 @@ namespace Database template Wt::Dbo::Query createQuery(Session& session, std::string_view itemToSelect, const Track::FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); std::string selectStatement{ params.distinct ? "SELECT DISTINCT" : "SELECT" }; auto query{ session.getDboSession().query(selectStatement + " " + std::string{ itemToSelect } + " FROM track t") }; @@ -200,21 +200,21 @@ namespace Database std::size_t Track::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM track"); } Track::pointer Track::findByPath(Session& session, const std::filesystem::path& p) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("file_path = ?").bind(p.string()).resultValue(); } Track::pointer Track::find(Session& session, TrackId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("id = ?").bind(id) @@ -223,14 +223,14 @@ namespace Database bool Track::exists(Session& session, TrackId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT 1 from track").where("id = ?").bind(id).resultValue() == 1; } std::vector Track::findByMBID(Session& session, const UUID& mbid) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto res{ session.getDboSession().find() .where("mbid = ?").bind(std::string {mbid.getAsString()}) @@ -241,7 +241,7 @@ namespace Database std::vector Track::findByRecordingMBID(Session& session, const UUID& mbid) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto res{ session.getDboSession().find() .where("recording_mbid = ?").bind(std::string {mbid.getAsString()}) @@ -253,7 +253,7 @@ namespace Database RangeResults Track::findPaths(Session& session, std::optional range) { using QueryResultType = std::tuple; - session.checkSharedLocked(); + session.checkReadTransaction(); // TODO Dbo traits on filesystem auto query{ session.getDboSession().query("SELECT id, file_path FROM track") }; @@ -276,7 +276,7 @@ namespace Database RangeResults Track::findIdsTrackMBIDDuplicates(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT track.id FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)") .orderBy("track.release_id,track.disc_number,track.track_number,track.mbid") }; @@ -286,7 +286,7 @@ namespace Database RangeResults Track::findIdsWithRecordingMBIDAndMissingFeatures(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT t.id FROM track t") .where("LENGTH(t.recording_mbid) > 0") @@ -314,7 +314,7 @@ namespace Database RangeResults Track::findIds(Session& session, const FindParameters& parameters) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery(session, parameters) }; return Utils::execQuery(query, parameters.range); @@ -322,7 +322,7 @@ namespace Database RangeResults Track::find(Session& session, const FindParameters& parameters) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery>(session, parameters) }; return Utils::execQuery(query, parameters.range); @@ -330,7 +330,7 @@ namespace Database void Track::find(Session& session, const FindParameters& params, std::function func) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery>(session, params)}; Utils::execQuery(query, params.range, func); @@ -339,7 +339,7 @@ namespace Database RangeResults Track::findSimilarTrackIds(Session& session, const std::vector& tracks, std::optional range) { assert(!tracks.empty()); - session.checkSharedLocked(); + session.checkReadTransaction(); std::ostringstream oss; for (std::size_t i{}; i < tracks.size(); ++i) @@ -524,7 +524,7 @@ namespace Database { std::ostream& operator<<(std::ostream& os, const TrackInfo& trackInfo) { - auto transaction{ trackInfo.session.createSharedTransaction() }; + auto transaction{ trackInfo.session.createReadTransaction() }; const Track::pointer track{ Track::find(trackInfo.session, trackInfo.trackId) }; if (track) diff --git a/src/libs/services/database/impl/TrackArtistLink.cpp b/src/libs/services/database/impl/TrackArtistLink.cpp index 97fda619..13e926a5 100644 --- a/src/libs/services/database/impl/TrackArtistLink.cpp +++ b/src/libs/services/database/impl/TrackArtistLink.cpp @@ -32,7 +32,7 @@ namespace Database { Wt::Dbo::Query createQuery(Session& session, const TrackArtistLink::FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT DISTINCT t_a_l.id FROM track_artist_link t_a_l") }; @@ -65,7 +65,7 @@ namespace Database TrackArtistLink::pointer TrackArtistLink::create(Session& session, ObjectPtr track, ObjectPtr artist, TrackArtistLinkType type, std::string_view subType) { - session.checkUniqueLocked(); + session.checkWriteTransaction(); TrackArtistLink::pointer res{ session.getDboSession().add(std::make_unique(track, artist, type, subType)) }; session.getDboSession().flush(); @@ -75,13 +75,13 @@ namespace Database TrackArtistLink::pointer TrackArtistLink::find(Session& session, TrackArtistLinkId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } RangeResults TrackArtistLink::find(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ createQuery(session, params) }; return Utils::execQuery(query, params.range); @@ -89,7 +89,7 @@ namespace Database EnumSet TrackArtistLink::findUsedTypes(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto res{ session.getDboSession().query("SELECT DISTINCT type from track_artist_link").resultList() }; @@ -98,7 +98,7 @@ namespace Database EnumSet TrackArtistLink::findUsedTypes(Session& session, ArtistId artistId) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto res{ session.getDboSession() .query("SELECT DISTINCT type from track_artist_link") diff --git a/src/libs/services/database/impl/TrackBookmark.cpp b/src/libs/services/database/impl/TrackBookmark.cpp index 48dae983..1ecc7dbf 100644 --- a/src/libs/services/database/impl/TrackBookmark.cpp +++ b/src/libs/services/database/impl/TrackBookmark.cpp @@ -40,14 +40,14 @@ namespace Database { std::size_t TrackBookmark::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM track_bookmark"); } RangeResults TrackBookmark::find(Session& session, UserId userId, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT id from track_bookmark") .where("user_id = ?").bind(userId) }; @@ -57,7 +57,7 @@ namespace Database { TrackBookmark::pointer TrackBookmark::find(Session& session, UserId userId, TrackId trackId) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("user_id = ?").bind(userId) @@ -67,7 +67,7 @@ namespace Database { TrackBookmark::pointer TrackBookmark::find(Session& session, TrackBookmarkId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("id = ?").bind(id) diff --git a/src/libs/services/database/impl/TrackFeatures.cpp b/src/libs/services/database/impl/TrackFeatures.cpp index c7a0293e..d7086c51 100644 --- a/src/libs/services/database/impl/TrackFeatures.cpp +++ b/src/libs/services/database/impl/TrackFeatures.cpp @@ -43,14 +43,14 @@ namespace Database { std::size_t TrackFeatures::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM track_features"); } TrackFeatures::pointer TrackFeatures::find(Session& session, TrackFeaturesId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("id = ?").bind(id) @@ -59,7 +59,7 @@ namespace Database { TrackFeatures::pointer TrackFeatures::find(Session& session, TrackId trackId) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find() .where("track_id = ?").bind(trackId) @@ -68,7 +68,7 @@ namespace Database { RangeResults TrackFeatures::find(Session& session, std::optional range) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT id from track_features") }; diff --git a/src/libs/services/database/impl/TrackList.cpp b/src/libs/services/database/impl/TrackList.cpp index 1e98fd6e..84286788 100644 --- a/src/libs/services/database/impl/TrackList.cpp +++ b/src/libs/services/database/impl/TrackList.cpp @@ -53,7 +53,7 @@ namespace Database std::size_t TrackList::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM tracklist"); } @@ -61,7 +61,7 @@ namespace Database TrackList::pointer TrackList::find(Session& session, std::string_view name, TrackListType type, UserId userId) { - session.checkSharedLocked(); + session.checkReadTransaction(); assert(userId.isValid()); return session.getDboSession().find() @@ -72,7 +72,7 @@ namespace Database RangeResults TrackList::find(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT DISTINCT t_l.id FROM tracklist t_l") }; @@ -122,7 +122,7 @@ namespace Database TrackList::pointer TrackList::find(Session& session, TrackListId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } @@ -323,7 +323,7 @@ namespace Database TrackListEntry::pointer TrackListEntry::getById(Session& session, TrackListEntryId id) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } diff --git a/src/libs/services/database/impl/TransactionChecker.cpp b/src/libs/services/database/impl/TransactionChecker.cpp new file mode 100644 index 00000000..6299036f --- /dev/null +++ b/src/libs/services/database/impl/TransactionChecker.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2023 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 . + */ + +#include "services/database/TransactionChecker.hpp" + +#include + +#include "services/database/Session.hpp" + +#if !defined(NDEBUG) +#define LMS_CHECK_TRANSACTION_ACCESSES 1 +#else +#define LMS_CHECK_TRANSACTION_ACCESSES 0 +#endif + +namespace Database +{ +#if LMS_CHECK_TRANSACTION_ACCESSES + namespace + { + struct StackEntry + { + TransactionChecker::TransactionType type; + Wt::Dbo::Session* session{}; + }; + + static thread_local std::vector transactionStack; + } +#endif + + void TransactionChecker::pushWriteTransaction(Wt::Dbo::Session& session) + { + pushTransaction(TransactionType::Write, session); + } + + void TransactionChecker::pushReadTransaction(Wt::Dbo::Session& session) + { + pushTransaction(TransactionType::Read, session); + } + + void TransactionChecker::popWriteTransaction(Wt::Dbo::Session& session) + { + popTransaction(TransactionType::Write, session); + } + + void TransactionChecker::popReadTransaction(Wt::Dbo::Session& session) + { + popTransaction(TransactionType::Read, session); + } + + void TransactionChecker::pushTransaction(TransactionType type, Wt::Dbo::Session& session) + { +#if LMS_CHECK_TRANSACTION_ACCESSES + assert(transactionStack.empty() || transactionStack.back().session == &session); + transactionStack.push_back(StackEntry{ type, &session }); +#endif // LMS_CHECK_TRANSACTION_ACCESSES + } + + void TransactionChecker::popTransaction(TransactionType type, Wt::Dbo::Session& session) + { +#if LMS_CHECK_TRANSACTION_ACCESSES + + assert(!transactionStack.empty()); + assert(transactionStack.back().type == type); + assert(transactionStack.back().session == &session); + transactionStack.pop_back(); +#endif // LMS_CHECK_TRANSACTION_ACCESSES + + } + + void TransactionChecker::checkWriteTransaction(Wt::Dbo::Session& session) + { + assert(!transactionStack.empty()); + assert(transactionStack.back().type == TransactionType::Write); + assert(transactionStack.back().session == &session); + } + + void TransactionChecker::checkWriteTransaction(Session& session) + { + checkWriteTransaction(session.getDboSession()); + } + + void TransactionChecker::checkReadTransaction(Wt::Dbo::Session& session) + { + assert(!transactionStack.empty()); + assert(transactionStack.back().session == &session); + } + + void TransactionChecker::checkReadTransaction(Session& session) + { + checkReadTransaction(session.getDboSession()); + } +} \ No newline at end of file diff --git a/src/libs/services/database/impl/User.cpp b/src/libs/services/database/impl/User.cpp index 5831b86b..c1854899 100644 --- a/src/libs/services/database/impl/User.cpp +++ b/src/libs/services/database/impl/User.cpp @@ -42,14 +42,14 @@ namespace Database { std::size_t User::getCount(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().query("SELECT COUNT(*) FROM user"); } RangeResults User::find(Session& session, const FindParameters& params) { - session.checkSharedLocked(); + session.checkReadTransaction(); auto query{ session.getDboSession().query("SELECT id FROM user") }; @@ -63,7 +63,7 @@ namespace Database { User::pointer User::findDemoUser(Session& session) { - session.checkSharedLocked(); + session.checkReadTransaction(); return session.getDboSession().find().where("type = ?").bind(UserType::DEMO).resultValue(); } diff --git a/src/libs/services/database/include/services/database/Object.hpp b/src/libs/services/database/include/services/database/Object.hpp index 1287349e..a62b5fb1 100644 --- a/src/libs/services/database/include/services/database/Object.hpp +++ b/src/libs/services/database/include/services/database/Object.hpp @@ -22,6 +22,7 @@ #include #include #include "services/database/IdType.hpp" +#include "services/database/TransactionChecker.hpp" namespace Database { @@ -38,9 +39,11 @@ namespace Database bool operator==(const ObjectPtr& other) const { return _obj == other._obj; } bool operator!=(const ObjectPtr& other) const { return other._obj != _obj; } - auto modify() { return _obj.modify(); } + auto modify() { TransactionChecker::checkWriteTransaction(*_obj.session()); return _obj.modify(); } void remove() { + TransactionChecker::checkWriteTransaction(*_obj.session()); + if (_obj->hasOnPreRemove()) _obj.modify()->onPreRemove(); _obj.remove(); diff --git a/src/libs/services/database/include/services/database/Session.hpp b/src/libs/services/database/include/services/database/Session.hpp index 504b516a..75e2b1de 100644 --- a/src/libs/services/database/include/services/database/Session.hpp +++ b/src/libs/services/database/include/services/database/Session.hpp @@ -24,27 +24,37 @@ #include "utils/RecursiveSharedMutex.hpp" #include "services/database/Object.hpp" - +#include "services/database/TransactionChecker.hpp" namespace Database { - class UniqueTransaction + class WriteTransaction { + public: + ~WriteTransaction(); private: friend class Session; - UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); + WriteTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); + + WriteTransaction(const WriteTransaction&) = delete; + WriteTransaction& operator=(const WriteTransaction&) = delete; std::unique_lock _lock; Wt::Dbo::Transaction _transaction; }; - class SharedTransaction + class ReadTransaction { + public: + ~ReadTransaction(); private: friend class Session; - SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); + ReadTransaction(Wt::Dbo::Session& session); + + + ReadTransaction(const ReadTransaction&) = delete; + ReadTransaction& operator=(const ReadTransaction&) = delete; - std::shared_lock _lock; Wt::Dbo::Transaction _transaction; }; @@ -54,11 +64,11 @@ namespace Database public: Session(Db& database); - [[nodiscard]] UniqueTransaction createUniqueTransaction(); - [[nodiscard]] SharedTransaction createSharedTransaction(); + [[nodiscard]] WriteTransaction createWriteTransaction(); + [[nodiscard]] ReadTransaction createReadTransaction(); - void checkUniqueLocked(); - void checkSharedLocked(); + void checkWriteTransaction() { TransactionChecker::checkWriteTransaction(_session); } + void checkReadTransaction() { TransactionChecker::checkReadTransaction(_session); } void analyze(); void optimize(); @@ -71,7 +81,7 @@ namespace Database template typename Object::pointer create(Args&&... args) { - checkUniqueLocked(); + TransactionChecker::checkWriteTransaction(_session); typename Object::pointer res{ Object::create(*this, std::forward(args)...) }; getDboSession().flush(); @@ -90,5 +100,3 @@ namespace Database Wt::Dbo::Session _session; }; } // namespace Database - - diff --git a/src/libs/services/database/include/services/database/TransactionChecker.hpp b/src/libs/services/database/include/services/database/TransactionChecker.hpp new file mode 100644 index 00000000..bde4f155 --- /dev/null +++ b/src/libs/services/database/include/services/database/TransactionChecker.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 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 + +namespace Database +{ + class Session; + + class TransactionChecker + { + public: + enum class TransactionType + { + Read, + Write, + }; + + static void pushWriteTransaction(Wt::Dbo::Session& session); + static void pushReadTransaction(Wt::Dbo::Session& session); + + static void popWriteTransaction(Wt::Dbo::Session& session); + static void popReadTransaction(Wt::Dbo::Session& session); + + static void checkWriteTransaction(Wt::Dbo::Session& session); + static void checkWriteTransaction(Session& session); + static void checkReadTransaction(Wt::Dbo::Session& session); + static void checkReadTransaction(Session& session); + + private: + static void pushTransaction(TransactionType type, Wt::Dbo::Session& session); + static void popTransaction(TransactionType type, Wt::Dbo::Session& session); + }; +} \ No newline at end of file diff --git a/src/libs/services/database/test/Artist.cpp b/src/libs/services/database/test/Artist.cpp index b3127c04..c1ebded7 100644 --- a/src/libs/services/database/test/Artist.cpp +++ b/src/libs/services/database/test/Artist.cpp @@ -24,7 +24,7 @@ using namespace Database; TEST_F(DatabaseFixture, Artist) { { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_FALSE(Artist::exists(session, 35)); EXPECT_FALSE(Artist::exists(session, 0)); EXPECT_FALSE(Artist::exists(session, 1)); @@ -34,7 +34,7 @@ TEST_F(DatabaseFixture, Artist) ScopedArtist artist{ session, "MyArtist" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(artist.get()); EXPECT_FALSE(!artist.get()); @@ -45,7 +45,7 @@ TEST_F(DatabaseFixture, Artist) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}) }; ASSERT_EQ(artists.results.size(), 1); @@ -58,7 +58,7 @@ TEST_F(DatabaseFixture, Artist) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::find(session, Artist::FindParameters {}) }; ASSERT_EQ(artists.results.size(), 1); @@ -66,7 +66,7 @@ TEST_F(DatabaseFixture, Artist) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; bool visited{}; Artist::find(session, Artist::FindParameters{}, [&](const Artist::pointer& a) @@ -84,19 +84,19 @@ TEST_F(DatabaseFixture, Artist_singleTrack) ScopedArtist artist{ session, "MyArtist" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setName("MyTrackName"); TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ track->getArtists({TrackArtistLinkType::Artist}) }; ASSERT_EQ(artists.size(), 1); @@ -113,7 +113,7 @@ TEST_F(DatabaseFixture, Artist_singleTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ track->getArtistIds({TrackArtistLinkType::Artist}) }; ASSERT_EQ(artists.size(), 1); @@ -125,23 +125,23 @@ TEST_F(DatabaseFixture, Artist_singleTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setArtistName("MyArtist")) }; ASSERT_EQ(tracks.results.size(), 1); EXPECT_EQ(tracks.results.front(), track.getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setArtistName("MyArtistFoo")) }; EXPECT_EQ(tracks.results.size(), 0); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackNameFoo").setArtistName("MyArtist")) }; EXPECT_EQ(tracks.results.size(), 0); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters{}.setTrack(track->getId())) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); @@ -153,7 +153,7 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles) ScopedTrack track{ session, "MyTrack" }; ScopedArtist artist{ session, "MyArtist" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::ReleaseArtist); @@ -161,12 +161,12 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}).results.size(), 1); EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Artist)).results.size(), 1); EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::ReleaseArtist)).results.size(), 1); @@ -175,7 +175,7 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ track->getArtists({TrackArtistLinkType::Artist}) }; ASSERT_EQ(artists.size(), 1); @@ -202,7 +202,7 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EnumSet types{ TrackArtistLink::findUsedTypes(session, artist.getId()) }; EXPECT_TRUE(types.contains(TrackArtistLinkType::ReleaseArtist)); EXPECT_TRUE(types.contains(TrackArtistLinkType::Artist)); @@ -219,19 +219,19 @@ TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists) ASSERT_NE(artist1.getId(), artist2.getId()); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist1.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, track.get(), artist2.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ track->getArtists({TrackArtistLinkType::Artist}) }; ASSERT_EQ(artists.size(), 2); @@ -246,7 +246,7 @@ TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist1->getId())) }; ASSERT_EQ(tracks.results.size(), 1); @@ -276,13 +276,13 @@ TEST_F(DatabaseFixture, Artist_findByName) ScopedTrack track{ session, "MyTrack" }; // filters does not work on orphans { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; artist.get().modify()->setSortName("ZZZ"); TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Artist::findIds(session, Artist::FindParameters{}.setKeywords({ "N" })).results.empty()); @@ -310,7 +310,7 @@ TEST_F(DatabaseFixture, Artist_findByNameEscaped) ScopedArtist artist6{ session, R"(%AMyArtist)" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { const auto artists{ Artist::find(session, R"(MyArtist%)") }; ASSERT_TRUE(artists.size() == 1); @@ -332,7 +332,7 @@ TEST_F(DatabaseFixture, Artist_findByNameEscaped) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"MyArtist"})) }; EXPECT_EQ(artists.results.size(), 6); @@ -366,14 +366,14 @@ TEST_F(DatabaseFixture, Artist_sortMethod) ScopedArtist artistB{ session, "artistB" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; artistA.get().modify()->setSortName("sortNameB"); artistB.get().modify()->setSortName("sortNameA"); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto allArtistsByName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::ByName)) }; auto allArtistsBySortName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) }; @@ -396,14 +396,14 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters {}.setNonRelease(true).setArtist(artist->getId())) }; EXPECT_EQ(tracks.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track1.get(), artist.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, track2.get(), artist.get(), TrackArtistLinkType::Artist); @@ -412,7 +412,7 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist.getId()).setNonRelease(true)) }; ASSERT_EQ(tracks.results.size(), 1); @@ -427,29 +427,29 @@ TEST_F(DatabaseFixture, Artist_findByRelease) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) }; EXPECT_EQ(artists.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) }; EXPECT_EQ(artists.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); diff --git a/src/libs/services/database/test/Cluster.cpp b/src/libs/services/database/test/Cluster.cpp index aeea9be6..90c31359 100644 --- a/src/libs/services/database/test/Cluster.cpp +++ b/src/libs/services/database/test/Cluster.cpp @@ -27,7 +27,7 @@ using namespace Database; TEST_F(DatabaseFixture, Cluster) { { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; EXPECT_EQ(Cluster::getCount(session), 0); EXPECT_EQ(ClusterType::getCount(session), 0); } @@ -35,7 +35,7 @@ TEST_F(DatabaseFixture, Cluster) ScopedClusterType clusterType{ session, "MyType" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; EXPECT_EQ(ClusterType::getCount(session), 1); } @@ -43,7 +43,7 @@ TEST_F(DatabaseFixture, Cluster) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; EXPECT_EQ(Cluster::getCount(session), 1); EXPECT_EQ(cluster->getType()->getId(), clusterType.getId()); @@ -74,7 +74,7 @@ TEST_F(DatabaseFixture, Cluster) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto clusterTypes{ ClusterType::findOrphans(session) }; ASSERT_EQ(clusterTypes.results.size(), 1); @@ -90,7 +90,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) ScopedClusterType clusterType{ session, "MyClusterType" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); auto clusterTypes{ ClusterType::findOrphans(session) }; ASSERT_EQ(clusterTypes.results.size(), 1); @@ -101,7 +101,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto clusters{ Cluster::findOrphans(session) }; EXPECT_EQ(clusters.results.size(), 2); EXPECT_TRUE(track->getClusters().empty()); @@ -111,13 +111,13 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto clusters{ Cluster::findIds(session, Cluster::FindParameters {}.setTrack(track.getId())) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster1.getId()); @@ -126,7 +126,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto clusters{ Cluster::findOrphans(session) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster2.getId()); @@ -135,7 +135,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters {}.setClusters({cluster1.getId()})) }; ASSERT_EQ(tracks.results.size(), 1); @@ -146,7 +146,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto clusters{ track->getClusters() }; ASSERT_EQ(clusters.size(), 1); @@ -169,20 +169,20 @@ TEST_F(DatabaseFixture, Cluster_singleTrackWithSeveralClusters) const std::vector clusterIds{ cluster1.getId(), cluster2.getId() }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters{}.setClusters(clusterIds)) }; EXPECT_TRUE(tracks.results.empty()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters{}.setClusters(clusterIds)) }; EXPECT_TRUE(tracks.results.empty()); @@ -191,13 +191,13 @@ TEST_F(DatabaseFixture, Cluster_singleTrackWithSeveralClusters) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster2.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters{}.setClusters(clusterIds)) }; ASSERT_FALSE(tracks.results.empty()); @@ -218,13 +218,13 @@ TEST_F(DatabaseFixture, Cluster_multiTracks) tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(tracks.back().get()); } } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); EXPECT_EQ(Cluster::computeTrackCount(session, cluster.getId()), tracks.size()); @@ -243,7 +243,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); } @@ -252,7 +252,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) ScopedCluster unusedCluster{ session, clusterType.lockAndGet(), "MyClusterUnused" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; ASSERT_EQ(Cluster::findOrphans(session).results.size(), 2); EXPECT_TRUE(Release::find(session, Release::FindParameters{}.setClusters({ unusedCluster.getId() })).results.empty()); EXPECT_EQ(Release::find(session, Release::FindParameters{}).results.size(), 1); @@ -261,14 +261,14 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { auto clusters{ Cluster::findOrphans(session) }; @@ -280,7 +280,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto clusters{ Cluster::findIds(session, Cluster::FindParameters{}.setRelease(release.getId())) }; ASSERT_EQ(clusters.results.size(), 1); @@ -288,7 +288,7 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(releases.results.size(), 1); @@ -296,14 +296,14 @@ TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters({unusedCluster.getId()})) }; EXPECT_EQ(releases.results.size(), 0); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 1); EXPECT_EQ(Cluster::computeTrackCount(session, cluster.getId()), 1); @@ -321,14 +321,14 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) ScopedCluster cluster2{ session, clusterType.lockAndGet(), "Cluster2" }; ScopedCluster cluster3{ session, clusterType.lockAndGet(), "Cluster3" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; cluster1.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(ClusterType::findOrphans(session).results.empty()); EXPECT_EQ(Cluster::findOrphans(session).results.size(), 2); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); @@ -336,13 +336,13 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(track->getClusters().size(), 1); EXPECT_EQ(track->getClusterIds().size(), 1); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; ASSERT_EQ(artists.results.size(), 1); @@ -355,7 +355,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; ASSERT_EQ(artists.results.size(), 1); @@ -381,7 +381,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::ReleaseArtist); @@ -389,14 +389,14 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(artists.results.size(), 1); @@ -421,7 +421,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) { tracks.emplace_back(session, "MyTrackFile" + std::to_string(i)); - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist); for (auto& cluster : clusters) @@ -429,13 +429,13 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; std::vector clusterIds; std::transform(std::cbegin(clusters), std::cend(clusters), std::back_inserter(clusterIds), [](const ScopedCluster& cluster) { return cluster.getId(); }); @@ -458,13 +458,13 @@ TEST_F(DatabaseFixture, MultipleTracksSingleClusterSimilarity) tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(tracks.back().get()); } } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto similarTracks{ Track::findSimilarTrackIds(session, {tracks.front().getId()}) }; EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); @@ -487,7 +487,7 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersSimilarity) tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(tracks.back().get()); } } @@ -497,14 +497,14 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersSimilarity) tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(tracks.back().get()); cluster2.get().modify()->addTrack(tracks.back().get()); } } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { auto similarTracks{ Track::findSimilarTrackIds(session, {tracks.back().getId()}, Range {0, 4}) }; @@ -531,7 +531,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); track.get().modify()->setRelease(release.get()); @@ -539,7 +539,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session).results.empty()); EXPECT_TRUE(ClusterType::findOrphans(session).results.empty()); @@ -548,7 +548,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(artists.results.size(), 1); @@ -574,7 +574,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters) ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; track.get().modify()->setRelease(release.get()); @@ -583,7 +583,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) }; ASSERT_EQ(releases.results.size(), 1); @@ -607,7 +607,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackSingleCluster) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (i < 5) session.create(tracks.back().get(), trackList.get()); @@ -617,7 +617,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackSingleCluster) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto similarTracks{ trackList->getSimilarTracks() }; EXPECT_EQ(similarTracks.size(), 5); @@ -640,7 +640,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (i < 5) session.create(tracks.back().get(), trackList.get()); @@ -657,7 +657,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { const auto similarTracks{ trackList->getSimilarTracks(0, 5) }; @@ -689,7 +689,7 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(artist1->findSimilarArtistIds().results.empty()); EXPECT_TRUE(artist2->findSimilarArtistIds().results.empty()); EXPECT_TRUE(artist3->findSimilarArtistIds().results.empty()); @@ -700,7 +700,7 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (i < 5) TrackArtistLink::create(session, tracks.back().get(), artist1.get(), TrackArtistLinkType::Artist); @@ -715,13 +715,13 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, tracks.back().get(), artist3.get(), TrackArtistLinkType::Artist); cluster2.get().modify()->addTrack(tracks.back().get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { auto artists{ artist1->findSimilarArtistIds() }; @@ -770,7 +770,7 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleReleasesMultiClusters) ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(release1->getSimilarReleases().empty()); EXPECT_TRUE(release2->getSimilarReleases().empty()); EXPECT_TRUE(release3->getSimilarReleases().empty()); @@ -781,7 +781,7 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleReleasesMultiClusters) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (i < 5) tracks.back().get().modify()->setRelease(release1.get()); @@ -796,13 +796,13 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleReleasesMultiClusters) tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; tracks.back().get().modify()->setRelease(release3.get()); cluster2.get().modify()->addTrack(tracks.back().get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { auto releases{ release1->getSimilarReleases() }; diff --git a/src/libs/services/database/test/Common.cpp b/src/libs/services/database/test/Common.cpp index d63ce4e4..a914ea2b 100644 --- a/src/libs/services/database/test/Common.cpp +++ b/src/libs/services/database/test/Common.cpp @@ -62,7 +62,7 @@ void DatabaseFixture::SetUpTestCase() // remove default created entries { - auto transaction{ s.createUniqueTransaction() }; + auto transaction{ s.createWriteTransaction() }; for (const Database::ClusterTypeId clusterTypeId : Database::ClusterType::find(s).results) { @@ -82,7 +82,7 @@ void DatabaseFixture::testDatabaseEmpty() { using namespace Database; - auto uniqueTransaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; EXPECT_EQ(Artist::getCount(session), 0); EXPECT_EQ(Cluster::getCount(session), 0); diff --git a/src/libs/services/database/test/Common.hpp b/src/libs/services/database/test/Common.hpp index 5a692695..c26e5630 100644 --- a/src/libs/services/database/test/Common.hpp +++ b/src/libs/services/database/test/Common.hpp @@ -49,7 +49,7 @@ class ScopedEntity ScopedEntity(Database::Session& session, Args&& ...args) : _session {session} { - auto transaction {_session.createUniqueTransaction()}; + auto transaction {_session.createWriteTransaction()}; auto entity {_session.create(std::forward(args)...)}; EXPECT_TRUE(entity); @@ -58,7 +58,7 @@ class ScopedEntity ~ScopedEntity() { - auto transaction {_session.createUniqueTransaction()}; + auto transaction {_session.createWriteTransaction()}; auto entity {T::find(_session, _id)}; // could not be here due to "on delete cascade" constraints... @@ -73,13 +73,13 @@ class ScopedEntity typename T::pointer lockAndGet() { - auto transaction {_session.createSharedTransaction()}; + auto transaction {_session.createReadTransaction()}; return get(); } typename T::pointer get() { - _session.checkSharedLocked(); + _session.checkReadTransaction(); auto entity {T::find(_session, _id)}; EXPECT_TRUE(entity); diff --git a/src/libs/services/database/test/DatabaseTest.cpp b/src/libs/services/database/test/DatabaseTest.cpp index bb1926dd..8894c112 100644 --- a/src/libs/services/database/test/DatabaseTest.cpp +++ b/src/libs/services/database/test/DatabaseTest.cpp @@ -34,20 +34,20 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist); tracks.back().get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) }; ASSERT_EQ(releases.results.size(), 1); @@ -65,14 +65,14 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist) ScopedArtist artist{ session, "MyArtist" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) }; ASSERT_EQ(releases.results.size(), 1); @@ -87,7 +87,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist) TEST_F(DatabaseFixture, SingleUser) { { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(User::find(session, User::FindParameters{}).results.empty()); EXPECT_EQ(User::getCount(session), 0); } @@ -95,7 +95,7 @@ TEST_F(DatabaseFixture, SingleUser) ScopedUser user{ session, "MyUser" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(User::find(session, User::FindParameters{}).results.size(), 1); EXPECT_EQ(User::getCount(session), 1); diff --git a/src/libs/services/database/test/Listen.cpp b/src/libs/services/database/test/Listen.cpp index 04330574..901a3f13 100644 --- a/src/libs/services/database/test/Listen.cpp +++ b/src/libs/services/database/test/Listen.cpp @@ -30,7 +30,7 @@ TEST_F(DatabaseFixture, Listen_getAll) ScopedUser user{ session, "MyUser" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Listen::getCount(session), 0); } @@ -38,20 +38,20 @@ TEST_F(DatabaseFixture, Listen_getAll) ScopedListen listen{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 1}} }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Listen::getCount(session), 1); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; ScopedListen listen2{ session, user.get(), track.get(), ScrobblingBackend::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{13, 0, 1}} }; EXPECT_EQ(Listen::getCount(session), 2); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Listen::getCount(session), 1); } @@ -64,14 +64,14 @@ TEST_F(DatabaseFixture, Listen_get) ScopedListen listen{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 1}} }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto listens{ Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobblingBackend(ScrobblingBackend::ListenBrainz)) }; EXPECT_EQ(listens.results.size(), 0); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { auto listens{ Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobblingBackend(ScrobblingBackend::Internal)) }; @@ -100,7 +100,7 @@ TEST_F(DatabaseFixture, Listen_get_multi) ScopedListen listen2{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 2}} }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto listens{ Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobblingBackend(ScrobblingBackend::Internal)) }; ASSERT_EQ(listens.results.size(), 3); @@ -122,7 +122,7 @@ TEST_F(DatabaseFixture, Listen_get_byDateTime) ScopedListen listen2{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime2 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { Listen::pointer listen{ Listen::find(session, user.getId(), track.getId(), ScrobblingBackend::Internal, dateTime1) }; @@ -151,7 +151,7 @@ TEST_F(DatabaseFixture, Listen_getTopArtists) ScopedListen listen1{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime1 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; EXPECT_EQ(artists.results.size(), 0); @@ -163,26 +163,26 @@ TEST_F(DatabaseFixture, Listen_getTopArtists) ScopedListen listen2{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime1.addSecs(1) }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track2.get(), artist1.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results[0], artist1->getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::ListenBrainz, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 0); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {}, TrackArtistLinkType::Producer) }; EXPECT_EQ(artists.results.size(), 0); @@ -192,7 +192,7 @@ TEST_F(DatabaseFixture, Listen_getTopArtists) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {cluster->getId()}, std::nullopt) }; EXPECT_EQ(artists.results.size(), 0); @@ -210,13 +210,13 @@ TEST_F(DatabaseFixture, Listen_getTopArtists_multi) const Wt::WDateTime dateTime{ Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1} }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; EXPECT_EQ(artists.results.size(), 0); @@ -224,7 +224,7 @@ TEST_F(DatabaseFixture, Listen_getTopArtists_multi) ScopedListen listen1{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(1) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 1); @@ -233,7 +233,7 @@ TEST_F(DatabaseFixture, Listen_getTopArtists_multi) ScopedListen listen2{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(2) }; ScopedListen listen3{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(3) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 2); @@ -241,7 +241,7 @@ TEST_F(DatabaseFixture, Listen_getTopArtists_multi) EXPECT_EQ(artists.results[1], artist1->getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt, Range {0, 1}) }; ASSERT_EQ(artists.results.size(), 1); @@ -261,22 +261,22 @@ TEST_F(DatabaseFixture, Listen_getTopArtists_cluster) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getTopArtists(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 1); @@ -291,12 +291,12 @@ TEST_F(DatabaseFixture, Listen_getTopReleases) const Wt::WDateTime dateTime{ Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1} }; ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -306,7 +306,7 @@ TEST_F(DatabaseFixture, Listen_getTopReleases) ScopedListen listen{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -314,7 +314,7 @@ TEST_F(DatabaseFixture, Listen_getTopReleases) EXPECT_EQ(releases.results[0], release.getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::ListenBrainz, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -333,13 +333,13 @@ TEST_F(DatabaseFixture, Listen_getTopReleases_multi) ScopedRelease release2{ session, "MyRelease2" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setRelease(release1.get()); track2.get().modify()->setRelease(release2.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -349,7 +349,7 @@ TEST_F(DatabaseFixture, Listen_getTopReleases_multi) ScopedListen listen2{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime }; ScopedListen listen3{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -360,7 +360,7 @@ TEST_F(DatabaseFixture, Listen_getTopReleases_multi) ScopedListen listen4{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime }; ScopedListen listen5{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -381,23 +381,23 @@ TEST_F(DatabaseFixture, Listen_getTopReleases_cluster) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(releases.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getTopReleases(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(releases.results.size(), 1); @@ -412,7 +412,7 @@ TEST_F(DatabaseFixture, Listen_getTopTracks) const Wt::WDateTime dateTime{ Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1} }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -422,7 +422,7 @@ TEST_F(DatabaseFixture, Listen_getTopTracks) ScopedListen listen{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -430,7 +430,7 @@ TEST_F(DatabaseFixture, Listen_getTopTracks) EXPECT_EQ(tracks.results[0], track.getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::ListenBrainz, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -447,7 +447,7 @@ TEST_F(DatabaseFixture, Listen_getTopTrack_multi) ScopedListen listen1{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -457,7 +457,7 @@ TEST_F(DatabaseFixture, Listen_getTopTrack_multi) ScopedListen listen2{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime }; ScopedListen listen3{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -468,7 +468,7 @@ TEST_F(DatabaseFixture, Listen_getTopTrack_multi) ScopedListen listen4{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime }; ScopedListen listen5{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -488,18 +488,18 @@ TEST_F(DatabaseFixture, Listen_getTopTracks_cluster) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(tracks.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getTopTracks(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(tracks.results.size(), 1); @@ -514,12 +514,12 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists) ScopedArtist artist{ session, "MyArtist" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; EXPECT_EQ(artists.results.size(), 0); @@ -530,20 +530,20 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists) ScopedListen listen1{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results[0], artist->getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::ListenBrainz, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 0); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {}, TrackArtistLinkType::Producer) }; EXPECT_EQ(artists.results.size(), 0); @@ -553,7 +553,7 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {cluster->getId()}, std::nullopt) }; EXPECT_EQ(artists.results.size(), 0); @@ -571,13 +571,13 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists_multi) const Wt::WDateTime dateTime{ Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1} }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; EXPECT_EQ(artists.results.size(), 0); @@ -585,7 +585,7 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists_multi) ScopedListen listen1{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(1) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 1); @@ -593,7 +593,7 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists_multi) } ScopedListen listen2{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(2) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 2); @@ -602,7 +602,7 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists_multi) } ScopedListen listen3{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(-1) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {}, std::nullopt, Range {0, 1}) }; ASSERT_EQ(artists.results.size(), 1); @@ -622,22 +622,22 @@ TEST_F(DatabaseFixture, Listen_getRecentArtists_cluster) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Listen::getRecentArtists(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}, std::nullopt) }; ASSERT_EQ(artists.results.size(), 1); @@ -652,12 +652,12 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -668,7 +668,7 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases) ScopedListen listen1{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -676,7 +676,7 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases) EXPECT_EQ(releases.results[0], release.getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::ListenBrainz, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -692,12 +692,12 @@ TEST_F(DatabaseFixture, Listen_getMostRecentRelease) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, release.getId()) }; EXPECT_FALSE(listen); @@ -707,7 +707,7 @@ TEST_F(DatabaseFixture, Listen_getMostRecentRelease) ScopedListen listen1{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime1 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, release.getId()) }; EXPECT_TRUE(listen); @@ -718,7 +718,7 @@ TEST_F(DatabaseFixture, Listen_getMostRecentRelease) ScopedListen listen2{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime2 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, release.getId()) }; EXPECT_TRUE(listen); @@ -729,7 +729,7 @@ TEST_F(DatabaseFixture, Listen_getMostRecentRelease) ScopedListen listen3{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime3 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, release.getId()) }; EXPECT_TRUE(listen); @@ -746,7 +746,7 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases_multi) ScopedRelease release2{ session, "MyRelease2" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setRelease(release1.get()); track2.get().modify()->setRelease(release2.get()); } @@ -755,7 +755,7 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases_multi) ScopedListen listen1{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -765,7 +765,7 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases_multi) ScopedListen listen2{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(1) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -776,7 +776,7 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases_multi) ScopedListen listen3{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(2) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -787,7 +787,7 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases_multi) ScopedListen listen4{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(-1) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(releases.moreResults, false); @@ -806,11 +806,11 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases_cluster) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(releases.results.size(), 0); @@ -820,18 +820,18 @@ TEST_F(DatabaseFixture, Listen_getRecentReleases_cluster) ScopedListen listen{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(releases.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Listen::getRecentReleases(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(releases.results.size(), 1); @@ -845,7 +845,7 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks) ScopedUser user{ session, "MyUser" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -856,7 +856,7 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks) ScopedListen listen1{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -865,7 +865,7 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::ListenBrainz, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -879,7 +879,7 @@ TEST_F(DatabaseFixture, Listen_getCount_track) ScopedUser user{ session, "MyUser" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const std::size_t count{ Listen::getCount(session, user->getId(), ScrobblingBackend::Internal, track.getId()) }; EXPECT_EQ(count, 0); @@ -889,7 +889,7 @@ TEST_F(DatabaseFixture, Listen_getCount_track) ScopedListen listen1{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime1 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const std::size_t count{ Listen::getCount(session, user->getId(), ScrobblingBackend::Internal, track.getId()) }; EXPECT_EQ(count, 1); @@ -905,7 +905,7 @@ TEST_F(DatabaseFixture, Listen_getCount_release) auto getReleaseListenCount{ [&] { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; return Listen::getCount(session, user->getId(), ScrobblingBackend::Internal, release.getId()); } }; @@ -917,14 +917,14 @@ TEST_F(DatabaseFixture, Listen_getCount_release) EXPECT_EQ(getReleaseListenCount(), 0); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setRelease(release.get()); } EXPECT_EQ(getReleaseListenCount(), 1); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track2.get().modify()->setRelease(release.get()); } @@ -944,7 +944,7 @@ TEST_F(DatabaseFixture, Listen_getMostRecentTrack) ScopedUser user{ session, "MyUser" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, track.getId()) }; EXPECT_FALSE(listen); @@ -954,7 +954,7 @@ TEST_F(DatabaseFixture, Listen_getMostRecentTrack) ScopedListen listen1{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime1 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, track.getId()) }; EXPECT_TRUE(listen); @@ -965,7 +965,7 @@ TEST_F(DatabaseFixture, Listen_getMostRecentTrack) ScopedListen listen2{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime2 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, track.getId()) }; EXPECT_TRUE(listen); @@ -976,7 +976,7 @@ TEST_F(DatabaseFixture, Listen_getMostRecentTrack) ScopedListen listen3{ session, user.lockAndGet(), track.lockAndGet(), ScrobblingBackend::Internal, dateTime3 }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto listen{ Listen::getMostRecentListen(session, user->getId(), ScrobblingBackend::Internal, track.getId()) }; EXPECT_TRUE(listen); @@ -994,7 +994,7 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks_multi) ScopedListen listen1{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -1004,7 +1004,7 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks_multi) ScopedListen listen2{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(1) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -1015,7 +1015,7 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks_multi) ScopedListen listen3{ session, user.lockAndGet(), track2.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(2) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -1026,7 +1026,7 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks_multi) ScopedListen listen4{ session, user.lockAndGet(), track1.lockAndGet(), ScrobblingBackend::Internal, dateTime.addSecs(-1) }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {}) }; EXPECT_EQ(tracks.moreResults, false); @@ -1046,18 +1046,18 @@ TEST_F(DatabaseFixture, Listen_getRecentTracks_cluster) ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(tracks.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Listen::getRecentTracks(session, user->getId(), ScrobblingBackend::Internal, {cluster.getId()}) }; EXPECT_EQ(tracks.results.size(), 1); diff --git a/src/libs/services/database/test/Release.cpp b/src/libs/services/database/test/Release.cpp index 1162a89a..f7824883 100644 --- a/src/libs/services/database/test/Release.cpp +++ b/src/libs/services/database/test/Release.cpp @@ -24,7 +24,7 @@ using namespace Database; TEST_F(DatabaseFixture, Release) { { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Release::getCount(session), 0); EXPECT_EQ(Release::getCount(session, Release::FindParameters{}), 0); @@ -35,7 +35,7 @@ TEST_F(DatabaseFixture, Release) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Release::getCount(session), 1); EXPECT_EQ(Release::getCount(session, Release::FindParameters{}), 1); @@ -79,7 +79,7 @@ TEST_F(DatabaseFixture, Release_singleTrack) { ScopedTrack track{ session, "MyTrack" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); track.get().modify()->setName("MyTrackName"); @@ -87,7 +87,7 @@ TEST_F(DatabaseFixture, Release_singleTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); const auto tracks{ Track::findIds(session, Track::FindParameters {}.setRelease(release.getId())) }; @@ -96,32 +96,32 @@ TEST_F(DatabaseFixture, Release_singleTrack) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; ASSERT_TRUE(track->getRelease()); EXPECT_EQ(track->getRelease()->getId(), release.getId()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setReleaseName("MyReleaseName")) }; ASSERT_EQ(tracks.results.size(), 1); EXPECT_EQ(tracks.results.front(), track.getId()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setReleaseName("MyReleaseFoo")) }; EXPECT_EQ(tracks.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackFoo").setReleaseName("MyReleaseName")) }; EXPECT_EQ(tracks.results.size(), 0); } } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters {}.setRelease(release.getId())) }; EXPECT_TRUE(tracks.results.empty()); @@ -150,7 +150,7 @@ TEST_F(DatabaseFixture, MulitpleReleaseSearchByName) ScopedTrack track6{ session, "MyTrack" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setRelease(release1.get()); track2.get().modify()->setRelease(release2.get()); @@ -161,7 +161,7 @@ TEST_F(DatabaseFixture, MulitpleReleaseSearchByName) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { const auto releases{ Release::findIds(session, Release::FindParameters {}.setKeywords({"Release"})) }; @@ -203,33 +203,33 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseTotalDiscTrack) ScopedRelease release1{ session, "MyRelease" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_FALSE(release1->getTotalDisc()); } ScopedTrack track1{ session, "MyTrack" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setRelease(release1.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_FALSE(release1->getTotalDisc()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setTotalTrack(36); release1.get().modify()->setTotalDisc(6); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; ASSERT_TRUE(track1->getTotalTrack()); EXPECT_EQ(*track1->getTotalTrack(), 36); @@ -239,7 +239,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseTotalDiscTrack) ScopedTrack track2{ session, "MyTrack2" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track2.get().modify()->setRelease(release1.get()); track2.get().modify()->setTotalTrack(37); @@ -247,7 +247,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseTotalDiscTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; ASSERT_TRUE(track1->getTotalTrack()); EXPECT_EQ(*track1->getTotalTrack(), 36); @@ -257,21 +257,21 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseTotalDiscTrack) ScopedRelease release2{ session, "MyRelease2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_FALSE(release2->getTotalDisc()); } ScopedTrack track3{ session, "MyTrack3" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track3.get().modify()->setRelease(release2.get()); track3.get().modify()->setTotalTrack(7); release2.get().modify()->setTotalDisc(5); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; ASSERT_TRUE(track1->getTotalTrack()); EXPECT_EQ(*track1->getTotalTrack(), 36); @@ -295,14 +295,14 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseFirstTrack) ScopedTrack track2B{ session, "MyTrack2B" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Track::findIds(session, Track::FindParameters{}.setRelease(release1.getId())).results.empty()); EXPECT_TRUE(Track::findIds(session, Track::FindParameters{}.setRelease(release2.getId())).results.empty()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1A.get().modify()->setRelease(release1.get()); track1B.get().modify()->setRelease(release1.get()); @@ -319,7 +319,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseFirstTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { const auto tracks{ Track::findIds(session, Track::FindParameters {}.setRelease(release1.getId()).setSortMethod(TrackSortMethod::Release)) }; @@ -348,14 +348,14 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseDate) ScopedTrack track2B{ session, "MyTrack2B" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(0, 3000))) }; EXPECT_EQ(releases.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1A.get().modify()->setRelease(release1.get()); track1B.get().modify()->setRelease(release1.get()); @@ -373,7 +373,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseDate) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Release::findIds(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(1950, 2000))) }; ASSERT_EQ(releases.results.size(), 1); @@ -396,25 +396,25 @@ TEST_F(DatabaseFixture, Release_writtenAfter) const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 1}, Wt::WTime {12, 30, 20} }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setLastWriteTime(dateTime); track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}) }; EXPECT_EQ(releases.results.size(), 1); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setWrittenAfter(dateTime.addSecs(-1))) }; EXPECT_EQ(releases.results.size(), 1); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setWrittenAfter(dateTime.addSecs(+1))) }; EXPECT_EQ(releases.results.size(), 0); } @@ -427,12 +427,12 @@ TEST_F(DatabaseFixture, Release_artist) ScopedArtist artist{ session, "MyArtist" }; ScopedArtist artist2{ session, "MyArtist2" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Artist})) }; EXPECT_EQ(releases.results.size(), 0); @@ -442,7 +442,7 @@ TEST_F(DatabaseFixture, Release_artist) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Artist})) }; @@ -488,40 +488,40 @@ TEST_F(DatabaseFixture, Release_getDiscCount) ScopedTrack track2{ session, "MyTrack2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release.get()->getDiscCount(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release.get()->getDiscCount(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setDiscNumber(5); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release.get()->getDiscCount(), 1); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track2.get().modify()->setRelease(release.get()); track2.get().modify()->setDiscNumber(5); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release.get()->getDiscCount(), 1); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track2.get().modify()->setDiscNumber(6); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release.get()->getDiscCount(), 2); } } @@ -531,19 +531,19 @@ TEST_F(DatabaseFixture, Release_releaseType) ScopedRelease release{ session, "MyRelease" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release.get()->getPrimaryType(), std::nullopt); EXPECT_EQ(release.get()->getSecondaryTypes(), EnumSet {}); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; release.get().modify()->setPrimaryType({ ReleaseTypePrimary::Album }); release.get().modify()->setSecondaryTypes({ ReleaseTypeSecondary::Compilation }); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release.get()->getPrimaryType(), ReleaseTypePrimary::Album); EXPECT_TRUE(release.get()->getSecondaryTypes().contains(ReleaseTypeSecondary::Compilation)); } @@ -565,7 +565,7 @@ TEST_F(DatabaseFixture, Release_sortMethod) ASSERT_GT(release2Date, release1OriginalDate); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setRelease(release1.get()); track1.get().modify()->setOriginalDate(release1OriginalDate); @@ -576,7 +576,7 @@ TEST_F(DatabaseFixture, Release_sortMethod) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setSortMethod(ReleaseSortMethod::Name)) }; ASSERT_EQ(releases.results.size(), 2); @@ -585,14 +585,14 @@ TEST_F(DatabaseFixture, Release_sortMethod) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setSortMethod(ReleaseSortMethod::Random)) }; ASSERT_EQ(releases.results.size(), 2); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setSortMethod(ReleaseSortMethod::Date)) }; ASSERT_EQ(releases.results.size(), 2); @@ -601,7 +601,7 @@ TEST_F(DatabaseFixture, Release_sortMethod) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setSortMethod(ReleaseSortMethod::OriginalDate)) }; ASSERT_EQ(releases.results.size(), 2); @@ -609,7 +609,7 @@ TEST_F(DatabaseFixture, Release_sortMethod) EXPECT_EQ(releases.results.back(), release2.getId()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setSortMethod(ReleaseSortMethod::OriginalDateDesc)) }; ASSERT_EQ(releases.results.size(), 2); @@ -627,14 +627,14 @@ TEST_F(DatabaseFixture, Release_meanBitrate) auto checkExpectedBitrate = [&](std::size_t bitrate) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(release1->getMeanBitrate(), bitrate); }; checkExpectedBitrate(0); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setBitrate(128); track1.get().modify()->setRelease(release1.get()); } @@ -642,14 +642,14 @@ TEST_F(DatabaseFixture, Release_meanBitrate) checkExpectedBitrate(128); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track2.get().modify()->setBitrate(256); track2.get().modify()->setRelease(release1.get()); } checkExpectedBitrate(192); { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track3.get().modify()->setBitrate(0); track3.get().modify()->setRelease(release1.get()); } diff --git a/src/libs/services/database/test/StarredArtist.cpp b/src/libs/services/database/test/StarredArtist.cpp index 55e7d9da..7e173d2f 100644 --- a/src/libs/services/database/test/StarredArtist.cpp +++ b/src/libs/services/database/test/StarredArtist.cpp @@ -31,7 +31,7 @@ TEST_F(DatabaseFixture, StarredArtist) ScopedUser user2{ session, "MyUser2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto starredArtist{ StarredArtist::find(session, artist->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_FALSE(starredArtist); @@ -43,7 +43,7 @@ TEST_F(DatabaseFixture, StarredArtist) ScopedStarredArtist starredArtist{ session, artist.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_EQ(gotArtist->getId(), starredArtist->getId()); @@ -51,7 +51,7 @@ TEST_F(DatabaseFixture, StarredArtist) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}) }; EXPECT_EQ(artists.results.size(), 1); @@ -64,24 +64,24 @@ TEST_F(DatabaseFixture, StarredArtist) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) }; EXPECT_EQ(gotArtist, Artist::pointer{}); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; user.get().modify()->setFeedbackBackend(FeedbackBackend::Internal); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) }; EXPECT_EQ(gotArtist->getId(), starredArtist->getId()); } @@ -94,7 +94,7 @@ TEST_F(DatabaseFixture, StarredArtist_PendingDestroy) ScopedStarredArtist starredArtist{ session, artist.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(artists.results.size(), 1); @@ -117,14 +117,14 @@ TEST_F(DatabaseFixture, StarredArtist_dateTime) const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto artists{ Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(artists.results.size(), 2); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; starredArtist1.get().modify()->setDateTime(dateTime); starredArtist2.get().modify()->setDateTime(dateTime.addSecs(-1)); @@ -135,7 +135,7 @@ TEST_F(DatabaseFixture, StarredArtist_dateTime) EXPECT_EQ(artists.results[1], starredArtist2->getArtist()->getId()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; starredArtist1.get().modify()->setDateTime(dateTime); starredArtist2.get().modify()->setDateTime(dateTime.addSecs(1)); diff --git a/src/libs/services/database/test/StarredRelease.cpp b/src/libs/services/database/test/StarredRelease.cpp index 91b3871e..181778f6 100644 --- a/src/libs/services/database/test/StarredRelease.cpp +++ b/src/libs/services/database/test/StarredRelease.cpp @@ -31,7 +31,7 @@ TEST_F(DatabaseFixture, StarredRelease) ScopedUser user2{ session, "MyUser2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto starredRelease{ StarredRelease::find(session, release->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_FALSE(starredRelease); @@ -43,7 +43,7 @@ TEST_F(DatabaseFixture, StarredRelease) ScopedStarredRelease starredRelease{ session, release.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto gotRelease{ StarredRelease::find(session, release->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_EQ(gotRelease->getId(), starredRelease->getId()); @@ -51,7 +51,7 @@ TEST_F(DatabaseFixture, StarredRelease) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Release::find(session, Release::FindParameters {}) }; EXPECT_EQ(releases.results.size(), 1); @@ -64,12 +64,12 @@ TEST_F(DatabaseFixture, StarredRelease) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto gotRelease{ StarredRelease::find(session, release->getId(), user->getId()) }; EXPECT_EQ(gotRelease, StarredRelease::pointer{}); @@ -83,7 +83,7 @@ TEST_F(DatabaseFixture, Starredrelease_PendingDestroy) ScopedStarredRelease starredRelease{ session, release.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto releases{ Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(releases.results.size(), 1); @@ -106,14 +106,14 @@ TEST_F(DatabaseFixture, StarredRelease_dateTime) const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto releases{ Release::findIds(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(releases.results.size(), 2); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; starredRelease1.get().modify()->setDateTime(dateTime); starredRelease2.get().modify()->setDateTime(dateTime.addSecs(-1)); @@ -124,7 +124,7 @@ TEST_F(DatabaseFixture, StarredRelease_dateTime) EXPECT_EQ(releases.results[1], starredRelease2->getRelease()->getId()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; starredRelease1.get().modify()->setDateTime(dateTime); starredRelease2.get().modify()->setDateTime(dateTime.addSecs(1)); diff --git a/src/libs/services/database/test/StarredTrack.cpp b/src/libs/services/database/test/StarredTrack.cpp index cd7f45f2..0bab5a60 100644 --- a/src/libs/services/database/test/StarredTrack.cpp +++ b/src/libs/services/database/test/StarredTrack.cpp @@ -31,7 +31,7 @@ TEST_F(DatabaseFixture, StarredTrack) ScopedUser user2{ session, "MyUser2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction {session.createReadTransaction()}; auto starredTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_FALSE(starredTrack); @@ -43,7 +43,7 @@ TEST_F(DatabaseFixture, StarredTrack) ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction {session.createReadTransaction()}; auto gotTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) }; EXPECT_EQ(gotTrack->getId(), starredTrack->getId()); @@ -51,7 +51,7 @@ TEST_F(DatabaseFixture, StarredTrack) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction {session.createReadTransaction()}; auto tracks{ Track::findIds(session, Track::FindParameters {}) }; EXPECT_EQ(tracks.results.size(), 1); @@ -64,12 +64,12 @@ TEST_F(DatabaseFixture, StarredTrack) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto gotRelease{ StarredTrack::find(session, track->getId(), user->getId()) }; EXPECT_EQ(gotRelease, StarredTrack::pointer{}); @@ -83,7 +83,7 @@ TEST_F(DatabaseFixture, Starredtrack_PendingDestroy) ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction {session.createWriteTransaction()}; auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(tracks.results.size(), 1); @@ -106,14 +106,14 @@ TEST_F(DatabaseFixture, StarredTrack_dateTime) const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction {session.createReadTransaction()}; auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(tracks.results.size(), 2); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction {session.createWriteTransaction()}; starredTrack1.get().modify()->setDateTime(dateTime); starredTrack2.get().modify()->setDateTime(dateTime.addSecs(-1)); @@ -124,7 +124,7 @@ TEST_F(DatabaseFixture, StarredTrack_dateTime) EXPECT_EQ(tracks.results[1], starredTrack2->getTrack()->getId()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction {session.createWriteTransaction()}; starredTrack1.get().modify()->setDateTime(dateTime); starredTrack2.get().modify()->setDateTime(dateTime.addSecs(1)); diff --git a/src/libs/services/database/test/Track.cpp b/src/libs/services/database/test/Track.cpp index c88e9b69..39cd5258 100644 --- a/src/libs/services/database/test/Track.cpp +++ b/src/libs/services/database/test/Track.cpp @@ -26,7 +26,7 @@ using namespace Database; TEST_F(DatabaseFixture, Track) { { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Track::find(session, Track::FindParameters{}).results.size(), 0); EXPECT_EQ(Track::findIds(session, Track::FindParameters{}).results.size(), 0); EXPECT_EQ(Track::getCount(session), 0); @@ -42,7 +42,7 @@ TEST_F(DatabaseFixture, Track) ScopedTrack track{ session, "MyTrackFile" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Track::find(session, Track::FindParameters{}).results.size(), 1); EXPECT_EQ(Track::getCount(session), 1); @@ -69,7 +69,7 @@ TEST_F(DatabaseFixture, MultipleTracks) ScopedTrack track2{ session, "MyTrackFile2" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(track1.getId() != track2.getId()); EXPECT_TRUE(track1.get() != track2.get()); @@ -87,7 +87,7 @@ TEST_F(DatabaseFixture, MultipleTracksSearchByFilter) ScopedTrack track6{ session, "" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track1.get().modify()->setName("MyTrack"); track2.get().modify()->setName("MyTrack%"); track3.get().modify()->setName("MyTrack%Foo"); @@ -97,7 +97,7 @@ TEST_F(DatabaseFixture, MultipleTracksSearchByFilter) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; { const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"Track"})) }; @@ -128,19 +128,19 @@ TEST_F(DatabaseFixture, Track_date) ScopedTrack track{ session, "MyTrack" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(track->getYear(), std::nullopt); EXPECT_EQ(track->getOriginalYear(), std::nullopt); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setDate(Wt::WDate{ 1995, 5, 5 }); track.get().modify()->setOriginalDate(Wt::WDate{ 1994, 2, 2 }); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(track->getYear(), 1995); EXPECT_EQ(track->getOriginalYear(), 1994); } @@ -153,24 +153,24 @@ TEST_F(DatabaseFixture, Track_writtenAfter) const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 1}, Wt::WTime {12, 30, 20} }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; track.get().modify()->setLastWriteTime(dateTime); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters {}) }; EXPECT_EQ(tracks.results.size(), 1); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters {}.setWrittenAfter(dateTime.addSecs(-1))) }; EXPECT_EQ(tracks.results.size(), 1); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters {}.setWrittenAfter(dateTime.addSecs(+1))) }; EXPECT_EQ(tracks.results.size(), 0); } diff --git a/src/libs/services/database/test/TrackBookmark.cpp b/src/libs/services/database/test/TrackBookmark.cpp index d8720beb..ed904bfa 100644 --- a/src/libs/services/database/test/TrackBookmark.cpp +++ b/src/libs/services/database/test/TrackBookmark.cpp @@ -31,21 +31,21 @@ TEST_F(DatabaseFixture, TrackBookmark) ScopedUser user {session, "MyUser"}; { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; EXPECT_EQ(TrackBookmark::getCount(session), 0); } ScopedTrackBookmark bookmark {session, user.lockAndGet(), track.lockAndGet()}; { - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; bookmark.get().modify()->setComment("MyComment"); bookmark.get().modify()->setOffset(std::chrono::milliseconds {5}); } { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; EXPECT_EQ(TrackBookmark::getCount(session), 1); @@ -54,7 +54,7 @@ TEST_F(DatabaseFixture, TrackBookmark) EXPECT_EQ(bookmarks.results.front(), bookmark.getId()); } { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; auto userBookmark {TrackBookmark::find(session, user.getId(), track.getId())}; ASSERT_TRUE(userBookmark); diff --git a/src/libs/services/database/test/TrackFeatures.cpp b/src/libs/services/database/test/TrackFeatures.cpp index 0f824124..552ada13 100644 --- a/src/libs/services/database/test/TrackFeatures.cpp +++ b/src/libs/services/database/test/TrackFeatures.cpp @@ -31,14 +31,14 @@ TEST_F(DatabaseFixture, TrackFeatures) ScopedUser user {session, "MyUser"}; { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; EXPECT_EQ(TrackFeatures::getCount(session), 0); } ScopedTrackFeatures trackFeatures {session, track.lockAndGet(), ""}; { - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createWriteTransaction()}; EXPECT_EQ(TrackFeatures::getCount(session), 1); auto allTrackFeatures {TrackFeatures::find(session)}; diff --git a/src/libs/services/database/test/TrackList.cpp b/src/libs/services/database/test/TrackList.cpp index 70467128..a1d0c979 100644 --- a/src/libs/services/database/test/TrackList.cpp +++ b/src/libs/services/database/test/TrackList.cpp @@ -27,14 +27,14 @@ TEST_F(DatabaseFixture, SingleTrackList) { ScopedUser user{ session, "MyUser" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(TrackList::getCount(session), 0); } ScopedTrackList trackList{ session, "MytrackList", TrackListType::Playlist, false, user.lockAndGet() }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; EXPECT_EQ(TrackList::getCount(session), 1); } } @@ -47,7 +47,7 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrack) ScopedTrack track{ session, "MyTrack" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters {}.setTrackList(trackList1.getId())) }; EXPECT_EQ(tracks.results.size(), 0); @@ -57,13 +57,13 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrack) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; session.create(track.get(), trackList1.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters {}.setTrackList(trackList1.getId())) }; ASSERT_EQ(tracks.results.size(), 1); @@ -82,7 +82,7 @@ TEST_F(DatabaseFixture, TrackList_SortMethod) ScopedTrack track{ session, "MyTrack" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::Name)) }; ASSERT_EQ(trackLists.results.size(), 2); @@ -91,14 +91,14 @@ TEST_F(DatabaseFixture, TrackList_SortMethod) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; trackList1.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,1} }); trackList2.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,2} }); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc)) }; ASSERT_EQ(trackLists.results.size(), 2); @@ -107,14 +107,14 @@ TEST_F(DatabaseFixture, TrackList_SortMethod) } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; trackList1.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,2} }); trackList2.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,1} }); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc)) }; ASSERT_EQ(trackLists.results.size(), 2); @@ -133,12 +133,12 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrack) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; session.create(tracks.back().get(), trackList.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; ASSERT_EQ(trackList->getCount(), tracks.size()); const auto trackIds{ trackList->getTrackIds() }; @@ -161,21 +161,21 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster) ScopedTrack track{ session, "MyTrack" }; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) }; EXPECT_EQ(trackLists.results.size(), 0); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; session.create(track.get(), trackList1.get()); cluster.get().modify()->addTrack(track.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(trackLists.results.size(), 1); @@ -191,24 +191,24 @@ TEST_F(DatabaseFixture, SingleTrackList_getEntries) ScopedTrack track2{ session, "MyTrack" }; { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; session.create(track1.get(), trackList.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto entries{ trackList.get()->getEntries() }; ASSERT_EQ(entries.size(), 1); EXPECT_EQ(entries.front()->getTrack()->getId(), track1.getId()); } { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; session.create(track2.get(), trackList.get()); } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto entries{ trackList.get()->getEntries() }; ASSERT_EQ(entries.size(), 2); EXPECT_EQ(entries[0]->getTrack()->getId(), track1.getId()); @@ -216,7 +216,7 @@ TEST_F(DatabaseFixture, SingleTrackList_getEntries) } { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; auto entries{ trackList.get()->getEntries(Range {1, 1}) }; ASSERT_EQ(entries.size(), 1); EXPECT_EQ(entries[0]->getTrack()->getId(), track2.getId()); diff --git a/src/libs/services/feedback/impl/FeedbackService.cpp b/src/libs/services/feedback/impl/FeedbackService.cpp index fb66c740..6afafe19 100644 --- a/src/libs/services/feedback/impl/FeedbackService.cpp +++ b/src/libs/services/feedback/impl/FeedbackService.cpp @@ -60,7 +60,7 @@ namespace Feedback std::optional feedbackBackend; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; if (const User::pointer user{ User::find(session, userId) }) feedbackBackend = user->getFeedbackBackend(); @@ -101,7 +101,7 @@ namespace Feedback searchParams.setRange(params.range); Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; return Artist::findIds(session, searchParams); } @@ -139,7 +139,7 @@ namespace Feedback searchParams.setRange(params.range); Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; return Release::findIds(session, searchParams); } @@ -177,7 +177,7 @@ namespace Feedback searchParams.setRange(params.range); Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; return Track::findIds(session, searchParams); } diff --git a/src/libs/services/feedback/impl/FeedbackService.impl.hpp b/src/libs/services/feedback/impl/FeedbackService.impl.hpp index b9220ffd..93ab8583 100644 --- a/src/libs/services/feedback/impl/FeedbackService.impl.hpp +++ b/src/libs/services/feedback/impl/FeedbackService.impl.hpp @@ -37,7 +37,7 @@ namespace Feedback typename StarredObjType::IdType starredObjId; { Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId, *backend) }; if (!starredObj) @@ -68,7 +68,7 @@ namespace Feedback typename StarredObjType::IdType starredObjId; { Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId, *backend) }; if (!starredObj) @@ -83,7 +83,7 @@ namespace Feedback bool FeedbackService::isStarred(UserId userId, ObjIdType objId) { Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId) }; return starredObj && (starredObj->getSyncState() != SyncState::PendingRemove); @@ -93,7 +93,7 @@ namespace Feedback Wt::WDateTime FeedbackService::getStarredDateTime(UserId userId, ObjIdType objId) { Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; typename StarredObjType::pointer starredObj{ StarredObjType::find(session, objId, userId) }; if (starredObj && (starredObj->getSyncState() != SyncState::PendingRemove)) diff --git a/src/libs/services/feedback/impl/internal/InternalBackend.cpp b/src/libs/services/feedback/impl/internal/InternalBackend.cpp index e73e3d1f..0c2582a9 100644 --- a/src/libs/services/feedback/impl/internal/InternalBackend.cpp +++ b/src/libs/services/feedback/impl/internal/InternalBackend.cpp @@ -32,7 +32,7 @@ namespace Feedback template void onStarred(Database::Session& session, typename StarredObjType::IdType id) { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (auto starredObj{ StarredObjType::find(session, id) }) starredObj.modify()->setSyncState(Database::SyncState::Synchronized); @@ -41,7 +41,7 @@ namespace Feedback template void onUnstarred(Database::Session& session, typename StarredObjType::IdType id) { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (auto starredObj{ StarredObjType::find(session, id) }) starredObj.remove(); diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp index 6a9e91fa..eeb6d792 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp @@ -77,7 +77,7 @@ namespace Feedback::ListenBrainz { Database::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; Database::StarredTrack::pointer starredTrack{ Database::StarredTrack::find(session, starredTrackId) }; if (!starredTrack) @@ -151,7 +151,7 @@ namespace Feedback::ListenBrainz assert(_strand.running_in_this_thread()); Database::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; Database::StarredTrack::pointer starredTrack{ Database::StarredTrack::find(session, starredTrackId) }; if (!starredTrack) @@ -202,7 +202,7 @@ namespace Feedback::ListenBrainz { Database::Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; StarredTrack::FindParameters params; params.setFeedbackBackend(Database::FeedbackBackend::ListenBrainz, scrobblingState) @@ -277,7 +277,7 @@ namespace Feedback::ListenBrainz Database::RangeResults userIds; { Database::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; userIds = Database::User::find(_db.getTLSSession(), Database::User::FindParameters{}.setFeedbackBackend(Database::FeedbackBackend::ListenBrainz)); } @@ -437,7 +437,7 @@ namespace Feedback::ListenBrainz TrackId trackId; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const std::vector tracks{ Track::findByRecordingMBID(session, feedback.recordingMBID) }; if (tracks.size() > 1) { @@ -463,7 +463,7 @@ namespace Feedback::ListenBrainz { LOG(DEBUG) << "Importing feedback '" << feedback << "'"; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; const Track::pointer track{ Track::find(session, trackId) }; if (!track) diff --git a/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp b/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp index d75a9072..c05a11bc 100644 --- a/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp @@ -37,7 +37,7 @@ namespace Feedback::ListenBrainz template void onStarred(Database::Session& session, typename StarredObjType::IdType id) { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (auto starredObj{ StarredObjType::find(session, id) }) { @@ -49,7 +49,7 @@ namespace Feedback::ListenBrainz template void onUnstarred(Database::Session& session, typename StarredObjType::IdType id) { - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (auto starredObj{ StarredObjType::find(session, id) }) starredObj.remove(); diff --git a/src/libs/services/feedback/impl/listenbrainz/Utils.cpp b/src/libs/services/feedback/impl/listenbrainz/Utils.cpp index 28b4de9d..58d28a76 100644 --- a/src/libs/services/feedback/impl/listenbrainz/Utils.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/Utils.cpp @@ -29,7 +29,7 @@ namespace Feedback::ListenBrainz::Utils { std::optional getListenBrainzToken(Database::Session& session, Database::UserId userId) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const Database::User::pointer user{ Database::User::find(session, userId) }; if (!user) diff --git a/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp b/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp index 6e235aea..9b3ffa37 100644 --- a/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp +++ b/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp @@ -100,7 +100,7 @@ namespace Recommendation TrackContainer tracks; Session& dbSession{ _db.getTLSSession() }; - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; Track::FindParameters params; params.setTrackList(tracklistId); diff --git a/src/libs/services/recommendation/impl/RecommendationService.cpp b/src/libs/services/recommendation/impl/RecommendationService.cpp index 02e77870..460b7ef7 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.cpp +++ b/src/libs/services/recommendation/impl/RecommendationService.cpp @@ -37,7 +37,7 @@ namespace Recommendation { Database::ScanSettings::SimilarityEngineType getSimilarityEngineType(Database::Session& session) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; return Database::ScanSettings::get(session)->getSimilarityEngineType(); } diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp index d34fb6f6..a35faf7a 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp @@ -42,7 +42,7 @@ namespace Recommendation { return {}; Session& dbSession{ _db.getTLSSession() }; - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; const auto similarTrackIds{ Track::findSimilarTrackIds(dbSession, trackIds, Range {0, maxCount}) }; return std::move(similarTrackIds.results); @@ -56,7 +56,7 @@ namespace Recommendation { { Session& dbSession{ _db.getTLSSession() }; - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; const TrackList::pointer trackList{ TrackList::find(dbSession, tracklistId) }; if (!trackList) @@ -78,7 +78,7 @@ namespace Recommendation { { Session& dbSession{ _db.getTLSSession() }; - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; auto release{ Release::find(dbSession, releaseId) }; if (!release) @@ -98,7 +98,7 @@ namespace Recommendation { return {}; Session& dbSession{ _db.getTLSSession() }; - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; auto artist{ Artist::find(dbSession, artistId) }; if (!artist) diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp index 60113e0b..7678e3ad 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp @@ -117,7 +117,7 @@ FeaturesEngine::loadFromTraining(const TrainSettings& trainSettings, const Progr RangeResults trackFeaturesIds; { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; LMS_LOG(RECOMMENDATION, DEBUG) << "Getting Track features..."; trackFeaturesIds = TrackFeatures::find(session); @@ -137,7 +137,7 @@ FeaturesEngine::loadFromTraining(const TrainSettings& trainSettings, const Progr if (_loadCancelled) return; - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; TrackFeatures::pointer trackFeatures {TrackFeatures::find(session, trackFeaturesId)}; if (!trackFeatures) @@ -228,7 +228,7 @@ FeaturesEngine::findSimilarTracksFromTrackList(TrackListId trackListId, std::siz Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; const TrackList::pointer trackList {TrackList::find(session, trackListId)}; if (trackList) @@ -249,7 +249,7 @@ FeaturesEngine::findSimilarTracks(const std::vector& tracksIds, std::si { // Report only existing ids, as tracks may have been removed a long time ago (refreshing the SOM takes some time) - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; similarTrackIds.erase(std::remove_if(std::begin(similarTrackIds), std::end(similarTrackIds), [&](TrackId trackId) @@ -271,7 +271,7 @@ FeaturesEngine::getSimilarReleases(ReleaseId releaseId, std::size_t maxCount) co if (!similarReleaseIds.empty()) { // Report only existing ids - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; similarReleaseIds.erase(std::remove_if(std::begin(similarReleaseIds), std::end(similarReleaseIds), [&](ReleaseId releaseId) @@ -312,7 +312,7 @@ FeaturesEngine::getSimilarArtists(ArtistId artistId, EnumSet tracks = Track::findIdsTrackMBIDDuplicates(session); for (const TrackId trackId : tracks.results) diff --git a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp index 38399599..6afb038e 100644 --- a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp +++ b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp @@ -36,7 +36,7 @@ namespace Scanner Session& dbSession{ _db.getTLSSession() }; const std::size_t clusterCount{ [&] { - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; return Cluster::getCount(dbSession); }() }; @@ -50,7 +50,7 @@ namespace Scanner params.setRange(range); { - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; return std::move(Cluster::findIds(dbSession, params).results); } }() }; @@ -61,14 +61,14 @@ namespace Scanner std::size_t releaseCount; { - auto transaction{ dbSession.createSharedTransaction() }; + auto transaction{ dbSession.createReadTransaction() }; trackCount = Cluster::computeTrackCount(dbSession, clusterId); releaseCount = Cluster::computeReleaseCount(dbSession, clusterId); } { - auto transaction{ dbSession.createUniqueTransaction() }; + auto transaction{ dbSession.createWriteTransaction() }; auto cluster{ Cluster::find(dbSession, clusterId) }; cluster.modify()->setTrackCount(trackCount); diff --git a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp index 1ae6781d..97b69385 100644 --- a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp @@ -52,7 +52,7 @@ namespace Scanner std::size_t trackCount{}; { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; trackCount = Track::getCount(session); } LMS_LOG(DBUPDATER, DEBUG) << trackCount << " tracks to be checked..."; @@ -68,7 +68,7 @@ namespace Scanner tracksToRemove.clear(); { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; trackPaths = Track::findPaths(session, Range{ i, batchSize }); } @@ -85,7 +85,7 @@ namespace Scanner if (!tracksToRemove.empty()) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createWriteTransaction() }; for (const TrackId trackId : tracksToRemove) { @@ -113,7 +113,7 @@ namespace Scanner LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan clusters..."; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; // Now process orphan Cluster (no track) auto clusterIds{ Cluster::findOrphans(session) }; @@ -132,7 +132,7 @@ namespace Scanner LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan artists..."; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto artistIds{ Artist::findOrphanIds(session) }; for (const ArtistId artistId : artistIds.results) @@ -151,7 +151,7 @@ namespace Scanner // TODO, by batch Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; auto releases{ Release::findOrphanIds(session) }; for (const ReleaseId releaseId : releases.results) diff --git a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp index 74a33540..615900fa 100644 --- a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp @@ -337,7 +337,7 @@ namespace Scanner { // Skip file if last write is the same Database::Session& dbSession{ _db.getTLSSession() }; - auto transaction{ _db.getTLSSession().createSharedTransaction() }; + auto transaction{ _db.getTLSSession().createReadTransaction() }; const Track::pointer track{ Track::findByPath(dbSession, file) }; @@ -359,7 +359,7 @@ namespace Scanner stats.scans++; Database::Session& dbSession{ _db.getTLSSession() }; - auto uniqueTransaction{ dbSession.createUniqueTransaction() }; + auto transaction{ dbSession.createWriteTransaction() }; Track::pointer track{ Track::findByPath(dbSession, file) }; diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index 17eba786..2a0b57bd 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -345,7 +345,7 @@ namespace Scanner newSettings.skipDuplicateMBID = Service::get()->getBool("scanner-skip-duplicate-mbid", false); { - auto transaction{ _dbSession.createSharedTransaction() }; + auto transaction{ _dbSession.createReadTransaction() }; const ScanSettings::pointer scanSettings{ ScanSettings::get(_dbSession) }; diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp index b28b64e2..512e224f 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -77,7 +77,7 @@ namespace Scrobbling std::optional backend; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; if (const User::pointer user{ User::find(session, userId) }) backend = user->getScrobblingBackend(); @@ -93,7 +93,7 @@ namespace Scrobbling return res; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; res = Database::Listen::getRecentArtists(session, userId, *backend, clusterIds, linkType, range); return res; @@ -108,7 +108,7 @@ namespace Scrobbling return res; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; res = Database::Listen::getRecentReleases(session, userId, *backend, clusterIds, range); return res; @@ -123,7 +123,7 @@ namespace Scrobbling return res; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; res = Database::Listen::getRecentTracks(session, userId, *backend, clusterIds, range); return res; @@ -136,7 +136,7 @@ namespace Scrobbling return {}; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const Database::Listen::pointer listen{ Database::Listen::getMostRecentListen(session, userId, *backend, releaseId) }; return listen ? listen->getDateTime() : Wt::WDateTime{}; @@ -149,7 +149,7 @@ namespace Scrobbling return {}; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const Database::Listen::pointer listen{ Database::Listen::getMostRecentListen(session, userId, *backend, trackId) }; return listen ? listen->getDateTime() : Wt::WDateTime{}; @@ -165,7 +165,7 @@ namespace Scrobbling return res; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; res = Database::Listen::getTopArtists(session, userId, *backend, clusterIds, linkType, range); return res; @@ -180,7 +180,7 @@ namespace Scrobbling return res; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; res = Database::Listen::getTopReleases(session, userId, *backend, clusterIds, range); return res; @@ -195,7 +195,7 @@ namespace Scrobbling return res; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; res = Database::Listen::getTopTracks(session, userId, *backend, clusterIds, range); return res; diff --git a/src/libs/services/scrobbling/impl/internal/InternalBackend.cpp b/src/libs/services/scrobbling/impl/internal/InternalBackend.cpp index 78c4353d..cf30a551 100644 --- a/src/libs/services/scrobbling/impl/internal/InternalBackend.cpp +++ b/src/libs/services/scrobbling/impl/internal/InternalBackend.cpp @@ -48,7 +48,7 @@ namespace Scrobbling void InternalBackend::addTimedListen(const TimedListen& listen) { Database::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; if (Database::Listen::find(session, listen.userId, listen.trackId, Database::ScrobblingBackend::Internal, listen.listenedAt)) return; diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp index 1b1a4350..b8e43523 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp @@ -36,7 +36,7 @@ namespace Scrobbling::ListenBrainz { bool canBeScrobbled(Session& session, TrackId trackId, std::chrono::seconds duration) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const Track::pointer track{ Track::find(session, trackId) }; if (!track) diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index b72a5238..09972615 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -46,7 +46,7 @@ namespace std::optional listenToJsonPayload(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const Database::Track::pointer track{ Database::Track::find(session, listen.trackId) }; if (!track) @@ -143,7 +143,7 @@ namespace { using namespace Database; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; // first try to match using track MBID, and then fallback on possibly ambiguous info if (listen.trackMBID) @@ -289,7 +289,7 @@ namespace Scrobbling::ListenBrainz using namespace Database; Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; // TODO: unique only if needed + auto transaction{ session.createWriteTransaction() }; // TODO: unique only if needed Database::Listen::pointer dbListen{ Database::Listen::find(session, listen.userId, listen.trackId, Database::ScrobblingBackend::ListenBrainz, listen.listenedAt) }; if (!dbListen) @@ -324,7 +324,7 @@ namespace Scrobbling::ListenBrainz { Database::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; Database::Listen::FindParameters params; params.setScrobblingBackend(Database::ScrobblingBackend::ListenBrainz) @@ -408,7 +408,7 @@ namespace Scrobbling::ListenBrainz Database::RangeResults userIds; { Database::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; userIds = Database::User::find(_db.getTLSSession(), Database::User::FindParameters{}.setScrobblingBackend(Database::ScrobblingBackend::ListenBrainz)); } diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp index e6ad55b7..2593dcf1 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp @@ -30,7 +30,7 @@ namespace Scrobbling::ListenBrainz::Utils std::optional getListenBrainzToken(Database::Session& session, Database::UserId userId) { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; const Database::User::pointer user {Database::User::find(session, userId)}; if (!user) diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 7c22f043..49c5f62a 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -135,7 +135,7 @@ namespace API::Subsonic void checkUserTypeIsAllowed(RequestContext& context, EnumSet allowedUserTypes) { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer currentUser{ User::find(context.dbSession, context.userId) }; if (!currentUser) @@ -403,7 +403,7 @@ namespace API::Subsonic if (request.continuation()) { Database::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const auto user{ Database::User::find(session, clientInfo.user) }; if (!user) diff --git a/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp b/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp index 6a55ffc6..d3b66a63 100644 --- a/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp +++ b/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp @@ -56,7 +56,7 @@ namespace API::Subsonic Scrobbling::IScrobblingService& scrobblingService{ *Service::get() }; Feedback::IFeedbackService& feedbackService{ *Service::get() }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -156,7 +156,7 @@ namespace API::Subsonic Response handleGetStarredRequestCommon(RequestContext& context, bool id3) { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -214,7 +214,7 @@ namespace API::Subsonic if (size > defaultMaxCountSize) throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -247,7 +247,7 @@ namespace API::Subsonic std::size_t offset{ getParameterAs(context.parameters, "offset").value_or(0) }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; auto clusterType{ ClusterType::find(context.dbSession, "GENRE") }; if (!clusterType) diff --git a/src/libs/subsonic/impl/entrypoints/Bookmarks.cpp b/src/libs/subsonic/impl/entrypoints/Bookmarks.cpp index 1d20eb76..ce6b3d8a 100644 --- a/src/libs/subsonic/impl/entrypoints/Bookmarks.cpp +++ b/src/libs/subsonic/impl/entrypoints/Bookmarks.cpp @@ -34,7 +34,7 @@ namespace API::Subsonic Response handleGetBookmarks(RequestContext& context) { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -64,7 +64,7 @@ namespace API::Subsonic unsigned long position{ getMandatoryParameterAs(context.parameters, "position") }; const std::optional comment{ getParameterAs(context.parameters, "comment") }; - auto transaction{ context.dbSession.createUniqueTransaction() }; + auto transaction{ context.dbSession.createWriteTransaction() }; const User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -91,7 +91,7 @@ namespace API::Subsonic // Mandatory params TrackId trackId{ getMandatoryParameterAs(context.parameters, "id") }; - auto transaction{ context.dbSession.createUniqueTransaction() }; + auto transaction{ context.dbSession.createWriteTransaction() }; auto bookmark{ TrackBookmark::find(context.dbSession, context.userId, trackId) }; if (!bookmark) diff --git a/src/libs/subsonic/impl/entrypoints/Browsing.cpp b/src/libs/subsonic/impl/entrypoints/Browsing.cpp index c9dcd7e0..2d9d4cde 100644 --- a/src/libs/subsonic/impl/entrypoints/Browsing.cpp +++ b/src/libs/subsonic/impl/entrypoints/Browsing.cpp @@ -58,7 +58,7 @@ namespace API::Subsonic Response::Node& artistInfoNode{ response.createNode(id3 ? Response::Node::Key{ "artistInfo2" } : Response::Node::Key{ "artistInfo" }) }; { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; const Artist::pointer artist{ Artist::find(context.dbSession, id) }; if (!artist) @@ -72,7 +72,7 @@ namespace API::Subsonic auto similarArtistsId{ Service::get()->getSimilarArtists(id, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, count) }; { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -99,7 +99,7 @@ namespace API::Subsonic Artist::FindParameters parameters; { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -129,7 +129,7 @@ namespace API::Subsonic bool hasMoreArtists{ true }; while (hasMoreArtists) { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; parameters.setRange(Range{ currentArtistOffset, batchSize }); const auto artists{ Artist::find(context.dbSession, parameters) }; @@ -159,7 +159,7 @@ namespace API::Subsonic for (const ArtistId artistId : artistIds) { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) throw UserNotAuthorizedError{}; @@ -181,7 +181,7 @@ namespace API::Subsonic const std::size_t meanTrackCountPerArtist{ (count / artistIds.size()) + 1 }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; std::vector tracks; tracks.reserve(count); @@ -212,7 +212,7 @@ namespace API::Subsonic const std::size_t meanTrackCountPerRelease{ (count / releaseIds.size()) + 1 }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; std::vector tracks; tracks.reserve(count); @@ -258,7 +258,7 @@ namespace API::Subsonic Random::shuffleContainer(tracks); - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -307,7 +307,7 @@ namespace API::Subsonic Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response::Node& directoryNode{ response.createNode("directory") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -366,7 +366,7 @@ namespace API::Subsonic Response::Node& genresNode{ response.createNode("genres") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; const ClusterType::pointer clusterType{ ClusterType::find(context.dbSession, "GENRE") }; if (clusterType) @@ -390,7 +390,7 @@ namespace API::Subsonic // Mandatory params ArtistId id{ getMandatoryParameterAs(context.parameters, "id") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; const Artist::pointer artist{ Artist::find(context.dbSession, id) }; if (!artist) @@ -417,7 +417,7 @@ namespace API::Subsonic // Mandatory params ReleaseId id{ getMandatoryParameterAs(context.parameters, "id") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; Release::pointer release{ Release::find(context.dbSession, id) }; if (!release) @@ -444,7 +444,7 @@ namespace API::Subsonic // Mandatory params TrackId id{ getMandatoryParameterAs(context.parameters, "id") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; const Track::pointer track{ Track::find(context.dbSession, id) }; if (!track) diff --git a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp index 574fb546..36e10b3f 100644 --- a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp @@ -127,7 +127,7 @@ namespace API::Subsonic StreamParameters parameters; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; const User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -199,7 +199,7 @@ namespace API::Subsonic std::filesystem::path trackPath; { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; auto track{ Track::find(context.dbSession, id) }; if (!track) diff --git a/src/libs/subsonic/impl/entrypoints/Playlists.cpp b/src/libs/subsonic/impl/entrypoints/Playlists.cpp index 8e52efbf..7d3307d6 100644 --- a/src/libs/subsonic/impl/entrypoints/Playlists.cpp +++ b/src/libs/subsonic/impl/entrypoints/Playlists.cpp @@ -34,7 +34,7 @@ namespace API::Subsonic Response handleGetPlaylistsRequest(RequestContext& context) { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response::Node& playlistsNode{ response.createNode("playlists") }; @@ -58,7 +58,7 @@ namespace API::Subsonic // Mandatory params TrackListId trackListId{ getMandatoryParameterAs(context.parameters, "id") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -91,7 +91,7 @@ namespace API::Subsonic if (!name && !id) throw RequiredParameterMissingError{ "name or id" }; - auto transaction{ context.dbSession.createUniqueTransaction() }; + auto transaction{ context.dbSession.createWriteTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -140,7 +140,7 @@ namespace API::Subsonic std::vector trackIdsToAdd{ getMultiParametersAs(context.parameters, "songIdToAdd") }; std::vector trackPositionsToRemove{ getMultiParametersAs(context.parameters, "songIndexToRemove") }; - auto transaction{ context.dbSession.createUniqueTransaction() }; + auto transaction{ context.dbSession.createWriteTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) @@ -189,7 +189,7 @@ namespace API::Subsonic { TrackListId id{ getMandatoryParameterAs(context.parameters, "id") }; - auto transaction{ context.dbSession.createUniqueTransaction() }; + auto transaction{ context.dbSession.createWriteTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) diff --git a/src/libs/subsonic/impl/entrypoints/Searching.cpp b/src/libs/subsonic/impl/entrypoints/Searching.cpp index 41d1e774..d613985d 100644 --- a/src/libs/subsonic/impl/entrypoints/Searching.cpp +++ b/src/libs/subsonic/impl/entrypoints/Searching.cpp @@ -67,7 +67,7 @@ namespace API::Subsonic Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response::Node& searchResult2Node{ response.createNode(id3 ? "searchResult3" : "searchResult2") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, context.userId) }; if (!user) diff --git a/src/libs/subsonic/impl/entrypoints/UserManagement.cpp b/src/libs/subsonic/impl/entrypoints/UserManagement.cpp index d154726c..254814e9 100644 --- a/src/libs/subsonic/impl/entrypoints/UserManagement.cpp +++ b/src/libs/subsonic/impl/entrypoints/UserManagement.cpp @@ -28,7 +28,7 @@ namespace API::Subsonic { std::string username{ getMandatoryParameterAs(context.parameters, "username") }; - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; checkUserIsMySelfOrAdmin(context, username); @@ -44,7 +44,7 @@ namespace API::Subsonic Response handleGetUsersRequest(RequestContext& context) { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response::Node& usersNode{ response.createNode("users") }; @@ -67,7 +67,7 @@ namespace API::Subsonic Database::UserId userId; { - auto transaction{ context.dbSession.createUniqueTransaction() }; + auto transaction{ context.dbSession.createWriteTransaction() }; User::pointer user{ User::find(context.dbSession, username) }; if (user) @@ -79,7 +79,7 @@ namespace API::Subsonic auto removeCreatedUser{ [&]() { - auto transaction {context.dbSession.createUniqueTransaction()}; + auto transaction {context.dbSession.createWriteTransaction()}; User::pointer user {User::find(context.dbSession, userId)}; if (user) user.remove(); @@ -112,7 +112,7 @@ namespace API::Subsonic { std::string username{ getMandatoryParameterAs(context.parameters, "username") }; - auto transaction{ context.dbSession.createUniqueTransaction() }; + auto transaction{ context.dbSession.createWriteTransaction() }; User::pointer user{ User::find(context.dbSession, username) }; if (!user) @@ -134,7 +134,7 @@ namespace API::Subsonic UserId userId; { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; User::pointer user{ User::find(context.dbSession, username) }; if (!user) @@ -177,7 +177,7 @@ namespace API::Subsonic { Database::UserId userId; { - auto transaction{ context.dbSession.createSharedTransaction() }; + auto transaction{ context.dbSession.createReadTransaction() }; checkUserIsMySelfOrAdmin(context, username); diff --git a/src/lms/main.cpp b/src/lms/main.cpp index ebef9dd4..79a74114 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -234,8 +234,8 @@ int main(int argc, char* argv[]) IOContextRunner ioContextRunner{ ioContext, getThreadCount() }; - // Initializing a connection pool to the database that will be shared along services - Database::Db database{ config->getPath("working-dir") / "lms.db", getThreadCount() }; + // Connection pool size must be twice the number of threads: we have at least 2 io pools with getThreadCount() each and they all may access the database + Database::Db database{ config->getPath("working-dir") / "lms.db", getThreadCount() * 2 }; { Database::Session session{ database }; session.prepareTables(); diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 95646614..47071201 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -106,7 +106,7 @@ class AuthModel : public Wt::WFormModel { bool isDemo; { - auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; + auto transaction {LmsApp->getDbSession().createWriteTransaction()}; Database::User::pointer user {Database::User::find(LmsApp->getDbSession(), valueText(LoginNameField).toUTF8())}; user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); @@ -200,7 +200,7 @@ Auth::Auth() setFormWidget(AuthModel::RememberMeField, std::make_unique()); { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; Database::User::pointer demoUser {Database::User::findDemoUser(LmsApp->getDbSession())}; if (demoUser) diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 0b7463f0..85d1fce2 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -169,7 +169,7 @@ LmsApplication::isUserAuthStrong() const Database::UserType LmsApplication::getUserType() { - auto transaction {getDbSession().createSharedTransaction()}; + auto transaction {getDbSession().createReadTransaction()}; return getUser()->getType(); } @@ -177,7 +177,7 @@ LmsApplication::getUserType() std::string LmsApplication::getUserLoginName() { - auto transaction {getDbSession().createSharedTransaction()}; + auto transaction {getDbSession().createReadTransaction()}; return getUser()->getLoginName(); } @@ -246,7 +246,7 @@ LmsApplication::processPasswordAuth() // If here is no account in the database, launch the first connection wizard bool firstConnection {}; { - auto transaction {getDbSession().createSharedTransaction()}; + auto transaction {getDbSession().createReadTransaction()}; firstConnection = Database::User::getCount(getDbSession()) == 0; } @@ -360,7 +360,7 @@ void LmsApplication::logoutUser() { { - auto transaction {getDbSession().createUniqueTransaction()}; + auto transaction {getDbSession().createWriteTransaction()}; getUser().modify()->clearAuthTokens(); } diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index 96e7413f..70797864 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -237,7 +237,7 @@ namespace UserInterface std::ostringstream oss; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const auto track{ Database::Track::find(LmsApp->getDbSession(), trackId) }; if (!track) diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index bb414679..5bdd331e 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -95,7 +95,7 @@ namespace UserInterface auto model{ std::make_shared() }; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; TrackList::FindParameters params; params.setType(TrackListType::Playlist); @@ -148,7 +148,7 @@ namespace UserInterface shuffleBtn->clicked().connect([=] { { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; Database::TrackList::pointer queue{ getQueue() }; auto entries{ queue->getEntries() }; @@ -165,13 +165,13 @@ namespace UserInterface _repeatBtn = bindNew("repeat-btn"); _repeatBtn->clicked().connect([=] { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; if (!LmsApp->getUser()->isDemo()) LmsApp->getUser().modify()->setRepeatAll(isRepeatAllSet()); }); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; if (LmsApp->getUser()->isRepeatAllSet()) _repeatBtn->setCheckState(Wt::CheckState::Checked); } @@ -180,7 +180,7 @@ namespace UserInterface _radioBtn->clicked().connect([=] { { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; if (!LmsApp->getUser()->isDemo()) LmsApp->getUser().modify()->setRadio(isRadioModeSet()); @@ -191,7 +191,7 @@ namespace UserInterface bool isRadioModeSet{}; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; isRadioModeSet = LmsApp->getUser()->isRadioSet(); } if (isRadioModeSet) @@ -213,7 +213,7 @@ namespace UserInterface std::size_t trackPos{}; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; trackPos = LmsApp->getUser()->getCurPlayingTrackPos(); } @@ -222,7 +222,7 @@ namespace UserInterface LmsApp->preQuit().connect([=] { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; if (LmsApp->getUser()->isDemo()) { @@ -252,14 +252,14 @@ namespace UserInterface bool PlayQueue::isFull() const { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; return getQueue()->getCount() == getCapacity(); } void PlayQueue::clearTracks() { { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; getQueue().modify()->clear(); } @@ -283,7 +283,7 @@ namespace UserInterface Database::TrackId trackId{}; std::optional replayGain{}; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; const Database::TrackList::pointer queue{ getQueue() }; @@ -345,13 +345,13 @@ namespace UserInterface std::size_t PlayQueue::getCount() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; return getQueue()->getCount(); } void PlayQueue::initTrackLists() { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; Database::TrackList::pointer queue; Database::TrackList::pointer radioStartingTracks; @@ -374,7 +374,7 @@ namespace UserInterface void PlayQueue::updateInfo() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::TrackList::pointer queue{ getQueue() }; const std::size_t trackCount{ queue->getCount() }; @@ -398,7 +398,7 @@ namespace UserInterface void PlayQueue::enqueueTracks(const std::vector& trackIds) { { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; Database::TrackList::pointer queue{ getQueue() }; const std::size_t queueSize{ queue->getCount() }; @@ -427,7 +427,7 @@ namespace UserInterface { std::vector tracks; - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; Database::TrackList::pointer queue{ getQueue() }; std::vector entries{ queue->getEntries(Database::Range {_trackPos ? *_trackPos + 1 : 0, getCapacity()}) }; @@ -489,7 +489,7 @@ namespace UserInterface void PlayQueue::addSome() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::TrackList::pointer queue{ getQueue() }; const auto tracklistEntries{ queue->getEntries(Database::Range {_entriesContainer->getCount(), _batchSize}) }; @@ -550,7 +550,7 @@ namespace UserInterface { // Remove the entry n both the widget tree and the playqueue { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; Database::TrackListEntry::pointer entryToRemove{ Database::TrackListEntry::getById(LmsApp->getDbSession(), tracklistEntryId) }; entryToRemove.remove(); @@ -584,7 +584,7 @@ namespace UserInterface Wt::WPushButton* starBtn{ entry->bindNew("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) }; starBtn->clicked().connect([=] { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; if (isStarred()) { @@ -609,7 +609,7 @@ namespace UserInterface bool addTracks{}; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::TrackList::pointer queue{ getQueue() }; @@ -776,7 +776,7 @@ namespace UserInterface { Session& session{ LmsApp->getDbSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackList::pointer trackList{ session.create(name.toUTF8(), TrackListType::Playlist, false, LmsApp->getUser()) }; trackListId = trackList->getId(); } @@ -789,7 +789,7 @@ namespace UserInterface using namespace Database; Session& session{ LmsApp->getDbSession() }; - auto transaction{ session.createUniqueTransaction() }; + auto transaction{ session.createWriteTransaction() }; TrackList::pointer trackList{ TrackList::find(LmsApp->getDbSession(), trackListId) }; trackList.modify()->clear(); diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 2c311077..139d5b59 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -134,7 +134,7 @@ namespace UserInterface void saveData() { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; User::pointer user{ LmsApp->getUser() }; @@ -202,7 +202,7 @@ namespace UserInterface void loadData() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; User::pointer user{ LmsApp->getUser() }; @@ -560,7 +560,7 @@ namespace UserInterface saveBtn->clicked().connect([=] { { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; if (LmsApp->getUser()->isDemo()) { diff --git a/src/lms/ui/Utils.cpp b/src/lms/ui/Utils.cpp index c18f53de..fce31efd 100644 --- a/src/lms/ui/Utils.cpp +++ b/src/lms/ui/Utils.cpp @@ -83,7 +83,7 @@ namespace UserInterface::Utils std::unique_ptr createCluster(Database::ClusterId clusterId, bool canDelete) { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Cluster::pointer cluster{ Database::Cluster::find(LmsApp->getDbSession(), clusterId) }; if (!cluster) @@ -124,7 +124,7 @@ namespace UserInterface::Utils bool firstArtist{ true }; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; for (const ArtistId artistId : artistIds) { const Artist::pointer artist{ Artist::find(LmsApp->getDbSession(), artistId) }; @@ -152,7 +152,7 @@ namespace UserInterface::Utils std::string_view::size_type currentOffset{}; auto result{ std::make_unique() }; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; // consider order is guaranteed + we will likely succeed for (const ArtistId artistId : artistIds) diff --git a/src/lms/ui/admin/DatabaseSettingsView.cpp b/src/lms/ui/admin/DatabaseSettingsView.cpp index 97db0680..a3b033e3 100644 --- a/src/lms/ui/admin/DatabaseSettingsView.cpp +++ b/src/lms/ui/admin/DatabaseSettingsView.cpp @@ -85,7 +85,7 @@ class DatabaseSettingsModel : public Wt::WFormModel void loadData() { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const ScanSettings::pointer scanSettings {ScanSettings::get(LmsApp->getDbSession())}; @@ -120,7 +120,7 @@ class DatabaseSettingsModel : public Wt::WFormModel void saveData() { - auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; + auto transaction {LmsApp->getDbSession().createWriteTransaction()}; ScanSettings::pointer scanSettings {ScanSettings::get(LmsApp->getDbSession())}; diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index 98d96f64..7ef70578 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -60,7 +60,7 @@ class InitWizardModel : public Wt::WFormModel void saveData() { - auto transaction(LmsApp->getDbSession().createUniqueTransaction()); + auto transaction(LmsApp->getDbSession().createWriteTransaction()); // Check if a user already exist // If it's the case, just do nothing diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index cb4ea443..728a9a9a 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -93,7 +93,7 @@ class ReportResource : public Wt::WResource response.out() << Wt::WString::tr("Lms.Admin.ScannerController.duplicates-header").arg(_stats->duplicates.size()).toUTF8() << std::endl; { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; for (const auto& duplicate : _stats->duplicates) { diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index 64fecb36..6610fd11 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -76,7 +76,7 @@ class UserModel : public Wt::WFormModel void saveData() { - auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; + auto transaction {LmsApp->getDbSession().createWriteTransaction()}; if (_userId) { @@ -112,7 +112,7 @@ class UserModel : public Wt::WFormModel if (!_userId) return; - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const User::pointer user {User::find(LmsApp->getDbSession(), *_userId)}; if (!user) @@ -125,7 +125,7 @@ class UserModel : public Wt::WFormModel { if (_userId) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const User::pointer user {User::find(LmsApp->getDbSession(), *_userId)}; return user->getType(); @@ -138,7 +138,7 @@ class UserModel : public Wt::WFormModel { if (_userId) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const User::pointer user {User::find(LmsApp->getDbSession(), *_userId)}; return user->getLoginName(); @@ -153,7 +153,7 @@ class UserModel : public Wt::WFormModel if (field == LoginField) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const User::pointer user {User::find(LmsApp->getDbSession(), valueText(LoginField).toUTF8())}; if (user) @@ -161,7 +161,7 @@ class UserModel : public Wt::WFormModel } else if (field == DemoField) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; if (Wt::asNumber(value(DemoField)) && User::findDemoUser(LmsApp->getDbSession())) error = Wt::WString::tr("Lms.Admin.User.demo-account-already-exists"); @@ -209,7 +209,7 @@ UserView::refreshView() if (userId) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const User::pointer user {User::find(LmsApp->getDbSession(), *userId)}; if (!user) diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 10117e02..144f55d6 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -70,7 +70,7 @@ UsersView::refreshView() _container->clear(); - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const User::IdType currentUserId {LmsApp->getUser()}; for (const UserId userId : User::find(LmsApp->getDbSession(), User::FindParameters {}).results) @@ -112,7 +112,7 @@ UsersView::refreshView() delBtn->clicked().connect([=] { { - auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; + auto transaction {LmsApp->getDbSession().createWriteTransaction()}; User::pointer user {User::find(LmsApp->getDbSession(), userId)}; if (user) diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 3f5ca7b5..3bed8cdf 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -77,7 +77,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; artists = Artist::findIds(LmsApp->getDbSession(), params); } break; @@ -92,7 +92,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; artists = Artist::findIds(LmsApp->getDbSession(), params); } break; @@ -107,7 +107,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; artists = Artist::findIds(LmsApp->getDbSession(), params); } break; @@ -133,7 +133,7 @@ namespace UserInterface params.setRange(Range{ 0, getMaxCount() }); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; _randomArtists = Artist::findIds(LmsApp->getDbSession(), params); } } diff --git a/src/lms/ui/explore/ArtistListHelpers.cpp b/src/lms/ui/explore/ArtistListHelpers.cpp index 2270307f..57dd82cc 100644 --- a/src/lms/ui/explore/ArtistListHelpers.cpp +++ b/src/lms/ui/explore/ArtistListHelpers.cpp @@ -47,7 +47,7 @@ namespace UserInterface::ArtistListHelpers EnumSet usedLinkTypes; { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; usedLinkTypes = TrackArtistLink::findUsedTypes(LmsApp->getDbSession()); } diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 8410ee68..08787bf0 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -57,7 +57,7 @@ namespace UserInterface const auto mbid{ UUID::fromString(wApp->internalPathNextPart("/artist/mbid/")) }; if (mbid) { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; if (const Database::Artist::pointer artist{ Database::Artist::find(LmsApp->getDbSession(), *mbid) }) return artist->getId(); } @@ -124,7 +124,7 @@ namespace UserInterface const auto similarArtistIds{ Service::get()->getSimilarArtists(*artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5) }; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Artist::pointer artist{ Database::Artist::find(LmsApp->getDbSession(), *artistId) }; if (!artist) @@ -334,7 +334,7 @@ namespace UserInterface void Artist::addSomeReleases(ReleaseContainer& releaseContainer) { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; if (const Database::Artist::pointer artist{ Database::Artist::find(LmsApp->getDbSession(), _artistId) }) { @@ -349,7 +349,7 @@ namespace UserInterface bool Artist::addSomeNonReleaseTracks() { bool areTracksAdded{}; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Range range{ static_cast(_trackContainer->getCount()), _tracksBatchSize }; diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index 1db66e42..4754abc9 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -121,7 +121,7 @@ Artists::addSome() const auto artistIds {_artistCollector.get(Range {static_cast(_container->getCount()), _batchSize})}; { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; for (const ArtistId artistId : artistIds.results) { diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp index e6b45457..a9bcaada 100644 --- a/src/lms/ui/explore/Filters.cpp +++ b/src/lms/ui/explore/Filters.cpp @@ -56,7 +56,7 @@ Filters::showDialog() ClusterId clusterId {}; { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; ClusterType::pointer clusterType {ClusterType::find(LmsApp->getDbSession(), type)}; if (!clusterType) @@ -82,7 +82,7 @@ Filters::showDialog() // Populate data { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const auto clusterTypesIds {ClusterType::findUsed(LmsApp->getDbSession())}; for (const ClusterTypeId clusterTypeId : clusterTypesIds.results) @@ -109,7 +109,7 @@ Filters::showDialog() valueCombo->clear(); - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; auto clusterType {ClusterType::find(LmsApp->getDbSession(), name)}; for (const Cluster::pointer& cluster : clusterType->getClusters()) diff --git a/src/lms/ui/explore/PlayQueueController.cpp b/src/lms/ui/explore/PlayQueueController.cpp index 74fc6911..8c038c7e 100644 --- a/src/lms/ui/explore/PlayQueueController.cpp +++ b/src/lms/ui/explore/PlayQueueController.cpp @@ -37,7 +37,7 @@ namespace UserInterface std::vector res; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; for (const Database::ArtistId artistId : artistsId) { @@ -66,7 +66,7 @@ namespace UserInterface std::vector res; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; for (const ReleaseId releaseId : releasesId) { @@ -93,7 +93,7 @@ namespace UserInterface using namespace Database; assert(maxTrackCount); - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; Database::Track::FindParameters params; params.setTrackList(trackListId); @@ -154,7 +154,7 @@ namespace UserInterface { Database::ReleaseId releaseId; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Track::pointer track{ Database::Track::find(LmsApp->getDbSession(), trackId) }; if (!track || !track->getRelease()) return; diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index c3ae1e15..b6bc7d6e 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -73,7 +73,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; releases = Release::findIds(LmsApp->getDbSession(), params); } break; @@ -87,7 +87,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; releases = Release::findIds(LmsApp->getDbSession(), params); } break; @@ -101,7 +101,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; releases = Release::findIds(LmsApp->getDbSession(), params); } break; @@ -126,7 +126,7 @@ namespace UserInterface params.setRange(Range{ 0, getMaxCount() }); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; _randomReleases = Release::findIds(LmsApp->getDbSession(), params); } } diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index f9f61291..ea62be99 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -56,7 +56,7 @@ namespace UserInterface { void showReleaseInfoModal(Database::ReleaseId releaseId) { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Release::pointer release{ Database::Release::find(LmsApp->getDbSession(), releaseId) }; if (!release) @@ -174,7 +174,7 @@ namespace UserInterface const auto mbid{ UUID::fromString(wApp->internalPathNextPart("/release/mbid/")) }; if (mbid) { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; if (const Database::Release::pointer release{ Database::Release::find(LmsApp->getDbSession(), *mbid) }) return release->getId(); } @@ -228,7 +228,7 @@ namespace UserInterface auto similarReleasesIds{ Service::get()->getSimilarReleases(*releaseId, 6) }; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Release::pointer release{ Database::Release::find(LmsApp->getDbSession(), *releaseId) }; if (!release) @@ -433,7 +433,7 @@ namespace UserInterface Wt::WPushButton* starBtn{ entry->bindNew("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) }; starBtn->clicked().connect([=] { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; if (isStarred()) { diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index f24517dc..aeddd543 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -122,7 +122,7 @@ namespace UserInterface const auto releaseIds{ _releaseCollector.get(Range {static_cast(_container->getCount()), _batchSize}) }; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; for (const ReleaseId releaseId : releaseIds.results) { diff --git a/src/lms/ui/explore/SearchView.cpp b/src/lms/ui/explore/SearchView.cpp index f04d5f3f..fb600c8b 100644 --- a/src/lms/ui/explore/SearchView.cpp +++ b/src/lms/ui/explore/SearchView.cpp @@ -150,7 +150,7 @@ namespace UserInterface const Range range{ _artists->getCount(), getBatchSize(Mode::Artist) }; const RangeResults artistIds{ _artistCollector.get(range) }; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; for (const ArtistId artistId : artistIds.results) { @@ -168,7 +168,7 @@ namespace UserInterface const Range range{ _releases->getCount(), getBatchSize(Mode::Release) }; const RangeResults releaseIds{ _releaseCollector.get(range) }; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; for (const ReleaseId releaseId : releaseIds.results) { @@ -187,7 +187,7 @@ namespace UserInterface const RangeResults trackIds{ _trackCollector.get(range) }; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; for (const TrackId trackId : trackIds.results) { diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 7f3ef822..e84270f0 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -76,7 +76,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; tracks = Track::findIds(LmsApp->getDbSession(), params); } break; @@ -90,7 +90,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; tracks = Track::findIds(LmsApp->getDbSession(), params); } break; @@ -103,7 +103,7 @@ namespace UserInterface params.setRange(range); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; tracks = Track::findIds(LmsApp->getDbSession(), params); } break; @@ -128,7 +128,7 @@ namespace UserInterface params.setRange(Range{ 0, getMaxCount() }); { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; _randomTracks = Track::findIds(LmsApp->getDbSession(), params); } } diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index ecb11c96..464fef06 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -49,7 +49,7 @@ namespace UserInterface::TrackListHelpers void showTrackInfoModal(Database::TrackId trackId, Filters& filters) { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Track::pointer track{ Track::find(LmsApp->getDbSession(), trackId) }; if (!track) @@ -219,7 +219,7 @@ namespace UserInterface::TrackListHelpers Wt::WPushButton* starBtn{ entry->bindNew("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) }; starBtn->clicked().connect([=] { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; if (isStarred()) { diff --git a/src/lms/ui/explore/TrackListView.cpp b/src/lms/ui/explore/TrackListView.cpp index e891c750..cbadd995 100644 --- a/src/lms/ui/explore/TrackListView.cpp +++ b/src/lms/ui/explore/TrackListView.cpp @@ -81,7 +81,7 @@ namespace UserInterface if (!trackListId) throw TrackListNotFoundException{}; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::TrackList::pointer trackList{ Database::TrackList::find(LmsApp->getDbSession(), *trackListId) }; if (!trackList) @@ -148,7 +148,7 @@ namespace UserInterface delBtn->clicked().connect([=] { { - auto transaction{ LmsApp->getDbSession().createUniqueTransaction() }; + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; Database::TrackList::pointer trackList{ Database::TrackList::find(LmsApp->getDbSession(), *trackListId) }; if (trackList) @@ -180,7 +180,7 @@ namespace UserInterface void TrackList::addSome() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; Database::Track::FindParameters params; params.setClusters(_filters.getClusterIds()); diff --git a/src/lms/ui/explore/TrackListsView.cpp b/src/lms/ui/explore/TrackListsView.cpp index 01cade9e..8dc37e01 100644 --- a/src/lms/ui/explore/TrackListsView.cpp +++ b/src/lms/ui/explore/TrackListsView.cpp @@ -101,7 +101,7 @@ namespace UserInterface const Range range {static_cast(_container->getCount()), _batchSize}; Session& session {LmsApp->getDbSession()}; - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; TrackList::FindParameters params; params.setClusters(_filters.getClusterIds()); diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index d4e21045..19e95808 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -119,7 +119,7 @@ namespace UserInterface void Tracks::addSome() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const auto trackIds{ _trackCollector.get(Range {static_cast(_container->getCount()), _batchSize}) }; diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index 293e7aac..7dbc762f 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -49,7 +49,7 @@ static std::optional getTrackPathFromTrackId(Database::TrackId trackId) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction {LmsApp->getDbSession().createReadTransaction()}; const Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), trackId)}; if (!track) diff --git a/src/lms/ui/resource/AudioTranscodingResource.cpp b/src/lms/ui/resource/AudioTranscodingResource.cpp index 8c4799b3..85878adf 100644 --- a/src/lms/ui/resource/AudioTranscodingResource.cpp +++ b/src/lms/ui/resource/AudioTranscodingResource.cpp @@ -137,7 +137,7 @@ namespace UserInterface std::filesystem::path trackPath; { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Track::pointer track{ Database::Track::find(LmsApp->getDbSession(), *trackId) }; if (!track) diff --git a/src/lms/ui/resource/DownloadResource.cpp b/src/lms/ui/resource/DownloadResource.cpp index 42138fb6..eeadb510 100644 --- a/src/lms/ui/resource/DownloadResource.cpp +++ b/src/lms/ui/resource/DownloadResource.cpp @@ -163,7 +163,7 @@ namespace UserInterface DownloadArtistResource::DownloadArtistResource(Database::ArtistId artistId) : _artistId{ artistId } { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; Database::Artist::pointer artist{ Database::Artist::find(LmsApp->getDbSession(), artistId) }; if (artist) @@ -172,7 +172,7 @@ namespace UserInterface std::unique_ptr DownloadArtistResource::createZipper() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const auto trackResults{ Database::Track::find(LmsApp->getDbSession(), Database::Track::FindParameters {}.setArtist(_artistId).setSortMethod(Database::TrackSortMethod::DateDescAndRelease)) }; return details::createZipper(trackResults.results); @@ -181,7 +181,7 @@ namespace UserInterface DownloadReleaseResource::DownloadReleaseResource(Database::ReleaseId releaseId) : _releaseId{ releaseId } { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; Database::Release::pointer release{ Database::Release::find(LmsApp->getDbSession(), releaseId) }; if (release) @@ -193,7 +193,7 @@ namespace UserInterface { using namespace Database; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; auto tracks{ Track::find(LmsApp->getDbSession(), Track::FindParameters {}.setRelease(_releaseId).setSortMethod(TrackSortMethod::Release)) }; return details::createZipper(tracks.results); @@ -202,7 +202,7 @@ namespace UserInterface DownloadTrackResource::DownloadTrackResource(Database::TrackId trackId) : _trackId{ trackId } { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; Database::Track::pointer track{ Database::Track::find(LmsApp->getDbSession(), trackId) }; if (track) @@ -211,7 +211,7 @@ namespace UserInterface std::unique_ptr DownloadTrackResource::createZipper() { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::Track::pointer track{ Database::Track::find(LmsApp->getDbSession(), _trackId) }; if (!track) @@ -226,7 +226,7 @@ namespace UserInterface DownloadTrackListResource::DownloadTrackListResource(Database::TrackListId trackListId) : _trackListId{ trackListId } { - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const Database::TrackList::pointer trackList{ Database::TrackList::find(LmsApp->getDbSession(), trackListId) }; if (trackList) @@ -236,7 +236,7 @@ namespace UserInterface std::unique_ptr DownloadTrackListResource::createZipper() { using namespace Database; - auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; Track::FindParameters params; params.setTrackList(_trackListId); diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index c90149ed..4d4a50d8 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -42,7 +42,7 @@ dumpTrackCovers(Database::Session& session, Image::ImageSize width) RangeResults trackIds; { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; trackIds = Database::Track::findIds(session, Database::Track::FindParameters {}); } diff --git a/src/tools/db-generator/LmsDbGenerator.cpp b/src/tools/db-generator/LmsDbGenerator.cpp index efd2b2a9..2f4eab71 100644 --- a/src/tools/db-generator/LmsDbGenerator.cpp +++ b/src/tools/db-generator/LmsDbGenerator.cpp @@ -116,7 +116,7 @@ namespace while(remainingCount > 0) { - auto transaction{ context.session.createUniqueTransaction() }; + auto transaction{ context.session.createWriteTransaction() }; std::cout << "Generating album #" << params.releaseCount - remainingCount << " / " << params.releaseCount << std::endl; for (std::size_t i{}; i < params.releaseCountPerBatch && remainingCount-- > 0; ++i) @@ -126,7 +126,7 @@ namespace void prepareContext(const GeneratorParameters& params, GenerationContext& context) { - auto transaction{ context.session.createUniqueTransaction() }; + auto transaction{ context.session.createWriteTransaction() }; // create some random genres/moods { diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index 43659f0d..0c1d24af 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -42,7 +42,7 @@ static void dumpTracksRecommendation(Session session, Recommendation::IRecommend { const RangeResults trackIds{ [&] { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; return Track::findIds(session, Track::FindParameters{}); }() }; @@ -52,7 +52,7 @@ static void dumpTracksRecommendation(Session session, Recommendation::IRecommend auto trackToString = [&](const TrackId trackId) { std::string res; - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; const Track::pointer track{ Track::find(session, trackId) }; res += track->getName(); @@ -76,7 +76,7 @@ static void dumpReleasesRecommendation(Session session, Recommendation::IRecomme { const RangeResults releaseIds{ std::invoke([&] { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; return Release::findIds(session, Release::FindParameters {}); }) }; @@ -85,7 +85,7 @@ static void dumpReleasesRecommendation(Session session, Recommendation::IRecomme { auto releaseToString = [&](ReleaseId releaseId) -> std::string { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; Release::pointer release{ Release::find(session, releaseId) }; return release->getName(); @@ -101,7 +101,7 @@ static void dumpArtistsRecommendation(Session session, Recommendation::IRecommen { const RangeResults artistIds = std::invoke([&]() { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; return Artist::findIds(session, Artist::FindParameters{}); }); @@ -110,7 +110,7 @@ static void dumpArtistsRecommendation(Session session, Recommendation::IRecommen { auto artistToString = [&](ArtistId artistId) { - auto transaction{ session.createSharedTransaction() }; + auto transaction{ session.createReadTransaction() }; Artist::pointer artist{ Artist::find(session, artistId) }; return artist->getName(); diff --git a/src/tools/similarity-parameters/LmsSimilarityParameters.cpp b/src/tools/similarity-parameters/LmsSimilarityParameters.cpp index b9ae643b..9829a513 100644 --- a/src/tools/similarity-parameters/LmsSimilarityParameters.cpp +++ b/src/tools/similarity-parameters/LmsSimilarityParameters.cpp @@ -170,7 +170,7 @@ constructFeaturesCache(Database::Session& session, const FeatureSettingsMap& fea std::transform(std::cbegin(featureSettings), std::cend(featureSettings), std::inserter(names, std::begin(names)), [](const auto& itFeature) { return itFeature.first; }); - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; for (auto trackId : Database::Track::getAllIdsWithFeatures(session)) { @@ -225,7 +225,7 @@ std::string trackToString(Database::Session& session, Database::IdType trackId) { std::string res; - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; Database::Track::pointer track {Database::Track::getById(session, trackId)}; res += track->getName(); @@ -245,7 +245,7 @@ computeTrackScore(Database::Session& session, Database::IdType track1Id, Databas { SimilarityScore score {}; - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; auto track1 {Database::Track::getById(session, track1Id)}; auto track2 {Database::Track::getById(session, track2Id)}; @@ -294,7 +294,7 @@ computeSimilarityScore(Database::Session& session, FeaturesSearcher::TrainSettin const std::vector trackIds = std::invoke([&]() { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; return Database::Track::getAllIdsWithFeatures(session); }); @@ -328,7 +328,7 @@ printBadlyClassifiedTracks(Database::Session& session, FeaturesSearcher::TrainSe const std::vector trackIds = std::invoke([&]() { - auto transaction {session.createSharedTransaction()}; + auto transaction {session.createReadTransaction()}; return Database::Track::getAllIdsWithFeatures(session); }); From b68b3ce0a6cb9a53f8debad6aedd25e1b19af184 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 19 Nov 2023 23:22:34 +0100 Subject: [PATCH 08/18] Scanner: fixed bad total step count --- .../services/scanner/include/services/scanner/ScannerStats.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp index 5801269f..e439f7a0 100644 --- a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp +++ b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp @@ -67,7 +67,7 @@ namespace Scanner ReloadingSimilarityEngine, ComputeClusterStats, }; - static inline constexpr unsigned ScanProgressStepCount{ 6 }; + static inline constexpr unsigned ScanProgressStepCount{ 7 }; // reduced scan stats struct ScanStepStats From b02021ac8fcbfe6c33fb620cf2b7e8eb6623a043 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 20 Nov 2023 09:51:11 +0100 Subject: [PATCH 09/18] Fallback on same album name only if in the same directory. fixes #370 --- src/libs/services/database/impl/Release.cpp | 10 +- .../include/services/database/Release.hpp | 7 +- src/libs/services/database/test/Release.cpp | 34 ++ .../scanner/impl/ScanStepScanFiles.cpp | 415 +++++++++--------- 4 files changed, 247 insertions(+), 219 deletions(-) diff --git a/src/libs/services/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp index 37c87efb..226cdb9f 100644 --- a/src/libs/services/database/impl/Release.cpp +++ b/src/libs/services/database/impl/Release.cpp @@ -196,13 +196,15 @@ namespace Database return session.getDboSession().add(std::unique_ptr {new Release{ name, MBID }}); } - std::vector Release::find(Session& session, const std::string& name) + std::vector Release::find(Session& session, const std::string& name, const std::filesystem::path& releaseDirectory) { - session.checkWriteTransaction(); + session.checkReadTransaction(); auto res{ session.getDboSession() - .find() - .where("name = ?").bind(std::string(name, 0, _maxNameLength)) + .query>("SELECT DISTINCT r from release r") + .join("track t ON t.release_id = r.id") + .where("r.name = ?").bind(std::string(name, 0, _maxNameLength)) + .where("t.file_path LIKE ?").bind(Utils::escapeLikeKeyword(releaseDirectory.string()) + "%") .resultList() }; return std::vector(res.begin(), res.end()); diff --git a/src/libs/services/database/include/services/database/Release.hpp b/src/libs/services/database/include/services/database/Release.hpp index 2000e5e0..ccf191bb 100644 --- a/src/libs/services/database/include/services/database/Release.hpp +++ b/src/libs/services/database/include/services/database/Release.hpp @@ -19,6 +19,7 @@ #pragma once +#include #include #include @@ -61,9 +62,9 @@ namespace Database ArtistId artist; // only releases that involved this user EnumSet trackArtistLinkTypes; // and for these link types EnumSet excludedTrackArtistLinkTypes; // but not for these link types - std::optional primaryType; // if, set, matching this primary type + std::optional primaryType; // if set, matching this primary type EnumSet secondaryTypes; // Matching all this (if any) - + FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; } FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; } FindParameters& setSortMethod(ReleaseSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; } @@ -86,7 +87,7 @@ namespace Database static std::size_t getCount(Session& session); static bool exists(Session& session, ReleaseId id); static pointer find(Session& session, const UUID& MBID); - static std::vector find(Session& session, const std::string& name); + static std::vector find(Session& session, const std::string& name, const std::filesystem::path& releaseDirectory); static pointer find(Session& session, ReleaseId id); static RangeResults find(Session& session, const FindParameters& parameters); static void find(Session& session, const FindParameters& parameters, std::function func); diff --git a/src/libs/services/database/test/Release.cpp b/src/libs/services/database/test/Release.cpp index f7824883..9a8ede14 100644 --- a/src/libs/services/database/test/Release.cpp +++ b/src/libs/services/database/test/Release.cpp @@ -132,6 +132,40 @@ TEST_F(DatabaseFixture, Release_singleTrack) } } +TEST_F(DatabaseFixture, Release_findByNameAndPath) +{ + ScopedRelease release1{ session, "MyRelease" }; + ScopedRelease release2{ session, "MyRelease" }; + ScopedTrack track1{ session, "MyTrack" }; + ScopedTrack track2{ session, "MyTrack" }; + + { + auto transaction{ session.createWriteTransaction() }; + + track1.get().modify()->setRelease(release1.get()); + track1.get().modify()->setPath("/tmp/foo/foo.mp3"); + + track2.get().modify()->setRelease(release2.get()); + track2.get().modify()->setPath("/tmp/bar/bar.mp3"); + } + + { + auto transaction{ session.createReadTransaction() }; + std::cout << "OK HERE" << std::endl; + { + const auto releases{ Release::find(session, "MyRelease", "/tmp/foo") }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release1.getId()); + } + + { + const auto releases{ Release::find(session, "MyRelease", "/tmp/bar") }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release2.getId()); + } + } +} + TEST_F(DatabaseFixture, MulitpleReleaseSearchByName) { ScopedRelease release1{ session, "MyRelease" }; diff --git a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp index 615900fa..f1e902aa 100644 --- a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp @@ -35,257 +35,249 @@ using namespace Database; -namespace +namespace Scanner { - Artist::pointer - createArtist(Session& session, const MetaData::Artist& artistInfo) + namespace { - Artist::pointer artist{ session.create(artistInfo.name) }; - - if (artistInfo.mbid) - artist.modify()->setMBID(*artistInfo.mbid); - if (artistInfo.sortName) - artist.modify()->setSortName(*artistInfo.sortName); - - return artist; - } - - void - updateArtistIfNeeded(Artist::pointer artist, const MetaData::Artist& artistInfo) - { - // Name may have been updated - if (artist->getName() != artistInfo.name) + Artist::pointer createArtist(Session& session, const MetaData::Artist& artistInfo) { - artist.modify()->setName(artistInfo.name); - } + Artist::pointer artist{ session.create(artistInfo.name) }; - // Sortname may have been updated - if (artistInfo.sortName && *artistInfo.sortName != artist->getSortName()) - { - artist.modify()->setSortName(*artistInfo.sortName); - } - } - - std::vector - getOrCreateArtists(Session& session, const std::vector& artistsInfo, bool allowFallbackOnMBIDEntries) - { - std::vector artists; - - for (const MetaData::Artist& artistInfo : artistsInfo) - { - Artist::pointer artist; - - // First try to get by MBID if (artistInfo.mbid) - { - artist = Artist::find(session, *artistInfo.mbid); - if (!artist) - artist = createArtist(session, artistInfo); - else - updateArtistIfNeeded(artist, artistInfo); + artist.modify()->setMBID(*artistInfo.mbid); + if (artistInfo.sortName) + artist.modify()->setSortName(*artistInfo.sortName); - artists.emplace_back(std::move(artist)); - continue; + return artist; + } + + void updateArtistIfNeeded(Artist::pointer artist, const MetaData::Artist& artistInfo) + { + // Name may have been updated + if (artist->getName() != artistInfo.name) + { + artist.modify()->setName(artistInfo.name); } - // Fall back on artist name (collisions may occur) - if (!artistInfo.name.empty()) + // Sortname may have been updated + if (artistInfo.sortName && *artistInfo.sortName != artist->getSortName()) { - for (const Artist::pointer& sameNamedArtist : Artist::find(session, artistInfo.name)) - { - // Do not fallback on artist that is correctly tagged - if (!allowFallbackOnMBIDEntries && sameNamedArtist->getMBID()) - continue; + artist.modify()->setSortName(*artistInfo.sortName); + } + } - artist = sameNamedArtist; - break; + std::vector getOrCreateArtists(Session& session, const std::vector& artistsInfo, bool allowFallbackOnMBIDEntries) + { + std::vector artists; + + for (const MetaData::Artist& artistInfo : artistsInfo) + { + Artist::pointer artist; + + // First try to get by MBID + if (artistInfo.mbid) + { + artist = Artist::find(session, *artistInfo.mbid); + if (!artist) + artist = createArtist(session, artistInfo); + else + updateArtistIfNeeded(artist, artistInfo); + + artists.emplace_back(std::move(artist)); + continue; } - // No Artist found with the same name and without MBID -> creating - if (!artist) - artist = createArtist(session, artistInfo); - else - updateArtistIfNeeded(artist, artistInfo); + // Fall back on artist name (collisions may occur) + if (!artistInfo.name.empty()) + { + for (const Artist::pointer& sameNamedArtist : Artist::find(session, artistInfo.name)) + { + // Do not fallback on artist that is correctly tagged + if (!allowFallbackOnMBIDEntries && sameNamedArtist->getMBID()) + continue; - artists.emplace_back(std::move(artist)); - continue; + artist = sameNamedArtist; + break; + } + + // No Artist found with the same name and without MBID -> creating + if (!artist) + artist = createArtist(session, artistInfo); + else + updateArtistIfNeeded(artist, artistInfo); + + artists.emplace_back(std::move(artist)); + continue; + } } + + return artists; } - return artists; - } - - ReleaseTypePrimary convertReleaseTypePrimary(MetaData::Release::PrimaryType type) - { - switch (type) - { - case MetaData::Release::PrimaryType::Album: return ReleaseTypePrimary::Album; - case MetaData::Release::PrimaryType::Single: return ReleaseTypePrimary::Single; - case MetaData::Release::PrimaryType::EP: return ReleaseTypePrimary::EP; - case MetaData::Release::PrimaryType::Broadcast: return ReleaseTypePrimary::Broadcast; - case MetaData::Release::PrimaryType::Other: return ReleaseTypePrimary::Other; - } - - return ReleaseTypePrimary::Other; - } - - EnumSet convertReleaseTypesSecondary(EnumSet types) - { - EnumSet res; - - for (MetaData::Release::SecondaryType type : types) + ReleaseTypePrimary convertReleaseTypePrimary(MetaData::Release::PrimaryType type) { switch (type) { - case MetaData::Release::SecondaryType::Compilation: - res.insert(ReleaseTypeSecondary::Compilation); - break; - case MetaData::Release::SecondaryType::Soundtrack: - res.insert(ReleaseTypeSecondary::Soundtrack); - break; - case MetaData::Release::SecondaryType::Spokenword: - res.insert(ReleaseTypeSecondary::Spokenword); - break; - case MetaData::Release::SecondaryType::Interview: - res.insert(ReleaseTypeSecondary::Interview); - break; - case MetaData::Release::SecondaryType::Audiobook: - res.insert(ReleaseTypeSecondary::Audiobook); - break; - case MetaData::Release::SecondaryType::AudioDrama: - res.insert(ReleaseTypeSecondary::AudioDrama); - break; - case MetaData::Release::SecondaryType::Live: - res.insert(ReleaseTypeSecondary::Live); - break; - case MetaData::Release::SecondaryType::Remix: - res.insert(ReleaseTypeSecondary::Remix); - break; - case MetaData::Release::SecondaryType::DJMix: - res.insert(ReleaseTypeSecondary::DJMix); - break; - case MetaData::Release::SecondaryType::Mixtape_Street: - res.insert(ReleaseTypeSecondary::Mixtape_Street); - break; - case MetaData::Release::SecondaryType::Demo: - res.insert(ReleaseTypeSecondary::Demo); - break; + case MetaData::Release::PrimaryType::Album: return ReleaseTypePrimary::Album; + case MetaData::Release::PrimaryType::Single: return ReleaseTypePrimary::Single; + case MetaData::Release::PrimaryType::EP: return ReleaseTypePrimary::EP; + case MetaData::Release::PrimaryType::Broadcast: return ReleaseTypePrimary::Broadcast; + case MetaData::Release::PrimaryType::Other: return ReleaseTypePrimary::Other; } + + return ReleaseTypePrimary::Other; } - return res; - } - - void - updateReleaseIfNeeded(Release::pointer release, const MetaData::Release& releaseInfo) - { - if (release->getName() != releaseInfo.name) - release.modify()->setName(releaseInfo.name); - if (release->getTotalDisc() != releaseInfo.mediumCount) - release.modify()->setTotalDisc(releaseInfo.mediumCount); - if (releaseInfo.primaryType) + EnumSet convertReleaseTypesSecondary(EnumSet types) { - const ReleaseTypePrimary primaryType{ convertReleaseTypePrimary(*releaseInfo.primaryType) }; - if (release->getPrimaryType() != primaryType) - release.modify()->setPrimaryType(primaryType); - } - const EnumSet secondaryTypes{ convertReleaseTypesSecondary(releaseInfo.secondaryTypes) }; - if (release->getSecondaryTypes() != secondaryTypes) - release.modify()->setSecondaryTypes(secondaryTypes); - if (release->getArtistDisplayName() != releaseInfo.artistDisplayName) - release.modify()->setArtistDisplayName(releaseInfo.artistDisplayName); - } + EnumSet res; - Release::pointer - getOrCreateRelease(Session& session, const MetaData::Release& releaseInfo) - { - Release::pointer release; - - // First try to get by MBID - if (releaseInfo.mbid) - { - release = Release::find(session, *releaseInfo.mbid); - if (!release) - release = session.create(releaseInfo.name, releaseInfo.mbid); - - updateReleaseIfNeeded(release, releaseInfo); - return release; - } - - // Fall back on release name (collisions may occur) - if (!releaseInfo.name.empty()) - { - for (const Release::pointer& sameNamedRelease : Release::find(session, releaseInfo.name)) + for (MetaData::Release::SecondaryType type : types) { - // do not fallback on properly tagged releases - if (sameNamedRelease->getMBID()) + switch (type) + { + case MetaData::Release::SecondaryType::Compilation: + res.insert(ReleaseTypeSecondary::Compilation); + break; + case MetaData::Release::SecondaryType::Soundtrack: + res.insert(ReleaseTypeSecondary::Soundtrack); + break; + case MetaData::Release::SecondaryType::Spokenword: + res.insert(ReleaseTypeSecondary::Spokenword); + break; + case MetaData::Release::SecondaryType::Interview: + res.insert(ReleaseTypeSecondary::Interview); + break; + case MetaData::Release::SecondaryType::Audiobook: + res.insert(ReleaseTypeSecondary::Audiobook); + break; + case MetaData::Release::SecondaryType::AudioDrama: + res.insert(ReleaseTypeSecondary::AudioDrama); + break; + case MetaData::Release::SecondaryType::Live: + res.insert(ReleaseTypeSecondary::Live); + break; + case MetaData::Release::SecondaryType::Remix: + res.insert(ReleaseTypeSecondary::Remix); + break; + case MetaData::Release::SecondaryType::DJMix: + res.insert(ReleaseTypeSecondary::DJMix); + break; + case MetaData::Release::SecondaryType::Mixtape_Street: + res.insert(ReleaseTypeSecondary::Mixtape_Street); + break; + case MetaData::Release::SecondaryType::Demo: + res.insert(ReleaseTypeSecondary::Demo); + break; + } + } + + return res; + } + + void updateReleaseIfNeeded(Release::pointer release, const MetaData::Release& releaseInfo) + { + if (release->getName() != releaseInfo.name) + release.modify()->setName(releaseInfo.name); + if (release->getTotalDisc() != releaseInfo.mediumCount) + release.modify()->setTotalDisc(releaseInfo.mediumCount); + if (releaseInfo.primaryType) + { + const ReleaseTypePrimary primaryType{ convertReleaseTypePrimary(*releaseInfo.primaryType) }; + if (release->getPrimaryType() != primaryType) + release.modify()->setPrimaryType(primaryType); + } + const EnumSet secondaryTypes{ convertReleaseTypesSecondary(releaseInfo.secondaryTypes) }; + if (release->getSecondaryTypes() != secondaryTypes) + release.modify()->setSecondaryTypes(secondaryTypes); + if (release->getArtistDisplayName() != releaseInfo.artistDisplayName) + release.modify()->setArtistDisplayName(releaseInfo.artistDisplayName); + } + + Release::pointer getOrCreateRelease(Session& session, const MetaData::Release& releaseInfo, const std::filesystem::path& expectedReleaseDirectory) + { + Release::pointer release; + + // First try to get by MBID + if (releaseInfo.mbid) + { + release = Release::find(session, *releaseInfo.mbid); + if (!release) + release = session.create(releaseInfo.name, releaseInfo.mbid); + + updateReleaseIfNeeded(release, releaseInfo); + return release; + } + + // Fall back on release name (collisions may occur), if and only if it is in the current directory + if (!releaseInfo.name.empty()) + { + for (const Release::pointer& sameNamedRelease : Release::find(session, releaseInfo.name, expectedReleaseDirectory)) + { + // do not fallback on properly tagged releases + if (sameNamedRelease->getMBID()) + continue; + + release = sameNamedRelease; + break; + } + + // No release found with the same name and without MBID -> creating + if (!release) + release = session.create(releaseInfo.name); + + updateReleaseIfNeeded(release, releaseInfo); + return release; + } + + return Release::pointer{}; + } + + std::vector getOrCreateClusters(Session& session, const MetaData::Tags& tags) + { + std::vector clusters; + + for (const auto& [tag, values] : tags) + { + auto clusterType = ClusterType::find(session, tag); + if (!clusterType) continue; - release = sameNamedRelease; - break; + for (auto clusterName : values) + { + auto cluster = clusterType->getCluster(clusterName); + if (!cluster) + cluster = session.create(clusterType, clusterName); + + clusters.push_back(cluster); + } } - // No release found with the same name and without MBID -> creating - if (!release) - release = session.create(releaseInfo.name); - - updateReleaseIfNeeded(release, releaseInfo); - return release; + return clusters; } - return Release::pointer{}; - } - - std::vector - getOrCreateClusters(Session& session, const MetaData::Tags& tags) - { - std::vector clusters; - - for (const auto& [tag, values] : tags) + MetaData::ParserReadStyle getParserReadStyle() { - auto clusterType = ClusterType::find(session, tag); - if (!clusterType) - continue; + std::string_view readStyle{ Service::get()->getString("scanner-parser-read-style", "accurate") }; - for (auto clusterName : values) - { - auto cluster = clusterType->getCluster(clusterName); - if (!cluster) - cluster = session.create(clusterType, clusterName); + if (readStyle == "fast") + return MetaData::ParserReadStyle::Fast; + else if (readStyle == "average") + return MetaData::ParserReadStyle::Average; + else if (readStyle == "accurate") + return MetaData::ParserReadStyle::Accurate; - clusters.push_back(cluster); - } + throw LmsException{ "Invalid value for 'scanner-parser-read-style'" }; } + } // namespace - return clusters; - } - - MetaData::ParserReadStyle - getParserReadStyle() - { - std::string_view readStyle{ Service::get()->getString("scanner-parser-read-style", "accurate") }; - - if (readStyle == "fast") - return MetaData::ParserReadStyle::Fast; - else if (readStyle == "average") - return MetaData::ParserReadStyle::Average; - else if (readStyle == "accurate") - return MetaData::ParserReadStyle::Accurate; - - throw LmsException{ "Invalid value for 'scanner-parser-read-style'" }; - } -} // namespace - -namespace Scanner -{ ScanStepScanFiles::ScanStepScanFiles(InitParams& initParams) : ScanStepBase{ initParams } , _metadataParser{ MetaData::createParser(MetaData::ParserType::TagLib, getParserReadStyle()) } // For now, always use TagLib { } - void - ScanStepScanFiles::process(ScanContext& context) + void ScanStepScanFiles::process(ScanContext& context) { _metadataParser->setClusterTypeNames(_settings.clusterTypeNames); @@ -317,8 +309,7 @@ namespace Scanner }, &excludeDirFileName); } - void - ScanStepScanFiles::scanAudioFile(const std::filesystem::path& file, ScanContext& context) + void ScanStepScanFiles::scanAudioFile(const std::filesystem::path& file, ScanContext& context) { ScanStats& stats{ context.stats }; Wt::WDateTime lastWriteTime; @@ -488,7 +479,7 @@ namespace Scanner track.modify()->setScanVersion(_settings.scanVersion); if (trackInfo->medium && trackInfo->medium->release) - track.modify()->setRelease(getOrCreateRelease(dbSession, *trackInfo->medium->release)); + track.modify()->setRelease(getOrCreateRelease(dbSession, *trackInfo->medium->release, file.parent_path())); else track.modify()->setRelease({}); track.modify()->setTotalTrack(trackInfo->medium ? trackInfo->medium->trackCount : std::nullopt); From 6d4808d1922fdafd49725c3a188b51d29a62e8a0 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 20 Nov 2023 11:50:17 +0100 Subject: [PATCH 10/18] Added a missing escape statement that broke some album merges --- src/libs/services/database/impl/Release.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/services/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp index 226cdb9f..66e43291 100644 --- a/src/libs/services/database/impl/Release.cpp +++ b/src/libs/services/database/impl/Release.cpp @@ -204,7 +204,7 @@ namespace Database .query>("SELECT DISTINCT r from release r") .join("track t ON t.release_id = r.id") .where("r.name = ?").bind(std::string(name, 0, _maxNameLength)) - .where("t.file_path LIKE ?").bind(Utils::escapeLikeKeyword(releaseDirectory.string()) + "%") + .where("t.file_path LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind(Utils::escapeLikeKeyword(releaseDirectory.string()) + "%") .resultList() }; return std::vector(res.begin(), res.end()); From b3acdbcb89ebaa31223c2eff356459d9781ce1d6 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 20 Nov 2023 13:29:57 +0100 Subject: [PATCH 11/18] Fixed bad content type in case of uppercase file extension --- src/libs/av/impl/AudioFile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/av/impl/AudioFile.cpp b/src/libs/av/impl/AudioFile.cpp index 33372075..0b62fa14 100644 --- a/src/libs/av/impl/AudioFile.cpp +++ b/src/libs/av/impl/AudioFile.cpp @@ -293,7 +293,7 @@ namespace Av // List should be sync with the demuxers shipped in the lms's docker version // + the _audioFileExtensions in ScanSettings // std::filesystem::path does not seem to have std::hash specialization on freebsd - static const std::map entries + static const std::unordered_map entries { {".mp3", "audio/mpeg"}, {".ogg", "audio/ogg"}, @@ -320,7 +320,7 @@ namespace Av {".mka", "audio/x-matroska"}, }; - auto it{ entries.find(fileExtension) }; + auto it{ entries.find(StringUtils::stringToLower(fileExtension.string())) }; if (it == std::cend(entries)) return ""; From 6f47c32ac29ea41276966c2b4339d3fc0c1ff723 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 20 Nov 2023 22:31:07 +0100 Subject: [PATCH 12/18] Silent warnings --- src/libs/services/database/impl/TransactionChecker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/services/database/impl/TransactionChecker.cpp b/src/libs/services/database/impl/TransactionChecker.cpp index 6299036f..d13f6d43 100644 --- a/src/libs/services/database/impl/TransactionChecker.cpp +++ b/src/libs/services/database/impl/TransactionChecker.cpp @@ -64,7 +64,7 @@ namespace Database popTransaction(TransactionType::Read, session); } - void TransactionChecker::pushTransaction(TransactionType type, Wt::Dbo::Session& session) + void TransactionChecker::pushTransaction([[maybe_unused]] TransactionType type, [[maybe_unused]] Wt::Dbo::Session& session) { #if LMS_CHECK_TRANSACTION_ACCESSES assert(transactionStack.empty() || transactionStack.back().session == &session); @@ -72,7 +72,7 @@ namespace Database #endif // LMS_CHECK_TRANSACTION_ACCESSES } - void TransactionChecker::popTransaction(TransactionType type, Wt::Dbo::Session& session) + void TransactionChecker::popTransaction([[maybe_unused]] TransactionType type, [[maybe_unused]] Wt::Dbo::Session& session) { #if LMS_CHECK_TRANSACTION_ACCESSES @@ -84,7 +84,7 @@ namespace Database } - void TransactionChecker::checkWriteTransaction(Wt::Dbo::Session& session) + void TransactionChecker::checkWriteTransaction([[maybe_unused]] Wt::Dbo::Session& session) { assert(!transactionStack.empty()); assert(transactionStack.back().type == TransactionType::Write); @@ -96,7 +96,7 @@ namespace Database checkWriteTransaction(session.getDboSession()); } - void TransactionChecker::checkReadTransaction(Wt::Dbo::Session& session) + void TransactionChecker::checkReadTransaction([[maybe_unused]] Wt::Dbo::Session& session) { assert(!transactionStack.empty()); assert(transactionStack.back().session == &session); From 9060c0d9257fc86bcb8c4e34d03b98b2a4edce14 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 24 Nov 2023 16:23:31 +0100 Subject: [PATCH 13/18] Simplified logger configuration, it no longer depends on Wt --- conf/lms.conf | 4 +- src/libs/av/impl/AudioFile.cpp | 12 +- src/libs/av/impl/Transcoder.cpp | 10 +- .../av/impl/TranscodingResourceHandler.cpp | 16 +- .../image/impl/graphicsmagick/JPEGImage.cpp | 4 +- .../image/impl/graphicsmagick/RawImage.cpp | 26 +- src/libs/metadata/impl/AvFormatParser.cpp | 2 +- src/libs/metadata/impl/Factory.cpp | 6 +- src/libs/metadata/impl/TagLibParser.cpp | 6 +- .../services/auth/impl/AuthServiceBase.cpp | 4 +- .../services/auth/impl/AuthTokenService.cpp | 6 +- .../services/auth/impl/LoginThrottler.cpp | 6 +- .../auth/impl/PasswordServiceBase.cpp | 4 +- .../http-headers/HttpHeadersEnvService.cpp | 59 +- .../impl/internal/InternalPasswordService.cpp | 6 +- .../auth/impl/pam/PAMPasswordService.cpp | 272 +++--- src/libs/services/cover/impl/CoverService.cpp | 24 +- src/libs/services/database/impl/Artist.cpp | 2 +- src/libs/services/database/impl/Cluster.cpp | 2 +- src/libs/services/database/impl/Db.cpp | 12 +- src/libs/services/database/impl/Migration.cpp | 10 +- src/libs/services/database/impl/Release.cpp | 2 +- .../services/database/impl/ScanSettings.cpp | 190 ++-- src/libs/services/database/impl/Session.cpp | 18 +- src/libs/services/database/impl/Track.cpp | 4 +- .../services/database/impl/TrackFeatures.cpp | 4 +- src/libs/services/database/impl/TrackList.cpp | 2 +- src/libs/services/database/impl/User.cpp | 2 +- .../include/services/database/Cluster.hpp | 4 +- .../services/database/ScanSettings.hpp | 4 +- .../feedback/impl/FeedbackService.cpp | 8 +- .../impl/listenbrainz/FeedbacksParser.cpp | 8 +- .../listenbrainz/FeedbacksSynchronizer.cpp | 44 +- .../impl/listenbrainz/ListenBrainzBackend.cpp | 6 +- .../feedback/impl/listenbrainz/Utils.cpp | 4 +- .../feedback/impl/listenbrainz/Utils.hpp | 4 +- .../impl/PlaylistGeneratorService.cpp | 4 +- .../impl/RecommendationService.cpp | 2 +- .../impl/features/FeaturesEngine.cpp | 749 ++++++++------- .../impl/features/FeaturesEngineCache.cpp | 351 ++++--- .../ConsecutiveArtists.cpp | 2 +- .../ConsecutiveReleases.cpp | 2 +- .../impl/ScanStepCheckDuplicatedDbFiles.cpp | 6 +- .../impl/ScanStepComputeClusterStats.cpp | 4 +- .../scanner/impl/ScanStepDiscoverFiles.cpp | 4 +- .../impl/ScanStepRemoveOrphanDbFiles.cpp | 22 +- .../scanner/impl/ScanStepScanFiles.cpp | 18 +- .../services/scanner/impl/ScannerService.cpp | 40 +- .../scrobbling/impl/ScrobblingService.cpp | 8 +- .../impl/listenbrainz/ListenBrainzBackend.cpp | 8 +- .../impl/listenbrainz/ListensParser.cpp | 140 ++- .../impl/listenbrainz/ListensSynchronizer.cpp | 40 +- .../scrobbling/impl/listenbrainz/Utils.cpp | 54 +- .../scrobbling/impl/listenbrainz/Utils.hpp | 4 +- .../services/scrobbling/test/Scrobbling.cpp | 12 +- src/libs/som/impl/Network.cpp | 2 +- src/libs/subsonic/impl/SubsonicId.cpp | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 14 +- .../subsonic/impl/entrypoints/Browsing.cpp | 6 +- .../impl/entrypoints/MediaRetrieval.cpp | 8 +- src/libs/utils/impl/ArchiveZipper.cpp | 6 +- src/libs/utils/impl/ChildProcess.cpp | 237 +++-- src/libs/utils/impl/ChildProcessManager.cpp | 2 +- src/libs/utils/impl/Config.cpp | 2 +- src/libs/utils/impl/FileResourceHandler.cpp | 33 +- src/libs/utils/impl/IOContextRunner.cpp | 10 +- src/libs/utils/impl/Logger.cpp | 17 +- src/libs/utils/impl/Path.cpp | 218 +++-- src/libs/utils/impl/String.cpp | 16 + src/libs/utils/impl/WtLogger.cpp | 28 +- src/libs/utils/impl/http/SendQueue.cpp | 26 +- .../include/utils/{Logger.hpp => ILogger.hpp} | 21 +- src/libs/utils/include/utils/StreamLogger.hpp | 19 +- src/libs/utils/include/utils/String.hpp | 5 +- src/libs/utils/include/utils/WtLogger.hpp | 18 +- src/libs/utils/test/String.cpp | 231 ++--- src/lms/main.cpp | 273 +++--- src/lms/ui/Auth.cpp | 2 +- src/lms/ui/LmsApplication.cpp | 867 +++++++++--------- src/lms/ui/MediaPlayer.cpp | 32 +- src/lms/ui/ModalManager.cpp | 96 +- src/lms/ui/NotificationContainer.cpp | 100 +- src/lms/ui/PlayQueue.cpp | 4 +- src/lms/ui/SettingsView.cpp | 5 +- src/lms/ui/Utils.cpp | 2 +- src/lms/ui/admin/DatabaseSettingsView.cpp | 10 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 2 +- src/lms/ui/admin/UsersView.cpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 2 +- src/lms/ui/explore/ArtistsView.cpp | 2 +- src/lms/ui/explore/Filters.cpp | 2 +- src/lms/ui/explore/ReleaseView.cpp | 2 +- src/lms/ui/explore/TrackListHelpers.cpp | 2 +- src/lms/ui/explore/TrackListView.cpp | 2 +- src/lms/ui/explore/TracksView.cpp | 2 +- src/lms/ui/resource/AudioFileResource.cpp | 125 ++- .../ui/resource/AudioTranscodingResource.cpp | 18 +- src/lms/ui/resource/CoverResource.cpp | 21 +- src/lms/ui/resource/DownloadResource.cpp | 10 +- src/tools/cover/LmsCover.cpp | 86 +- src/tools/db-generator/LmsDbGenerator.cpp | 6 +- src/tools/metadata/LmsMetadata.cpp | 332 +++---- .../recommendation/LmsRecommendation.cpp | 4 +- 104 files changed, 2603 insertions(+), 2598 deletions(-) rename src/libs/utils/include/utils/{Logger.hpp => ILogger.hpp} (75%) diff --git a/conf/lms.conf b/conf/lms.conf index 36d07f83..2013d7c0 100644 --- a/conf/lms.conf +++ b/conf/lms.conf @@ -10,8 +10,8 @@ ffmpeg-file = "/usr/bin/ffmpeg"; # Log files, empty means stdout log-file = ""; access-log-file = ""; -# Logger configuration, see log-config in https://webtoolkit.eu/wt/doc/reference/html/overview.html#config_general -log-config = "* -debug -info:WebRequest"; +# Minimum severity, can be "debug", "info", "warning", "error" or "fatal" +log-min-severity = "info"; # Output db queries on stdout db-show-queries = false; diff --git a/src/libs/av/impl/AudioFile.cpp b/src/libs/av/impl/AudioFile.cpp index 0b62fa14..e739c57b 100644 --- a/src/libs/av/impl/AudioFile.cpp +++ b/src/libs/av/impl/AudioFile.cpp @@ -31,7 +31,7 @@ extern "C" #include #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/String.hpp" namespace Av @@ -105,14 +105,14 @@ namespace Av int error{ avformat_open_input(&_context, _p.string().c_str(), nullptr, nullptr) }; if (error < 0) { - LMS_LOG(AV, ERROR) << "Cannot open " << _p.string() << ": " << averror_to_string(error); + LMS_LOG(AV, ERROR, "Cannot open " << _p.string() << ": " << averror_to_string(error)); throw AudioFileException{ error }; } error = avformat_find_stream_info(_context, nullptr); if (error < 0) { - LMS_LOG(AV, ERROR) << "Cannot find stream information on " << _p.string() << ": " << averror_to_string(error); + LMS_LOG(AV, ERROR, "Cannot find stream information on " << _p.string() << ": " << averror_to_string(error)); avformat_close_input(&_context); throw AudioFileException{ error }; } @@ -233,7 +233,7 @@ namespace Av if (avstream->codecpar == nullptr) { - LMS_LOG(AV, ERROR) << "Skipping stream " << i << " since no codecpar is set"; + LMS_LOG(AV, ERROR, "Skipping stream " << i << " since no codecpar is set"); continue; } @@ -247,7 +247,7 @@ namespace Av else { picture.mimeType = "application/octet-stream"; - LMS_LOG(AV, ERROR) << "CODEC ID " << avstream->codecpar->codec_id << " not handled in mime type conversion"; + LMS_LOG(AV, ERROR, "CODEC ID " << avstream->codecpar->codec_id << " not handled in mime type conversion"); } const AVPacket& pkt{ avstream->attached_pic }; @@ -271,7 +271,7 @@ namespace Av if (!avstream->codecpar) { - LMS_LOG(AV, ERROR) << "Skipping stream " << streamIndex << " since no codecpar is set"; + LMS_LOG(AV, ERROR, "Skipping stream " << streamIndex << " since no codecpar is set"); return res; } diff --git a/src/libs/av/impl/Transcoder.cpp b/src/libs/av/impl/Transcoder.cpp index c4c76902..074675bc 100644 --- a/src/libs/av/impl/Transcoder.cpp +++ b/src/libs/av/impl/Transcoder.cpp @@ -25,13 +25,13 @@ #include "utils/IChildProcessManager.hpp" #include "utils/IConfig.hpp" #include "utils/Path.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" namespace Av::Transcoding { -#define LOG(sev) LMS_LOG(TRANSCODING, sev) << "[" << _debugId << "] - " +#define LOG(severity, message) LMS_LOG(TRANSCODING, severity, "[" << _debugId << "] - " << message) static std::atomic globalId{}; static std::filesystem::path ffmpegPath; @@ -84,7 +84,7 @@ namespace Av::Transcoding throw Exception{ "File error '" + _inputParameters.trackPath.string() + "': " + e.what() }; } - LOG(INFO) << "Transcoding file '" << _inputParameters.trackPath.string() << "'"; + LOG(INFO, "Transcoding file '" << _inputParameters.trackPath.string() << "'"); std::vector args; @@ -176,9 +176,9 @@ namespace Av::Transcoding args.emplace_back("pipe:1"); - LOG(DEBUG) << "Dumping args (" << args.size() << ")"; + LOG(DEBUG, "Dumping args (" << args.size() << ")"); for (const std::string& arg : args) - LOG(DEBUG) << "Arg = '" << arg << "'"; + LOG(DEBUG, "Arg = '" << arg << "'"); // Caution: stdin must have been closed before try diff --git a/src/libs/av/impl/TranscodingResourceHandler.cpp b/src/libs/av/impl/TranscodingResourceHandler.cpp index f5d2ca0f..4fbbb518 100644 --- a/src/libs/av/impl/TranscodingResourceHandler.cpp +++ b/src/libs/av/impl/TranscodingResourceHandler.cpp @@ -18,7 +18,7 @@ */ #include "TranscodingResourceHandler.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Av::Transcoding { @@ -43,9 +43,9 @@ namespace Av::Transcoding , _transcoder{ inputParameters, outputParameters } { if (_estimatedContentLength) - LMS_LOG(TRANSCODING, DEBUG) << "Estimated content length = " << *_estimatedContentLength; + LMS_LOG(TRANSCODING, DEBUG, "Estimated content length = " << *_estimatedContentLength); else - LMS_LOG(TRANSCODING, DEBUG) << "Not using estimated content length"; + LMS_LOG(TRANSCODING, DEBUG, "Not using estimated content length"); } Wt::Http::ResponseContinuation* TranscodingResourceHandler::processRequest(const Wt::Http::Request& /*request*/, Wt::Http::Response& response) @@ -53,11 +53,11 @@ namespace Av::Transcoding if (_estimatedContentLength) response.setContentLength(*_estimatedContentLength); response.setMimeType(_transcoder.getOutputMimeType()); - LMS_LOG(TRANSCODING, DEBUG) << "Transcoder finished = " << _transcoder.finished() << ", total served bytes = " << _totalServedByteCount << ", mime type = " << _transcoder.getOutputMimeType(); + LMS_LOG(TRANSCODING, DEBUG, "Transcoder finished = " << _transcoder.finished() << ", total served bytes = " << _totalServedByteCount << ", mime type = " << _transcoder.getOutputMimeType()); if (_bytesReadyCount > 0) { - LMS_LOG(TRANSCODING, DEBUG) << "Writing " << _bytesReadyCount << " bytes back to client"; + LMS_LOG(TRANSCODING, DEBUG, "Writing " << _bytesReadyCount << " bytes back to client"); response.out().write(reinterpret_cast(&_buffer[0]), _bytesReadyCount); _totalServedByteCount += _bytesReadyCount; @@ -70,7 +70,7 @@ namespace Av::Transcoding continuation->waitForMoreData(); _transcoder.asyncRead(_buffer.data(), _buffer.size(), [=](std::size_t nbBytesRead) { - LMS_LOG(TRANSCODING, DEBUG) << "Have " << nbBytesRead << " more bytes to send back"; + LMS_LOG(TRANSCODING, DEBUG, "Have " << nbBytesRead << " more bytes to send back"); assert(_bytesReadyCount == 0); _bytesReadyCount = nbBytesRead; @@ -86,7 +86,7 @@ namespace Av::Transcoding { const std::size_t padSize{ *_estimatedContentLength - _totalServedByteCount }; - LMS_LOG(TRANSCODING, DEBUG) << "Adding " << padSize << " padding bytes"; + LMS_LOG(TRANSCODING, DEBUG, "Adding " << padSize << " padding bytes"); for (std::size_t i{}; i < padSize; ++i) response.out().put(0); @@ -94,7 +94,7 @@ namespace Av::Transcoding _totalServedByteCount += padSize; } - LMS_LOG(TRANSCODING, DEBUG) << "Transcoding finished. Total served byte count = " << _totalServedByteCount; + LMS_LOG(TRANSCODING, DEBUG, "Transcoding finished. Total served byte count = " << _totalServedByteCount); } return {}; diff --git a/src/libs/image/impl/graphicsmagick/JPEGImage.cpp b/src/libs/image/impl/graphicsmagick/JPEGImage.cpp index 85b2fac3..f2600f2c 100644 --- a/src/libs/image/impl/graphicsmagick/JPEGImage.cpp +++ b/src/libs/image/impl/graphicsmagick/JPEGImage.cpp @@ -21,7 +21,7 @@ #include "RawImage.hpp" #include "image/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Image::GraphicsMagick { @@ -36,7 +36,7 @@ namespace Image::GraphicsMagick } catch (Magick::Exception& e) { - LMS_LOG(COVER, ERROR) << "Caught Magick exception: " << e.what(); + LMS_LOG(COVER, ERROR, "Caught Magick exception: " << e.what()); throw ImageException {std::string {"Magick read error: "} + e.what()}; } } diff --git a/src/libs/image/impl/graphicsmagick/RawImage.cpp b/src/libs/image/impl/graphicsmagick/RawImage.cpp index fc74ae37..0217c5db 100644 --- a/src/libs/image/impl/graphicsmagick/RawImage.cpp +++ b/src/libs/image/impl/graphicsmagick/RawImage.cpp @@ -23,7 +23,7 @@ #include "JPEGImage.hpp" #include "image/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Image { @@ -43,16 +43,16 @@ namespace Image Magick::InitializeMagick(path.string().c_str()); if (auto nbThreads {MagickLib::GetMagickResourceLimit(MagickLib::ThreadsResource)}; nbThreads != 1) - LMS_LOG(COVER, WARNING) << "Consider setting env var OMP_NUM_THREADS=1 to save resources"; + LMS_LOG(COVER, WARNING, "Consider setting env var OMP_NUM_THREADS=1 to save resources"); if (!MagickLib::SetMagickResourceLimit(MagickLib::ThreadsResource, 1)) - LMS_LOG(COVER, ERROR) << "Cannot set Magick thread resource limit to 1!"; + LMS_LOG(COVER, ERROR, "Cannot set Magick thread resource limit to 1!"); if (!MagickLib::SetMagickResourceLimit(MagickLib::DiskResource, 0)) - LMS_LOG(COVER, ERROR) << "Cannot set Magick disk resource limit to 0!"; + LMS_LOG(COVER, ERROR, "Cannot set Magick disk resource limit to 0!"); - LMS_LOG(COVER, INFO) << "Magick threads resource limit = " << GetMagickResourceLimit(MagickLib::ThreadsResource); - LMS_LOG(COVER, INFO) << "Magick Disk resource limit = " << GetMagickResourceLimit(MagickLib::DiskResource); + LMS_LOG(COVER, INFO, "Magick threads resource limit = " << GetMagickResourceLimit(MagickLib::ThreadsResource)); + LMS_LOG(COVER, INFO, "Magick Disk resource limit = " << GetMagickResourceLimit(MagickLib::DiskResource)); } } @@ -68,16 +68,16 @@ RawImage::RawImage(const std::byte* encodedData, std::size_t encodedDataSize) } catch (Magick::WarningCoder& e) { - LMS_LOG(COVER, WARNING) << "Caught Magick WarningCoder: " << e.what(); + LMS_LOG(COVER, WARNING, "Caught Magick WarningCoder: " << e.what()); } catch (Magick::Warning& e) { - LMS_LOG(COVER, WARNING) << "Caught Magick warning: " << e.what(); + LMS_LOG(COVER, WARNING, "Caught Magick warning: " << e.what()); throw ImageException {std::string {"Magick read warning: "} + e.what()}; } catch (Magick::Exception& e) { - LMS_LOG(COVER, ERROR) << "Caught Magick exception: " << e.what(); + LMS_LOG(COVER, ERROR, "Caught Magick exception: " << e.what()); throw ImageException {std::string {"Magick read error: "} + e.what()}; } } @@ -90,16 +90,16 @@ RawImage::RawImage(const std::filesystem::path& p) } catch (Magick::WarningCoder& e) { - LMS_LOG(COVER, WARNING) << "Caught Magick WarningCoder: " << e.what(); + LMS_LOG(COVER, WARNING, "Caught Magick WarningCoder: " << e.what()); } catch (Magick::Warning& e) { - LMS_LOG(COVER, WARNING) << "Caught Magick warning: " << e.what(); + LMS_LOG(COVER, WARNING, "Caught Magick warning: " << e.what()); throw ImageException {std::string {"Magick read warning: "} + e.what()}; } catch (Magick::Exception& e) { - LMS_LOG(COVER, ERROR) << "Caught Magick exception: " << e.what(); + LMS_LOG(COVER, ERROR, "Caught Magick exception: " << e.what()); throw ImageException {std::string {"Magick read error: "} + e.what()}; } } @@ -113,7 +113,7 @@ RawImage::resize(ImageSize width) } catch (Magick::Exception& e) { - LMS_LOG(COVER, ERROR) << "Caught Magick exception while resizing: " << e.what(); + LMS_LOG(COVER, ERROR, "Caught Magick exception while resizing: " << e.what()); throw ImageException {std::string {"Magick resize error: "} + e.what()}; } } diff --git a/src/libs/metadata/impl/AvFormatParser.cpp b/src/libs/metadata/impl/AvFormatParser.cpp index 63347cc5..2db802cd 100644 --- a/src/libs/metadata/impl/AvFormatParser.cpp +++ b/src/libs/metadata/impl/AvFormatParser.cpp @@ -23,7 +23,7 @@ #include #include "av/IAudioFile.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/String.hpp" #include "Utils.hpp" diff --git a/src/libs/metadata/impl/Factory.cpp b/src/libs/metadata/impl/Factory.cpp index 4341024e..d6d1d725 100644 --- a/src/libs/metadata/impl/Factory.cpp +++ b/src/libs/metadata/impl/Factory.cpp @@ -20,7 +20,7 @@ #include "metadata/IParser.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "AvFormatParser.hpp" #include "TagLibParser.hpp" @@ -34,10 +34,10 @@ namespace MetaData switch (parserType) { case ParserType::TagLib: - LMS_LOG(METADATA, INFO) << "Creating TagLib parser with read style = " << Utils::readStyleToString(parserReadStyle); + LMS_LOG(METADATA, INFO, "Creating TagLib parser with read style = " << Utils::readStyleToString(parserReadStyle)); return std::make_unique(parserReadStyle); case ParserType::AvFormat: - LMS_LOG(METADATA, INFO) << "Creating AvFormat parser"; + LMS_LOG(METADATA, INFO, "Creating AvFormat parser"); return std::make_unique(); } diff --git a/src/libs/metadata/impl/TagLibParser.cpp b/src/libs/metadata/impl/TagLibParser.cpp index 1540142f..cb4fa0c6 100644 --- a/src/libs/metadata/impl/TagLibParser.cpp +++ b/src/libs/metadata/impl/TagLibParser.cpp @@ -37,7 +37,7 @@ #include "utils/IConfig.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" #include "utils/String.hpp" #include "Utils.hpp" @@ -391,7 +391,7 @@ namespace MetaData if (f.isNull()) { - LMS_LOG(METADATA, ERROR) << "File '" << p.string() << "': parsing failed"; + LMS_LOG(METADATA, ERROR, "File '" << p.string() << "': parsing failed"); return std::nullopt; } @@ -404,7 +404,7 @@ namespace MetaData } else { - LMS_LOG(METADATA, INFO) << "File '" << p.string() << "': no audio properties"; + LMS_LOG(METADATA, INFO, "File '" << p.string() << "': no audio properties"); return std::nullopt; } diff --git a/src/libs/services/auth/impl/AuthServiceBase.cpp b/src/libs/services/auth/impl/AuthServiceBase.cpp index 1dc01a72..657b54e8 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.cpp +++ b/src/libs/services/auth/impl/AuthServiceBase.cpp @@ -22,7 +22,7 @@ #include "services/database/Db.hpp" #include "services/database/Session.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Auth { @@ -43,7 +43,7 @@ namespace Auth { const UserType type {User::getCount(session) == 0 ? UserType::ADMIN : UserType::REGULAR}; - LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN); + LMS_LOG(AUTH, DEBUG, "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN)); user = session.create(loginName); user.modify()->setType(type); diff --git a/src/libs/services/auth/impl/AuthTokenService.cpp b/src/libs/services/auth/impl/AuthTokenService.cpp index 95d33343..36bb0ce1 100644 --- a/src/libs/services/auth/impl/AuthTokenService.cpp +++ b/src/libs/services/auth/impl/AuthTokenService.cpp @@ -28,7 +28,7 @@ #include "services/database/Session.hpp" #include "services/database/User.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Auth { @@ -62,7 +62,7 @@ namespace Auth Database::AuthToken::pointer authToken {session.create(secretHash, expiry, user)}; - LMS_LOG(UI, DEBUG) << "Created auth token for user '" << user->getLoginName() << "', expiry = " << expiry.toString(); + LMS_LOG(UI, DEBUG, "Created auth token for user '" << user->getLoginName() << "', expiry = " << expiry.toString()); if (user->getAuthTokensCount() >= 50) Database::AuthToken::removeExpiredTokens(session, Wt::WDateTime::currentDateTime()); @@ -88,7 +88,7 @@ namespace Auth return std::nullopt; } - LMS_LOG(UI, DEBUG) << "Found auth token for user '" << authToken->getUser()->getLoginName() << "'!"; + LMS_LOG(UI, DEBUG, "Found auth token for user '" << authToken->getUser()->getLoginName() << "'!"); AuthTokenService::AuthTokenProcessResult::AuthTokenInfo res {authToken->getUser()->getId(), authToken->getExpiry()}; authToken.remove(); diff --git a/src/libs/services/auth/impl/LoginThrottler.cpp b/src/libs/services/auth/impl/LoginThrottler.cpp index 7f2de19b..5d1fceea 100644 --- a/src/libs/services/auth/impl/LoginThrottler.cpp +++ b/src/libs/services/auth/impl/LoginThrottler.cpp @@ -21,7 +21,7 @@ #include "LoginThrottler.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Random.hpp" namespace Auth { @@ -81,10 +81,10 @@ LoginThrottler::onBadClientAttempt(const boost::asio::ip::address& address) attemptInfo.badConsecutiveAttemptCount += 1; - LMS_LOG(AUTH, DEBUG) << "Registering bad attempt for '" << clientAddress.to_string() << "', consecutive bad attempts count = " << attemptInfo.badConsecutiveAttemptCount; + LMS_LOG(AUTH, DEBUG, "Registering bad attempt for '" << clientAddress.to_string() << "', consecutive bad attempts count = " << attemptInfo.badConsecutiveAttemptCount); if (attemptInfo.badConsecutiveAttemptCount >= _maxBadConsecutiveAttemptCount) { - LMS_LOG(AUTH, DEBUG) << "Throttling '" << clientAddress.to_string() << "'"; + LMS_LOG(AUTH, DEBUG, "Throttling '" << clientAddress.to_string() << "'"); attemptInfo.nextAttempt = now.addMSecs(std::chrono::duration_cast(_throttlingDuration).count()); } else diff --git a/src/libs/services/auth/impl/PasswordServiceBase.cpp b/src/libs/services/auth/impl/PasswordServiceBase.cpp index 79f57af1..bdd75af5 100644 --- a/src/libs/services/auth/impl/PasswordServiceBase.cpp +++ b/src/libs/services/auth/impl/PasswordServiceBase.cpp @@ -31,7 +31,7 @@ #include "services/database/Session.hpp" #include "services/database/User.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Auth { @@ -61,7 +61,7 @@ namespace Auth PasswordServiceBase::CheckResult PasswordServiceBase::checkUserPassword(const boost::asio::ip::address& clientAddress, std::string_view loginName, std::string_view password) { - LMS_LOG(AUTH, DEBUG) << "Checking password for user '" << loginName << "'"; + LMS_LOG(AUTH, DEBUG, "Checking password for user '" << loginName << "'"); // Do not waste too much resource on brute force attacks (optim) { diff --git a/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.cpp b/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.cpp index 951925ec..de975251 100644 --- a/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.cpp +++ b/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.cpp @@ -22,46 +22,41 @@ #include #include "utils/IConfig.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" namespace Auth { + HttpHeadersEnvService::HttpHeadersEnvService(Database::Db& db) + : AuthServiceBase{ db } + , _fieldName{ Service::get()->getString("http-headers-login-field", "X-Forwarded-User") } + { + LMS_LOG(AUTH, INFO, "Using http header field = '" << _fieldName << "'"); + } - HttpHeadersEnvService::HttpHeadersEnvService(Database::Db& db) - : AuthServiceBase {db} - , _fieldName {Service::get()->getString("http-headers-login-field", "X-Forwarded-User")} - { - LMS_LOG(AUTH, INFO) << "Using http header field = '" << _fieldName << "'"; - } + HttpHeadersEnvService::CheckResult HttpHeadersEnvService::processEnv(const Wt::WEnvironment& env) + { + const std::string loginName{ env.headerValue(_fieldName) }; + if (loginName.empty()) + return { CheckResult::State::Denied }; - HttpHeadersEnvService::CheckResult - HttpHeadersEnvService::processEnv(const Wt::WEnvironment& env) - { - const std::string loginName {env.headerValue(_fieldName)}; - if (loginName.empty()) - return {CheckResult::State::Denied}; + LMS_LOG(AUTH, DEBUG, "Extracted login name = '" << loginName << "' from HTTP header"); - LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; + const Database::UserId userId{ getOrCreateUser(loginName) }; + onUserAuthenticated(userId); + return { CheckResult::State::Granted, userId }; + } - const Database::UserId userId {getOrCreateUser(loginName)}; - onUserAuthenticated(userId); - return {CheckResult::State::Granted, userId}; - } + HttpHeadersEnvService::CheckResult HttpHeadersEnvService::processRequest(const Wt::Http::Request& request) + { + const std::string loginName{ request.headerValue(_fieldName) }; + if (loginName.empty()) + return { CheckResult::State::Denied }; - HttpHeadersEnvService::CheckResult - HttpHeadersEnvService::processRequest(const Wt::Http::Request& request) - { - const std::string loginName {request.headerValue(_fieldName)}; - if (loginName.empty()) - return {CheckResult::State::Denied}; - - LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; - - const Database::UserId userId {getOrCreateUser(loginName)}; - onUserAuthenticated(userId); - return {CheckResult::State::Granted, userId}; - } + LMS_LOG(AUTH, DEBUG, "Extracted login name = '" << loginName << "' from HTTP header"); + const Database::UserId userId{ getOrCreateUser(loginName) }; + onUserAuthenticated(userId); + return { CheckResult::State::Granted, userId }; + } } // namespace Auth - diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp index 9d078b02..5387b29d 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp @@ -25,7 +25,7 @@ #include "services/database/Session.hpp" #include "services/database/User.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Auth { @@ -44,7 +44,7 @@ namespace Auth bool InternalPasswordService::checkUserPassword(std::string_view loginName, std::string_view password) { - LMS_LOG(AUTH, DEBUG) << "Checking internal password for user '" << loginName << "'"; + LMS_LOG(AUTH, DEBUG, "Checking internal password for user '" << loginName << "'"); Database::User::PasswordHash passwordHash; { @@ -54,7 +54,7 @@ namespace Auth const Database::User::pointer user {Database::User::find(session, loginName)}; if (!user) { - LMS_LOG(AUTH, DEBUG) << "hashing random stuff"; + LMS_LOG(AUTH, DEBUG, "hashing random stuff"); // hash random stuff here to waste some time hashRandomPassword(); return false; diff --git a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp index 80c92644..991e8545 100644 --- a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp @@ -28,175 +28,173 @@ #include "services/auth/Types.hpp" #include "services/database/Session.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Auth { - class PAMError - { - public: - PAMError(std::string_view msg, pam_handle_t *pamh, int err) - { - _errorMsg = std::string {msg} + ": " + pam_strerror(pamh, err); - } + namespace + { + class PAMError + { + public: + PAMError(std::string_view msg, pam_handle_t* pamh, int err) + { + _errorMsg = std::string{ msg } + ": " + pam_strerror(pamh, err); + } - std::string_view message() const { return _errorMsg; } + std::string_view message() const { return _errorMsg; } - private: - std::string _errorMsg; - }; + private: + std::string _errorMsg; + }; - class PAMContext - { - public: - PAMContext(std::string_view loginName) - { - int err {pam_start("lms", std::string {loginName}.c_str(), &_conv, &_pamh)}; - if (err != PAM_SUCCESS) - throw PAMError {"start failed", _pamh, err}; - } + class PAMContext + { + public: + PAMContext(std::string_view loginName) + { + int err{ pam_start("lms", std::string {loginName}.c_str(), &_conv, &_pamh) }; + if (err != PAM_SUCCESS) + throw PAMError{ "start failed", _pamh, err }; + } - ~PAMContext() - { - int err {pam_end(_pamh, 0)}; - if (err != PAM_SUCCESS) - LMS_LOG(AUTH, ERROR) << "end failed: " << pam_strerror(_pamh, err); - } + ~PAMContext() + { + int err{ pam_end(_pamh, 0) }; + if (err != PAM_SUCCESS) + LMS_LOG(AUTH, ERROR, "end failed: " << pam_strerror(_pamh, err)); + } - void authenticate(std::string_view password) - { - AuthenticateConvContext authContext {password}; - ScopedConvContextSetter scopedContext {*this, authContext}; + void authenticate(std::string_view password) + { + AuthenticateConvContext authContext{ password }; + ScopedConvContextSetter scopedContext{ *this, authContext }; - int err {pam_authenticate(_pamh, 0)}; - if (err != PAM_SUCCESS) - throw PAMError {"authenticate failed", _pamh, err}; - } + int err{ pam_authenticate(_pamh, 0) }; + if (err != PAM_SUCCESS) + throw PAMError{ "authenticate failed", _pamh, err }; + } - void validateAccount() - { - int err {pam_acct_mgmt(_pamh, PAM_SILENT)}; - if (err != PAM_SUCCESS) - throw PAMError {"acct_mgmt failed", _pamh, err}; - } + void validateAccount() + { + int err{ pam_acct_mgmt(_pamh, PAM_SILENT) }; + if (err != PAM_SUCCESS) + throw PAMError{ "acct_mgmt failed", _pamh, err }; + } - private: - class ConvContext - { - public: - virtual ~ConvContext() = default; - }; + private: + class ConvContext + { + public: + virtual ~ConvContext() = default; + }; - class AuthenticateConvContext final : public ConvContext - { - public: - AuthenticateConvContext(std::string_view password) : _password {password} {} + class AuthenticateConvContext final : public ConvContext + { + public: + AuthenticateConvContext(std::string_view password) : _password{ password } {} - std::string_view getPassword() const { return _password; } + std::string_view getPassword() const { return _password; } - private: - std::string_view _password; - }; + private: + std::string_view _password; + }; - class ScopedConvContextSetter - { - public: - ScopedConvContextSetter(PAMContext& pamContext, ConvContext& convContext) - : _pamContext {pamContext} - { - _pamContext._convContext = &convContext; - } + class ScopedConvContextSetter + { + public: + ScopedConvContextSetter(PAMContext& pamContext, ConvContext& convContext) + : _pamContext{ pamContext } + { + _pamContext._convContext = &convContext; + } - ~ScopedConvContextSetter() - { - _pamContext._convContext = nullptr; - } + ~ScopedConvContextSetter() + { + _pamContext._convContext = nullptr; + } - ScopedConvContextSetter(const ScopedConvContextSetter&) = delete; - ScopedConvContextSetter(ScopedConvContextSetter&&) = delete; - ScopedConvContextSetter& operator=(const ScopedConvContextSetter&) = delete; - ScopedConvContextSetter& operator=(ScopedConvContextSetter&&) = delete; + ScopedConvContextSetter(const ScopedConvContextSetter&) = delete; + ScopedConvContextSetter(ScopedConvContextSetter&&) = delete; + ScopedConvContextSetter& operator=(const ScopedConvContextSetter&) = delete; + ScopedConvContextSetter& operator=(ScopedConvContextSetter&&) = delete; - private: - PAMContext& _pamContext; - }; + private: + PAMContext& _pamContext; + }; + static int conv(int msgCount, const pam_message** msgs, pam_response** resps, void* userData) + { + if (msgCount < 1) + return PAM_CONV_ERR; + if (!resps || !msgs || !userData) + return PAM_CONV_ERR; - static int conv(int msgCount, const pam_message** msgs, pam_response** resps, void* userData) - { - if (msgCount < 1) - return PAM_CONV_ERR; - if (!resps || !msgs || !userData) - return PAM_CONV_ERR; + PAMContext& context{ *static_cast(userData) }; - PAMContext& context {*static_cast(userData)}; + AuthenticateConvContext* authenticateContext = dynamic_cast(context._convContext); + if (!authenticateContext) + { + LMS_LOG(AUTH, ERROR, "Unexpected conv!"); + return PAM_CONV_ERR; + } - AuthenticateConvContext* authenticateContext = dynamic_cast(context._convContext); - if (!authenticateContext) - { - LMS_LOG(AUTH, ERROR) << "Unexpected conv!"; - return PAM_CONV_ERR; - } + // Only expect a PAM_PROMPT_ECHO_OFF msg + if (msgCount != 1 || msgs[0]->msg_style != PAM_PROMPT_ECHO_OFF) + { + LMS_LOG(AUTH, ERROR, "Unexpected conv message. Count = " << msgCount); + return PAM_CONV_ERR; + } - // Only expect a PAM_PROMPT_ECHO_OFF msg - if (msgCount != 1 || msgs[0]->msg_style != PAM_PROMPT_ECHO_OFF) - { - LMS_LOG(AUTH, ERROR) << "Unexpected conv message. Count = " << msgCount; - return PAM_CONV_ERR; - } + pam_response* response{ static_cast(malloc(sizeof(pam_response))) }; + if (!response) + return PAM_CONV_ERR; - pam_response* response {static_cast(malloc(sizeof(pam_response)))}; - if (!response) - return PAM_CONV_ERR; + response->resp = strdup(std::string{ authenticateContext->getPassword() }.c_str()); - response->resp = strdup(std::string {authenticateContext->getPassword()}.c_str()); + *resps = response; + return PAM_SUCCESS; + } - *resps = response; - return PAM_SUCCESS; - } + ConvContext* _convContext{}; + pam_conv _conv{ &PAMContext::conv, this }; + pam_handle_t* _pamh{}; + }; + } - ConvContext* _convContext {}; - pam_conv _conv {&PAMContext::conv, this}; - pam_handle_t *_pamh {}; - }; + bool PAMPasswordService::checkUserPassword(std::string_view loginName, std::string_view password) + { + try + { + LMS_LOG(AUTH, DEBUG, "Checking PAM password for user '" << loginName << "'"); + PAMContext pamContext{ loginName }; - bool - PAMPasswordService::checkUserPassword(std::string_view loginName, std::string_view password) - { - try - { - LMS_LOG(AUTH, DEBUG) << "Checking PAM password for user '" << loginName << "'"; - PAMContext pamContext {loginName}; + pamContext.authenticate(password); + pamContext.validateAccount(); - pamContext.authenticate(password); - pamContext.validateAccount(); + return true; + } + catch (const PAMError& error) + { + LMS_LOG(AUTH, ERROR, "PAM error: " << error.message()); + return false; + } + } - return true; - } - catch (const PAMError& error) - { - LMS_LOG(AUTH, ERROR) << "PAM error: " << error.message(); - return false; - } - } + bool PAMPasswordService::canSetPasswords() const + { + return false; + } - bool - PAMPasswordService::canSetPasswords() const - { - return false; - } + IPasswordService::PasswordAcceptabilityResult PAMPasswordService::checkPasswordAcceptability(std::string_view, const PasswordValidationContext&) const + { + throw NotImplementedException{}; + } - IPasswordService::PasswordAcceptabilityResult - PAMPasswordService::checkPasswordAcceptability(std::string_view, const PasswordValidationContext&) const - { - throw NotImplementedException {}; - } - - void - PAMPasswordService::setPassword(Database::UserId, std::string_view) - { - throw NotImplementedException {}; - } + void PAMPasswordService::setPassword(Database::UserId, std::string_view) + { + throw NotImplementedException{}; + } } // namespace Auth diff --git a/src/libs/services/cover/impl/CoverService.cpp b/src/libs/services/cover/impl/CoverService.cpp index 30708f6b..0560c66b 100644 --- a/src/libs/services/cover/impl/CoverService.cpp +++ b/src/libs/services/cover/impl/CoverService.cpp @@ -29,7 +29,7 @@ #include "image/Exception.hpp" #include "image/IRawImage.hpp" #include "utils/IConfig.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Random.hpp" #include "utils/String.hpp" #include "utils/Utils.hpp" @@ -110,10 +110,10 @@ namespace Cover { setJpegQuality(Service::get()->getULong("cover-jpeg-quality", 75)); - LMS_LOG(COVER, INFO) << "Default cover path = '" << _defaultCoverPath.string() << "'"; - LMS_LOG(COVER, INFO) << "Max cache size = " << _maxCacheSize; - LMS_LOG(COVER, INFO) << "Max file size = " << _maxFileSize; - LMS_LOG(COVER, INFO) << "Preferred file names: " << StringUtils::joinStrings(_preferredFileNames, ","); + LMS_LOG(COVER, INFO, "Default cover path = '" << _defaultCoverPath.string() << "'"); + LMS_LOG(COVER, INFO, "Max cache size = " << _maxCacheSize); + LMS_LOG(COVER, INFO, "Max file size = " << _maxFileSize); + LMS_LOG(COVER, INFO, "Preferred file names: " << StringUtils::joinStrings(_preferredFileNames, ",")); #if LMS_SUPPORT_IMAGE_GM GraphicsMagick::init(execPath); @@ -148,7 +148,7 @@ namespace Cover } catch (const Image::ImageException& e) { - LMS_LOG(COVER, ERROR) << "Cannot read embedded cover: " << e.what(); + LMS_LOG(COVER, ERROR, "Cannot read embedded cover: " << e.what()); } }); @@ -167,7 +167,7 @@ namespace Cover } catch (const ImageException& e) { - LMS_LOG(COVER, ERROR) << "Cannot read cover in file '" << p.string() << "': " << e.what(); + LMS_LOG(COVER, ERROR, "Cannot read cover in file '" << p.string() << "': " << e.what()); } return image; @@ -190,7 +190,7 @@ namespace Cover std::shared_ptr image{ getFromCoverFile(_defaultCoverPath, width) }; _defaultCoverCache[width] = image; - LMS_LOG(COVER, DEBUG) << "Default cache entries = " << _defaultCoverCache.size(); + LMS_LOG(COVER, DEBUG, "Default cache entries = " << _defaultCoverCache.size()); return image; } @@ -269,7 +269,7 @@ namespace Cover if (std::filesystem::file_size(filePath, ec) > _maxFileSize && !ec) { - LMS_LOG(COVER, INFO) << "Cover file '" << filePath.string() << " is too big (" << std::filesystem::file_size(filePath, ec) << "), limit is " << _maxFileSize; + LMS_LOG(COVER, INFO, "Cover file '" << filePath.string() << " is too big (" << std::filesystem::file_size(filePath, ec) << "), limit is " << _maxFileSize); return false; } @@ -306,7 +306,7 @@ namespace Cover } catch (Av::Exception& e) { - LMS_LOG(COVER, ERROR) << "Cannot get covers from track " << p.string() << ": " << e.what(); + LMS_LOG(COVER, ERROR, "Cannot get covers from track " << p.string() << ": " << e.what()); } return image; @@ -404,7 +404,7 @@ namespace Cover { std::unique_lock lock{ _cacheMutex }; - LMS_LOG(COVER, DEBUG) << "Cache stats: hits = " << _cacheHits << ", misses = " << _cacheMisses << ", nb entries = " << _cache.size() << ", size = " << _cacheSize; + LMS_LOG(COVER, DEBUG, "Cache stats: hits = " << _cacheHits << ", misses = " << _cacheMisses << ", nb entries = " << _cache.size() << ", size = " << _cacheSize); _cacheHits = 0; _cacheMisses = 0; _cacheSize = 0; @@ -415,7 +415,7 @@ namespace Cover { _jpegQuality = Utils::clamp(quality, 1, 100); - LMS_LOG(COVER, INFO) << "JPEG export quality = " << _jpegQuality; + LMS_LOG(COVER, INFO, "JPEG export quality = " << _jpegQuality); } void CoverService::saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr image) diff --git a/src/libs/services/database/impl/Artist.cpp b/src/libs/services/database/impl/Artist.cpp index 393c2736..f8f233de 100644 --- a/src/libs/services/database/impl/Artist.cpp +++ b/src/libs/services/database/impl/Artist.cpp @@ -25,7 +25,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "SqlQuery.hpp" #include "Utils.hpp" #include "EnumSetTraits.hpp" diff --git a/src/libs/services/database/impl/Cluster.cpp b/src/libs/services/database/impl/Cluster.cpp index 9464eaae..4833d8c5 100644 --- a/src/libs/services/database/impl/Cluster.cpp +++ b/src/libs/services/database/impl/Cluster.cpp @@ -157,7 +157,7 @@ namespace Database { } - ClusterType::pointer ClusterType::create(Session& session, const std::string& name) + ClusterType::pointer ClusterType::create(Session& session, std::string_view name) { return session.getDboSession().add(std::unique_ptr {new ClusterType{ name }}); } diff --git a/src/libs/services/database/impl/Db.cpp b/src/libs/services/database/impl/Db.cpp index 2f67b77a..88a83c3f 100644 --- a/src/libs/services/database/impl/Db.cpp +++ b/src/libs/services/database/impl/Db.cpp @@ -26,7 +26,7 @@ #include "services/database/User.hpp" #include "utils/IConfig.hpp" #include "utils/Service.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Database { @@ -65,18 +65,18 @@ namespace Database void prepare() { - LMS_LOG(DB, DEBUG) << "Setting per-connection settings..."; + LMS_LOG(DB, DEBUG, "Setting per-connection settings..."); executeSql("pragma journal_mode=WAL"); executeSql("pragma synchronous=normal"); executeSql("pragma analysis_limit=2000"); // to help make analyze command faster, 1000 does not seem to be enough to speed up all queries - LMS_LOG(DB, DEBUG) << "Setting per-connection settings done!"; + LMS_LOG(DB, DEBUG, "Setting per-connection settings done!"); } void optimize() { - LMS_LOG(DB, DEBUG) << "connection close: Running pragma optimize..."; + LMS_LOG(DB, DEBUG, "connection close: Running pragma optimize..."); executeSql("pragma optimize"); - LMS_LOG(DB, DEBUG) << "connection close: pragma optimize complete"; + LMS_LOG(DB, DEBUG, "connection close: pragma optimize complete"); } std::filesystem::path _dbPath; @@ -86,7 +86,7 @@ namespace Database // Session living class handling the database and the login Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount) { - LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string(); + LMS_LOG(DB, INFO, "Creating connection pool on file " << dbPath.string()); auto connection{ std::make_unique(dbPath.string()) }; if (IConfig * config{ Service::get() })// may not be here on testU diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index fbf5d577..1da6bae2 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -26,7 +26,7 @@ #include "services/database/Session.hpp" #include "services/database/User.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Database { @@ -282,11 +282,11 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( try { version = VersionInfo::getOrCreate(session)->getVersion(); - LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION; + LMS_LOG(DB, INFO, "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION); } catch (std::exception& e) { - LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what(); + LMS_LOG(DB, ERROR, "Cannot get database version info: " << e.what()); throw LmsException{ outdatedMsg }; } @@ -298,7 +298,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( while (version < LMS_DATABASE_VERSION) { - LMS_LOG(DB, INFO) << "Migrating database from version " << version << " to " << version + 1 << "..."; + LMS_LOG(DB, INFO, "Migrating database from version " << version << " to " << version + 1 << "..."); auto itMigrationFunc{ migrationFunctions.find(version) }; assert(itMigrationFunc != std::cend(migrationFunctions)); @@ -306,7 +306,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( VersionInfo::get(session).modify()->setVersion(++version); - LMS_LOG(DB, INFO) << "Migration complete to version " << version; + LMS_LOG(DB, INFO, "Migration complete to version " << version); } } } diff --git a/src/libs/services/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp index 66e43291..d5f397b8 100644 --- a/src/libs/services/database/impl/Release.cpp +++ b/src/libs/services/database/impl/Release.cpp @@ -26,7 +26,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "SqlQuery.hpp" #include "EnumSetTraits.hpp" #include "IdTypeTraits.hpp" diff --git a/src/libs/services/database/impl/ScanSettings.cpp b/src/libs/services/database/impl/ScanSettings.cpp index e89757cc..ad0563c9 100644 --- a/src/libs/services/database/impl/ScanSettings.cpp +++ b/src/libs/services/database/impl/ScanSettings.cpp @@ -22,131 +22,123 @@ #include #include "utils/Path.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/String.hpp" #include "services/database/Cluster.hpp" #include "services/database/Session.hpp" -namespace { - -const std::set defaultClusterTypeNames = +namespace Database { - "GENRE", - "ALBUMGROUPING", - "MOOD", - "ALBUMMOOD", -}; + namespace + { -} + const std::set defaultClusterTypeNames = + { + "GENRE", + "ALBUMGROUPING", + "MOOD", + "ALBUMMOOD", + }; -namespace Database { + } + void ScanSettings::init(Session& session) + { + session.checkWriteTransaction(); -void -ScanSettings::init(Session& session) -{ - session.checkWriteTransaction(); + pointer settings{ get(session) }; + if (settings) + return; - pointer settings {get(session)}; - if (settings) - return; + settings = session.getDboSession().add(std::make_unique()); + settings.modify()->setClusterTypes(session, defaultClusterTypeNames); + } - settings = session.getDboSession().add(std::make_unique()); - settings.modify()->setClusterTypes(session, defaultClusterTypeNames ); -} + ScanSettings::pointer ScanSettings::get(Session& session) + { + session.checkReadTransaction(); -ScanSettings::pointer -ScanSettings::get(Session& session) -{ - session.checkReadTransaction(); + return session.getDboSession().find().resultValue(); + } - return session.getDboSession().find().resultValue(); -} + std::vector ScanSettings::getAudioFileExtensions() const + { + const auto extensions{ StringUtils::splitString(_audioFileExtensions, " ") }; -std::vector -ScanSettings::getAudioFileExtensions() const -{ - const auto extensions {StringUtils::splitString(_audioFileExtensions, " ")}; + std::vector res(std::cbegin(extensions), std::cend(extensions)); + std::sort(std::begin(res), std::end(res)); + res.erase(std::unique(std::begin(res), std::end(res)), std::end(res)); - std::vector res (std::cbegin(extensions), std::cend(extensions)); - std::sort(std::begin(res), std::end(res)); - res.erase(std::unique( std::begin(res), std::end(res)), std::end(res)); + return res; + } - return res; -} + void ScanSettings::addAudioFileExtension(const std::filesystem::path& ext) + { + _audioFileExtensions += " " + ext.string(); + } -void -ScanSettings::addAudioFileExtension(const std::filesystem::path& ext) -{ - _audioFileExtensions += " " + ext.string(); -} + std::vector ScanSettings::getClusterTypes() const + { + return std::vector(std::cbegin(_clusterTypes), std::cend(_clusterTypes)); + } -std::vector -ScanSettings::getClusterTypes() const -{ - return std::vector(std::cbegin(_clusterTypes), std::cend(_clusterTypes)); -} + void ScanSettings::setMediaDirectory(const std::filesystem::path& p) + { + _mediaDirectory = StringUtils::stringTrimEnd(p.string(), "/\\"); + } -void -ScanSettings::setMediaDirectory(const std::filesystem::path& p) -{ - _mediaDirectory = StringUtils::stringTrimEnd(p.string(), "/\\"); -} + template + std::set getNames(It begin, It end) + { + std::set names; + std::transform(begin, end, std::inserter(names, std::cbegin(names)), + [](const ClusterType::pointer& clusterType) + { + return clusterType->getName(); + }); -template -std::set getNames(It begin, It end) -{ - std::set names; - std::transform(begin, end, std::inserter(names, std::cbegin(names)), - [](const ClusterType::pointer& clusterType) - { - return clusterType->getName(); - }); + return names; + } - return names; -} + void ScanSettings::setClusterTypes(Session& session, const std::set& clusterTypeNames) + { + session.checkWriteTransaction(); -void -ScanSettings::setClusterTypes(Session& session, const std::set& clusterTypeNames) -{ - session.checkWriteTransaction(); + bool needRescan{}; - bool needRescan {}; + // Create any missing cluster type + for (const std::string_view clusterTypeName : clusterTypeNames) + { + ClusterType::pointer clusterType{ ClusterType::find(session, clusterTypeName) }; + if (!clusterType) + { + LMS_LOG(DB, INFO, "Creating cluster type " << clusterTypeName); + clusterType = session.create(clusterTypeName); + _clusterTypes.insert(getDboPtr(clusterType)); - // Create any missing cluster type - for (const std::string& clusterTypeName : clusterTypeNames) - { - ClusterType::pointer clusterType {ClusterType::find(session, clusterTypeName)}; - if (!clusterType) - { - LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName; - clusterType = session.create(clusterTypeName); - _clusterTypes.insert(getDboPtr(clusterType)); + needRescan = true; + } + } - needRescan = true; - } - } + // Delete no longer existing cluster types + for (Wt::Dbo::ptr clusterType : _clusterTypes) + { + if (std::none_of(clusterTypeNames.begin(), clusterTypeNames.end(), + [clusterType](std::string_view name) { return name == clusterType->getName(); })) + { + LMS_LOG(DB, INFO, "Deleting cluster type " << clusterType->getName()); + clusterType.remove(); + } + } - // Delete no longer existing cluster types - for (Wt::Dbo::ptr clusterType : _clusterTypes) - { - if (std::none_of(clusterTypeNames.begin(), clusterTypeNames.end(), - [clusterType](const std::string& name) { return name == clusterType->getName(); })) - { - LMS_LOG(DB, INFO) << "Deleting cluster type " << clusterType->getName(); - clusterType.remove(); - } - } + if (needRescan) + _scanVersion += 1; + } - if (needRescan) - _scanVersion += 1; -} - -void -ScanSettings::incScanVersion() -{ - _scanVersion += 1; -} + void + ScanSettings::incScanVersion() + { + _scanVersion += 1; + } } // namespace Database - diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 126c0c19..4386d0e8 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -22,7 +22,7 @@ #include #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "services/database/Artist.hpp" #include "services/database/AuthToken.hpp" @@ -107,20 +107,20 @@ namespace Database void Session::prepareTables() { - LMS_LOG(DB, INFO) << "Preparing tables..."; + LMS_LOG(DB, INFO, "Preparing tables..."); // Initial creation case try { _session.createTables(); - LMS_LOG(DB, INFO) << "Tables created"; + LMS_LOG(DB, INFO, "Tables created"); } catch (Wt::Dbo::Exception& e) { - LMS_LOG(DB, DEBUG) << "Cannot create tables: " << e.what(); + LMS_LOG(DB, DEBUG, "Cannot create tables: " << e.what()); if (std::string_view{ e.what() }.find("already exists") == std::string_view::npos) { - LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what(); + LMS_LOG(DB, ERROR, "Cannot create tables: " << e.what()); throw e; } } @@ -182,22 +182,22 @@ namespace Database void Session::analyze() { - LMS_LOG(DB, INFO) << "Analyzing database..."; + LMS_LOG(DB, INFO, "Analyzing database..."); { auto transaction{ createWriteTransaction() }; _session.execute("ANALYZE"); } - LMS_LOG(DB, INFO) << "Database Analyze complete"; + LMS_LOG(DB, INFO, "Database Analyze complete"); } void Session::optimize() { - LMS_LOG(DB, INFO) << "Optimizing database..."; + LMS_LOG(DB, INFO, "Optimizing database..."); { auto transaction{ createWriteTransaction() }; _session.execute("PRAGMA optimize"); } - LMS_LOG(DB, INFO) << "Database optimizing complete"; + LMS_LOG(DB, INFO, "Database optimizing complete"); } } // namespace Database diff --git a/src/libs/services/database/impl/Track.cpp b/src/libs/services/database/impl/Track.cpp index 58be0924..71229eee 100644 --- a/src/libs/services/database/impl/Track.cpp +++ b/src/libs/services/database/impl/Track.cpp @@ -28,7 +28,7 @@ #include "services/database/TrackFeatures.hpp" #include "services/database/Session.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "IdTypeTraits.hpp" #include "SqlQuery.hpp" @@ -536,7 +536,7 @@ namespace Database for (auto artist : track->getArtists({ TrackArtistLinkType::Artist })) os << " - " << artist->getName(); for (auto cluster : track->getClusters()) - os << " {" + cluster->getType()->getName() << "-" << cluster->getName() << "}"; + os << " {" << cluster->getType()->getName() << "-" << cluster->getName() << "}"; } else { diff --git a/src/libs/services/database/impl/TrackFeatures.cpp b/src/libs/services/database/impl/TrackFeatures.cpp index d7086c51..4b196497 100644 --- a/src/libs/services/database/impl/TrackFeatures.cpp +++ b/src/libs/services/database/impl/TrackFeatures.cpp @@ -24,7 +24,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "IdTypeTraits.hpp" #include "Utils.hpp" @@ -111,7 +111,7 @@ namespace Database { } catch (boost::property_tree::ptree_error& error) { - LMS_LOG(DB, ERROR) << "Track " << _track.id() << ": ptree exception: " << error.what(); + LMS_LOG(DB, ERROR, "Track " << _track.id() << ": ptree exception: " << error.what()); res.clear(); } diff --git a/src/libs/services/database/impl/TrackList.cpp b/src/libs/services/database/impl/TrackList.cpp index 84286788..1f571c03 100644 --- a/src/libs/services/database/impl/TrackList.cpp +++ b/src/libs/services/database/impl/TrackList.cpp @@ -20,7 +20,7 @@ #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "services/database/Artist.hpp" #include "services/database/Cluster.hpp" diff --git a/src/libs/services/database/impl/User.cpp b/src/libs/services/database/impl/User.cpp index c1854899..054babb0 100644 --- a/src/libs/services/database/impl/User.cpp +++ b/src/libs/services/database/impl/User.cpp @@ -23,7 +23,7 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "IdTypeTraits.hpp" #include "StringViewTraits.hpp" #include "Utils.hpp" diff --git a/src/libs/services/database/include/services/database/Cluster.hpp b/src/libs/services/database/include/services/database/Cluster.hpp index c5215c01..f2cabf82 100644 --- a/src/libs/services/database/include/services/database/Cluster.hpp +++ b/src/libs/services/database/include/services/database/Cluster.hpp @@ -126,7 +126,7 @@ namespace Database { static void remove(Session& session, const std::string& name); // Accessors - const std::string& getName() const { return _name; } + std::string_view getName() const { return _name; } std::vector getClusters() const; Cluster::pointer getCluster(const std::string& name) const; @@ -141,7 +141,7 @@ namespace Database { private: friend class Session; ClusterType(std::string_view name); - static pointer create(Session& session, const std::string& name); + static pointer create(Session& session, std::string_view name); static const std::size_t _maxNameLength = 128; diff --git a/src/libs/services/database/include/services/database/ScanSettings.hpp b/src/libs/services/database/include/services/database/ScanSettings.hpp index cf2aa26b..8aec15b1 100644 --- a/src/libs/services/database/include/services/database/ScanSettings.hpp +++ b/src/libs/services/database/include/services/database/ScanSettings.hpp @@ -20,6 +20,8 @@ #pragma once #include +#include +#include #include #include @@ -74,7 +76,7 @@ namespace Database { void setMediaDirectory(const std::filesystem::path& p); void setUpdateStartTime(Wt::WTime t) { _startTime = t; } void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; } - void setClusterTypes(Session& session, const std::set& clusterTypeNames); + void setClusterTypes(Session& session, const std::set& clusterTypeNames); void setSimilarityEngineType(SimilarityEngineType type) { _similarityEngineType = type; } void incScanVersion(); diff --git a/src/libs/services/feedback/impl/FeedbackService.cpp b/src/libs/services/feedback/impl/FeedbackService.cpp index 6afafe19..9b0993de 100644 --- a/src/libs/services/feedback/impl/FeedbackService.cpp +++ b/src/libs/services/feedback/impl/FeedbackService.cpp @@ -29,7 +29,7 @@ #include "services/database/StarredTrack.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "internal/InternalBackend.hpp" #include "listenbrainz/ListenBrainzBackend.hpp" @@ -44,15 +44,15 @@ namespace Feedback FeedbackService::FeedbackService(boost::asio::io_context& ioContext, Db& db) : _db{ db } { - LMS_LOG(SCROBBLING, INFO) << "Starting service..."; + LMS_LOG(SCROBBLING, INFO, "Starting service..."); _backends.emplace(Database::FeedbackBackend::Internal, std::make_unique(_db)); _backends.emplace(Database::FeedbackBackend::ListenBrainz, std::make_unique(ioContext, _db)); - LMS_LOG(SCROBBLING, INFO) << "Service started!"; + LMS_LOG(SCROBBLING, INFO, "Service started!"); } FeedbackService::~FeedbackService() { - LMS_LOG(SCROBBLING, INFO) << "Service stopped!"; + LMS_LOG(SCROBBLING, INFO, "Service stopped!"); } std::optional FeedbackService::getUserFeedbackBackend(UserId userId) diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp index 123a0a2e..bc925a98 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp @@ -57,7 +57,7 @@ namespace Feedback::ListenBrainz const Wt::Json::Array& feedbacks = root.get("feedback"); - LOG(DEBUG) << "Got " << feedbacks.size() << " feedbacks"; + LOG(DEBUG, "Got " << feedbacks.size() << " feedbacks"); if (feedbacks.empty()) return res; @@ -72,17 +72,17 @@ namespace Feedback::ListenBrainz } catch (const Exception& e) { - LOG(DEBUG) << "Cannot parse feedback: " << e.what() << ", skipping"; + LOG(DEBUG, "Cannot parse feedback: " << e.what() << ", skipping"); } catch (const Wt::WException& e) { - LOG(DEBUG) << "Cannot parse feedback: " << e.what() << ", skipping"; + LOG(DEBUG, "Cannot parse feedback: " << e.what() << ", skipping"); } } } catch (const Wt::WException& error) { - LOG(ERROR) << "Cannot parse 'feedback' result: " << error.what(); + LOG(ERROR, "Cannot parse 'feedback' result: " << error.what()); } return res; diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp index eeb6d792..d1cf0e0e 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp @@ -53,7 +53,7 @@ namespace Feedback::ListenBrainz } catch (const Wt::WException& e) { - LOG(ERROR) << "Cannot parse listen count response: " << e.what(); + LOG(ERROR, "Cannot parse listen count response: " << e.what()); return std::nullopt; } } @@ -66,7 +66,7 @@ namespace Feedback::ListenBrainz , _maxSyncFeedbackCount{ Service::get()->getULong("listenbrainz-max-sync-feedback-count", 1000) } , _syncFeedbacksPeriod{ Service::get()->getULong("listenbrainz-sync-feedbacks-period-hours", 1) } { - LOG(INFO) << "Starting Feedbacks synchronizer, maxSyncFeedbackCount = " << _maxSyncFeedbackCount << ", _syncFeedbacksPeriod = " << _syncFeedbacksPeriod.count() << " hours"; + LOG(INFO, "Starting Feedbacks synchronizer, maxSyncFeedbackCount = " << _maxSyncFeedbackCount << ", _syncFeedbacksPeriod = " << _syncFeedbacksPeriod.count() << " hours"); scheduleSync(std::chrono::seconds{ 30 }); } @@ -95,7 +95,7 @@ namespace Feedback::ListenBrainz case FeedbackType::Erase: if (!recordingMBID) { - LOG(DEBUG) << "Track has no recording MBID: erasing star"; + LOG(DEBUG, "Track has no recording MBID: erasing star"); starredTrack.remove(); } else @@ -112,7 +112,7 @@ namespace Feedback::ListenBrainz if (!recordingMBID) { - LOG(DEBUG) << "Track has no recording MBID: skipping"; + LOG(DEBUG, "Track has no recording MBID: skipping"); return; } @@ -142,7 +142,7 @@ namespace Feedback::ListenBrainz } catch (Exception& e) { - LOG(DEBUG) << "Cannot send feedback: " << e.what(); + LOG(DEBUG, "Cannot send feedback: " << e.what()); } } @@ -156,7 +156,7 @@ namespace Feedback::ListenBrainz Database::StarredTrack::pointer starredTrack{ Database::StarredTrack::find(session, starredTrackId) }; if (!starredTrack) { - LOG(DEBUG) << "Starred track not found. deleted?"; + LOG(DEBUG, "Starred track not found. deleted?"); return; } @@ -166,23 +166,23 @@ namespace Feedback::ListenBrainz { case FeedbackType::Love: starredTrack.modify()->setSyncState(Database::SyncState::Synchronized); - LOG(DEBUG) << "State set to synchronized"; + LOG(DEBUG, "State set to synchronized"); if (userContext.feedbackCount) { (*userContext.feedbackCount)++; - LOG(DEBUG) << "Feedback count set to " << *userContext.feedbackCount << " for user '" << userContext.listenBrainzUserName << "'"; + LOG(DEBUG, "Feedback count set to " << *userContext.feedbackCount << " for user '" << userContext.listenBrainzUserName << "'"); } break; case FeedbackType::Erase: starredTrack.remove(); - LOG(DEBUG) << "Removed starred track"; + LOG(DEBUG, "Removed starred track"); if (userContext.feedbackCount && *userContext.feedbackCount > 0) { (*userContext.feedbackCount)--; - LOG(DEBUG) << "Feedback count set to " << *userContext.feedbackCount << " for user '" << userContext.listenBrainzUserName << "'"; + LOG(DEBUG, "Feedback count set to " << *userContext.feedbackCount << " for user '" << userContext.listenBrainzUserName << "'"); } break; @@ -211,7 +211,7 @@ namespace Feedback::ListenBrainz pendingFeedbacks = StarredTrack::find(session, params); } - LOG(DEBUG) << "Queing " << pendingFeedbacks.results.size() << " pending '" << (feedbackType == FeedbackType::Love ? "love" : "erase") << "' feedbacks"; + LOG(DEBUG, "Queing " << pendingFeedbacks.results.size() << " pending '" << (feedbackType == FeedbackType::Love ? "love" : "erase") << "' feedbacks"); for (const StarredTrackId starredTrackId : pendingFeedbacks.results) enqueFeedback(feedbackType, starredTrackId); @@ -247,13 +247,13 @@ namespace Feedback::ListenBrainz if (_syncFeedbacksPeriod.count() == 0 || _maxSyncFeedbackCount == 0) return; - LOG(DEBUG) << "Scheduled sync in " << fromNow.count() << " seconds..."; + LOG(DEBUG, "Scheduled sync in " << fromNow.count() << " seconds..."); _syncTimer.expires_after(fromNow); _syncTimer.async_wait(boost::asio::bind_executor(_strand, [this](const boost::system::error_code& ec) { if (ec == boost::asio::error::operation_aborted) { - LOG(DEBUG) << "getFeedbacks aborted"; + LOG(DEBUG, "getFeedbacks aborted"); return; } else if (ec) @@ -267,7 +267,7 @@ namespace Feedback::ListenBrainz void FeedbacksSynchronizer::startSync() { - LOG(DEBUG) << "Starting sync!"; + LOG(DEBUG, "Starting sync!"); assert(!isSyncing()); assert(_strand.running_in_this_thread()); @@ -303,7 +303,7 @@ namespace Feedback::ListenBrainz { _strand.dispatch([this, &context] { - LOG(INFO) << "Feedback sync done for user '" << context.listenBrainzUserName << "', fetched: " << context.fetchedFeedbackCount << ", matched: " << context.matchedFeedbackCount << ", imported: " << context.importedFeedbackCount; + LOG(INFO, "Feedback sync done for user '" << context.listenBrainzUserName << "', fetched: " << context.fetchedFeedbackCount << ", matched: " << context.matchedFeedbackCount << ", imported: " << context.importedFeedbackCount); context.syncing = false; if (!isSyncing()) @@ -356,11 +356,11 @@ namespace Feedback::ListenBrainz std::string msgBodyCopy{ msgBody }; _strand.dispatch([this, msgBodyCopy, &context] { - LOG(DEBUG) << "Current feedback count = " << (context.feedbackCount ? *context.feedbackCount : 0) << " for user '" << context.listenBrainzUserName << "'"; + LOG(DEBUG, "Current feedback count = " << (context.feedbackCount ? *context.feedbackCount : 0) << " for user '" << context.listenBrainzUserName << "'"); const auto totalFeedbackCount = parseTotalFeedbackCount(msgBodyCopy); if (totalFeedbackCount) - LOG(DEBUG) << "Feedback count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *totalFeedbackCount; + LOG(DEBUG, "Feedback count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *totalFeedbackCount); bool needSync{ totalFeedbackCount && (!context.feedbackCount || *context.feedbackCount != *totalFeedbackCount) }; context.feedbackCount = totalFeedbackCount; @@ -416,7 +416,7 @@ namespace Feedback::ListenBrainz { const FeedbacksParser::Result parseResult{ FeedbacksParser::parse(msgBody) }; - LOG(DEBUG) << "Parsed " << parseResult.feedbackCount << " feedbacks, found " << parseResult.feedbacks.size() << " usable entries"; + LOG(DEBUG, "Parsed " << parseResult.feedbackCount << " feedbacks, found " << parseResult.feedbacks.size() << " usable entries"); context.fetchedFeedbackCount += parseResult.feedbackCount; for (const Feedback& feedback : parseResult.feedbacks) @@ -441,12 +441,12 @@ namespace Feedback::ListenBrainz const std::vector tracks{ Track::findByRecordingMBID(session, feedback.recordingMBID) }; if (tracks.size() > 1) { - LOG(DEBUG) << "Too many matches for feedback '" << feedback << "': duplicate recording MBIDs found"; + LOG(DEBUG, "Too many matches for feedback '" << feedback << "': duplicate recording MBIDs found"); return; } else if (tracks.empty()) { - LOG(DEBUG) << "Cannot match feedback '" << feedback << "': no track found for this recording MBID"; + LOG(DEBUG, "Cannot match feedback '" << feedback << "': no track found for this recording MBID"); return; } @@ -461,7 +461,7 @@ namespace Feedback::ListenBrainz if (needImport) { - LOG(DEBUG) << "Importing feedback '" << feedback << "'"; + LOG(DEBUG, "Importing feedback '" << feedback << "'"); auto transaction{ session.createWriteTransaction() }; @@ -481,7 +481,7 @@ namespace Feedback::ListenBrainz } else { - LOG(DEBUG) << "No need to import feedback '" << feedback << "', already imported"; + LOG(DEBUG, "No need to import feedback '" << feedback << "', already imported"); context.matchedFeedbackCount++; } } diff --git a/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp b/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp index c05a11bc..aa559b66 100644 --- a/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp @@ -26,7 +26,7 @@ #include "services/database/Track.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" #include "Utils.hpp" @@ -63,12 +63,12 @@ namespace Feedback::ListenBrainz , _client{ Http::createClient(_ioContext, _baseAPIUrl) } , _feedbacksSynchronizer{ _ioContext, db, *_client } { - LOG(INFO) << "Starting ListenBrainz feedback backend... API endpoint = '" << _baseAPIUrl << "'"; + LOG(INFO, "Starting ListenBrainz feedback backend... API endpoint = '" << _baseAPIUrl << "'"); } ListenBrainzBackend::~ListenBrainzBackend() { - LOG(INFO) << "Stopped ListenBrainz feedback backend!"; + LOG(INFO, "Stopped ListenBrainz feedback backend!"); } void ListenBrainzBackend::onStarred(Database::StarredArtistId starredArtistId) diff --git a/src/libs/services/feedback/impl/listenbrainz/Utils.cpp b/src/libs/services/feedback/impl/listenbrainz/Utils.cpp index 58d28a76..1824f3b7 100644 --- a/src/libs/services/feedback/impl/listenbrainz/Utils.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/Utils.cpp @@ -46,13 +46,13 @@ namespace Feedback::ListenBrainz::Utils Wt::Json::Object root; if (!Wt::Json::parse(std::string{ msgBody }, root, error)) { - LOG(ERROR) << "Cannot parse 'validate-token' result: " << error.what(); + LOG(ERROR, "Cannot parse 'validate-token' result: " << error.what()); return listenBrainzUserName; } if (!root.get("valid").orIfNull(false)) { - LOG(INFO) << "Invalid listenbrainz user"; + LOG(INFO, "Invalid listenbrainz user"); return listenBrainzUserName; } diff --git a/src/libs/services/feedback/impl/listenbrainz/Utils.hpp b/src/libs/services/feedback/impl/listenbrainz/Utils.hpp index fcbd3d9c..02edaccc 100644 --- a/src/libs/services/feedback/impl/listenbrainz/Utils.hpp +++ b/src/libs/services/feedback/impl/listenbrainz/Utils.hpp @@ -20,10 +20,10 @@ #pragma once #include "services/database/UserId.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/UUID.hpp" -#define LOG(sev) LMS_LOG(FEEDBACK, sev) << "[listenbrainz] " +#define LOG(sev, message) LMS_LOG(FEEDBACK, sev, "[listenbrainz] " << message) namespace Database { diff --git a/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp b/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp index 9b3ffa37..8792c464 100644 --- a/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp +++ b/src/libs/services/recommendation/impl/PlaylistGeneratorService.cpp @@ -26,7 +26,7 @@ #include "playlist-constraints/ConsecutiveArtists.hpp" #include "playlist-constraints/ConsecutiveReleases.hpp" #include "playlist-constraints/DuplicateTracks.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Recommendation { @@ -48,7 +48,7 @@ namespace Recommendation std::vector PlaylistGeneratorService::extendPlaylist(TrackListId tracklistId, std::size_t maxCount) const { - LMS_LOG(RECOMMENDATION, DEBUG) << "Requested to extend playlist by " << maxCount << " similar tracks"; + LMS_LOG(RECOMMENDATION, DEBUG, "Requested to extend playlist by " << maxCount << " similar tracks"); // supposed to be ordered from most similar to least similar std::vector similarTracks{ _recommendationService.findSimilarTracks(tracklistId, maxCount * 2) }; // ask for more tracks than we need as it will be easier to respect constraints diff --git a/src/libs/services/recommendation/impl/RecommendationService.cpp b/src/libs/services/recommendation/impl/RecommendationService.cpp index 460b7ef7..267f1233 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.cpp +++ b/src/libs/services/recommendation/impl/RecommendationService.cpp @@ -29,7 +29,7 @@ #include "services/database/Session.hpp" #include "services/database/ScanSettings.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Recommendation { diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp index 7678e3ad..a6bcd40a 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp @@ -30,397 +30,384 @@ #include "services/database/TrackFeatures.hpp" #include "services/database/TrackList.hpp" #include "som/DataNormalizer.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Random.hpp" - -namespace Recommendation { - -using namespace Database; - -std::unique_ptr createFeaturesEngine(Db& db) +namespace Recommendation { - return std::make_unique(db); -} - -const FeatureSettingsMap& -FeaturesEngine::getDefaultTrainFeatureSettings() -{ - static const FeatureSettingsMap defaultTrainFeatureSettings - { - { "lowlevel.spectral_energyband_high.mean", {1}}, - { "lowlevel.spectral_rolloff.median", {1}}, - { "lowlevel.spectral_contrast_valleys.var", {1}}, - { "lowlevel.erbbands.mean", {1}}, - { "lowlevel.gfcc.mean", {1}}, - }; - - return defaultTrainFeatureSettings; -} - -static -std::optional -convertFeatureValuesMapToInputVector(const FeatureValuesMap& featureValuesMap, std::size_t nbDimensions) -{ - std::size_t i {}; - std::optional res {SOM::InputVector {nbDimensions}}; - for (const auto& [featureName, values] : featureValuesMap) - { - if (values.size() != getFeatureDef(featureName).nbDimensions) - { - LMS_LOG(RECOMMENDATION, WARNING) << "Dimension mismatch for feature '" << featureName << "'. Expected " << getFeatureDef(featureName).nbDimensions << ", got " << values.size(); - res.reset(); - break; - } - - for (double val : values) - (*res)[i++] = val; - } - - return res; -} - -static -SOM::InputVector -getInputVectorWeights(const FeatureSettingsMap& featureSettingsMap, std::size_t nbDimensions) -{ - SOM::InputVector weights {nbDimensions}; - std::size_t index {}; - for (const auto& [featureName, featureSettings] : featureSettingsMap) - { - const std::size_t featureNbDimensions {getFeatureDef(featureName).nbDimensions}; - - for (std::size_t i {}; i < featureNbDimensions; ++i) - weights[index++] = (1. / featureNbDimensions * featureSettings.weight); - } - - assert(index == nbDimensions); - - return weights; -} - -void -FeaturesEngine::loadFromTraining(const TrainSettings& trainSettings, const ProgressCallback& progressCallback) -{ - LMS_LOG(RECOMMENDATION, INFO) << "Constructing features classifier..."; - - std::unordered_set featureNames; - std::transform(std::cbegin(trainSettings.featureSettingsMap), std::cend(trainSettings.featureSettingsMap), std::inserter(featureNames, std::begin(featureNames)), - [](const auto& itFeatureSetting) { return itFeatureSetting.first; }); - - const std::size_t nbDimensions {std::accumulate(std::cbegin(featureNames), std::cend(featureNames), std::size_t {0}, - [](std::size_t sum, const FeatureName& featureName) { return sum + getFeatureDef(featureName).nbDimensions; })}; - - LMS_LOG(RECOMMENDATION, DEBUG) << "Features dimension = " << nbDimensions; - - Session& session {_db.getTLSSession()}; - - RangeResults trackFeaturesIds; - { - auto transaction {session.createReadTransaction()}; - - LMS_LOG(RECOMMENDATION, DEBUG) << "Getting Track features..."; - trackFeaturesIds = TrackFeatures::find(session); - LMS_LOG(RECOMMENDATION, DEBUG) << "Getting Track features DONE (found " << trackFeaturesIds.results.size() << " track features)"; - } - - std::vector samples; - std::vector samplesTrackIds; - - samples.reserve(trackFeaturesIds.results.size()); - samplesTrackIds.reserve(trackFeaturesIds.results.size()); - - LMS_LOG(RECOMMENDATION, DEBUG) << "Extracting features..."; - // TODO handle errors using exceptions - for (const TrackFeaturesId trackFeaturesId : trackFeaturesIds.results) - { - if (_loadCancelled) - return; - - auto transaction {session.createReadTransaction()}; - - TrackFeatures::pointer trackFeatures {TrackFeatures::find(session, trackFeaturesId)}; - if (!trackFeatures) - continue; - - FeatureValuesMap featureValuesMap {trackFeatures->getFeatureValuesMap(featureNames)}; - if (featureValuesMap.empty()) - continue; - - std::optional inputVector {convertFeatureValuesMapToInputVector(featureValuesMap, nbDimensions)}; - if (!inputVector) - continue; - - samples.emplace_back(std::move(*inputVector)); - samplesTrackIds.emplace_back(trackFeatures->getTrack()->getId()); - } - LMS_LOG(RECOMMENDATION, DEBUG) << "Extracting features DONE"; - - if (samples.empty()) - { - LMS_LOG(RECOMMENDATION, INFO) << "Nothing to classify!"; - return; - } - - LMS_LOG(RECOMMENDATION, DEBUG) << "Normalizing data..."; - SOM::DataNormalizer dataNormalizer {nbDimensions}; - - dataNormalizer.computeNormalizationFactors(samples); - for (auto& sample : samples) - dataNormalizer.normalizeData(sample); + using namespace Database; + + std::unique_ptr createFeaturesEngine(Db& db) + { + return std::make_unique(db); + } + + namespace + { + std::optional convertFeatureValuesMapToInputVector(const FeatureValuesMap& featureValuesMap, std::size_t nbDimensions) + { + std::size_t i{}; + std::optional res{ SOM::InputVector {nbDimensions} }; + for (const auto& [featureName, values] : featureValuesMap) + { + if (values.size() != getFeatureDef(featureName).nbDimensions) + { + LMS_LOG(RECOMMENDATION, WARNING, "Dimension mismatch for feature '" << featureName << "'. Expected " << getFeatureDef(featureName).nbDimensions << ", got " << values.size()); + res.reset(); + break; + } + + for (double val : values) + (*res)[i++] = val; + } + + return res; + } + + SOM::InputVector getInputVectorWeights(const FeatureSettingsMap& featureSettingsMap, std::size_t nbDimensions) + { + SOM::InputVector weights{ nbDimensions }; + std::size_t index{}; + for (const auto& [featureName, featureSettings] : featureSettingsMap) + { + const std::size_t featureNbDimensions{ getFeatureDef(featureName).nbDimensions }; + + for (std::size_t i{}; i < featureNbDimensions; ++i) + weights[index++] = (1. / featureNbDimensions * featureSettings.weight); + } + + assert(index == nbDimensions); + + return weights; + } + } + + const FeatureSettingsMap& FeaturesEngine::getDefaultTrainFeatureSettings() + { + static const FeatureSettingsMap defaultTrainFeatureSettings + { + { "lowlevel.spectral_energyband_high.mean", {1}}, + { "lowlevel.spectral_rolloff.median", {1}}, + { "lowlevel.spectral_contrast_valleys.var", {1}}, + { "lowlevel.erbbands.mean", {1}}, + { "lowlevel.gfcc.mean", {1}}, + }; + + return defaultTrainFeatureSettings; + } + + void FeaturesEngine::loadFromTraining(const TrainSettings& trainSettings, const ProgressCallback& progressCallback) + { + LMS_LOG(RECOMMENDATION, INFO, "Constructing features classifier..."); + + std::unordered_set featureNames; + std::transform(std::cbegin(trainSettings.featureSettingsMap), std::cend(trainSettings.featureSettingsMap), std::inserter(featureNames, std::begin(featureNames)), + [](const auto& itFeatureSetting) { return itFeatureSetting.first; }); + + const std::size_t nbDimensions{ std::accumulate(std::cbegin(featureNames), std::cend(featureNames), std::size_t {0}, + [](std::size_t sum, const FeatureName& featureName) { return sum + getFeatureDef(featureName).nbDimensions; }) }; + + LMS_LOG(RECOMMENDATION, DEBUG, "Features dimension = " << nbDimensions); + + Session & session{ _db.getTLSSession() }; + + RangeResults trackFeaturesIds; + { + auto transaction{ session.createReadTransaction() }; + + LMS_LOG(RECOMMENDATION, DEBUG, "Getting Track features..."); + trackFeaturesIds = TrackFeatures::find(session); + LMS_LOG(RECOMMENDATION, DEBUG, "Getting Track features DONE (found " << trackFeaturesIds.results.size() << " track features)"); + } + + std::vector samples; + std::vector samplesTrackIds; + + samples.reserve(trackFeaturesIds.results.size()); + samplesTrackIds.reserve(trackFeaturesIds.results.size()); + + LMS_LOG(RECOMMENDATION, DEBUG, "Extracting features..."); + // TODO handle errors using exceptions + for (const TrackFeaturesId trackFeaturesId : trackFeaturesIds.results) + { + if (_loadCancelled) + return; + + auto transaction{ session.createReadTransaction() }; + + TrackFeatures::pointer trackFeatures{ TrackFeatures::find(session, trackFeaturesId) }; + if (!trackFeatures) + continue; + + FeatureValuesMap featureValuesMap{ trackFeatures->getFeatureValuesMap(featureNames) }; + if (featureValuesMap.empty()) + continue; + + std::optional inputVector{ convertFeatureValuesMapToInputVector(featureValuesMap, nbDimensions) }; + if (!inputVector) + continue; + + samples.emplace_back(std::move(*inputVector)); + samplesTrackIds.emplace_back(trackFeatures->getTrack()->getId()); + } + LMS_LOG(RECOMMENDATION, DEBUG, "Extracting features DONE"); + + if (samples.empty()) + { + LMS_LOG(RECOMMENDATION, INFO, "Nothing to classify!"); + return; + } - SOM::Coordinate size {static_cast(std::sqrt(samples.size() / trainSettings.sampleCountPerNeuron))}; - if (size < 2) - { - LMS_LOG(RECOMMENDATION, WARNING) << "Very few tracks (" << samples.size() << ") are being used by the features engine, expect bad behaviors"; - size = 2; - } - LMS_LOG(RECOMMENDATION, INFO) << "Found " << samples.size() << " tracks, constructing a " << size << "*" << size << " network"; + LMS_LOG(RECOMMENDATION, DEBUG, "Normalizing data..."); + SOM::DataNormalizer dataNormalizer{ nbDimensions }; - SOM::Network network {size, size, nbDimensions}; - - SOM::InputVector weights {getInputVectorWeights(trainSettings.featureSettingsMap, nbDimensions)}; - network.setDataWeights(weights); - - auto somProgressCallback{[&](const SOM::Network::CurrentIteration& iter) - { - LMS_LOG(RECOMMENDATION, DEBUG) << "Current pass = " << iter.idIteration << " / " << iter.iterationCount; - progressCallback(Progress {iter.idIteration, iter.iterationCount}); - }}; + dataNormalizer.computeNormalizationFactors(samples); + for (auto& sample : samples) + dataNormalizer.normalizeData(sample); - LMS_LOG(RECOMMENDATION, DEBUG) << "Training network..."; - network.train(samples, trainSettings.iterationCount, - progressCallback ? somProgressCallback : SOM::Network::ProgressCallback {}, - [this] { return _loadCancelled; }); - LMS_LOG(RECOMMENDATION, DEBUG) << "Training network DONE"; + SOM::Coordinate size{ static_cast(std::sqrt(samples.size() / trainSettings.sampleCountPerNeuron)) }; + if (size < 2) + { + LMS_LOG(RECOMMENDATION, WARNING, "Very few tracks (" << samples.size() << ") are being used by the features engine, expect bad behaviors"); + size = 2; + } + LMS_LOG(RECOMMENDATION, INFO, "Found " << samples.size() << " tracks, constructing a " << size << "*" << size << " network"); - LMS_LOG(RECOMMENDATION, DEBUG) << "Classifying tracks..."; - TrackPositions trackPositions; - for (std::size_t i {}; i < samples.size(); ++i) - { - if (_loadCancelled) - return; + SOM::Network network{ size, size, nbDimensions }; - const SOM::Position position {network.getClosestRefVectorPosition(samples[i])}; + SOM::InputVector weights{ getInputVectorWeights(trainSettings.featureSettingsMap, nbDimensions) }; + network.setDataWeights(weights); - trackPositions[samplesTrackIds[i]].push_back(position); - } - - LMS_LOG(RECOMMENDATION, DEBUG) << "Classifying tracks DONE"; + auto somProgressCallback{ [&](const SOM::Network::CurrentIteration& iter) + { + LMS_LOG(RECOMMENDATION, DEBUG, "Current pass = " << iter.idIteration << " / " << iter.iterationCount); + progressCallback(Progress {iter.idIteration, iter.iterationCount}); + } }; + + LMS_LOG(RECOMMENDATION, DEBUG, "Training network..."); + network.train(samples, trainSettings.iterationCount, + progressCallback ? somProgressCallback : SOM::Network::ProgressCallback{}, + [this] { return _loadCancelled; }); + LMS_LOG(RECOMMENDATION, DEBUG, "Training network DONE"); - load(std::move(network), std::move(trackPositions)); -} + LMS_LOG(RECOMMENDATION, DEBUG, "Classifying tracks..."); + TrackPositions trackPositions; + for (std::size_t i{}; i < samples.size(); ++i) + { + if (_loadCancelled) + return; -void -FeaturesEngine::loadFromCache(FeaturesEngineCache&& cache) -{ - LMS_LOG(RECOMMENDATION, INFO) << "Constructing features classifier from cache..."; - - load(std::move(cache._network), cache._trackPositions); -} - -TrackContainer -FeaturesEngine::findSimilarTracksFromTrackList(TrackListId trackListId, std::size_t maxCount) const -{ - const TrackContainer trackIds {[&] - { - TrackContainer res; - - Session& session {_db.getTLSSession()}; - - auto transaction {session.createReadTransaction()}; - - const TrackList::pointer trackList {TrackList::find(session, trackListId)}; - if (trackList) - res = trackList->getTrackIds(); - - return res; - }()}; - - return findSimilarTracks(trackIds, maxCount); -} - -TrackContainer -FeaturesEngine::findSimilarTracks(const std::vector& tracksIds, std::size_t maxCount) const -{ - auto similarTrackIds {getSimilarObjects(tracksIds, _trackMatrix, _trackPositions, maxCount)}; - - Session& session {_db.getTLSSession()}; - - { - // Report only existing ids, as tracks may have been removed a long time ago (refreshing the SOM takes some time) - auto transaction {session.createReadTransaction()}; - - similarTrackIds.erase(std::remove_if(std::begin(similarTrackIds), std::end(similarTrackIds), - [&](TrackId trackId) - { - return !Track::exists(session, trackId); - }), std::end(similarTrackIds)); - } - - return similarTrackIds; -} - -ReleaseContainer -FeaturesEngine::getSimilarReleases(ReleaseId releaseId, std::size_t maxCount) const -{ - auto similarReleaseIds {getSimilarObjects({releaseId}, _releaseMatrix, _releasePositions, maxCount)}; - - Session& session {_db.getTLSSession()}; - - if (!similarReleaseIds.empty()) - { - // Report only existing ids - auto transaction {session.createReadTransaction()}; - - similarReleaseIds.erase(std::remove_if(std::begin(similarReleaseIds), std::end(similarReleaseIds), - [&](ReleaseId releaseId) - { - return !Release::exists(session, releaseId); - }), std::end(similarReleaseIds)); - } - - return similarReleaseIds; -} - -ArtistContainer -FeaturesEngine::getSimilarArtists(ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const -{ - auto getSimilarArtistIdsForLinkType {[&] (TrackArtistLinkType linkType) - { - ArtistContainer similarArtistIds; - - const auto itArtists {_artistMatrix.find(linkType)}; - if (itArtists == std::cend(_artistMatrix)) - { - return similarArtistIds; - } - - return getSimilarObjects({artistId}, itArtists->second, _artistPositions, maxCount); - }}; - - std::unordered_set similarArtistIds; - - for (TrackArtistLinkType linkType : linkTypes) - { - const auto similarArtistIdsForLinkType {getSimilarArtistIdsForLinkType(linkType)}; - similarArtistIds.insert(std::begin(similarArtistIdsForLinkType), std::end(similarArtistIdsForLinkType)); - } - - ArtistContainer res(std::cbegin(similarArtistIds), std::cend(similarArtistIds)); - - Session& session {_db.getTLSSession()}; - { - // Report only existing ids - auto transaction {session.createReadTransaction()}; - - res.erase(std::remove_if(std::begin(res), std::end(res), - [&](ArtistId artistId) - { - return !Artist::exists(session, artistId); - }), std::end(res)); - } - - while (res.size() > maxCount) - res.erase(Random::pickRandom(res)); - - return res; -} - -FeaturesEngineCache -FeaturesEngine::toCache() const -{ - return FeaturesEngineCache {*_network, _trackPositions}; -} - -void -FeaturesEngine::load(bool forceReload, const ProgressCallback& progressCallback) -{ - if (forceReload) - { - FeaturesEngineCache::invalidate(); - } - else if (std::optional cache {FeaturesEngineCache::read()}) - { - loadFromCache(std::move(*cache)); - return; - } - - TrainSettings trainSettings; - trainSettings.featureSettingsMap = getDefaultTrainFeatureSettings(); - - loadFromTraining(trainSettings, progressCallback); - if (!_loadCancelled && _network) - toCache().write(); -} - -void -FeaturesEngine::requestCancelLoad() -{ - LMS_LOG(RECOMMENDATION, DEBUG) << "Requesting init cancellation"; - _loadCancelled = true; -} - -void -FeaturesEngine::load(const SOM::Network& network, const TrackPositions& trackPositions) -{ - using namespace Database; - - _networkRefVectorsDistanceMedian = network.computeRefVectorsDistanceMedian(); - LMS_LOG(RECOMMENDATION, DEBUG) << "Median distance betweend ref vectors = " << _networkRefVectorsDistanceMedian; - - const SOM::Coordinate width {network.getWidth()}; - const SOM::Coordinate height {network.getHeight()}; - - _releaseMatrix = ReleaseMatrix {width, height}; - _trackMatrix = TrackMatrix {width, height}; - - LMS_LOG(RECOMMENDATION, DEBUG) << "Constructing maps..."; - - Session& session {_db.getTLSSession()}; - - for (const auto& [trackId, positions] : trackPositions) - { - if (_loadCancelled) - return; - - auto transaction {session.createReadTransaction()}; - - const Track::pointer track {Track::find(session, trackId)}; - if (!track) - continue; - - for (const SOM::Position& position : positions) - { - Utils::push_back_if_not_present(_trackPositions[trackId], position); - Utils::push_back_if_not_present(_trackMatrix[position], trackId); - - if (Release::pointer release {track->getRelease()}) - { - const ReleaseId releaseId {release->getId()}; - Utils::push_back_if_not_present(_releasePositions[releaseId], position); - Utils::push_back_if_not_present(_releaseMatrix[position], releaseId); - } - for (const TrackArtistLink::pointer& artistLink : track->getArtistLinks()) - { - const ArtistId artistId {artistLink->getArtist()->getId()}; - - Utils::push_back_if_not_present(_artistPositions[artistId], position); - auto itArtists {_artistMatrix.find(artistLink->getType())}; - if (itArtists == std::cend(_artistMatrix)) - { - [[maybe_unused]] auto [it, inserted] = _artistMatrix.try_emplace(artistLink->getType(), ArtistMatrix {width, height}); - assert(inserted); - itArtists = it; - } - Utils::push_back_if_not_present(itArtists->second[position], artistId); - } - } - } - - _network = std::make_unique(network); - - LMS_LOG(RECOMMENDATION, INFO) << "Classifier successfully loaded!"; -} + const SOM::Position position{ network.getClosestRefVectorPosition(samples[i]) }; + + trackPositions[samplesTrackIds[i]].push_back(position); + } + + LMS_LOG(RECOMMENDATION, DEBUG, "Classifying tracks DONE"); + + load(std::move(network), std::move(trackPositions)); + } + + void FeaturesEngine::loadFromCache(FeaturesEngineCache&& cache) + { + LMS_LOG(RECOMMENDATION, INFO, "Constructing features classifier from cache..."); + + load(std::move(cache._network), cache._trackPositions); + } + + TrackContainer FeaturesEngine::findSimilarTracksFromTrackList(TrackListId trackListId, std::size_t maxCount) const + { + const TrackContainer trackIds{ [&] + { + TrackContainer res; + + Session& session {_db.getTLSSession()}; + + auto transaction {session.createReadTransaction()}; + + const TrackList::pointer trackList {TrackList::find(session, trackListId)}; + if (trackList) + res = trackList->getTrackIds(); + + return res; + }() }; + + return findSimilarTracks(trackIds, maxCount); + } + + TrackContainer FeaturesEngine::findSimilarTracks(const std::vector& tracksIds, std::size_t maxCount) const + { + auto similarTrackIds{ getSimilarObjects(tracksIds, _trackMatrix, _trackPositions, maxCount) }; + + Session& session{ _db.getTLSSession() }; + + { + // Report only existing ids, as tracks may have been removed a long time ago (refreshing the SOM takes some time) + auto transaction{ session.createReadTransaction() }; + + similarTrackIds.erase(std::remove_if(std::begin(similarTrackIds), std::end(similarTrackIds), + [&](TrackId trackId) + { + return !Track::exists(session, trackId); + }), std::end(similarTrackIds)); + } + + return similarTrackIds; + } + + ReleaseContainer FeaturesEngine::getSimilarReleases(ReleaseId releaseId, std::size_t maxCount) const + { + auto similarReleaseIds{ getSimilarObjects({releaseId}, _releaseMatrix, _releasePositions, maxCount) }; + + Session& session{ _db.getTLSSession() }; + + if (!similarReleaseIds.empty()) + { + // Report only existing ids + auto transaction{ session.createReadTransaction() }; + + similarReleaseIds.erase(std::remove_if(std::begin(similarReleaseIds), std::end(similarReleaseIds), + [&](ReleaseId releaseId) + { + return !Release::exists(session, releaseId); + }), std::end(similarReleaseIds)); + } + + return similarReleaseIds; + } + + ArtistContainer FeaturesEngine::getSimilarArtists(ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const + { + auto getSimilarArtistIdsForLinkType{ [&](TrackArtistLinkType linkType) + { + ArtistContainer similarArtistIds; + + const auto itArtists {_artistMatrix.find(linkType)}; + if (itArtists == std::cend(_artistMatrix)) + { + return similarArtistIds; + } + + return getSimilarObjects({artistId}, itArtists->second, _artistPositions, maxCount); + } }; + + std::unordered_set similarArtistIds; + + for (TrackArtistLinkType linkType : linkTypes) + { + const auto similarArtistIdsForLinkType{ getSimilarArtistIdsForLinkType(linkType) }; + similarArtistIds.insert(std::begin(similarArtistIdsForLinkType), std::end(similarArtistIdsForLinkType)); + } + + ArtistContainer res(std::cbegin(similarArtistIds), std::cend(similarArtistIds)); + + Session& session{ _db.getTLSSession() }; + { + // Report only existing ids + auto transaction{ session.createReadTransaction() }; + + res.erase(std::remove_if(std::begin(res), std::end(res), + [&](ArtistId artistId) + { + return !Artist::exists(session, artistId); + }), std::end(res)); + } + + while (res.size() > maxCount) + res.erase(Random::pickRandom(res)); + + return res; + } + + FeaturesEngineCache FeaturesEngine::toCache() const + { + return FeaturesEngineCache{ *_network, _trackPositions }; + } + + void FeaturesEngine::load(bool forceReload, const ProgressCallback& progressCallback) + { + if (forceReload) + { + FeaturesEngineCache::invalidate(); + } + else if (std::optional cache{ FeaturesEngineCache::read() }) + { + loadFromCache(std::move(*cache)); + return; + } + + TrainSettings trainSettings; + trainSettings.featureSettingsMap = getDefaultTrainFeatureSettings(); + + loadFromTraining(trainSettings, progressCallback); + if (!_loadCancelled && _network) + toCache().write(); + } + + void FeaturesEngine::requestCancelLoad() + { + LMS_LOG(RECOMMENDATION, DEBUG, "Requesting init cancellation"); + _loadCancelled = true; + } + + void FeaturesEngine::load(const SOM::Network& network, const TrackPositions& trackPositions) + { + using namespace Database; + + _networkRefVectorsDistanceMedian = network.computeRefVectorsDistanceMedian(); + LMS_LOG(RECOMMENDATION, DEBUG, "Median distance betweend ref vectors = " << _networkRefVectorsDistanceMedian); + + const SOM::Coordinate width{ network.getWidth() }; + const SOM::Coordinate height{ network.getHeight() }; + + _releaseMatrix = ReleaseMatrix{ width, height }; + _trackMatrix = TrackMatrix{ width, height }; + + LMS_LOG(RECOMMENDATION, DEBUG, "Constructing maps..."); + + Session & session{ _db.getTLSSession() }; + + for (const auto& [trackId, positions] : trackPositions) + { + if (_loadCancelled) + return; + + auto transaction{ session.createReadTransaction() }; + + const Track::pointer track{ Track::find(session, trackId) }; + if (!track) + continue; + + for (const SOM::Position& position : positions) + { + Utils::push_back_if_not_present(_trackPositions[trackId], position); + Utils::push_back_if_not_present(_trackMatrix[position], trackId); + + if (Release::pointer release{ track->getRelease() }) + { + const ReleaseId releaseId{ release->getId() }; + Utils::push_back_if_not_present(_releasePositions[releaseId], position); + Utils::push_back_if_not_present(_releaseMatrix[position], releaseId); + } + for (const TrackArtistLink::pointer& artistLink : track->getArtistLinks()) + { + const ArtistId artistId{ artistLink->getArtist()->getId() }; + + Utils::push_back_if_not_present(_artistPositions[artistId], position); + auto itArtists{ _artistMatrix.find(artistLink->getType()) }; + if (itArtists == std::cend(_artistMatrix)) + { + [[maybe_unused]] auto [it, inserted] = _artistMatrix.try_emplace(artistLink->getType(), ArtistMatrix{ width, height }); + assert(inserted); + itArtists = it; + } + Utils::push_back_if_not_present(itArtists->second[position], artistId); + } + } + } + + _network = std::make_unique(network); + + LMS_LOG(RECOMMENDATION, INFO, "Classifier successfully loaded!"); + } } // ns Recommendation diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngineCache.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.cpp index 0f238627..5985cc80 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngineCache.cpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.cpp @@ -23,233 +23,226 @@ #include #include "utils/IConfig.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" -namespace Recommendation { - - -static -std::filesystem::path getCacheDirectory() +namespace Recommendation { - return Service::get()->getPath("working-dir") / "cache" / "features"; -} + namespace + { + std::filesystem::path getCacheDirectory() + { + return Service::get()->getPath("working-dir") / "cache" / "features"; + } -static std::filesystem::path getCacheNetworkFilePath() -{ - return getCacheDirectory() / "network"; -} + std::filesystem::path getCacheNetworkFilePath() + { + return getCacheDirectory() / "network"; + } -static std::filesystem::path getCacheTrackPositionsFilePath() -{ - return getCacheDirectory() / "track_positions"; -} + std::filesystem::path getCacheTrackPositionsFilePath() + { + return getCacheDirectory() / "track_positions"; + } -static -bool -networkToCacheFile(const SOM::Network& network, std::filesystem::path path) -{ - try - { - boost::property_tree::ptree root; + bool networkToCacheFile(const SOM::Network& network, std::filesystem::path path) + { + try + { + boost::property_tree::ptree root; - root.put("width", network.getWidth()); - root.put("height", network.getHeight()); - root.put("dim_count", network.getInputDimCount()); + root.put("width", network.getWidth()); + root.put("height", network.getHeight()); + root.put("dim_count", network.getInputDimCount()); - for (SOM::InputVector::value_type weight : network.getDataWeights()) - root.add("weights.weight", weight); + for (SOM::InputVector::value_type weight : network.getDataWeights()) + root.add("weights.weight", weight); - for (SOM::Coordinate x = 0; x < network.getWidth(); ++x) - { - for (SOM::Coordinate y = 0; y < network.getWidth(); ++y) - { - const auto& refVector = network.getRefVector({x, y}); + for (SOM::Coordinate x = 0; x < network.getWidth(); ++x) + { + for (SOM::Coordinate y = 0; y < network.getWidth(); ++y) + { + const auto& refVector = network.getRefVector({ x, y }); - boost::property_tree::ptree node; - for (auto value : refVector) - node.add("values.value", value); + boost::property_tree::ptree node; + for (auto value : refVector) + node.add("values.value", value); - node.put("coord_x", x); - node.put("coord_y", y); + node.put("coord_x", x); + node.put("coord_y", y); - root.add_child("ref_vectors.ref_vector", node); - } - } + root.add_child("ref_vectors.ref_vector", node); + } + } - boost::property_tree::write_xml(path.string(), root); + boost::property_tree::write_xml(path.string(), root); - LMS_LOG(RECOMMENDATION, DEBUG) << "Created network cache"; - return true; - } - catch (boost::property_tree::ptree_error& error) - { - LMS_LOG(RECOMMENDATION, ERROR) << "Cannot create network cache: " << error.what(); - return false; - } -} + LMS_LOG(RECOMMENDATION, DEBUG, "Created network cache"); + return true; + } + catch (boost::property_tree::ptree_error& error) + { + LMS_LOG(RECOMMENDATION, ERROR, "Cannot create network cache: " << error.what()); + return false; + } + } + } -std::optional -FeaturesEngineCache::createNetworkFromCacheFile(const std::filesystem::path& path) -{ - if (!std::filesystem::exists(path)) - return std::nullopt; + std::optional FeaturesEngineCache::createNetworkFromCacheFile(const std::filesystem::path& path) + { + if (!std::filesystem::exists(path)) + return std::nullopt; - try - { - LMS_LOG(RECOMMENDATION, INFO) << "Reading network from cache..."; + try + { + LMS_LOG(RECOMMENDATION, INFO, "Reading network from cache..."); - boost::property_tree::ptree root; + boost::property_tree::ptree root; - boost::property_tree::read_xml(path.string(), root); + boost::property_tree::read_xml(path.string(), root); - SOM::Coordinate width {root.get("width")}; - SOM::Coordinate height {root.get("height")}; - std::size_t dimCount {root.get("dim_count")}; + SOM::Coordinate width{ root.get("width") }; + SOM::Coordinate height{ root.get("height") }; + std::size_t dimCount{ root.get("dim_count") }; - SOM::Network res {width, height, dimCount}; + SOM::Network res{ width, height, dimCount }; - { - SOM::InputVector weights {dimCount}; - std::size_t i {}; - for (const auto& val : root.get_child("weights")) - weights[i++] = val.second.get_value(); + { + SOM::InputVector weights{ dimCount }; + std::size_t i{}; + for (const auto& val : root.get_child("weights")) + weights[i++] = val.second.get_value(); - res.setDataWeights(weights); - } + res.setDataWeights(weights); + } - for (const auto& node : root.get_child("ref_vectors")) - { - SOM::Coordinate x {node.second.get("coord_x")}; - SOM::Coordinate y {node.second.get("coord_y")}; + for (const auto& node : root.get_child("ref_vectors")) + { + SOM::Coordinate x{ node.second.get("coord_x") }; + SOM::Coordinate y{ node.second.get("coord_y") }; - SOM::InputVector refVector {dimCount}; - std::size_t i {}; - for (const auto& val : node.second.get_child("values")) - refVector[i++] = val.second.get_value(); + SOM::InputVector refVector{ dimCount }; + std::size_t i{}; + for (const auto& val : node.second.get_child("values")) + refVector[i++] = val.second.get_value(); - res.setRefVector({x, y}, refVector); - } + res.setRefVector({ x, y }, refVector); + } - LMS_LOG(RECOMMENDATION, INFO) << "Successfully read network from cache"; + LMS_LOG(RECOMMENDATION, INFO, "Successfully read network from cache"); - return res; - } - catch (boost::property_tree::ptree_error& error) - { - LMS_LOG(RECOMMENDATION, ERROR) << "Cannot read network cache: " << error.what(); - return std::nullopt; - } -} + return res; + } + catch (boost::property_tree::ptree_error& error) + { + LMS_LOG(RECOMMENDATION, ERROR, "Cannot read network cache: " << error.what()); + return std::nullopt; + } + } -bool -FeaturesEngineCache::objectPositionToCacheFile(const TrackPositions& trackPositions, const std::filesystem::path& path) -{ - try - { - boost::property_tree::ptree root; + bool FeaturesEngineCache::objectPositionToCacheFile(const TrackPositions& trackPositions, const std::filesystem::path& path) + { + try + { + boost::property_tree::ptree root; - for (const auto& [id, positions] : trackPositions) - { - boost::property_tree::ptree node; + for (const auto& [id, positions] : trackPositions) + { + boost::property_tree::ptree node; - node.put("id", id.getValue()); + node.put("id", id.getValue()); - for (const SOM::Position& position : positions) - { - boost::property_tree::ptree positionNode; - positionNode.put("x", position.x); - positionNode.put("y", position.y); + for (const SOM::Position& position : positions) + { + boost::property_tree::ptree positionNode; + positionNode.put("x", position.x); + positionNode.put("y", position.y); - node.add_child("position.position", positionNode); - } + node.add_child("position.position", positionNode); + } - root.add_child("objects.object", node); - } + root.add_child("objects.object", node); + } - boost::property_tree::write_xml(path.string(), root); - return true; - } - catch (boost::property_tree::ptree_error& error) - { - LMS_LOG(RECOMMENDATION, ERROR) << "Cannot cache object position: " << error.what(); - return false; - } -} + boost::property_tree::write_xml(path.string(), root); + return true; + } + catch (boost::property_tree::ptree_error& error) + { + LMS_LOG(RECOMMENDATION, ERROR, "Cannot cache object position: " << error.what()); + return false; + } + } -std::optional -FeaturesEngineCache::createObjectPositionsFromCacheFile(const std::filesystem::path& path) -{ - try - { - LMS_LOG(RECOMMENDATION, INFO) << "Reading object position from cache..."; + std::optional FeaturesEngineCache::createObjectPositionsFromCacheFile(const std::filesystem::path& path) + { + try + { + LMS_LOG(RECOMMENDATION, INFO, "Reading object position from cache..."); - boost::property_tree::ptree root; + boost::property_tree::ptree root; - boost::property_tree::read_xml(path.string(), root); + boost::property_tree::read_xml(path.string(), root); - TrackPositions res; + TrackPositions res; - for (const auto& object : root.get_child("objects")) - { - const Database::TrackId id {object.second.get("id")}; - for (const auto& position : object.second.get_child("position")) - { - auto x = position.second.get("x"); - auto y = position.second.get("y"); + for (const auto& object : root.get_child("objects")) + { + const Database::TrackId id{ object.second.get("id") }; + for (const auto& position : object.second.get_child("position")) + { + auto x = position.second.get("x"); + auto y = position.second.get("y"); - res[id].push_back({x, y}); - } - } + res[id].push_back({ x, y }); + } + } - LMS_LOG(RECOMMENDATION, INFO) << "Successfully read object position from cache"; + LMS_LOG(RECOMMENDATION, INFO, "Successfully read object position from cache"); - return res; - } - catch (boost::property_tree::ptree_error& error) - { - LMS_LOG(RECOMMENDATION, ERROR) << "Cannot create object position from cache file: " << error.what(); - return std::nullopt; - } -} + return res; + } + catch (boost::property_tree::ptree_error& error) + { + LMS_LOG(RECOMMENDATION, ERROR, "Cannot create object position from cache file: " << error.what()); + return std::nullopt; + } + } -void -FeaturesEngineCache::invalidate() -{ - std::filesystem::remove(getCacheNetworkFilePath()); - std::filesystem::remove(getCacheTrackPositionsFilePath()); -} + void FeaturesEngineCache::invalidate() + { + std::filesystem::remove(getCacheNetworkFilePath()); + std::filesystem::remove(getCacheTrackPositionsFilePath()); + } -std::optional -FeaturesEngineCache::read() -{ - auto network{createNetworkFromCacheFile(getCacheNetworkFilePath())}; - if (!network) - return std::nullopt; + std::optional FeaturesEngineCache::read() + { + auto network{ createNetworkFromCacheFile(getCacheNetworkFilePath()) }; + if (!network) + return std::nullopt; - auto trackPositions{createObjectPositionsFromCacheFile(getCacheTrackPositionsFilePath())}; - if (!trackPositions) - return std::nullopt; + auto trackPositions{ createObjectPositionsFromCacheFile(getCacheTrackPositionsFilePath()) }; + if (!trackPositions) + return std::nullopt; - return FeaturesEngineCache {std::move(*network), std::move(*trackPositions)}; -} + return FeaturesEngineCache{ std::move(*network), std::move(*trackPositions) }; + } -void -FeaturesEngineCache::write() const -{ - std::filesystem::create_directories(Service::get()->getPath("working-dir") / "cache" / "features"); + void FeaturesEngineCache::write() const + { + std::filesystem::create_directories(Service::get()->getPath("working-dir") / "cache" / "features"); - if (!networkToCacheFile(_network, getCacheNetworkFilePath()) - || !objectPositionToCacheFile(_trackPositions, getCacheTrackPositionsFilePath())) - { - invalidate(); - } -} + if (!networkToCacheFile(_network, getCacheNetworkFilePath()) + || !objectPositionToCacheFile(_trackPositions, getCacheTrackPositionsFilePath())) + { + invalidate(); + } + } -FeaturesEngineCache::FeaturesEngineCache(SOM::Network network, TrackPositions trackPositions) -: _network {std::move(network)}, -_trackPositions {std::move(trackPositions)} -{ -} + FeaturesEngineCache::FeaturesEngineCache(SOM::Network network, TrackPositions trackPositions) + : _network{ std::move(network) }, + _trackPositions{ std::move(trackPositions) } + { + } } // namespace Recommendation diff --git a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp index 2130f5b4..a7388a6e 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp @@ -25,7 +25,7 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Recommendation::PlaylistGeneratorConstraint { diff --git a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp index 668cb6b3..80bc45f7 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp @@ -23,7 +23,7 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Recommendation::PlaylistGeneratorConstraint { diff --git a/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp b/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp index 328e998d..59641345 100644 --- a/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepCheckDuplicatedDbFiles.cpp @@ -22,7 +22,7 @@ #include "services/database/Db.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Scanner { @@ -45,13 +45,13 @@ namespace Scanner const Track::pointer track{ Track::find(session, trackId) }; if (auto trackMBID{ track->getTrackMBID() }) { - LMS_LOG(DBUPDATER, INFO) << "Found duplicated track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); + LMS_LOG(DBUPDATER, INFO, "Found duplicated track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName()); context.stats.duplicates.emplace_back(ScanDuplicate{ track->getId(), DuplicateReason::SameTrackMBID }); context.currentStepStats.processedElems++; _progressCallback(context.currentStepStats); } } - LMS_LOG(DBUPDATER, DEBUG) << "Found " << context.currentStepStats.processedElems << " duplicated audio files"; + LMS_LOG(DBUPDATER, DEBUG, "Found " << context.currentStepStats.processedElems << " duplicated audio files"); } } diff --git a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp index a67636b2..7b7daf0a 100644 --- a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp +++ b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp @@ -21,7 +21,7 @@ #include "services/database/Db.hpp" #include "services/database/Cluster.hpp" #include "services/database/Session.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Path.hpp" namespace Scanner @@ -84,6 +84,6 @@ namespace Scanner return true; }); - LMS_LOG(DBUPDATER, DEBUG) << "Recomputed stats for " << context.currentStepStats.processedElems << " clusters!"; + LMS_LOG(DBUPDATER, DEBUG, "Recomputed stats for " << context.currentStepStats.processedElems << " clusters!"); } } diff --git a/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp b/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp index 929ff07e..da2c9b51 100644 --- a/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepDiscoverFiles.cpp @@ -18,7 +18,7 @@ */ #include "ScanStepDiscoverFiles.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Path.hpp" namespace Scanner @@ -42,6 +42,6 @@ namespace Scanner context.stats.filesScanned = context.currentStepStats.processedElems; - LMS_LOG(DBUPDATER, DEBUG) << "Discovered " << context.stats.filesScanned << " files in '" << context.directory << "'"; + LMS_LOG(DBUPDATER, DEBUG, "Discovered " << context.stats.filesScanned << " files in '" << context.directory << "'"); } } diff --git a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp index 53e56686..78f12180 100644 --- a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp @@ -25,7 +25,7 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Path.hpp" namespace Scanner @@ -87,14 +87,14 @@ namespace Scanner Session& session{ _db.getTLSSession() }; - LMS_LOG(DBUPDATER, DEBUG) << "Checking tracks to be removed..."; + LMS_LOG(DBUPDATER, DEBUG, "Checking tracks to be removed..."); std::size_t trackCount{}; { auto transaction{ session.createReadTransaction() }; trackCount = Track::getCount(session); } - LMS_LOG(DBUPDATER, DEBUG) << trackCount << " tracks to be checked..."; + LMS_LOG(DBUPDATER, DEBUG, trackCount << " tracks to be checked..."); context.currentStepStats.totalElems = trackCount; @@ -143,24 +143,24 @@ namespace Scanner break; } - LMS_LOG(DBUPDATER, DEBUG) << trackCount << " tracks checked!"; + LMS_LOG(DBUPDATER, DEBUG, trackCount << " tracks checked!"); } void ScanStepRemoveOrphanDbFiles::removeOrphanClusters() { - LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan clusters..."; + LMS_LOG(DBUPDATER, DEBUG, "Checking orphan clusters..."); removeOrphanEntries(_db.getTLSSession(), _abortScan); } void ScanStepRemoveOrphanDbFiles::removeOrphanArtists() { - LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan artists..."; + LMS_LOG(DBUPDATER, DEBUG, "Checking orphan artists..."); removeOrphanEntries(_db.getTLSSession(), _abortScan); } void ScanStepRemoveOrphanDbFiles::removeOrphanReleases() { - LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan releases..."; + LMS_LOG(DBUPDATER, DEBUG, "Checking orphan releases..."); removeOrphanEntries(_db.getTLSSession(), _abortScan); } @@ -172,19 +172,19 @@ namespace Scanner // and still belongs to a media directory if (!std::filesystem::exists(p) || !std::filesystem::is_regular_file(p)) { - LMS_LOG(DBUPDATER, INFO) << "Removing '" << p.string() << "': missing"; + LMS_LOG(DBUPDATER, INFO, "Removing '" << p.string() << "': missing"); return false; } if (!PathUtils::isPathInRootPath(p, _settings.mediaDirectory, &excludeDirFileName)) { - LMS_LOG(DBUPDATER, INFO) << "Removing '" << p.string() << "': out of media directory"; + LMS_LOG(DBUPDATER, INFO, "Removing '" << p.string() << "': out of media directory"); return false; } if (!PathUtils::hasFileAnyExtension(p, _settings.supportedExtensions)) { - LMS_LOG(DBUPDATER, INFO) << "Removing '" << p.string() << "': file format no longer handled"; + LMS_LOG(DBUPDATER, INFO, "Removing '" << p.string() << "': file format no longer handled"); return false; } @@ -192,7 +192,7 @@ namespace Scanner } catch (std::filesystem::filesystem_error& e) { - LMS_LOG(DBUPDATER, ERROR) << "Caught exception while checking file '" << p.string() << "': " << e.what(); + LMS_LOG(DBUPDATER, ERROR, "Caught exception while checking file '" << p.string() << "': " << e.what()); return false; } } diff --git a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp index f1e902aa..a0b008a7 100644 --- a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp @@ -30,7 +30,7 @@ #include "services/database/TrackArtistLink.hpp" #include "utils/Exception.hpp" #include "utils/IConfig.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Path.hpp" using namespace Database; @@ -258,7 +258,7 @@ namespace Scanner MetaData::ParserReadStyle getParserReadStyle() { - std::string_view readStyle{ Service::get()->getString("scanner-parser-read-style", "accurate") }; + std::string_view readStyle{ Service::get()->getString("scanner-parser-read-style", "average") }; if (readStyle == "fast") return MetaData::ParserReadStyle::Fast; @@ -290,7 +290,7 @@ namespace Scanner if (ec) { - LMS_LOG(DBUPDATER, ERROR) << "Cannot process entry '" << path.string() << "': " << ec.message(); + LMS_LOG(DBUPDATER, ERROR, "Cannot process entry '" << path.string() << "': " << ec.message()); context.stats.errors.emplace_back(ScanError{ path, ScanErrorType::CannotReadFile, ec.message() }); } else if (PathUtils::hasFileAnyExtension(path, _settings.supportedExtensions)) @@ -319,7 +319,7 @@ namespace Scanner } catch (LmsException& e) { - LMS_LOG(DBUPDATER, ERROR) << e.what(); + LMS_LOG(DBUPDATER, ERROR, e.what()); stats.skips++; return; } @@ -365,7 +365,7 @@ namespace Scanner std::error_code ec; if (!std::filesystem::exists(otherTrack->getPath(), ec)) { - LMS_LOG(DBUPDATER, DEBUG) << "Considering track '" << file.string() << "' moved from '" << otherTrack->getPath() << "'"; + LMS_LOG(DBUPDATER, DEBUG, "Considering track '" << file.string() << "' moved from '" << otherTrack->getPath() << "'"); track = otherTrack; track.modify()->setPath(file); } @@ -384,7 +384,7 @@ namespace Scanner if (!PathUtils::isPathInRootPath(file, _settings.mediaDirectory, &excludeDirFileName)) continue; - LMS_LOG(DBUPDATER, DEBUG) << "Skipped '" << file.string() << "' (similar MBID in '" << otherTrack->getPath().string() << "')"; + LMS_LOG(DBUPDATER, DEBUG, "Skipped '" << file.string() << "' (similar MBID in '" << otherTrack->getPath().string() << "')"); // As this MBID already exists, just remove what we just scanned if (track) { @@ -399,7 +399,7 @@ namespace Scanner // We estimate this is an audio file if the duration is not null if (trackInfo->duration == std::chrono::milliseconds::zero()) { - LMS_LOG(DBUPDATER, DEBUG) << "Skipped '" << file.string() << "' (duration is 0)"; + LMS_LOG(DBUPDATER, DEBUG, "Skipped '" << file.string() << "' (duration is 0)"); // If Track exists here, delete it! if (track) @@ -427,12 +427,12 @@ namespace Scanner if (!track) { track = dbSession.create(file); - LMS_LOG(DBUPDATER, DEBUG) << "Adding '" << file.string() << "'"; + LMS_LOG(DBUPDATER, DEBUG, "Adding '" << file.string() << "'"); stats.additions++; } else { - LMS_LOG(DBUPDATER, DEBUG) << "Updating '" << file.string() << "'"; + LMS_LOG(DBUPDATER, DEBUG, "Updating '" << file.string() << "'"); stats.updates++; } diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index 2a0b57bd..f648df17 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -27,7 +27,7 @@ #include "services/database/ScanSettings.hpp" #include "utils/Exception.hpp" #include "utils/IConfig.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Path.hpp" #include "utils/Tuple.hpp" @@ -82,9 +82,9 @@ namespace Scanner ScannerService::~ScannerService() { - LMS_LOG(DBUPDATER, INFO) << "Stopping service..."; + LMS_LOG(DBUPDATER, INFO, "Stopping service..."); stop(); - LMS_LOG(DBUPDATER, INFO) << "Service stopped!"; + LMS_LOG(DBUPDATER, INFO, "Service stopped!"); } void ScannerService::start() @@ -113,15 +113,15 @@ namespace Scanner void ScannerService::abortScan() { - LMS_LOG(DBUPDATER, DEBUG) << "Aborting scan..."; + LMS_LOG(DBUPDATER, DEBUG, "Aborting scan..."); std::scoped_lock lock{ _controlMutex }; - LMS_LOG(DBUPDATER, DEBUG) << "Waiting for the scan to abort..."; + LMS_LOG(DBUPDATER, DEBUG, "Waiting for the scan to abort..."); _abortScan = true; _scheduleTimer.cancel(); _ioService.stop(); - LMS_LOG(DBUPDATER, DEBUG) << "Scan abort done!"; + LMS_LOG(DBUPDATER, DEBUG, "Scan abort done!"); _abortScan = false; _ioService.start(); @@ -167,7 +167,7 @@ namespace Scanner void ScannerService::scheduleNextScan() { - LMS_LOG(DBUPDATER, DEBUG) << "Scheduling next scan"; + LMS_LOG(DBUPDATER, DEBUG, "Scheduling next scan"); refreshScanSettings(); @@ -202,7 +202,7 @@ namespace Scanner break; case ScanSettings::UpdatePeriod::Never: - LMS_LOG(DBUPDATER, INFO) << "Auto scan disabled!"; + LMS_LOG(DBUPDATER, INFO, "Auto scan disabled!"); break; } @@ -230,7 +230,7 @@ namespace Scanner if (dateTime.isNull()) { - LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan right now"; + LMS_LOG(DBUPDATER, INFO, "Scheduling next scan right now"); _scheduleTimer.expires_from_now(std::chrono::seconds{ 0 }); _scheduleTimer.async_wait(cb); } @@ -240,7 +240,7 @@ namespace Scanner std::time_t t{ std::chrono::system_clock::to_time_t(timePoint) }; char ctimeStr[26]; - LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan at " << std::string(::ctime_r(&t, ctimeStr)); + LMS_LOG(DBUPDATER, INFO, "Scheduling next scan at " << std::string(::ctime_r(&t, ctimeStr))); _scheduleTimer.expires_at(timePoint); _scheduleTimer.async_wait(cb); } @@ -257,7 +257,7 @@ namespace Scanner } - LMS_LOG(UI, INFO) << "New scan started!"; + LMS_LOG(UI, INFO, "New scan started!"); refreshScanSettings(); @@ -267,16 +267,16 @@ namespace Scanner for (auto& scanStep : _scanSteps) { - LMS_LOG(DBUPDATER, DEBUG) << "Starting scan step '" << scanStep->getStepName() << "'"; + LMS_LOG(DBUPDATER, DEBUG, "Starting scan step '" << scanStep->getStepName() << "'"); scanContext.currentStepStats = ScanStepStats{ Wt::WDateTime::currentDateTime(), scanStep->getStep() }; notifyInProgress(scanContext.currentStepStats); scanStep->process(scanContext); notifyInProgress(scanContext.currentStepStats); - LMS_LOG(DBUPDATER, DEBUG) << "Completed scan step '" << scanStep->getStepName() << "'"; + LMS_LOG(DBUPDATER, DEBUG, "Completed scan step '" << scanStep->getStepName() << "'"); } - 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 << ", 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 << ", duplicates = " << stats.duplicates.size()); _dbSession.analyze(); @@ -290,14 +290,14 @@ namespace Scanner _currentScanStepStats.reset(); } - LMS_LOG(DBUPDATER, DEBUG) << "Scan not aborted, scheduling next scan!"; + LMS_LOG(DBUPDATER, DEBUG, "Scan not aborted, scheduling next scan!"); scheduleNextScan(); _events.scanComplete.emit(stats); } else { - LMS_LOG(DBUPDATER, DEBUG) << "Scan aborted, not scheduling next scan!"; + LMS_LOG(DBUPDATER, DEBUG, "Scan aborted, not scheduling next scan!"); std::unique_lock lock{ _statusMutex }; @@ -312,9 +312,9 @@ namespace Scanner if (_settings == newSettings) return; - LMS_LOG(DBUPDATER, DEBUG) << "Scanner settings updated"; - LMS_LOG(DBUPDATER, DEBUG) << "skipDuplicateMBID = " << newSettings.skipDuplicateMBID; - LMS_LOG(DBUPDATER, DEBUG) << "Using scan settings version " << newSettings.scanVersion; + LMS_LOG(DBUPDATER, DEBUG, "Scanner settings updated"); + LMS_LOG(DBUPDATER, DEBUG, "skipDuplicateMBID = " << newSettings.skipDuplicateMBID); + LMS_LOG(DBUPDATER, DEBUG, "Using scan settings version " << newSettings.scanVersion); _settings = std::move(newSettings); @@ -366,7 +366,7 @@ namespace Scanner std::transform(std::cbegin(clusterTypes), std::cend(clusterTypes), std::inserter(clusterTypeNames, clusterTypeNames.begin()), - [](ClusterType::pointer clusterType) { return clusterType->getName(); }); + [](const ClusterType::pointer& clusterType) { return std::string{ clusterType->getName() }; }); newSettings.clusterTypeNames = std::move(clusterTypeNames); } diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp index 512e224f..65b8a32b 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -26,7 +26,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "internal/InternalBackend.hpp" #include "listenbrainz/ListenBrainzBackend.hpp" @@ -43,15 +43,15 @@ namespace Scrobbling ScrobblingService::ScrobblingService(boost::asio::io_context& ioContext, Db& db) : _db{ db } { - LMS_LOG(SCROBBLING, INFO) << "Starting service..."; + LMS_LOG(SCROBBLING, INFO, "Starting service..."); _scrobblingBackends.emplace(ScrobblingBackend::Internal, std::make_unique(_db)); _scrobblingBackends.emplace(ScrobblingBackend::ListenBrainz, std::make_unique(ioContext, _db)); - LMS_LOG(SCROBBLING, INFO) << "Service started!"; + LMS_LOG(SCROBBLING, INFO, "Service started!"); } ScrobblingService::~ScrobblingService() { - LMS_LOG(SCROBBLING, INFO) << "Service stopped!"; + LMS_LOG(SCROBBLING, INFO, "Service stopped!"); } void ScrobblingService::listenStarted(const Listen& listen) diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp index b8e43523..92e4701a 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzBackend.cpp @@ -24,7 +24,7 @@ #include "services/database/Track.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" #include "Utils.hpp" @@ -44,7 +44,7 @@ namespace Scrobbling::ListenBrainz const bool res{ duration >= std::chrono::minutes(4) || (duration >= track->getDuration() / 2) }; if (!res) - LOG(DEBUG) << "Track cannot be scrobbled since played duration is too short: " << duration.count() << "s, total duration = " << std::chrono::duration_cast(track->getDuration()).count() << "s"; + LOG(DEBUG, "Track cannot be scrobbled since played duration is too short: " << duration.count() << "s, total duration = " << std::chrono::duration_cast(track->getDuration()).count() << "s"); return res; } @@ -57,12 +57,12 @@ namespace Scrobbling::ListenBrainz , _client{ Http::createClient(_ioContext, _baseAPIUrl) } , _listensSynchronizer{ _ioContext, db, *_client } { - LOG(INFO) << "Starting ListenBrainz backend... API endpoint = '" << _baseAPIUrl << "'"; + LOG(INFO, "Starting ListenBrainz backend... API endpoint = '" << _baseAPIUrl << "'"); } ListenBrainzBackend::~ListenBrainzBackend() { - LOG(INFO) << "Stopped ListenBrainz backend!"; + LOG(INFO, "Stopped ListenBrainz backend!"); } void ListenBrainzBackend::listenStarted(const Listen& listen) diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensParser.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensParser.cpp index 3b2787fd..3c40d004 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensParser.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensParser.cpp @@ -24,86 +24,82 @@ #include #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "Utils.hpp" -namespace -{ - using namespace Scrobbling::ListenBrainz; - - Listen - parseListen(const Wt::Json::Object& listenObject) - { - Listen listen; - - // Mandatory fields - const Wt::Json::Object& metadata = listenObject.get("track_metadata"); - listen.trackName = static_cast(metadata.get("track_name")); - listen.artistName = static_cast(metadata.get("artist_name")); - - // Optional fields - listen.releaseName = static_cast(metadata.get("release_name").orIfNull("")); - if (listenObject.type("listened_at") == Wt::Json::Type::Number) - listen.listenedAt = Wt::WDateTime::fromTime_t(static_cast(listenObject.get("listened_at"))); - if (!listen.listenedAt.isValid()) - LOG(ERROR) << "Invalid or missing 'listened_at' field!"; - - if (metadata.type("additional_info") == Wt::Json::Type::Object) - { - const Wt::Json::Object& additionalInfo = metadata.get("additional_info"); - listen.trackMBID = UUID::fromString(additionalInfo.get("track_mbid").orIfNull("")); - listen.recordingMBID = UUID::fromString(additionalInfo.get("recording_mbid").orIfNull("")); - listen.releaseMBID = UUID::fromString(additionalInfo.get("release_mbid").orIfNull("")); - - // tracknumber should be an integer but some players encode as strings - int trackNumber {additionalInfo.get("tracknumber").toNumber().orIfNull(-1)}; - if (trackNumber > 0) - listen.trackNumber = trackNumber; - } - - return listen; - } -} // namespace - namespace Scrobbling::ListenBrainz { - ListensParser::Result - ListensParser::parse(std::string_view msgBody) - { - Result result; + namespace + { + Listen parseListen(const Wt::Json::Object& listenObject) + { + Listen listen; - try - { - Wt::Json::Object root; - Wt::Json::parse(std::string {msgBody}, root); + // Mandatory fields + const Wt::Json::Object& metadata = listenObject.get("track_metadata"); + listen.trackName = static_cast(metadata.get("track_name")); + listen.artistName = static_cast(metadata.get("artist_name")); - const Wt::Json::Object& payload = root.get("payload"); - const Wt::Json::Array& listens = payload.get("listens"); + // Optional fields + listen.releaseName = static_cast(metadata.get("release_name").orIfNull("")); + if (listenObject.type("listened_at") == Wt::Json::Type::Number) + listen.listenedAt = Wt::WDateTime::fromTime_t(static_cast(listenObject.get("listened_at"))); + if (!listen.listenedAt.isValid()) + LOG(ERROR, "Invalid or missing 'listened_at' field!"); - LOG(DEBUG) << "Parsing " << listens.size() << " listens..."; - result.listenCount = listens.size(); + if (metadata.type("additional_info") == Wt::Json::Type::Object) + { + const Wt::Json::Object& additionalInfo = metadata.get("additional_info"); + listen.trackMBID = UUID::fromString(additionalInfo.get("track_mbid").orIfNull("")); + listen.recordingMBID = UUID::fromString(additionalInfo.get("recording_mbid").orIfNull("")); + listen.releaseMBID = UUID::fromString(additionalInfo.get("release_mbid").orIfNull("")); - if (listens.empty()) - return result; + // tracknumber should be an integer but some players encode as strings + int trackNumber{ additionalInfo.get("tracknumber").toNumber().orIfNull(-1) }; + if (trackNumber > 0) + listen.trackNumber = trackNumber; + } - for (const Wt::Json::Value& value : listens) - { - try - { - const Wt::Json::Object& listen = value; - result.listens.push_back(parseListen(listen)); - } - catch (const Wt::WException& error) - { - LOG(ERROR) << "Cannot parse 'listen': " << error.what(); - } - } - } - catch (const Wt::WException& error) - { - LOG(ERROR) << "Cannot parse 'listens': " << error.what(); - } + return listen; + } + } // namespace - return result; - } + ListensParser::Result ListensParser::parse(std::string_view msgBody) + { + Result result; + + try + { + Wt::Json::Object root; + Wt::Json::parse(std::string{ msgBody }, root); + + const Wt::Json::Object& payload = root.get("payload"); + const Wt::Json::Array& listens = payload.get("listens"); + + LOG(DEBUG, "Parsing " << listens.size() << " listens..."); + result.listenCount = listens.size(); + + if (listens.empty()) + return result; + + for (const Wt::Json::Value& value : listens) + { + try + { + const Wt::Json::Object& listen = value; + result.listens.push_back(parseListen(listen)); + } + catch (const Wt::WException& error) + { + LOG(ERROR, "Cannot parse 'listen': " << error.what()); + } + } + } + catch (const Wt::WException& error) + { + LOG(ERROR, "Cannot parse 'listens': " << error.what()); + } + + return result; + } } // Scrobbling::ListenBrainz diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 09972615..8960dd2b 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -58,7 +58,7 @@ namespace if (artists.empty()) { - LOG(DEBUG) << "Track cannot be scrobbled since it does not have any artist"; + LOG(DEBUG, "Track cannot be scrobbled since it does not have any artist"); return std::nullopt; } @@ -134,7 +134,7 @@ namespace } catch (const Wt::WException& e) { - LOG(ERROR) << "Cannot parse listen count response: " << e.what(); + LOG(ERROR, "Cannot parse listen count response: " << e.what()); return std::nullopt; } } @@ -152,12 +152,12 @@ namespace // if duplicated files, do not record it (let the user correct its database) if (tracks.size() == 1) { - LOG(DEBUG) << "Matched listen '" << listen << "' using track MBID"; + LOG(DEBUG, "Matched listen '" << listen << "' using track MBID"); return tracks.front()->getId(); } else if (tracks.size() > 1) { - LOG(DEBUG) << "Too many matches for listen '" << listen << "' using track MBID!"; + LOG(DEBUG, "Too many matches for listen '" << listen << "' using track MBID!"); return {}; } } @@ -168,12 +168,12 @@ namespace // if duplicated files, do not record it (let the user correct its database) if (tracks.size() == 1) { - LOG(DEBUG) << "Matched listen '" << listen << "' using recording MBID"; + LOG(DEBUG, "Matched listen '" << listen << "' using recording MBID"); return tracks.front()->getId(); } else if (tracks.size() > 1) { - LOG(DEBUG) << "Too many matches for listen '" << listen << "' using recording MBID!"; + LOG(DEBUG, "Too many matches for listen '" << listen << "' using recording MBID!"); return {}; } } @@ -192,16 +192,16 @@ namespace // conservative behavior: in case of multiple matches: reject if (tracks.results.size() == 1) { - LOG(DEBUG) << "Matched listen '" << listen << "' using metadata"; + LOG(DEBUG, "Matched listen '" << listen << "' using metadata"); return tracks.results.front(); } else if (tracks.results.size() > 1) { - LOG(DEBUG) << "Too many matches for listen '" << listen << "' using metadata"; + LOG(DEBUG, "Too many matches for listen '" << listen << "' using metadata"); return {}; } - LOG(DEBUG) << "No match for listen '" << listen << "'"; + LOG(DEBUG, "No match for listen '" << listen << "'"); return {}; } } @@ -215,7 +215,7 @@ namespace Scrobbling::ListenBrainz , _maxSyncListenCount{ Service::get()->getULong("listenbrainz-max-sync-listen-count", 1000) } , _syncListensPeriod{ Service::get()->getULong("listenbrainz-sync-listens-period-hours", 1) } { - LOG(INFO) << "Starting Listens synchronizer, maxSyncListenCount = " << _maxSyncListenCount << ", _syncListensPeriod = " << _syncListensPeriod.count() << " hours"; + LOG(INFO, "Starting Listens synchronizer, maxSyncListenCount = " << _maxSyncListenCount << ", _syncListensPeriod = " << _syncListensPeriod.count() << " hours"); scheduleSync(std::chrono::seconds{ 30 }); } @@ -267,14 +267,14 @@ namespace Scrobbling::ListenBrainz std::string bodyText{ listenToJsonString(_db.getTLSSession(), listen, timePoint, timePoint.isValid() ? "single" : "playing_now") }; if (bodyText.empty()) { - LOG(DEBUG) << "Cannot convert listen to json: skipping"; + LOG(DEBUG, "Cannot convert listen to json: skipping"); return; } const std::optional listenBrainzToken{ Utils::getListenBrainzToken(_db.getTLSSession(), listen.userId) }; if (!listenBrainzToken) { - LOG(DEBUG) << "No listenbrainz token found: skipping"; + LOG(DEBUG, "No listenbrainz token found: skipping"); return; } @@ -305,7 +305,7 @@ namespace Scrobbling::ListenBrainz dbListen = session.create(user, track, Database::ScrobblingBackend::ListenBrainz, listen.listenedAt); dbListen.modify()->setSyncState(scrobblingState); - LOG(DEBUG) << "LISTEN CREATED for user " << user->getLoginName() << ", track '" << track->getName() << "' AT " << listen.listenedAt.toString(); + LOG(DEBUG, "LISTEN CREATED for user " << user->getLoginName() << ", track '" << track->getName() << "' AT " << listen.listenedAt.toString()); return true; } @@ -347,7 +347,7 @@ namespace Scrobbling::ListenBrainz } } - LOG(DEBUG) << "Queing " << pendingListens.size() << " pending listen"; + LOG(DEBUG, "Queing " << pendingListens.size() << " pending listen"); for (const TimedListen& pendingListen : pendingListens) enqueListen(pendingListen); @@ -379,13 +379,13 @@ namespace Scrobbling::ListenBrainz if (_syncListensPeriod.count() == 0 || _maxSyncListenCount == 0) return; - LOG(DEBUG) << "Scheduled sync in " << fromNow.count() << " seconds..."; + LOG(DEBUG, "Scheduled sync in " << fromNow.count() << " seconds..."); _syncTimer.expires_after(fromNow); _syncTimer.async_wait(boost::asio::bind_executor(_strand, [this](const boost::system::error_code& ec) { if (ec == boost::asio::error::operation_aborted) { - LOG(DEBUG) << "getListens aborted"; + LOG(DEBUG, "getListens aborted"); return; } else if (ec) @@ -399,7 +399,7 @@ namespace Scrobbling::ListenBrainz void ListensSynchronizer::startSync() { - LOG(DEBUG) << "Starting sync!"; + LOG(DEBUG, "Starting sync!"); assert(!isSyncing()); @@ -435,7 +435,7 @@ namespace Scrobbling::ListenBrainz { _strand.dispatch([this, &context] { - LOG(INFO) << "Sync done for user '" << context.listenBrainzUserName << "', fetched: " << context.fetchedListenCount << ", matched: " << context.matchedListenCount << ", imported: " << context.importedListenCount; + LOG(INFO, "Sync done for user '" << context.listenBrainzUserName << "', fetched: " << context.fetchedListenCount << ", matched: " << context.matchedListenCount << ", imported: " << context.importedListenCount); context.syncing = false; if (!isSyncing()) @@ -489,7 +489,7 @@ namespace Scrobbling::ListenBrainz { const auto listenCount = parseListenCount(msgBody); if (listenCount) - LOG(DEBUG) << "Listen count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *listenCount; + LOG(DEBUG, "Listen count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *listenCount); bool needSync{ listenCount && (!context.listenCount || *context.listenCount != *listenCount) }; context.listenCount = listenCount; @@ -551,7 +551,7 @@ namespace Scrobbling::ListenBrainz // update oldest listen for the next query if (!parsedListen.listenedAt.isValid()) { - LOG(DEBUG) << "Skipping entry due to invalid listenedAt"; + LOG(DEBUG, "Skipping entry due to invalid listenedAt"); continue; } diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp index 2593dcf1..8c215d3d 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp @@ -27,38 +27,36 @@ namespace Scrobbling::ListenBrainz::Utils { - std::optional - getListenBrainzToken(Database::Session& session, Database::UserId userId) - { - auto transaction {session.createReadTransaction()}; + std::optional getListenBrainzToken(Database::Session& session, Database::UserId userId) + { + auto transaction{ session.createReadTransaction() }; - const Database::User::pointer user {Database::User::find(session, userId)}; - if (!user) - return std::nullopt; + const Database::User::pointer user{ Database::User::find(session, userId) }; + if (!user) + return std::nullopt; - return user->getListenBrainzToken(); - } + return user->getListenBrainzToken(); + } - std::string - parseValidateToken(std::string_view msgBody) - { - std::string listenBrainzUserName; + std::string parseValidateToken(std::string_view msgBody) + { + std::string listenBrainzUserName; - Wt::Json::ParseError error; - Wt::Json::Object root; - if (!Wt::Json::parse(std::string {msgBody}, root, error)) - { - LOG(ERROR) << "Cannot parse 'validate-token' result: " << error.what(); - return listenBrainzUserName; - } + Wt::Json::ParseError error; + Wt::Json::Object root; + if (!Wt::Json::parse(std::string{ msgBody }, root, error)) + { + LOG(ERROR, "Cannot parse 'validate-token' result: " << error.what()); + return listenBrainzUserName; + } - if (!root.get("valid").orIfNull(false)) - { - LOG(INFO) << "Invalid listenbrainz user"; - return listenBrainzUserName; - } + if (!root.get("valid").orIfNull(false)) + { + LOG(INFO, "Invalid listenbrainz user"); + return listenBrainzUserName; + } - listenBrainzUserName = root.get("user_name").orIfNull(""); - return listenBrainzUserName; - } + listenBrainzUserName = root.get("user_name").orIfNull(""); + return listenBrainzUserName; + } } diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp index a02eaca7..805d65a2 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp @@ -20,10 +20,10 @@ #pragma once #include "services/database/UserId.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/UUID.hpp" -#define LOG(sev) LMS_LOG(SCROBBLING, sev) << "[listenbrainz] " +#define LOG(sev, message) LMS_LOG(SCROBBLING, sev, message << "[listenbrainz] ") namespace Database { diff --git a/src/libs/services/scrobbling/test/Scrobbling.cpp b/src/libs/services/scrobbling/test/Scrobbling.cpp index d4a68962..88d0f430 100644 --- a/src/libs/services/scrobbling/test/Scrobbling.cpp +++ b/src/libs/services/scrobbling/test/Scrobbling.cpp @@ -19,16 +19,16 @@ #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" #include "utils/StreamLogger.hpp" -int main(int argc, char **argv) +int main(int argc, char** argv) { - // log to stdout - Service logger {std::make_unique(std::cout, EnumSet {Severity::FATAL, Severity::ERROR})}; + // log to stdout + Service logger{ std::make_unique(std::cout, EnumSet {Severity::FATAL, Severity::ERROR}) }; - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/src/libs/som/impl/Network.cpp b/src/libs/som/impl/Network.cpp index 83f952d7..b34a125b 100644 --- a/src/libs/som/impl/Network.cpp +++ b/src/libs/som/impl/Network.cpp @@ -26,7 +26,7 @@ #include #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Random.hpp" namespace SOM diff --git a/src/libs/subsonic/impl/SubsonicId.cpp b/src/libs/subsonic/impl/SubsonicId.cpp index ddd3090f..073c961e 100644 --- a/src/libs/subsonic/impl/SubsonicId.cpp +++ b/src/libs/subsonic/impl/SubsonicId.cpp @@ -21,7 +21,7 @@ #include "SubsonicResponse.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/String.hpp" namespace API::Subsonic diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 49c5f62a..bcbdd8aa 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -29,7 +29,7 @@ #include "services/database/User.hpp" #include "utils/EnumSet.hpp" #include "utils/IConfig.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" #include "utils/String.hpp" #include "utils/Utils.hpp" @@ -290,7 +290,7 @@ namespace API::Subsonic const std::size_t requestId{ curRequestId++ }; - LMS_LOG(API_SUBSONIC, DEBUG) << "Handling request " << requestId << " '" << request.pathInfo() << "', continuation = " << (request.continuation() ? "true" : "false") << ", params = " << parameterMapToDebugString(request.getParameterMap()); + LMS_LOG(API_SUBSONIC, DEBUG, "Handling request " << requestId << " '" << request.pathInfo() << "', continuation = " << (request.continuation() ? "true" : "false") << ", params = " << parameterMapToDebugString(request.getParameterMap())); std::string requestPath{ request.pathInfo() }; if (StringUtils::stringEndsWith(requestPath, ".view")) @@ -319,7 +319,7 @@ namespace API::Subsonic resp.write(response.out(), format); response.setMimeType(std::string{ ResponseFormatToMimeType(format) }); - LMS_LOG(API_SUBSONIC, DEBUG) << "Request " << requestId << " '" << requestPath << "' handled!"; + LMS_LOG(API_SUBSONIC, DEBUG, "Request " << requestId << " '" << requestPath << "' handled!"); return; } @@ -328,18 +328,18 @@ namespace API::Subsonic if (itStreamHandler != mediaRetrievalHandlers.end()) { itStreamHandler->second(requestContext, request, response); - LMS_LOG(API_SUBSONIC, DEBUG) << "Request " << requestId << " '" << requestPath << "' handled!"; + LMS_LOG(API_SUBSONIC, DEBUG, "Request " << requestId << " '" << requestPath << "' handled!"); return; } - LMS_LOG(API_SUBSONIC, ERROR) << "Unhandled command '" << requestPath << "'"; + LMS_LOG(API_SUBSONIC, ERROR, "Unhandled command '" << requestPath << "'"); throw UnknownEntryPointGenericError{}; } catch (const Error& e) { - LMS_LOG(API_SUBSONIC, ERROR) << "Error while processing request '" << requestPath << "'" + LMS_LOG(API_SUBSONIC, ERROR, "Error while processing request '" << requestPath << "'" << ", params = [" << parameterMapToDebugString(request.getParameterMap()) << "]" - << ", code = " << static_cast(e.getCode()) << ", msg = '" << e.getMessage() << "'"; + << ", code = " << static_cast(e.getCode()) << ", msg = '" << e.getMessage() << "'"); Response resp{ Response::createFailedResponse(protocolVersion, e) }; resp.write(response.out(), format); response.setMimeType(std::string{ ResponseFormatToMimeType(format) }); diff --git a/src/libs/subsonic/impl/entrypoints/Browsing.cpp b/src/libs/subsonic/impl/entrypoints/Browsing.cpp index 2d9d4cde..e0e7fc7e 100644 --- a/src/libs/subsonic/impl/entrypoints/Browsing.cpp +++ b/src/libs/subsonic/impl/entrypoints/Browsing.cpp @@ -26,7 +26,7 @@ #include "services/database/Track.hpp" #include "services/database/User.hpp" #include "services/recommendation/IRecommendationService.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Random.hpp" #include "utils/Service.hpp" #include "responses/Album.hpp" @@ -122,7 +122,7 @@ namespace API::Subsonic // This endpoint does not scale: make sort lived transactions in order not to block the whole application // first pass: dispatch the artists by first letter - LMS_LOG(API_SUBSONIC, DEBUG) << "GetArtists: fetching all artists..."; + LMS_LOG(API_SUBSONIC, DEBUG, "GetArtists: fetching all artists..."); std::map> artistsSortedByFirstChar; std::size_t currentArtistOffset{ 0 }; constexpr std::size_t batchSize{ 100 }; @@ -151,7 +151,7 @@ namespace API::Subsonic } // second pass: add each artist - LMS_LOG(API_SUBSONIC, DEBUG) << "GetArtists: constructing response..."; + LMS_LOG(API_SUBSONIC, DEBUG, "GetArtists: constructing response..."); for (const auto& [sortChar, artistIds] : artistsSortedByFirstChar) { Response::Node& indexNode{ artistsNode.createArrayChild("index") }; diff --git a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp index 36e10b3f..ed00e7b5 100644 --- a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp @@ -29,7 +29,7 @@ #include "services/database/Track.hpp" #include "services/database/User.hpp" #include "utils/IResourceHandler.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/FileResourceHandlerCreator.hpp" #include "utils/Utils.hpp" #include "utils/String.hpp" @@ -153,7 +153,7 @@ namespace API::Subsonic if (!requestedFormat && (maxBitRate == 0 || track->getBitrate() <= maxBitRate )) { - LMS_LOG(API_SUBSONIC, DEBUG) << "File's bitrate is compatible with parameters => no transcoding"; + LMS_LOG(API_SUBSONIC, DEBUG, "File's bitrate is compatible with parameters => no transcoding"); return parameters; // no transcoding needed } @@ -165,7 +165,7 @@ namespace API::Subsonic { if (maxBitRate == 0 || track->getBitrate() <= maxBitRate) { - LMS_LOG(API_SUBSONIC, DEBUG) << "File's bitrate and format are compatible with parameters => no transcoding"; + LMS_LOG(API_SUBSONIC, DEBUG, "File's bitrate and format are compatible with parameters => no transcoding"); return parameters; // no transcoding needed } bitrate = maxBitRate; @@ -246,7 +246,7 @@ namespace API::Subsonic } catch (const Av::Exception& e) { - LMS_LOG(API_SUBSONIC, ERROR) << "Caught Av exception: " << e.what(); + LMS_LOG(API_SUBSONIC, ERROR, "Caught Av exception: " << e.what()); } } diff --git a/src/libs/utils/impl/ArchiveZipper.cpp b/src/libs/utils/impl/ArchiveZipper.cpp index e4d8c850..f90b1e58 100644 --- a/src/libs/utils/impl/ArchiveZipper.cpp +++ b/src/libs/utils/impl/ArchiveZipper.cpp @@ -26,7 +26,7 @@ #include #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace Zip { @@ -73,7 +73,7 @@ namespace Zip { const int res {::archive_write_free(arch)}; if (res != ARCHIVE_OK) - LMS_LOG(UTILS, ERROR) << "Failure while freeing archive control struct: " << std::string {::strerror(res)}; + LMS_LOG(UTILS, ERROR, "Failure while freeing archive control struct: " << std::string {::strerror(res)}); } void @@ -176,7 +176,7 @@ namespace Zip void ArchiveZipper::abort() { - LMS_LOG(UTILS, DEBUG) << "Aborting zip creation"; + LMS_LOG(UTILS, DEBUG, "Aborting zip creation"); if (_archive) { ::archive_write_fail(_archive.get()); diff --git a/src/libs/utils/impl/ChildProcess.cpp b/src/libs/utils/impl/ChildProcess.cpp index b0c4aef2..d9d1b614 100644 --- a/src/libs/utils/impl/ChildProcess.cpp +++ b/src/libs/utils/impl/ChildProcess.cpp @@ -36,177 +36,172 @@ #include #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" namespace { - class SystemException : public ChildProcessException - { - public: - SystemException(int err, const std::string& errMsg) - : ChildProcessException {errMsg + ": " + ::strerror(err)} - {} + class SystemException : public ChildProcessException + { + public: + SystemException(int err, const std::string& errMsg) + : ChildProcessException{ errMsg + ": " + ::strerror(err) } + {} - SystemException(boost::system::error_code ec, const std::string& errMsg) - : ChildProcessException {errMsg + ": " + ec.message()} - {} - }; + SystemException(boost::system::error_code ec, const std::string& errMsg) + : ChildProcessException{ errMsg + ": " + ec.message() } + {} + }; } ChildProcess::ChildProcess(boost::asio::io_context& ioContext, const std::filesystem::path& path, const Args& args) -: _ioContext {ioContext} -, _childStdout {_ioContext} + : _ioContext{ ioContext } + , _childStdout{ _ioContext } { - // make sure only one thread is executing this part of code - static std::mutex mutex; - std::unique_lock lock {mutex}; + // make sure only one thread is executing this part of code + static std::mutex mutex; + std::unique_lock lock{ mutex }; - int pipe[2]; + int pipe[2]; - int res {pipe2(pipe, O_NONBLOCK | O_CLOEXEC)}; - if (res < 0) - throw SystemException {errno, "pipe2 failed!"}; + int res{ pipe2(pipe, O_NONBLOCK | O_CLOEXEC) }; + if (res < 0) + throw SystemException{ errno, "pipe2 failed!" }; - { + { #if defined(__linux__) && defined(F_SETPIPE_SZ) - // Just a hint here to prevent the writer from writing too many bytes ahead of the reader - constexpr std::size_t pipeSize {65536*4}; + // Just a hint here to prevent the writer from writing too many bytes ahead of the reader + constexpr std::size_t pipeSize{ 65536 * 4 }; - if (fcntl(pipe[0], F_SETPIPE_SZ, pipeSize) == -1) - throw SystemException {errno, "fcntl failed!"}; - if (fcntl(pipe[1], F_SETPIPE_SZ, pipeSize) == -1) - throw SystemException {errno, "fcntl failed!"}; + if (fcntl(pipe[0], F_SETPIPE_SZ, pipeSize) == -1) + throw SystemException{ errno, "fcntl failed!" }; + if (fcntl(pipe[1], F_SETPIPE_SZ, pipeSize) == -1) + throw SystemException{ errno, "fcntl failed!" }; #endif - } + } - res = fork(); - if (res == -1) - throw SystemException {errno, "fork failed!"}; + res = fork(); + if (res == -1) + throw SystemException{ errno, "fork failed!" }; - if (res == 0) // CHILD - { - close(pipe[0]); - close(STDIN_FILENO); - close(STDERR_FILENO); + if (res == 0) // CHILD + { + close(pipe[0]); + close(STDIN_FILENO); + close(STDERR_FILENO); - // Replace stdout with pipe write - if (dup2(pipe[1], STDOUT_FILENO) == -1) - exit(-1); + // Replace stdout with pipe write + if (dup2(pipe[1], STDOUT_FILENO) == -1) + exit(-1); - std::vector execArgs; - std::transform(std::cbegin(args), std::cend(args), std::back_inserter(execArgs), [](const std::string& arg) { return arg.c_str(); }); - execArgs.push_back(nullptr); + std::vector execArgs; + std::transform(std::cbegin(args), std::cend(args), std::back_inserter(execArgs), [](const std::string& arg) { return arg.c_str(); }); + execArgs.push_back(nullptr); - res = execv(path.string().c_str(), (char *const*)&execArgs[0]); - if (res == -1) - exit(-1); - } - else // PARENT - { - close(pipe[1]); - { - boost::system::error_code assignError; - _childStdout.assign(pipe[0], assignError); - if (assignError) - throw SystemException {assignError, "fork failed!"}; - } - _childPID = res; - } + res = execv(path.string().c_str(), (char* const*)&execArgs[0]); + if (res == -1) + exit(-1); + } + else // PARENT + { + close(pipe[1]); + { + boost::system::error_code assignError; + _childStdout.assign(pipe[0], assignError); + if (assignError) + throw SystemException{ assignError, "fork failed!" }; + } + _childPID = res; + } } ChildProcess::~ChildProcess() { - LMS_LOG(CHILDPROCESS, DEBUG) << "Closing child process..."; - { - boost::system::error_code closeError; - _childStdout.close(closeError); - if (closeError) - LMS_LOG(CHILDPROCESS, ERROR) << "Closed failed: " << closeError.message(); - } + LMS_LOG(CHILDPROCESS, DEBUG, "Closing child process..."); + { + boost::system::error_code closeError; + _childStdout.close(closeError); + if (closeError) + LMS_LOG(CHILDPROCESS, ERROR, "Closed failed: " << closeError.message()); + } - if (!_finished) - kill(); + if (!_finished) + kill(); - wait(true); + wait(true); } -void -ChildProcess::kill() +void ChildProcess::kill() { - // process may already have finished - LMS_LOG(CHILDPROCESS, DEBUG) << "Killing child process..."; - if (::kill(_childPID, SIGKILL) == -1) - LMS_LOG(CHILDPROCESS, DEBUG) << "Kill failed: " << ::strerror(errno); + // process may already have finished + LMS_LOG(CHILDPROCESS, DEBUG, "Killing child process..."); + if (::kill(_childPID, SIGKILL) == -1) + LMS_LOG(CHILDPROCESS, DEBUG, "Kill failed: " << ::strerror(errno)); } -bool -ChildProcess::wait(bool block) +bool ChildProcess::wait(bool block) { - assert(!_waited); + assert(!_waited); - int wstatus {}; - const pid_t pid {waitpid(_childPID, &wstatus, block ? 0 : WNOHANG)}; + int wstatus{}; + const pid_t pid{ waitpid(_childPID, &wstatus, block ? 0 : WNOHANG) }; - if (pid == -1) - throw SystemException {errno, "waitpid failed!"}; - else if (pid == 0) - return false; + if (pid == -1) + throw SystemException{ errno, "waitpid failed!" }; + else if (pid == 0) + return false; - if (WIFEXITED(wstatus)) - { - _exitCode = WEXITSTATUS(wstatus); - LMS_LOG(CHILDPROCESS, DEBUG) << "Exit code = " << *_exitCode; - } + if (WIFEXITED(wstatus)) + { + _exitCode = WEXITSTATUS(wstatus); + LMS_LOG(CHILDPROCESS, DEBUG, "Exit code = " << *_exitCode); + } - _waited = true; - return true; + _waited = true; + return true; } -void -ChildProcess::asyncRead(std::byte* data, std::size_t bufferSize, ReadCallback callback) +void ChildProcess::asyncRead(std::byte* data, std::size_t bufferSize, ReadCallback callback) { - assert(!finished()); + assert(!finished()); - LMS_LOG(CHILDPROCESS, DEBUG) << "Async read, bufferSize = " << bufferSize; + LMS_LOG(CHILDPROCESS, DEBUG, "Async read, bufferSize = " << bufferSize); - boost::asio::async_read(_childStdout, boost::asio::buffer(data, bufferSize), - [this, callback {std::move(callback)}](const boost::system::error_code& error, std::size_t bytesTransferred) - { - LMS_LOG(CHILDPROCESS, DEBUG) << "Async read cb - ec = '" << error.message() << "' (" << error.value() << "), bytesTransferred = " << bytesTransferred; + boost::asio::async_read(_childStdout, boost::asio::buffer(data, bufferSize), + [this, callback{ std::move(callback) }](const boost::system::error_code& error, std::size_t bytesTransferred) + { + LMS_LOG(CHILDPROCESS, DEBUG, "Async read cb - ec = '" << error.message() << "' (" << error.value() << "), bytesTransferred = " << bytesTransferred); - ReadResult readResult {ReadResult::Success}; - if (error) - { - if (error != boost::asio::error::eof) - { - // forbidden to read any captured param here as the ChildProcess instance may already have been killed - return; - } + ReadResult readResult{ ReadResult::Success }; + if (error) + { + if (error != boost::asio::error::eof) + { + // forbidden to read any captured param here as the ChildProcess instance may already have been killed + return; + } - readResult = ReadResult::EndOfFile; - _finished = true; - } + readResult = ReadResult::EndOfFile; + _finished = true; + } - callback(readResult, bytesTransferred); - }); + callback(readResult, bytesTransferred); + }); } -std::size_t -ChildProcess::readSome(std::byte* data, std::size_t bufferSize) +std::size_t ChildProcess::readSome(std::byte* data, std::size_t bufferSize) { - boost::system::error_code ec; - const std::size_t res {_childStdout.read_some(boost::asio::buffer(data, bufferSize), ec)}; - LMS_LOG(CHILDPROCESS, DEBUG) << "read some " << res << " bytes, ec = " << ec.message(); - if (ec) - _childStdout.close(ec); + boost::system::error_code ec; + const std::size_t res{ _childStdout.read_some(boost::asio::buffer(data, bufferSize), ec) }; + LMS_LOG(CHILDPROCESS, DEBUG, "read some " << res << " bytes, ec = " << ec.message()); + if (ec) + _childStdout.close(ec); - return res; + return res; } -bool -ChildProcess::finished() const +bool ChildProcess::finished() const { - return _finished; + return _finished; } diff --git a/src/libs/utils/impl/ChildProcessManager.cpp b/src/libs/utils/impl/ChildProcessManager.cpp index ab563329..4d58c49f 100644 --- a/src/libs/utils/impl/ChildProcessManager.cpp +++ b/src/libs/utils/impl/ChildProcessManager.cpp @@ -19,7 +19,7 @@ #include "ChildProcessManager.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "ChildProcess.hpp" diff --git a/src/libs/utils/impl/Config.cpp b/src/libs/utils/impl/Config.cpp index e53b90c5..c21f0aaa 100644 --- a/src/libs/utils/impl/Config.cpp +++ b/src/libs/utils/impl/Config.cpp @@ -20,7 +20,7 @@ #include "Config.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" std::unique_ptr createConfig(const std::filesystem::path& p) { diff --git a/src/libs/utils/impl/FileResourceHandler.cpp b/src/libs/utils/impl/FileResourceHandler.cpp index a99227da..026befd1 100644 --- a/src/libs/utils/impl/FileResourceHandler.cpp +++ b/src/libs/utils/impl/FileResourceHandler.cpp @@ -21,7 +21,7 @@ #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" std::unique_ptr createFileResourceHandler(const std::filesystem::path& path, std::string_view mimeType) @@ -45,7 +45,7 @@ FileResourceHandler::processRequest(const Wt::Http::Request& request, Wt::Http:: { if (!ifs) { - LMS_LOG(UTILS, ERROR) << "Cannot open file stream for '" << _path.string() << "'"; + LMS_LOG(UTILS, ERROR, "Cannot open file stream for '" << _path.string() << "'"); response.setStatus(404); return {}; } @@ -54,7 +54,7 @@ FileResourceHandler::processRequest(const Wt::Http::Request& request, Wt::Http:: const ::uint64_t fileSize{ static_cast<::uint64_t>(ifs.tellg()) }; ifs.seekg(0, std::ios::beg); - LMS_LOG(UTILS, DEBUG) << "File '" << _path.string() << "', fileSize = " << fileSize; + LMS_LOG(UTILS, DEBUG, "File '" << _path.string() << "', fileSize = " << fileSize); response.addHeader("Accept-Ranges", "bytes"); @@ -66,13 +66,13 @@ FileResourceHandler::processRequest(const Wt::Http::Request& request, Wt::Http:: response.setStatus(416); // Requested range not satisfiable response.addHeader("Content-Range", contentRange.str()); - LMS_LOG(UTILS, DEBUG) << "Range not satisfiable"; + LMS_LOG(UTILS, DEBUG, "Range not satisfiable"); return {}; } if (ranges.size() == 1) { - LMS_LOG(UTILS, DEBUG) << "Range requested = " << ranges[0].firstByte() << "/" << ranges[0].lastByte(); + LMS_LOG(UTILS, DEBUG, "Range requested = " << ranges[0].firstByte() << "-" << ranges[0].lastByte()); response.setStatus(206); startByte = ranges[0].firstByte(); @@ -87,19 +87,19 @@ FileResourceHandler::processRequest(const Wt::Http::Request& request, Wt::Http:: } else { - LMS_LOG(UTILS, DEBUG) << "No range requested"; + LMS_LOG(UTILS, DEBUG, "No range requested"); response.setStatus(200); _beyondLastByte = fileSize; response.setContentLength(_beyondLastByte); } - LMS_LOG(UTILS, DEBUG) << "Mimetype set to '" << _mimeType << "'"; + LMS_LOG(UTILS, DEBUG, "Mimetype set to '" << _mimeType << "'"); response.setMimeType(_mimeType); } else if (!ifs) { - LMS_LOG(UTILS, ERROR) << "Cannot reopen file stream for '" << _path.string() << "'"; + LMS_LOG(UTILS, ERROR, "Cannot reopen file stream for '" << _path.string() << "'"); return {}; } @@ -113,19 +113,24 @@ FileResourceHandler::processRequest(const Wt::Http::Request& request, Wt::Http:: ifs.read(&buf[0], pieceSize); const ::uint64_t actualPieceSize{ static_cast<::uint64_t>(ifs.gcount()) }; - response.out().write(&buf[0], actualPieceSize); + if (actualPieceSize > 0) + { + response.out().write(&buf[0], actualPieceSize); + LMS_LOG(UTILS, DEBUG, "Written " << actualPieceSize << " bytes, range = " << startByte << "-" << startByte + actualPieceSize - 1 << ""); + } + else + { + LMS_LOG(UTILS, DEBUG, "Written 0 byte"); + } - LMS_LOG(UTILS, DEBUG) << "Written " << actualPieceSize << " bytes"; - - LMS_LOG(UTILS, DEBUG) << "Progress: " << actualPieceSize << "/" << restSize; if (ifs.good() && actualPieceSize < restSize) { _offset = startByte + actualPieceSize; - LMS_LOG(UTILS, DEBUG) << "Job not complete! Next chunk offset = " << _offset; + LMS_LOG(UTILS, DEBUG, "Job not complete! Remaining range: " << _offset << "-" << _beyondLastByte - 1); return response.createContinuation(); } - LMS_LOG(UTILS, DEBUG) << "Job complete!"; + LMS_LOG(UTILS, DEBUG, "Job complete!"); return nullptr; } diff --git a/src/libs/utils/impl/IOContextRunner.cpp b/src/libs/utils/impl/IOContextRunner.cpp index 8cd1fd14..42fbd182 100644 --- a/src/libs/utils/impl/IOContextRunner.cpp +++ b/src/libs/utils/impl/IOContextRunner.cpp @@ -21,13 +21,13 @@ #include -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" IOContextRunner::IOContextRunner(boost::asio::io_service& ioService, std::size_t threadCount) : _ioService {ioService} , _work {ioService} { - LMS_LOG(UTILS, INFO) << "Starting IO context with " << threadCount << " threads..."; + LMS_LOG(UTILS, INFO, "Starting IO context with " << threadCount << " threads..."); for (std::size_t i {}; i < threadCount; ++i) { _threads.emplace_back([&] @@ -38,7 +38,7 @@ IOContextRunner::IOContextRunner(boost::asio::io_service& ioService, std::size_t } catch (const std::exception& e) { - LMS_LOG(UTILS, FATAL) << "Exception caught in IO context: " << e.what(); + LMS_LOG(UTILS, FATAL, "Exception caught in IO context: " << e.what()); std::abort(); } }); @@ -48,10 +48,10 @@ IOContextRunner::IOContextRunner(boost::asio::io_service& ioService, std::size_t void IOContextRunner::stop() { - LMS_LOG(UTILS, DEBUG) << "Stopping IO context..."; + LMS_LOG(UTILS, DEBUG, "Stopping IO context..."); _work.reset(); _ioService.stop(); - LMS_LOG(UTILS, DEBUG) << "IO context stopped!"; + LMS_LOG(UTILS, DEBUG, "IO context stopped!"); } IOContextRunner::~IOContextRunner() diff --git a/src/libs/utils/impl/Logger.cpp b/src/libs/utils/impl/Logger.cpp index 28176166..1d95ced4 100644 --- a/src/libs/utils/impl/Logger.cpp +++ b/src/libs/utils/impl/Logger.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" const char* getModuleName(Module mod) { @@ -59,20 +59,19 @@ const char* getSeverityName(Severity sev) return ""; } -Log::Log(Logger* logger, Module module, Severity severity) - : _module{ module }, - _severity{ severity }, - _logger{ logger } +Log::Log(ILogger& logger, Module module, Severity severity) + : _logger{ logger } + , _module{ module } + , _severity{ severity } + {} Log::~Log() { - if (_logger) - _logger->processLog(*this); + _logger.processLog(*this); } -std::string -Log::getMessage() const +std::string Log::getMessage() const { return _oss.str(); } diff --git a/src/libs/utils/impl/Path.cpp b/src/libs/utils/impl/Path.cpp index 089472d5..b42de130 100644 --- a/src/libs/utils/impl/Path.cpp +++ b/src/libs/utils/impl/Path.cpp @@ -30,146 +30,136 @@ #include "utils/Crc32Calculator.hpp" #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/String.hpp" namespace PathUtils { + std::uint32_t computeCrc32(const std::filesystem::path& p) + { + Utils::Crc32Calculator crc32; - std::uint32_t - computeCrc32(const std::filesystem::path& p) - { - Utils::Crc32Calculator crc32; + std::ifstream ifs{ p.string().c_str(), std::ios_base::binary }; + if (ifs) + { + do + { + std::array buffer; - std::ifstream ifs {p.string().c_str(), std::ios_base::binary}; - if (ifs) - { - do - { - std::array buffer; + ifs.read(buffer.data(), buffer.size()); + crc32.processBytes(reinterpret_cast(buffer.data()), ifs.gcount()); + } while (ifs); + } + else + { + LMS_LOG(DBUPDATER, ERROR, "Failed to open file '" << p.string() << "'"); + throw LmsException("Failed to open file '" + p.string() + "'"); + } - ifs.read( buffer.data(), buffer.size() ); - crc32.processBytes( reinterpret_cast(buffer.data()), ifs.gcount() ); - } - while (ifs); - } - else - { - LMS_LOG(DBUPDATER, ERROR) << "Failed to open file '" << p.string() << "'"; - throw LmsException("Failed to open file '" + p.string() + "'" ); - } + return crc32.getResult(); + } - return crc32.getResult(); - } + bool ensureDirectory(const std::filesystem::path& dir) + { + if (std::filesystem::exists(dir)) + return std::filesystem::is_directory(dir); + else + return std::filesystem::create_directory(dir); + } - bool - ensureDirectory(const std::filesystem::path& dir) - { - if (std::filesystem::exists(dir)) - return std::filesystem::is_directory(dir); - else - return std::filesystem::create_directory(dir); - } + Wt::WDateTime getLastWriteTime(const std::filesystem::path& file) + { + struct stat sb {}; - Wt::WDateTime - getLastWriteTime(const std::filesystem::path& file) - { - struct stat sb {}; + if (stat(file.string().c_str(), &sb) == -1) + throw LmsException("Failed to get stats on file '" + file.string() + "'"); - if (stat(file.string().c_str(), &sb) == -1) - throw LmsException("Failed to get stats on file '" + file.string() + "'" ); + return Wt::WDateTime::fromTime_t(sb.st_mtime); + } - return Wt::WDateTime::fromTime_t(sb.st_mtime); - } + bool exploreFilesRecursive(const std::filesystem::path& directory, std::function cb, const std::filesystem::path* excludeDirFileName) + { + std::error_code ec; + std::filesystem::directory_iterator itPath{ directory, std::filesystem::directory_options::follow_directory_symlink, ec }; - bool - exploreFilesRecursive(const std::filesystem::path& directory, std::function cb, const std::filesystem::path* excludeDirFileName) - { - std::error_code ec; - std::filesystem::directory_iterator itPath {directory, std::filesystem::directory_options::follow_directory_symlink, ec}; + if (ec) + { + cb(ec, directory); + return true; // try to continue exploring anyway + } - if (ec) - { - cb(ec, directory); - return true; // try to continue exploring anyway - } + if (excludeDirFileName && !excludeDirFileName->empty()) + { + const std::filesystem::path excludePath{ directory / *excludeDirFileName }; - if (excludeDirFileName && !excludeDirFileName->empty()) - { - const std::filesystem::path excludePath {directory / *excludeDirFileName}; + if (std::filesystem::exists(excludePath, ec)) + { + LMS_LOG(DBUPDATER, DEBUG, "Found '" << excludePath.string() << "': skipping directory"); + return true; + } + } - if (std::filesystem::exists(excludePath, ec)) - { - LMS_LOG(DBUPDATER, DEBUG) << "Found '" << excludePath.string() << "': skipping directory"; - return true; - } - } + std::filesystem::directory_iterator itEnd; + while (itPath != itEnd) + { + bool continueExploring{ true }; - std::filesystem::directory_iterator itEnd; - while (itPath != itEnd) - { - bool continueExploring {true}; + if (ec) + { + continueExploring = cb(ec, *itPath); + } + else + { + if (std::filesystem::is_regular_file(*itPath, ec)) + { + continueExploring = cb(ec, *itPath); + } + else if (std::filesystem::is_directory(*itPath, ec)) + { + if (!ec) + continueExploring = exploreFilesRecursive(*itPath, cb, excludeDirFileName); + else + continueExploring = cb(ec, *itPath); + } + } - if (ec) - { - continueExploring = cb(ec, *itPath); - } - else - { - if (std::filesystem::is_regular_file(*itPath, ec)) - { - continueExploring = cb(ec, *itPath); - } - else if (std::filesystem::is_directory(*itPath, ec)) - { - if (!ec) - continueExploring = exploreFilesRecursive(*itPath, cb, excludeDirFileName); - else - continueExploring = cb(ec, *itPath); - } - } + if (!continueExploring) + return false; - if (!continueExploring) - return false; + itPath.increment(ec); + } - itPath.increment(ec); - } + return true; + } - return true; - } + bool hasFileAnyExtension(const std::filesystem::path& file, const std::vector& supportedExtensions) + { + const std::filesystem::path extension{ StringUtils::stringToLower(file.extension().string()) }; - bool - hasFileAnyExtension(const std::filesystem::path& file, const std::vector& supportedExtensions) - { - const std::filesystem::path extension {StringUtils::stringToLower(file.extension().string())}; + return (std::find(std::cbegin(supportedExtensions), std::cend(supportedExtensions), extension) != std::cend(supportedExtensions)); + } - return (std::find(std::cbegin(supportedExtensions), std::cend(supportedExtensions), extension) != std::cend(supportedExtensions)); - } + bool isPathInRootPath(const std::filesystem::path& path, const std::filesystem::path& rootPath, const std::filesystem::path* excludeDirFileName) + { + std::filesystem::path curPath = path; - bool - isPathInRootPath(const std::filesystem::path& path, const std::filesystem::path& rootPath, const std::filesystem::path* excludeDirFileName) - { - std::filesystem::path curPath = path; + while (curPath.parent_path() != curPath) + { + curPath = curPath.parent_path(); - while (curPath.parent_path() != curPath) - { - curPath = curPath.parent_path(); + if (excludeDirFileName && !excludeDirFileName->empty()) + { + assert(!excludeDirFileName->has_parent_path()); - if (excludeDirFileName && !excludeDirFileName->empty()) - { - assert(!excludeDirFileName->has_parent_path()); - - std::error_code ec; - if (std::filesystem::exists(curPath / *excludeDirFileName, ec)) - return false; - } - - if (curPath == rootPath) - return true; - } - - return false; - } + std::error_code ec; + if (std::filesystem::exists(curPath / *excludeDirFileName, ec)) + return false; + } + if (curPath == rootPath) + return true; + } + return false; + } } // ns PathUtils diff --git a/src/libs/utils/impl/String.cpp b/src/libs/utils/impl/String.cpp index 836dfa22..02b1a59c 100644 --- a/src/libs/utils/impl/String.cpp +++ b/src/libs/utils/impl/String.cpp @@ -170,6 +170,22 @@ namespace StringUtils return res; } + std::string joinStrings(const std::vector& strings, std::string_view delimiter) + { + std::string res; + bool first{ true }; + + for (std::string_view str : strings) + { + if (!first) + res += delimiter; + res += str; + first = false; + } + + return res; + } + std::string joinStrings(const std::vector& strings, const std::string& delimiter) { return boost::algorithm::join(strings, delimiter); diff --git a/src/libs/utils/impl/WtLogger.cpp b/src/libs/utils/impl/WtLogger.cpp index ded7522e..f5f34660 100644 --- a/src/libs/utils/impl/WtLogger.cpp +++ b/src/libs/utils/impl/WtLogger.cpp @@ -21,10 +21,10 @@ #include #include -#include +#include #include -#include "utils/Logger.hpp" +#include "utils/Exception.hpp" namespace { @@ -36,6 +36,30 @@ namespace } } +WtLogger::WtLogger(Severity minSeverity) + : _minSeverity{ minSeverity } +{ +} + +std::string WtLogger::computeLogConfig(Severity minSeverity) +{ + switch (minSeverity) + { + case Severity::DEBUG: return "*"; + case Severity::INFO: return "* -debug"; + case Severity::WARNING: return "* -debug -info"; + case Severity::ERROR: return "* -debug -info -warning"; + case Severity::FATAL: return "* -debug -info -warning -error"; + } + + throw LmsException{ "Unhandled severity" }; +} + +bool WtLogger::isSeverityActive(Severity severity) const +{ + return static_cast(severity) <= static_cast(_minSeverity); +} + void WtLogger::processLog(const Log& log) { Wt::log(getSeverityName(log.getSeverity())) << Wt::WLogger::sep << to_string(std::this_thread::get_id()) << Wt::WLogger::sep << "[" << getModuleName(log.getModule()) << "]" << Wt::WLogger::sep << log.getMessage(); diff --git a/src/libs/utils/impl/http/SendQueue.cpp b/src/libs/utils/impl/http/SendQueue.cpp index 8d99fc72..91a4a01a 100644 --- a/src/libs/utils/impl/http/SendQueue.cpp +++ b/src/libs/utils/impl/http/SendQueue.cpp @@ -23,10 +23,10 @@ #include #include "utils/Exception.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/String.hpp" -#define LOG(sev) LMS_LOG(SCROBBLING, sev) << "[Http SendQueue] - " +#define LOG(sev, message) LMS_LOG(SCROBBLING, sev, "[Http SendQueue] - " << message) namespace StringUtils { @@ -98,7 +98,7 @@ namespace Http for (auto& [prio, requests] : _sendQueue) { - LOG(DEBUG) << "Processing prio " << static_cast(prio) << ", request count = " << requests.size(); + LOG(DEBUG, "Processing prio " << static_cast(prio) << ", request count = " << requests.size()); while (!requests.empty()) { std::unique_ptr request {std::move(requests.front())}; @@ -118,7 +118,7 @@ namespace Http SendQueue::sendRequest(const ClientRequest& request) { std::string url {_baseUrl + request.getParameters().relativeUrl}; - LOG(DEBUG) << "Sending request to url '" << url << "'"; + LOG(DEBUG, "Sending request to url '" << url << "'"); bool res {}; switch (request.getType()) @@ -133,7 +133,7 @@ namespace Http } if (!res) - LOG(ERROR) << "Send failed, bad url or unsupported scheme?"; + LOG(ERROR, "Send failed, bad url or unsupported scheme?"); return res; } @@ -143,14 +143,14 @@ namespace Http { if (ec == boost::asio::error::operation_aborted) { - LOG(DEBUG) << "Client aborted"; + LOG(DEBUG, "Client aborted"); return; } assert(_currentRequest); _state = State::Idle; - LOG(DEBUG) << "Client done. status = " << msg.status(); + LOG(DEBUG, "Client done. status = " << msg.status()); if (ec) onClientDoneError(std::move(_currentRequest), ec); else @@ -160,7 +160,7 @@ namespace Http void SendQueue::onClientDoneError(std::unique_ptr request, Wt::AsioWrapper::error_code ec) { - LOG(ERROR) << "Retry " << request->retryCount << ", client error: '" << ec.message() << "'"; + LOG(ERROR, "Retry " << request->retryCount << ", client error: '" << ec.message() << "'"); // may be a network error, try again later throttle(_defaultRetryWaitDuration); @@ -171,7 +171,7 @@ namespace Http } else { - LOG(ERROR) << "Too many retries, giving up operation and throttle"; + LOG(ERROR, "Too many retries, giving up operation and throttle"); if (request->getParameters().onFailureFunc) request->getParameters().onFailureFunc(); } @@ -189,7 +189,7 @@ namespace Http } const auto remainingCount {headerReadAs(msg, "X-RateLimit-Remaining")}; - LOG(DEBUG) << "Remaining messages = " << (remainingCount ? *remainingCount : 0); + LOG(DEBUG, "Remaining messages = " << (remainingCount ? *remainingCount : 0)); if (mustThrottle || (remainingCount && *remainingCount == 0)) { const auto waitDuration {headerReadAs(msg, "X-RateLimit-Reset-In")}; @@ -205,7 +205,7 @@ namespace Http } else { - LOG(ERROR) << "Send error: '" << msg.body() << "'"; + LOG(ERROR, "Send error: '" << msg.body() << "'"); if (requestParameters.onFailureFunc) requestParameters.onFailureFunc(); } @@ -221,14 +221,14 @@ namespace Http assert(_state == State::Idle); const std::chrono::seconds duration {clamp(requestedDuration, _minRetryWaitDuration, _maxRetryWaitDuration)}; - LOG(DEBUG) << "Throttling for " << duration.count() << " seconds"; + LOG(DEBUG, "Throttling for " << duration.count() << " seconds"); _throttleTimer.expires_after(duration); _throttleTimer.async_wait([this](const boost::system::error_code& ec) { if (ec == boost::asio::error::operation_aborted) { - LOG(DEBUG) << "Throttle aborted"; + LOG(DEBUG, "Throttle aborted"); return; } else if (ec) diff --git a/src/libs/utils/include/utils/Logger.hpp b/src/libs/utils/include/utils/ILogger.hpp similarity index 75% rename from src/libs/utils/include/utils/Logger.hpp rename to src/libs/utils/include/utils/ILogger.hpp index d14fe5ab..b6a334e3 100644 --- a/src/libs/utils/include/utils/Logger.hpp +++ b/src/libs/utils/include/utils/ILogger.hpp @@ -22,6 +22,7 @@ #include #include +#include "utils/String.hpp" #include "Service.hpp" enum class Severity @@ -59,11 +60,11 @@ enum class Module const char* getModuleName(Module mod); const char* getSeverityName(Severity sev); -class Logger; +class ILogger; class Log { public: - Log(Logger* logger, Module module, Severity severity); + Log(ILogger& logger, Module module, Severity severity); ~Log(); Module getModule() const { return _module; } @@ -76,18 +77,24 @@ private: Log(const Log&) = delete; Log& operator=(const Log&) = delete; + ILogger& _logger; Module _module; Severity _severity; std::ostringstream _oss; - Logger* _logger{}; }; -class Logger +class ILogger { public: - virtual ~Logger() = default; + virtual ~ILogger() = default; + + virtual bool isSeverityActive(Severity severity) const = 0; virtual void processLog(const Log& log) = 0; }; -#define LMS_LOG(module, severity) Log{Service::get(), Module::module, Severity::severity}.getOstream() -#define LMS_LOG_EX(module, severity) Log{Service::get(), module, severity}.getOstream() +#define LMS_LOG(module, severity, message) \ + do \ + { \ + if (auto* logger {::Service<::ILogger>::get()}; logger && logger->isSeverityActive(::Severity::severity)) \ + ::Log{ *logger, ::Module::module, ::Severity::severity }.getOstream() << message; \ + } while(0) diff --git a/src/libs/utils/include/utils/StreamLogger.hpp b/src/libs/utils/include/utils/StreamLogger.hpp index f41c7eb2..7ef757bd 100644 --- a/src/libs/utils/include/utils/StreamLogger.hpp +++ b/src/libs/utils/include/utils/StreamLogger.hpp @@ -20,19 +20,20 @@ #pragma once #include "utils/EnumSet.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" -class StreamLogger final : public Logger +class StreamLogger final : public ILogger { - public: - static constexpr EnumSet defaultSeverities {Severity::FATAL, Severity::ERROR, Severity::WARNING, Severity::INFO}; +public: + static constexpr EnumSet defaultSeverities{ Severity::FATAL, Severity::ERROR, Severity::WARNING, Severity::INFO }; - StreamLogger(std::ostream& oss, EnumSet severities = defaultSeverities); + StreamLogger(std::ostream& oss, EnumSet severities = defaultSeverities); - void processLog(const Log& log); + bool isSeverityActive(Severity) const override { return true; } + void processLog(const Log& log) override; - private: - std::ostream& _os; - const EnumSet _severities; +private: + std::ostream& _os; + const EnumSet _severities; }; diff --git a/src/libs/utils/include/utils/String.hpp b/src/libs/utils/include/utils/String.hpp index 9cf8970a..3712bb39 100644 --- a/src/libs/utils/include/utils/String.hpp +++ b/src/libs/utils/include/utils/String.hpp @@ -38,19 +38,16 @@ namespace Wt namespace StringUtils { [[nodiscard]] std::vector splitStringCopy(std::string_view string, std::string_view separators); - [[nodiscard]] std::vector splitString(std::string_view string, std::string_view separators); + [[nodiscard]] std::string joinStrings(const std::vector& strings, std::string_view delimiter); [[nodiscard]] std::string joinStrings(const std::vector& strings, const std::string& delimiter); [[nodiscard]] std::string_view stringTrim(std::string_view str, std::string_view whitespaces = " \t"); - [[nodiscard]] std::string_view stringTrimEnd(std::string_view str, std::string_view whitespaces = " \t"); [[nodiscard]] std::string stringToLower(std::string_view str); - void stringToLower(std::string& str); - [[nodiscard]] std::string stringToUpper(const std::string& str); [[nodiscard]] std::string bufferToString(const std::vector& data); diff --git a/src/libs/utils/include/utils/WtLogger.hpp b/src/libs/utils/include/utils/WtLogger.hpp index 65b94018..c3e80461 100644 --- a/src/libs/utils/include/utils/WtLogger.hpp +++ b/src/libs/utils/include/utils/WtLogger.hpp @@ -19,11 +19,19 @@ #pragma once -#include "Logger.hpp" +#include -class WtLogger final : public Logger +#include "utils/ILogger.hpp" + +class WtLogger final : public ILogger { - public: - void processLog(const Log& log) override; -}; +public: + WtLogger(Severity minSeverity); + static std::string computeLogConfig(Severity minSeverity); + +private: + bool isSeverityActive(Severity severity) const override; + void processLog(const Log& log) override; + const Severity _minSeverity; +}; \ No newline at end of file diff --git a/src/libs/utils/test/String.cpp b/src/libs/utils/test/String.cpp index abfb53c2..6b8dc78d 100644 --- a/src/libs/utils/test/String.cpp +++ b/src/libs/utils/test/String.cpp @@ -26,155 +26,182 @@ TEST(StringUtils, splitString) { - { - const std::string test{ "a" }; + { + const std::string test{ "a" }; - const std::vector strings{ StringUtils::splitString(test, "") }; - ASSERT_EQ(strings.size(), 1); - EXPECT_EQ(strings.front(), "a"); - } + const std::vector strings{ StringUtils::splitString(test, "") }; + ASSERT_EQ(strings.size(), 1); + EXPECT_EQ(strings.front(), "a"); + } - { - const std::string test{ "a b" }; + { + const std::string test{ "a b" }; - const std::vector strings{ StringUtils::splitString(test, "|") }; - ASSERT_EQ(strings.size(), 1); - EXPECT_EQ(strings.front(), "a b"); - } + const std::vector strings{ StringUtils::splitString(test, "|") }; + ASSERT_EQ(strings.size(), 1); + EXPECT_EQ(strings.front(), "a b"); + } - { - const std::string test{ " a" }; + { + const std::string test{ " a" }; - const std::vector strings{ StringUtils::splitString(test, " ") }; - ASSERT_EQ(strings.size(), 1); - EXPECT_EQ(strings.front(), "a"); - } + const std::vector strings{ StringUtils::splitString(test, " ") }; + ASSERT_EQ(strings.size(), 1); + EXPECT_EQ(strings.front(), "a"); + } - { - const std::string test{ "a " }; + { + const std::string test{ "a " }; - const std::vector strings{ StringUtils::splitString(test, " ") }; - ASSERT_EQ(strings.size(), 1); - EXPECT_EQ(strings.front(), "a"); - } + const std::vector strings{ StringUtils::splitString(test, " ") }; + ASSERT_EQ(strings.size(), 1); + EXPECT_EQ(strings.front(), "a"); + } - { - const std::string test{ "a b" }; + { + const std::string test{ "a b" }; - const std::vector strings{ StringUtils::splitString(test, " ") }; - ASSERT_EQ(strings.size(), 2); - EXPECT_EQ(strings.front(), "a"); - EXPECT_EQ(strings.back(), "b"); - } + const std::vector strings{ StringUtils::splitString(test, " ") }; + ASSERT_EQ(strings.size(), 2); + EXPECT_EQ(strings.front(), "a"); + EXPECT_EQ(strings.back(), "b"); + } - { - const std::string test{ "a b,c|defgh " }; + { + const std::string test{ "a b,c|defgh " }; - const std::vector strings{ StringUtils::splitString(test, " ,|") }; - ASSERT_EQ(strings.size(), 4); - EXPECT_EQ(strings[0], "a"); - EXPECT_EQ(strings[1], "b"); - EXPECT_EQ(strings[2], "c"); - EXPECT_EQ(strings[3], "defgh"); - } + const std::vector strings{ StringUtils::splitString(test, " ,|") }; + ASSERT_EQ(strings.size(), 4); + EXPECT_EQ(strings[0], "a"); + EXPECT_EQ(strings[1], "b"); + EXPECT_EQ(strings[2], "c"); + EXPECT_EQ(strings[3], "defgh"); + } } TEST(StringUtils, splitStringCopy) { - { - const std::string test{ "test=foo" }; + { + const std::string test{ "test=foo" }; - const std::vector strings{ StringUtils::splitStringCopy(test, "=") }; - ASSERT_EQ(strings.size(), 2); - EXPECT_EQ(strings[0], "test"); - EXPECT_EQ(strings[1], "foo"); - } + const std::vector strings{ StringUtils::splitStringCopy(test, "=") }; + ASSERT_EQ(strings.size(), 2); + EXPECT_EQ(strings[0], "test"); + EXPECT_EQ(strings[1], "foo"); + } - { - const std::string test{ "test=foo bar" }; + { + const std::string test{ "test=foo bar" }; - const std::vector strings{ StringUtils::splitStringCopy(test, "=") }; - ASSERT_EQ(strings.size(), 2); - EXPECT_EQ(strings[0], "test"); - EXPECT_EQ(strings[1], "foo bar"); - } + const std::vector strings{ StringUtils::splitStringCopy(test, "=") }; + ASSERT_EQ(strings.size(), 2); + EXPECT_EQ(strings[0], "test"); + EXPECT_EQ(strings[1], "foo bar"); + } +} + +TEST(StringUtils, joinStrings) +{ + struct TestCase + { + std::vector input; + std::string delimiter; + std::string expectedOutput; + }; + + TestCase tests[] + { + {{"a", "b", "c"}, "-", "a-b-c"}, + {{"a", "b", "c"}, ",", "a,b,c"}, + {{"a", "b", "c"}, "***", "a***b***c"}, + {{"a", "", "c"}, "-", "a--c"}, + {{"", "b", "c"}, "-", "-b-c"}, + {{"a"}, "-", "a"}, + {{"a"}, ",", "a"}, + }; + + for (const TestCase& test : tests) + { + const std::string str{ StringUtils::joinStrings(test.input, test.delimiter) }; + EXPECT_EQ(str, test.expectedOutput); + } } TEST(StringUtils, escapeJSString) { - EXPECT_EQ(StringUtils::jsEscape(""), ""); - EXPECT_EQ(StringUtils::jsEscape(R"(Test'.mp3)"), R"(Test\'.mp3)"); - EXPECT_EQ(StringUtils::jsEscape(R"(Test"".mp3)"), R"(Test\"\".mp3)"); - EXPECT_EQ(StringUtils::jsEscape(R"(\Test\.mp3)"), R"(\\Test\\.mp3)"); + EXPECT_EQ(StringUtils::jsEscape(""), ""); + EXPECT_EQ(StringUtils::jsEscape(R"(Test'.mp3)"), R"(Test\'.mp3)"); + EXPECT_EQ(StringUtils::jsEscape(R"(Test"".mp3)"), R"(Test\"\".mp3)"); + EXPECT_EQ(StringUtils::jsEscape(R"(\Test\.mp3)"), R"(\\Test\\.mp3)"); } TEST(StringUtils, escapeJsonString) { - EXPECT_EQ(StringUtils::jsonEscape(""), ""); - EXPECT_EQ(StringUtils::jsonEscape(R"(Test'.mp3)"), R"(Test'.mp3)"); - EXPECT_EQ(StringUtils::jsonEscape(R"(Test"".mp3)"), R"(Test\"\".mp3)"); - EXPECT_EQ(StringUtils::jsonEscape(R"(\Test\.mp3)"), R"(\\Test\\.mp3)"); + EXPECT_EQ(StringUtils::jsonEscape(""), ""); + EXPECT_EQ(StringUtils::jsonEscape(R"(Test'.mp3)"), R"(Test'.mp3)"); + EXPECT_EQ(StringUtils::jsonEscape(R"(Test"".mp3)"), R"(Test\"\".mp3)"); + EXPECT_EQ(StringUtils::jsonEscape(R"(\Test\.mp3)"), R"(\\Test\\.mp3)"); } TEST(StringUtils, escapeString) { - EXPECT_EQ(StringUtils::escapeString("", "*", ' '), ""); - EXPECT_EQ(StringUtils::escapeString("", "", ' '), ""); - EXPECT_EQ(StringUtils::escapeString("a", "", ' '), "a"); - EXPECT_EQ(StringUtils::escapeString("*", "*", '_'), "_*"); - EXPECT_EQ(StringUtils::escapeString("*a*", "*", '_'), "_*a_*"); - EXPECT_EQ(StringUtils::escapeString("*a|", "*|", '_'), "_*a_|"); - EXPECT_EQ(StringUtils::escapeString("**||", "*|", '_'), "_*_*_|_|"); + EXPECT_EQ(StringUtils::escapeString("", "*", ' '), ""); + EXPECT_EQ(StringUtils::escapeString("", "", ' '), ""); + EXPECT_EQ(StringUtils::escapeString("a", "", ' '), "a"); + EXPECT_EQ(StringUtils::escapeString("*", "*", '_'), "_*"); + EXPECT_EQ(StringUtils::escapeString("*a*", "*", '_'), "_*a_*"); + EXPECT_EQ(StringUtils::escapeString("*a|", "*|", '_'), "_*a_|"); + EXPECT_EQ(StringUtils::escapeString("**||", "*|", '_'), "_*_*_|_|"); } TEST(StringUtils, readAs) { - EXPECT_EQ(StringUtils::readAs("true"), true); - EXPECT_EQ(StringUtils::readAs("1"), true); - EXPECT_EQ(StringUtils::readAs("false"), false); - EXPECT_EQ(StringUtils::readAs("0"), false); - EXPECT_EQ(StringUtils::readAs("foo"), std::nullopt); - EXPECT_EQ(StringUtils::readAs(""), std::nullopt); + EXPECT_EQ(StringUtils::readAs("true"), true); + EXPECT_EQ(StringUtils::readAs("1"), true); + EXPECT_EQ(StringUtils::readAs("false"), false); + EXPECT_EQ(StringUtils::readAs("0"), false); + EXPECT_EQ(StringUtils::readAs("foo"), std::nullopt); + EXPECT_EQ(StringUtils::readAs(""), std::nullopt); } TEST(StringUtils, capitalize) { - struct TestCase - { - std::string input; - std::string expectedOutput; - }; + struct TestCase + { + std::string input; + std::string expectedOutput; + }; - TestCase tests[] - { - {"", ""}, - {"C", "C"}, - {"c", "C"}, - {" c", " C"}, - {" cc", " Cc"}, - {"(c", "(c"}, - {"1c", "1c"}, - {"&c", "&c"}, - {"c c", "C c"} - }; + TestCase tests[] + { + {"", ""}, + {"C", "C"}, + {"c", "C"}, + {" c", " C"}, + {" cc", " Cc"}, + {"(c", "(c"}, + {"1c", "1c"}, + {"&c", "&c"}, + {"c c", "C c"} + }; - for (const TestCase& test : tests) - { - std::string str{ test.input }; - StringUtils::capitalize(str); - EXPECT_EQ(str, test.expectedOutput) << " str was '" << test.input << "'"; - } + for (const TestCase& test : tests) + { + std::string str{ test.input }; + StringUtils::capitalize(str); + EXPECT_EQ(str, test.expectedOutput) << " str was '" << test.input << "'"; + } } TEST(Stringutils, date) { - const Wt::WDate date{ 2020, 01, 03 }; - EXPECT_EQ(StringUtils::toISO8601String(date), "2020-01-03"); + const Wt::WDate date{ 2020, 01, 03 }; + EXPECT_EQ(StringUtils::toISO8601String(date), "2020-01-03"); } TEST(Stringutils, dateTime) { - const Wt::WDateTime dateTime{ Wt::WDate {2020, 01, 03 }, Wt::WTime{9, 8, 11, 75} }; - EXPECT_EQ(StringUtils::toISO8601String(dateTime), "2020-01-03T09:08:11.075"); + const Wt::WDateTime dateTime{ Wt::WDate {2020, 01, 03 }, Wt::WTime{9, 8, 11, 75} }; + EXPECT_EQ(StringUtils::toISO8601String(dateTime), "2020-01-03T09:08:11.075"); } \ No newline at end of file diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 79a74114..2db7e143 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -47,140 +47,156 @@ #include "utils/String.hpp" #include "utils/WtLogger.hpp" -static -std::size_t -getThreadCount() +namespace { - const unsigned long configHttpServerThreadCount{ 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()); -} - -static -std::vector -generateWtConfig(std::string execPath) -{ - std::vector args; - - const std::filesystem::path wtConfigPath{ Service::get()->getPath("working-dir") / "wt_config.xml" }; - const std::filesystem::path wtLogFilePath{ Service::get()->getPath("log-file", "/var/log/lms.log") }; - const std::filesystem::path wtAccessLogFilePath{ Service::get()->getPath("access-log-file", "/var/log/lms.access.log") }; - const std::filesystem::path wtResourcesPath{ 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{ Service::get()->getString("docroot") }); - args.push_back("--approot=" + std::string{ Service::get()->getString("approot") }); - args.push_back("--deploy-path=" + std::string{ Service::get()->getString("deploy-path", "/") }); - if (!wtResourcesPath.empty()) - args.push_back("--resources-dir=" + wtResourcesPath.string()); - - if (Service::get()->getBool("tls-enable", false)) + std::size_t getThreadCount() { - args.push_back("--https-port=" + std::to_string(Service::get()->getULong("listen-port", 5082))); - args.push_back("--https-address=" + std::string{ Service::get()->getString("listen-addr", "0.0.0.0") }); - args.push_back("--ssl-certificate=" + std::string{ Service::get()->getString("tls-cert") }); - args.push_back("--ssl-private-key=" + std::string{ Service::get()->getString("tls-key") }); - args.push_back("--ssl-tmp-dh=" + std::string{ Service::get()->getString("tls-dh") }); - } - else - { - args.push_back("--http-port=" + std::to_string(Service::get()->getULong("listen-port", 5082))); - args.push_back("--http-address=" + std::string{ Service::get()->getString("listen-addr", "0.0.0.0") }); + const unsigned long configHttpServerThreadCount{ 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()); - pt.put("server.application-settings.log-config", Service::get()->getString("log-config", "* -debug -info:WebRequest")); - pt.put("server.application-settings.behind-reverse-proxy", Service::get()->getBool("behind-reverse-proxy", false)); - + 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{ Service::get()->getString("log-min-severity", "info") }; + + if (minSeverity == "debug") + return Severity::DEBUG; + else if (minSeverity == "info") + return Severity::INFO; + else if (minSeverity == "warning") + return Severity::WARNING; + else if (minSeverity == "error") + return Severity::ERROR; + else if (minSeverity == "fatal") + return Severity::FATAL; + + throw LmsException{ "Invalid config value for 'log-min-severity'" }; } - + std::vector generateWtConfig(std::string execPath, Severity minSeverity) { - std::ofstream oss{ wtConfigPath.string().c_str(), std::ios::out }; - if (!oss) - throw LmsException{ "Can't open '" + wtConfigPath.string() + "' for writing!" }; + std::vector args; - boost::property_tree::xml_parser::write_xml(oss, pt); + const std::filesystem::path wtConfigPath{ Service::get()->getPath("working-dir") / "wt_config.xml" }; + const std::filesystem::path wtLogFilePath{ Service::get()->getPath("log-file", "/var/log/lms.log") }; + const std::filesystem::path wtAccessLogFilePath{ Service::get()->getPath("access-log-file", "/var/log/lms.access.log") }; + const std::filesystem::path wtResourcesPath{ Service::get()->getPath("wt-resources", "/usr/share/Wt/resources") }; - if (!oss) - throw LmsException{ "Can't write in file '" + wtConfigPath.string() + "', no space left?" }; - } + args.push_back(execPath); + args.push_back("--config=" + wtConfigPath.string()); + args.push_back("--docroot=" + std::string{ Service::get()->getString("docroot") }); + args.push_back("--approot=" + std::string{ Service::get()->getString("approot") }); + args.push_back("--deploy-path=" + std::string{ Service::get()->getString("deploy-path", "/") }); + if (!wtResourcesPath.empty()) + args.push_back("--resources-dir=" + wtResourcesPath.string()); - return args; -} - - -static -void -proxyScannerEventsToApplication(Scanner::IScannerService& scanner, Wt::WServer& server) -{ - auto postAll{ [](Wt::WServer& server, std::function cb) - { - server.postAll([cb = std::move(cb)] + if (Service::get()->getBool("tls-enable", false)) { - // may be nullptr, see https://redmine.webtoolkit.eu/issues/8202 - if (LmsApp) - cb(); + args.push_back("--https-port=" + std::to_string(Service::get()->getULong("listen-port", 5082))); + args.push_back("--https-address=" + std::string{ Service::get()->getString("listen-addr", "0.0.0.0") }); + args.push_back("--ssl-certificate=" + std::string{ Service::get()->getString("tls-cert") }); + args.push_back("--ssl-private-key=" + std::string{ Service::get()->getString("tls-key") }); + args.push_back("--ssl-tmp-dh=" + std::string{ Service::get()->getString("tls-dh") }); + } + else + { + args.push_back("--http-port=" + std::to_string(Service::get()->getULong("listen-port", 5082))); + args.push_back("--http-address=" + std::string{ Service::get()->getString("listen-addr", "0.0.0.0") }); + } + + 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", WtLogger::computeLogConfig(minSeverity)); + pt.put("server.application-settings.behind-reverse-proxy", 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 LmsException{ "Can't open '" + wtConfigPath.string() + "' for writing!" }; + + boost::property_tree::xml_parser::write_xml(oss, pt); + + if (!oss) + throw 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().scanStarted.connect([&] + { + postAll(server, [] + { + LmsApp->getScannerEvents().scanStarted.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().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().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(); - }); - }); + scanner.getEvents().scanScheduled.connect([&](const Wt::WDateTime dateTime) + { + postAll(server, [=] + { + LmsApp->getScannerEvents().scanScheduled.emit(dateTime); + LmsApp->triggerUpdate(); + }); + }); + } } int main(int argc, char* argv[]) @@ -206,20 +222,21 @@ int main(int argc, char* argv[]) close(STDIN_FILENO); Service config{ createConfig(configFilePath) }; - Service logger{ std::make_unique() }; + const Severity minLogSeverity{getLogMinSeverity()}; + Service logger{ std::make_unique(minLogSeverity) }; // use system locale. libarchive relies on this to write filenames if (char* locale{ ::setlocale(LC_ALL, "") }) - LMS_LOG(MAIN, INFO) << "locale set to '" << locale << "'"; + LMS_LOG(MAIN, INFO, "locale set to '" << locale << "'"); else - LMS_LOG(MAIN, WARNING) << "Cannot set locale from system"; + LMS_LOG(MAIN, WARNING, "Cannot set locale from system"); // Make sure the working directory exists std::filesystem::create_directories(config->getPath("working-dir")); std::filesystem::create_directories(config->getPath("working-dir") / "cache"); // Construct WT configuration and get the argc/argv back - const std::vector wtServerArgs{ generateWtConfig(argv[0]) }; + const std::vector wtServerArgs{ generateWtConfig(argv[0], minLogSeverity) }; std::vector wtArgv(wtServerArgs.size()); for (std::size_t i = 0; i < wtServerArgs.size(); ++i) @@ -300,27 +317,27 @@ int main(int argc, char* argv[]) proxyScannerEventsToApplication(*scannerService, server); - LMS_LOG(MAIN, INFO) << "Starting server..."; + LMS_LOG(MAIN, INFO, "Starting server..."); server.start(); - LMS_LOG(MAIN, INFO) << "Now running..."; + LMS_LOG(MAIN, INFO, "Now running..."); Wt::WServer::waitForShutdown(); - LMS_LOG(MAIN, INFO) << "Stopping server..."; + LMS_LOG(MAIN, INFO, "Stopping server..."); server.stop(); - LMS_LOG(MAIN, INFO) << "Quitting..."; + LMS_LOG(MAIN, INFO, "Quitting..."); res = EXIT_SUCCESS; } catch (const Wt::WServer::Exception& e) { - LMS_LOG(MAIN, FATAL) << "Caught WServer::Exception: " << e.what(); + LMS_LOG(MAIN, FATAL, "Caught WServer::Exception: " << e.what()); std::cerr << "Caught a WServer::Exception: " << e.what() << std::endl; res = EXIT_FAILURE; } catch (const std::exception& e) { - LMS_LOG(MAIN, FATAL) << "Caught std::exception: " << e.what(); + LMS_LOG(MAIN, FATAL, "Caught std::exception: " << e.what()); std::cerr << "Caught std::exception: " << e.what() << std::endl; res = EXIT_FAILURE; } diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 47071201..2a536678 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -32,7 +32,7 @@ #include "services/auth/IPasswordService.hpp" #include "services/database/Session.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" #include "common/LoginNameValidator.hpp" diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 85d1fce2..47bcf481 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -37,7 +37,7 @@ #include "services/database/TrackList.hpp" #include "services/database/User.hpp" #include "services/scrobbling/IScrobblingService.hpp" -#include "utils/Logger.hpp" +#include "utils/ILogger.hpp" #include "utils/Service.hpp" #include "utils/String.hpp" @@ -62,534 +62,507 @@ #include "PlayQueue.hpp" #include "SettingsView.hpp" -namespace UserInterface { - -static -std::shared_ptr -createMessageResourceBundle() +namespace UserInterface { - const std::string appRoot {Wt::WApplication::appRoot()}; + namespace + { + constexpr const char* defaultPath{ "/releases" }; - auto res {std::make_shared()}; - res->use(appRoot + "admin-database"); - res->use(appRoot + "admin-initwizard"); - res->use(appRoot + "admin-scannercontroller"); - res->use(appRoot + "admin-user"); - res->use(appRoot + "admin-users"); - res->use(appRoot + "artist"); - res->use(appRoot + "artists"); - res->use(appRoot + "error"); - res->use(appRoot + "explore"); - res->use(appRoot + "login"); - res->use(appRoot + "main"); - res->use(appRoot + "mediaplayer"); - res->use(appRoot + "messages"); - res->use(appRoot + "misc"); - res->use(appRoot + "notifications"); - res->use(appRoot + "playqueue"); - res->use(appRoot + "release"); - res->use(appRoot + "releases"); - res->use(appRoot + "search"); - res->use(appRoot + "settings"); - res->use(appRoot + "tracklist"); - res->use(appRoot + "tracklists"); - res->use(appRoot + "tracks"); + std::shared_ptr createMessageResourceBundle() + { + const std::string appRoot{ Wt::WApplication::appRoot() }; - return res; -} + auto res{ std::make_shared() }; + res->use(appRoot + "admin-database"); + res->use(appRoot + "admin-initwizard"); + res->use(appRoot + "admin-scannercontroller"); + res->use(appRoot + "admin-user"); + res->use(appRoot + "admin-users"); + res->use(appRoot + "artist"); + res->use(appRoot + "artists"); + res->use(appRoot + "error"); + res->use(appRoot + "explore"); + res->use(appRoot + "login"); + res->use(appRoot + "main"); + res->use(appRoot + "mediaplayer"); + res->use(appRoot + "messages"); + res->use(appRoot + "misc"); + res->use(appRoot + "notifications"); + res->use(appRoot + "playqueue"); + res->use(appRoot + "release"); + res->use(appRoot + "releases"); + res->use(appRoot + "search"); + res->use(appRoot + "settings"); + res->use(appRoot + "tracklist"); + res->use(appRoot + "tracklists"); + res->use(appRoot + "tracks"); -static -std::shared_ptr -getOrCreateMessageBundle() -{ - static std::shared_ptr res {createMessageResourceBundle()}; - return res; -} + return res; + } -static constexpr const char* defaultPath {"/releases"}; + std::shared_ptr getOrCreateMessageBundle() + { + static std::shared_ptr res{ createMessageResourceBundle() }; + return res; + } -std::unique_ptr -LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager) -{ - if (auto *authEnvService {Service<::Auth::IEnvService>::get()}) - { - const auto checkResult {authEnvService->processEnv(env)}; - if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted) - { - LMS_LOG(UI, ERROR) << "Cannot authenticate user from environment!"; - // return a blank page - return std::make_unique(env); - } + enum IdxRoot + { + IdxExplore = 0, + IdxPlayQueue, + IdxSettings, + IdxAdminDatabase, + IdxAdminUsers, + IdxAdminUser, + }; - return std::make_unique(env, db, appManager, checkResult.userId); - } + void handlePathChange(Wt::WStackedWidget& stack, bool isAdmin) + { + static const struct + { + std::string path; + int index; + bool admin; + std::optional title; + } views[] = + { + { "/artists", IdxExplore, false, Wt::WString::tr("Lms.Explore.artists") }, + { "/artist", IdxExplore, false, std::nullopt }, + { "/releases", IdxExplore, false, Wt::WString::tr("Lms.Explore.releases") }, + { "/release", IdxExplore, false, std::nullopt }, + { "/search", IdxExplore, false, Wt::WString::tr("Lms.Explore.search") }, + { "/tracks", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracks") }, + { "/tracklists", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracklists") }, + { "/tracklist", IdxExplore, false, std::nullopt }, + { "/playqueue", IdxPlayQueue, false, Wt::WString::tr("Lms.PlayQueue.playqueue") }, + { "/settings", IdxSettings, false, Wt::WString::tr("Lms.Settings.settings") }, + { "/admin/database", IdxAdminDatabase, true, Wt::WString::tr("Lms.Admin.Database.database") }, + { "/admin/users", IdxAdminUsers, true, Wt::WString::tr("Lms.Admin.Users.users") }, + { "/admin/user", IdxAdminUser, true, std::nullopt }, + }; - return std::make_unique(env, db, appManager); -} + LMS_LOG(UI, DEBUG, "Internal path changed to '" << wApp->internalPath() << "'"); -LmsApplication* -LmsApplication::instance() -{ - return reinterpret_cast(Wt::WApplication::instance()); -} + for (const auto& view : views) + { + if (wApp->internalPathMatches(view.path)) + { + if (view.admin && !isAdmin) + break; -Database::Db& -LmsApplication::getDb() -{ - return _db; -} + stack.setCurrentIndex(view.index); + if (view.title) + LmsApp->setTitle(*view.title); -Database::Session& -LmsApplication::getDbSession() -{ - return _db.getTLSSession(); -} + LmsApp->doJavaScript(LmsApp->javaScriptClass() + ".updateActiveNav('" + view.path + "')"); + return; + } + } -Database::User::pointer -LmsApplication::getUser() -{ - if (!_authenticatedUser) - return {}; + wApp->setInternalPath(defaultPath, true); + } + } - return Database::User::find(getDbSession(), _authenticatedUser->userId); -} + std::unique_ptr LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager) + { + if (auto * authEnvService{ Service<::Auth::IEnvService>::get() }) + { + const auto checkResult{ authEnvService->processEnv(env) }; + if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted) + { + LMS_LOG(UI, ERROR, "Cannot authenticate user from environment!"); + // return a blank page + return std::make_unique(env); + } -Database::UserId -LmsApplication::getUserId() -{ - return _authenticatedUser->userId; -} + return std::make_unique(env, db, appManager, checkResult.userId); + } -bool -LmsApplication::isUserAuthStrong() const -{ - return _authenticatedUser->strongAuth; -} + return std::make_unique(env, db, appManager); + } -Database::UserType -LmsApplication::getUserType() -{ - auto transaction {getDbSession().createReadTransaction()}; + LmsApplication* LmsApplication::instance() + { + return static_cast(Wt::WApplication::instance()); + } - return getUser()->getType(); -} + Database::Db& LmsApplication::getDb() + { + return _db; + } -std::string -LmsApplication::getUserLoginName() -{ - auto transaction {getDbSession().createReadTransaction()}; + Database::Session& LmsApplication::getDbSession() + { + return _db.getTLSSession(); + } - return getUser()->getLoginName(); -} + Database::User::pointer LmsApplication::getUser() + { + if (!_authenticatedUser) + return {}; -LmsApplication::LmsApplication(const Wt::WEnvironment& env, - Database::Db& db, - LmsApplicationManager& appManager, - std::optional userId) -: Wt::WApplication {env} -, _db {db} -, _appManager {appManager} -, _authenticatedUser {userId ? std::make_optional(UserAuthInfo {*userId, false}) : std::nullopt} -{ - try - { - init(); - } - catch (LmsApplicationException& e) - { - LMS_LOG(UI, WARNING) << "Caught a LmsApplication exception: " << e.what(); - handleException(e); - } - catch (std::exception& e) - { - LMS_LOG(UI, ERROR) << "Caught exception: " << e.what(); - throw LmsException {"Internal error"}; // Do not put details here at it may appear on the user rendered html - } -} + return Database::User::find(getDbSession(), _authenticatedUser->userId); + } -LmsApplication::~LmsApplication() = default; + Database::UserId LmsApplication::getUserId() + { + return _authenticatedUser->userId; + } -void -LmsApplication::init() -{ - setTheme(std::make_shared()); + bool LmsApplication::isUserAuthStrong() const + { + return _authenticatedUser->strongAuth; + } - useStyleSheet("resources/font-awesome/css/font-awesome.min.css"); - require("js/mediaplayer.js"); + Database::UserType LmsApplication::getUserType() + { + auto transaction{ getDbSession().createReadTransaction() }; - setTitle(); - setLocalizedStrings(getOrCreateMessageBundle()); + return getUser()->getType(); + } - // Handle Media Scanner events and other session events - enableUpdates(true); + std::string LmsApplication::getUserLoginName() + { + auto transaction{ getDbSession().createReadTransaction() }; - if (_authenticatedUser) - onUserLoggedIn(); - else if (Service<::Auth::IPasswordService>::exists()) - processPasswordAuth(); -} + return getUser()->getLoginName(); + } -void -LmsApplication::processPasswordAuth() -{ - { - std::optional userId {processAuthToken(environment())}; - if (userId) - { - LMS_LOG(UI, DEBUG) << "User authenticated using Auth token!"; - _authenticatedUser = {*userId, false}; - onUserLoggedIn(); - return; - } - } + LmsApplication::LmsApplication(const Wt::WEnvironment& env, + Database::Db& db, + LmsApplicationManager& appManager, + std::optional userId) + : Wt::WApplication{ env } + , _db{ db } + , _appManager{ appManager } + , _authenticatedUser{ userId ? std::make_optional(UserAuthInfo {*userId, false}) : std::nullopt } + { + try + { + init(); + } + catch (LmsApplicationException& e) + { + LMS_LOG(UI, WARNING, "Caught a LmsApplication exception: " << e.what()); + handleException(e); + } + catch (std::exception& e) + { + LMS_LOG(UI, ERROR, "Caught exception: " << e.what()); + throw LmsException{ "Internal error" }; // Do not put details here at it may appear on the user rendered html + } + } - // If here is no account in the database, launch the first connection wizard - bool firstConnection {}; - { - auto transaction {getDbSession().createReadTransaction()}; - firstConnection = Database::User::getCount(getDbSession()) == 0; - } + LmsApplication::~LmsApplication() = default; - LMS_LOG(UI, DEBUG) << "Creating root widget. First connection = " << firstConnection; + void LmsApplication::init() + { + setTheme(std::make_shared()); - if (firstConnection && Service<::Auth::IPasswordService>::get()->canSetPasswords()) - { - root()->addWidget(std::make_unique()); - } - else - { - Auth* auth {root()->addNew()}; - auth->userLoggedIn.connect(this, [this](Database::UserId userId) - { - _authenticatedUser = {userId, true}; - onUserLoggedIn(); - }); - } -} + useStyleSheet("resources/font-awesome/css/font-awesome.min.css"); + require("js/mediaplayer.js"); -void -LmsApplication::finalize() -{ - if (_authenticatedUser) - _appManager.unregisterApplication(*this); + setTitle(); + setLocalizedStrings(getOrCreateMessageBundle()); - preQuit().emit(); -} + // Handle Media Scanner events and other session events + enableUpdates(true); -void -LmsApplication::handleException(LmsApplicationException& e) -{ - root()->clear(); - Wt::WTemplate* t {root()->addNew(Wt::WString::tr("Lms.Error.template"))}; - t->addFunction("tr", &Wt::WTemplate::Functions::tr); + if (_authenticatedUser) + onUserLoggedIn(); + else if (Service<::Auth::IPasswordService>::exists()) + processPasswordAuth(); + } - t->bindString("error", e.what(), Wt::TextFormat::Plain); - Wt::WPushButton* btn {t->bindNew("btn-go-home", Wt::WString::tr("Lms.Error.go-home"))}; - btn->clicked().connect([this]() - { - redirect(defaultPath); - }); -} + void LmsApplication::processPasswordAuth() + { + { + std::optional userId{ processAuthToken(environment()) }; + if (userId) + { + LMS_LOG(UI, DEBUG, "User authenticated using Auth token!"); + _authenticatedUser = { *userId, false }; + onUserLoggedIn(); + return; + } + } -void -LmsApplication::goHomeAndQuit() -{ - WApplication::quit(""); - redirect("."); -} + // If here is no account in the database, launch the first connection wizard + bool firstConnection{}; + { + auto transaction{ getDbSession().createReadTransaction() }; + firstConnection = Database::User::getCount(getDbSession()) == 0; + } -enum IdxRoot -{ - IdxExplore = 0, - IdxPlayQueue, - IdxSettings, - IdxAdminDatabase, - IdxAdminUsers, - IdxAdminUser, -}; + LMS_LOG(UI, DEBUG, "Creating root widget. First connection = " << firstConnection); -static -void -handlePathChange(Wt::WStackedWidget& stack, bool isAdmin) -{ - static const struct - { - std::string path; - int index; - bool admin; - std::optional title; - } views[] = - { - { "/artists", IdxExplore, false, Wt::WString::tr("Lms.Explore.artists") }, - { "/artist", IdxExplore, false, std::nullopt }, - { "/releases", IdxExplore, false, Wt::WString::tr("Lms.Explore.releases") }, - { "/release", IdxExplore, false, std::nullopt }, - { "/search", IdxExplore, false, Wt::WString::tr("Lms.Explore.search") }, - { "/tracks", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracks") }, - { "/tracklists", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracklists") }, - { "/tracklist", IdxExplore, false, std::nullopt }, - { "/playqueue", IdxPlayQueue, false, Wt::WString::tr("Lms.PlayQueue.playqueue") }, - { "/settings", IdxSettings, false, Wt::WString::tr("Lms.Settings.settings") }, - { "/admin/database", IdxAdminDatabase, true, Wt::WString::tr("Lms.Admin.Database.database") }, - { "/admin/users", IdxAdminUsers, true, Wt::WString::tr("Lms.Admin.Users.users") }, - { "/admin/user", IdxAdminUser, true, std::nullopt }, - }; + if (firstConnection && Service<::Auth::IPasswordService>::get()->canSetPasswords()) + { + root()->addWidget(std::make_unique()); + } + else + { + Auth* auth{ root()->addNew() }; + auth->userLoggedIn.connect(this, [this](Database::UserId userId) + { + _authenticatedUser = { userId, true }; + onUserLoggedIn(); + }); + } + } - LMS_LOG(UI, DEBUG) << "Internal path changed to '" << wApp->internalPath() << "'"; + void LmsApplication::finalize() + { + if (_authenticatedUser) + _appManager.unregisterApplication(*this); - for (const auto& view : views) - { - if (wApp->internalPathMatches(view.path)) - { - if (view.admin && !isAdmin) - break; + preQuit().emit(); + } - stack.setCurrentIndex(view.index); - if (view.title) - LmsApp->setTitle(*view.title); + void LmsApplication::handleException(LmsApplicationException& e) + { + root()->clear(); + Wt::WTemplate* t{ root()->addNew(Wt::WString::tr("Lms.Error.template")) }; + t->addFunction("tr", &Wt::WTemplate::Functions::tr); - LmsApp->doJavaScript(LmsApp->javaScriptClass() + ".updateActiveNav('" + view.path +"')"); - return; - } - } + t->bindString("error", e.what(), Wt::TextFormat::Plain); + Wt::WPushButton* btn{ t->bindNew("btn-go-home", Wt::WString::tr("Lms.Error.go-home")) }; + btn->clicked().connect([this]() + { + redirect(defaultPath); + }); + } - wApp->setInternalPath(defaultPath, true); -} + void LmsApplication::goHomeAndQuit() + { + WApplication::quit(""); + redirect("."); + } -void -LmsApplication::logoutUser() -{ - { - auto transaction {getDbSession().createWriteTransaction()}; - getUser().modify()->clearAuthTokens(); - } + void LmsApplication::logoutUser() + { + { + auto transaction{ getDbSession().createWriteTransaction() }; + getUser().modify()->clearAuthTokens(); + } - LMS_LOG(UI, INFO) << "User '" << getUserLoginName() << " 'logged out"; - goHomeAndQuit(); -} + LMS_LOG(UI, INFO, "User '" << getUserLoginName() << " 'logged out"); + goHomeAndQuit(); + } -void -LmsApplication::onUserLoggedIn() -{ - root()->clear(); + void LmsApplication::onUserLoggedIn() + { + root()->clear(); - LMS_LOG(UI, INFO) << "User '" << getUserLoginName() << "' logged in from '" << environment().clientAddress() << "', user agent = " << environment().userAgent(); + LMS_LOG(UI, INFO, "User '" << getUserLoginName() << "' logged in from '" << environment().clientAddress() << "', user agent = " << environment().userAgent()); - _appManager.registerApplication(*this); - _appManager.applicationRegistered.connect(this, [this] (LmsApplication& otherApplication) - { - // Only one active session by user - if (otherApplication.getUserId() == getUserId()) - { - if (LmsApp->getUserType() != Database::UserType::DEMO) - { - quit(Wt::WString::tr("Lms.quit-other-session")); - } - } - }); + _appManager.registerApplication(*this); + _appManager.applicationRegistered.connect(this, [this](LmsApplication& otherApplication) + { + // Only one active session by user + if (otherApplication.getUserId() == getUserId()) + { + if (LmsApp->getUserType() != Database::UserType::DEMO) + { + quit(Wt::WString::tr("Lms.quit-other-session")); + } + } + }); - createHome(); -} + createHome(); + } -void -LmsApplication::createHome() -{ - _coverResource = std::make_shared(); + void LmsApplication::createHome() + { + _coverResource = std::make_shared(); - declareJavaScriptFunction("onLoadCover", "function(id) { id.className += \" Lms-cover-loaded\"}"); - declareJavaScriptFunction("updateActiveNav", -R"(function(current) { - const menuItems = document.querySelectorAll('.nav-item a[href]:not([href=""])'); + declareJavaScriptFunction("onLoadCover", "function(id) { id.className += \" Lms-cover-loaded\"}"); + declareJavaScriptFunction("updateActiveNav", + R"(function(current) { + const menuItems = document.querySelectorAll('.nav-item a[href]:not([href=""])'); for (const menuItem of menuItems) { if (menuItem.getAttribute("href") === current) { menuItem.classList.add('active'); } - else { + else { menuItem.classList.remove('active'); - } + } } })"); - Wt::WTemplate* main {root()->addWidget(std::make_unique(Wt::WString::tr("Lms.main.template")))}; - main->addFunction("tr", &Wt::WTemplate::Functions::tr); + Wt::WTemplate* main{ root()->addWidget(std::make_unique(Wt::WString::tr("Lms.main.template"))) }; + main->addFunction("tr", &Wt::WTemplate::Functions::tr); - Template* navbar {main->bindNew