From b15a7fa1cd3a0cff18d4d3e62db1c7b8bfc6f29f Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 29 Sep 2021 09:23:37 +0200 Subject: [PATCH 01/49] Scanner: fixed some values not updated when scanned again --- src/libs/database/include/database/Track.hpp | 4 ++-- src/libs/scanner/impl/Scanner.cpp | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/database/include/database/Track.hpp index 1e193405..0d78c627 100644 --- a/src/libs/database/include/database/Track.hpp +++ b/src/libs/database/include/database/Track.hpp @@ -109,8 +109,8 @@ class Track : public Object void setRecordingMBID(const std::optional& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; } void setCopyright(const std::string& copyright) { _copyright = std::string(copyright, 0, _maxCopyrightLength); } void setCopyrightURL(const std::string& copyrightURL) { _copyrightURL = std::string(copyrightURL, 0, _maxCopyrightURLLength); } - void setTrackReplayGain(float replayGain) { _trackReplayGain = replayGain; } - void setReleaseReplayGain(float replayGain) { _releaseReplayGain = replayGain; } + void setTrackReplayGain(std::optional replayGain) { _trackReplayGain = replayGain; } + void setReleaseReplayGain(std::optional replayGain) { _releaseReplayGain = replayGain; } void clearArtistLinks(); void addArtistLink(const ObjectPtr& artistLink); void setRelease(ObjectPtr release) { _release = getDboPtr(release); } diff --git a/src/libs/scanner/impl/Scanner.cpp b/src/libs/scanner/impl/Scanner.cpp index bbc10fda..125324dc 100644 --- a/src/libs/scanner/impl/Scanner.cpp +++ b/src/libs/scanner/impl/Scanner.cpp @@ -806,6 +806,8 @@ Scanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanSt track.modify()->setScanVersion(_scanVersion); if (trackInfo->album) track.modify()->setRelease(getOrCreateRelease(_dbSession, *trackInfo->album)); + else + track.modify()->setRelease({}); track.modify()->setClusters(getOrCreateClusters(_dbSession, trackInfo->clusters)); track.modify()->setLastWriteTime(lastWriteTime); track.modify()->setName(title); @@ -815,8 +817,7 @@ Scanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanSt track.modify()->setDiscNumber(trackInfo->discNumber ? *trackInfo->discNumber : 0); track.modify()->setTotalTrack(trackInfo->totalTrack); track.modify()->setTotalDisc(trackInfo->totalDisc); - if (!trackInfo->discSubtitle.empty()) - track.modify()->setDiscSubtitle(trackInfo->discSubtitle); + track.modify()->setDiscSubtitle(trackInfo->discSubtitle); track.modify()->setDate(trackInfo->date); track.modify()->setOriginalDate(trackInfo->originalDate); @@ -830,10 +831,8 @@ Scanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanSt track.modify()->setHasCover(trackInfo->hasCover); track.modify()->setCopyright(trackInfo->copyright); track.modify()->setCopyrightURL(trackInfo->copyrightURL); - if (trackInfo->trackReplayGain) - track.modify()->setTrackReplayGain(*trackInfo->trackReplayGain); - if (trackInfo->albumReplayGain) - track.modify()->setReleaseReplayGain(*trackInfo->albumReplayGain); + track.modify()->setTrackReplayGain(trackInfo->trackReplayGain); + track.modify()->setReleaseReplayGain(trackInfo->albumReplayGain); } void From 744f5f8017748d18ad4978e4c28d416a20bb4ea4 Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 29 Sep 2021 09:26:59 +0200 Subject: [PATCH 02/49] Fixed row height to make them stable on hover --- docroot/css/lms.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docroot/css/lms.css b/docroot/css/lms.css index c2ff47ac..cf818f88 100644 --- a/docroot/css/lms.css +++ b/docroot/css/lms.css @@ -184,7 +184,7 @@ a.Lms-artistname:hover, a.Lms-artistname:focus { } .Lms-explore-release-entry { - min-height: 34px; + min-height: 36px; } .Lms-explore-release-entry:hover .Lms-explore-release-entry-controls { From 75036b768e325b4ce9b74b4cbd9fa2482e663a6e Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 29 Sep 2021 09:31:39 +0200 Subject: [PATCH 03/49] Removed bad copy/paste --- src/libs/recommendation/impl/features/FeaturesDefs.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libs/recommendation/impl/features/FeaturesDefs.cpp b/src/libs/recommendation/impl/features/FeaturesDefs.cpp index b3de7dcb..1982b683 100644 --- a/src/libs/recommendation/impl/features/FeaturesDefs.cpp +++ b/src/libs/recommendation/impl/features/FeaturesDefs.cpp @@ -93,15 +93,6 @@ static const std::unordered_map featureDefinitions { "lowlevel.dissonance.min", {1}}, { "lowlevel.dissonance.var", {1}}, { "lowlevel.dynamic_complexity", {1}}, - { "lowlevel.spectral_contrast_coeffs.dmean", {6}}, - { "lowlevel.spectral_contrast_coeffs.dmean2", {6}}, - { "lowlevel.spectral_contrast_coeffs.dvar", {6}}, - { "lowlevel.spectral_contrast_coeffs.dvar2", {6}}, - { "lowlevel.spectral_contrast_coeffs.max", {6}}, - { "lowlevel.spectral_contrast_coeffs.mean", {6}}, - { "lowlevel.spectral_contrast_coeffs.median", {6}}, - { "lowlevel.spectral_contrast_coeffs.min", {6}}, - { "lowlevel.spectral_contrast_coeffs.var", {6}}, { "lowlevel.erbbands.dmean", {40}}, { "lowlevel.erbbands.dmean2", {40}}, { "lowlevel.erbbands.dvar", {40}}, From 6a8063640beb385c80cfcfdd2e298a17f414428c Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 29 Sep 2021 09:39:21 +0200 Subject: [PATCH 04/49] Minor cleanup in the generated dom --- src/lms/ui/PlayQueue.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 846069b0..f07501fe 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -409,6 +409,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) Wt::WTemplate* entry = _entriesContainer->addNew(Wt::WString::tr("Lms.PlayQueue.template.entry")); + entry->bindString("is-selected", ""); entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain); const auto artists {track->getArtists({Database::TrackArtistLinkType::Artist})}; From 4e9cf0d77fd177a5809b0dda8ecde1fc7c9b55e2 Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 29 Sep 2021 11:57:11 +0200 Subject: [PATCH 05/49] Moved unit tests close to the sources --- src/CMakeLists.txt | 3 --- src/libs/database/CMakeLists.txt | 4 +++- src/{test/database => libs/database/test}/Artist.cpp | 0 src/{test/database => libs/database/test}/CMakeLists.txt | 0 src/{test/database => libs/database/test}/Cluster.cpp | 0 src/{test/database => libs/database/test}/Common.hpp | 0 src/{test/database => libs/database/test}/DatabaseTest.cpp | 0 src/{test/database => libs/database/test}/Release.cpp | 0 src/{test/database => libs/database/test}/Track.cpp | 0 src/libs/som/CMakeLists.txt | 5 ++++- src/{test/som => libs/som/test}/CMakeLists.txt | 0 src/{test/som => libs/som/test}/SomTest.cpp | 0 src/libs/utils/CMakeLists.txt | 4 +++- src/{test/utils => libs/utils/test}/CMakeLists.txt | 0 src/{test/utils => libs/utils/test}/RecursiveSharedMutex.cpp | 0 src/{test/utils => libs/utils/test}/String.cpp | 0 src/{test/utils => libs/utils/test}/Utils.cpp | 0 src/test/CMakeLists.txt | 5 ----- 18 files changed, 10 insertions(+), 11 deletions(-) rename src/{test/database => libs/database/test}/Artist.cpp (100%) rename src/{test/database => libs/database/test}/CMakeLists.txt (100%) rename src/{test/database => libs/database/test}/Cluster.cpp (100%) rename src/{test/database => libs/database/test}/Common.hpp (100%) rename src/{test/database => libs/database/test}/DatabaseTest.cpp (100%) rename src/{test/database => libs/database/test}/Release.cpp (100%) rename src/{test/database => libs/database/test}/Track.cpp (100%) rename src/{test/som => libs/som/test}/CMakeLists.txt (100%) rename src/{test/som => libs/som/test}/SomTest.cpp (100%) rename src/{test/utils => libs/utils/test}/CMakeLists.txt (100%) rename src/{test/utils => libs/utils/test}/RecursiveSharedMutex.cpp (100%) rename src/{test/utils => libs/utils/test}/String.cpp (100%) rename src/{test/utils => libs/utils/test}/Utils.cpp (100%) delete mode 100644 src/test/CMakeLists.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c796f0e9..79236cc5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,8 +4,5 @@ add_subdirectory(libs) add_subdirectory(lms) add_subdirectory(tools) -if(BUILD_TESTING) - add_subdirectory(test) -endif() diff --git a/src/libs/database/CMakeLists.txt b/src/libs/database/CMakeLists.txt index cdf1288f..38b13e75 100644 --- a/src/libs/database/CMakeLists.txt +++ b/src/libs/database/CMakeLists.txt @@ -1,4 +1,3 @@ - add_library(lmsdatabase SHARED impl/Artist.cpp impl/Cluster.cpp @@ -36,3 +35,6 @@ target_link_libraries(lmsdatabase PUBLIC install(TARGETS lmsdatabase DESTINATION lib) +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/test/database/Artist.cpp b/src/libs/database/test/Artist.cpp similarity index 100% rename from src/test/database/Artist.cpp rename to src/libs/database/test/Artist.cpp diff --git a/src/test/database/CMakeLists.txt b/src/libs/database/test/CMakeLists.txt similarity index 100% rename from src/test/database/CMakeLists.txt rename to src/libs/database/test/CMakeLists.txt diff --git a/src/test/database/Cluster.cpp b/src/libs/database/test/Cluster.cpp similarity index 100% rename from src/test/database/Cluster.cpp rename to src/libs/database/test/Cluster.cpp diff --git a/src/test/database/Common.hpp b/src/libs/database/test/Common.hpp similarity index 100% rename from src/test/database/Common.hpp rename to src/libs/database/test/Common.hpp diff --git a/src/test/database/DatabaseTest.cpp b/src/libs/database/test/DatabaseTest.cpp similarity index 100% rename from src/test/database/DatabaseTest.cpp rename to src/libs/database/test/DatabaseTest.cpp diff --git a/src/test/database/Release.cpp b/src/libs/database/test/Release.cpp similarity index 100% rename from src/test/database/Release.cpp rename to src/libs/database/test/Release.cpp diff --git a/src/test/database/Track.cpp b/src/libs/database/test/Track.cpp similarity index 100% rename from src/test/database/Track.cpp rename to src/libs/database/test/Track.cpp diff --git a/src/libs/som/CMakeLists.txt b/src/libs/som/CMakeLists.txt index 4c967429..938e835a 100644 --- a/src/libs/som/CMakeLists.txt +++ b/src/libs/som/CMakeLists.txt @@ -1,4 +1,3 @@ - add_library(lmssom SHARED impl/DataNormalizer.cpp impl/Network.cpp @@ -19,3 +18,7 @@ target_link_libraries(lmssom PUBLIC set_property(TARGET lmssom PROPERTY POSITION_INDEPENDENT_CODE ON) install(TARGETS lmssom DESTINATION lib) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/test/som/CMakeLists.txt b/src/libs/som/test/CMakeLists.txt similarity index 100% rename from src/test/som/CMakeLists.txt rename to src/libs/som/test/CMakeLists.txt diff --git a/src/test/som/SomTest.cpp b/src/libs/som/test/SomTest.cpp similarity index 100% rename from src/test/som/SomTest.cpp rename to src/libs/som/test/SomTest.cpp diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index f6ef50b7..34744ea7 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -1,4 +1,3 @@ - add_library(lmsutils SHARED impl/ChildProcess.cpp impl/ChildProcessManager.cpp @@ -37,3 +36,6 @@ target_link_libraries(lmsutils PUBLIC install(TARGETS lmsutils DESTINATION lib) +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/test/utils/CMakeLists.txt b/src/libs/utils/test/CMakeLists.txt similarity index 100% rename from src/test/utils/CMakeLists.txt rename to src/libs/utils/test/CMakeLists.txt diff --git a/src/test/utils/RecursiveSharedMutex.cpp b/src/libs/utils/test/RecursiveSharedMutex.cpp similarity index 100% rename from src/test/utils/RecursiveSharedMutex.cpp rename to src/libs/utils/test/RecursiveSharedMutex.cpp diff --git a/src/test/utils/String.cpp b/src/libs/utils/test/String.cpp similarity index 100% rename from src/test/utils/String.cpp rename to src/libs/utils/test/String.cpp diff --git a/src/test/utils/Utils.cpp b/src/libs/utils/test/Utils.cpp similarity index 100% rename from src/test/utils/Utils.cpp rename to src/libs/utils/test/Utils.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt deleted file mode 100644 index ea5a1fe2..00000000 --- a/src/test/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ - -add_subdirectory(database) -add_subdirectory(som) -add_subdirectory(utils) - From b4d8633bc1a052f1907605fdc5ca96a0a9897bb5 Mon Sep 17 00:00:00 2001 From: danoloan10 Date: Wed, 29 Sep 2021 18:27:42 +0200 Subject: [PATCH 06/49] Exclude gtests when cross-compiling --- src/libs/database/test/CMakeLists.txt | 4 +++- src/libs/metadata/test/CMakeLists.txt | 4 +++- src/libs/som/test/CMakeLists.txt | 4 +++- src/libs/utils/test/CMakeLists.txt | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libs/database/test/CMakeLists.txt b/src/libs/database/test/CMakeLists.txt index 0ad6756d..605c3af5 100644 --- a/src/libs/database/test/CMakeLists.txt +++ b/src/libs/database/test/CMakeLists.txt @@ -12,5 +12,7 @@ target_link_libraries(test-database PRIVATE GTest::GTest ) -gtest_discover_tests(test-database) +if (NOT CMAKE_CROSSCOMPILING) + gtest_discover_tests(test-database) +endif() diff --git a/src/libs/metadata/test/CMakeLists.txt b/src/libs/metadata/test/CMakeLists.txt index acd4aec2..b1c57bee 100644 --- a/src/libs/metadata/test/CMakeLists.txt +++ b/src/libs/metadata/test/CMakeLists.txt @@ -14,5 +14,7 @@ target_link_libraries(test-metadata PRIVATE GTest::GTest ) -gtest_discover_tests(test-metadata) +if (NOT CMAKE_CROSSCOMPILING) + gtest_discover_tests(test-metadata) +endif() diff --git a/src/libs/som/test/CMakeLists.txt b/src/libs/som/test/CMakeLists.txt index 4c409913..7781e77d 100644 --- a/src/libs/som/test/CMakeLists.txt +++ b/src/libs/som/test/CMakeLists.txt @@ -9,5 +9,7 @@ target_link_libraries(test-som PRIVATE GTest::GTest ) -gtest_discover_tests(test-som) +if (NOT CMAKE_CROSSCOMPILING) + gtest_discover_tests(test-som) +endif() diff --git a/src/libs/utils/test/CMakeLists.txt b/src/libs/utils/test/CMakeLists.txt index 1b355f9b..9f33170b 100644 --- a/src/libs/utils/test/CMakeLists.txt +++ b/src/libs/utils/test/CMakeLists.txt @@ -12,5 +12,7 @@ target_link_libraries(test-utils PRIVATE GTest::GTest ) -gtest_discover_tests(test-utils) +if (NOT CMAKE_CROSSCOMPILING) + gtest_discover_tests(test-utils) +endif() From 6baf0d313d31e75af3b6ce9aee9215e0de0b16f4 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 30 Sep 2021 19:02:49 +0200 Subject: [PATCH 07/49] Finished clean on recommendation engines --- src/libs/database/impl/Artist.cpp | 7 + src/libs/database/impl/Release.cpp | 7 + src/libs/database/impl/Track.cpp | 8 + src/libs/database/include/database/Artist.hpp | 3 +- .../database/include/database/Release.hpp | 1 + src/libs/database/include/database/Track.hpp | 7 +- src/libs/database/test/Artist.cpp | 9 + src/libs/database/test/Release.cpp | 9 + src/libs/database/test/Track.cpp | 5 + src/libs/recommendation/CMakeLists.txt | 2 +- ...rCreator.hpp => ClustersEngineCreator.hpp} | 12 +- src/libs/recommendation/impl/Engine.cpp | 463 ++++++++---------- src/libs/recommendation/impl/Engine.hpp | 41 +- .../impl/FeaturesEngineCreator.hpp | 9 +- src/libs/recommendation/impl/IClassifier.hpp | 65 --- .../impl/clusters/ClustersClassifier.cpp | 103 ---- .../impl/clusters/ClustersClassifier.hpp | 53 -- .../impl/clusters/ClustersEngine.cpp | 119 +++++ .../impl/clusters/ClustersEngine.hpp | 51 ++ .../impl/features/FeaturesEngine.cpp | 104 ++-- .../impl/features/FeaturesEngine.hpp | 31 +- .../include/recommendation/IEngine.hpp | 16 +- .../recommendation/IRecommendation.hpp | 38 -- .../listenbrainz/FeedbackSynchronizer.hpp | 92 ++++ src/libs/subsonic/impl/SubsonicResource.cpp | 10 +- src/lms/ui/PlayQueue.cpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 5 +- src/lms/ui/explore/ReleaseView.cpp | 2 +- .../recommendation/LmsRecommendation.cpp | 8 +- 29 files changed, 648 insertions(+), 634 deletions(-) rename src/libs/recommendation/impl/{ClustersClassifierCreator.hpp => ClustersEngineCreator.hpp} (86%) delete mode 100644 src/libs/recommendation/impl/IClassifier.hpp delete mode 100644 src/libs/recommendation/impl/clusters/ClustersClassifier.cpp delete mode 100644 src/libs/recommendation/impl/clusters/ClustersClassifier.hpp create mode 100644 src/libs/recommendation/impl/clusters/ClustersEngine.cpp create mode 100644 src/libs/recommendation/impl/clusters/ClustersEngine.hpp delete mode 100644 src/libs/recommendation/include/recommendation/IRecommendation.hpp create mode 100644 src/libs/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/database/impl/Artist.cpp index a519f4c2..9ca631fa 100644 --- a/src/libs/database/impl/Artist.cpp +++ b/src/libs/database/impl/Artist.cpp @@ -66,6 +66,13 @@ Artist::getById(Session& session, ArtistId id) return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } +bool +Artist::exists(Session& session, ArtistId id) +{ + session.checkSharedLocked(); + return session.getDboSession().query("SELECT 1 FROM artist").where("id = ?").bind(id).resultValue() == 1; +} + Artist::pointer Artist::create(Session& session, const std::string& name, const std::optional& MBID) { diff --git a/src/libs/database/impl/Release.cpp b/src/libs/database/impl/Release.cpp index a42d7c91..310de51e 100644 --- a/src/libs/database/impl/Release.cpp +++ b/src/libs/database/impl/Release.cpp @@ -114,6 +114,13 @@ Release::getById(Session& session, ReleaseId id) .resultValue(); } +bool +Release::exists(Session& session, ReleaseId id) +{ + session.checkSharedLocked(); + return session.getDboSession().query("SELECT 1 FROM release").where("id = ?").bind(id).resultValue() == 1; +} + Release::pointer Release::create(Session& session, const std::string& name, const std::optional& MBID) { diff --git a/src/libs/database/impl/Track.cpp b/src/libs/database/impl/Track.cpp index e8f3436b..82dc99cf 100644 --- a/src/libs/database/impl/Track.cpp +++ b/src/libs/database/impl/Track.cpp @@ -156,6 +156,14 @@ Track::getById(Session& session, TrackId id) .resultValue(); } +bool +Track::exists(Session& session, TrackId id) +{ + session.checkSharedLocked(); + + return session.getDboSession().query("SELECT 1 from track").where("id = ?").bind(id).resultValue() == 1; +} + std::vector Track::getByRecordingMBID(Session& session, const UUID& mbid) { diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/database/include/database/Artist.hpp index d5bab873..23ef7812 100644 --- a/src/libs/database/include/database/Artist.hpp +++ b/src/libs/database/include/database/Artist.hpp @@ -58,8 +58,9 @@ class Artist : public Object // Accessors static pointer getByMBID(Session& session, const UUID& MBID); static pointer getById(Session& session, ArtistId id); + static bool exists(Session& session, ArtistId id); static std::vector getByName(Session& session, const std::string& name); // exact match on name field - static std::vector getByClusters(Session& session, + static std::vector getByClusters(Session& session, const std::vector& clusters, // at least one track that belongs to these clusters SortMethod sortMethod ); diff --git a/src/libs/database/include/database/Release.hpp b/src/libs/database/include/database/Release.hpp index 80c16eeb..ce45a1a8 100644 --- a/src/libs/database/include/database/Release.hpp +++ b/src/libs/database/include/database/Release.hpp @@ -50,6 +50,7 @@ class Release : public Object static pointer getByMBID(Session& session, const UUID& MBID); static std::vector getByName(Session& session, const std::string& name); static pointer getById(Session& session, ReleaseId id); + static bool exists(Session& session, ReleaseId id); static std::vector getAllOrphans(Session& session); // no track related static std::vector getAll(Session& session, std::optional range = std::nullopt); static std::vector getAllIds(Session& session); diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/database/include/database/Track.hpp index 0d78c627..3e2c2ced 100644 --- a/src/libs/database/include/database/Track.hpp +++ b/src/libs/database/include/database/Track.hpp @@ -56,9 +56,10 @@ class Track : public Object Track(const std::filesystem::path& p); // Find utility functions - static std::size_t getCount(Session& session); - static pointer getByPath(Session& session, const std::filesystem::path& p); - static pointer getById(Session& session, TrackId id); + static std::size_t getCount(Session& session); + static pointer getByPath(Session& session, const std::filesystem::path& p); + static pointer getById(Session& session, TrackId id); + static bool exists(Session& session, TrackId id); static std::vector getByRecordingMBID(Session& session, const UUID& MBID); static std::vector getSimilarTracks(Session& session, const std::vector& trackIds, diff --git a/src/libs/database/test/Artist.cpp b/src/libs/database/test/Artist.cpp index 7193b164..dcace44e 100644 --- a/src/libs/database/test/Artist.cpp +++ b/src/libs/database/test/Artist.cpp @@ -23,6 +23,13 @@ using namespace Database; TEST_F(DatabaseFixture, SingleArtist) { + { + auto transaction {session.createSharedTransaction()}; + EXPECT_FALSE(Artist::exists(session, 35)); + EXPECT_FALSE(Artist::exists(session, 0)); + EXPECT_FALSE(Artist::exists(session, 1)); + } + ScopedArtist artist {session, "MyArtist"}; { @@ -31,6 +38,8 @@ TEST_F(DatabaseFixture, SingleArtist) EXPECT_TRUE(artist.get()); EXPECT_FALSE(!artist.get()); EXPECT_EQ(artist.get()->getId(), artist.getId()); + + EXPECT_TRUE(Artist::exists(session, artist.getId())); } { diff --git a/src/libs/database/test/Release.cpp b/src/libs/database/test/Release.cpp index a65db032..801fd9bf 100644 --- a/src/libs/database/test/Release.cpp +++ b/src/libs/database/test/Release.cpp @@ -23,11 +23,20 @@ using namespace Database; TEST_F(DatabaseFixture, SingleRelease) { + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_FALSE(Release::exists(session, 0)); + EXPECT_FALSE(Release::exists(session, 1)); + } + ScopedRelease release {session, "MyRelease"}; { auto transaction {session.createSharedTransaction()}; + EXPECT_TRUE(Release::exists(session, release.getId())); + auto releases {Release::getAllOrphans(session)}; ASSERT_EQ(releases.size(), 1); EXPECT_EQ(releases.front()->getId(), release.getId()); diff --git a/src/libs/database/test/Track.cpp b/src/libs/database/test/Track.cpp index ac919bff..bdc54855 100644 --- a/src/libs/database/test/Track.cpp +++ b/src/libs/database/test/Track.cpp @@ -28,6 +28,7 @@ TEST_F(DatabaseFixture, SingleTrack) { auto transaction {session.createSharedTransaction()}; EXPECT_EQ(Track::getCount(session), 0); + EXPECT_FALSE(Track::exists(session, 0)); } ScopedTrack track {session, "MyTrackFile"}; @@ -37,6 +38,10 @@ TEST_F(DatabaseFixture, SingleTrack) EXPECT_EQ(Track::getAll(session).size(), 1); EXPECT_EQ(Track::getCount(session), 1); + EXPECT_TRUE(Track::exists(session, track.getId())); + auto myTrack {Track::getById(session, track.getId())}; + ASSERT_TRUE(myTrack); + EXPECT_EQ(myTrack->getId(), track.getId()); } } diff --git a/src/libs/recommendation/CMakeLists.txt b/src/libs/recommendation/CMakeLists.txt index 890cf3f9..4682173d 100644 --- a/src/libs/recommendation/CMakeLists.txt +++ b/src/libs/recommendation/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(lmsrecommendation SHARED - impl/clusters/ClustersClassifier.cpp + impl/clusters/ClustersEngine.cpp impl/features/FeaturesEngineCache.cpp impl/features/FeaturesEngine.cpp impl/features/FeaturesDefs.cpp diff --git a/src/libs/recommendation/impl/ClustersClassifierCreator.hpp b/src/libs/recommendation/impl/ClustersEngineCreator.hpp similarity index 86% rename from src/libs/recommendation/impl/ClustersClassifierCreator.hpp rename to src/libs/recommendation/impl/ClustersEngineCreator.hpp index c9527ef8..e0fcc842 100644 --- a/src/libs/recommendation/impl/ClustersClassifierCreator.hpp +++ b/src/libs/recommendation/impl/ClustersEngineCreator.hpp @@ -21,10 +21,14 @@ #include -namespace Recommendation +namespace Database { - class IClassifier; - - std::unique_ptr createClustersClassifier(); + class Db; +} + +namespace Recommendation +{ + class IEngine; + std::unique_ptr createClustersEngine(Database::Db& db); } diff --git a/src/libs/recommendation/impl/Engine.cpp b/src/libs/recommendation/impl/Engine.cpp index 741a9dad..b6808ceb 100644 --- a/src/libs/recommendation/impl/Engine.cpp +++ b/src/libs/recommendation/impl/Engine.cpp @@ -22,7 +22,7 @@ #include #include -#include "ClustersClassifierCreator.hpp" +#include "ClustersEngineCreator.hpp" #include "FeaturesEngineCreator.hpp" #include "database/Db.hpp" @@ -31,260 +31,229 @@ #include "utils/Exception.hpp" #include "utils/Logger.hpp" -namespace Recommendation { - - -static -std::unique_ptr -createClassifier(ClassifierType type) +namespace Recommendation { - switch (type) + + static + std::string_view + engineTypeToString(EngineType engineType) { - case ClassifierType::Clusters: - return createClustersClassifier(); - break; - - case ClassifierType::Features: - return createFeaturesEngine(); - break; - } - - return {}; -} - -std::unique_ptr -createEngine(Database::Db& db) -{ - return std::make_unique(db); -} - -Engine::Engine(Database::Db& db) -: _db {db} -{ -} - -Engine::TrackContainer -Engine::getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId trackListId, std::size_t maxCount) -{ - TrackContainer res; - - std::shared_lock lock {_classifiersMutex}; - for (const auto& classifierName : _classifierPriorities) - { - auto itClassifier {_classifiers.find(classifierName)}; - if (itClassifier == std::cend(_classifiers)) - continue; - - res = itClassifier->second->getSimilarTracksFromTrackList(session, trackListId, maxCount); - if (!res.empty()) - break; - } - - return res; -} - -Engine::TrackContainer -Engine::getSimilarTracks(Database::Session& dbSession, const std::vector& trackIds, std::size_t maxCount) -{ - TrackContainer res; - - std::shared_lock lock {_classifiersMutex}; - for (ClassifierType classifierType : _classifierPriorities) - { - auto itClassifier {_classifiers.find(classifierType)}; - if (itClassifier == std::cend(_classifiers)) - continue; - - const IClassifier& classifier {*itClassifier->second}; - res = classifier.getSimilarTracks(dbSession, trackIds, maxCount); - if (!res.empty()) + switch (engineType) { - LMS_LOG(RECOMMENDATION, DEBUG) << "Got " << res.size() << " similar tracks using classifier '" << classifier.getName() << "'"; - break; + case EngineType::Clusters: return "clusters"; + case EngineType::Features: return "features"; + } + + throw LmsException {"Internal error"}; + } + + std::unique_ptr + createEngine(Database::Db& db) + { + return std::make_unique(db); + } + + Engine::Engine(Database::Db& db) + : _db {db} + { + } + + Engine::TrackContainer + Engine::getSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const + { + TrackContainer res; + + std::shared_lock lock {_enginesMutex}; + for (const auto& engineType : _enginePriorities) + { + auto itEngine {_engines.find(engineType)}; + if (itEngine == std::cend(_engines)) + continue; + + res = itEngine->second->getSimilarTracksFromTrackList(trackListId, maxCount); + if (!res.empty()) + break; + } + + return res; + } + + Engine::TrackContainer + Engine::getSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const + { + TrackContainer res; + + std::shared_lock lock {_enginesMutex}; + for (EngineType engineType : _enginePriorities) + { + auto itEngine {_engines.find(engineType)}; + if (itEngine == std::cend(_engines)) + continue; + + const IEngine& engine {*itEngine->second}; + res = engine.getSimilarTracks(trackIds, maxCount); + if (!res.empty()) + { + LMS_LOG(RECOMMENDATION, DEBUG) << "Got " << res.size() << " similar tracks using engine '" << engineTypeToString(engineType) << "'"; + break; + } + } + + return res; + } + + Engine::ReleaseContainer + Engine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const + { + ReleaseContainer res; + + std::shared_lock lock {_enginesMutex}; + for (EngineType engineType : _enginePriorities) + { + auto itEngine {_engines.find(engineType)}; + if (itEngine == std::cend(_engines)) + continue; + + const IEngine& engine {*itEngine->second}; + res = engine.getSimilarReleases(releaseId, maxCount); + if (!res.empty()) + { + LMS_LOG(RECOMMENDATION, DEBUG) << "Got " << res.size() << " similar releases using engine '" << engineTypeToString(engineType) << "'"; + break; + } + + LMS_LOG(RECOMMENDATION, DEBUG) << "No result using engine '" << engineTypeToString(engineType) << "'"; + } + + return res; + } + + Engine::ArtistContainer + Engine::getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const + { + ArtistContainer res; + + std::shared_lock lock {_enginesMutex}; + for (EngineType engineType : _enginePriorities) + { + auto itEngine {_engines.find(engineType)}; + if (itEngine == std::cend(_engines)) + continue; + + const IEngine& engine {*itEngine->second}; + res = engine.getSimilarArtists(artistId, linkTypes, maxCount); + if (!res.empty()) + { + LMS_LOG(RECOMMENDATION, DEBUG) << "Got " << res.size() << " similar artists using engine '" << engineTypeToString(engineType) << "'"; + return res; + } + } + + return res; + } + + static + Database::ScanSettings::RecommendationEngineType + getRecommendationEngineType(Database::Session& session) + { + auto transaction {session.createSharedTransaction()}; + + return Database::ScanSettings::get(session)->getRecommendationEngineType(); + } + + void + Engine::load(bool forceReload, const ProgressCallback& progressCallback) + { + using namespace Database; + + LMS_LOG(RECOMMENDATION, INFO) << "Reloading recommendation engines..."; + + EngineContainer enginesToLoad; + + { + std::unique_lock controlLock {_controlMutex}; + + { + std::unique_lock lock {_enginesMutex}; + _engines.clear(); + } + + switch (getRecommendationEngineType(_db.getTLSSession())) + { + case ScanSettings::RecommendationEngineType::Clusters: + _enginePriorities = {EngineType::Clusters}; + enginesToLoad.try_emplace(EngineType::Clusters, createClustersEngine(_db)); + break; + + case ScanSettings::RecommendationEngineType::Features: + _enginePriorities = {EngineType::Features, EngineType::Clusters}; + + // not same order since clusters is faster to load + enginesToLoad.try_emplace(EngineType::Clusters, createClustersEngine(_db)); + enginesToLoad.try_emplace(EngineType::Features, createFeaturesEngine(_db)); + break; + } + + assert(_pendingEngines.empty()); + for (auto& [engineType, engine] : enginesToLoad) + _pendingEngines.push_back(engine.get()); + } + + for (auto& [engineType, engine] : enginesToLoad) + loadPendingEngine(engineType, std::move(engine), forceReload, progressCallback); + + _pendingEnginesCondvar.notify_all(); + + LMS_LOG(RECOMMENDATION, INFO) << "Recommendation engines loaded!"; + } + + void + Engine::loadPendingEngine(EngineType engineType, std::unique_ptr engine, bool forceReload, const ProgressCallback& progressCallback) + { + if (!_loadCancelled) + { + LMS_LOG(RECOMMENDATION, INFO) << "Initializing engine '" << engineTypeToString(engineType) << "'..."; + + auto progress {[&](const IEngine::Progress& progress) + { + progressCallback(progress); + }}; + + engine->load(forceReload, progressCallback ? progress : IEngine::ProgressCallback {}); + + { + std::scoped_lock lock {_controlMutex}; + _pendingEngines.erase(std::find(std::begin(_pendingEngines), std::end(_pendingEngines), engine.get())); + } + LMS_LOG(RECOMMENDATION, INFO) << "Initializing engine '" << engineTypeToString(engineType) << "': " << (_loadCancelled ? "aborted" : "complete"); + } + + if (!_loadCancelled) + { + std::unique_lock lock {_enginesMutex}; + _engines.emplace(engineType, std::move(engine)); } } - return res; -} - -Engine::ReleaseContainer -Engine::getSimilarReleases(Database::Session& dbSession, Database::ReleaseId releaseId, std::size_t maxCount) -{ - ReleaseContainer res; - - std::shared_lock lock {_classifiersMutex}; - for (ClassifierType classifierType : _classifierPriorities) + void + Engine::cancelLoad() { - auto itClassifier {_classifiers.find(classifierType)}; - if (itClassifier == std::cend(_classifiers)) - continue; + LMS_LOG(RECOMMENDATION, DEBUG) << "Cancelling loading..."; - const IClassifier& classifier {*itClassifier->second}; - res = classifier.getSimilarReleases(dbSession, releaseId, maxCount); - if (!res.empty()) - { - LMS_LOG(RECOMMENDATION, DEBUG) << "Got " << res.size() << " similar releases using classifier '" << classifier.getName() << "'"; - break; - } + std::unique_lock controlLock {_controlMutex}; + + assert(!_loadCancelled); + _loadCancelled = true; + + LMS_LOG(RECOMMENDATION, DEBUG) << "Still " << _pendingEngines.size() << " pending engines!"; + + for (IEngine* engine : _pendingEngines) + engine->requestCancelLoad(); + + _pendingEnginesCondvar.wait(controlLock, [this] {return _pendingEngines.empty();}); + _loadCancelled = false; + + LMS_LOG(RECOMMENDATION, DEBUG) << "Cancelling loading DONE"; } - return res; -} - -Engine::ArtistContainer -Engine::getSimilarArtists(Database::Session& dbSession, Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) -{ - ArtistContainer res; - - std::shared_lock lock {_classifiersMutex}; - for (ClassifierType classifierType : _classifierPriorities) - { - auto itClassifier {_classifiers.find(classifierType)}; - if (itClassifier == std::cend(_classifiers)) - continue; - - const IClassifier& classifier {*itClassifier->second}; - res = classifier.getSimilarArtists(dbSession, artistId, linkTypes, maxCount); - if (!res.empty()) - { - LMS_LOG(RECOMMENDATION, DEBUG) << "Got " << res.size() << " similar artists using classifier '" << classifier.getName() << "'"; - return res; - } - } - - return res; -} - -static -Database::ScanSettings::RecommendationEngineType -getRecommendationEngineType(Database::Session& session) -{ - auto transaction {session.createSharedTransaction()}; - - return Database::ScanSettings::get(session)->getRecommendationEngineType(); -} - -void -Engine::load(bool forceReload, const ProgressCallback& progressCallback) -{ - using namespace Database; - - LMS_LOG(RECOMMENDATION, INFO) << "Reloading recommendation engines..."; - struct ClassifierWithType - { - ClassifierType type; - std::unique_ptr classifier; - }; - - std::vector classifiers; - auto addClassifier {[&](ClassifierType type) - { - classifiers.emplace_back(ClassifierWithType {type, createClassifier(type)}); - }}; - - switch (getRecommendationEngineType(_db.getTLSSession())) - { - case ScanSettings::RecommendationEngineType::Clusters: - setClassifierPriorities({ClassifierType::Clusters}); - addClassifier(ClassifierType::Clusters); - break; - case ScanSettings::RecommendationEngineType::Features: - setClassifierPriorities({ClassifierType::Features, ClassifierType::Clusters}); - // not same order since clusters is faster to load - addClassifier(ClassifierType::Clusters); - addClassifier(ClassifierType::Features); - break; - } - - assert(_pendingClassifiers.empty()); - clearClassifiers(); - - { - std::scoped_lock lock {_controlMutex}; - - std::transform(std::cbegin(classifiers), std::cend(classifiers), std::inserter(_pendingClassifiers, std::end(_pendingClassifiers)), - [](auto& classifier) { return classifier.classifier.get(); }); - } - - for (ClassifierWithType& classifier : classifiers) - loadClassifier(std::move(classifier.classifier), classifier.type, forceReload, progressCallback); - - LMS_LOG(RECOMMENDATION, INFO) << "Recommendation engines loaded!"; -} - -void -Engine::setClassifierPriorities(const std::vector& classifierPriorities) -{ - std::unique_lock lock {_classifiersMutex}; - - _classifierPriorities = classifierPriorities; -} - -void -Engine::clearClassifiers() -{ - std::unique_lock lock {_classifiersMutex}; - - _classifiers.clear(); -} - -void -Engine::loadClassifier(std::unique_ptr classifier, - ClassifierType classifierType, - bool forceReload, - const ProgressCallback& progressCallback) -{ - IClassifier* rawClassifier {classifier.get()}; - - bool res {}; - if (!_loadCancelled) - { - LMS_LOG(RECOMMENDATION, INFO) << "Initializing classifier '" << classifier->getName() << "'..."; - - auto progress {[&](IClassifier::Progress progress) - { - progressCallback(Progress {progress.processedElems, progress.totalElems}); - }}; - - res = classifier->load(_db.getTLSSession(), forceReload, progressCallback ? progress : IClassifier::ProgressCallback {}); - - LMS_LOG(RECOMMENDATION, INFO) << "Initializing classifier '" << classifier->getName() << "': " << (res ? "SUCCESS" : "FAILURE"); - } - - if (res) - { - std::unique_lock lock {_classifiersMutex}; - - _classifiers.emplace(classifierType, std::move(classifier)); - } - - { - std::scoped_lock lock {_controlMutex}; - - _pendingClassifiers.erase(rawClassifier); - } - - _pendingClassifiersCondvar.notify_one(); -} - -void -Engine::cancelLoad() -{ - LMS_LOG(RECOMMENDATION, DEBUG) << "Cancelling loading..."; - - std::unique_lock lock {_controlMutex}; - - LMS_LOG(RECOMMENDATION, DEBUG) << "Still " << _pendingClassifiers.size() << " pending classifiers!"; - - _loadCancelled = true; - - for (IClassifier* classifier : _pendingClassifiers) - classifier->requestCancelLoad(); - - _pendingClassifiersCondvar.wait(lock, [this] {return _pendingClassifiers.empty();}); - _loadCancelled = false; - - LMS_LOG(RECOMMENDATION, DEBUG) << "Cancelling loading DONE"; -} - } // ns Similarity diff --git a/src/libs/recommendation/impl/Engine.hpp b/src/libs/recommendation/impl/Engine.hpp index 39338fbc..ac253b5c 100644 --- a/src/libs/recommendation/impl/Engine.hpp +++ b/src/libs/recommendation/impl/Engine.hpp @@ -20,13 +20,12 @@ #pragma once #include +#include #include #include -#include #include #include "recommendation/IEngine.hpp" -#include "IClassifier.hpp" namespace Database { @@ -35,7 +34,7 @@ namespace Database namespace Recommendation { - enum class ClassifierType + enum class EngineType { Clusters, Features, @@ -57,29 +56,29 @@ namespace Recommendation void cancelLoad() override; void requestCancelLoad() override {}; - ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId tracklistId, std::size_t maxCount) override; - ResultContainer getSimilarTracks(Database::Session& session, const std::vector& tracksId, std::size_t maxCount) override; - ResultContainer getSimilarReleases(Database::Session& session, Database::ReleaseId releaseId, std::size_t maxCount) override; - ResultContainer getSimilarArtists(Database::Session& session, - Database::ArtistId artistId, - EnumSet linkTypes, - std::size_t maxCount) override; + TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; + TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override; + ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const override; - void setClassifierPriorities(const std::vector& classifierTypes); - void clearClassifiers(); - void loadClassifier(std::unique_ptr classifier, ClassifierType classifierType, bool forceReload, const ProgressCallback& progressCallback); + void setEnginePriorities(const std::vector& engineTypes); + void clearEngines(); + void loadPendingEngine(EngineType engineType, std::unique_ptr engine, bool forceReload, const ProgressCallback& progressCallback); Database::Db& _db; - std::mutex _controlMutex; - bool _loadCancelled {}; - std::condition_variable _pendingClassifiersCondvar; - std::unordered_set _pendingClassifiers; + std::mutex _controlMutex; + bool _loadCancelled {}; - std::shared_mutex _classifiersMutex; - using ClassifierContainer = std::unordered_map>; - ClassifierContainer _classifiers; - std::vector _classifierPriorities; // ordered by priority + using EngineContainer = std::unordered_map>; + EngineContainer _engines; + mutable std::shared_mutex _enginesMutex; + + std::vector _pendingEngines; + std::shared_mutex _pendingEnginesMutex; + std::condition_variable _pendingEnginesCondvar; + + std::vector _enginePriorities; // ordered by priority }; } // ns Recommendation diff --git a/src/libs/recommendation/impl/FeaturesEngineCreator.hpp b/src/libs/recommendation/impl/FeaturesEngineCreator.hpp index 5b948e70..1378970f 100644 --- a/src/libs/recommendation/impl/FeaturesEngineCreator.hpp +++ b/src/libs/recommendation/impl/FeaturesEngineCreator.hpp @@ -20,10 +20,15 @@ #pragma once #include -#include "IClassifier.hpp" +#include "recommendation/IEngine.hpp" + +namespace Database +{ + class Db; +} namespace Recommendation { - std::unique_ptr createFeaturesEngine(); + std::unique_ptr createFeaturesEngine(Database::Db& db); } diff --git a/src/libs/recommendation/impl/IClassifier.hpp b/src/libs/recommendation/impl/IClassifier.hpp deleted file mode 100644 index 62483d9e..00000000 --- a/src/libs/recommendation/impl/IClassifier.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2020 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#pragma once - -#include -#include -#include - -#include "database/Types.hpp" -#include "recommendation/IRecommendation.hpp" -#include "utils/EnumSet.hpp" - -namespace Database -{ - class Session; -} - -namespace Recommendation -{ - - class IClassifier : public IRecommendation - { - public: - virtual ~IClassifier() = default; - - virtual std::string_view getName() const = 0; - - struct Progress - { - std::size_t totalElems {}; - std::size_t processedElems {}; - }; - using ProgressCallback = std::function; - virtual bool load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) = 0; - virtual void requestCancelLoad() = 0; - - template - using ResultContainer = std::vector; - - virtual ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId tracklistId, std::size_t maxCount) const = 0; - virtual ResultContainer getSimilarTracks(Database::Session& session, const std::vector& tracksId, std::size_t maxCount) const = 0; - virtual ResultContainer getSimilarReleases(Database::Session& session, Database::ReleaseId releaseId, std::size_t maxCount) const = 0; - virtual ResultContainer getSimilarArtists(Database::Session& session, - Database::ArtistId artistId, - EnumSet linkTypes, std::size_t maxCount) const = 0; - }; - -} // ns Recommendation diff --git a/src/libs/recommendation/impl/clusters/ClustersClassifier.cpp b/src/libs/recommendation/impl/clusters/ClustersClassifier.cpp deleted file mode 100644 index 44f39a76..00000000 --- a/src/libs/recommendation/impl/clusters/ClustersClassifier.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2018 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 "ClustersClassifier.hpp" - -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" - -namespace Recommendation { - -std::unique_ptr createClustersClassifier() -{ - return std::make_unique(); -} - -IClassifier::ResultContainer -ClusterClassifier::getSimilarTracks(Database::Session& dbSession, const std::vector& trackIds, std::size_t maxCount) const -{ - ResultContainer res; - - auto transaction {dbSession.createSharedTransaction()}; - - const auto tracks {Database::Track::getSimilarTracks(dbSession, trackIds, 0, maxCount)}; - std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const auto& track) { return track->getId(); }); - - return res; -} - -IClassifier::ResultContainer -ClusterClassifier::getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId tracklistId, std::size_t maxCount) const -{ - ResultContainer res; - - auto transaction {session.createSharedTransaction()}; - - const Database::TrackList::pointer trackList {Database::TrackList::getById(session, tracklistId)}; - if (!trackList) - return res; - - const auto tracks {trackList->getSimilarTracks(0, maxCount)}; - std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const auto& track) { return track->getId(); }); - - return res; -} - -IClassifier::ResultContainer -ClusterClassifier::getSimilarReleases(Database::Session& dbSession, Database::ReleaseId releaseId, std::size_t maxCount) const -{ - ResultContainer res; - - auto transaction {dbSession.createSharedTransaction()}; - - auto release {Database::Release::getById(dbSession, releaseId)}; - if (!release) - return res; - - const auto releases {release->getSimilarReleases(0, maxCount)}; - std::transform(std::cbegin(releases), std::cend(releases), std::back_inserter(res), [](const auto& release) { return release->getId(); }); - - return res; -} - -IClassifier::ResultContainer -ClusterClassifier::getSimilarArtists(Database::Session& dbSession, - Database::ArtistId artistId, - EnumSet artistLinkTypes, - std::size_t maxCount) const -{ - ResultContainer res; - - auto transaction {dbSession.createSharedTransaction()}; - - auto artist {Database::Artist::getById(dbSession, artistId)}; - if (!artist) - return res; - - const auto artists {artist->getSimilarArtists(artistLinkTypes, Database::Range {0, maxCount})}; - std::transform(std::cbegin(artists), std::cend(artists), std::back_inserter(res), [](const auto& artist) { return artist->getId(); }); - - return res; -} - -} // namespace Recommendation diff --git a/src/libs/recommendation/impl/clusters/ClustersClassifier.hpp b/src/libs/recommendation/impl/clusters/ClustersClassifier.hpp deleted file mode 100644 index 718d6872..00000000 --- a/src/libs/recommendation/impl/clusters/ClustersClassifier.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2018 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 "IClassifier.hpp" - -namespace Recommendation -{ - - class ClusterClassifier : public IClassifier - { - public: - ClusterClassifier() = default; - ClusterClassifier(const ClusterClassifier&) = delete; - ClusterClassifier(ClusterClassifier&&) = delete; - ClusterClassifier& operator=(const ClusterClassifier&) = delete; - ClusterClassifier& operator=(ClusterClassifier&&) = delete; - - private: - - std::string_view getName() const override { return "Clusters"; } - - bool load(Database::Session&, bool, const ProgressCallback&) override { return true; } - void requestCancelLoad() override {} - - ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId tracklistId, std::size_t maxCount) const override; - ResultContainer getSimilarTracks(Database::Session& session, const std::vector& tracksId, std::size_t maxCount) const override; - ResultContainer getSimilarReleases(Database::Session& session, Database::ReleaseId releaseId, std::size_t maxCount) const override; - ResultContainer getSimilarArtists(Database::Session& session, - Database::ArtistId artistId, - EnumSet linkTypes, - std::size_t maxCount) const override; -}; - -} // namespace Recommendation - diff --git a/src/libs/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/recommendation/impl/clusters/ClustersEngine.cpp new file mode 100644 index 00000000..b0703a0f --- /dev/null +++ b/src/libs/recommendation/impl/clusters/ClustersEngine.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2018 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 "ClustersEngine.hpp" + +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" + +namespace Recommendation { + +std::unique_ptr createClustersEngine(Database::Db& db) +{ + return std::make_unique(db); +} + +IEngine::TrackContainer +ClusterEngine::getSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const +{ + Database::Session& dbSession {_db.getTLSSession()}; + + TrackContainer res; + + { + auto transaction {dbSession.createSharedTransaction()}; + + const auto tracks {Database::Track::getSimilarTracks(dbSession, trackIds, 0, maxCount)}; + res.reserve(tracks.size()); + std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const auto& track) { return track->getId(); }); + } + + return res; +} + +IEngine::ResultContainer +ClusterEngine::getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const +{ + Database::Session& dbSession {_db.getTLSSession()}; + + TrackContainer res; + + { + auto transaction {dbSession.createSharedTransaction()}; + + const Database::TrackList::pointer trackList {Database::TrackList::getById(dbSession, tracklistId)}; + if (!trackList) + return res; + + const auto tracks {trackList->getSimilarTracks(0, maxCount)}; + res.reserve(tracks.size()); + std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const auto& track) { return track->getId(); }); + } + + return res; +} + +IEngine::ResultContainer +ClusterEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const +{ + Database::Session& dbSession {_db.getTLSSession()}; + + ReleaseContainer res; + { + auto transaction {dbSession.createSharedTransaction()}; + + auto release {Database::Release::getById(dbSession, releaseId)}; + if (!release) + return res; + + const auto releases {release->getSimilarReleases(0, maxCount)}; + res.reserve(releases.size()); + std::transform(std::cbegin(releases), std::cend(releases), std::back_inserter(res), [](const auto& release) { return release->getId(); }); + } + + return res; +} + +IEngine::ResultContainer +ClusterEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet artistLinkTypes, std::size_t maxCount) const +{ + Database::Session& dbSession {_db.getTLSSession()}; + + ResultContainer res; + { + auto transaction {dbSession.createSharedTransaction()}; + + auto artist {Database::Artist::getById(dbSession, artistId)}; + if (!artist) + return res; + + const auto artists {artist->getSimilarArtists(artistLinkTypes, Database::Range {0, maxCount})}; + res.reserve(artists.size()); + std::transform(std::cbegin(artists), std::cend(artists), std::back_inserter(res), [](const auto& artist) { return artist->getId(); }); + } + + return res; +} + +} // namespace Recommendation diff --git a/src/libs/recommendation/impl/clusters/ClustersEngine.hpp b/src/libs/recommendation/impl/clusters/ClustersEngine.hpp new file mode 100644 index 00000000..47654aac --- /dev/null +++ b/src/libs/recommendation/impl/clusters/ClustersEngine.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 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 "recommendation/IEngine.hpp" + +namespace Recommendation +{ + + class ClusterEngine : public IEngine + { + public: + ClusterEngine(Database::Db& db) : _db {db} {} + + ClusterEngine(const ClusterEngine&) = delete; + ClusterEngine(ClusterEngine&&) = delete; + ClusterEngine& operator=(const ClusterEngine&) = delete; + ClusterEngine& operator=(ClusterEngine&&) = delete; + + private: + void load(bool, const ProgressCallback&) override {} + void requestCancelLoad() override {} + void cancelLoad() {} + + TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; + TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override; + ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const override; + + Database::Db& _db; + }; + +} // namespace Recommendation + diff --git a/src/libs/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/recommendation/impl/features/FeaturesEngine.cpp index 03fb0db6..7b1f39b6 100644 --- a/src/libs/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/recommendation/impl/features/FeaturesEngine.cpp @@ -22,6 +22,7 @@ #include #include "database/Artist.hpp" +#include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" @@ -35,9 +36,9 @@ namespace Recommendation { -std::unique_ptr createFeaturesEngine() +std::unique_ptr createFeaturesEngine(Database::Db& db) { - return std::make_unique(); + return std::make_unique(db); } const FeatureSettingsMap& @@ -127,8 +128,8 @@ getInputVectorWeights(const FeatureSettingsMap& featureSettingsMap, std::size_t return weights; } -bool -FeaturesEngine::loadFromTraining(Database::Session& session, const TrainSettings& trainSettings, const ProgressCallback& progressCallback) +void +FeaturesEngine::loadFromTraining(const TrainSettings& trainSettings, const ProgressCallback& progressCallback) { LMS_LOG(RECOMMENDATION, INFO) << "Constructing features classifier..."; @@ -141,6 +142,8 @@ FeaturesEngine::loadFromTraining(Database::Session& session, const TrainSettings LMS_LOG(RECOMMENDATION, DEBUG) << "Features dimension = " << nbDimensions; + Database::Session& session {_db.getTLSSession()}; + std::vector trackIds; { auto transaction {session.createSharedTransaction()}; @@ -160,7 +163,7 @@ FeaturesEngine::loadFromTraining(Database::Session& session, const TrainSettings for (Database::TrackId trackId : trackIds) { if (_loadCancelled) - return false; + return; std::optional featureValuesMap; @@ -184,7 +187,7 @@ FeaturesEngine::loadFromTraining(Database::Session& session, const TrainSettings if (samples.empty()) { LMS_LOG(RECOMMENDATION, INFO) << "Nothing to classify!"; - return false; + return; } LMS_LOG(RECOMMENDATION, DEBUG) << "Normalizing data..."; @@ -219,15 +222,13 @@ FeaturesEngine::loadFromTraining(Database::Session& session, const TrainSettings [this] { return _loadCancelled; }); LMS_LOG(RECOMMENDATION, DEBUG) << "Training network DONE"; - if (_loadCancelled) - return false; LMS_LOG(RECOMMENDATION, DEBUG) << "Classifying tracks..."; TrackPositions trackPositions; for (std::size_t i {}; i < samples.size(); ++i) { if (_loadCancelled) - return false; + return; const SOM::Position position {network.getClosestRefVectorPosition(samples[i])}; @@ -236,23 +237,25 @@ FeaturesEngine::loadFromTraining(Database::Session& session, const TrainSettings LMS_LOG(RECOMMENDATION, DEBUG) << "Classifying tracks DONE"; - return load(session, std::move(network), std::move(trackPositions)); + load(std::move(network), std::move(trackPositions)); } -bool -FeaturesEngine::loadFromCache(Database::Session& session, const FeaturesEngineCache& cache) +void +FeaturesEngine::loadFromCache(FeaturesEngineCache cache) { LMS_LOG(RECOMMENDATION, INFO) << "Constructing features classifier from cache..."; - return load(session, std::move(cache._network), cache._trackPositions); + load(std::move(cache._network), cache._trackPositions); } -IClassifier::ResultContainer -FeaturesEngine::getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId trackListId, std::size_t maxCount) const +IEngine::TrackContainer +FeaturesEngine::getSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const { - const std::vector trackIds {[&] + const TrackContainer trackIds {[&] { - std::vector res; + TrackContainer res; + + Database::Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; @@ -263,14 +266,16 @@ FeaturesEngine::getSimilarTracksFromTrackList(Database::Session& session, Databa return res; }()}; - return getSimilarTracks(session, trackIds, maxCount); + return getSimilarTracks(trackIds, maxCount); } -std::vector -FeaturesEngine::getSimilarTracks(Database::Session& session, const std::vector& tracksIds, std::size_t maxCount) const +IEngine::TrackContainer +FeaturesEngine::getSimilarTracks(const std::vector& tracksIds, std::size_t maxCount) const { auto similarTrackIds {getSimilarObjects(tracksIds, _trackMatrix, _trackPositions, maxCount)}; + Database::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.createSharedTransaction()}; @@ -278,18 +283,21 @@ FeaturesEngine::getSimilarTracks(Database::Session& session, const std::vector -FeaturesEngine::getSimilarReleases(Database::Session& session, Database::ReleaseId releaseId, std::size_t maxCount) const +IEngine::ReleaseContainer +FeaturesEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const { - auto similarReleaseIds {getSimilarObjects({releaseId}, _releaseMatrix, _releasePositions, maxCount)}; + auto similarReleaseIds {getSimilarObjects({releaseId}, _releaseMatrix, _releasePositions, maxCount)}; + Database::Session& session {_db.getTLSSession()}; + + if (!similarReleaseIds.empty()) { // Report only existing ids auto transaction {session.createSharedTransaction()}; @@ -297,7 +305,7 @@ FeaturesEngine::getSimilarReleases(Database::Session& session, Database::Release similarReleaseIds.erase(std::remove_if(std::begin(similarReleaseIds), std::end(similarReleaseIds), [&](Database::ReleaseId releaseId) { - return Database::Release::getById(session, releaseId); // TODO exists + return !Database::Release::exists(session, releaseId); }), std::end(similarReleaseIds)); } @@ -305,10 +313,7 @@ FeaturesEngine::getSimilarReleases(Database::Session& session, Database::Release } std::vector -FeaturesEngine::getSimilarArtists(Database::Session& session, - Database::ArtistId artistId, - EnumSet linkTypes, - std::size_t maxCount) const +FeaturesEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const { auto getSimilarArtistIdsForLinkType {[&] (Database::TrackArtistLinkType linkType) { @@ -333,15 +338,16 @@ FeaturesEngine::getSimilarArtists(Database::Session& session, std::vector res(std::cbegin(similarArtistIds), std::cend(similarArtistIds)); + Database::Session& session {_db.getTLSSession()}; { // Report only existing ids auto transaction {session.createSharedTransaction()}; res.erase(std::remove_if(std::begin(res), std::end(res), - [&](Database::ArtistId artistId) - { - return Database::Artist::getById(session, artistId); // TODO exists - }), std::end(res)); + [&](Database::ArtistId artistId) + { + return !Database::Artist::exists(session, artistId); + }), std::end(res)); } while (res.size() > maxCount) @@ -356,29 +362,25 @@ FeaturesEngine::toCache() const return FeaturesEngineCache {*_network, _trackPositions}; } -bool -FeaturesEngine::load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) +void +FeaturesEngine::load(bool forceReload, const ProgressCallback& progressCallback) { if (forceReload) - { FeaturesEngineCache::invalidate(); } - else + else if (const std::optional cache {FeaturesEngineCache::read()}) { - const std::optional cache {FeaturesEngineCache::read()}; - if (cache) - return loadFromCache(session, *cache); + loadFromCache(*cache); + return; } TrainSettings trainSettings; trainSettings.featureSettingsMap = getDefaultTrainFeatureSettings(); - const bool res {loadFromTraining(session, trainSettings, progressCallback)}; - if (res) + loadFromTraining(trainSettings, progressCallback); + if (!_loadCancelled) toCache().write(); - - return res; } void @@ -388,10 +390,8 @@ FeaturesEngine::requestCancelLoad() _loadCancelled = true; } -bool -FeaturesEngine::load(Database::Session& session, - SOM::Network network, - const TrackPositions& trackPositions) +void +FeaturesEngine::load(const SOM::Network& network, const TrackPositions& trackPositions) { using namespace Database; @@ -406,10 +406,12 @@ FeaturesEngine::load(Database::Session& session, LMS_LOG(RECOMMENDATION, DEBUG) << "Constructing maps..."; + Database::Session& session {_db.getTLSSession()}; + for (const auto& [trackId, positions] : trackPositions) { if (_loadCancelled) - return false; + return; auto transaction {session.createSharedTransaction()}; @@ -445,11 +447,9 @@ FeaturesEngine::load(Database::Session& session, } } - _network = std::make_unique(std::move(network)); + _network = std::make_unique(network); LMS_LOG(RECOMMENDATION, INFO) << "Classifier successfully loaded!"; - - return true; } } // ns Recommendation diff --git a/src/libs/recommendation/impl/features/FeaturesEngine.hpp b/src/libs/recommendation/impl/features/FeaturesEngine.hpp index 9ed313a4..d6c1e81b 100644 --- a/src/libs/recommendation/impl/features/FeaturesEngine.hpp +++ b/src/libs/recommendation/impl/features/FeaturesEngine.hpp @@ -26,12 +26,12 @@ #include #include +#include "recommendation/IEngine.hpp" #include "som/DataNormalizer.hpp" #include "som/Network.hpp" #include "utils/Utils.hpp" #include "FeaturesEngineCache.hpp" #include "FeaturesDefs.hpp" -#include "IClassifier.hpp" namespace Database { @@ -42,10 +42,11 @@ namespace Recommendation { using FeatureWeight = double; -class FeaturesEngine : public IClassifier +class FeaturesEngine : public IEngine { public: - FeaturesEngine() = default; + FeaturesEngine(Database::Db& db) : _db {db} {} + FeaturesEngine(const FeaturesEngine&) = delete; FeaturesEngine(FeaturesEngine&&) = delete; FeaturesEngine& operator=(const FeaturesEngine&) = delete; @@ -59,21 +60,16 @@ class FeaturesEngine : public IClassifier static const FeatureSettingsMap& getDefaultTrainFeatureSettings(); private: - - std::string_view getName() const override { return "Features"; } - - bool load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) override; + void load(bool forceReload, const ProgressCallback& progressCallback) override; void requestCancelLoad() override; + void cancelLoad() override {} - ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId tracklistId, std::size_t maxCount) const override; - ResultContainer getSimilarTracks(Database::Session& session, const std::vector& tracksId, std::size_t maxCount) const override; - ResultContainer getSimilarReleases(Database::Session& session, Database::ReleaseId releaseId, std::size_t maxCount) const override; - ResultContainer getSimilarArtists(Database::Session& session, - Database::ArtistId artistId, - EnumSet linkTypes, - std::size_t maxCount) const override; + TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; + TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override; + ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const override; - bool loadFromCache(Database::Session& session, const FeaturesEngineCache& cache); + void loadFromCache(FeaturesEngineCache cache); // Use training (may be very slow) struct TrainSettings @@ -82,7 +78,7 @@ class FeaturesEngine : public IClassifier float sampleCountPerNeuron {4}; FeatureSettingsMap featureSettingsMap; }; - bool loadFromTraining(Database::Session& session, const TrainSettings& trainSettings, const ProgressCallback& progressCallback); + void loadFromTraining(const TrainSettings& trainSettings, const ProgressCallback& progressCallback); template using ObjectPositions = std::unordered_map>; @@ -97,7 +93,7 @@ class FeaturesEngine : public IClassifier using ReleaseMatrix = ObjectMatrix; using TrackMatrix = ObjectMatrix; - bool load(Database::Session& session, SOM::Network network, const TrackPositions& tracksPosition); + void load(const SOM::Network& network, const TrackPositions& tracksPosition); FeaturesEngineCache toCache() const; @@ -113,6 +109,7 @@ class FeaturesEngine : public IClassifier const ObjectPositions& objectPositions, std::size_t maxCount) const; + Database::Db& _db; bool _loadCancelled {}; std::unique_ptr _network; double _networkRefVectorsDistanceMedian {}; diff --git a/src/libs/recommendation/include/recommendation/IEngine.hpp b/src/libs/recommendation/include/recommendation/IEngine.hpp index 6a15187b..26f180bc 100644 --- a/src/libs/recommendation/include/recommendation/IEngine.hpp +++ b/src/libs/recommendation/include/recommendation/IEngine.hpp @@ -21,13 +21,13 @@ #include #include +#include #include "database/Types.hpp" #include "utils/EnumSet.hpp" namespace Database { class Db; - class Session; } namespace Recommendation @@ -44,7 +44,8 @@ namespace Recommendation }; using ProgressCallback = std::function; virtual void load(bool forceReload, const ProgressCallback& progressCallback = {}) = 0; - virtual void cancelLoad() = 0; + virtual void cancelLoad() = 0; // wait for cancel done + virtual void requestCancelLoad() = 0; template using ResultContainer = std::vector; @@ -53,13 +54,10 @@ namespace Recommendation using ReleaseContainer = ResultContainer; using TrackContainer = ResultContainer; - virtual TrackContainer getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId tracklistId, std::size_t maxCount) = 0; - virtual TrackContainer getSimilarTracks(Database::Session& session, const std::vector& tracksId, std::size_t maxCount) = 0; - virtual ReleaseContainer getSimilarReleases(Database::Session& session, Database::ReleaseId releaseId, std::size_t maxCount) = 0; - virtual ArtistContainer getSimilarArtists(Database::Session& session, Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) = 0; - - protected: - virtual void requestCancelLoad() = 0; + virtual TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0; + virtual TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const = 0; + virtual ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const = 0; + virtual ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const = 0; }; std::unique_ptr createEngine(Database::Db& db); diff --git a/src/libs/recommendation/include/recommendation/IRecommendation.hpp b/src/libs/recommendation/include/recommendation/IRecommendation.hpp deleted file mode 100644 index 834a983e..00000000 --- a/src/libs/recommendation/include/recommendation/IRecommendation.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2021 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 "database/Types.hpp" -#include "utils/EnumSet.hpp" - -namespace Database -{ - class Db; - class Session; -} - -namespace Recommendation -{ - class IRecommendation - { - }; - -} // ns Recommendation - diff --git a/src/libs/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp b/src/libs/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp new file mode 100644 index 00000000..0547921a --- /dev/null +++ b/src/libs/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include "SendQueue.hpp" + +namespace Database +{ + class Db; + class Session; + class User; +} + +namespace Scrobbling::ListenBrainz +{ + class ListensSynchronizer + { + public: + FeedbackSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, SendQueue& sendQueue); + + // void updateFeedback(const TimedListen& listen); + + private: + struct UserContext + { + UserContext(Database::UserId id) : userId {id} {} + + UserContext(const UserContext&) = delete; + UserContext(UserContext&&) = delete; + UserContext& operator=(const UserContext&) = delete; + UserContext& operator=(UserContext&&) = delete; + + const Database::UserId userId; + bool fetching {}; + std::optional listenCount {}; + + // resetted at each fetch + std::string listenBrainzUserName; // need to be resolved first + Wt::WDateTime maxDateTime; + std::size_t fetchedListenCount{}; + std::size_t matchedListenCount{}; + std::size_t importedListenCount{}; + }; + + UserContext& getUserContext(Database::UserId userId); + bool isFetching() const; + void scheduleGetListens(std::chrono::seconds fromNow); + void startGetListens(); + void startGetListens(UserContext& context); + void onGetListensEnded(UserContext& context); + void enqueValidateToken(UserContext& context); + void enqueGetListenCount(UserContext& context); + void enqueGetListens(UserContext& context); + std::optional createValidateTokenRequestData(Database::UserId userId); + std::optional createGetListensRequestData(std::string_view listenBrainzUserName, const Wt::WDateTime& maxDateTime); + void processGetListensResponse(std::string_view body, UserContext& context); + + boost::asio::io_context& _ioContext; + boost::asio::io_context::strand _strand {_ioContext}; + Database::Db& _db; + SendQueue& _sendQueue; + boost::asio::steady_timer _getListensTimer {_ioContext}; + + std::unordered_map _userContexts; + + const std::size_t _maxSyncFeedbackCount; + const std::chrono::hours _syncFeedbackPeriod; + }; +} // Scrobbling::ListenBrainz + diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 403e4ba4..c3c4ba04 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -896,10 +896,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) artistInfoNode.createChild("musicBrainzId").setValue(artistMBID->getAsString()); } - auto similarArtistsId {Service::get()->getSimilarArtists(context.dbSession, - id, - {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, - count)}; + auto similarArtistsId {Service::get()->getSimilarArtists(id, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, count)}; { auto transaction {context.dbSession.createSharedTransaction()}; @@ -1135,10 +1132,7 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3) // Optional params std::size_t count {getParameterAs(context.parameters, "count").value_or(50)}; - const auto similarArtistIds {Service::get()->getSimilarArtists(context.dbSession, - artistId, - {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, - 5)}; + const auto similarArtistIds {Service::get()->getSimilarArtists(artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; auto transaction {context.dbSession.createSharedTransaction()}; diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index f07501fe..554b34d2 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -520,7 +520,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) void PlayQueue::enqueueRadioTracks() { - const auto similarTrackIds {Service::get()->getSimilarTracksFromTrackList(LmsApp->getDbSession(), _tracklistId, 3)}; + const auto similarTrackIds {Service::get()->getSimilarTracksFromTrackList(_tracklistId, 3)}; std::vector trackToAddIds(std::cbegin(similarTrackIds), std::cend(similarTrackIds)); Random::shuffleContainer(trackToAddIds); diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 0e803db5..eb6f1d41 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -102,10 +102,7 @@ Artist::refreshView() if (!artistId) throw ArtistNotFoundException {}; - const auto similarArtistIds {Service::get()->getSimilarArtists(LmsApp->getDbSession(), - *artistId, - {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, - 5)}; + const auto similarArtistIds {Service::get()->getSimilarArtists(*artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; auto transaction {LmsApp->getDbSession().createSharedTransaction()}; diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index ceb82d90..15334246 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -101,7 +101,7 @@ Release::refreshView() if (!releaseId) throw ReleaseNotFoundException {}; - auto similarReleasesIds {Service::get()->getSimilarReleases(LmsApp->getDbSession(), *releaseId, 6)}; + auto similarReleasesIds {Service::get()->getSimilarReleases(*releaseId, 6)}; auto transaction {LmsApp->getDbSession().createSharedTransaction()}; diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index 0a1f4a3c..c88dcc36 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -66,7 +66,7 @@ dumpTracksRecommendation(Database::Session session, Recommendation::IEngine& eng }; std::cout << "Processing track '" << trackToString(trackId) << std::endl; - for (Database::TrackId similarTrackId : engine.getSimilarTracks(session, {trackId}, maxSimilarityCount)) + for (Database::TrackId similarTrackId : engine.getSimilarTracks({trackId}, maxSimilarityCount)) std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl; } } @@ -84,7 +84,7 @@ dumpReleasesRecommendation(Database::Session session, Recommendation::IEngine& e std::cout << "*** Releases ***" << std::endl; for (Database::ReleaseId releaseId : releaseIds) { - auto releaseToString = [&](Database::ReleaseId releaseId) + auto releaseToString = [&](Database::ReleaseId releaseId) -> std::string { auto transaction {session.createSharedTransaction()}; @@ -93,7 +93,7 @@ dumpReleasesRecommendation(Database::Session session, Recommendation::IEngine& e }; std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl; - for (Database::ReleaseId similarReleaseId : engine.getSimilarReleases(session, releaseId, maxSimilarityCount)) + for (Database::ReleaseId similarReleaseId : engine.getSimilarReleases(releaseId, maxSimilarityCount)) std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl; } } @@ -120,7 +120,7 @@ dumpArtistsRecommendation(Database::Session session, Recommendation::IEngine& en }; std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl; - for (Database::ArtistId similarArtistId : engine.getSimilarArtists(session, artistId, {Database::TrackArtistLinkType::Artist, Database::TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) + for (Database::ArtistId similarArtistId : engine.getSimilarArtists(artistId, {Database::TrackArtistLinkType::Artist, Database::TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) { std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl; } From 00c756f7e112341e38347a7f31cacf8ed84b4788 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 30 Sep 2021 22:31:15 +0200 Subject: [PATCH 08/49] Removed session parameters from cover service --- src/libs/cover/impl/CoverArtGrabber.cpp | 46 +++++++++++-------- src/libs/cover/impl/CoverArtGrabber.hpp | 17 +++---- .../cover/include/cover/ICoverArtGrabber.hpp | 16 +++---- src/libs/subsonic/impl/SubsonicResource.cpp | 4 +- src/lms/main.cpp | 8 +--- src/lms/ui/resource/CoverResource.cpp | 4 +- src/tools/cover/LmsCover.cpp | 14 ++---- 7 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/libs/cover/impl/CoverArtGrabber.cpp b/src/libs/cover/impl/CoverArtGrabber.cpp index 226d28a1..58fff328 100644 --- a/src/libs/cover/impl/CoverArtGrabber.cpp +++ b/src/libs/cover/impl/CoverArtGrabber.cpp @@ -21,6 +21,7 @@ #include "av/IAudioFile.hpp" +#include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" @@ -33,6 +34,7 @@ using RawImage = CoverArt::STB::RawImage; using RawImage = CoverArt::GraphicsMagick::RawImage; #endif +#include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" #include "utils/Utils.hpp" @@ -85,27 +87,25 @@ isFileSupported(const std::filesystem::path& file, const std::vector -createGrabber(const std::filesystem::path& execPath, - const std::filesystem::path& defaultCoverPath, - std::size_t maxCacheSize, std::size_t maxFileSize, unsigned jpegQuality) +createGrabber(Database::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath) { - return std::make_unique(execPath, defaultCoverPath, maxCacheSize, maxFileSize, jpegQuality); + return std::make_unique(db, execPath, defaultCoverPath); } -Grabber::Grabber(const std::filesystem::path& execPath, - const std::filesystem::path& defaultCoverPath, - std::size_t maxCacheSize, - std::size_t maxFileSize, - unsigned jpegQuality) - : _defaultCoverPath {defaultCoverPath} - , _maxCacheSize {maxCacheSize} - , _maxFileSize {maxFileSize} - , _jpegQuality {Utils::clamp(jpegQuality, 1, 100)} +Grabber::Grabber(Database::Db& db, + const std::filesystem::path& execPath, + const std::filesystem::path& defaultCoverPath) + : _db {db} + , _defaultCoverPath {defaultCoverPath} + , _maxCacheSize {Service::get()->getULong("cover-max-cache-size", 30) * 1000 * 1000} + , _maxFileSize {Service::get()->getULong("cover-max-file-size", 10) * 1000 * 1000} + { + 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) << "JPEG export quality = " << _jpegQuality; #if LMS_SUPPORT_IMAGE_GM GraphicsMagick::init(execPath); @@ -313,9 +313,9 @@ Grabber::getFromTrack(const std::filesystem::path& p, ImageSize width) const } std::shared_ptr -Grabber::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width) +Grabber::getFromTrack(Database::TrackId trackId, ImageSize width) { - return getFromTrack(dbSession, trackId, width, true /* allow release fallback*/); + return getFromTrack(_db.getTLSSession(), trackId, width, true /* allow release fallback*/); } std::shared_ptr @@ -338,7 +338,7 @@ Grabber::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, I cover = getFromSameNamedFile(trackInfo->trackPath, width); if (!cover && trackInfo->releaseId && allowReleaseFallback) - cover = getFromRelease(dbSession, *trackInfo->releaseId, width); + cover = getFromRelease(*trackInfo->releaseId, width); if (!cover && trackInfo->isMultiDisc) { @@ -357,7 +357,7 @@ Grabber::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, I } std::shared_ptr -Grabber::getFromRelease(Database::Session& session, Database::ReleaseId releaseId, ImageSize width) +Grabber::getFromRelease(Database::ReleaseId releaseId, ImageSize width) { const CacheEntryDesc cacheEntryDesc {releaseId, width}; @@ -371,6 +371,8 @@ Grabber::getFromRelease(Database::Session& session, Database::ReleaseId releaseI std::filesystem::path releaseDirectory; }; + Database::Session& session {_db.getTLSSession()}; + auto getReleaseInfo {[&] { std::optional res; @@ -418,6 +420,14 @@ Grabber::flushCache() _cache.clear(); } +void +Grabber::setJpegQuality(unsigned quality) +{ + _jpegQuality = Utils::clamp(quality, 1, 100); + + LMS_LOG(COVER, INFO) << "JPEG export quality = " << _jpegQuality; +} + void Grabber::saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr image) { diff --git a/src/libs/cover/impl/CoverArtGrabber.hpp b/src/libs/cover/impl/CoverArtGrabber.hpp index e5c33fb8..15e5f1ec 100644 --- a/src/libs/cover/impl/CoverArtGrabber.hpp +++ b/src/libs/cover/impl/CoverArtGrabber.hpp @@ -86,11 +86,9 @@ namespace CoverArt class Grabber : public IGrabber { public: - Grabber(const std::filesystem::path& execPath, - const std::filesystem::path& defaultCoverPath, - std::size_t maxCacheEntries, - std::size_t maxFileSize, - unsigned jpegQuality); + Grabber(Database::Db& db, + const std::filesystem::path& execPath, + const std::filesystem::path& defaultCoverPath); Grabber(const Grabber&) = delete; Grabber& operator=(const Grabber&) = delete; @@ -98,9 +96,10 @@ namespace CoverArt Grabber& operator=(Grabber&&) = delete; private: - std::shared_ptr getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width) override; - std::shared_ptr getFromRelease(Database::Session& dbSession, Database::ReleaseId releaseId, ImageSize width) override; + std::shared_ptr getFromTrack(Database::TrackId trackId, ImageSize width) override; + std::shared_ptr getFromRelease(Database::ReleaseId releaseId, ImageSize width) override; void flushCache() override; + void setJpegQuality(unsigned quality) override; std::shared_ptr getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width, bool allowReleaseFallback); std::unique_ptr getFromAvMediaFile(const Av::IAudioFile& input, ImageSize width) const; @@ -114,6 +113,8 @@ namespace CoverArt bool checkCoverFile(const std::filesystem::path& directoryPath) const; + Database::Db& _db; + std::shared_mutex _cacheMutex; std::unordered_map> _cache; std::unordered_map> _defaultCoverCache; @@ -129,7 +130,7 @@ namespace CoverArt static inline const std::vector _fileExtensions {".jpg", ".jpeg", ".png", ".bmp"}; // TODO parametrize const std::size_t _maxFileSize; static inline const std::vector _preferredFileNames {"cover", "front"}; // TODO parametrize - const unsigned _jpegQuality; + unsigned _jpegQuality; }; } // namespace CoverArt diff --git a/src/libs/cover/include/cover/ICoverArtGrabber.hpp b/src/libs/cover/include/cover/ICoverArtGrabber.hpp index 544cee7a..d6bcdf67 100644 --- a/src/libs/cover/include/cover/ICoverArtGrabber.hpp +++ b/src/libs/cover/include/cover/ICoverArtGrabber.hpp @@ -27,7 +27,7 @@ namespace Database { - class Session; + class Db; } namespace CoverArt @@ -37,17 +37,17 @@ namespace CoverArt public: virtual ~IGrabber() = default; - virtual std::shared_ptr getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width) = 0; - virtual std::shared_ptr getFromRelease(Database::Session& dbSession, Database::ReleaseId releaseId, ImageSize width) = 0; + virtual std::shared_ptr getFromTrack(Database::TrackId trackId, ImageSize width) = 0; + virtual std::shared_ptr getFromRelease(Database::ReleaseId releaseId, ImageSize width) = 0; virtual void flushCache() = 0; + + virtual void setJpegQuality(unsigned quality) = 0; // from 1 to 100 }; - std::unique_ptr createGrabber(const std::filesystem::path& execPath, - const std::filesystem::path& defaultCoverPath, - std::size_t maxCacheEntries, - std::size_t maxFileSize, - unsigned jpegQuality); + std::unique_ptr createGrabber(Database::Db& db, + const std::filesystem::path& execPath, + const std::filesystem::path& defaultCoverPath); } // namespace CoverArt diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index c3c4ba04..3fc97965 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -1795,9 +1795,9 @@ handleGetCoverArt(RequestContext& context, const Wt::Http::Request& /*request*/, std::shared_ptr cover; if (trackId) - cover = Service::get()->getFromTrack(context.dbSession, *trackId, size); + cover = Service::get()->getFromTrack(*trackId, size); else if (releaseId) - cover = Service::get()->getFromRelease(context.dbSession, *releaseId, size); + cover = Service::get()->getFromRelease(*releaseId, size); response.out().write(reinterpret_cast(cover->getData()), cover->getDataSize()); response.setMimeType(std::string {cover->getMimeType()}); diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 98c0cec6..e14c3573 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -250,13 +250,9 @@ int main(int argc, char* argv[]) else throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"}; - Service coverArtService {CoverArt::createGrabber(argv[0], - server.appRoot() + "/images/unknown-cover.jpg", - config->getULong("cover-max-cache-size", 30) * 1000 * 1000, - config->getULong("cover-max-file-size", 10) * 1000 * 1000, - config->getULong("cover-jpeg-quality", 75))}; + Service coverArtService {CoverArt::createGrabber(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; Service recommendationEngineService {Recommendation::createEngine(database)}; - Service scannerService {Scanner::createScanner(/*ioContext,*/ database, *recommendationEngineService)}; + Service scannerService {Scanner::createScanner(database, *recommendationEngineService)}; scannerService->getEvents().scanComplete.connect([&] { diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index a2978c6b..2ed89d9e 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -96,7 +96,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons return; } - cover = Service::get()->getFromTrack(LmsApp->getDbSession(), *trackId, *size); + cover = Service::get()->getFromTrack(*trackId, *size); } else if (releaseIdStr) { @@ -106,7 +106,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons if (!releaseId) return; - cover = Service::get()->getFromRelease(LmsApp->getDbSession(), *releaseId, *size); + cover = Service::get()->getFromRelease(*releaseId, *size); } else { diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index 6a9e2767..f1b44c97 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -47,7 +47,7 @@ dumpTrackCovers(Database::Session& session, CoverArt::ImageSize width) for (const Database::TrackId trackId : trackIds) { std::cout << "Getting cover for track id " << trackId.toString() << std::endl; - Service::get()->getFromTrack(session, trackId, width); + Service::get()->getFromTrack(trackId, width); } } @@ -82,15 +82,11 @@ int main(int argc, char *argv[]) } Service config {createConfig(vm["conf"].as())}; - - Service coverArtService {CoverArt::createGrabber(argv[0], - vm["default-cover"].as(), - config->getULong("cover-max-cache-size", 30) * 1000 * 1000, - config->getULong("cover-max-file-size", 10) * 1000 * 1000, - config->getULong("cover-jpeg-quality", vm["quality"].as()) - )}; - Database::Db db {config->getPath("working-dir") / "lms.db"}; + Service coverArtService {CoverArt::createGrabber(db, argv[0], vm["default-cover"].as())}; + + coverArtService->setJpegQuality(config->getULong("cover-jpeg-quality", vm["quality"].as())); + Database::Session session {db}; if (vm.count("tracks")) From 3f229dbbe301f950861f8073b213dbe2d90ea47d Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 2 Oct 2021 20:24:28 +0200 Subject: [PATCH 09/49] Removed session parameters from scrobbling service --- src/libs/scrobbling/impl/Scrobbling.cpp | 176 ++++++++++++------ src/libs/scrobbling/impl/Scrobbling.hpp | 58 +++--- .../include/scrobbling/IScrobbling.hpp | 64 +++---- src/libs/subsonic/impl/SubsonicResource.cpp | 13 +- src/lms/ui/explore/ArtistCollector.cpp | 20 +- src/lms/ui/explore/ReleaseCollector.cpp | 14 +- src/lms/ui/explore/TrackCollector.cpp | 37 ++-- 7 files changed, 233 insertions(+), 149 deletions(-) diff --git a/src/libs/scrobbling/impl/Scrobbling.cpp b/src/libs/scrobbling/impl/Scrobbling.cpp index add4cf2e..b1a91936 100644 --- a/src/libs/scrobbling/impl/Scrobbling.cpp +++ b/src/libs/scrobbling/impl/Scrobbling.cpp @@ -19,8 +19,11 @@ #include "Scrobbling.hpp" +#include "database/Artist.hpp" #include "database/Db.hpp" +#include "database/Release.hpp" #include "database/Session.hpp" +#include "database/Track.hpp" #include "database/TrackList.hpp" #include "database/User.hpp" @@ -29,13 +32,15 @@ namespace Scrobbling { + using namespace Database; + std::unique_ptr - createScrobbling(boost::asio::io_context& ioContext, Database::Db& db) + createScrobbling(boost::asio::io_context& ioContext, Db& db) { return std::make_unique(ioContext, db); } - Scrobbling::Scrobbling(boost::asio::io_context& ioContext, Database::Db& db) + Scrobbling::Scrobbling(boost::asio::io_context& ioContext, Db& db) : _db {db} { _scrobblers.emplace(Database::Scrobbler::Internal, std::make_unique(_db)); @@ -68,116 +73,169 @@ namespace Scrobbling { std::optional scrobbler; - Database::Session& session {_db.getTLSSession()}; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - if (const Database::User::pointer user {Database::User::getById(session, userId)}) + if (const User::pointer user {User::getById(session, userId)}) scrobbler = user->getScrobbler(); return scrobbler; } - std::vector> - Scrobbling::getRecentArtists(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) + Scrobbling::ArtistContainer + Scrobbling::getRecentArtists(UserId userId, + const std::vector& clusterIds, + std::optional linkType, + std::optional range, + bool& moreResults) { - const Database::ObjectPtr history {getListensTrackList(session, user)}; + ArtistContainer res; - std::vector> res; + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + const User::pointer user {User::getById(session, userId)}; + if (!user) + return res; + + const ObjectPtr history {getListensTrackList(session, user)}; if (history) - res = history->getArtistsReverse(clusterIds, linkType, range, moreResults); + { + for (const Artist::pointer& artist : history->getArtistsReverse(clusterIds, linkType, range, moreResults)) + res.push_back(artist->getId()); + } return res; } - std::vector> - Scrobbling::getRecentReleases(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) + Scrobbling::ReleaseContainer + Scrobbling::getRecentReleases(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) { - const Database::ObjectPtr history {getListensTrackList(session, user)}; + ReleaseContainer res; - std::vector> res; + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + const User::pointer user {User::getById(session, userId)}; + if (!user) + return res; + + const ObjectPtr history {getListensTrackList(session, user)}; if (history) - res = history->getReleasesReverse(clusterIds, range, moreResults); + { + for (const Release::pointer& release : history->getReleasesReverse(clusterIds, range, moreResults)) + res.push_back(release->getId()); + } return res; } - std::vector> - Scrobbling::getRecentTracks(Database::Session& session, - Database::ObjectPtr user, + Scrobbling::TrackContainer + Scrobbling::getRecentTracks(Database::UserId userId, const std::vector& clusterIds, std::optional range, bool& moreResults) { - const Database::ObjectPtr history {getListensTrackList(session, user)}; + TrackContainer res; - std::vector> res; + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + const User::pointer user {User::getById(session, userId)}; + if (!user) + return res; + + const ObjectPtr history {getListensTrackList(session, user)}; if (history) - res = history->getTracksReverse(clusterIds, range, moreResults); + { + for (const Track::pointer& track : history->getTracksReverse(clusterIds, range, moreResults)) + res.push_back(track->getId()); + } return res; } // Top - std::vector> - Scrobbling::getTopArtists(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) + Scrobbling::ArtistContainer + Scrobbling::getTopArtists(UserId userId, + const std::vector& clusterIds, + std::optional linkType, + std::optional range, + bool& moreResults) { - const Database::ObjectPtr history {getListensTrackList(session, user)}; + ArtistContainer res; - std::vector> res; + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + const User::pointer user {User::getById(session, userId)}; + if (!user) + return res; + + const ObjectPtr history {getListensTrackList(session, user)}; if (history) - res = history->getTopArtists(clusterIds, linkType, range, moreResults); + { + for (const Artist::pointer& artist : history->getTopArtists(clusterIds, linkType, range, moreResults)) + res.push_back(artist->getId()); + } return res; } - std::vector> - Scrobbling::getTopReleases(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) + Scrobbling::ReleaseContainer + Scrobbling::getTopReleases(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) { - const Database::ObjectPtr history {getListensTrackList(session, user)}; + ReleaseContainer res; - std::vector> res; + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + const User::pointer user {User::getById(session, userId)}; + if (!user) + return res; + + const ObjectPtr history {getListensTrackList(session, user)}; if (history) - res = history->getTopReleases(clusterIds, range, moreResults); + { + for (const Release::pointer& release : history->getTopReleases(clusterIds, range, moreResults)) + res.push_back(release->getId()); + } return res; } - std::vector> - Scrobbling::getTopTracks(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) + Scrobbling::TrackContainer + Scrobbling::getTopTracks(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) { - const Database::ObjectPtr history {getListensTrackList(session, user)}; + TrackContainer res; - std::vector> res; - if (history) - res = history->getTopTracks(clusterIds, range, moreResults); + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + const User::pointer user {User::getById(session, userId)}; + if (!user) + return res; + + if (const ObjectPtr history {getListensTrackList(session, user)}) + { + for (const Track::pointer& track : history->getTopTracks(clusterIds, range, moreResults)) + res.push_back(track->getId()); + } return res; } Database::ObjectPtr - Scrobbling::getListensTrackList(Database::Session& session, Database::ObjectPtr user) + Scrobbling::getListensTrackList(Session& session, Database::ObjectPtr user) { return _scrobblers[user->getScrobbler()]->getListensTrackList(session, user); } diff --git a/src/libs/scrobbling/impl/Scrobbling.hpp b/src/libs/scrobbling/impl/Scrobbling.hpp index 6aa2ed2f..dd338dd3 100644 --- a/src/libs/scrobbling/impl/Scrobbling.hpp +++ b/src/libs/scrobbling/impl/Scrobbling.hpp @@ -38,43 +38,37 @@ namespace Scrobbling void listenFinished(const Listen& listen, std::optional duration) override; void addTimedListen(const TimedListen& listen) override; - std::vector> getRecentArtists(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) override; + ArtistContainer getRecentArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + std::optional range, + bool& moreResults) override; - std::vector> getRecentReleases(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + ReleaseContainer getRecentReleases(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) override; - std::vector> getRecentTracks(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + TrackContainer getRecentTracks(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) override; - std::vector> getTopArtists(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) override; + ArtistContainer getTopArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + std::optional range, + bool& moreResults) override; - std::vector> getTopReleases(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + ReleaseContainer getTopReleases(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) override; - std::vector> getTopTracks(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + TrackContainer getTopTracks(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) override; Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user); diff --git a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp index 6f4436c7..cda27ca1 100644 --- a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp +++ b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp @@ -56,45 +56,45 @@ namespace Scrobbling virtual void addTimedListen(const TimedListen& listen) = 0; // Stats + template + using ResultContainer = std::vector; + + using ArtistContainer = ResultContainer; + using ReleaseContainer = ResultContainer; + using TrackContainer = ResultContainer; // From most recent to oldest - virtual std::vector> getRecentArtists(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) = 0; + virtual ArtistContainer getRecentArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + std::optional range, + bool& moreResults) = 0; - virtual std::vector> getRecentReleases(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + virtual ReleaseContainer getRecentReleases(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) = 0; - virtual std::vector> getRecentTracks(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + virtual TrackContainer getRecentTracks(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) = 0; // Top - virtual std::vector> getTopArtists(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) = 0; + virtual ArtistContainer getTopArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + std::optional range, + bool& moreResults) = 0; - virtual std::vector> getTopReleases(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + virtual ReleaseContainer getTopReleases(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) = 0; - virtual std::vector> getTopTracks(Database::Session& session, - Database::ObjectPtr user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + virtual TrackContainer getTopTracks(Database::UserId userId, + const std::vector& clusterIds, + std::optional range, + bool& moreResults) = 0; }; std::unique_ptr createScrobbling(boost::asio::io_service& ioService, Database::Db& db); diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 3fc97965..3f6e9ec8 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -724,6 +724,7 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) const Range range {offset, size}; std::vector releases; + Scrobbling::IScrobbling& scrobbling {*Service::get()}; auto transaction {context.dbSession.createSharedTransaction()}; @@ -765,7 +766,11 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) else if (type == "frequent") { bool moreResults {}; - releases = Service::get()->getTopReleases(context.dbSession, user, {}, range, moreResults); + for (ReleaseId releaseId : scrobbling.getTopReleases(context.userId, {}, range, moreResults)) + { + if (Release::pointer release {Release::getById(context.dbSession, releaseId)}) + releases.push_back(release ); + } } else if (type == "newest") { @@ -780,7 +785,11 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) else if (type == "recent") { bool moreResults {}; - releases = Service::get()->getRecentReleases(context.dbSession, user, {}, range, moreResults); + for (ReleaseId releaseId : scrobbling.getRecentReleases(context.userId, {}, range, moreResults)) + { + if (Release::pointer release {Release::getById(context.dbSession, releaseId)}) + releases.push_back(release ); + } } else if (type == "starred") { diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 49e19731..0c73aa3e 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -34,6 +34,8 @@ namespace UserInterface std::vector> ArtistCollector::get(std::optional range, bool& moreResults) { + Scrobbling::IScrobbling& scrobbling {*Service::get()}; + range = getActualRange(range); std::vector artists; @@ -53,17 +55,19 @@ namespace UserInterface break; case Mode::RecentlyPlayed: - artists = Service::get()->getRecentArtists(LmsApp->getDbSession(), LmsApp->getUser(), - getFilters().getClusterIds(), - _linkType, - range, moreResults); + for (const ArtistId artistId : scrobbling.getRecentArtists(LmsApp->getUserId(), getFilters().getClusterIds(), _linkType, range, moreResults)) + { + if (Artist::pointer artist {Artist::getById(LmsApp->getDbSession(), artistId)}) + artists.push_back(artist); + } break; case Mode::MostPlayed: - artists = Service::get()->getTopArtists(LmsApp->getDbSession(), LmsApp->getUser(), - getFilters().getClusterIds(), - _linkType, - range, moreResults); + for (const ArtistId artistId : scrobbling.getTopArtists(LmsApp->getUserId(), getFilters().getClusterIds(), _linkType, range, moreResults)) + { + if (Artist::pointer artist {Artist::getById(LmsApp->getDbSession(), artistId)}) + artists.push_back(artist); + } break; case Mode::RecentlyAdded: diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index 473278df..c5671f54 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -37,6 +37,8 @@ namespace UserInterface std::vector ReleaseCollector::get(std::optional range, bool& moreResults) { + Scrobbling::IScrobbling& scrobbling {*Service::get()}; + range = getActualRange(range); std::vector releases; @@ -51,11 +53,19 @@ namespace UserInterface break; case ReleaseCollector::Mode::RecentlyPlayed: - releases = Service::get()->getRecentReleases(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); + for (const ReleaseId releaseId : scrobbling.getRecentReleases(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) + { + if (const Release::pointer release {Release::getById(LmsApp->getDbSession(), releaseId)}) + releases.push_back(release); + } break; case Mode::MostPlayed: - releases = Service::get()->getTopReleases(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); + for (const ReleaseId releaseId : scrobbling.getTopReleases(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) + { + if (const Release::pointer release {Release::getById(LmsApp->getDbSession(), releaseId)}) + releases.push_back(release); + } break; case Mode::RecentlyAdded: diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 26495626..77106931 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -37,44 +37,53 @@ namespace UserInterface std::vector TrackCollector::get(std::optional range, bool& moreResults) { + Scrobbling::IScrobbling& scrobbling {*Service::get()}; range = getActualRange(range); - std::vector releases; + std::vector tracks; switch (getMode()) { case Mode::Random: - releases = getRandomTracks(range, moreResults); + tracks = getRandomTracks(range, moreResults); break; case Mode::Starred: - releases = Track::getStarred(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); + tracks = Track::getStarred(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); break; case TrackCollector::Mode::RecentlyPlayed: - releases = Service::get()->getRecentTracks(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); + for (const TrackId trackId : scrobbling.getRecentTracks(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) + { + if (const Track::pointer track {Track::getById(LmsApp->getDbSession(), trackId)}) + tracks.push_back(track); + } break; case Mode::MostPlayed: - releases = Service::get()->getTopTracks(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); + for (const TrackId trackId : scrobbling.getTopTracks(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) + { + if (const Track::pointer track {Track::getById(LmsApp->getDbSession(), trackId)}) + tracks.push_back(track); + } break; case Mode::RecentlyAdded: - releases = Track::getLastWritten(LmsApp->getDbSession(), std::nullopt, getFilters().getClusterIds(), range, moreResults); + tracks = Track::getLastWritten(LmsApp->getDbSession(), std::nullopt, getFilters().getClusterIds(), range, moreResults); break; case Mode::Search: - releases = Track::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), getSearchKeywords(), range, moreResults); + tracks = Track::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), getSearchKeywords(), range, moreResults); break; case Mode::All: - releases = Track::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), {}, range, moreResults); + tracks = Track::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), {}, range, moreResults); break; } if (range && getMaxCount() && (range->offset + range->limit == *getMaxCount())) moreResults = false; - return releases; + return tracks; } std::vector @@ -95,7 +104,7 @@ namespace UserInterface std::vector TrackCollector::getRandomTracks(std::optional range, bool& moreResults) { - std::vector releases; + std::vector tracks; assert(getMode() == Mode::Random); @@ -108,15 +117,15 @@ namespace UserInterface for (auto it {itBegin}; it != itEnd; ++it) { - Track::pointer release {Track::getById(LmsApp->getDbSession(), *it)}; - if (release) - releases.push_back(release); + Track::pointer track {Track::getById(LmsApp->getDbSession(), *it)}; + if (track) + tracks.push_back(track); } moreResults = (itEnd != std::cend(_randomTracks)); } - return releases; + return tracks; } } // ns UserInterface From 44116bc055511f5859f5dfbc1339fbf02161aa07 Mon Sep 17 00:00:00 2001 From: Bram Date: Sat, 2 Oct 2021 21:44:52 +0200 Subject: [PATCH 10/49] Added missing include This fixes the compilation on FreeBSD. --- src/libs/subsonic/impl/SubsonicResource.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index fd53029b..8efa1094 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -19,6 +19,8 @@ #pragma once #include +#include + #include #include From cbc137529b2528331328fbeb73893536296bcad8 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 3 Oct 2021 14:07:33 +0200 Subject: [PATCH 11/49] Made connection count aligned with thread count --- src/libs/database/impl/Db.cpp | 4 ++-- src/libs/database/include/database/Db.hpp | 2 +- src/lms/main.cpp | 19 ++++++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/libs/database/impl/Db.cpp b/src/libs/database/impl/Db.cpp index 9854facb..5035466c 100644 --- a/src/libs/database/impl/Db.cpp +++ b/src/libs/database/impl/Db.cpp @@ -29,7 +29,7 @@ namespace Database { // Session living class handling the database and the login -Db::Db(const std::filesystem::path& dbPath) +Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount) { LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string(); @@ -38,7 +38,7 @@ Db::Db(const std::filesystem::path& dbPath) connection->executeSql("pragma journal_mode=WAL"); connection->executeSql("pragma synchronous=normal"); - auto connectionPool = std::make_unique(std::move(connection), 10); + auto connectionPool = std::make_unique(std::move(connection), connectionCount); connectionPool->setTimeout(std::chrono::seconds(10)); _connectionPool = std::move(connectionPool); diff --git a/src/libs/database/include/database/Db.hpp b/src/libs/database/include/database/Db.hpp index bdcd20b3..5effb856 100644 --- a/src/libs/database/include/database/Db.hpp +++ b/src/libs/database/include/database/Db.hpp @@ -32,7 +32,7 @@ class Db { public: - Db(const std::filesystem::path& dbPath); + Db(const std::filesystem::path& dbPath, std::size_t connectionCount = 10); ~Db(); Db(const Db&) = delete; diff --git a/src/lms/main.cpp b/src/lms/main.cpp index e14c3573..26da8ef4 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -44,6 +44,16 @@ #include "utils/String.hpp" #include "utils/WtLogger.hpp" +static +std::size_t +getThreadCount() +{ + 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) @@ -54,7 +64,6 @@ generateWtConfig(std::string execPath) 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")}; - const unsigned long configHttpServerThreadCount {Service::get()->getULong("http-server-thread-count", 0)}; args.push_back(execPath); args.push_back("--config=" + wtConfigPath.string()); @@ -81,11 +90,7 @@ generateWtConfig(std::string execPath) if (!wtAccessLogFilePath.empty()) args.push_back("--accesslog=" + wtAccessLogFilePath.string()); - { - // Reserve at least 2 threads since we still have some blocking IO (for example when reading from ffmpeg) - const unsigned long httpServerThreadCount {configHttpServerThreadCount ? configHttpServerThreadCount : std::max(2, std::thread::hardware_concurrency())}; - args.push_back("--threads=" + std::to_string(httpServerThreadCount)); - } + args.push_back("--threads=" + std::to_string(getThreadCount())); // Generate the wt_config.xml file boost::property_tree::ptree pt; @@ -221,7 +226,7 @@ int main(int argc, char* argv[]) IOContextRunner ioContextRunner {ioContext, std::max(2, std::thread::hardware_concurrency())}; // Initializing a connection pool to the database that will be shared along services - Database::Db database {config->getPath("working-dir") / "lms.db"}; + Database::Db database {config->getPath("working-dir") / "lms.db", getThreadCount()}; { Database::Session session {database}; session.prepareTables(); From 617139c2f4d28eb252790d321b016980edf0033b Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 3 Oct 2021 14:35:25 +0200 Subject: [PATCH 12/49] Removed session parameters from auth services --- src/libs/auth/impl/AuthServiceBase.cpp | 32 ++++++++++++++----- src/libs/auth/impl/AuthServiceBase.hpp | 12 +++++-- src/libs/auth/impl/AuthTokenService.cpp | 27 +++++++++------- src/libs/auth/impl/AuthTokenService.hpp | 10 +++--- src/libs/auth/impl/EnvService.cpp | 4 +-- src/libs/auth/impl/PasswordServiceBase.cpp | 22 ++++++------- src/libs/auth/impl/PasswordServiceBase.hpp | 14 ++++---- .../http-headers/HttpHeadersEnvService.cpp | 17 +++++----- .../http-headers/HttpHeadersEnvService.hpp | 6 ++-- .../impl/internal/InternalPasswordService.cpp | 14 ++++---- .../impl/internal/InternalPasswordService.hpp | 8 ++--- src/libs/auth/impl/pam/PAMPasswordService.cpp | 4 +-- src/libs/auth/impl/pam/PAMPasswordService.hpp | 9 ++---- .../auth/include/auth/IAuthTokenService.hpp | 10 +++--- src/libs/auth/include/auth/IEnvService.hpp | 7 ++-- .../auth/include/auth/IPasswordService.hpp | 13 ++++---- src/libs/subsonic/impl/SubsonicResource.cpp | 23 ++++++------- src/libs/subsonic/impl/SubsonicResource.hpp | 2 +- src/lms/main.cpp | 6 ++-- src/lms/ui/Auth.cpp | 5 ++- src/lms/ui/LmsApplication.cpp | 2 +- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 4 +-- src/lms/ui/common/PasswordValidator.cpp | 1 - 25 files changed, 135 insertions(+), 121 deletions(-) diff --git a/src/libs/auth/impl/AuthServiceBase.cpp b/src/libs/auth/impl/AuthServiceBase.cpp index b4ab450a..6e0d0589 100644 --- a/src/libs/auth/impl/AuthServiceBase.cpp +++ b/src/libs/auth/impl/AuthServiceBase.cpp @@ -19,25 +19,33 @@ #include "AuthServiceBase.hpp" +#include "database/Db.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Logger.hpp" namespace Auth { - Database::UserId - AuthServiceBase::getOrCreateUser(Database::Session& session, std::string_view loginName) + using namespace Database; + + AuthServiceBase::AuthServiceBase(Db& db) + : _db {db} + {} + + UserId + AuthServiceBase::getOrCreateUser(std::string_view loginName) { + Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; - Database::User::pointer user {Database::User::getByLoginName(session, loginName)}; + User::pointer user {User::getByLoginName(session, loginName)}; if (!user) { - const Database::UserType type {Database::User::getCount(session) == 0 ? Database::UserType::ADMIN : Database::UserType::REGULAR}; + const UserType type {User::getCount(session) == 0 ? UserType::ADMIN : UserType::REGULAR}; - LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == Database::UserType::ADMIN); + LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN); - user = Database::User::create(session, loginName); + user = User::create(session, loginName); user.modify()->setType(type); } @@ -45,11 +53,19 @@ namespace Auth } void - AuthServiceBase::onUserAuthenticated(Database::Session& session, Database::UserId userId) + AuthServiceBase::onUserAuthenticated(UserId userId) { + Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; - Database::User::pointer user {Database::User::getById(session, userId)}; + + User::pointer user {User::getById(session, userId)}; if (user) user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); } + + Session& + AuthServiceBase::getDbSession() + { + return _db.getTLSSession(); + } } diff --git a/src/libs/auth/impl/AuthServiceBase.hpp b/src/libs/auth/impl/AuthServiceBase.hpp index 032d8f84..81acba59 100644 --- a/src/libs/auth/impl/AuthServiceBase.hpp +++ b/src/libs/auth/impl/AuthServiceBase.hpp @@ -24,6 +24,7 @@ namespace Database { + class Db; class Session; } @@ -32,7 +33,14 @@ namespace Auth class AuthServiceBase { protected: - Database::UserId getOrCreateUser(Database::Session& session, std::string_view loginName); - void onUserAuthenticated(Database::Session& session, Database::UserId userId); + AuthServiceBase(Database::Db& db); + + Database::UserId getOrCreateUser(std::string_view loginName); + void onUserAuthenticated(Database::UserId userId); + + Database::Session& getDbSession(); + + private: + Database::Db& _db; }; } diff --git a/src/libs/auth/impl/AuthTokenService.cpp b/src/libs/auth/impl/AuthTokenService.cpp index 89ad8870..f6f305b6 100644 --- a/src/libs/auth/impl/AuthTokenService.cpp +++ b/src/libs/auth/impl/AuthTokenService.cpp @@ -32,24 +32,27 @@ namespace Auth { - std::unique_ptr createAuthTokenService(std::size_t maxThrottlerEntries) + std::unique_ptr createAuthTokenService(Database::Db& db, std::size_t maxThrottlerEntries) { - return std::make_unique(maxThrottlerEntries); + return std::make_unique(db, maxThrottlerEntries); } static const Wt::Auth::SHA1HashFunction sha1Function; - AuthTokenService::AuthTokenService(std::size_t maxThrottlerEntries) - : _loginThrottler {maxThrottlerEntries} + AuthTokenService::AuthTokenService(Database::Db& db, std::size_t maxThrottlerEntries) + : AuthServiceBase {db} + , _loginThrottler {maxThrottlerEntries} { } std::string - AuthTokenService::createAuthToken(Database::Session& session, Database::UserId userId, const Wt::WDateTime& expiry) + AuthTokenService::createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry) { const std::string secret {Wt::WRandom::generateId(32)}; const std::string secretHash {sha1Function.compute(secret, {})}; + Database::Session& session {getDbSession()}; + auto transaction {session.createUniqueTransaction()}; Database::User::pointer user {Database::User::getById(session, userId)}; @@ -66,12 +69,12 @@ namespace Auth return secret; } - static std::optional - processAuthToken(Database::Session& session, std::string_view secret) + AuthTokenService::processAuthToken(std::string_view secret) { const std::string secretHash {sha1Function.compute(std::string {secret}, {})}; + Database::Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; Database::AuthToken::pointer authToken {Database::AuthToken::getByValue(session, secretHash)}; @@ -93,7 +96,7 @@ namespace Auth } AuthTokenService::AuthTokenProcessResult - AuthTokenService::processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) + AuthTokenService::processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) { // Do not waste too much resource on brute force attacks (optim) { @@ -103,7 +106,7 @@ namespace Auth return AuthTokenProcessResult {AuthTokenProcessResult::State::Throttled}; } - auto res {Auth::processAuthToken(session, tokenValue)}; + auto res {processAuthToken(tokenValue)}; { std::unique_lock lock {_mutex}; @@ -117,14 +120,16 @@ namespace Auth } _loginThrottler.onGoodClientAttempt(clientAddress); - onUserAuthenticated(session, res->userId); + onUserAuthenticated(res->userId); return AuthTokenProcessResult {AuthTokenProcessResult::State::Granted, std::move(*res)}; } } void - AuthTokenService::clearAuthTokens(Database::Session& session, Database::UserId userId) + AuthTokenService::clearAuthTokens(Database::UserId userId) { + Database::Session& session {getDbSession()}; + auto transaction {session.createUniqueTransaction()}; Database::User::pointer user {Database::User::getById(session, userId)}; diff --git a/src/libs/auth/impl/AuthTokenService.hpp b/src/libs/auth/impl/AuthTokenService.hpp index 088f82ee..8825a3d3 100644 --- a/src/libs/auth/impl/AuthTokenService.hpp +++ b/src/libs/auth/impl/AuthTokenService.hpp @@ -35,7 +35,7 @@ namespace Auth class AuthTokenService : public IAuthTokenService, public AuthServiceBase { public: - AuthTokenService(std::size_t maxThrottlerEntries); + AuthTokenService(Database::Db& db, std::size_t maxThrottlerEntries); AuthTokenService(const AuthTokenService&) = delete; AuthTokenService& operator=(const AuthTokenService&) = delete; @@ -43,9 +43,11 @@ namespace Auth AuthTokenService& operator=(AuthTokenService&&) = delete; private: - AuthTokenProcessResult processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) override; - std::string createAuthToken(Database::Session& session, Database::UserId userId, const Wt::WDateTime& expiry) override; - void clearAuthTokens(Database::Session& session, Database::UserId userId) override; + AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) override; + std::string createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry) override; + void clearAuthTokens(Database::UserId userId) override; + + std::optional processAuthToken(std::string_view secret); std::shared_mutex _mutex; LoginThrottler _loginThrottler; diff --git a/src/libs/auth/impl/EnvService.cpp b/src/libs/auth/impl/EnvService.cpp index 5ca529da..12de9858 100644 --- a/src/libs/auth/impl/EnvService.cpp +++ b/src/libs/auth/impl/EnvService.cpp @@ -25,10 +25,10 @@ namespace Auth { std::unique_ptr - createEnvService(std::string_view backendName) + createEnvService(std::string_view backendName, Database::Db& db) { if (backendName == "http-headers") - return std::make_unique(); + return std::make_unique(db); throw Exception {"Authentication backend '" + std::string {backendName} + "' is not supported!"}; } diff --git a/src/libs/auth/impl/PasswordServiceBase.cpp b/src/libs/auth/impl/PasswordServiceBase.cpp index 02a9c63d..3b230503 100644 --- a/src/libs/auth/impl/PasswordServiceBase.cpp +++ b/src/libs/auth/impl/PasswordServiceBase.cpp @@ -39,29 +39,27 @@ namespace Auth static const Wt::Auth::SHA1HashFunction sha1Function; std::unique_ptr - createPasswordService(std::string_view passwordAuthenticationBackend, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) + createPasswordService(std::string_view passwordAuthenticationBackend, Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) { if (passwordAuthenticationBackend == "internal") - return std::make_unique(maxThrottlerEntries, authTokenService); + return std::make_unique(db, maxThrottlerEntries, authTokenService); #ifdef LMS_SUPPORT_PAM else if (passwordAuthenticationBackend == "pam") - return std::make_unique(maxThrottlerEntries, authTokenService); + return std::make_unique(db, maxThrottlerEntries, authTokenService); #endif // LMS_SUPPORT_PAM throw Exception {"Authentication backend '" + std::string {passwordAuthenticationBackend} + "' is not supported!"}; } - PasswordServiceBase::PasswordServiceBase(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) - : _loginThrottler {maxThrottlerEntries} + PasswordServiceBase::PasswordServiceBase(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) + : AuthServiceBase {db} + , _loginThrottler {maxThrottlerEntries} , _authTokenService {authTokenService} { } PasswordServiceBase::CheckResult - PasswordServiceBase::checkUserPassword(Database::Session& session, - const boost::asio::ip::address& clientAddress, - std::string_view loginName, - std::string_view password) + 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 << "'"; @@ -73,7 +71,7 @@ namespace Auth return {CheckResult::State::Throttled}; } - const bool match {checkUserPassword(session, loginName, password)}; + const bool match {checkUserPassword(loginName, password)}; { std::unique_lock lock {_mutex}; @@ -84,8 +82,8 @@ namespace Auth { _loginThrottler.onGoodClientAttempt(clientAddress); - const Database::UserId userId {getOrCreateUser(session, loginName)}; - onUserAuthenticated(session, userId); + const Database::UserId userId {getOrCreateUser(loginName)}; + onUserAuthenticated(userId); return {CheckResult::State::Granted, userId}; } else diff --git a/src/libs/auth/impl/PasswordServiceBase.hpp b/src/libs/auth/impl/PasswordServiceBase.hpp index 8b5bc3dd..f575e4e1 100644 --- a/src/libs/auth/impl/PasswordServiceBase.hpp +++ b/src/libs/auth/impl/PasswordServiceBase.hpp @@ -27,6 +27,7 @@ namespace Database { + class Db; class Session; } @@ -36,7 +37,7 @@ namespace Auth class PasswordServiceBase : public IPasswordService, public AuthServiceBase { public: - PasswordServiceBase(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); + PasswordServiceBase(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); PasswordServiceBase(const PasswordServiceBase&) = delete; PasswordServiceBase& operator=(const PasswordServiceBase&) = delete; @@ -47,14 +48,11 @@ namespace Auth IAuthTokenService& getAuthTokenService() { return _authTokenService; } private: - virtual bool checkUserPassword(Database::Session& session, - std::string_view loginName, - std::string_view password) = 0; + virtual bool checkUserPassword(std::string_view loginName, std::string_view password) = 0; - CheckResult checkUserPassword(Database::Session& session, - const boost::asio::ip::address& clientAddress, - std::string_view loginName, - std::string_view password) override; + CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress, + std::string_view loginName, + std::string_view password) override; std::shared_mutex _mutex; LoginThrottler _loginThrottler; diff --git a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp index 3db1fb4a..951925ec 100644 --- a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp +++ b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp @@ -28,14 +28,15 @@ namespace Auth { - HttpHeadersEnvService::HttpHeadersEnvService() - : _fieldName {Service::get()->getString("http-headers-login-field", "X-Forwarded-User")} + 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(Database::Session& session, const Wt::WEnvironment& env) + HttpHeadersEnvService::processEnv(const Wt::WEnvironment& env) { const std::string loginName {env.headerValue(_fieldName)}; if (loginName.empty()) @@ -43,13 +44,13 @@ namespace Auth LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; - const Database::UserId userId {getOrCreateUser(session, loginName)}; - onUserAuthenticated(session, userId); + const Database::UserId userId {getOrCreateUser(loginName)}; + onUserAuthenticated(userId); return {CheckResult::State::Granted, userId}; } HttpHeadersEnvService::CheckResult - HttpHeadersEnvService::processRequest(Database::Session& session, const Wt::Http::Request& request) + HttpHeadersEnvService::processRequest(const Wt::Http::Request& request) { const std::string loginName {request.headerValue(_fieldName)}; if (loginName.empty()) @@ -57,8 +58,8 @@ namespace Auth LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; - const Database::UserId userId {getOrCreateUser(session, loginName)}; - onUserAuthenticated(session, userId); + const Database::UserId userId {getOrCreateUser(loginName)}; + onUserAuthenticated(userId); return {CheckResult::State::Granted, userId}; } diff --git a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp index 1295f182..5c44b9fb 100644 --- a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp +++ b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp @@ -27,11 +27,11 @@ namespace Auth class HttpHeadersEnvService : public IEnvService, public AuthServiceBase { public: - HttpHeadersEnvService(); + HttpHeadersEnvService(Database::Db& db); private: - CheckResult processEnv(Database::Session& session, const Wt::WEnvironment& env) override; - CheckResult processRequest(Database::Session& session, const Wt::Http::Request& request) override; + CheckResult processEnv(const Wt::WEnvironment& env) override; + CheckResult processRequest(const Wt::Http::Request& request) override; std::string _fieldName; }; diff --git a/src/libs/auth/impl/internal/InternalPasswordService.cpp b/src/libs/auth/impl/internal/InternalPasswordService.cpp index a021dd3c..96a6c34a 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.cpp @@ -29,8 +29,8 @@ namespace Auth { - InternalPasswordService::InternalPasswordService(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) - : PasswordServiceBase {maxThrottlerEntries, authTokenService} + InternalPasswordService::InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService) + : PasswordServiceBase {db, maxThrottlerEntries, authTokenService} { _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::OneCharClass, 4); _validator.setMinimumLength(Wt::Auth::PasswordStrengthType::TwoCharClass, 4); @@ -42,14 +42,13 @@ namespace Auth } bool - InternalPasswordService::checkUserPassword(Database::Session& session, - std::string_view loginName, - std::string_view password) + InternalPasswordService::checkUserPassword(std::string_view loginName, std::string_view password) { LMS_LOG(AUTH, DEBUG) << "Checking internal password for user '" << loginName << "'"; Database::User::PasswordHash passwordHash; { + Database::Session& session {getDbSession()}; auto transaction {session.createSharedTransaction()}; const Database::User::pointer user {Database::User::getByLoginName(session, loginName)}; @@ -96,10 +95,11 @@ namespace Auth } void - InternalPasswordService::setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) + InternalPasswordService::setPassword(Database::UserId userId, std::string_view newPassword) { const Database::User::PasswordHash passwordHash {hashPassword(newPassword)}; + Database::Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; Database::User::pointer user {Database::User::getById(session, userId)}; @@ -117,7 +117,7 @@ namespace Auth } user.modify()->setPasswordHash(passwordHash); - getAuthTokenService().clearAuthTokens(session, userId); + getAuthTokenService().clearAuthTokens(userId); } Database::User::PasswordHash diff --git a/src/libs/auth/impl/internal/InternalPasswordService.hpp b/src/libs/auth/impl/internal/InternalPasswordService.hpp index 33cdb5cd..6c241f31 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.hpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.hpp @@ -33,16 +33,14 @@ namespace Auth class InternalPasswordService : public PasswordServiceBase { public: - InternalPasswordService(std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); + InternalPasswordService(Database::Db& db, std::size_t maxThrottlerEntries, IAuthTokenService& authTokenService); private: - bool checkUserPassword(Database::Session& session, - std::string_view loginName, - std::string_view password) override; + bool checkUserPassword(std::string_view loginName, std::string_view password) override; bool canSetPasswords() const override; PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; - void setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) override; + void setPassword(Database::UserId userId, std::string_view newPassword) override; Database::User::PasswordHash hashPassword(std::string_view password) const; void hashRandomPassword() const; diff --git a/src/libs/auth/impl/pam/PAMPasswordService.cpp b/src/libs/auth/impl/pam/PAMPasswordService.cpp index 1b25f537..c5682f42 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.cpp @@ -161,7 +161,7 @@ namespace Auth }; bool - PAMPasswordService::checkUserPassword(Database::Session& /*session*/, std::string_view loginName, std::string_view password) + PAMPasswordService::checkUserPassword(std::string_view loginName, std::string_view password) { try { @@ -193,7 +193,7 @@ namespace Auth } void - PAMPasswordService::setPassword(Database::Session&, Database::UserId, std::string_view) + PAMPasswordService::setPassword(Database::UserId, std::string_view) { throw NotImplementedException {}; } diff --git a/src/libs/auth/impl/pam/PAMPasswordService.hpp b/src/libs/auth/impl/pam/PAMPasswordService.hpp index 4b661b4b..2de5bc82 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.hpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.hpp @@ -31,14 +31,9 @@ namespace Auth using PasswordServiceBase::PasswordServiceBase; private: - bool checkUserPassword(Database::Session& session, - std::string_view loginName, - std::string_view password) override; - + bool checkUserPassword(std::string_view loginName,std::string_view password) override; bool canSetPasswords() const override; PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; - void setPassword(Database::Session& session, - Database::UserId userId, - std::string_view newPassword) override; + void setPassword(Database::UserId userId, std::string_view newPassword) override; }; } diff --git a/src/libs/auth/include/auth/IAuthTokenService.hpp b/src/libs/auth/include/auth/IAuthTokenService.hpp index e57ee6a9..5d9dd51b 100644 --- a/src/libs/auth/include/auth/IAuthTokenService.hpp +++ b/src/libs/auth/include/auth/IAuthTokenService.hpp @@ -31,7 +31,7 @@ namespace Database { - class Session; + class Db; class User; } @@ -63,12 +63,12 @@ namespace Auth }; // Provided token is only accepted once - virtual AuthTokenProcessResult processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) = 0; + virtual AuthTokenProcessResult processAuthToken(const boost::asio::ip::address& clientAddress, std::string_view tokenValue) = 0; // Returns a one time token - virtual std::string createAuthToken(Database::Session& session, Database::UserId userid, const Wt::WDateTime& expiry) = 0; - virtual void clearAuthTokens(Database::Session& session, Database::UserId userid) = 0; + virtual std::string createAuthToken(Database::UserId userid, const Wt::WDateTime& expiry) = 0; + virtual void clearAuthTokens(Database::UserId userid) = 0; }; - std::unique_ptr createAuthTokenService(std::size_t maxThrottlerEntryCount); + std::unique_ptr createAuthTokenService(Database::Db& db, std::size_t maxThrottlerEntryCount); } diff --git a/src/libs/auth/include/auth/IEnvService.hpp b/src/libs/auth/include/auth/IEnvService.hpp index b001d602..ecb23349 100644 --- a/src/libs/auth/include/auth/IEnvService.hpp +++ b/src/libs/auth/include/auth/IEnvService.hpp @@ -26,6 +26,7 @@ namespace Database { + class Db; class Session; } @@ -60,9 +61,9 @@ namespace Auth std::optional userId {}; }; - virtual CheckResult processEnv(Database::Session& session, const Wt::WEnvironment& env) = 0; - virtual CheckResult processRequest(Database::Session& session, const Wt::Http::Request& request) = 0; + virtual CheckResult processEnv(const Wt::WEnvironment& env) = 0; + virtual CheckResult processRequest(const Wt::Http::Request& request) = 0; }; - std::unique_ptr createEnvService(std::string_view backendName); + std::unique_ptr createEnvService(std::string_view backendName, Database::Db& db); } // namespace Auth diff --git a/src/libs/auth/include/auth/IPasswordService.hpp b/src/libs/auth/include/auth/IPasswordService.hpp index 69278fc5..f197ae16 100644 --- a/src/libs/auth/include/auth/IPasswordService.hpp +++ b/src/libs/auth/include/auth/IPasswordService.hpp @@ -30,7 +30,7 @@ namespace Database { - class Session; + class Db; class User; } @@ -56,10 +56,9 @@ namespace Auth std::optional userId {}; std::optional expiry {}; }; - virtual CheckResult checkUserPassword(Database::Session& session, - const boost::asio::ip::address& clientAddress, - std::string_view loginName, - std::string_view password) = 0; + virtual CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress, + std::string_view loginName, + std::string_view password) = 0; virtual bool canSetPasswords() const = 0; @@ -70,9 +69,9 @@ namespace Auth MustMatchLoginName, }; virtual PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const = 0; - virtual void setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) = 0; + virtual void setPassword(Database::UserId userId, std::string_view newPassword) = 0; }; - std::unique_ptr createPasswordService(std::string_view authPasswordBackend, std::size_t maxThrottlerEntryCount, IAuthTokenService& authTokenService); + std::unique_ptr createPasswordService(std::string_view authPasswordBackend, Database::Db& db, std::size_t maxThrottlerEntryCount, IAuthTokenService& authTokenService); } diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 3f6e9ec8..5cc0b4e4 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -506,7 +506,7 @@ handleChangePassword(RequestContext& context) userId = user->getId(); } - Service::get()->setPassword(context.dbSession, userId, password); + Service::get()->setPassword(userId, password); } catch (const Auth::PasswordMustMatchLoginNameException&) { @@ -604,7 +604,7 @@ handleCreateUserRequest(RequestContext& context) try { - Service::get()->setPassword(context.dbSession, userId, password); + Service::get()->setPassword(userId, password); } catch (const Auth::PasswordMustMatchLoginNameException&) { @@ -1620,7 +1620,7 @@ handleUpdateUserRequest(RequestContext& context) try { - Service<::Auth::IPasswordService>()->setPassword(context.dbSession, userId, decodePasswordIfNeeded(*password)); + Service<::Auth::IPasswordService>()->setPassword(userId, decodePasswordIfNeeded(*password)); } catch (const Auth::PasswordMustMatchLoginNameException&) { @@ -2043,22 +2043,18 @@ RequestContext SubsonicResource::buildRequestContext(const Wt::Http::Request& request) { const Wt::Http::ParameterMap& parameters {request.getParameterMap()}; - const ClientInfo clientInfo {getClientInfo(parameters)}; + const Database::UserId userId {authenticateUser(request, clientInfo)}; - Session& dbSession {_db.getTLSSession()}; - - const Database::UserId userId {authenticateUser(request, clientInfo, dbSession)}; - - return {parameters, dbSession, userId, clientInfo, getServerProtocolVersion(clientInfo.name)}; + return {parameters, _db.getTLSSession(), userId, clientInfo, getServerProtocolVersion(clientInfo.name)}; } Database::UserId -SubsonicResource::authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo, Session& dbSession) +SubsonicResource::authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo) { if (auto *authEnvService {Service<::Auth::IEnvService>::get()}) { - const auto checkResult {authEnvService->processRequest(dbSession, request)}; + const auto checkResult {authEnvService->processRequest(request)}; if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted) throw UserNotAuthorizedError {}; @@ -2066,9 +2062,8 @@ SubsonicResource::authenticateUser(const Wt::Http::Request& request, const Clien } else if (auto *authPasswordService {Service<::Auth::IPasswordService>::get()}) { - const auto checkResult {authPasswordService->checkUserPassword(dbSession, - boost::asio::ip::address::from_string(request.clientAddress()), - clientInfo.user, clientInfo.password)}; + const auto checkResult {authPasswordService->checkUserPassword(boost::asio::ip::address::from_string(request.clientAddress()), + clientInfo.user, clientInfo.password)}; switch (checkResult.state) { diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index 8efa1094..2d549f41 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -48,7 +48,7 @@ namespace API::Subsonic static void checkProtocolVersion(ProtocolVersion client, ProtocolVersion server); ClientInfo getClientInfo(const Wt::Http::ParameterMap& parameters); RequestContext buildRequestContext(const Wt::Http::Request& request); - Database::UserId authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo, Database::Session& dbSession); + Database::UserId authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo); const std::unordered_map _serverProtocolVersionsByClient; Database::Db& _db; diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 26da8ef4..40a2fc03 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -245,12 +245,12 @@ int main(int argc, char* argv[]) const std::string authenticationBackend {StringUtils::stringToLower(config->getString("authentication-backend", "internal"))}; if (authenticationBackend == "internal" || authenticationBackend == "pam") { - authTokenService.assign(Auth::createAuthTokenService(config->getULong("login-throttler-max-entriees", 10000))); - authPasswordService.assign(Auth::createPasswordService(authenticationBackend, config->getULong("login-throttler-max-entriees", 10000), *authTokenService.get())); + authTokenService.assign(Auth::createAuthTokenService(database, config->getULong("login-throttler-max-entriees", 10000))); + authPasswordService.assign(Auth::createPasswordService(authenticationBackend, database, config->getULong("login-throttler-max-entriees", 10000), *authTokenService.get())); } else if (authenticationBackend == "http-headers") { - authEnvService.assign(Auth::createEnvService(authenticationBackend)); + authEnvService.assign(Auth::createEnvService(authenticationBackend, database)); } else throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"}; diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 5e1ec8ea..29414b43 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -49,7 +49,7 @@ static void createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry) { - const std::string secret {Service<::Auth::IAuthTokenService>::get()->createAuthToken(LmsApp->getDbSession(), userId, expiry)}; + const std::string secret {Service<::Auth::IAuthTokenService>::get()->createAuthToken(userId, expiry)}; LmsApp->setCookie(authCookieName, secret, @@ -67,7 +67,7 @@ processAuthToken(const Wt::WEnvironment& env) if (!authCookie) return std::nullopt; - const auto res {Service<::Auth::IAuthTokenService>::get()->processAuthToken(LmsApp->getDbSession(), boost::asio::ip::address::from_string(env.clientAddress()), *authCookie)}; + const auto res {Service<::Auth::IAuthTokenService>::get()->processAuthToken(boost::asio::ip::address::from_string(env.clientAddress()), *authCookie)}; switch (res.state) { case ::Auth::IAuthTokenService::AuthTokenProcessResult::State::Denied: @@ -131,7 +131,6 @@ class AuthModel : public Wt::WFormModel if (field == PasswordField) { const auto checkResult {Service<::Auth::IPasswordService>::get()->checkUserPassword( - LmsApp->getDbSession(), boost::asio::ip::address::from_string(LmsApp->environment().clientAddress()), valueText(LoginNameField).toUTF8(), valueText(PasswordField).toUTF8())}; diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index a2d0b4e3..e192763e 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -69,7 +69,7 @@ LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplica { if (auto *authEnvService {Service<::Auth::IEnvService>::get()}) { - const auto checkResult {authEnvService->processEnv(db.getTLSSession(), env)}; + const auto checkResult {authEnvService->processEnv(env)}; if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted) { LMS_LOG(UI, ERROR) << "Cannot authenticate user from environment!"; diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 1f25251e..66105842 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -197,7 +197,7 @@ class SettingsModel : public Wt::WFormModel if (_authPasswordService && !valueText(PasswordField).empty()) { - _authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); + _authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8()); } } diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index b187f472..b37645b3 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -70,7 +70,7 @@ class InitWizardModel : public Wt::WFormModel Database::User::pointer user {Database::User::create(LmsApp->getDbSession(), valueText(AdminLoginField).toUTF8())}; user.modify()->setType(Database::UserType::ADMIN); - Service<::Auth::IPasswordService>::get()->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); + Service<::Auth::IPasswordService>::get()->setPassword(user->getId(), valueText(PasswordField).toUTF8()); } bool validateField(Field field) diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index f27901b9..16374d80 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -87,7 +87,7 @@ class UserModel : public Wt::WFormModel throw UserNotFoundException {}; if (_authPasswordService && !valueText(PasswordField).empty()) - _authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); + _authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8()); } else { @@ -103,7 +103,7 @@ class UserModel : public Wt::WFormModel user.modify()->setType(Database::UserType::DEMO); if (_authPasswordService) - _authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); + _authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8()); } } diff --git a/src/lms/ui/common/PasswordValidator.cpp b/src/lms/ui/common/PasswordValidator.cpp index 443d556f..2df00c3f 100644 --- a/src/lms/ui/common/PasswordValidator.cpp +++ b/src/lms/ui/common/PasswordValidator.cpp @@ -80,7 +80,6 @@ namespace UserInterface return Wt::WValidator::validate(input); const auto checkResult {Service<::Auth::IPasswordService>::get()->checkUserPassword( - LmsApp->getDbSession(), boost::asio::ip::address::from_string(LmsApp->environment().clientAddress()), LmsApp->getUserLoginName(), input.toUTF8())}; From 11f8a9e38b089238fb70f1f952586ca7921b716f Mon Sep 17 00:00:00 2001 From: emeric Date: Tue, 5 Oct 2021 18:41:54 +0200 Subject: [PATCH 13/49] Reworked http client to ease reuse --- src/libs/scrobbling/CMakeLists.txt | 1 - .../listenbrainz/ListenBrainzScrobbler.cpp | 52 ++---- .../listenbrainz/ListenBrainzScrobbler.hpp | 5 +- .../impl/listenbrainz/ListensSynchronizer.cpp | 164 +++++++----------- .../impl/listenbrainz/ListensSynchronizer.hpp | 7 +- src/libs/utils/CMakeLists.txt | 2 + src/libs/utils/impl/Logger.cpp | 1 + src/libs/utils/impl/http/Client.cpp | 73 ++++++++ src/libs/utils/impl/http/Client.hpp | 47 +++++ src/libs/utils/impl/http/ClientRequest.hpp | 75 ++++++++ .../impl/http}/SendQueue.cpp | 70 ++++---- .../impl/http}/SendQueue.hpp | 76 ++------ src/libs/utils/include/utils/Logger.hpp | 1 + .../utils/http/ClientRequestParameters.hpp | 60 +++++++ src/libs/utils/include/utils/http/IClient.hpp | 40 +++++ src/lms/main.cpp | 2 + 16 files changed, 438 insertions(+), 238 deletions(-) create mode 100644 src/libs/utils/impl/http/Client.cpp create mode 100644 src/libs/utils/impl/http/Client.hpp create mode 100644 src/libs/utils/impl/http/ClientRequest.hpp rename src/libs/{scrobbling/impl/listenbrainz => utils/impl/http}/SendQueue.cpp (69%) rename src/libs/{scrobbling/impl/listenbrainz => utils/impl/http}/SendQueue.hpp (50%) create mode 100644 src/libs/utils/include/utils/http/ClientRequestParameters.hpp create mode 100644 src/libs/utils/include/utils/http/IClient.hpp diff --git a/src/libs/scrobbling/CMakeLists.txt b/src/libs/scrobbling/CMakeLists.txt index bd8a0960..d69a6f5a 100644 --- a/src/libs/scrobbling/CMakeLists.txt +++ b/src/libs/scrobbling/CMakeLists.txt @@ -3,7 +3,6 @@ add_library(lmsscrobbling SHARED impl/internal/InternalScrobbler.cpp impl/listenbrainz/ListenBrainzScrobbler.cpp impl/listenbrainz/ListensSynchronizer.cpp - impl/listenbrainz/SendQueue.cpp impl/listenbrainz/Utils.cpp impl/Scrobbling.cpp ) diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index c17254da..08202d9e 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -32,6 +32,7 @@ #include "database/TrackList.hpp" #include "database/User.hpp" #include "utils/IConfig.hpp" +#include "utils/http/IClient.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" #include "Utils.hpp" @@ -142,10 +143,10 @@ namespace Scrobbling::ListenBrainz Scrobbler::Scrobbler(boost::asio::io_context& ioContext, Database::Db& db) : _ioContext {ioContext} , _db {db} - , _sendQueue {_ioContext, Service::get()->getString("listenbrainz-api-base-url", "https://api.listenbrainz.org")} - , _listensSynchronizer {_ioContext, db, _sendQueue} + , _baseAPIUrl {Service::get()->getString("listenbrainz-api-base-url", "https://api.listenbrainz.org")} + , _listensSynchronizer {_ioContext, db, _baseAPIUrl} { - LOG(INFO) << "Starting ListenBrainz scrobbler... API endpoint = '" << _sendQueue.getAPIBaseURL(); + LOG(INFO) << "Starting ListenBrainz scrobbler... API endpoint = '" << _baseAPIUrl; } Scrobbler::~Scrobbler() @@ -187,53 +188,38 @@ namespace Scrobbling::ListenBrainz void Scrobbler::enqueListen(const Listen& listen, const Wt::WDateTime& timePoint) { - std::optional requestData {createSubmitListenRequestData(listen, timePoint)}; - if (!requestData) - return; + Http::ClientPOSTRequestParameters request; + request.url = _baseAPIUrl + "/1/submit-listens"; - SendQueue::Request submitListen {std::move(*requestData)}; if (timePoint.isValid()) { - submitListen.setPriority(SendQueue::Request::Priority::Normal); - submitListen.setOnSuccessFunc([=](std::string_view) + request.priority = Http::ClientRequestParameters::Priority::Normal; + request.onSuccessFunc = [=](std::string_view) { _listensSynchronizer.saveListen(TimedListen {listen, timePoint}); - }); + }; } else { // We want "listen now" to appear as soon as possible - submitListen.setPriority(SendQueue::Request::Priority::High); + request.priority = Http::ClientRequestParameters::Priority::High; } - _sendQueue.enqueueRequest(std::move(submitListen)); - } - - std::optional - Scrobbler::createSubmitListenRequestData(const Listen& listen, const Wt::WDateTime& timePoint) - { - Database::Session& session {_db.getTLSSession()}; - - const std::optional listenBrainzToken {Utils::getListenBrainzToken(session, listen.userId)}; - if (!listenBrainzToken) - return std::nullopt; - - SendQueue::RequestData requestData; - requestData.endpoint = "/1/submit-listens"; - requestData.type = SendQueue::RequestData::Type::POST; - - std::string bodyText {listenToJsonString(session, listen, timePoint, timePoint.isValid() ? "single" : "playing_now")}; + std::string bodyText {listenToJsonString(_db.getTLSSession(), listen, timePoint, timePoint.isValid() ? "single" : "playing_now")}; if (bodyText.empty()) { LOG(DEBUG) << "Cannot convert listen to json: skipping"; - return std::nullopt; + return; } - requestData.message.addBodyText(bodyText); - requestData.message.addHeader("Authorization", "Token " + std::string {listenBrainzToken->getAsString()}); - requestData.message.addHeader("Content-Type", "application/json"); + const std::optional listenBrainzToken {Utils::getListenBrainzToken(_db.getTLSSession(), listen.userId)}; + if (!listenBrainzToken) + return; - return requestData; + request.message.addBodyText(bodyText); + request.message.addHeader("Authorization", "Token " + std::string {listenBrainzToken->getAsString()}); + request.message.addHeader("Content-Type", "application/json"); + Service::get()->sendPOSTRequest(std::move(request)); } } // namespace Scrobbling::ListenBrainz diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp index 30e85bad..c1b2406e 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp @@ -24,7 +24,6 @@ #include "IScrobbler.hpp" #include "ListensSynchronizer.hpp" -#include "SendQueue.hpp" namespace Database { @@ -54,11 +53,11 @@ namespace Scrobbling::ListenBrainz // Submit listens void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); - std::optional createSubmitListenRequestData(const Listen& listen, const Wt::WDateTime& timePoint); + //std::optional createSubmitListenRequestData(const Listen& listen, const Wt::WDateTime& timePoint); boost::asio::io_context& _ioContext; Database::Db& _db; - SendQueue _sendQueue; + std::string _baseAPIUrl; ListensSynchronizer _listensSynchronizer; }; } // Scrobbling::ListenBrainz diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 8a1809cc..8196da8b 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -34,6 +34,7 @@ #include "database/User.hpp" #include "scrobbling/Exception.hpp" #include "utils/IConfig.hpp" +#include "utils/http/IClient.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" @@ -45,17 +46,6 @@ namespace { using namespace Scrobbling::ListenBrainz; - SendQueue::RequestData - createValidateTokenRequestData(std::string_view authToken) - { - SendQueue::RequestData requestData; - requestData.type = SendQueue::RequestData::Type::GET; - requestData.endpoint = "/1/validate-token"; - requestData.headers = { {"Authorization", "Token " + std::string {authToken}} }; - - return requestData; - } - std::string parseValidateToken(std::string_view msgBody) { @@ -79,18 +69,6 @@ namespace return listenBrainzUserName; } - SendQueue::RequestData - createListenCountRequestData(std::string_view listenBrainzUserName) - { - LOG(DEBUG) << "Getting listen count for listenbrainz user '" << listenBrainzUserName << "'"; - - SendQueue::RequestData requestData; - requestData.type = SendQueue::RequestData::Type::GET; - requestData.endpoint = "/1/user/" + std::string {listenBrainzUserName} + "/listen-count"; - - return requestData; - } - std::optional parseListenCount(std::string_view msgBody) { @@ -109,18 +87,6 @@ namespace } } - SendQueue::RequestData - createGetListensRequestData(std::string_view listenBrainzUserName, const Wt::WDateTime& maxDateTime) - { - LOG(DEBUG) << "Getting listens for listenbrainz user '" << listenBrainzUserName << "' with max_ts = " << maxDateTime.toString(); - - SendQueue::RequestData requestData; - requestData.type = SendQueue::RequestData::Type::GET; - requestData.endpoint = "/1/user/" + std::string {listenBrainzUserName} + "/listens?max_ts=" + std::to_string(maxDateTime.toTime_t()); - - return requestData; - } - Database::Track::pointer tryMatchListen(Database::Session& session, const Wt::Json::Object& metadata) { @@ -247,10 +213,10 @@ namespace namespace Scrobbling::ListenBrainz { - ListensSynchronizer::ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, SendQueue& sendQueue) + ListensSynchronizer::ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, std::string_view baseAPIUrl) : _ioContext {ioContext} , _db {db} - , _sendQueue {sendQueue} + , _baseAPIUrl {baseAPIUrl} , _maxSyncListenCount {Service::get()->getULong("listenbrainz-max-sync-listen-count", 1000)} , _syncListensPeriod {Service::get()->getULong("listenbrainz-sync-listens-period-hours", 1)} { @@ -386,31 +352,33 @@ namespace Scrobbling::ListenBrainz { assert(context.listenBrainzUserName.empty()); - std::optional requestData {createValidateTokenRequestData(context.userId)}; - if (!requestData) + const std::optional listenBrainzToken {Utils::getListenBrainzToken(_db.getTLSSession(), context.userId)}; + if (!listenBrainzToken) { onGetListensEnded(context); return; } - SendQueue::Request validateTokenRequest {std::move(*requestData)}; - validateTokenRequest.setOnSuccessFunc([this, &context] (std::string_view msgBody) - { - context.listenBrainzUserName = parseValidateToken(msgBody); - if (context.listenBrainzUserName.empty()) + Http::ClientGETRequestParameters request; + request.priority = Http::ClientRequestParameters::Priority::Low; + request.url = _baseAPIUrl + "/1/validate-token"; + request.headers = { {"Authorization", "Token " + std::string {listenBrainzToken->getAsString()}} }; + request.onSuccessFunc = [this, &context] (std::string_view msgBody) + { + context.listenBrainzUserName = parseValidateToken(msgBody); + if (context.listenBrainzUserName.empty()) + { + onGetListensEnded(context); + return; + } + enqueGetListenCount(context); + }; + request.onFailureFunc = [this, &context] { onGetListensEnded(context); - return; - } - enqueGetListenCount(context); - }); - validateTokenRequest.setOnFailureFunc([this, &context] - { - onGetListensEnded(context); - }); + }; - validateTokenRequest.setPriority(SendQueue::Request::Priority::Low); - _sendQueue.enqueueRequest(std::move(validateTokenRequest)); + Service::get()->sendGETRequest(std::move(request)); } void @@ -418,32 +386,33 @@ namespace Scrobbling::ListenBrainz { assert(!context.listenBrainzUserName.empty()); - SendQueue::Request getListenCountRequest {createListenCountRequestData(context.listenBrainzUserName)}; - getListenCountRequest.setOnSuccessFunc([=, &context] (std::string_view msgBody) - { - const auto listenCount = parseListenCount(msgBody); - if (listenCount) - LOG(DEBUG) << "Listen count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *listenCount; + Http::ClientGETRequestParameters request; + request.url = _baseAPIUrl + "/1/user/" + std::string {context.listenBrainzUserName} + "/listen-count"; + request.priority = Http::ClientRequestParameters::Priority::Low; + request.onSuccessFunc = [=, &context] (std::string_view msgBody) + { + const auto listenCount = parseListenCount(msgBody); + if (listenCount) + LOG(DEBUG) << "Listen count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *listenCount; - bool needSync {listenCount && (!context.listenCount || *context.listenCount != *listenCount)}; - context.listenCount = listenCount; + bool needSync {listenCount && (!context.listenCount || *context.listenCount != *listenCount)}; + context.listenCount = listenCount; - if (!needSync) + if (!needSync) + { + onGetListensEnded(context); + return; + } + + context.maxDateTime = Wt::WDateTime::currentDateTime(); + enqueGetListens(context); + }; + request.onFailureFunc = [this, &context] { onGetListensEnded(context); - return; - } + }; - context.maxDateTime = Wt::WDateTime::currentDateTime(); - enqueGetListens(context); - }); - getListenCountRequest.setOnFailureFunc([this, &context] - { - onGetListensEnded(context); - }); - - getListenCountRequest.setPriority(SendQueue::Request::Priority::Low); - _sendQueue.enqueueRequest(std::move(getListenCountRequest)); + Service::get()->sendGETRequest(std::move(request)); } void @@ -451,37 +420,26 @@ namespace Scrobbling::ListenBrainz { assert(!context.listenBrainzUserName.empty()); - SendQueue::Request getListensRequest {::createGetListensRequestData(context.listenBrainzUserName, context.maxDateTime)}; - getListensRequest.setOnSuccessFunc([=, &context] (std::string_view msgBody) - { - processGetListensResponse(msgBody, context); - if (context.fetchedListenCount >= _maxSyncListenCount || !context.maxDateTime.isValid()) + Http::ClientGETRequestParameters request; + request.url = _baseAPIUrl + "/1/user/" + context.listenBrainzUserName + "/listens?max_ts=" + std::to_string(context.maxDateTime.toTime_t()); + request.priority = Http::ClientRequestParameters::Priority::Low; + request.onSuccessFunc = [=, &context] (std::string_view msgBody) + { + processGetListensResponse(msgBody, context); + if (context.fetchedListenCount >= _maxSyncListenCount || !context.maxDateTime.isValid()) + { + onGetListensEnded(context); + return; + } + + enqueGetListens(context); + }; + request.onFailureFunc = [=, &context] { onGetListensEnded(context); - return; - } + }; - enqueGetListens(context); - }); - getListensRequest.setOnFailureFunc([=, &context] - { - onGetListensEnded(context); - }); - - getListensRequest.setPriority(SendQueue::Request::Priority::Low); - _sendQueue.enqueueRequest(std::move(getListensRequest)); - } - - std::optional - ListensSynchronizer::createValidateTokenRequestData(Database::UserId userId) - { - Database::Session& session {_db.getTLSSession()}; - - const std::optional listenBrainzToken {Utils::getListenBrainzToken(session, userId)}; - if (!listenBrainzToken) - return std::nullopt; - - return ::createValidateTokenRequestData(listenBrainzToken->getAsString()); + Service::get()->sendGETRequest(std::move(request)); } void diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index e2914dee..e71a8ae4 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -28,7 +28,6 @@ #include "database/Types.hpp" #include "scrobbling/Listen.hpp" -#include "SendQueue.hpp" namespace Database { @@ -43,7 +42,7 @@ namespace Scrobbling::ListenBrainz class ListensSynchronizer { public: - ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, SendQueue& sendQueue); + ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, std::string_view baseAPIUrl); void saveListen(const TimedListen& listen); @@ -78,14 +77,12 @@ namespace Scrobbling::ListenBrainz void enqueValidateToken(UserContext& context); void enqueGetListenCount(UserContext& context); void enqueGetListens(UserContext& context); - std::optional createValidateTokenRequestData(Database::UserId userId); - std::optional createGetListensRequestData(std::string_view listenBrainzUserName, const Wt::WDateTime& maxDateTime); void processGetListensResponse(std::string_view body, UserContext& context); boost::asio::io_context& _ioContext; boost::asio::io_context::strand _strand {_ioContext}; Database::Db& _db; - SendQueue& _sendQueue; + std::string _baseAPIUrl; boost::asio::steady_timer _getListensTimer {_ioContext}; std::unordered_map _userContexts; diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 34744ea7..261ec14c 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(lmsutils SHARED + impl/http/Client.cpp + impl/http/SendQueue.cpp impl/ChildProcess.cpp impl/ChildProcessManager.cpp impl/Config.cpp diff --git a/src/libs/utils/impl/Logger.cpp b/src/libs/utils/impl/Logger.cpp index 3c0d6668..57d7a35d 100644 --- a/src/libs/utils/impl/Logger.cpp +++ b/src/libs/utils/impl/Logger.cpp @@ -31,6 +31,7 @@ const char* getModuleName(Module mod) case Module::DB: return "DB"; case Module::DBUPDATER: return "DB UPDATER"; case Module::FEATURE: return "FEATURE"; + case Module::HTTP: return "HTTP"; case Module::MAIN: return "MAIN"; case Module::METADATA: return "METADATA"; case Module::REMOTE: return "REMOTE"; diff --git a/src/libs/utils/impl/http/Client.cpp b/src/libs/utils/impl/http/Client.cpp new file mode 100644 index 00000000..e4a12a49 --- /dev/null +++ b/src/libs/utils/impl/http/Client.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 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 "Client.hpp" +#include "utils/Exception.hpp" + +namespace Http +{ + std::unique_ptr + createClient(boost::asio::io_context& ioContext) + { + return std::make_unique(ioContext); + } + + void + Client::sendGETRequest(ClientGETRequestParameters&& GETParams) + { + SendQueue& sendQueue {getOrCreateSendQueue(GETParams.url)}; + sendQueue.sendRequest(std::make_unique(std::move(GETParams))); + } + + void + Client::sendPOSTRequest(ClientPOSTRequestParameters&& POSTParams) + { + SendQueue& sendQueue {getOrCreateSendQueue(POSTParams.url)}; + sendQueue.sendRequest(std::make_unique(std::move(POSTParams))); + } + + SendQueue& + Client::getOrCreateSendQueue(const std::string& url) + { + Wt::Http::Client::URL parsedURL; + if (!Wt::Http::Client::parseUrl(url, parsedURL)) + throw LmsException {"Cannot parse URL '" + url + "'"}; + + { + std::shared_lock lock {_sendQueuesMutex}; + + if (auto it = _sendQueues.find(parsedURL.host); it != std::cend(_sendQueues)) + return it->second; + } + + { + std::unique_lock lock {_sendQueuesMutex}; + + if (auto it = _sendQueues.find(parsedURL.host); it != std::cend(_sendQueues)) + return it->second; + + auto [it, inserted] {_sendQueues.emplace(parsedURL.host, _ioContext)}; + assert(inserted); + + return it->second; + } + } + +} // namespace Http + diff --git a/src/libs/utils/impl/http/Client.hpp b/src/libs/utils/impl/http/Client.hpp new file mode 100644 index 00000000..d6bff7f4 --- /dev/null +++ b/src/libs/utils/impl/http/Client.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "utils/http/IClient.hpp" +#include "SendQueue.hpp" + +namespace Http +{ + class Client final : public IClient + { + public: + Client(boost::asio::io_context& ioContext) : _ioContext {ioContext} {} + + private: + void sendGETRequest(ClientGETRequestParameters&& request) override; + void sendPOSTRequest(ClientPOSTRequestParameters&& request) override; + + SendQueue& getOrCreateSendQueue(const std::string& host); + + boost::asio::io_context& _ioContext; + std::shared_mutex _sendQueuesMutex; + std::unordered_map _sendQueues; + }; +} // namespace Http + diff --git a/src/libs/utils/impl/http/ClientRequest.hpp b/src/libs/utils/impl/http/ClientRequest.hpp new file mode 100644 index 00000000..0b9f028d --- /dev/null +++ b/src/libs/utils/impl/http/ClientRequest.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include "utils/http/ClientRequestParameters.hpp" + +namespace Http +{ + class ClientRequest + { + public: + ClientRequest(ClientGETRequestParameters&& GETParams) : _parameters {std::move(GETParams)} {} + ClientRequest(ClientPOSTRequestParameters&& POSTParams) : _parameters {std::move(POSTParams)} {} + + std::size_t retryCount {}; + + const ClientRequestParameters& getParameters() const + { + const ClientRequestParameters* res; + + std::visit([&](const auto& parameters) + { + res = &static_cast(parameters); + }, _parameters); + + return *res; + } + + + enum class Type + { + GET, + POST + }; + Type getType() const + { + if (std::holds_alternative(_parameters)) + return Type::GET; + else + return Type::POST; + } + + const ClientGETRequestParameters& getGETParameters() const + { + return std::get(_parameters); + } + + const ClientPOSTRequestParameters& getPOSTParameters() const + { + return std::get(_parameters); + } + + private: + std::variant _parameters; + }; +} diff --git a/src/libs/scrobbling/impl/listenbrainz/SendQueue.cpp b/src/libs/utils/impl/http/SendQueue.cpp similarity index 69% rename from src/libs/scrobbling/impl/listenbrainz/SendQueue.cpp rename to src/libs/utils/impl/http/SendQueue.cpp index aa1cfde7..de5a0722 100644 --- a/src/libs/scrobbling/impl/listenbrainz/SendQueue.cpp +++ b/src/libs/utils/impl/http/SendQueue.cpp @@ -19,9 +19,10 @@ #include "SendQueue.hpp" +#include #include -#include "scrobbling/Exception.hpp" +#include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" @@ -57,11 +58,10 @@ namespace } } -namespace Scrobbling::ListenBrainz +namespace Http { - SendQueue::SendQueue(boost::asio::io_context& ioContext, std::string_view apiBaseURL) + SendQueue::SendQueue(boost::asio::io_context& ioContext) : _ioContext {ioContext} - , _apiBaseURL {apiBaseURL} { _client.done().connect([this](Wt::AsioWrapper::error_code ec, const Wt::Http::Message& msg) { @@ -78,11 +78,11 @@ namespace Scrobbling::ListenBrainz } void - SendQueue::enqueueRequest(Request request) + SendQueue::sendRequest(std::unique_ptr request) { - _strand.dispatch([this, request = std::move(request)]() + boost::asio::dispatch(_strand, [this, request = std::move(request)]() mutable { - _sendQueue[request._priority].emplace_back(std::move(request)); + _sendQueue[request->getParameters().priority].emplace_back(std::move(request)); if (_state == State::Idle) sendNextQueuedRequest(); @@ -93,16 +93,17 @@ namespace Scrobbling::ListenBrainz SendQueue::sendNextQueuedRequest() { assert(_state == State::Idle); + assert(!_currentRequest); for (auto& [prio, requests] : _sendQueue) { LOG(DEBUG) << "Processing prio " << static_cast(prio) << ", request count = " << requests.size(); while (!requests.empty()) { - Request request {std::move(requests.front())}; + std::unique_ptr request {std::move(requests.front())}; requests.pop_front(); - if (!sendRequest(request._requestData)) + if (!sendRequest(*request)) continue; _state = State::Sending; @@ -113,20 +114,19 @@ namespace Scrobbling::ListenBrainz } bool - SendQueue::sendRequest(const RequestData& requestData) + SendQueue::sendRequest(const ClientRequest& request) { - const std::string url {_apiBaseURL + requestData.endpoint}; + LOG(DEBUG) << "Sending request to url '" << request.getParameters().url << "'"; - LOG(DEBUG) << "Sending request type " << (requestData.type == RequestData::Type::GET ? "GET" : "POST") << " to url '" << url << "'"; - - bool res{}; - switch (requestData.type) + bool res {}; + switch (request.getType()) { - case RequestData::Type::GET: - res = _client.get(url, requestData.headers); + case ClientRequest::Type::GET: + res = _client.get(request.getParameters().url, request.getGETParameters().headers); break; - case RequestData::Type::POST: - res = _client.post(url, requestData.message); + + case ClientRequest::Type::POST: + res = _client.post(request.getParameters().url, request.getPOSTParameters().message); break; } @@ -146,43 +146,43 @@ namespace Scrobbling::ListenBrainz } assert(_currentRequest); - Request request {std::move(*_currentRequest)}; _state = State::Idle; LOG(DEBUG) << "Client done. status = " << msg.status(); if (ec) - onClientDoneError(std::move(request), ec); + onClientDoneError(std::move(_currentRequest), ec); else - onClientDoneSuccess(std::move(request), msg); + onClientDoneSuccess(std::move(_currentRequest), msg); } void - SendQueue::onClientDoneError(Request request, Wt::AsioWrapper::error_code ec) + 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); - if (request._retryCount++ < _maxRetryCount) + if (request->retryCount++ < _maxRetryCount) { - _sendQueue[request._priority].emplace_front(std::move(request)); + _sendQueue[request->getParameters().priority].emplace_front(std::move(request)); } else { LOG(ERROR) << "Too many retries, giving up operation and throttle"; - if (request._onFailureFunc) - request._onFailureFunc(); + if (request->getParameters().onFailureFunc) + request->getParameters().onFailureFunc(); } } void - SendQueue::onClientDoneSuccess(Request request, const Wt::Http::Message& msg) + SendQueue::onClientDoneSuccess(std::unique_ptr request, const Wt::Http::Message& msg) { + const ClientRequestParameters& requestParameters {request->getParameters()}; bool mustThrottle{}; if (msg.status() == 429) { - _sendQueue[request._priority].emplace_front(std::move(request)); + _sendQueue[requestParameters.priority].emplace_front(std::move(request)); mustThrottle = true; } @@ -198,14 +198,14 @@ namespace Scrobbling::ListenBrainz { if (msg.status() == 200) { - if (request._onSuccessFunc) - request._onSuccessFunc(msg.body()); + if (requestParameters.onSuccessFunc) + requestParameters.onSuccessFunc(msg.body()); } else { LOG(ERROR) << "Send error: '" << msg.body() << "'"; - if (request._onFailureFunc) - request._onFailureFunc(); + if (requestParameters.onFailureFunc) + requestParameters.onFailureFunc(); } } @@ -231,7 +231,7 @@ namespace Scrobbling::ListenBrainz } else if (ec) { - throw Exception {"Throttle timer failure: " + std::string {ec.message()} }; + throw LmsException {"Throttle timer failure: " + std::string {ec.message()} }; } _state = State::Idle; diff --git a/src/libs/scrobbling/impl/listenbrainz/SendQueue.hpp b/src/libs/utils/impl/http/SendQueue.hpp similarity index 50% rename from src/libs/scrobbling/impl/listenbrainz/SendQueue.hpp rename to src/libs/utils/impl/http/SendQueue.hpp index 9878d5d8..0e3bba24 100644 --- a/src/libs/scrobbling/impl/listenbrainz/SendQueue.hpp +++ b/src/libs/utils/impl/http/SendQueue.hpp @@ -20,19 +20,22 @@ #pragma once #include +#include +#include #include #include #include #include +#include "ClientRequest.hpp" -namespace Scrobbling::ListenBrainz +namespace Http { class SendQueue { public: - SendQueue(boost::asio::io_context& ioContext, std::string_view apiBaseURL); + SendQueue(boost::asio::io_context& ioContext); ~SendQueue(); SendQueue(const SendQueue&) = delete; @@ -40,58 +43,15 @@ namespace Scrobbling::ListenBrainz SendQueue& operator=(const SendQueue&) = delete; SendQueue& operator=(const SendQueue&&) = delete; - // generic queue operations - struct RequestData - { - enum class Type - { - GET, - POST, - }; - - Type type; - std::string endpoint; // relative URL to the base API - std::vector headers; // used by GET - Wt::Http::Message message; // used by POST - }; - - class Request - { - public: - enum class Priority - { - High, - Normal, - Low, - }; - - Request(RequestData requestData) : _requestData {std::move(requestData)} {} - - using OnSuccessFunc = std::function; - using OnFailureFunc = std::function; - - void setOnSuccessFunc(OnSuccessFunc onSuccessFunc) { _onSuccessFunc = onSuccessFunc; } - void setOnFailureFunc(OnFailureFunc onFailureFunc) { _onFailureFunc = onFailureFunc; } - void setPriority(Priority priority) { _priority = priority; } - - private: - friend class SendQueue; - RequestData _requestData; - Priority _priority {Priority::Normal}; - std::size_t _retryCount {}; - OnSuccessFunc _onSuccessFunc; - OnFailureFunc _onFailureFunc; - }; - - std::string_view getAPIBaseURL() const { return _apiBaseURL; } - void enqueueRequest(Request request); + void sendRequest(std::unique_ptr request); private: + void sendNextQueuedRequest(); - bool sendRequest(const RequestData& request); + bool sendRequest(const ClientRequest& request); void onClientDone(Wt::AsioWrapper::error_code ec, const Wt::Http::Message& msg); - void onClientDoneError(Request request, Wt::AsioWrapper::error_code ec); - void onClientDoneSuccess(Request request, const Wt::Http::Message& msg); + void onClientDoneError(std::unique_ptr request, Wt::AsioWrapper::error_code ec); + void onClientDoneSuccess(std::unique_ptr request, const Wt::Http::Message& msg); void throttle(std::chrono::seconds duration); const std::size_t _maxRetryCount {2}; @@ -99,20 +59,20 @@ namespace Scrobbling::ListenBrainz const std::chrono::seconds _minRetryWaitDuration {1}; const std::chrono::seconds _maxRetryWaitDuration {300}; + boost::asio::io_context& _ioContext; + boost::asio::io_context::strand _strand {_ioContext}; + boost::asio::steady_timer _throttleTimer {_ioContext}; + enum class State { Idle, Throttled, Sending, }; - boost::asio::io_context& _ioContext; - boost::asio::io_context::strand _strand {_ioContext}; - boost::asio::steady_timer _throttleTimer {_ioContext}; - std::string _apiBaseURL; - State _state {State::Idle}; - Wt::Http::Client _client {_ioContext}; - std::map> _sendQueue; - std::optional _currentRequest; + State _state {State::Idle}; + Wt::Http::Client _client {_ioContext}; + std::map>> _sendQueue; + std::unique_ptr _currentRequest; }; } // namespace Scrobbling::ListenBrainz diff --git a/src/libs/utils/include/utils/Logger.hpp b/src/libs/utils/include/utils/Logger.hpp index 4da7f4ba..43b2f893 100644 --- a/src/libs/utils/include/utils/Logger.hpp +++ b/src/libs/utils/include/utils/Logger.hpp @@ -43,6 +43,7 @@ enum class Module DB, DBUPDATER, FEATURE, + HTTP, MAIN, METADATA, REMOTE, diff --git a/src/libs/utils/include/utils/http/ClientRequestParameters.hpp b/src/libs/utils/include/utils/http/ClientRequestParameters.hpp new file mode 100644 index 00000000..2e11f183 --- /dev/null +++ b/src/libs/utils/include/utils/http/ClientRequestParameters.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include + +namespace Http +{ + struct ClientRequestParameters + { + enum class Priority + { + High, + Normal, + Low, + }; + + Priority priority {Priority::Normal}; + std::string url; + + using OnSuccessFunc = std::function; + OnSuccessFunc onSuccessFunc; + + using OnFailureFunc = std::function; + OnFailureFunc onFailureFunc; + }; + + struct ClientGETRequestParameters final : public ClientRequestParameters + { + std::vector headers; + }; + + struct ClientPOSTRequestParameters final : public ClientRequestParameters + { + Wt::Http::Message message; + }; + +} // namespace Http + diff --git a/src/libs/utils/include/utils/http/IClient.hpp b/src/libs/utils/include/utils/http/IClient.hpp new file mode 100644 index 00000000..0feb33df --- /dev/null +++ b/src/libs/utils/include/utils/http/IClient.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include + +#include "utils/http/ClientRequestParameters.hpp" + +namespace Http +{ + class IClient + { + public: + virtual ~IClient() = default; + + virtual void sendGETRequest(ClientGETRequestParameters&& request) = 0; + virtual void sendPOSTRequest(ClientPOSTRequestParameters&& request) = 0; + }; + + std::unique_ptr createClient(boost::asio::io_context& ioContext); +} // namespace Http + diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 40a2fc03..ec7b906c 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -37,6 +37,7 @@ #include "scrobbling/IScrobbling.hpp" #include "ui/LmsApplication.hpp" #include "ui/LmsApplicationManager.hpp" +#include "utils/http/IClient.hpp" #include "utils/IChildProcessManager.hpp" #include "utils/IConfig.hpp" #include "utils/IOContextRunner.hpp" @@ -255,6 +256,7 @@ int main(int argc, char* argv[]) else throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"}; + Service httpClient {Http::createClient(ioContext)}; Service coverArtService {CoverArt::createGrabber(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; Service recommendationEngineService {Recommendation::createEngine(database)}; Service scannerService {Scanner::createScanner(database, *recommendationEngineService)}; From 1c29962606e8213366aae29698a0a9256e79b149 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 10 Oct 2021 20:37:27 +0200 Subject: [PATCH 14/49] Fixed regression on db object comparison, fixes #178 --- src/libs/database/include/database/Types.hpp | 2 ++ src/libs/database/test/Track.cpp | 14 ++++++++++++++ src/lms/ui/admin/UsersView.cpp | 6 +++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/libs/database/include/database/Types.hpp b/src/libs/database/include/database/Types.hpp index c2fb4146..f89a3eb7 100644 --- a/src/libs/database/include/database/Types.hpp +++ b/src/libs/database/include/database/Types.hpp @@ -105,6 +105,8 @@ namespace Database const T* operator->() const { return _obj.get(); } operator bool() const { return _obj.get(); } bool operator!() const { return !_obj.get(); } + bool operator==(const ObjectPtr& other) const { return other._obj == _obj; } + bool operator!=(const ObjectPtr& other) const { return other._obj != _obj; } auto modify() { return _obj.modify(); } void remove() { _obj.remove(); } diff --git a/src/libs/database/test/Track.cpp b/src/libs/database/test/Track.cpp index bdc54855..98934310 100644 --- a/src/libs/database/test/Track.cpp +++ b/src/libs/database/test/Track.cpp @@ -45,6 +45,20 @@ TEST_F(DatabaseFixture, SingleTrack) } } +TEST_F(DatabaseFixture, MultipleTracks) +{ + ScopedTrack track1 {session, "MyTrackFile1"}; + ScopedTrack track2 {session, "MyTrackFile2"}; + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_TRUE(track1.getId() != track2.getId()); + EXPECT_TRUE(track1.get() != track2.get()); + EXPECT_FALSE(track1.get() == track2.get()); + } +} + TEST_F(DatabaseFixture, MultipleTracksSearchByFilter) { ScopedTrack track1 {session, ""}; diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 50bc58e7..b0608d81 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -69,8 +69,8 @@ UsersView::refreshView() auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - auto users = Database::User::getAll(LmsApp->getDbSession()); - for (const auto& user : users) + const Database::User::pointer currentUser {LmsApp->getUser()}; + for (const Database::User::pointer& user : Database::User::getAll(LmsApp->getDbSession())) { const Database::UserId userId {user->getId()}; @@ -86,7 +86,7 @@ UsersView::refreshView() } // Don't edit ourself this way - if (LmsApp->getUser() == user) + if (currentUser == user) continue; entry->setCondition("if-edit", true); From 0dcef2e94ce705c84acd2f1b28bd7c665609f4b2 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 15 Oct 2021 20:58:14 +0200 Subject: [PATCH 15/49] Renamed CoverArtGrabber to CoverService --- src/libs/cover/CMakeLists.txt | 2 +- .../{CoverArtGrabber.cpp => CoverService.cpp} | 48 +++++++++---------- .../{CoverArtGrabber.hpp => CoverService.hpp} | 26 +++++----- src/libs/cover/impl/Exception.hpp | 4 +- src/libs/cover/impl/IRawImage.hpp | 2 +- src/libs/cover/impl/stb/JPEGImage.cpp | 2 +- src/libs/cover/impl/stb/JPEGImage.hpp | 2 +- src/libs/cover/impl/stb/RawImage.cpp | 2 +- src/libs/cover/impl/stb/RawImage.hpp | 2 +- ...ICoverArtGrabber.hpp => ICoverService.hpp} | 12 ++--- .../cover/include/cover/IEncodedImage.hpp | 4 +- src/libs/subsonic/impl/SubsonicResource.cpp | 8 ++-- src/lms/main.cpp | 8 ++-- src/lms/ui/LmsApplication.cpp | 2 +- src/lms/ui/resource/CoverResource.cpp | 8 ++-- src/tools/cover/LmsCover.cpp | 8 ++-- 16 files changed, 70 insertions(+), 70 deletions(-) rename src/libs/cover/impl/{CoverArtGrabber.cpp => CoverService.cpp} (86%) rename src/libs/cover/impl/{CoverArtGrabber.hpp => CoverService.hpp} (89%) rename src/libs/cover/include/cover/{ICoverArtGrabber.hpp => ICoverService.hpp} (82%) diff --git a/src/libs/cover/CMakeLists.txt b/src/libs/cover/CMakeLists.txt index a675db76..12627c9e 100644 --- a/src/libs/cover/CMakeLists.txt +++ b/src/libs/cover/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(lmscover SHARED - impl/CoverArtGrabber.cpp + impl/CoverService.cpp ) target_include_directories(lmscover INTERFACE diff --git a/src/libs/cover/impl/CoverArtGrabber.cpp b/src/libs/cover/impl/CoverService.cpp similarity index 86% rename from src/libs/cover/impl/CoverArtGrabber.cpp rename to src/libs/cover/impl/CoverService.cpp index 58fff328..af768802 100644 --- a/src/libs/cover/impl/CoverArtGrabber.cpp +++ b/src/libs/cover/impl/CoverService.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "CoverArtGrabber.hpp" +#include "CoverService.hpp" #include "av/IAudioFile.hpp" @@ -28,10 +28,10 @@ #if LMS_SUPPORT_IMAGE_STB #include "stb/RawImage.hpp" -using RawImage = CoverArt::STB::RawImage; +using RawImage = Cover::STB::RawImage; #elif LMS_SUPPORT_IMAGE_GM #include "graphicsmagick/RawImage.hpp" -using RawImage = CoverArt::GraphicsMagick::RawImage; +using RawImage = Cover::GraphicsMagick::RawImage; #endif #include "utils/IConfig.hpp" @@ -77,7 +77,7 @@ namespace } } -namespace CoverArt { +namespace Cover { static bool @@ -86,13 +86,13 @@ isFileSupported(const std::filesystem::path& file, const std::vector -createGrabber(Database::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath) +std::unique_ptr +createCoverService(Database::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath) { - return std::make_unique(db, execPath, defaultCoverPath); + return std::make_unique(db, execPath, defaultCoverPath); } -Grabber::Grabber(Database::Db& db, +CoverService::CoverService(Database::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath) : _db {db} @@ -124,7 +124,7 @@ Grabber::Grabber(Database::Db& db, } std::unique_ptr -Grabber::getFromAvMediaFile(const Av::IAudioFile& input, ImageSize width) const +CoverService::getFromAvMediaFile(const Av::IAudioFile& input, ImageSize width) const { std::unique_ptr image; @@ -149,7 +149,7 @@ Grabber::getFromAvMediaFile(const Av::IAudioFile& input, ImageSize width) const } std::unique_ptr -Grabber::getFromCoverFile(const std::filesystem::path& p, ImageSize width) const +CoverService::getFromCoverFile(const std::filesystem::path& p, ImageSize width) const { std::unique_ptr image; @@ -168,7 +168,7 @@ Grabber::getFromCoverFile(const std::filesystem::path& p, ImageSize width) const } std::shared_ptr -Grabber::getDefault(ImageSize width) +CoverService::getDefault(ImageSize width) { { std::shared_lock lock {_cacheMutex}; @@ -192,7 +192,7 @@ Grabber::getDefault(ImageSize width) } std::unique_ptr -Grabber::getFromDirectory(const std::filesystem::path& directory, ImageSize width) const +CoverService::getFromDirectory(const std::filesystem::path& directory, ImageSize width) const { const std::multimap coverPaths {getCoverPaths(directory)}; @@ -231,7 +231,7 @@ Grabber::getFromDirectory(const std::filesystem::path& directory, ImageSize widt } std::unique_ptr -Grabber::getFromSameNamedFile(const std::filesystem::path& filePath, ImageSize width) const +CoverService::getFromSameNamedFile(const std::filesystem::path& filePath, ImageSize width) const { std::unique_ptr res; @@ -252,7 +252,7 @@ Grabber::getFromSameNamedFile(const std::filesystem::path& filePath, ImageSize w } bool -Grabber::checkCoverFile(const std::filesystem::path& filePath) const +CoverService::checkCoverFile(const std::filesystem::path& filePath) const { std::error_code ec; @@ -275,7 +275,7 @@ Grabber::checkCoverFile(const std::filesystem::path& filePath) const } std::multimap -Grabber::getCoverPaths(const std::filesystem::path& directoryPath) const +CoverService::getCoverPaths(const std::filesystem::path& directoryPath) const { std::multimap res; std::error_code ec; @@ -296,7 +296,7 @@ Grabber::getCoverPaths(const std::filesystem::path& directoryPath) const } std::unique_ptr -Grabber::getFromTrack(const std::filesystem::path& p, ImageSize width) const +CoverService::getFromTrack(const std::filesystem::path& p, ImageSize width) const { std::unique_ptr image; @@ -313,13 +313,13 @@ Grabber::getFromTrack(const std::filesystem::path& p, ImageSize width) const } std::shared_ptr -Grabber::getFromTrack(Database::TrackId trackId, ImageSize width) +CoverService::getFromTrack(Database::TrackId trackId, ImageSize width) { return getFromTrack(_db.getTLSSession(), trackId, width, true /* allow release fallback*/); } std::shared_ptr -Grabber::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width, bool allowReleaseFallback) +CoverService::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width, bool allowReleaseFallback) { using namespace Database; @@ -357,7 +357,7 @@ Grabber::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, I } std::shared_ptr -Grabber::getFromRelease(Database::ReleaseId releaseId, ImageSize width) +CoverService::getFromRelease(Database::ReleaseId releaseId, ImageSize width) { const CacheEntryDesc cacheEntryDesc {releaseId, width}; @@ -409,7 +409,7 @@ Grabber::getFromRelease(Database::ReleaseId releaseId, ImageSize width) } void -Grabber::flushCache() +CoverService::flushCache() { std::unique_lock lock {_cacheMutex}; @@ -421,7 +421,7 @@ Grabber::flushCache() } void -Grabber::setJpegQuality(unsigned quality) +CoverService::setJpegQuality(unsigned quality) { _jpegQuality = Utils::clamp(quality, 1, 100); @@ -429,7 +429,7 @@ Grabber::setJpegQuality(unsigned quality) } void -Grabber::saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr image) +CoverService::saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr image) { std::unique_lock lock {_cacheMutex}; @@ -445,7 +445,7 @@ Grabber::saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr -Grabber::loadFromCache(const CacheEntryDesc& entryDesc) +CoverService::loadFromCache(const CacheEntryDesc& entryDesc) { std::shared_lock lock {_cacheMutex}; @@ -460,5 +460,5 @@ Grabber::loadFromCache(const CacheEntryDesc& entryDesc) return it->second; } -} // namespace CoverArt +} // namespace Cover diff --git a/src/libs/cover/impl/CoverArtGrabber.hpp b/src/libs/cover/impl/CoverService.hpp similarity index 89% rename from src/libs/cover/impl/CoverArtGrabber.hpp rename to src/libs/cover/impl/CoverService.hpp index 15e5f1ec..c67f068c 100644 --- a/src/libs/cover/impl/CoverArtGrabber.hpp +++ b/src/libs/cover/impl/CoverService.hpp @@ -29,7 +29,7 @@ #include #include -#include "cover/ICoverArtGrabber.hpp" +#include "cover/ICoverService.hpp" #include "cover/IEncodedImage.hpp" #include "database/Types.hpp" @@ -43,7 +43,7 @@ namespace Av class IAudioFile; } -namespace CoverArt +namespace Cover { struct CacheEntryDesc { @@ -57,16 +57,16 @@ namespace CoverArt } }; -} // ns CoverArt +} // ns Cover namespace std { template<> - class hash + class hash { public: - size_t operator()(const CoverArt::CacheEntryDesc& e) const + size_t operator()(const Cover::CacheEntryDesc& e) const { size_t h {}; std::visit([&](auto id) @@ -81,19 +81,19 @@ namespace std } // ns std -namespace CoverArt +namespace Cover { - class Grabber : public IGrabber + class CoverService : public ICoverService { public: - Grabber(Database::Db& db, + CoverService(Database::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath); - Grabber(const Grabber&) = delete; - Grabber& operator=(const Grabber&) = delete; - Grabber(Grabber&&) = delete; - Grabber& operator=(Grabber&&) = delete; + CoverService(const CoverService&) = delete; + CoverService& operator=(const CoverService&) = delete; + CoverService(CoverService&&) = delete; + CoverService& operator=(CoverService&&) = delete; private: std::shared_ptr getFromTrack(Database::TrackId trackId, ImageSize width) override; @@ -133,5 +133,5 @@ namespace CoverArt unsigned _jpegQuality; }; -} // namespace CoverArt +} // namespace Cover diff --git a/src/libs/cover/impl/Exception.hpp b/src/libs/cover/impl/Exception.hpp index e6c8c342..78708f85 100644 --- a/src/libs/cover/impl/Exception.hpp +++ b/src/libs/cover/impl/Exception.hpp @@ -21,7 +21,7 @@ #include "utils/Exception.hpp" -namespace CoverArt +namespace Cover { // internal use only class ImageException : public LmsException @@ -30,5 +30,5 @@ namespace CoverArt using LmsException::LmsException; }; -} // namespace CoverArt +} // namespace Cover diff --git a/src/libs/cover/impl/IRawImage.hpp b/src/libs/cover/impl/IRawImage.hpp index 6ff9e6d1..02e13ce8 100644 --- a/src/libs/cover/impl/IRawImage.hpp +++ b/src/libs/cover/impl/IRawImage.hpp @@ -23,7 +23,7 @@ #include "cover/IEncodedImage.hpp" -namespace CoverArt +namespace Cover { class IRawImage { diff --git a/src/libs/cover/impl/stb/JPEGImage.cpp b/src/libs/cover/impl/stb/JPEGImage.cpp index 68eafe43..aad386c2 100644 --- a/src/libs/cover/impl/stb/JPEGImage.cpp +++ b/src/libs/cover/impl/stb/JPEGImage.cpp @@ -25,7 +25,7 @@ #include "RawImage.hpp" #include "Exception.hpp" -namespace CoverArt::STB +namespace Cover::STB { JPEGImage::JPEGImage(const RawImage& rawImage, unsigned quality) { diff --git a/src/libs/cover/impl/stb/JPEGImage.hpp b/src/libs/cover/impl/stb/JPEGImage.hpp index 3256cd10..0ccfd98f 100644 --- a/src/libs/cover/impl/stb/JPEGImage.hpp +++ b/src/libs/cover/impl/stb/JPEGImage.hpp @@ -23,7 +23,7 @@ #include "cover/IEncodedImage.hpp" -namespace CoverArt::STB +namespace Cover::STB { class RawImage; class JPEGImage : public IEncodedImage diff --git a/src/libs/cover/impl/stb/RawImage.cpp b/src/libs/cover/impl/stb/RawImage.cpp index 13ff1d75..9df69d80 100644 --- a/src/libs/cover/impl/stb/RawImage.cpp +++ b/src/libs/cover/impl/stb/RawImage.cpp @@ -32,7 +32,7 @@ #include "Exception.hpp" -namespace CoverArt::STB +namespace Cover::STB { RawImage::RawImage(const std::byte* encodedData, std::size_t encodedDataSize) { diff --git a/src/libs/cover/impl/stb/RawImage.hpp b/src/libs/cover/impl/stb/RawImage.hpp index d4f36aa1..b0753268 100644 --- a/src/libs/cover/impl/stb/RawImage.hpp +++ b/src/libs/cover/impl/stb/RawImage.hpp @@ -29,7 +29,7 @@ #include "cover/IEncodedImage.hpp" #include "IRawImage.hpp" -namespace CoverArt::STB +namespace Cover::STB { class RawImage : public IRawImage { diff --git a/src/libs/cover/include/cover/ICoverArtGrabber.hpp b/src/libs/cover/include/cover/ICoverService.hpp similarity index 82% rename from src/libs/cover/include/cover/ICoverArtGrabber.hpp rename to src/libs/cover/include/cover/ICoverService.hpp index d6bcdf67..1df5942b 100644 --- a/src/libs/cover/include/cover/ICoverArtGrabber.hpp +++ b/src/libs/cover/include/cover/ICoverService.hpp @@ -30,12 +30,12 @@ namespace Database class Db; } -namespace CoverArt +namespace Cover { - class IGrabber + class ICoverService { public: - virtual ~IGrabber() = default; + virtual ~ICoverService() = default; virtual std::shared_ptr getFromTrack(Database::TrackId trackId, ImageSize width) = 0; virtual std::shared_ptr getFromRelease(Database::ReleaseId releaseId, ImageSize width) = 0; @@ -45,9 +45,9 @@ namespace CoverArt virtual void setJpegQuality(unsigned quality) = 0; // from 1 to 100 }; - std::unique_ptr createGrabber(Database::Db& db, - const std::filesystem::path& execPath, - const std::filesystem::path& defaultCoverPath); + std::unique_ptr createCoverService(Database::Db& db, + const std::filesystem::path& execPath, + const std::filesystem::path& defaultCoverPath); } // namespace CoverArt diff --git a/src/libs/cover/include/cover/IEncodedImage.hpp b/src/libs/cover/include/cover/IEncodedImage.hpp index 0fc245be..8567a57b 100644 --- a/src/libs/cover/include/cover/IEncodedImage.hpp +++ b/src/libs/cover/include/cover/IEncodedImage.hpp @@ -22,7 +22,7 @@ #include #include -namespace CoverArt +namespace Cover { using ImageSize = std::size_t; @@ -36,5 +36,5 @@ namespace CoverArt virtual std::string_view getMimeType() const = 0; }; -} // namespace CoverArt +} // namespace Cover diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 5cc0b4e4..27881aa0 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -28,7 +28,7 @@ #include "auth/IPasswordService.hpp" #include "auth/IEnvService.hpp" -#include "cover/ICoverArtGrabber.hpp" +#include "cover/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" @@ -1802,11 +1802,11 @@ handleGetCoverArt(RequestContext& context, const Wt::Http::Request& /*request*/, std::size_t size {getParameterAs(context.parameters, "size").value_or(256)}; size = Utils::clamp(size, std::size_t {32}, std::size_t {1024}); - std::shared_ptr cover; + std::shared_ptr cover; if (trackId) - cover = Service::get()->getFromTrack(*trackId, size); + cover = Service::get()->getFromTrack(*trackId, size); else if (releaseId) - cover = Service::get()->getFromRelease(*releaseId, size); + cover = Service::get()->getFromRelease(*releaseId, size); response.out().write(reinterpret_cast(cover->getData()), cover->getDataSize()); response.setMimeType(std::string {cover->getMimeType()}); diff --git a/src/lms/main.cpp b/src/lms/main.cpp index ec7b906c..5ad76bfe 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -28,7 +28,7 @@ #include "auth/IAuthTokenService.hpp" #include "auth/IPasswordService.hpp" #include "auth/IEnvService.hpp" -#include "cover/ICoverArtGrabber.hpp" +#include "cover/ICoverService.hpp" #include "database/Db.hpp" #include "database/Session.hpp" #include "scanner/IScanner.hpp" @@ -224,7 +224,7 @@ int main(int argc, char* argv[]) Wt::WServer server {argv[0]}; server.setServerConfiguration(wtServerArgs.size(), const_cast(&wtArgv[0])); - IOContextRunner ioContextRunner {ioContext, std::max(2, std::thread::hardware_concurrency())}; + 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()}; @@ -257,7 +257,7 @@ int main(int argc, char* argv[]) throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"}; Service httpClient {Http::createClient(ioContext)}; - Service coverArtService {CoverArt::createGrabber(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; + Service coverService {Cover::createCoverService(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; Service recommendationEngineService {Recommendation::createEngine(database)}; Service scannerService {Scanner::createScanner(database, *recommendationEngineService)}; @@ -265,7 +265,7 @@ int main(int argc, char* argv[]) { // Flush cover cache even if no changes: // covers may be external files that changed and we don't keep track of them - coverArtService->flushCache(); + coverService->flushCache(); }); Service scrobblingService {Scrobbling::createScrobbling(ioContext, database)}; diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index e192763e..38116bd8 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -30,7 +30,7 @@ #include "auth/IEnvService.hpp" #include "auth/IPasswordService.hpp" -#include "cover/ICoverArtGrabber.hpp" +#include "cover/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index 2ed89d9e..c06b3159 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -22,7 +22,7 @@ #include #include -#include "cover/ICoverArtGrabber.hpp" +#include "cover/ICoverService.hpp" #include "database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" @@ -83,7 +83,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons return; } - std::shared_ptr cover; + std::shared_ptr cover; if (trackIdStr) { @@ -96,7 +96,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons return; } - cover = Service::get()->getFromTrack(*trackId, *size); + cover = Service::get()->getFromTrack(*trackId, *size); } else if (releaseIdStr) { @@ -106,7 +106,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons if (!releaseId) return; - cover = Service::get()->getFromRelease(*releaseId, *size); + cover = Service::get()->getFromRelease(*releaseId, *size); } else { diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index f1b44c97..e09b223d 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -24,7 +24,7 @@ #include -#include "cover/ICoverArtGrabber.hpp" +#include "cover/ICoverService.hpp" #include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" @@ -36,7 +36,7 @@ static void -dumpTrackCovers(Database::Session& session, CoverArt::ImageSize width) +dumpTrackCovers(Database::Session& session, Cover::ImageSize width) { std::vector trackIds; { @@ -47,7 +47,7 @@ dumpTrackCovers(Database::Session& session, CoverArt::ImageSize width) for (const Database::TrackId trackId : trackIds) { std::cout << "Getting cover for track id " << trackId.toString() << std::endl; - Service::get()->getFromTrack(trackId, width); + Service::get()->getFromTrack(trackId, width); } } @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) Service config {createConfig(vm["conf"].as())}; Database::Db db {config->getPath("working-dir") / "lms.db"}; - Service coverArtService {CoverArt::createGrabber(db, argv[0], vm["default-cover"].as())}; + Service coverArtService {Cover::createCoverService(db, argv[0], vm["default-cover"].as())}; coverArtService->setJpegQuality(config->getULong("cover-jpeg-quality", vm["quality"].as())); From 32a944e3d4c58270e88392e02153d643f79eb9a6 Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 16 Oct 2021 15:22:59 +0200 Subject: [PATCH 16/49] Introduced lmscore lib to centralize services, migrated cover service --- src/libs/CMakeLists.txt | 3 +- src/libs/cover/CMakeLists.txt | 44 ------------------- src/libs/image/CMakeLists.txt | 38 ++++++++++++++++ .../impl/graphicsmagick/JPEGImage.cpp | 0 .../impl/graphicsmagick/JPEGImage.hpp | 0 .../impl/graphicsmagick/RawImage.cpp | 13 +++++- .../impl/graphicsmagick/RawImage.hpp | 0 .../{cover => image}/impl/stb/JPEGImage.cpp | 4 +- .../{cover => image}/impl/stb/JPEGImage.hpp | 4 +- .../{cover => image}/impl/stb/RawImage.cpp | 17 ++++++- .../{cover => image}/impl/stb/RawImage.hpp | 6 +-- .../include/image}/Exception.hpp | 2 +- .../include/image}/IEncodedImage.hpp | 2 +- .../include/image}/IRawImage.hpp | 7 ++- src/libs/lmscore/CMakeLists.txt | 28 ++++++++++++ .../{cover => lmscore}/impl/CoverService.cpp | 29 +++++------- .../{cover => lmscore}/impl/CoverService.hpp | 30 ++++++------- .../lmscore/services}/ICoverService.hpp | 6 +-- src/libs/subsonic/CMakeLists.txt | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 4 +- src/lms/CMakeLists.txt | 2 +- src/lms/main.cpp | 2 +- src/lms/ui/LmsApplication.cpp | 2 +- src/lms/ui/resource/CoverResource.cpp | 4 +- src/tools/cover/CMakeLists.txt | 2 +- src/tools/cover/LmsCover.cpp | 4 +- 26 files changed, 149 insertions(+), 106 deletions(-) delete mode 100644 src/libs/cover/CMakeLists.txt create mode 100644 src/libs/image/CMakeLists.txt rename src/libs/{cover => image}/impl/graphicsmagick/JPEGImage.cpp (100%) rename src/libs/{cover => image}/impl/graphicsmagick/JPEGImage.hpp (100%) rename src/libs/{cover => image}/impl/graphicsmagick/RawImage.cpp (91%) rename src/libs/{cover => image}/impl/graphicsmagick/RawImage.hpp (100%) rename src/libs/{cover => image}/impl/stb/JPEGImage.cpp (97%) rename src/libs/{cover => image}/impl/stb/JPEGImage.hpp (95%) rename src/libs/{cover => image}/impl/stb/RawImage.cpp (88%) rename src/libs/{cover => image}/impl/stb/RawImage.hpp (94%) rename src/libs/{cover/impl => image/include/image}/Exception.hpp (98%) rename src/libs/{cover/include/cover => image/include/image}/IEncodedImage.hpp (98%) rename src/libs/{cover/impl => image/include/image}/IRawImage.hpp (80%) create mode 100644 src/libs/lmscore/CMakeLists.txt rename src/libs/{cover => lmscore}/impl/CoverService.cpp (95%) rename src/libs/{cover => lmscore}/impl/CoverService.hpp (66%) rename src/libs/{cover/include/cover => lmscore/include/lmscore/services}/ICoverService.hpp (81%) diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index e8c1413c..728cc175 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -1,8 +1,9 @@ add_subdirectory(auth) add_subdirectory(av) -add_subdirectory(cover) add_subdirectory(database) +add_subdirectory(image) +add_subdirectory(lmscore) add_subdirectory(metadata) add_subdirectory(recommendation) add_subdirectory(scanner) diff --git a/src/libs/cover/CMakeLists.txt b/src/libs/cover/CMakeLists.txt deleted file mode 100644 index 12627c9e..00000000 --- a/src/libs/cover/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ - -add_library(lmscover SHARED - impl/CoverService.cpp - ) - -target_include_directories(lmscover INTERFACE - include - ) - -target_include_directories(lmscover PRIVATE - include - impl - ) - -target_link_libraries(lmscover PRIVATE - lmsav - ) - -target_link_libraries(lmscover PUBLIC - lmsdatabase - lmsutils - std::filesystem - ) - -if (IMAGE_LIBRARY STREQUAL STB) - target_sources(lmscover PRIVATE - impl/stb/JPEGImage.cpp - impl/stb/RawImage.cpp - ) - target_compile_options(lmscover PRIVATE "-DLMS_SUPPORT_IMAGE_STB") - target_include_directories(lmscover PRIVATE ${STB_INCLUDE_DIR}) -elseif (IMAGE_LIBRARY STREQUAL GraphicsMagick++) - target_sources(lmscover PRIVATE - impl/graphicsmagick/JPEGImage.cpp - impl/graphicsmagick/RawImage.cpp - ) - target_compile_options(lmscover PRIVATE "-DLMS_SUPPORT_IMAGE_GM") - target_link_libraries(lmscover PRIVATE PkgConfig::GraphicsMagick++) -else () - message(FATAL_ERROR "Invalid IMAGE_LIBRARY provided") -endif() - -install(TARGETS lmscover DESTINATION lib) - diff --git a/src/libs/image/CMakeLists.txt b/src/libs/image/CMakeLists.txt new file mode 100644 index 00000000..cb855563 --- /dev/null +++ b/src/libs/image/CMakeLists.txt @@ -0,0 +1,38 @@ + +add_library(lmsimage SHARED + ) + +target_include_directories(lmsimage INTERFACE + include + ) + +target_include_directories(lmsimage PRIVATE + include + impl + ) + +target_link_libraries(lmsimage PUBLIC + lmsutils + std::filesystem + ) + +if (IMAGE_LIBRARY STREQUAL STB) + target_sources(lmsimage PRIVATE + impl/stb/JPEGImage.cpp + impl/stb/RawImage.cpp + ) + target_compile_options(lmsimage PRIVATE "-DLMS_SUPPORT_IMAGE_STB") + target_include_directories(lmsimage PRIVATE ${STB_INCLUDE_DIR}) +elseif (IMAGE_LIBRARY STREQUAL GraphicsMagick++) + target_sources(lmsimage PRIVATE + impl/graphicsmagick/JPEGImage.cpp + impl/graphicsmagick/RawImage.cpp + ) + target_compile_options(lmsimage PRIVATE "-DLMS_SUPPORT_IMAGE_GM") + target_link_libraries(lmsimage PRIVATE PkgConfig::GraphicsMagick++) +else () + message(FATAL_ERROR "Invalid IMAGE_LIBRARY provided") +endif() + +install(TARGETS lmsimage DESTINATION lib) + diff --git a/src/libs/cover/impl/graphicsmagick/JPEGImage.cpp b/src/libs/image/impl/graphicsmagick/JPEGImage.cpp similarity index 100% rename from src/libs/cover/impl/graphicsmagick/JPEGImage.cpp rename to src/libs/image/impl/graphicsmagick/JPEGImage.cpp diff --git a/src/libs/cover/impl/graphicsmagick/JPEGImage.hpp b/src/libs/image/impl/graphicsmagick/JPEGImage.hpp similarity index 100% rename from src/libs/cover/impl/graphicsmagick/JPEGImage.hpp rename to src/libs/image/impl/graphicsmagick/JPEGImage.hpp diff --git a/src/libs/cover/impl/graphicsmagick/RawImage.cpp b/src/libs/image/impl/graphicsmagick/RawImage.cpp similarity index 91% rename from src/libs/cover/impl/graphicsmagick/RawImage.cpp rename to src/libs/image/impl/graphicsmagick/RawImage.cpp index 3beabc27..8ec77b2e 100644 --- a/src/libs/cover/impl/graphicsmagick/RawImage.cpp +++ b/src/libs/image/impl/graphicsmagick/RawImage.cpp @@ -25,7 +25,16 @@ #include "JPEGImage.hpp" #include "Exception.hpp" -namespace CoverArt::GraphicsMagick { +namespace Image::GraphicsMagick +{ + + std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize) + { + return std::make_unique(encodedData, encodedDataSize); + } + + std::unique_ptr decodeImage(const std::filesystem::path& path) + { void init(const std::filesystem::path& path) @@ -116,5 +125,5 @@ RawImage::getMagickImage() const return _image; } -} // namespace CoverArt::GraphicsMagick +} // namespace Image::GraphicsMagick diff --git a/src/libs/cover/impl/graphicsmagick/RawImage.hpp b/src/libs/image/impl/graphicsmagick/RawImage.hpp similarity index 100% rename from src/libs/cover/impl/graphicsmagick/RawImage.hpp rename to src/libs/image/impl/graphicsmagick/RawImage.hpp diff --git a/src/libs/cover/impl/stb/JPEGImage.cpp b/src/libs/image/impl/stb/JPEGImage.cpp similarity index 97% rename from src/libs/cover/impl/stb/JPEGImage.cpp rename to src/libs/image/impl/stb/JPEGImage.cpp index aad386c2..4bfcef4c 100644 --- a/src/libs/cover/impl/stb/JPEGImage.cpp +++ b/src/libs/image/impl/stb/JPEGImage.cpp @@ -22,10 +22,10 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include +#include "image/Exception.hpp" #include "RawImage.hpp" -#include "Exception.hpp" -namespace Cover::STB +namespace Image::STB { JPEGImage::JPEGImage(const RawImage& rawImage, unsigned quality) { diff --git a/src/libs/cover/impl/stb/JPEGImage.hpp b/src/libs/image/impl/stb/JPEGImage.hpp similarity index 95% rename from src/libs/cover/impl/stb/JPEGImage.hpp rename to src/libs/image/impl/stb/JPEGImage.hpp index 0ccfd98f..ddce80f2 100644 --- a/src/libs/cover/impl/stb/JPEGImage.hpp +++ b/src/libs/image/impl/stb/JPEGImage.hpp @@ -21,9 +21,9 @@ #include -#include "cover/IEncodedImage.hpp" +#include "image/IEncodedImage.hpp" -namespace Cover::STB +namespace Image::STB { class RawImage; class JPEGImage : public IEncodedImage diff --git a/src/libs/cover/impl/stb/RawImage.cpp b/src/libs/image/impl/stb/RawImage.cpp similarity index 88% rename from src/libs/cover/impl/stb/RawImage.cpp rename to src/libs/image/impl/stb/RawImage.cpp index 9df69d80..8ad944bc 100644 --- a/src/libs/cover/impl/stb/RawImage.cpp +++ b/src/libs/image/impl/stb/RawImage.cpp @@ -30,9 +30,22 @@ #include "JPEGImage.hpp" -#include "Exception.hpp" +#include "image/Exception.hpp" -namespace Cover::STB +namespace Image +{ + std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize) + { + return std::make_unique(encodedData, encodedDataSize); + } + + std::unique_ptr decodeImage(const std::filesystem::path& path) + { + return std::make_unique(path); + } +} + +namespace Image::STB { RawImage::RawImage(const std::byte* encodedData, std::size_t encodedDataSize) { diff --git a/src/libs/cover/impl/stb/RawImage.hpp b/src/libs/image/impl/stb/RawImage.hpp similarity index 94% rename from src/libs/cover/impl/stb/RawImage.hpp rename to src/libs/image/impl/stb/RawImage.hpp index b0753268..c7d7921a 100644 --- a/src/libs/cover/impl/stb/RawImage.hpp +++ b/src/libs/image/impl/stb/RawImage.hpp @@ -26,10 +26,10 @@ #include #include -#include "cover/IEncodedImage.hpp" -#include "IRawImage.hpp" +#include "image/IEncodedImage.hpp" +#include "image/IRawImage.hpp" -namespace Cover::STB +namespace Image::STB { class RawImage : public IRawImage { diff --git a/src/libs/cover/impl/Exception.hpp b/src/libs/image/include/image/Exception.hpp similarity index 98% rename from src/libs/cover/impl/Exception.hpp rename to src/libs/image/include/image/Exception.hpp index 78708f85..53428934 100644 --- a/src/libs/cover/impl/Exception.hpp +++ b/src/libs/image/include/image/Exception.hpp @@ -21,7 +21,7 @@ #include "utils/Exception.hpp" -namespace Cover +namespace Image { // internal use only class ImageException : public LmsException diff --git a/src/libs/cover/include/cover/IEncodedImage.hpp b/src/libs/image/include/image/IEncodedImage.hpp similarity index 98% rename from src/libs/cover/include/cover/IEncodedImage.hpp rename to src/libs/image/include/image/IEncodedImage.hpp index 8567a57b..96fc03a5 100644 --- a/src/libs/cover/include/cover/IEncodedImage.hpp +++ b/src/libs/image/include/image/IEncodedImage.hpp @@ -22,7 +22,7 @@ #include #include -namespace Cover +namespace Image { using ImageSize = std::size_t; diff --git a/src/libs/cover/impl/IRawImage.hpp b/src/libs/image/include/image/IRawImage.hpp similarity index 80% rename from src/libs/cover/impl/IRawImage.hpp rename to src/libs/image/include/image/IRawImage.hpp index 02e13ce8..67968ed0 100644 --- a/src/libs/cover/impl/IRawImage.hpp +++ b/src/libs/image/include/image/IRawImage.hpp @@ -21,9 +21,9 @@ #include -#include "cover/IEncodedImage.hpp" +#include "image/IEncodedImage.hpp" -namespace Cover +namespace Image { class IRawImage { @@ -32,5 +32,8 @@ namespace Cover virtual void resize(ImageSize width) = 0; virtual std::unique_ptr encodeToJPEG(unsigned quality) const = 0; }; + + std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize); + std::unique_ptr decodeImage(const std::filesystem::path& path); } diff --git a/src/libs/lmscore/CMakeLists.txt b/src/libs/lmscore/CMakeLists.txt new file mode 100644 index 00000000..358df97a --- /dev/null +++ b/src/libs/lmscore/CMakeLists.txt @@ -0,0 +1,28 @@ + +add_library(lmscore SHARED + impl/CoverService.cpp + ) + +target_include_directories(lmscore INTERFACE + include + ) + +target_include_directories(lmscore PRIVATE + include + impl + ) + +target_link_libraries(lmscore PRIVATE + lmsav + lmsimage + ) + +target_link_libraries(lmscore PUBLIC + lmsdatabase + lmsimage + lmsutils + std::filesystem + ) + +install(TARGETS lmscore DESTINATION lib) + diff --git a/src/libs/cover/impl/CoverService.cpp b/src/libs/lmscore/impl/CoverService.cpp similarity index 95% rename from src/libs/cover/impl/CoverService.cpp rename to src/libs/lmscore/impl/CoverService.cpp index af768802..a968d5c4 100644 --- a/src/libs/cover/impl/CoverService.cpp +++ b/src/libs/lmscore/impl/CoverService.cpp @@ -26,19 +26,12 @@ #include "database/Session.hpp" #include "database/Track.hpp" -#if LMS_SUPPORT_IMAGE_STB -#include "stb/RawImage.hpp" -using RawImage = Cover::STB::RawImage; -#elif LMS_SUPPORT_IMAGE_GM -#include "graphicsmagick/RawImage.hpp" -using RawImage = Cover::GraphicsMagick::RawImage; -#endif - +#include "image/Exception.hpp" +#include "image/IRawImage.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" #include "utils/Utils.hpp" -#include "Exception.hpp" namespace { @@ -79,6 +72,8 @@ namespace namespace Cover { +using namespace Image; + static bool isFileSupported(const std::filesystem::path& file, const std::vector& extensions) @@ -117,7 +112,7 @@ CoverService::CoverService(Database::Db& db, { getDefault(512); } - catch (const ImageException& e) + catch (const Image::ImageException& e) { throw LmsException("Cannot read default cover file '" + _defaultCoverPath.string() + "': " + e.what()); } @@ -135,11 +130,11 @@ CoverService::getFromAvMediaFile(const Av::IAudioFile& input, ImageSize width) c try { - RawImage rawImage {picture.data, picture.dataSize}; - rawImage.resize(width); - image = rawImage.encodeToJPEG(_jpegQuality); + std::unique_ptr rawImage {decodeImage(picture.data, picture.dataSize)}; + rawImage->resize(width); + image = rawImage->encodeToJPEG(_jpegQuality); } - catch (const ImageException& e) + catch (const Image::ImageException& e) { LMS_LOG(COVER, ERROR) << "Cannot read embedded cover: " << e.what(); } @@ -155,9 +150,9 @@ CoverService::getFromCoverFile(const std::filesystem::path& p, ImageSize width) try { - RawImage rawImage {p}; - rawImage.resize(width); - image = rawImage.encodeToJPEG(_jpegQuality); + std::unique_ptr rawImage {decodeImage(p)}; + rawImage->resize(width); + image = rawImage->encodeToJPEG(_jpegQuality); } catch (const ImageException& e) { diff --git a/src/libs/cover/impl/CoverService.hpp b/src/libs/lmscore/impl/CoverService.hpp similarity index 66% rename from src/libs/cover/impl/CoverService.hpp rename to src/libs/lmscore/impl/CoverService.hpp index c67f068c..a740673e 100644 --- a/src/libs/cover/impl/CoverService.hpp +++ b/src/libs/lmscore/impl/CoverService.hpp @@ -29,8 +29,8 @@ #include #include -#include "cover/ICoverService.hpp" -#include "cover/IEncodedImage.hpp" +#include "lmscore/services/ICoverService.hpp" +#include "image/IEncodedImage.hpp" #include "database/Types.hpp" namespace Database @@ -96,34 +96,34 @@ namespace Cover CoverService& operator=(CoverService&&) = delete; private: - std::shared_ptr getFromTrack(Database::TrackId trackId, ImageSize width) override; - std::shared_ptr getFromRelease(Database::ReleaseId releaseId, ImageSize width) override; + std::shared_ptr getFromTrack(Database::TrackId trackId, Image::ImageSize width) override; + std::shared_ptr getFromRelease(Database::ReleaseId releaseId, Image::ImageSize width) override; void flushCache() override; void setJpegQuality(unsigned quality) override; - std::shared_ptr getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width, bool allowReleaseFallback); - std::unique_ptr getFromAvMediaFile(const Av::IAudioFile& input, ImageSize width) const; - std::unique_ptr getFromCoverFile(const std::filesystem::path& p, ImageSize width) const; + std::shared_ptr getFromTrack(Database::Session& dbSession, Database::TrackId trackId, Image::ImageSize width, bool allowReleaseFallback); + std::unique_ptr getFromAvMediaFile(const Av::IAudioFile& input, Image::ImageSize width) const; + std::unique_ptr getFromCoverFile(const std::filesystem::path& p, Image::ImageSize width) const; - std::unique_ptr getFromTrack(const std::filesystem::path& path, ImageSize width) const; + std::unique_ptr getFromTrack(const std::filesystem::path& path, Image::ImageSize width) const; std::multimap getCoverPaths(const std::filesystem::path& directoryPath) const; - std::unique_ptr getFromDirectory(const std::filesystem::path& directory, ImageSize width) const; - std::unique_ptr getFromSameNamedFile(const std::filesystem::path& filePath, ImageSize width) const; - std::shared_ptr getDefault(ImageSize width); + std::unique_ptr getFromDirectory(const std::filesystem::path& directory, Image::ImageSize width) const; + std::unique_ptr getFromSameNamedFile(const std::filesystem::path& filePath, Image::ImageSize width) const; + std::shared_ptr getDefault(Image::ImageSize width); bool checkCoverFile(const std::filesystem::path& directoryPath) const; Database::Db& _db; std::shared_mutex _cacheMutex; - std::unordered_map> _cache; - std::unordered_map> _defaultCoverCache; + std::unordered_map> _cache; + std::unordered_map> _defaultCoverCache; std::atomic _cacheMisses {}; std::atomic _cacheHits {}; std::size_t _cacheSize {}; - void saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr image); - std::shared_ptr loadFromCache(const CacheEntryDesc& entryDesc); + void saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr image); + std::shared_ptr loadFromCache(const CacheEntryDesc& entryDesc); const std::filesystem::path _defaultCoverPath; const std::size_t _maxCacheSize; diff --git a/src/libs/cover/include/cover/ICoverService.hpp b/src/libs/lmscore/include/lmscore/services/ICoverService.hpp similarity index 81% rename from src/libs/cover/include/cover/ICoverService.hpp rename to src/libs/lmscore/include/lmscore/services/ICoverService.hpp index 1df5942b..039260ef 100644 --- a/src/libs/cover/include/cover/ICoverService.hpp +++ b/src/libs/lmscore/include/lmscore/services/ICoverService.hpp @@ -23,7 +23,7 @@ #include #include "database/Types.hpp" -#include "cover/IEncodedImage.hpp" +#include "image/IEncodedImage.hpp" namespace Database { @@ -37,8 +37,8 @@ namespace Cover public: virtual ~ICoverService() = default; - virtual std::shared_ptr getFromTrack(Database::TrackId trackId, ImageSize width) = 0; - virtual std::shared_ptr getFromRelease(Database::ReleaseId releaseId, ImageSize width) = 0; + virtual std::shared_ptr getFromTrack(Database::TrackId trackId, Image::ImageSize width) = 0; + virtual std::shared_ptr getFromRelease(Database::ReleaseId releaseId, Image::ImageSize width) = 0; virtual void flushCache() = 0; diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index 98093601..aad488ca 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -19,7 +19,7 @@ target_include_directories(lmssubsonic PRIVATE target_link_libraries(lmssubsonic PRIVATE lmsauth lmsav - lmscover + lmscore lmsdatabase lmsrecommendation lmsscanner diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 27881aa0..888eadf6 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -28,7 +28,7 @@ #include "auth/IPasswordService.hpp" #include "auth/IEnvService.hpp" -#include "cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" @@ -1802,7 +1802,7 @@ handleGetCoverArt(RequestContext& context, const Wt::Http::Request& /*request*/, std::size_t size {getParameterAs(context.parameters, "size").value_or(256)}; size = Utils::clamp(size, std::size_t {32}, std::size_t {1024}); - std::shared_ptr cover; + std::shared_ptr cover; if (trackId) cover = Service::get()->getFromTrack(*trackId, size); else if (releaseId) diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt index 77570c7e..beff7583 100644 --- a/src/lms/CMakeLists.txt +++ b/src/lms/CMakeLists.txt @@ -51,7 +51,7 @@ target_include_directories(lms PRIVATE target_link_libraries(lms PRIVATE lmsav lmsauth - lmscover + lmscore lmsdatabase lmsrecommendation lmsscanner diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 5ad76bfe..2d6230cf 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -28,7 +28,7 @@ #include "auth/IAuthTokenService.hpp" #include "auth/IPasswordService.hpp" #include "auth/IEnvService.hpp" -#include "cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Db.hpp" #include "database/Session.hpp" #include "scanner/IScanner.hpp" diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 38116bd8..8eb688e2 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -30,7 +30,7 @@ #include "auth/IEnvService.hpp" #include "auth/IPasswordService.hpp" -#include "cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index c06b3159..c13c5996 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -22,7 +22,7 @@ #include #include -#include "cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" @@ -83,7 +83,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons return; } - std::shared_ptr cover; + std::shared_ptr cover; if (trackIdStr) { diff --git a/src/tools/cover/CMakeLists.txt b/src/tools/cover/CMakeLists.txt index 141e2983..b695300f 100644 --- a/src/tools/cover/CMakeLists.txt +++ b/src/tools/cover/CMakeLists.txt @@ -4,7 +4,7 @@ add_executable(lms-cover ) target_link_libraries(lms-cover PRIVATE - lmscover + lmscore Boost::program_options ) diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index e09b223d..d2a5e784 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -24,7 +24,7 @@ #include -#include "cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" @@ -36,7 +36,7 @@ static void -dumpTrackCovers(Database::Session& session, Cover::ImageSize width) +dumpTrackCovers(Database::Session& session, Image::ImageSize width) { std::vector trackIds; { From 0f5c4870842bab897fac19ea8dd1bcd37f6d1c37 Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 16 Oct 2021 15:44:42 +0200 Subject: [PATCH 17/49] Migrated auth service --- src/libs/CMakeLists.txt | 1 - src/libs/auth/CMakeLists.txt | 40 ------------------- src/libs/lmscore/CMakeLists.txt | 19 ++++++++- .../impl/auth}/AuthServiceBase.cpp | 0 .../impl/auth}/AuthServiceBase.hpp | 0 .../impl/auth}/AuthTokenService.cpp | 2 +- .../impl/auth}/AuthTokenService.hpp | 2 +- .../impl => lmscore/impl/auth}/EnvService.cpp | 5 +-- .../impl/auth}/LoginThrottler.cpp | 0 .../impl/auth}/LoginThrottler.hpp | 0 .../impl/auth}/PasswordServiceBase.cpp | 2 +- .../impl/auth}/PasswordServiceBase.hpp | 4 +- .../http-headers/HttpHeadersEnvService.cpp | 0 .../http-headers/HttpHeadersEnvService.hpp | 4 +- .../internal/InternalPasswordService.cpp | 4 +- .../internal/InternalPasswordService.hpp | 4 +- .../impl/auth}/pam/PAMPasswordService.cpp | 2 +- .../impl/auth}/pam/PAMPasswordService.hpp | 4 +- .../lmscore/impl/{ => cover}/CoverService.cpp | 0 .../lmscore/impl/{ => cover}/CoverService.hpp | 2 +- .../lmscore}/auth/IAuthTokenService.hpp | 0 .../include/lmscore}/auth/IEnvService.hpp | 0 .../lmscore}/auth/IPasswordService.hpp | 4 +- .../include/lmscore}/auth/Types.hpp | 0 .../{services => cover}/ICoverService.hpp | 0 src/libs/subsonic/impl/SubsonicResource.cpp | 6 +-- src/lms/main.cpp | 8 ++-- src/lms/ui/Auth.cpp | 4 +- src/lms/ui/LmsApplication.cpp | 6 +-- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 2 +- src/lms/ui/admin/UsersView.cpp | 2 +- src/lms/ui/common/PasswordValidator.cpp | 2 +- src/lms/ui/common/PasswordValidator.hpp | 2 +- src/lms/ui/resource/CoverResource.cpp | 2 +- src/tools/cover/LmsCover.cpp | 2 +- 37 files changed, 55 insertions(+), 84 deletions(-) delete mode 100644 src/libs/auth/CMakeLists.txt rename src/libs/{auth/impl => lmscore/impl/auth}/AuthServiceBase.cpp (100%) rename src/libs/{auth/impl => lmscore/impl/auth}/AuthServiceBase.hpp (100%) rename src/libs/{auth/impl => lmscore/impl/auth}/AuthTokenService.cpp (99%) rename src/libs/{auth/impl => lmscore/impl/auth}/AuthTokenService.hpp (97%) rename src/libs/{auth/impl => lmscore/impl/auth}/EnvService.cpp (93%) rename src/libs/{auth/impl => lmscore/impl/auth}/LoginThrottler.cpp (100%) rename src/libs/{auth/impl => lmscore/impl/auth}/LoginThrottler.hpp (100%) rename src/libs/{auth/impl => lmscore/impl/auth}/PasswordServiceBase.cpp (98%) rename src/libs/{auth/impl => lmscore/impl/auth}/PasswordServiceBase.hpp (95%) rename src/libs/{auth/impl => lmscore/impl/auth}/http-headers/HttpHeadersEnvService.cpp (100%) rename src/libs/{auth/impl => lmscore/impl/auth}/http-headers/HttpHeadersEnvService.hpp (93%) rename src/libs/{auth/impl => lmscore/impl/auth}/internal/InternalPasswordService.cpp (98%) rename src/libs/{auth/impl => lmscore/impl/auth}/internal/InternalPasswordService.hpp (95%) rename src/libs/{auth/impl => lmscore/impl/auth}/pam/PAMPasswordService.cpp (99%) rename src/libs/{auth/impl => lmscore/impl/auth}/pam/PAMPasswordService.hpp (95%) rename src/libs/lmscore/impl/{ => cover}/CoverService.cpp (100%) rename src/libs/lmscore/impl/{ => cover}/CoverService.hpp (98%) rename src/libs/{auth/include => lmscore/include/lmscore}/auth/IAuthTokenService.hpp (100%) rename src/libs/{auth/include => lmscore/include/lmscore}/auth/IEnvService.hpp (100%) rename src/libs/{auth/include => lmscore/include/lmscore}/auth/IPasswordService.hpp (98%) rename src/libs/{auth/include => lmscore/include/lmscore}/auth/Types.hpp (100%) rename src/libs/lmscore/include/lmscore/{services => cover}/ICoverService.hpp (100%) diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 728cc175..196b2a1b 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -1,5 +1,4 @@ -add_subdirectory(auth) add_subdirectory(av) add_subdirectory(database) add_subdirectory(image) diff --git a/src/libs/auth/CMakeLists.txt b/src/libs/auth/CMakeLists.txt deleted file mode 100644 index 07400a6b..00000000 --- a/src/libs/auth/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ - -add_library(lmsauth SHARED - impl/AuthTokenService.cpp - impl/AuthServiceBase.cpp - impl/EnvService.cpp - impl/LoginThrottler.cpp - impl/PasswordServiceBase.cpp - impl/http-headers/HttpHeadersEnvService.cpp - impl/internal/InternalPasswordService.cpp - ) - -target_include_directories(lmsauth INTERFACE - include - ) - -target_include_directories(lmsauth PRIVATE - include - impl - ) - -target_link_libraries(lmsauth PRIVATE - lmsutils - lmsdatabase - ) - -target_link_libraries(lmsauth PUBLIC - pthread - Boost::system - Wt::Wt - ) - -if (USE_PAM) - target_compile_options(lmsauth PRIVATE "-DLMS_SUPPORT_PAM") - target_sources(lmsauth PRIVATE impl/pam/PAMPasswordService.cpp) - target_include_directories(lmsauth PRIVATE ${PAM_INCLUDE_DIR}) - target_link_libraries(lmsauth PRIVATE ${PAM_LIBRARIES}) -endif (USE_PAM) - -install(TARGETS lmsauth DESTINATION lib) - diff --git a/src/libs/lmscore/CMakeLists.txt b/src/libs/lmscore/CMakeLists.txt index 358df97a..4403380a 100644 --- a/src/libs/lmscore/CMakeLists.txt +++ b/src/libs/lmscore/CMakeLists.txt @@ -1,6 +1,13 @@ add_library(lmscore SHARED - impl/CoverService.cpp + impl/auth/AuthTokenService.cpp + impl/auth/AuthServiceBase.cpp + impl/auth/EnvService.cpp + impl/auth/LoginThrottler.cpp + impl/auth/PasswordServiceBase.cpp + impl/auth/http-headers/HttpHeadersEnvService.cpp + impl/auth/internal/InternalPasswordService.cpp + impl/cover/CoverService.cpp ) target_include_directories(lmscore INTERFACE @@ -14,7 +21,6 @@ target_include_directories(lmscore PRIVATE target_link_libraries(lmscore PRIVATE lmsav - lmsimage ) target_link_libraries(lmscore PUBLIC @@ -22,7 +28,16 @@ target_link_libraries(lmscore PUBLIC lmsimage lmsutils std::filesystem + Wt::Wt ) +if (USE_PAM) + target_compile_options(lmscore PRIVATE "-DLMS_SUPPORT_PAM") + target_sources(lmscore PRIVATE impl/auth/pam/PAMPasswordService.cpp) + target_include_directories(lmscore PRIVATE ${PAM_INCLUDE_DIR}) + target_link_libraries(lmscore PRIVATE ${PAM_LIBRARIES}) +endif (USE_PAM) + + install(TARGETS lmscore DESTINATION lib) diff --git a/src/libs/auth/impl/AuthServiceBase.cpp b/src/libs/lmscore/impl/auth/AuthServiceBase.cpp similarity index 100% rename from src/libs/auth/impl/AuthServiceBase.cpp rename to src/libs/lmscore/impl/auth/AuthServiceBase.cpp diff --git a/src/libs/auth/impl/AuthServiceBase.hpp b/src/libs/lmscore/impl/auth/AuthServiceBase.hpp similarity index 100% rename from src/libs/auth/impl/AuthServiceBase.hpp rename to src/libs/lmscore/impl/auth/AuthServiceBase.hpp diff --git a/src/libs/auth/impl/AuthTokenService.cpp b/src/libs/lmscore/impl/auth/AuthTokenService.cpp similarity index 99% rename from src/libs/auth/impl/AuthTokenService.cpp rename to src/libs/lmscore/impl/auth/AuthTokenService.cpp index f6f305b6..05949870 100644 --- a/src/libs/auth/impl/AuthTokenService.cpp +++ b/src/libs/lmscore/impl/auth/AuthTokenService.cpp @@ -23,7 +23,7 @@ #include #include -#include "auth/Types.hpp" +#include "lmscore/auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/auth/impl/AuthTokenService.hpp b/src/libs/lmscore/impl/auth/AuthTokenService.hpp similarity index 97% rename from src/libs/auth/impl/AuthTokenService.hpp rename to src/libs/lmscore/impl/auth/AuthTokenService.hpp index 8825a3d3..f1425117 100644 --- a/src/libs/auth/impl/AuthTokenService.hpp +++ b/src/libs/lmscore/impl/auth/AuthTokenService.hpp @@ -21,7 +21,7 @@ #include -#include "auth/IAuthTokenService.hpp" +#include "lmscore/auth/IAuthTokenService.hpp" #include "AuthServiceBase.hpp" #include "LoginThrottler.hpp" diff --git a/src/libs/auth/impl/EnvService.cpp b/src/libs/lmscore/impl/auth/EnvService.cpp similarity index 93% rename from src/libs/auth/impl/EnvService.cpp rename to src/libs/lmscore/impl/auth/EnvService.cpp index 12de9858..99046d40 100644 --- a/src/libs/auth/impl/EnvService.cpp +++ b/src/libs/lmscore/impl/auth/EnvService.cpp @@ -17,9 +17,8 @@ * along with LMS. If not, see . */ -#include "auth/IEnvService.hpp" - -#include "auth/Types.hpp" +#include "lmscore/auth/IEnvService.hpp" +#include "lmscore/auth/Types.hpp" #include "http-headers/HttpHeadersEnvService.hpp" namespace Auth diff --git a/src/libs/auth/impl/LoginThrottler.cpp b/src/libs/lmscore/impl/auth/LoginThrottler.cpp similarity index 100% rename from src/libs/auth/impl/LoginThrottler.cpp rename to src/libs/lmscore/impl/auth/LoginThrottler.cpp diff --git a/src/libs/auth/impl/LoginThrottler.hpp b/src/libs/lmscore/impl/auth/LoginThrottler.hpp similarity index 100% rename from src/libs/auth/impl/LoginThrottler.hpp rename to src/libs/lmscore/impl/auth/LoginThrottler.hpp diff --git a/src/libs/auth/impl/PasswordServiceBase.cpp b/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp similarity index 98% rename from src/libs/auth/impl/PasswordServiceBase.cpp rename to src/libs/lmscore/impl/auth/PasswordServiceBase.cpp index 3b230503..e80d1b9b 100644 --- a/src/libs/auth/impl/PasswordServiceBase.cpp +++ b/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp @@ -27,7 +27,7 @@ #include "pam/PAMPasswordService.hpp" #endif // LMS_SUPPORT_PAM -#include "auth/Types.hpp" +#include "lmscore/auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/auth/impl/PasswordServiceBase.hpp b/src/libs/lmscore/impl/auth/PasswordServiceBase.hpp similarity index 95% rename from src/libs/auth/impl/PasswordServiceBase.hpp rename to src/libs/lmscore/impl/auth/PasswordServiceBase.hpp index f575e4e1..5c922f55 100644 --- a/src/libs/auth/impl/PasswordServiceBase.hpp +++ b/src/libs/lmscore/impl/auth/PasswordServiceBase.hpp @@ -21,8 +21,8 @@ #include -#include "auth/IPasswordService.hpp" -#include "AuthServiceBase.hpp" +#include "lmscore/auth/IPasswordService.hpp" +#include "auth/AuthServiceBase.hpp" #include "LoginThrottler.hpp" namespace Database diff --git a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp b/src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.cpp similarity index 100% rename from src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp rename to src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.cpp diff --git a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp b/src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.hpp similarity index 93% rename from src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp rename to src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.hpp index 5c44b9fb..7cad1441 100644 --- a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp +++ b/src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.hpp @@ -19,8 +19,8 @@ #pragma once -#include "auth/IEnvService.hpp" -#include "AuthServiceBase.hpp" +#include "lmscore/auth/IEnvService.hpp" +#include "auth/AuthServiceBase.hpp" namespace Auth { diff --git a/src/libs/auth/impl/internal/InternalPasswordService.cpp b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp similarity index 98% rename from src/libs/auth/impl/internal/InternalPasswordService.cpp rename to src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp index 96a6c34a..233fb9aa 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp @@ -20,8 +20,8 @@ #include "InternalPasswordService.hpp" #include -#include "auth/IAuthTokenService.hpp" -#include "auth/Types.hpp" +#include "lmscore/auth/IAuthTokenService.hpp" +#include "lmscore/auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/auth/impl/internal/InternalPasswordService.hpp b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp similarity index 95% rename from src/libs/auth/impl/internal/InternalPasswordService.hpp rename to src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp index 6c241f31..42b830f1 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.hpp +++ b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp @@ -23,8 +23,8 @@ #include #include "database/User.hpp" -#include "PasswordServiceBase.hpp" -#include "LoginThrottler.hpp" +#include "auth/PasswordServiceBase.hpp" +#include "auth/LoginThrottler.hpp" namespace Auth { diff --git a/src/libs/auth/impl/pam/PAMPasswordService.cpp b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp similarity index 99% rename from src/libs/auth/impl/pam/PAMPasswordService.cpp rename to src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp index c5682f42..708ce457 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp @@ -26,7 +26,7 @@ #include #include -#include "auth/Types.hpp" +#include "lmscore/auth/Types.hpp" #include "database/Session.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/auth/impl/pam/PAMPasswordService.hpp b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.hpp similarity index 95% rename from src/libs/auth/impl/pam/PAMPasswordService.hpp rename to src/libs/lmscore/impl/auth/pam/PAMPasswordService.hpp index 2de5bc82..9c88c955 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.hpp +++ b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.hpp @@ -19,9 +19,7 @@ #pragma once -#include - -#include "PasswordServiceBase.hpp" +#include "auth/PasswordServiceBase.hpp" namespace Auth { diff --git a/src/libs/lmscore/impl/CoverService.cpp b/src/libs/lmscore/impl/cover/CoverService.cpp similarity index 100% rename from src/libs/lmscore/impl/CoverService.cpp rename to src/libs/lmscore/impl/cover/CoverService.cpp diff --git a/src/libs/lmscore/impl/CoverService.hpp b/src/libs/lmscore/impl/cover/CoverService.hpp similarity index 98% rename from src/libs/lmscore/impl/CoverService.hpp rename to src/libs/lmscore/impl/cover/CoverService.hpp index a740673e..f1a845ac 100644 --- a/src/libs/lmscore/impl/CoverService.hpp +++ b/src/libs/lmscore/impl/cover/CoverService.hpp @@ -29,7 +29,7 @@ #include #include -#include "lmscore/services/ICoverService.hpp" +#include "lmscore/cover/ICoverService.hpp" #include "image/IEncodedImage.hpp" #include "database/Types.hpp" diff --git a/src/libs/auth/include/auth/IAuthTokenService.hpp b/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp similarity index 100% rename from src/libs/auth/include/auth/IAuthTokenService.hpp rename to src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp diff --git a/src/libs/auth/include/auth/IEnvService.hpp b/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp similarity index 100% rename from src/libs/auth/include/auth/IEnvService.hpp rename to src/libs/lmscore/include/lmscore/auth/IEnvService.hpp diff --git a/src/libs/auth/include/auth/IPasswordService.hpp b/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp similarity index 98% rename from src/libs/auth/include/auth/IPasswordService.hpp rename to src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp index f197ae16..86edc868 100644 --- a/src/libs/auth/include/auth/IPasswordService.hpp +++ b/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp @@ -21,11 +21,11 @@ #include +#include #include #include -#include -#include "auth/Types.hpp" +#include "lmscore/auth/Types.hpp" #include "database/Types.hpp" namespace Database diff --git a/src/libs/auth/include/auth/Types.hpp b/src/libs/lmscore/include/lmscore/auth/Types.hpp similarity index 100% rename from src/libs/auth/include/auth/Types.hpp rename to src/libs/lmscore/include/lmscore/auth/Types.hpp diff --git a/src/libs/lmscore/include/lmscore/services/ICoverService.hpp b/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/services/ICoverService.hpp rename to src/libs/lmscore/include/lmscore/cover/ICoverService.hpp diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 888eadf6..866cccf9 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -26,9 +26,9 @@ #include -#include "auth/IPasswordService.hpp" -#include "auth/IEnvService.hpp" -#include "lmscore/services/ICoverService.hpp" +#include "lmscore/auth/IPasswordService.hpp" +#include "lmscore/auth/IEnvService.hpp" +#include "lmscore/cover/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 2d6230cf..7ae3a787 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -25,10 +25,10 @@ #include #include -#include "auth/IAuthTokenService.hpp" -#include "auth/IPasswordService.hpp" -#include "auth/IEnvService.hpp" -#include "lmscore/services/ICoverService.hpp" +#include "lmscore/auth/IAuthTokenService.hpp" +#include "lmscore/auth/IPasswordService.hpp" +#include "lmscore/auth/IEnvService.hpp" +#include "lmscore/cover/ICoverService.hpp" #include "database/Db.hpp" #include "database/Session.hpp" #include "scanner/IScanner.hpp" diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 29414b43..6f406cac 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -28,8 +28,8 @@ #include #include -#include "auth/IAuthTokenService.hpp" -#include "auth/IPasswordService.hpp" +#include "lmscore/auth/IAuthTokenService.hpp" +#include "lmscore/auth/IPasswordService.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 8eb688e2..c68012fc 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -28,9 +28,9 @@ #include #include -#include "auth/IEnvService.hpp" -#include "auth/IPasswordService.hpp" -#include "lmscore/services/ICoverService.hpp" +#include "lmscore/auth/IEnvService.hpp" +#include "lmscore/auth/IPasswordService.hpp" +#include "lmscore/cover/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 66105842..d2cb3a95 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -34,7 +34,7 @@ #include "common/UUIDValidator.hpp" #include "common/ValueStringModel.hpp" -#include "auth/IPasswordService.hpp" +#include "lmscore/auth/IPasswordService.hpp" #include "database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index b37645b3..3ca4d184 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -24,7 +24,7 @@ #include #include -#include "auth/IPasswordService.hpp" +#include "lmscore/auth/IPasswordService.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index 16374d80..b0f99755 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -27,7 +27,7 @@ #include -#include "auth/IPasswordService.hpp" +#include "lmscore/auth/IPasswordService.hpp" #include "database/User.hpp" #include "database/Session.hpp" #include "utils/IConfig.hpp" diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 50bc58e7..8f26e470 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -23,7 +23,7 @@ #include #include -#include "auth/IPasswordService.hpp" +#include "lmscore/auth/IPasswordService.hpp" #include "database/User.hpp" #include "database/Session.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/common/PasswordValidator.cpp b/src/lms/ui/common/PasswordValidator.cpp index 2df00c3f..3a559c71 100644 --- a/src/lms/ui/common/PasswordValidator.cpp +++ b/src/lms/ui/common/PasswordValidator.cpp @@ -21,7 +21,7 @@ #include -#include "auth/IPasswordService.hpp" +#include "lmscore/auth/IPasswordService.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/common/PasswordValidator.hpp b/src/lms/ui/common/PasswordValidator.hpp index d504dbc1..a3efe8e6 100644 --- a/src/lms/ui/common/PasswordValidator.hpp +++ b/src/lms/ui/common/PasswordValidator.hpp @@ -23,7 +23,7 @@ #include #include "database/Types.hpp" -#include "auth/Types.hpp" +#include "lmscore/auth/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index c13c5996..44d1189d 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/services/ICoverService.hpp" +#include "lmscore/cover/ICoverService.hpp" #include "database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index d2a5e784..8202497c 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -24,7 +24,7 @@ #include -#include "lmscore/services/ICoverService.hpp" +#include "lmscore/cover/ICoverService.hpp" #include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" From c41cc77e21656df27a2402386b4caf57bb503797 Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 16 Oct 2021 16:28:29 +0200 Subject: [PATCH 18/49] Migrated database stuff --- src/libs/CMakeLists.txt | 1 - src/libs/database/CMakeLists.txt | 40 ------------------- src/libs/lmscore/CMakeLists.txt | 19 +++++++++ .../lmscore/impl/auth/AuthServiceBase.cpp | 6 +-- .../lmscore/impl/auth/AuthServiceBase.hpp | 2 +- .../lmscore/impl/auth/AuthTokenService.cpp | 4 +- .../lmscore/impl/auth/PasswordServiceBase.cpp | 4 +- .../auth/internal/InternalPasswordService.cpp | 4 +- .../auth/internal/InternalPasswordService.hpp | 2 +- .../impl/auth/pam/PAMPasswordService.cpp | 1 - src/libs/lmscore/impl/cover/CoverService.cpp | 8 ++-- src/libs/lmscore/impl/cover/CoverService.hpp | 2 +- .../impl => lmscore/impl/database}/Artist.cpp | 12 +++--- .../impl/database}/Cluster.cpp | 12 +++--- .../impl => lmscore/impl/database}/Db.cpp | 6 +-- .../impl/database}/Release.cpp | 12 +++--- .../impl/database}/ScanSettings.cpp | 6 +-- .../impl/database}/Session.cpp | 29 ++++++-------- .../impl/database}/SqlQuery.cpp | 0 .../impl/database}/SqlQuery.hpp | 0 .../impl/database}/StringViewTraits.hpp | 0 .../impl => lmscore/impl/database}/Track.cpp | 16 ++++---- .../impl/database}/TrackArtistLink.cpp | 8 ++-- .../impl/database}/TrackBookmark.cpp | 8 ++-- .../impl/database}/TrackFeatures.cpp | 6 +-- .../impl/database}/TrackList.cpp | 14 +++---- .../impl => lmscore/impl/database}/Traits.hpp | 2 +- .../impl => lmscore/impl/database}/User.cpp | 12 +++--- .../impl => lmscore/impl/database}/Utils.cpp | 0 .../impl => lmscore/impl/database}/Utils.hpp | 0 .../lmscore/auth/IAuthTokenService.hpp | 2 +- .../include/lmscore/auth/IEnvService.hpp | 3 +- .../include/lmscore/auth/IPasswordService.hpp | 2 +- .../lmscore/include/lmscore/auth/Types.hpp | 2 +- .../include/lmscore/cover/ICoverService.hpp | 2 +- .../include/lmscore}/database/Artist.hpp | 2 +- .../include/lmscore}/database/Cluster.hpp | 2 +- .../include/lmscore}/database/Db.hpp | 0 .../include/lmscore}/database/Release.hpp | 2 +- .../lmscore}/database/ScanSettings.hpp | 2 +- .../include/lmscore}/database/Session.hpp | 0 .../include/lmscore}/database/Track.hpp | 2 +- .../lmscore}/database/TrackArtistLink.hpp | 2 +- .../lmscore}/database/TrackBookmark.hpp | 2 +- .../lmscore}/database/TrackFeatures.hpp | 2 +- .../include/lmscore}/database/TrackList.hpp | 3 +- .../include/lmscore}/database/Types.hpp | 0 .../include/lmscore}/database/User.hpp | 2 +- src/libs/lmscore/test/CMakeLists.txt | 3 ++ .../test => lmscore/test/database}/Artist.cpp | 0 .../test/database}/CMakeLists.txt | 2 +- .../test/database}/Cluster.cpp | 0 .../test => lmscore/test/database}/Common.hpp | 22 +++++----- .../test/database}/DatabaseTest.cpp | 0 .../test/database}/Release.cpp | 0 .../test => lmscore/test/database}/Track.cpp | 0 src/libs/recommendation/CMakeLists.txt | 2 +- src/libs/recommendation/impl/Engine.cpp | 6 +-- .../impl/clusters/ClustersEngine.cpp | 14 +++---- .../impl/features/FeaturesEngine.cpp | 16 ++++---- .../impl/features/FeaturesEngineCache.hpp | 2 +- .../include/recommendation/IEngine.hpp | 4 +- src/libs/scanner/CMakeLists.txt | 2 +- src/libs/scanner/impl/Scanner.cpp | 14 +++---- src/libs/scanner/impl/Scanner.hpp | 6 +-- .../scanner/include/scanner/ScannerStats.hpp | 2 +- src/libs/scrobbling/CMakeLists.txt | 2 +- src/libs/scrobbling/impl/Scrobbling.cpp | 14 +++---- .../impl/internal/InternalScrobbler.cpp | 10 ++--- .../listenbrainz/ListenBrainzScrobbler.cpp | 14 +++---- .../impl/listenbrainz/ListensSynchronizer.cpp | 14 +++---- .../impl/listenbrainz/ListensSynchronizer.hpp | 2 +- .../scrobbling/impl/listenbrainz/Utils.cpp | 6 +-- .../scrobbling/impl/listenbrainz/Utils.hpp | 2 +- .../include/scrobbling/IScrobbling.hpp | 2 +- .../scrobbling/include/scrobbling/Listen.hpp | 2 +- src/libs/subsonic/impl/ParameterParsing.hpp | 2 +- src/libs/subsonic/impl/RequestContext.hpp | 2 +- src/libs/subsonic/impl/Stream.cpp | 6 +-- src/libs/subsonic/impl/SubsonicId.hpp | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 18 ++++----- src/libs/subsonic/impl/SubsonicResource.hpp | 2 +- src/lms/main.cpp | 4 +- src/lms/ui/Auth.cpp | 4 +- src/lms/ui/Auth.hpp | 21 +++++----- src/lms/ui/LmsApplication.cpp | 12 +++--- src/lms/ui/LmsApplicationException.hpp | 2 +- src/lms/ui/LmsApplicationManager.hpp | 2 +- src/lms/ui/LmsTheme.hpp | 2 +- src/lms/ui/MediaPlayer.cpp | 14 +++---- src/lms/ui/MediaPlayer.hpp | 2 +- src/lms/ui/PlayQueue.cpp | 12 +++--- src/lms/ui/PlayQueue.hpp | 2 +- src/lms/ui/PlayQueueAction.hpp | 2 +- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/DatabaseSettingsView.cpp | 6 +-- src/lms/ui/admin/InitWizardView.cpp | 4 +- src/lms/ui/admin/ScannerController.cpp | 4 +- src/lms/ui/admin/UserView.cpp | 4 +- src/lms/ui/admin/UsersView.cpp | 4 +- src/lms/ui/common/LoginNameValidator.cpp | 2 +- src/lms/ui/common/PasswordValidator.hpp | 1 - src/lms/ui/explore/ArtistCollector.cpp | 6 +-- src/lms/ui/explore/ArtistCollector.hpp | 2 +- src/lms/ui/explore/ArtistListHelpers.cpp | 2 +- src/lms/ui/explore/ArtistListHelpers.hpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 14 +++---- src/lms/ui/explore/ArtistView.hpp | 2 +- src/lms/ui/explore/ArtistsView.cpp | 4 +- src/lms/ui/explore/ArtistsView.hpp | 2 +- src/lms/ui/explore/DatabaseCollectorBase.hpp | 2 +- src/lms/ui/explore/Explore.cpp | 8 ++-- src/lms/ui/explore/Explore.hpp | 2 +- src/lms/ui/explore/Filters.cpp | 4 +- src/lms/ui/explore/Filters.hpp | 2 +- src/lms/ui/explore/ReleaseCollector.cpp | 8 ++-- src/lms/ui/explore/ReleaseListHelpers.cpp | 4 +- src/lms/ui/explore/ReleaseListHelpers.hpp | 2 +- src/lms/ui/explore/ReleasePopup.cpp | 6 +-- src/lms/ui/explore/ReleasePopup.hpp | 2 +- src/lms/ui/explore/ReleaseView.cpp | 10 ++--- src/lms/ui/explore/ReleaseView.hpp | 2 +- src/lms/ui/explore/ReleasesView.cpp | 2 +- src/lms/ui/explore/ReleasesView.hpp | 2 +- src/lms/ui/explore/SearchView.cpp | 8 ++-- src/lms/ui/explore/TrackCollector.cpp | 8 ++-- src/lms/ui/explore/TrackListHelpers.cpp | 6 +-- src/lms/ui/explore/TrackListHelpers.hpp | 2 +- src/lms/ui/explore/TrackPopup.cpp | 6 +-- src/lms/ui/explore/TrackPopup.hpp | 2 +- src/lms/ui/explore/TracksView.cpp | 2 +- src/lms/ui/explore/TracksView.hpp | 2 +- src/lms/ui/resource/AudioFileResource.cpp | 4 +- src/lms/ui/resource/AudioFileResource.hpp | 2 +- .../ui/resource/AudioTranscodeResource.cpp | 6 +-- .../ui/resource/AudioTranscodeResource.hpp | 2 +- src/lms/ui/resource/CoverResource.cpp | 2 +- src/lms/ui/resource/CoverResource.hpp | 2 +- src/lms/ui/resource/DownloadResource.cpp | 8 ++-- src/lms/ui/resource/DownloadResource.hpp | 2 +- src/tools/cover/LmsCover.cpp | 8 ++-- src/tools/recommendation/CMakeLists.txt | 2 +- .../recommendation/LmsRecommendation.cpp | 12 +++--- 143 files changed, 358 insertions(+), 387 deletions(-) delete mode 100644 src/libs/database/CMakeLists.txt rename src/libs/{database/impl => lmscore/impl/database}/Artist.cpp (98%) rename src/libs/{database/impl => lmscore/impl/database}/Cluster.cpp (95%) rename src/libs/{database/impl => lmscore/impl/database}/Db.cpp (95%) rename src/libs/{database/impl => lmscore/impl/database}/Release.cpp (98%) rename src/libs/{database/impl => lmscore/impl/database}/ScanSettings.cpp (96%) rename src/libs/{database/impl => lmscore/impl/database}/Session.cpp (97%) rename src/libs/{database/impl => lmscore/impl/database}/SqlQuery.cpp (100%) rename src/libs/{database/impl => lmscore/impl/database}/SqlQuery.hpp (100%) rename src/libs/{database/impl => lmscore/impl/database}/StringViewTraits.hpp (100%) rename src/libs/{database/impl => lmscore/impl/database}/Track.cpp (98%) rename src/libs/{database/impl => lmscore/impl/database}/TrackArtistLink.cpp (90%) rename src/libs/{database/impl => lmscore/impl/database}/TrackBookmark.cpp (93%) rename src/libs/{database/impl => lmscore/impl/database}/TrackFeatures.cpp (94%) rename src/libs/{database/impl => lmscore/impl/database}/TrackList.cpp (98%) rename src/libs/{database/impl => lmscore/impl/database}/Traits.hpp (97%) rename src/libs/{database/impl => lmscore/impl/database}/User.cpp (95%) rename src/libs/{database/impl => lmscore/impl/database}/Utils.cpp (100%) rename src/libs/{database/impl => lmscore/impl/database}/Utils.hpp (100%) rename src/libs/{database/include => lmscore/include/lmscore}/database/Artist.hpp (99%) rename src/libs/{database/include => lmscore/include/lmscore}/database/Cluster.hpp (98%) rename src/libs/{database/include => lmscore/include/lmscore}/database/Db.hpp (100%) rename src/libs/{database/include => lmscore/include/lmscore}/database/Release.hpp (99%) rename src/libs/{database/include => lmscore/include/lmscore}/database/ScanSettings.hpp (98%) rename src/libs/{database/include => lmscore/include/lmscore}/database/Session.hpp (100%) rename src/libs/{database/include => lmscore/include/lmscore}/database/Track.hpp (99%) rename src/libs/{database/include => lmscore/include/lmscore}/database/TrackArtistLink.hpp (98%) rename src/libs/{database/include => lmscore/include/lmscore}/database/TrackBookmark.hpp (98%) rename src/libs/{database/include => lmscore/include/lmscore}/database/TrackFeatures.hpp (97%) rename src/libs/{database/include => lmscore/include/lmscore}/database/TrackList.hpp (99%) rename src/libs/{database/include => lmscore/include/lmscore}/database/Types.hpp (100%) rename src/libs/{database/include => lmscore/include/lmscore}/database/User.hpp (99%) create mode 100644 src/libs/lmscore/test/CMakeLists.txt rename src/libs/{database/test => lmscore/test/database}/Artist.cpp (100%) rename src/libs/{database/test => lmscore/test/database}/CMakeLists.txt (94%) rename src/libs/{database/test => lmscore/test/database}/Cluster.cpp (100%) rename src/libs/{database/test => lmscore/test/database}/Common.hpp (90%) rename src/libs/{database/test => lmscore/test/database}/DatabaseTest.cpp (100%) rename src/libs/{database/test => lmscore/test/database}/Release.cpp (100%) rename src/libs/{database/test => lmscore/test/database}/Track.cpp (100%) diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 196b2a1b..211f6226 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -1,6 +1,5 @@ add_subdirectory(av) -add_subdirectory(database) add_subdirectory(image) add_subdirectory(lmscore) add_subdirectory(metadata) diff --git a/src/libs/database/CMakeLists.txt b/src/libs/database/CMakeLists.txt deleted file mode 100644 index 38b13e75..00000000 --- a/src/libs/database/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -add_library(lmsdatabase SHARED - impl/Artist.cpp - impl/Cluster.cpp - impl/Db.cpp - impl/TrackArtistLink.cpp - impl/TrackFeatures.cpp - impl/TrackList.cpp - impl/Release.cpp - impl/ScanSettings.cpp - impl/Session.cpp - impl/SqlQuery.cpp - impl/Track.cpp - impl/TrackBookmark.cpp - impl/User.cpp - impl/Utils.cpp - ) - -target_include_directories(lmsdatabase INTERFACE - include - ) - -target_include_directories(lmsdatabase PRIVATE - include - ) - -target_link_libraries(lmsdatabase PRIVATE - Wt::DboSqlite3 - ) - -target_link_libraries(lmsdatabase PUBLIC - lmsutils - std::filesystem - Wt::Dbo - ) - -install(TARGETS lmsdatabase DESTINATION lib) - -if(BUILD_TESTING) - add_subdirectory(test) -endif() diff --git a/src/libs/lmscore/CMakeLists.txt b/src/libs/lmscore/CMakeLists.txt index 4403380a..40460f1b 100644 --- a/src/libs/lmscore/CMakeLists.txt +++ b/src/libs/lmscore/CMakeLists.txt @@ -7,6 +7,20 @@ add_library(lmscore SHARED impl/auth/PasswordServiceBase.cpp impl/auth/http-headers/HttpHeadersEnvService.cpp impl/auth/internal/InternalPasswordService.cpp + impl/database/Artist.cpp + impl/database/Cluster.cpp + impl/database/Db.cpp + impl/database/TrackArtistLink.cpp + impl/database/TrackFeatures.cpp + impl/database/TrackList.cpp + impl/database/Release.cpp + impl/database/ScanSettings.cpp + impl/database/Session.cpp + impl/database/SqlQuery.cpp + impl/database/Track.cpp + impl/database/TrackBookmark.cpp + impl/database/User.cpp + impl/database/Utils.cpp impl/cover/CoverService.cpp ) @@ -21,6 +35,7 @@ target_include_directories(lmscore PRIVATE target_link_libraries(lmscore PRIVATE lmsav + Wt::DboSqlite3 ) target_link_libraries(lmscore PUBLIC @@ -29,6 +44,7 @@ target_link_libraries(lmscore PUBLIC lmsutils std::filesystem Wt::Wt + Wt::Dbo ) if (USE_PAM) @@ -41,3 +57,6 @@ endif (USE_PAM) install(TARGETS lmscore DESTINATION lib) +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/libs/lmscore/impl/auth/AuthServiceBase.cpp b/src/libs/lmscore/impl/auth/AuthServiceBase.cpp index 6e0d0589..680226f7 100644 --- a/src/libs/lmscore/impl/auth/AuthServiceBase.cpp +++ b/src/libs/lmscore/impl/auth/AuthServiceBase.cpp @@ -19,9 +19,9 @@ #include "AuthServiceBase.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" namespace Auth diff --git a/src/libs/lmscore/impl/auth/AuthServiceBase.hpp b/src/libs/lmscore/impl/auth/AuthServiceBase.hpp index 81acba59..f002baf0 100644 --- a/src/libs/lmscore/impl/auth/AuthServiceBase.hpp +++ b/src/libs/lmscore/impl/auth/AuthServiceBase.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/impl/auth/AuthTokenService.cpp b/src/libs/lmscore/impl/auth/AuthTokenService.cpp index 05949870..32a9b03f 100644 --- a/src/libs/lmscore/impl/auth/AuthTokenService.cpp +++ b/src/libs/lmscore/impl/auth/AuthTokenService.cpp @@ -24,8 +24,8 @@ #include #include "lmscore/auth/Types.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp b/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp index e80d1b9b..23c0d41f 100644 --- a/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp +++ b/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp @@ -28,8 +28,8 @@ #endif // LMS_SUPPORT_PAM #include "lmscore/auth/Types.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp index 233fb9aa..ff26a585 100644 --- a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp +++ b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp @@ -22,8 +22,8 @@ #include "lmscore/auth/IAuthTokenService.hpp" #include "lmscore/auth/Types.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp index 42b830f1..9e3a1921 100644 --- a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp +++ b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/User.hpp" +#include "lmscore/database/User.hpp" #include "auth/PasswordServiceBase.hpp" #include "auth/LoginThrottler.hpp" diff --git a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp index 708ce457..9f612633 100644 --- a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp +++ b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp @@ -27,7 +27,6 @@ #include #include "lmscore/auth/Types.hpp" -#include "database/Session.hpp" #include "utils/Logger.hpp" namespace Auth diff --git a/src/libs/lmscore/impl/cover/CoverService.cpp b/src/libs/lmscore/impl/cover/CoverService.cpp index a968d5c4..f773d8e5 100644 --- a/src/libs/lmscore/impl/cover/CoverService.cpp +++ b/src/libs/lmscore/impl/cover/CoverService.cpp @@ -21,10 +21,10 @@ #include "av/IAudioFile.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "image/Exception.hpp" #include "image/IRawImage.hpp" diff --git a/src/libs/lmscore/impl/cover/CoverService.hpp b/src/libs/lmscore/impl/cover/CoverService.hpp index f1a845ac..30c93bed 100644 --- a/src/libs/lmscore/impl/cover/CoverService.hpp +++ b/src/libs/lmscore/impl/cover/CoverService.hpp @@ -31,7 +31,7 @@ #include "lmscore/cover/ICoverService.hpp" #include "image/IEncodedImage.hpp" -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/lmscore/impl/database/Artist.cpp similarity index 98% rename from src/libs/database/impl/Artist.cpp rename to src/libs/lmscore/impl/database/Artist.cpp index 9ca631fa..3e9e35b3 100644 --- a/src/libs/database/impl/Artist.cpp +++ b/src/libs/lmscore/impl/database/Artist.cpp @@ -16,15 +16,15 @@ * You should have received a copy of the GNU General Public License * along with LMS. If not, see . */ -#include "database/Artist.hpp" +#include "lmscore/database/Artist.hpp" #include -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Utils.hpp" diff --git a/src/libs/database/impl/Cluster.cpp b/src/libs/lmscore/impl/database/Cluster.cpp similarity index 95% rename from src/libs/database/impl/Cluster.cpp rename to src/libs/lmscore/impl/database/Cluster.cpp index 184a03a1..2fdf0cc7 100644 --- a/src/libs/database/impl/Cluster.cpp +++ b/src/libs/lmscore/impl/database/Cluster.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/Cluster.hpp" +#include "lmscore/database/Cluster.hpp" -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/ScanSettings.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "SqlQuery.hpp" #include "Traits.hpp" diff --git a/src/libs/database/impl/Db.cpp b/src/libs/lmscore/impl/database/Db.cpp similarity index 95% rename from src/libs/database/impl/Db.cpp rename to src/libs/lmscore/impl/database/Db.cpp index 5035466c..2d434ae7 100644 --- a/src/libs/database/impl/Db.cpp +++ b/src/libs/lmscore/impl/database/Db.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/Db.hpp" +#include "lmscore/database/Db.hpp" #include #include -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" namespace Database { diff --git a/src/libs/database/impl/Release.cpp b/src/libs/lmscore/impl/database/Release.cpp similarity index 98% rename from src/libs/database/impl/Release.cpp rename to src/libs/lmscore/impl/database/Release.cpp index 310de51e..3a9c91ef 100644 --- a/src/libs/database/impl/Release.cpp +++ b/src/libs/lmscore/impl/database/Release.cpp @@ -17,15 +17,15 @@ * along with LMS. If not, see . */ -#include "database/Release.hpp" +#include "lmscore/database/Release.hpp" #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Traits.hpp" diff --git a/src/libs/database/impl/ScanSettings.cpp b/src/libs/lmscore/impl/database/ScanSettings.cpp similarity index 96% rename from src/libs/database/impl/ScanSettings.cpp rename to src/libs/lmscore/impl/database/ScanSettings.cpp index 9aafed53..fefa2d8c 100644 --- a/src/libs/database/impl/ScanSettings.cpp +++ b/src/libs/lmscore/impl/database/ScanSettings.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "database/ScanSettings.hpp" +#include "lmscore/database/ScanSettings.hpp" #include @@ -25,8 +25,8 @@ #include "utils/Logger.hpp" #include "utils/String.hpp" -#include "database/Cluster.hpp" -#include "database/Session.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Session.hpp" namespace { diff --git a/src/libs/database/impl/Session.cpp b/src/libs/lmscore/impl/database/Session.cpp similarity index 97% rename from src/libs/database/impl/Session.cpp rename to src/libs/lmscore/impl/database/Session.cpp index 56ec77d1..9d0280ed 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/lmscore/impl/database/Session.cpp @@ -17,27 +17,22 @@ * along with LMS. If not, see . */ -#include "database/Session.hpp" - -#include -#include -#include -#include +#include "lmscore/database/Session.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Track.hpp" -#include "database/TrackBookmark.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackList.hpp" -#include "database/TrackFeatures.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/ScanSettings.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackBookmark.hpp" +#include "lmscore/database/TrackArtistLink.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/TrackFeatures.hpp" +#include "lmscore/database/User.hpp" namespace Database { diff --git a/src/libs/database/impl/SqlQuery.cpp b/src/libs/lmscore/impl/database/SqlQuery.cpp similarity index 100% rename from src/libs/database/impl/SqlQuery.cpp rename to src/libs/lmscore/impl/database/SqlQuery.cpp diff --git a/src/libs/database/impl/SqlQuery.hpp b/src/libs/lmscore/impl/database/SqlQuery.hpp similarity index 100% rename from src/libs/database/impl/SqlQuery.hpp rename to src/libs/lmscore/impl/database/SqlQuery.hpp diff --git a/src/libs/database/impl/StringViewTraits.hpp b/src/libs/lmscore/impl/database/StringViewTraits.hpp similarity index 100% rename from src/libs/database/impl/StringViewTraits.hpp rename to src/libs/lmscore/impl/database/StringViewTraits.hpp diff --git a/src/libs/database/impl/Track.cpp b/src/libs/lmscore/impl/database/Track.cpp similarity index 98% rename from src/libs/database/impl/Track.cpp rename to src/libs/lmscore/impl/database/Track.cpp index 82dc99cf..3e72e71a 100644 --- a/src/libs/database/impl/Track.cpp +++ b/src/libs/lmscore/impl/database/Track.cpp @@ -17,17 +17,17 @@ * along with LMS. If not, see . */ -#include "database/Track.hpp" +#include "lmscore/database/Track.hpp" #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackFeatures.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/TrackArtistLink.hpp" +#include "lmscore/database/TrackFeatures.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" diff --git a/src/libs/database/impl/TrackArtistLink.cpp b/src/libs/lmscore/impl/database/TrackArtistLink.cpp similarity index 90% rename from src/libs/database/impl/TrackArtistLink.cpp rename to src/libs/lmscore/impl/database/TrackArtistLink.cpp index f8791dee..059338c5 100644 --- a/src/libs/database/impl/TrackArtistLink.cpp +++ b/src/libs/lmscore/impl/database/TrackArtistLink.cpp @@ -17,11 +17,11 @@ * along with LMS. If not, see . */ -#include "database/TrackArtistLink.hpp" +#include "lmscore/database/TrackArtistLink.hpp" -#include "database/Artist.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "Traits.hpp" diff --git a/src/libs/database/impl/TrackBookmark.cpp b/src/libs/lmscore/impl/database/TrackBookmark.cpp similarity index 93% rename from src/libs/database/impl/TrackBookmark.cpp rename to src/libs/lmscore/impl/database/TrackBookmark.cpp index 91f457bd..adf3e23a 100644 --- a/src/libs/database/impl/TrackBookmark.cpp +++ b/src/libs/lmscore/impl/database/TrackBookmark.cpp @@ -17,11 +17,11 @@ * along with LMS. If not, see . */ -#include "database/TrackBookmark.hpp" +#include "lmscore/database/TrackBookmark.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/User.hpp" #include "Traits.hpp" namespace Database { diff --git a/src/libs/database/impl/TrackFeatures.cpp b/src/libs/lmscore/impl/database/TrackFeatures.cpp similarity index 94% rename from src/libs/database/impl/TrackFeatures.cpp rename to src/libs/lmscore/impl/database/TrackFeatures.cpp index 5770d0b1..dde9cd72 100644 --- a/src/libs/database/impl/TrackFeatures.cpp +++ b/src/libs/lmscore/impl/database/TrackFeatures.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/TrackFeatures.hpp" +#include "lmscore/database/TrackFeatures.hpp" #include #include -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "utils/Logger.hpp" namespace Database { diff --git a/src/libs/database/impl/TrackList.cpp b/src/libs/lmscore/impl/database/TrackList.cpp similarity index 98% rename from src/libs/database/impl/TrackList.cpp rename to src/libs/lmscore/impl/database/TrackList.cpp index a9863d52..c8bff04b 100644 --- a/src/libs/database/impl/TrackList.cpp +++ b/src/libs/lmscore/impl/database/TrackList.cpp @@ -16,18 +16,18 @@ * You should have received a copy of the GNU General Public License * along with LMS. If not, see . */ -#include "database/TrackList.hpp" +#include "lmscore/database/TrackList.hpp" #include #include "utils/Logger.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" +#include "lmscore/database/Track.hpp" #include "SqlQuery.hpp" #include "StringViewTraits.hpp" #include "Traits.hpp" diff --git a/src/libs/database/impl/Traits.hpp b/src/libs/lmscore/impl/database/Traits.hpp similarity index 97% rename from src/libs/database/impl/Traits.hpp rename to src/libs/lmscore/impl/database/Traits.hpp index bdfa0045..9365085c 100644 --- a/src/libs/database/impl/Traits.hpp +++ b/src/libs/lmscore/impl/database/Traits.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Wt::Dbo { diff --git a/src/libs/database/impl/User.cpp b/src/libs/lmscore/impl/database/User.cpp similarity index 95% rename from src/libs/database/impl/User.cpp rename to src/libs/lmscore/impl/database/User.cpp index 44f8c2b1..3f8ca7e0 100644 --- a/src/libs/database/impl/User.cpp +++ b/src/libs/lmscore/impl/database/User.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/User.hpp" +#include "lmscore/database/User.hpp" -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" #include "utils/Logger.hpp" #include "StringViewTraits.hpp" #include "Traits.hpp" diff --git a/src/libs/database/impl/Utils.cpp b/src/libs/lmscore/impl/database/Utils.cpp similarity index 100% rename from src/libs/database/impl/Utils.cpp rename to src/libs/lmscore/impl/database/Utils.cpp diff --git a/src/libs/database/impl/Utils.hpp b/src/libs/lmscore/impl/database/Utils.hpp similarity index 100% rename from src/libs/database/impl/Utils.hpp rename to src/libs/lmscore/impl/database/Utils.hpp diff --git a/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp b/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp index 5d9dd51b..0fa5f2cb 100644 --- a/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp +++ b/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp @@ -27,7 +27,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp b/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp index ecb23349..9a845fcc 100644 --- a/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp +++ b/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp @@ -20,9 +20,8 @@ #pragma once #include -#include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp b/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp index 86edc868..3509d6f1 100644 --- a/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp +++ b/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp @@ -26,7 +26,7 @@ #include #include "lmscore/auth/Types.hpp" -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/auth/Types.hpp b/src/libs/lmscore/include/lmscore/auth/Types.hpp index 8b97df6e..519c9981 100644 --- a/src/libs/lmscore/include/lmscore/auth/Types.hpp +++ b/src/libs/lmscore/include/lmscore/auth/Types.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/Exception.hpp" namespace Auth diff --git a/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp b/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp index 039260ef..925e18ed 100644 --- a/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp +++ b/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "image/IEncodedImage.hpp" namespace Database diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/lmscore/include/lmscore/database/Artist.hpp similarity index 99% rename from src/libs/database/include/database/Artist.hpp rename to src/libs/lmscore/include/lmscore/database/Artist.hpp index 23ef7812..a3458a3e 100644 --- a/src/libs/database/include/database/Artist.hpp +++ b/src/libs/lmscore/include/lmscore/database/Artist.hpp @@ -27,7 +27,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" diff --git a/src/libs/database/include/database/Cluster.hpp b/src/libs/lmscore/include/lmscore/database/Cluster.hpp similarity index 98% rename from src/libs/database/include/database/Cluster.hpp rename to src/libs/lmscore/include/lmscore/database/Cluster.hpp index 42a11e77..290d1a30 100644 --- a/src/libs/database/include/database/Cluster.hpp +++ b/src/libs/lmscore/include/lmscore/database/Cluster.hpp @@ -26,7 +26,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/database/include/database/Db.hpp b/src/libs/lmscore/include/lmscore/database/Db.hpp similarity index 100% rename from src/libs/database/include/database/Db.hpp rename to src/libs/lmscore/include/lmscore/database/Db.hpp diff --git a/src/libs/database/include/database/Release.hpp b/src/libs/lmscore/include/lmscore/database/Release.hpp similarity index 99% rename from src/libs/database/include/database/Release.hpp rename to src/libs/lmscore/include/lmscore/database/Release.hpp index ce45a1a8..d75d9921 100644 --- a/src/libs/database/include/database/Release.hpp +++ b/src/libs/lmscore/include/lmscore/database/Release.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/UUID.hpp" namespace Database diff --git a/src/libs/database/include/database/ScanSettings.hpp b/src/libs/lmscore/include/lmscore/database/ScanSettings.hpp similarity index 98% rename from src/libs/database/include/database/ScanSettings.hpp rename to src/libs/lmscore/include/lmscore/database/ScanSettings.hpp index 9cb8cb5a..4d8673cb 100644 --- a/src/libs/database/include/database/ScanSettings.hpp +++ b/src/libs/lmscore/include/lmscore/database/ScanSettings.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/database/include/database/Session.hpp b/src/libs/lmscore/include/lmscore/database/Session.hpp similarity index 100% rename from src/libs/database/include/database/Session.hpp rename to src/libs/lmscore/include/lmscore/database/Session.hpp diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/lmscore/include/lmscore/database/Track.hpp similarity index 99% rename from src/libs/database/include/database/Track.hpp rename to src/libs/lmscore/include/lmscore/database/Track.hpp index 3e2c2ced..dc71a21f 100644 --- a/src/libs/database/include/database/Track.hpp +++ b/src/libs/lmscore/include/lmscore/database/Track.hpp @@ -34,7 +34,7 @@ #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/database/include/database/TrackArtistLink.hpp b/src/libs/lmscore/include/lmscore/database/TrackArtistLink.hpp similarity index 98% rename from src/libs/database/include/database/TrackArtistLink.hpp rename to src/libs/lmscore/include/lmscore/database/TrackArtistLink.hpp index 55d3b0c5..e8a140e5 100644 --- a/src/libs/database/include/database/TrackArtistLink.hpp +++ b/src/libs/lmscore/include/lmscore/database/TrackArtistLink.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/EnumSet.hpp" namespace Database diff --git a/src/libs/database/include/database/TrackBookmark.hpp b/src/libs/lmscore/include/lmscore/database/TrackBookmark.hpp similarity index 98% rename from src/libs/database/include/database/TrackBookmark.hpp rename to src/libs/lmscore/include/lmscore/database/TrackBookmark.hpp index 2c4af0db..089bac05 100644 --- a/src/libs/database/include/database/TrackBookmark.hpp +++ b/src/libs/lmscore/include/lmscore/database/TrackBookmark.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/database/include/database/TrackFeatures.hpp b/src/libs/lmscore/include/lmscore/database/TrackFeatures.hpp similarity index 97% rename from src/libs/database/include/database/TrackFeatures.hpp rename to src/libs/lmscore/include/lmscore/database/TrackFeatures.hpp index 99c36d6d..527cde69 100644 --- a/src/libs/database/include/database/TrackFeatures.hpp +++ b/src/libs/lmscore/include/lmscore/database/TrackFeatures.hpp @@ -26,7 +26,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/database/include/database/TrackList.hpp b/src/libs/lmscore/include/lmscore/database/TrackList.hpp similarity index 99% rename from src/libs/database/include/database/TrackList.hpp rename to src/libs/lmscore/include/lmscore/database/TrackList.hpp index 03aaca0f..4449e057 100644 --- a/src/libs/database/include/database/TrackList.hpp +++ b/src/libs/lmscore/include/lmscore/database/TrackList.hpp @@ -21,13 +21,12 @@ #include #include -#include #include #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/database/include/database/Types.hpp b/src/libs/lmscore/include/lmscore/database/Types.hpp similarity index 100% rename from src/libs/database/include/database/Types.hpp rename to src/libs/lmscore/include/lmscore/database/Types.hpp diff --git a/src/libs/database/include/database/User.hpp b/src/libs/lmscore/include/lmscore/database/User.hpp similarity index 99% rename from src/libs/database/include/database/User.hpp rename to src/libs/lmscore/include/lmscore/database/User.hpp index b94d41af..8422132a 100644 --- a/src/libs/database/include/database/User.hpp +++ b/src/libs/lmscore/include/lmscore/database/User.hpp @@ -26,7 +26,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/UUID.hpp" namespace Database { diff --git a/src/libs/lmscore/test/CMakeLists.txt b/src/libs/lmscore/test/CMakeLists.txt new file mode 100644 index 00000000..d74e1951 --- /dev/null +++ b/src/libs/lmscore/test/CMakeLists.txt @@ -0,0 +1,3 @@ + +add_subdirectory(database) + diff --git a/src/libs/database/test/Artist.cpp b/src/libs/lmscore/test/database/Artist.cpp similarity index 100% rename from src/libs/database/test/Artist.cpp rename to src/libs/lmscore/test/database/Artist.cpp diff --git a/src/libs/database/test/CMakeLists.txt b/src/libs/lmscore/test/database/CMakeLists.txt similarity index 94% rename from src/libs/database/test/CMakeLists.txt rename to src/libs/lmscore/test/database/CMakeLists.txt index 605c3af5..0a107ff2 100644 --- a/src/libs/database/test/CMakeLists.txt +++ b/src/libs/lmscore/test/database/CMakeLists.txt @@ -8,7 +8,7 @@ add_executable(test-database ) target_link_libraries(test-database PRIVATE - lmsdatabase + lmscore GTest::GTest ) diff --git a/src/libs/database/test/Cluster.cpp b/src/libs/lmscore/test/database/Cluster.cpp similarity index 100% rename from src/libs/database/test/Cluster.cpp rename to src/libs/lmscore/test/database/Cluster.cpp diff --git a/src/libs/database/test/Common.hpp b/src/libs/lmscore/test/database/Common.hpp similarity index 90% rename from src/libs/database/test/Common.hpp rename to src/libs/lmscore/test/database/Common.hpp index 8864e681..3d6ac552 100644 --- a/src/libs/database/test/Common.hpp +++ b/src/libs/lmscore/test/database/Common.hpp @@ -24,17 +24,17 @@ #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackBookmark.hpp" -#include "database/TrackList.hpp" -#include "database/Types.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackArtistLink.hpp" +#include "lmscore/database/TrackBookmark.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/Types.hpp" +#include "lmscore/database/User.hpp" template class ScopedEntity diff --git a/src/libs/database/test/DatabaseTest.cpp b/src/libs/lmscore/test/database/DatabaseTest.cpp similarity index 100% rename from src/libs/database/test/DatabaseTest.cpp rename to src/libs/lmscore/test/database/DatabaseTest.cpp diff --git a/src/libs/database/test/Release.cpp b/src/libs/lmscore/test/database/Release.cpp similarity index 100% rename from src/libs/database/test/Release.cpp rename to src/libs/lmscore/test/database/Release.cpp diff --git a/src/libs/database/test/Track.cpp b/src/libs/lmscore/test/database/Track.cpp similarity index 100% rename from src/libs/database/test/Track.cpp rename to src/libs/lmscore/test/database/Track.cpp diff --git a/src/libs/recommendation/CMakeLists.txt b/src/libs/recommendation/CMakeLists.txt index 4682173d..3130d76b 100644 --- a/src/libs/recommendation/CMakeLists.txt +++ b/src/libs/recommendation/CMakeLists.txt @@ -17,7 +17,7 @@ target_include_directories(lmsrecommendation PRIVATE ) target_link_libraries(lmsrecommendation PRIVATE - lmsdatabase + lmscore lmssom std::filesystem ) diff --git a/src/libs/recommendation/impl/Engine.cpp b/src/libs/recommendation/impl/Engine.cpp index b6808ceb..f7d2bf05 100644 --- a/src/libs/recommendation/impl/Engine.cpp +++ b/src/libs/recommendation/impl/Engine.cpp @@ -25,9 +25,9 @@ #include "ClustersEngineCreator.hpp" #include "FeaturesEngineCreator.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" -#include "database/ScanSettings.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/ScanSettings.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/recommendation/impl/clusters/ClustersEngine.cpp index b0703a0f..13be84a3 100644 --- a/src/libs/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/recommendation/impl/clusters/ClustersEngine.cpp @@ -19,13 +19,13 @@ #include "ClustersEngine.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" namespace Recommendation { diff --git a/src/libs/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/recommendation/impl/features/FeaturesEngine.cpp index 7b1f39b6..560e0126 100644 --- a/src/libs/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/recommendation/impl/features/FeaturesEngine.cpp @@ -21,14 +21,14 @@ #include -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackFeatures.hpp" -#include "database/TrackList.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackArtistLink.hpp" +#include "lmscore/database/TrackFeatures.hpp" +#include "lmscore/database/TrackList.hpp" #include "som/DataNormalizer.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" diff --git a/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp b/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp index 08372345..5dd0032a 100644 --- a/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp +++ b/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp @@ -23,7 +23,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "som/Network.hpp" namespace Recommendation { diff --git a/src/libs/recommendation/include/recommendation/IEngine.hpp b/src/libs/recommendation/include/recommendation/IEngine.hpp index 26f180bc..d0a4218c 100644 --- a/src/libs/recommendation/include/recommendation/IEngine.hpp +++ b/src/libs/recommendation/include/recommendation/IEngine.hpp @@ -21,8 +21,8 @@ #include #include -#include -#include "database/Types.hpp" + +#include "lmscore/database/Types.hpp" #include "utils/EnumSet.hpp" namespace Database diff --git a/src/libs/scanner/CMakeLists.txt b/src/libs/scanner/CMakeLists.txt index 5f1467b8..6dcbfebc 100644 --- a/src/libs/scanner/CMakeLists.txt +++ b/src/libs/scanner/CMakeLists.txt @@ -14,7 +14,7 @@ target_include_directories(lmsscanner PRIVATE ) target_link_libraries(lmsscanner PRIVATE - lmsdatabase + lmscore lmsmetadata lmsrecommendation lmsutils diff --git a/src/libs/scanner/impl/Scanner.cpp b/src/libs/scanner/impl/Scanner.cpp index 125324dc..e8563f00 100644 --- a/src/libs/scanner/impl/Scanner.cpp +++ b/src/libs/scanner/impl/Scanner.cpp @@ -24,13 +24,13 @@ #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Track.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackFeatures.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/ScanSettings.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackArtistLink.hpp" +#include "lmscore/database/TrackFeatures.hpp" #include "metadata/TagLibParser.hpp" #include "recommendation/IEngine.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/scanner/impl/Scanner.hpp b/src/libs/scanner/impl/Scanner.hpp index 76aeb819..7469cb11 100644 --- a/src/libs/scanner/impl/Scanner.hpp +++ b/src/libs/scanner/impl/Scanner.hpp @@ -30,9 +30,9 @@ #include -#include "database/Types.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" +#include "lmscore/database/Types.hpp" +#include "lmscore/database/ScanSettings.hpp" +#include "lmscore/database/Session.hpp" #include "metadata/IParser.hpp" #include "scanner/IScanner.hpp" #include "utils/Path.hpp" diff --git a/src/libs/scanner/include/scanner/ScannerStats.hpp b/src/libs/scanner/include/scanner/ScannerStats.hpp index 5528ce8b..440cf63c 100644 --- a/src/libs/scanner/include/scanner/ScannerStats.hpp +++ b/src/libs/scanner/include/scanner/ScannerStats.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Scanner { diff --git a/src/libs/scrobbling/CMakeLists.txt b/src/libs/scrobbling/CMakeLists.txt index d69a6f5a..6b26570c 100644 --- a/src/libs/scrobbling/CMakeLists.txt +++ b/src/libs/scrobbling/CMakeLists.txt @@ -17,7 +17,7 @@ target_include_directories(lmsscrobbling PRIVATE ) target_link_libraries(lmsscrobbling PRIVATE - lmsutils + lmscore ) target_link_libraries(lmsscrobbling PUBLIC diff --git a/src/libs/scrobbling/impl/Scrobbling.cpp b/src/libs/scrobbling/impl/Scrobbling.cpp index b1a91936..39f3b67d 100644 --- a/src/libs/scrobbling/impl/Scrobbling.cpp +++ b/src/libs/scrobbling/impl/Scrobbling.cpp @@ -19,13 +19,13 @@ #include "Scrobbling.hpp" -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" #include "internal/InternalScrobbler.hpp" #include "listenbrainz/ListenBrainzScrobbler.hpp" diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp index e5b23d51..eb71a438 100644 --- a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp @@ -19,11 +19,11 @@ #include "InternalScrobbler.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" namespace Scrobbling diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index 08202d9e..12660e0a 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -24,13 +24,13 @@ #include #include -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 8196da8b..c4522501 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -25,13 +25,13 @@ #include #include -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" #include "scrobbling/Exception.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index e71a8ae4..f1043c69 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -26,7 +26,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "scrobbling/Listen.hpp" namespace Database diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/scrobbling/impl/listenbrainz/Utils.cpp index ab293954..0cda3eb1 100644 --- a/src/libs/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/Utils.cpp @@ -21,9 +21,9 @@ #include -#include "database/Session.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" static constexpr std::string_view historyTracklistName {"__scrobbler_listenbrainz_history__"}; diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/scrobbling/impl/listenbrainz/Utils.hpp index 6b2d1271..206fa527 100644 --- a/src/libs/scrobbling/impl/listenbrainz/Utils.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/Utils.hpp @@ -22,7 +22,7 @@ #include #include "utils/UUID.hpp" -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp index cda27ca1..4937494d 100644 --- a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp +++ b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp @@ -29,7 +29,7 @@ #include #include "scrobbling/Listen.hpp" -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/libs/scrobbling/include/scrobbling/Listen.hpp b/src/libs/scrobbling/include/scrobbling/Listen.hpp index ae80dbac..e0d71cba 100644 --- a/src/libs/scrobbling/include/scrobbling/Listen.hpp +++ b/src/libs/scrobbling/include/scrobbling/Listen.hpp @@ -21,7 +21,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Scrobbling { diff --git a/src/libs/subsonic/impl/ParameterParsing.hpp b/src/libs/subsonic/impl/ParameterParsing.hpp index bb8a91d3..bd2f536c 100644 --- a/src/libs/subsonic/impl/ParameterParsing.hpp +++ b/src/libs/subsonic/impl/ParameterParsing.hpp @@ -20,7 +20,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/String.hpp" #include "SubsonicResponse.hpp" diff --git a/src/libs/subsonic/impl/RequestContext.hpp b/src/libs/subsonic/impl/RequestContext.hpp index 9d73bcb4..d15a29ca 100644 --- a/src/libs/subsonic/impl/RequestContext.hpp +++ b/src/libs/subsonic/impl/RequestContext.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "ClientInfo.hpp" #include "ProtocolVersion.hpp" diff --git a/src/libs/subsonic/impl/Stream.cpp b/src/libs/subsonic/impl/Stream.cpp index d3d181ed..610cd24c 100644 --- a/src/libs/subsonic/impl/Stream.cpp +++ b/src/libs/subsonic/impl/Stream.cpp @@ -22,9 +22,9 @@ #include "av/TranscodeParameters.hpp" #include "av/TranscodeResourceHandlerCreator.hpp" #include "av/Types.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/User.hpp" #include "utils/IResourceHandler.hpp" #include "utils/Logger.hpp" #include "utils/FileResourceHandlerCreator.hpp" diff --git a/src/libs/subsonic/impl/SubsonicId.hpp b/src/libs/subsonic/impl/SubsonicId.hpp index 0ac2a16a..2ff87309 100644 --- a/src/libs/subsonic/impl/SubsonicId.hpp +++ b/src/libs/subsonic/impl/SubsonicId.hpp @@ -19,7 +19,7 @@ #pragma once -#include "database/Types.hpp" +#include "lmscore/database/Types.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 866cccf9..d15aeba3 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -29,15 +29,15 @@ #include "lmscore/auth/IPasswordService.hpp" #include "lmscore/auth/IEnvService.hpp" #include "lmscore/cover/ICoverService.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackBookmark.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackBookmark.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" #include "recommendation/IEngine.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/IConfig.hpp" diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index 2d549f41..a5c2c344 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "ClientInfo.hpp" #include "RequestContext.hpp" diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 7ae3a787..8ebbd485 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -29,8 +29,8 @@ #include "lmscore/auth/IPasswordService.hpp" #include "lmscore/auth/IEnvService.hpp" #include "lmscore/cover/ICoverService.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Session.hpp" #include "scanner/IScanner.hpp" #include "recommendation/IEngine.hpp" #include "subsonic/SubsonicResource.hpp" diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 6f406cac..8158d63f 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -30,8 +30,8 @@ #include "lmscore/auth/IAuthTokenService.hpp" #include "lmscore/auth/IPasswordService.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/Auth.hpp b/src/lms/ui/Auth.hpp index 5fda238b..dc893223 100644 --- a/src/lms/ui/Auth.hpp +++ b/src/lms/ui/Auth.hpp @@ -21,19 +21,18 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" -namespace UserInterface { - -std::optional -processAuthToken(const Wt::WEnvironment& env); - -class Auth : public Wt::WTemplateFormView +namespace UserInterface { - public: - Auth(); + std::optional + processAuthToken(const Wt::WEnvironment& env); - Wt::Signal userLoggedIn; -}; + class Auth : public Wt::WTemplateFormView + { + public: + Auth(); + Wt::Signal userLoggedIn; + }; } // namespace UserInterface diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index c68012fc..7f6b059f 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -31,12 +31,12 @@ #include "lmscore/auth/IEnvService.hpp" #include "lmscore/auth/IPasswordService.hpp" #include "lmscore/cover/ICoverService.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/LmsApplicationException.hpp b/src/lms/ui/LmsApplicationException.hpp index 0671a6c4..2bb07492 100644 --- a/src/lms/ui/LmsApplicationException.hpp +++ b/src/lms/ui/LmsApplicationException.hpp @@ -19,7 +19,7 @@ #pragma once -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/Exception.hpp" namespace UserInterface { diff --git a/src/lms/ui/LmsApplicationManager.hpp b/src/lms/ui/LmsApplicationManager.hpp index 1d7761f7..a8f61d1e 100644 --- a/src/lms/ui/LmsApplicationManager.hpp +++ b/src/lms/ui/LmsApplicationManager.hpp @@ -25,7 +25,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/LmsTheme.hpp b/src/lms/ui/LmsTheme.hpp index 10ee14c2..3e354b1e 100644 --- a/src/lms/ui/LmsTheme.hpp +++ b/src/lms/ui/LmsTheme.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/User.hpp" +#include "lmscore/database/User.hpp" namespace UserInterface { diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index 4ee65e06..96cb31f2 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -25,13 +25,13 @@ #include "utils/Logger.hpp" -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/Types.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/Types.hpp" +#include "lmscore/database/User.hpp" #include "resource/CoverResource.hpp" #include "resource/AudioTranscodeResource.hpp" diff --git a/src/lms/ui/MediaPlayer.hpp b/src/lms/ui/MediaPlayer.hpp index d4f1700b..7cb028c9 100644 --- a/src/lms/ui/MediaPlayer.hpp +++ b/src/lms/ui/MediaPlayer.hpp @@ -27,7 +27,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 554b34d2..49c7ab53 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -22,12 +22,12 @@ #include #include -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" #include "recommendation/IEngine.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" diff --git a/src/lms/ui/PlayQueue.hpp b/src/lms/ui/PlayQueue.hpp index 1bbb996a..a8e14046 100644 --- a/src/lms/ui/PlayQueue.hpp +++ b/src/lms/ui/PlayQueue.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Similarity diff --git a/src/lms/ui/PlayQueueAction.hpp b/src/lms/ui/PlayQueueAction.hpp index fffd8133..aa52bddc 100644 --- a/src/lms/ui/PlayQueueAction.hpp +++ b/src/lms/ui/PlayQueueAction.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index d2cb3a95..9aecceda 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -35,7 +35,7 @@ #include "common/ValueStringModel.hpp" #include "lmscore/auth/IPasswordService.hpp" -#include "database/Session.hpp" +#include "lmscore/database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/DatabaseSettingsView.cpp b/src/lms/ui/admin/DatabaseSettingsView.cpp index d410b4cd..cef99be5 100644 --- a/src/lms/ui/admin/DatabaseSettingsView.cpp +++ b/src/lms/ui/admin/DatabaseSettingsView.cpp @@ -26,9 +26,9 @@ #include #include -#include "database/Cluster.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/ScanSettings.hpp" +#include "lmscore/database/Session.hpp" #include "scanner/IScanner.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index 3ca4d184..b3ba29da 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -25,8 +25,8 @@ #include #include "lmscore/auth/IPasswordService.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index f01cfc71..bd12f280 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -28,8 +28,8 @@ #include #include -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "scanner/IScanner.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index b0f99755..874442cd 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -28,8 +28,8 @@ #include #include "lmscore/auth/IPasswordService.hpp" -#include "database/User.hpp" -#include "database/Session.hpp" +#include "lmscore/database/User.hpp" +#include "lmscore/database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 8f26e470..358021bf 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -24,8 +24,8 @@ #include #include "lmscore/auth/IPasswordService.hpp" -#include "database/User.hpp" -#include "database/Session.hpp" +#include "lmscore/database/User.hpp" +#include "lmscore/database/Session.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/common/LoginNameValidator.cpp b/src/lms/ui/common/LoginNameValidator.cpp index d1574d3f..a7767364 100644 --- a/src/lms/ui/common/LoginNameValidator.cpp +++ b/src/lms/ui/common/LoginNameValidator.cpp @@ -20,7 +20,7 @@ #include "LoginNameValidator.hpp" #include -#include "database/User.hpp" +#include "lmscore/database/User.hpp" namespace UserInterface { diff --git a/src/lms/ui/common/PasswordValidator.hpp b/src/lms/ui/common/PasswordValidator.hpp index a3efe8e6..337341bb 100644 --- a/src/lms/ui/common/PasswordValidator.hpp +++ b/src/lms/ui/common/PasswordValidator.hpp @@ -22,7 +22,6 @@ #include #include -#include "database/Types.hpp" #include "lmscore/auth/Types.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 0c73aa3e..4a698e36 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -19,9 +19,9 @@ #include "ArtistCollector.hpp" -#include "database/Artist.hpp" -#include "database/User.hpp" -#include "database/TrackList.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/User.hpp" +#include "lmscore/database/TrackList.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ArtistCollector.hpp b/src/lms/ui/explore/ArtistCollector.hpp index 05734af0..98c6008c 100644 --- a/src/lms/ui/explore/ArtistCollector.hpp +++ b/src/lms/ui/explore/ArtistCollector.hpp @@ -24,7 +24,7 @@ #include "DatabaseCollectorBase.hpp" -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ArtistListHelpers.cpp b/src/lms/ui/explore/ArtistListHelpers.cpp index 85adb362..2187c585 100644 --- a/src/lms/ui/explore/ArtistListHelpers.cpp +++ b/src/lms/ui/explore/ArtistListHelpers.cpp @@ -20,7 +20,7 @@ #include -#include "database/Artist.hpp" +#include "lmscore/database/Artist.hpp" #include "LmsApplication.hpp" namespace UserInterface::ArtistListHelpers diff --git a/src/lms/ui/explore/ArtistListHelpers.hpp b/src/lms/ui/explore/ArtistListHelpers.hpp index 02659bf7..3f20d107 100644 --- a/src/lms/ui/explore/ArtistListHelpers.hpp +++ b/src/lms/ui/explore/ArtistListHelpers.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index eb6f1d41..0ea8c110 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -25,13 +25,13 @@ #include #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/ScanSettings.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/User.hpp" #include "recommendation/IEngine.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index 18b3894c..bc0b1cac 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index 3d71b94c..de06b920 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -21,8 +21,8 @@ #include -#include "database/Session.hpp" -#include "database/TrackArtistLink.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/TrackArtistLink.hpp" #include "utils/EnumSet.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/explore/ArtistsView.hpp b/src/lms/ui/explore/ArtistsView.hpp index f7e61e22..44ffaea0 100644 --- a/src/lms/ui/explore/ArtistsView.hpp +++ b/src/lms/ui/explore/ArtistsView.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "ArtistCollector.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/DatabaseCollectorBase.hpp b/src/lms/ui/explore/DatabaseCollectorBase.hpp index 224f7657..677a2746 100644 --- a/src/lms/ui/explore/DatabaseCollectorBase.hpp +++ b/src/lms/ui/explore/DatabaseCollectorBase.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/explore/Explore.cpp b/src/lms/ui/explore/Explore.cpp index 8c821fdb..16dab2e2 100644 --- a/src/lms/ui/explore/Explore.cpp +++ b/src/lms/ui/explore/Explore.cpp @@ -23,10 +23,10 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "utils/Logger.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/Explore.hpp b/src/lms/ui/explore/Explore.hpp index 66815002..7bd4dd8c 100644 --- a/src/lms/ui/explore/Explore.hpp +++ b/src/lms/ui/explore/Explore.hpp @@ -21,7 +21,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface { diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp index f8ef1c74..2749373e 100644 --- a/src/lms/ui/explore/Filters.cpp +++ b/src/lms/ui/explore/Filters.cpp @@ -24,8 +24,8 @@ #include #include -#include "database/Cluster.hpp" -#include "database/Session.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Session.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/Filters.hpp b/src/lms/ui/explore/Filters.hpp index 079d52c2..ce4abab9 100644 --- a/src/lms/ui/explore/Filters.hpp +++ b/src/lms/ui/explore/Filters.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index c5671f54..f6939725 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -21,10 +21,10 @@ #include -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" -#include "database/TrackList.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" +#include "lmscore/database/TrackList.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleaseListHelpers.cpp b/src/lms/ui/explore/ReleaseListHelpers.cpp index 71c5daa5..e374b6fb 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.cpp +++ b/src/lms/ui/explore/ReleaseListHelpers.cpp @@ -23,8 +23,8 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" #include "resource/CoverResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/ReleaseListHelpers.hpp b/src/lms/ui/explore/ReleaseListHelpers.hpp index 3aad6698..65219f1f 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.hpp +++ b/src/lms/ui/explore/ReleaseListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp index 7224683f..88482867 100644 --- a/src/lms/ui/explore/ReleasePopup.cpp +++ b/src/lms/ui/explore/ReleasePopup.cpp @@ -21,9 +21,9 @@ #include -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/User.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/ReleasePopup.hpp b/src/lms/ui/explore/ReleasePopup.hpp index 5ffb4f48..393eb072 100644 --- a/src/lms/ui/explore/ReleasePopup.hpp +++ b/src/lms/ui/explore/ReleasePopup.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index 15334246..2a4cd6d0 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -25,11 +25,11 @@ #include #include -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/ScanSettings.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "recommendation/IEngine.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/explore/ReleaseView.hpp b/src/lms/ui/explore/ReleaseView.hpp index d3700eaa..aba128b1 100644 --- a/src/lms/ui/explore/ReleaseView.hpp +++ b/src/lms/ui/explore/ReleaseView.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index 93c56428..71e8a608 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -23,7 +23,7 @@ #include #include -#include "database/Session.hpp" +#include "lmscore/database/Session.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "ReleaseListHelpers.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleasesView.hpp b/src/lms/ui/explore/ReleasesView.hpp index f2a9b91b..f8e9578f 100644 --- a/src/lms/ui/explore/ReleasesView.hpp +++ b/src/lms/ui/explore/ReleasesView.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" #include "ReleaseCollector.hpp" diff --git a/src/lms/ui/explore/SearchView.cpp b/src/lms/ui/explore/SearchView.cpp index 90a950e8..143f4c7f 100644 --- a/src/lms/ui/explore/SearchView.cpp +++ b/src/lms/ui/explore/SearchView.cpp @@ -25,10 +25,10 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "common/LoadingIndicator.hpp" diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 77106931..22eb6013 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -21,10 +21,10 @@ #include -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/TrackList.hpp" +#include "lmscore/database/User.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index ba36cb9d..1687bd82 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -24,9 +24,9 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Track.hpp" #include "resource/DownloadResource.hpp" #include "resource/CoverResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/TrackListHelpers.hpp b/src/lms/ui/explore/TrackListHelpers.hpp index 2e3ea306..94bcc1e6 100644 --- a/src/lms/ui/explore/TrackListHelpers.hpp +++ b/src/lms/ui/explore/TrackListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/TrackPopup.cpp b/src/lms/ui/explore/TrackPopup.cpp index 8884347c..143ea92e 100644 --- a/src/lms/ui/explore/TrackPopup.cpp +++ b/src/lms/ui/explore/TrackPopup.cpp @@ -21,9 +21,9 @@ #include -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/User.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/TrackPopup.hpp b/src/lms/ui/explore/TrackPopup.hpp index 1597d994..4b745f73 100644 --- a/src/lms/ui/explore/TrackPopup.hpp +++ b/src/lms/ui/explore/TrackPopup.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index daec0fcd..43bd3c0e 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -23,7 +23,7 @@ #include #include -#include "database/Session.hpp" +#include "lmscore/database/Session.hpp" #include "utils/Logger.hpp" #include "common/InfiniteScrollingContainer.hpp" diff --git a/src/lms/ui/explore/TracksView.hpp b/src/lms/ui/explore/TracksView.hpp index ac011e01..12fcb897 100644 --- a/src/lms/ui/explore/TracksView.hpp +++ b/src/lms/ui/explore/TracksView.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "PlayQueueAction.hpp" #include "TrackCollector.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index def318df..ad9d6c91 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -23,8 +23,8 @@ #include #include "av/IAudioFile.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "utils/FileResourceHandlerCreator.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.hpp b/src/lms/ui/resource/AudioFileResource.hpp index fda116eb..76fbbe7a 100644 --- a/src/lms/ui/resource/AudioFileResource.hpp +++ b/src/lms/ui/resource/AudioFileResource.hpp @@ -21,7 +21,7 @@ #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/AudioTranscodeResource.cpp b/src/lms/ui/resource/AudioTranscodeResource.cpp index 66ecac06..7845f3c8 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.cpp +++ b/src/lms/ui/resource/AudioTranscodeResource.cpp @@ -25,9 +25,9 @@ #include "av/TranscodeParameters.hpp" #include "av/TranscodeResourceHandlerCreator.hpp" #include "av/Types.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" +#include "lmscore/database/User.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/resource/AudioTranscodeResource.hpp b/src/lms/ui/resource/AudioTranscodeResource.hpp index ef516e91..4c9a594c 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.hpp +++ b/src/lms/ui/resource/AudioTranscodeResource.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace Database { diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index 44d1189d..4918b3c4 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -23,7 +23,7 @@ #include #include "lmscore/cover/ICoverService.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/resource/CoverResource.hpp b/src/lms/ui/resource/CoverResource.hpp index 1e8cb8ad..2b5d3b84 100644 --- a/src/lms/ui/resource/CoverResource.hpp +++ b/src/lms/ui/resource/CoverResource.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/DownloadResource.cpp b/src/lms/ui/resource/DownloadResource.cpp index 998096a5..35061ef6 100644 --- a/src/lms/ui/resource/DownloadResource.cpp +++ b/src/lms/ui/resource/DownloadResource.cpp @@ -26,10 +26,10 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Zipper.hpp" diff --git a/src/lms/ui/resource/DownloadResource.hpp b/src/lms/ui/resource/DownloadResource.hpp index 581a55ec..20e432cb 100644 --- a/src/lms/ui/resource/DownloadResource.hpp +++ b/src/lms/ui/resource/DownloadResource.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "lmscore/database/Types.hpp" #include "utils/Zipper.hpp" namespace UserInterface { diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index 8202497c..f6757c8b 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -25,10 +25,10 @@ #include #include "lmscore/cover/ICoverService.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/tools/recommendation/CMakeLists.txt b/src/tools/recommendation/CMakeLists.txt index 12f20ef0..5fd6b6a8 100644 --- a/src/tools/recommendation/CMakeLists.txt +++ b/src/tools/recommendation/CMakeLists.txt @@ -4,7 +4,7 @@ add_executable(lms-recommendation ) target_link_libraries(lms-recommendation PRIVATE - lmsdatabase + lmscore lmsrecommendation Boost::program_options ) diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index c88dcc36..23c11d26 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -24,12 +24,12 @@ #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "lmscore/database/Artist.hpp" +#include "lmscore/database/Cluster.hpp" +#include "lmscore/database/Db.hpp" +#include "lmscore/database/Release.hpp" +#include "lmscore/database/Session.hpp" +#include "lmscore/database/Track.hpp" #include "utils/IConfig.hpp" #include "utils/Service.hpp" #include "utils/StreamLogger.hpp" From 1faea94a24c7ebc15d0bb4af975c8fdf105202f9 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 17 Oct 2021 21:08:48 +0200 Subject: [PATCH 19/49] Revert "Migrated database stuff" This reverts commit c41cc77e21656df27a2402386b4caf57bb503797. --- src/libs/CMakeLists.txt | 1 + src/libs/database/CMakeLists.txt | 40 +++++++++++++++++++ .../database => database/impl}/Artist.cpp | 12 +++--- .../database => database/impl}/Cluster.cpp | 12 +++--- .../impl/database => database/impl}/Db.cpp | 6 +-- .../database => database/impl}/Release.cpp | 12 +++--- .../impl}/ScanSettings.cpp | 6 +-- .../database => database/impl}/Session.cpp | 29 ++++++++------ .../database => database/impl}/SqlQuery.cpp | 0 .../database => database/impl}/SqlQuery.hpp | 0 .../impl}/StringViewTraits.hpp | 0 .../impl/database => database/impl}/Track.cpp | 16 ++++---- .../impl}/TrackArtistLink.cpp | 8 ++-- .../impl}/TrackBookmark.cpp | 8 ++-- .../impl}/TrackFeatures.cpp | 6 +-- .../database => database/impl}/TrackList.cpp | 14 +++---- .../database => database/impl}/Traits.hpp | 2 +- .../impl/database => database/impl}/User.cpp | 12 +++--- .../impl/database => database/impl}/Utils.cpp | 0 .../impl/database => database/impl}/Utils.hpp | 0 .../include}/database/Artist.hpp | 2 +- .../include}/database/Cluster.hpp | 2 +- .../include}/database/Db.hpp | 0 .../include}/database/Release.hpp | 2 +- .../include}/database/ScanSettings.hpp | 2 +- .../include}/database/Session.hpp | 0 .../include}/database/Track.hpp | 2 +- .../include}/database/TrackArtistLink.hpp | 2 +- .../include}/database/TrackBookmark.hpp | 2 +- .../include}/database/TrackFeatures.hpp | 2 +- .../include}/database/TrackList.hpp | 3 +- .../include}/database/Types.hpp | 0 .../include}/database/User.hpp | 2 +- .../database => database/test}/Artist.cpp | 0 .../database => database/test}/CMakeLists.txt | 2 +- .../database => database/test}/Cluster.cpp | 0 .../database => database/test}/Common.hpp | 22 +++++----- .../test}/DatabaseTest.cpp | 0 .../database => database/test}/Release.cpp | 0 .../test/database => database/test}/Track.cpp | 0 src/libs/lmscore/CMakeLists.txt | 19 --------- .../lmscore/impl/auth/AuthServiceBase.cpp | 6 +-- .../lmscore/impl/auth/AuthServiceBase.hpp | 2 +- .../lmscore/impl/auth/AuthTokenService.cpp | 4 +- .../lmscore/impl/auth/PasswordServiceBase.cpp | 4 +- .../auth/internal/InternalPasswordService.cpp | 4 +- .../auth/internal/InternalPasswordService.hpp | 2 +- .../impl/auth/pam/PAMPasswordService.cpp | 1 + src/libs/lmscore/impl/cover/CoverService.cpp | 8 ++-- src/libs/lmscore/impl/cover/CoverService.hpp | 2 +- .../lmscore/auth/IAuthTokenService.hpp | 2 +- .../include/lmscore/auth/IEnvService.hpp | 3 +- .../include/lmscore/auth/IPasswordService.hpp | 2 +- .../lmscore/include/lmscore/auth/Types.hpp | 2 +- .../include/lmscore/cover/ICoverService.hpp | 2 +- src/libs/lmscore/test/CMakeLists.txt | 3 -- src/libs/recommendation/CMakeLists.txt | 2 +- src/libs/recommendation/impl/Engine.cpp | 6 +-- .../impl/clusters/ClustersEngine.cpp | 14 +++---- .../impl/features/FeaturesEngine.cpp | 16 ++++---- .../impl/features/FeaturesEngineCache.hpp | 2 +- .../include/recommendation/IEngine.hpp | 4 +- src/libs/scanner/CMakeLists.txt | 2 +- src/libs/scanner/impl/Scanner.cpp | 14 +++---- src/libs/scanner/impl/Scanner.hpp | 6 +-- .../scanner/include/scanner/ScannerStats.hpp | 2 +- src/libs/scrobbling/CMakeLists.txt | 2 +- src/libs/scrobbling/impl/Scrobbling.cpp | 14 +++---- .../impl/internal/InternalScrobbler.cpp | 10 ++--- .../listenbrainz/ListenBrainzScrobbler.cpp | 14 +++---- .../impl/listenbrainz/ListensSynchronizer.cpp | 14 +++---- .../impl/listenbrainz/ListensSynchronizer.hpp | 2 +- .../scrobbling/impl/listenbrainz/Utils.cpp | 6 +-- .../scrobbling/impl/listenbrainz/Utils.hpp | 2 +- .../include/scrobbling/IScrobbling.hpp | 2 +- .../scrobbling/include/scrobbling/Listen.hpp | 2 +- src/libs/subsonic/impl/ParameterParsing.hpp | 2 +- src/libs/subsonic/impl/RequestContext.hpp | 2 +- src/libs/subsonic/impl/Stream.cpp | 6 +-- src/libs/subsonic/impl/SubsonicId.hpp | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 18 ++++----- src/libs/subsonic/impl/SubsonicResource.hpp | 2 +- src/lms/main.cpp | 4 +- src/lms/ui/Auth.cpp | 4 +- src/lms/ui/Auth.hpp | 21 +++++----- src/lms/ui/LmsApplication.cpp | 12 +++--- src/lms/ui/LmsApplicationException.hpp | 2 +- src/lms/ui/LmsApplicationManager.hpp | 2 +- src/lms/ui/LmsTheme.hpp | 2 +- src/lms/ui/MediaPlayer.cpp | 14 +++---- src/lms/ui/MediaPlayer.hpp | 2 +- src/lms/ui/PlayQueue.cpp | 12 +++--- src/lms/ui/PlayQueue.hpp | 2 +- src/lms/ui/PlayQueueAction.hpp | 2 +- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/DatabaseSettingsView.cpp | 6 +-- src/lms/ui/admin/InitWizardView.cpp | 4 +- src/lms/ui/admin/ScannerController.cpp | 4 +- src/lms/ui/admin/UserView.cpp | 4 +- src/lms/ui/admin/UsersView.cpp | 4 +- src/lms/ui/common/LoginNameValidator.cpp | 2 +- src/lms/ui/common/PasswordValidator.hpp | 1 + src/lms/ui/explore/ArtistCollector.cpp | 6 +-- src/lms/ui/explore/ArtistCollector.hpp | 2 +- src/lms/ui/explore/ArtistListHelpers.cpp | 2 +- src/lms/ui/explore/ArtistListHelpers.hpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 14 +++---- src/lms/ui/explore/ArtistView.hpp | 2 +- src/lms/ui/explore/ArtistsView.cpp | 4 +- src/lms/ui/explore/ArtistsView.hpp | 2 +- src/lms/ui/explore/DatabaseCollectorBase.hpp | 2 +- src/lms/ui/explore/Explore.cpp | 8 ++-- src/lms/ui/explore/Explore.hpp | 2 +- src/lms/ui/explore/Filters.cpp | 4 +- src/lms/ui/explore/Filters.hpp | 2 +- src/lms/ui/explore/ReleaseCollector.cpp | 8 ++-- src/lms/ui/explore/ReleaseListHelpers.cpp | 4 +- src/lms/ui/explore/ReleaseListHelpers.hpp | 2 +- src/lms/ui/explore/ReleasePopup.cpp | 6 +-- src/lms/ui/explore/ReleasePopup.hpp | 2 +- src/lms/ui/explore/ReleaseView.cpp | 10 ++--- src/lms/ui/explore/ReleaseView.hpp | 2 +- src/lms/ui/explore/ReleasesView.cpp | 2 +- src/lms/ui/explore/ReleasesView.hpp | 2 +- src/lms/ui/explore/SearchView.cpp | 8 ++-- src/lms/ui/explore/TrackCollector.cpp | 8 ++-- src/lms/ui/explore/TrackListHelpers.cpp | 6 +-- src/lms/ui/explore/TrackListHelpers.hpp | 2 +- src/lms/ui/explore/TrackPopup.cpp | 6 +-- src/lms/ui/explore/TrackPopup.hpp | 2 +- src/lms/ui/explore/TracksView.cpp | 2 +- src/lms/ui/explore/TracksView.hpp | 2 +- src/lms/ui/resource/AudioFileResource.cpp | 4 +- src/lms/ui/resource/AudioFileResource.hpp | 2 +- .../ui/resource/AudioTranscodeResource.cpp | 6 +-- .../ui/resource/AudioTranscodeResource.hpp | 2 +- src/lms/ui/resource/CoverResource.cpp | 2 +- src/lms/ui/resource/CoverResource.hpp | 2 +- src/lms/ui/resource/DownloadResource.cpp | 8 ++-- src/lms/ui/resource/DownloadResource.hpp | 2 +- src/tools/cover/LmsCover.cpp | 8 ++-- src/tools/recommendation/CMakeLists.txt | 2 +- .../recommendation/LmsRecommendation.cpp | 12 +++--- 143 files changed, 387 insertions(+), 358 deletions(-) create mode 100644 src/libs/database/CMakeLists.txt rename src/libs/{lmscore/impl/database => database/impl}/Artist.cpp (98%) rename src/libs/{lmscore/impl/database => database/impl}/Cluster.cpp (95%) rename src/libs/{lmscore/impl/database => database/impl}/Db.cpp (95%) rename src/libs/{lmscore/impl/database => database/impl}/Release.cpp (98%) rename src/libs/{lmscore/impl/database => database/impl}/ScanSettings.cpp (96%) rename src/libs/{lmscore/impl/database => database/impl}/Session.cpp (97%) rename src/libs/{lmscore/impl/database => database/impl}/SqlQuery.cpp (100%) rename src/libs/{lmscore/impl/database => database/impl}/SqlQuery.hpp (100%) rename src/libs/{lmscore/impl/database => database/impl}/StringViewTraits.hpp (100%) rename src/libs/{lmscore/impl/database => database/impl}/Track.cpp (98%) rename src/libs/{lmscore/impl/database => database/impl}/TrackArtistLink.cpp (90%) rename src/libs/{lmscore/impl/database => database/impl}/TrackBookmark.cpp (93%) rename src/libs/{lmscore/impl/database => database/impl}/TrackFeatures.cpp (94%) rename src/libs/{lmscore/impl/database => database/impl}/TrackList.cpp (98%) rename src/libs/{lmscore/impl/database => database/impl}/Traits.hpp (97%) rename src/libs/{lmscore/impl/database => database/impl}/User.cpp (95%) rename src/libs/{lmscore/impl/database => database/impl}/Utils.cpp (100%) rename src/libs/{lmscore/impl/database => database/impl}/Utils.hpp (100%) rename src/libs/{lmscore/include/lmscore => database/include}/database/Artist.hpp (99%) rename src/libs/{lmscore/include/lmscore => database/include}/database/Cluster.hpp (98%) rename src/libs/{lmscore/include/lmscore => database/include}/database/Db.hpp (100%) rename src/libs/{lmscore/include/lmscore => database/include}/database/Release.hpp (99%) rename src/libs/{lmscore/include/lmscore => database/include}/database/ScanSettings.hpp (98%) rename src/libs/{lmscore/include/lmscore => database/include}/database/Session.hpp (100%) rename src/libs/{lmscore/include/lmscore => database/include}/database/Track.hpp (99%) rename src/libs/{lmscore/include/lmscore => database/include}/database/TrackArtistLink.hpp (98%) rename src/libs/{lmscore/include/lmscore => database/include}/database/TrackBookmark.hpp (98%) rename src/libs/{lmscore/include/lmscore => database/include}/database/TrackFeatures.hpp (97%) rename src/libs/{lmscore/include/lmscore => database/include}/database/TrackList.hpp (99%) rename src/libs/{lmscore/include/lmscore => database/include}/database/Types.hpp (100%) rename src/libs/{lmscore/include/lmscore => database/include}/database/User.hpp (99%) rename src/libs/{lmscore/test/database => database/test}/Artist.cpp (100%) rename src/libs/{lmscore/test/database => database/test}/CMakeLists.txt (94%) rename src/libs/{lmscore/test/database => database/test}/Cluster.cpp (100%) rename src/libs/{lmscore/test/database => database/test}/Common.hpp (90%) rename src/libs/{lmscore/test/database => database/test}/DatabaseTest.cpp (100%) rename src/libs/{lmscore/test/database => database/test}/Release.cpp (100%) rename src/libs/{lmscore/test/database => database/test}/Track.cpp (100%) delete mode 100644 src/libs/lmscore/test/CMakeLists.txt diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 211f6226..196b2a1b 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(av) +add_subdirectory(database) add_subdirectory(image) add_subdirectory(lmscore) add_subdirectory(metadata) diff --git a/src/libs/database/CMakeLists.txt b/src/libs/database/CMakeLists.txt new file mode 100644 index 00000000..38b13e75 --- /dev/null +++ b/src/libs/database/CMakeLists.txt @@ -0,0 +1,40 @@ +add_library(lmsdatabase SHARED + impl/Artist.cpp + impl/Cluster.cpp + impl/Db.cpp + impl/TrackArtistLink.cpp + impl/TrackFeatures.cpp + impl/TrackList.cpp + impl/Release.cpp + impl/ScanSettings.cpp + impl/Session.cpp + impl/SqlQuery.cpp + impl/Track.cpp + impl/TrackBookmark.cpp + impl/User.cpp + impl/Utils.cpp + ) + +target_include_directories(lmsdatabase INTERFACE + include + ) + +target_include_directories(lmsdatabase PRIVATE + include + ) + +target_link_libraries(lmsdatabase PRIVATE + Wt::DboSqlite3 + ) + +target_link_libraries(lmsdatabase PUBLIC + lmsutils + std::filesystem + Wt::Dbo + ) + +install(TARGETS lmsdatabase DESTINATION lib) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/libs/lmscore/impl/database/Artist.cpp b/src/libs/database/impl/Artist.cpp similarity index 98% rename from src/libs/lmscore/impl/database/Artist.cpp rename to src/libs/database/impl/Artist.cpp index 3e9e35b3..9ca631fa 100644 --- a/src/libs/lmscore/impl/database/Artist.cpp +++ b/src/libs/database/impl/Artist.cpp @@ -16,15 +16,15 @@ * You should have received a copy of the GNU General Public License * along with LMS. If not, see . */ -#include "lmscore/database/Artist.hpp" +#include "database/Artist.hpp" #include -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/User.hpp" +#include "database/Cluster.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Utils.hpp" diff --git a/src/libs/lmscore/impl/database/Cluster.cpp b/src/libs/database/impl/Cluster.cpp similarity index 95% rename from src/libs/lmscore/impl/database/Cluster.cpp rename to src/libs/database/impl/Cluster.cpp index 2fdf0cc7..184a03a1 100644 --- a/src/libs/lmscore/impl/database/Cluster.cpp +++ b/src/libs/database/impl/Cluster.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "lmscore/database/Cluster.hpp" +#include "database/Cluster.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/ScanSettings.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" +#include "database/ScanSettings.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "SqlQuery.hpp" #include "Traits.hpp" diff --git a/src/libs/lmscore/impl/database/Db.cpp b/src/libs/database/impl/Db.cpp similarity index 95% rename from src/libs/lmscore/impl/database/Db.cpp rename to src/libs/database/impl/Db.cpp index 2d434ae7..5035466c 100644 --- a/src/libs/lmscore/impl/database/Db.cpp +++ b/src/libs/database/impl/Db.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "lmscore/database/Db.hpp" +#include "database/Db.hpp" #include #include -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" namespace Database { diff --git a/src/libs/lmscore/impl/database/Release.cpp b/src/libs/database/impl/Release.cpp similarity index 98% rename from src/libs/lmscore/impl/database/Release.cpp rename to src/libs/database/impl/Release.cpp index 3a9c91ef..310de51e 100644 --- a/src/libs/lmscore/impl/database/Release.cpp +++ b/src/libs/database/impl/Release.cpp @@ -17,15 +17,15 @@ * along with LMS. If not, see . */ -#include "lmscore/database/Release.hpp" +#include "database/Release.hpp" #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Traits.hpp" diff --git a/src/libs/lmscore/impl/database/ScanSettings.cpp b/src/libs/database/impl/ScanSettings.cpp similarity index 96% rename from src/libs/lmscore/impl/database/ScanSettings.cpp rename to src/libs/database/impl/ScanSettings.cpp index fefa2d8c..9aafed53 100644 --- a/src/libs/lmscore/impl/database/ScanSettings.cpp +++ b/src/libs/database/impl/ScanSettings.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "lmscore/database/ScanSettings.hpp" +#include "database/ScanSettings.hpp" #include @@ -25,8 +25,8 @@ #include "utils/Logger.hpp" #include "utils/String.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Session.hpp" +#include "database/Cluster.hpp" +#include "database/Session.hpp" namespace { diff --git a/src/libs/lmscore/impl/database/Session.cpp b/src/libs/database/impl/Session.cpp similarity index 97% rename from src/libs/lmscore/impl/database/Session.cpp rename to src/libs/database/impl/Session.cpp index 9d0280ed..56ec77d1 100644 --- a/src/libs/lmscore/impl/database/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -17,22 +17,27 @@ * along with LMS. If not, see . */ -#include "lmscore/database/Session.hpp" +#include "database/Session.hpp" + +#include +#include +#include +#include #include "utils/Exception.hpp" #include "utils/Logger.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/ScanSettings.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackBookmark.hpp" -#include "lmscore/database/TrackArtistLink.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/TrackFeatures.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/ScanSettings.hpp" +#include "database/Track.hpp" +#include "database/TrackBookmark.hpp" +#include "database/TrackArtistLink.hpp" +#include "database/TrackList.hpp" +#include "database/TrackFeatures.hpp" +#include "database/User.hpp" namespace Database { diff --git a/src/libs/lmscore/impl/database/SqlQuery.cpp b/src/libs/database/impl/SqlQuery.cpp similarity index 100% rename from src/libs/lmscore/impl/database/SqlQuery.cpp rename to src/libs/database/impl/SqlQuery.cpp diff --git a/src/libs/lmscore/impl/database/SqlQuery.hpp b/src/libs/database/impl/SqlQuery.hpp similarity index 100% rename from src/libs/lmscore/impl/database/SqlQuery.hpp rename to src/libs/database/impl/SqlQuery.hpp diff --git a/src/libs/lmscore/impl/database/StringViewTraits.hpp b/src/libs/database/impl/StringViewTraits.hpp similarity index 100% rename from src/libs/lmscore/impl/database/StringViewTraits.hpp rename to src/libs/database/impl/StringViewTraits.hpp diff --git a/src/libs/lmscore/impl/database/Track.cpp b/src/libs/database/impl/Track.cpp similarity index 98% rename from src/libs/lmscore/impl/database/Track.cpp rename to src/libs/database/impl/Track.cpp index 3e72e71a..82dc99cf 100644 --- a/src/libs/lmscore/impl/database/Track.cpp +++ b/src/libs/database/impl/Track.cpp @@ -17,17 +17,17 @@ * along with LMS. If not, see . */ -#include "lmscore/database/Track.hpp" +#include "database/Track.hpp" #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/TrackArtistLink.hpp" -#include "lmscore/database/TrackFeatures.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Release.hpp" +#include "database/TrackArtistLink.hpp" +#include "database/TrackFeatures.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" diff --git a/src/libs/lmscore/impl/database/TrackArtistLink.cpp b/src/libs/database/impl/TrackArtistLink.cpp similarity index 90% rename from src/libs/lmscore/impl/database/TrackArtistLink.cpp rename to src/libs/database/impl/TrackArtistLink.cpp index 059338c5..f8791dee 100644 --- a/src/libs/lmscore/impl/database/TrackArtistLink.cpp +++ b/src/libs/database/impl/TrackArtistLink.cpp @@ -17,11 +17,11 @@ * along with LMS. If not, see . */ -#include "lmscore/database/TrackArtistLink.hpp" +#include "database/TrackArtistLink.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "Traits.hpp" diff --git a/src/libs/lmscore/impl/database/TrackBookmark.cpp b/src/libs/database/impl/TrackBookmark.cpp similarity index 93% rename from src/libs/lmscore/impl/database/TrackBookmark.cpp rename to src/libs/database/impl/TrackBookmark.cpp index adf3e23a..91f457bd 100644 --- a/src/libs/lmscore/impl/database/TrackBookmark.cpp +++ b/src/libs/database/impl/TrackBookmark.cpp @@ -17,11 +17,11 @@ * along with LMS. If not, see . */ -#include "lmscore/database/TrackBookmark.hpp" +#include "database/TrackBookmark.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/User.hpp" #include "Traits.hpp" namespace Database { diff --git a/src/libs/lmscore/impl/database/TrackFeatures.cpp b/src/libs/database/impl/TrackFeatures.cpp similarity index 94% rename from src/libs/lmscore/impl/database/TrackFeatures.cpp rename to src/libs/database/impl/TrackFeatures.cpp index dde9cd72..5770d0b1 100644 --- a/src/libs/lmscore/impl/database/TrackFeatures.cpp +++ b/src/libs/database/impl/TrackFeatures.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "lmscore/database/TrackFeatures.hpp" +#include "database/TrackFeatures.hpp" #include #include -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "utils/Logger.hpp" namespace Database { diff --git a/src/libs/lmscore/impl/database/TrackList.cpp b/src/libs/database/impl/TrackList.cpp similarity index 98% rename from src/libs/lmscore/impl/database/TrackList.cpp rename to src/libs/database/impl/TrackList.cpp index c8bff04b..a9863d52 100644 --- a/src/libs/lmscore/impl/database/TrackList.cpp +++ b/src/libs/database/impl/TrackList.cpp @@ -16,18 +16,18 @@ * You should have received a copy of the GNU General Public License * along with LMS. If not, see . */ -#include "lmscore/database/TrackList.hpp" +#include "database/TrackList.hpp" #include #include "utils/Logger.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" +#include "database/Track.hpp" #include "SqlQuery.hpp" #include "StringViewTraits.hpp" #include "Traits.hpp" diff --git a/src/libs/lmscore/impl/database/Traits.hpp b/src/libs/database/impl/Traits.hpp similarity index 97% rename from src/libs/lmscore/impl/database/Traits.hpp rename to src/libs/database/impl/Traits.hpp index 9365085c..bdfa0045 100644 --- a/src/libs/lmscore/impl/database/Traits.hpp +++ b/src/libs/database/impl/Traits.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Wt::Dbo { diff --git a/src/libs/lmscore/impl/database/User.cpp b/src/libs/database/impl/User.cpp similarity index 95% rename from src/libs/lmscore/impl/database/User.cpp rename to src/libs/database/impl/User.cpp index 3f8ca7e0..44f8c2b1 100644 --- a/src/libs/lmscore/impl/database/User.cpp +++ b/src/libs/database/impl/User.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "lmscore/database/User.hpp" +#include "database/User.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" #include "utils/Logger.hpp" #include "StringViewTraits.hpp" #include "Traits.hpp" diff --git a/src/libs/lmscore/impl/database/Utils.cpp b/src/libs/database/impl/Utils.cpp similarity index 100% rename from src/libs/lmscore/impl/database/Utils.cpp rename to src/libs/database/impl/Utils.cpp diff --git a/src/libs/lmscore/impl/database/Utils.hpp b/src/libs/database/impl/Utils.hpp similarity index 100% rename from src/libs/lmscore/impl/database/Utils.hpp rename to src/libs/database/impl/Utils.hpp diff --git a/src/libs/lmscore/include/lmscore/database/Artist.hpp b/src/libs/database/include/database/Artist.hpp similarity index 99% rename from src/libs/lmscore/include/lmscore/database/Artist.hpp rename to src/libs/database/include/database/Artist.hpp index a3458a3e..23ef7812 100644 --- a/src/libs/lmscore/include/lmscore/database/Artist.hpp +++ b/src/libs/database/include/database/Artist.hpp @@ -27,7 +27,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" diff --git a/src/libs/lmscore/include/lmscore/database/Cluster.hpp b/src/libs/database/include/database/Cluster.hpp similarity index 98% rename from src/libs/lmscore/include/lmscore/database/Cluster.hpp rename to src/libs/database/include/database/Cluster.hpp index 290d1a30..42a11e77 100644 --- a/src/libs/lmscore/include/lmscore/database/Cluster.hpp +++ b/src/libs/database/include/database/Cluster.hpp @@ -26,7 +26,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/database/Db.hpp b/src/libs/database/include/database/Db.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/database/Db.hpp rename to src/libs/database/include/database/Db.hpp diff --git a/src/libs/lmscore/include/lmscore/database/Release.hpp b/src/libs/database/include/database/Release.hpp similarity index 99% rename from src/libs/lmscore/include/lmscore/database/Release.hpp rename to src/libs/database/include/database/Release.hpp index d75d9921..ce45a1a8 100644 --- a/src/libs/lmscore/include/lmscore/database/Release.hpp +++ b/src/libs/database/include/database/Release.hpp @@ -25,7 +25,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/UUID.hpp" namespace Database diff --git a/src/libs/lmscore/include/lmscore/database/ScanSettings.hpp b/src/libs/database/include/database/ScanSettings.hpp similarity index 98% rename from src/libs/lmscore/include/lmscore/database/ScanSettings.hpp rename to src/libs/database/include/database/ScanSettings.hpp index 4d8673cb..9cb8cb5a 100644 --- a/src/libs/lmscore/include/lmscore/database/ScanSettings.hpp +++ b/src/libs/database/include/database/ScanSettings.hpp @@ -25,7 +25,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/database/Session.hpp b/src/libs/database/include/database/Session.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/database/Session.hpp rename to src/libs/database/include/database/Session.hpp diff --git a/src/libs/lmscore/include/lmscore/database/Track.hpp b/src/libs/database/include/database/Track.hpp similarity index 99% rename from src/libs/lmscore/include/lmscore/database/Track.hpp rename to src/libs/database/include/database/Track.hpp index dc71a21f..3e2c2ced 100644 --- a/src/libs/lmscore/include/lmscore/database/Track.hpp +++ b/src/libs/database/include/database/Track.hpp @@ -34,7 +34,7 @@ #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/database/TrackArtistLink.hpp b/src/libs/database/include/database/TrackArtistLink.hpp similarity index 98% rename from src/libs/lmscore/include/lmscore/database/TrackArtistLink.hpp rename to src/libs/database/include/database/TrackArtistLink.hpp index e8a140e5..55d3b0c5 100644 --- a/src/libs/lmscore/include/lmscore/database/TrackArtistLink.hpp +++ b/src/libs/database/include/database/TrackArtistLink.hpp @@ -23,7 +23,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/EnumSet.hpp" namespace Database diff --git a/src/libs/lmscore/include/lmscore/database/TrackBookmark.hpp b/src/libs/database/include/database/TrackBookmark.hpp similarity index 98% rename from src/libs/lmscore/include/lmscore/database/TrackBookmark.hpp rename to src/libs/database/include/database/TrackBookmark.hpp index 089bac05..2c4af0db 100644 --- a/src/libs/lmscore/include/lmscore/database/TrackBookmark.hpp +++ b/src/libs/database/include/database/TrackBookmark.hpp @@ -23,7 +23,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/database/TrackFeatures.hpp b/src/libs/database/include/database/TrackFeatures.hpp similarity index 97% rename from src/libs/lmscore/include/lmscore/database/TrackFeatures.hpp rename to src/libs/database/include/database/TrackFeatures.hpp index 527cde69..99c36d6d 100644 --- a/src/libs/lmscore/include/lmscore/database/TrackFeatures.hpp +++ b/src/libs/database/include/database/TrackFeatures.hpp @@ -26,7 +26,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/database/TrackList.hpp b/src/libs/database/include/database/TrackList.hpp similarity index 99% rename from src/libs/lmscore/include/lmscore/database/TrackList.hpp rename to src/libs/database/include/database/TrackList.hpp index 4449e057..03aaca0f 100644 --- a/src/libs/lmscore/include/lmscore/database/TrackList.hpp +++ b/src/libs/database/include/database/TrackList.hpp @@ -21,12 +21,13 @@ #include #include +#include #include #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/database/Types.hpp b/src/libs/database/include/database/Types.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/database/Types.hpp rename to src/libs/database/include/database/Types.hpp diff --git a/src/libs/lmscore/include/lmscore/database/User.hpp b/src/libs/database/include/database/User.hpp similarity index 99% rename from src/libs/lmscore/include/lmscore/database/User.hpp rename to src/libs/database/include/database/User.hpp index 8422132a..b94d41af 100644 --- a/src/libs/lmscore/include/lmscore/database/User.hpp +++ b/src/libs/database/include/database/User.hpp @@ -26,7 +26,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/UUID.hpp" namespace Database { diff --git a/src/libs/lmscore/test/database/Artist.cpp b/src/libs/database/test/Artist.cpp similarity index 100% rename from src/libs/lmscore/test/database/Artist.cpp rename to src/libs/database/test/Artist.cpp diff --git a/src/libs/lmscore/test/database/CMakeLists.txt b/src/libs/database/test/CMakeLists.txt similarity index 94% rename from src/libs/lmscore/test/database/CMakeLists.txt rename to src/libs/database/test/CMakeLists.txt index 0a107ff2..605c3af5 100644 --- a/src/libs/lmscore/test/database/CMakeLists.txt +++ b/src/libs/database/test/CMakeLists.txt @@ -8,7 +8,7 @@ add_executable(test-database ) target_link_libraries(test-database PRIVATE - lmscore + lmsdatabase GTest::GTest ) diff --git a/src/libs/lmscore/test/database/Cluster.cpp b/src/libs/database/test/Cluster.cpp similarity index 100% rename from src/libs/lmscore/test/database/Cluster.cpp rename to src/libs/database/test/Cluster.cpp diff --git a/src/libs/lmscore/test/database/Common.hpp b/src/libs/database/test/Common.hpp similarity index 90% rename from src/libs/lmscore/test/database/Common.hpp rename to src/libs/database/test/Common.hpp index 3d6ac552..8864e681 100644 --- a/src/libs/lmscore/test/database/Common.hpp +++ b/src/libs/database/test/Common.hpp @@ -24,17 +24,17 @@ #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackArtistLink.hpp" -#include "lmscore/database/TrackBookmark.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/Types.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackArtistLink.hpp" +#include "database/TrackBookmark.hpp" +#include "database/TrackList.hpp" +#include "database/Types.hpp" +#include "database/User.hpp" template class ScopedEntity diff --git a/src/libs/lmscore/test/database/DatabaseTest.cpp b/src/libs/database/test/DatabaseTest.cpp similarity index 100% rename from src/libs/lmscore/test/database/DatabaseTest.cpp rename to src/libs/database/test/DatabaseTest.cpp diff --git a/src/libs/lmscore/test/database/Release.cpp b/src/libs/database/test/Release.cpp similarity index 100% rename from src/libs/lmscore/test/database/Release.cpp rename to src/libs/database/test/Release.cpp diff --git a/src/libs/lmscore/test/database/Track.cpp b/src/libs/database/test/Track.cpp similarity index 100% rename from src/libs/lmscore/test/database/Track.cpp rename to src/libs/database/test/Track.cpp diff --git a/src/libs/lmscore/CMakeLists.txt b/src/libs/lmscore/CMakeLists.txt index 40460f1b..4403380a 100644 --- a/src/libs/lmscore/CMakeLists.txt +++ b/src/libs/lmscore/CMakeLists.txt @@ -7,20 +7,6 @@ add_library(lmscore SHARED impl/auth/PasswordServiceBase.cpp impl/auth/http-headers/HttpHeadersEnvService.cpp impl/auth/internal/InternalPasswordService.cpp - impl/database/Artist.cpp - impl/database/Cluster.cpp - impl/database/Db.cpp - impl/database/TrackArtistLink.cpp - impl/database/TrackFeatures.cpp - impl/database/TrackList.cpp - impl/database/Release.cpp - impl/database/ScanSettings.cpp - impl/database/Session.cpp - impl/database/SqlQuery.cpp - impl/database/Track.cpp - impl/database/TrackBookmark.cpp - impl/database/User.cpp - impl/database/Utils.cpp impl/cover/CoverService.cpp ) @@ -35,7 +21,6 @@ target_include_directories(lmscore PRIVATE target_link_libraries(lmscore PRIVATE lmsav - Wt::DboSqlite3 ) target_link_libraries(lmscore PUBLIC @@ -44,7 +29,6 @@ target_link_libraries(lmscore PUBLIC lmsutils std::filesystem Wt::Wt - Wt::Dbo ) if (USE_PAM) @@ -57,6 +41,3 @@ endif (USE_PAM) install(TARGETS lmscore DESTINATION lib) -if(BUILD_TESTING) - add_subdirectory(test) -endif() diff --git a/src/libs/lmscore/impl/auth/AuthServiceBase.cpp b/src/libs/lmscore/impl/auth/AuthServiceBase.cpp index 680226f7..6e0d0589 100644 --- a/src/libs/lmscore/impl/auth/AuthServiceBase.cpp +++ b/src/libs/lmscore/impl/auth/AuthServiceBase.cpp @@ -19,9 +19,9 @@ #include "AuthServiceBase.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Db.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" namespace Auth diff --git a/src/libs/lmscore/impl/auth/AuthServiceBase.hpp b/src/libs/lmscore/impl/auth/AuthServiceBase.hpp index f002baf0..81acba59 100644 --- a/src/libs/lmscore/impl/auth/AuthServiceBase.hpp +++ b/src/libs/lmscore/impl/auth/AuthServiceBase.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/impl/auth/AuthTokenService.cpp b/src/libs/lmscore/impl/auth/AuthTokenService.cpp index 32a9b03f..05949870 100644 --- a/src/libs/lmscore/impl/auth/AuthTokenService.cpp +++ b/src/libs/lmscore/impl/auth/AuthTokenService.cpp @@ -24,8 +24,8 @@ #include #include "lmscore/auth/Types.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp b/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp index 23c0d41f..e80d1b9b 100644 --- a/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp +++ b/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp @@ -28,8 +28,8 @@ #endif // LMS_SUPPORT_PAM #include "lmscore/auth/Types.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp index ff26a585..233fb9aa 100644 --- a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp +++ b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp @@ -22,8 +22,8 @@ #include "lmscore/auth/IAuthTokenService.hpp" #include "lmscore/auth/Types.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp index 9e3a1921..42b830f1 100644 --- a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp +++ b/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/User.hpp" +#include "database/User.hpp" #include "auth/PasswordServiceBase.hpp" #include "auth/LoginThrottler.hpp" diff --git a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp index 9f612633..708ce457 100644 --- a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp +++ b/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp @@ -27,6 +27,7 @@ #include #include "lmscore/auth/Types.hpp" +#include "database/Session.hpp" #include "utils/Logger.hpp" namespace Auth diff --git a/src/libs/lmscore/impl/cover/CoverService.cpp b/src/libs/lmscore/impl/cover/CoverService.cpp index f773d8e5..a968d5c4 100644 --- a/src/libs/lmscore/impl/cover/CoverService.cpp +++ b/src/libs/lmscore/impl/cover/CoverService.cpp @@ -21,10 +21,10 @@ #include "av/IAudioFile.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "image/Exception.hpp" #include "image/IRawImage.hpp" diff --git a/src/libs/lmscore/impl/cover/CoverService.hpp b/src/libs/lmscore/impl/cover/CoverService.hpp index 30c93bed..f1a845ac 100644 --- a/src/libs/lmscore/impl/cover/CoverService.hpp +++ b/src/libs/lmscore/impl/cover/CoverService.hpp @@ -31,7 +31,7 @@ #include "lmscore/cover/ICoverService.hpp" #include "image/IEncodedImage.hpp" -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp b/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp index 0fa5f2cb..5d9dd51b 100644 --- a/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp +++ b/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp @@ -27,7 +27,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp b/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp index 9a845fcc..ecb23349 100644 --- a/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp +++ b/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp @@ -20,8 +20,9 @@ #pragma once #include +#include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp b/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp index 3509d6f1..86edc868 100644 --- a/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp +++ b/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp @@ -26,7 +26,7 @@ #include #include "lmscore/auth/Types.hpp" -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/lmscore/include/lmscore/auth/Types.hpp b/src/libs/lmscore/include/lmscore/auth/Types.hpp index 519c9981..8b97df6e 100644 --- a/src/libs/lmscore/include/lmscore/auth/Types.hpp +++ b/src/libs/lmscore/include/lmscore/auth/Types.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/Exception.hpp" namespace Auth diff --git a/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp b/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp index 925e18ed..039260ef 100644 --- a/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp +++ b/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "image/IEncodedImage.hpp" namespace Database diff --git a/src/libs/lmscore/test/CMakeLists.txt b/src/libs/lmscore/test/CMakeLists.txt deleted file mode 100644 index d74e1951..00000000 --- a/src/libs/lmscore/test/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ - -add_subdirectory(database) - diff --git a/src/libs/recommendation/CMakeLists.txt b/src/libs/recommendation/CMakeLists.txt index 3130d76b..4682173d 100644 --- a/src/libs/recommendation/CMakeLists.txt +++ b/src/libs/recommendation/CMakeLists.txt @@ -17,7 +17,7 @@ target_include_directories(lmsrecommendation PRIVATE ) target_link_libraries(lmsrecommendation PRIVATE - lmscore + lmsdatabase lmssom std::filesystem ) diff --git a/src/libs/recommendation/impl/Engine.cpp b/src/libs/recommendation/impl/Engine.cpp index f7d2bf05..b6808ceb 100644 --- a/src/libs/recommendation/impl/Engine.cpp +++ b/src/libs/recommendation/impl/Engine.cpp @@ -25,9 +25,9 @@ #include "ClustersEngineCreator.hpp" #include "FeaturesEngineCreator.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/ScanSettings.hpp" +#include "database/Db.hpp" +#include "database/Session.hpp" +#include "database/ScanSettings.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/recommendation/impl/clusters/ClustersEngine.cpp index 13be84a3..b0703a0f 100644 --- a/src/libs/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/recommendation/impl/clusters/ClustersEngine.cpp @@ -19,13 +19,13 @@ #include "ClustersEngine.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" namespace Recommendation { diff --git a/src/libs/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/recommendation/impl/features/FeaturesEngine.cpp index 560e0126..7b1f39b6 100644 --- a/src/libs/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/recommendation/impl/features/FeaturesEngine.cpp @@ -21,14 +21,14 @@ #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackArtistLink.hpp" -#include "lmscore/database/TrackFeatures.hpp" -#include "lmscore/database/TrackList.hpp" +#include "database/Artist.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackArtistLink.hpp" +#include "database/TrackFeatures.hpp" +#include "database/TrackList.hpp" #include "som/DataNormalizer.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" diff --git a/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp b/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp index 5dd0032a..08372345 100644 --- a/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp +++ b/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp @@ -23,7 +23,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "som/Network.hpp" namespace Recommendation { diff --git a/src/libs/recommendation/include/recommendation/IEngine.hpp b/src/libs/recommendation/include/recommendation/IEngine.hpp index d0a4218c..26f180bc 100644 --- a/src/libs/recommendation/include/recommendation/IEngine.hpp +++ b/src/libs/recommendation/include/recommendation/IEngine.hpp @@ -21,8 +21,8 @@ #include #include - -#include "lmscore/database/Types.hpp" +#include +#include "database/Types.hpp" #include "utils/EnumSet.hpp" namespace Database diff --git a/src/libs/scanner/CMakeLists.txt b/src/libs/scanner/CMakeLists.txt index 6dcbfebc..5f1467b8 100644 --- a/src/libs/scanner/CMakeLists.txt +++ b/src/libs/scanner/CMakeLists.txt @@ -14,7 +14,7 @@ target_include_directories(lmsscanner PRIVATE ) target_link_libraries(lmsscanner PRIVATE - lmscore + lmsdatabase lmsmetadata lmsrecommendation lmsutils diff --git a/src/libs/scanner/impl/Scanner.cpp b/src/libs/scanner/impl/Scanner.cpp index e8563f00..125324dc 100644 --- a/src/libs/scanner/impl/Scanner.cpp +++ b/src/libs/scanner/impl/Scanner.cpp @@ -24,13 +24,13 @@ #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/ScanSettings.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackArtistLink.hpp" -#include "lmscore/database/TrackFeatures.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Release.hpp" +#include "database/ScanSettings.hpp" +#include "database/Track.hpp" +#include "database/TrackArtistLink.hpp" +#include "database/TrackFeatures.hpp" #include "metadata/TagLibParser.hpp" #include "recommendation/IEngine.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/scanner/impl/Scanner.hpp b/src/libs/scanner/impl/Scanner.hpp index 7469cb11..76aeb819 100644 --- a/src/libs/scanner/impl/Scanner.hpp +++ b/src/libs/scanner/impl/Scanner.hpp @@ -30,9 +30,9 @@ #include -#include "lmscore/database/Types.hpp" -#include "lmscore/database/ScanSettings.hpp" -#include "lmscore/database/Session.hpp" +#include "database/Types.hpp" +#include "database/ScanSettings.hpp" +#include "database/Session.hpp" #include "metadata/IParser.hpp" #include "scanner/IScanner.hpp" #include "utils/Path.hpp" diff --git a/src/libs/scanner/include/scanner/ScannerStats.hpp b/src/libs/scanner/include/scanner/ScannerStats.hpp index 440cf63c..5528ce8b 100644 --- a/src/libs/scanner/include/scanner/ScannerStats.hpp +++ b/src/libs/scanner/include/scanner/ScannerStats.hpp @@ -24,7 +24,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Scanner { diff --git a/src/libs/scrobbling/CMakeLists.txt b/src/libs/scrobbling/CMakeLists.txt index 6b26570c..d69a6f5a 100644 --- a/src/libs/scrobbling/CMakeLists.txt +++ b/src/libs/scrobbling/CMakeLists.txt @@ -17,7 +17,7 @@ target_include_directories(lmsscrobbling PRIVATE ) target_link_libraries(lmsscrobbling PRIVATE - lmscore + lmsutils ) target_link_libraries(lmsscrobbling PUBLIC diff --git a/src/libs/scrobbling/impl/Scrobbling.cpp b/src/libs/scrobbling/impl/Scrobbling.cpp index 39f3b67d..b1a91936 100644 --- a/src/libs/scrobbling/impl/Scrobbling.cpp +++ b/src/libs/scrobbling/impl/Scrobbling.cpp @@ -19,13 +19,13 @@ #include "Scrobbling.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" #include "internal/InternalScrobbler.hpp" #include "listenbrainz/ListenBrainzScrobbler.hpp" diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp index eb71a438..e5b23d51 100644 --- a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp @@ -19,11 +19,11 @@ #include "InternalScrobbler.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Db.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" namespace Scrobbling diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index 12660e0a..08202d9e 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -24,13 +24,13 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index c4522501..8196da8b 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -25,13 +25,13 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" #include "scrobbling/Exception.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index f1043c69..e71a8ae4 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -26,7 +26,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "scrobbling/Listen.hpp" namespace Database diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/scrobbling/impl/listenbrainz/Utils.cpp index 0cda3eb1..ab293954 100644 --- a/src/libs/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/Utils.cpp @@ -21,9 +21,9 @@ #include -#include "lmscore/database/Session.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" static constexpr std::string_view historyTracklistName {"__scrobbler_listenbrainz_history__"}; diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/scrobbling/impl/listenbrainz/Utils.hpp index 206fa527..6b2d1271 100644 --- a/src/libs/scrobbling/impl/listenbrainz/Utils.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/Utils.hpp @@ -22,7 +22,7 @@ #include #include "utils/UUID.hpp" -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp index 4937494d..cda27ca1 100644 --- a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp +++ b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp @@ -29,7 +29,7 @@ #include #include "scrobbling/Listen.hpp" -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/libs/scrobbling/include/scrobbling/Listen.hpp b/src/libs/scrobbling/include/scrobbling/Listen.hpp index e0d71cba..ae80dbac 100644 --- a/src/libs/scrobbling/include/scrobbling/Listen.hpp +++ b/src/libs/scrobbling/include/scrobbling/Listen.hpp @@ -21,7 +21,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Scrobbling { diff --git a/src/libs/subsonic/impl/ParameterParsing.hpp b/src/libs/subsonic/impl/ParameterParsing.hpp index bd2f536c..bb8a91d3 100644 --- a/src/libs/subsonic/impl/ParameterParsing.hpp +++ b/src/libs/subsonic/impl/ParameterParsing.hpp @@ -20,7 +20,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/String.hpp" #include "SubsonicResponse.hpp" diff --git a/src/libs/subsonic/impl/RequestContext.hpp b/src/libs/subsonic/impl/RequestContext.hpp index d15a29ca..9d73bcb4 100644 --- a/src/libs/subsonic/impl/RequestContext.hpp +++ b/src/libs/subsonic/impl/RequestContext.hpp @@ -23,7 +23,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "ClientInfo.hpp" #include "ProtocolVersion.hpp" diff --git a/src/libs/subsonic/impl/Stream.cpp b/src/libs/subsonic/impl/Stream.cpp index 610cd24c..d3d181ed 100644 --- a/src/libs/subsonic/impl/Stream.cpp +++ b/src/libs/subsonic/impl/Stream.cpp @@ -22,9 +22,9 @@ #include "av/TranscodeParameters.hpp" #include "av/TranscodeResourceHandlerCreator.hpp" #include "av/Types.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/User.hpp" #include "utils/IResourceHandler.hpp" #include "utils/Logger.hpp" #include "utils/FileResourceHandlerCreator.hpp" diff --git a/src/libs/subsonic/impl/SubsonicId.hpp b/src/libs/subsonic/impl/SubsonicId.hpp index 2ff87309..0ac2a16a 100644 --- a/src/libs/subsonic/impl/SubsonicId.hpp +++ b/src/libs/subsonic/impl/SubsonicId.hpp @@ -19,7 +19,7 @@ #pragma once -#include "lmscore/database/Types.hpp" +#include "database/Types.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 d15aeba3..866cccf9 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -29,15 +29,15 @@ #include "lmscore/auth/IPasswordService.hpp" #include "lmscore/auth/IEnvService.hpp" #include "lmscore/cover/ICoverService.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackBookmark.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackBookmark.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" #include "recommendation/IEngine.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/IConfig.hpp" diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index a5c2c344..2d549f41 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -24,7 +24,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "ClientInfo.hpp" #include "RequestContext.hpp" diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 8ebbd485..7ae3a787 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -29,8 +29,8 @@ #include "lmscore/auth/IPasswordService.hpp" #include "lmscore/auth/IEnvService.hpp" #include "lmscore/cover/ICoverService.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Session.hpp" +#include "database/Db.hpp" +#include "database/Session.hpp" #include "scanner/IScanner.hpp" #include "recommendation/IEngine.hpp" #include "subsonic/SubsonicResource.hpp" diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 8158d63f..6f406cac 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -30,8 +30,8 @@ #include "lmscore/auth/IAuthTokenService.hpp" #include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/Auth.hpp b/src/lms/ui/Auth.hpp index dc893223..5fda238b 100644 --- a/src/lms/ui/Auth.hpp +++ b/src/lms/ui/Auth.hpp @@ -21,18 +21,19 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" -namespace UserInterface +namespace UserInterface { + +std::optional +processAuthToken(const Wt::WEnvironment& env); + +class Auth : public Wt::WTemplateFormView { - std::optional - processAuthToken(const Wt::WEnvironment& env); + public: + Auth(); - class Auth : public Wt::WTemplateFormView - { - public: - Auth(); + Wt::Signal userLoggedIn; +}; - Wt::Signal userLoggedIn; - }; } // namespace UserInterface diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 7f6b059f..c68012fc 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -31,12 +31,12 @@ #include "lmscore/auth/IEnvService.hpp" #include "lmscore/auth/IPasswordService.hpp" #include "lmscore/cover/ICoverService.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/LmsApplicationException.hpp b/src/lms/ui/LmsApplicationException.hpp index 2bb07492..0671a6c4 100644 --- a/src/lms/ui/LmsApplicationException.hpp +++ b/src/lms/ui/LmsApplicationException.hpp @@ -19,7 +19,7 @@ #pragma once -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/Exception.hpp" namespace UserInterface { diff --git a/src/lms/ui/LmsApplicationManager.hpp b/src/lms/ui/LmsApplicationManager.hpp index a8f61d1e..1d7761f7 100644 --- a/src/lms/ui/LmsApplicationManager.hpp +++ b/src/lms/ui/LmsApplicationManager.hpp @@ -25,7 +25,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/LmsTheme.hpp b/src/lms/ui/LmsTheme.hpp index 3e354b1e..10ee14c2 100644 --- a/src/lms/ui/LmsTheme.hpp +++ b/src/lms/ui/LmsTheme.hpp @@ -24,7 +24,7 @@ #include #include -#include "lmscore/database/User.hpp" +#include "database/User.hpp" namespace UserInterface { diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index 96cb31f2..4ee65e06 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -25,13 +25,13 @@ #include "utils/Logger.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/Types.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" +#include "database/Types.hpp" +#include "database/User.hpp" #include "resource/CoverResource.hpp" #include "resource/AudioTranscodeResource.hpp" diff --git a/src/lms/ui/MediaPlayer.hpp b/src/lms/ui/MediaPlayer.hpp index 7cb028c9..d4f1700b 100644 --- a/src/lms/ui/MediaPlayer.hpp +++ b/src/lms/ui/MediaPlayer.hpp @@ -27,7 +27,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 49c7ab53..554b34d2 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -22,12 +22,12 @@ #include #include -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Cluster.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" #include "recommendation/IEngine.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" diff --git a/src/lms/ui/PlayQueue.hpp b/src/lms/ui/PlayQueue.hpp index a8e14046..1bbb996a 100644 --- a/src/lms/ui/PlayQueue.hpp +++ b/src/lms/ui/PlayQueue.hpp @@ -25,7 +25,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace Similarity diff --git a/src/lms/ui/PlayQueueAction.hpp b/src/lms/ui/PlayQueueAction.hpp index aa52bddc..fffd8133 100644 --- a/src/lms/ui/PlayQueueAction.hpp +++ b/src/lms/ui/PlayQueueAction.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 9aecceda..d2cb3a95 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -35,7 +35,7 @@ #include "common/ValueStringModel.hpp" #include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/database/Session.hpp" +#include "database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/DatabaseSettingsView.cpp b/src/lms/ui/admin/DatabaseSettingsView.cpp index cef99be5..d410b4cd 100644 --- a/src/lms/ui/admin/DatabaseSettingsView.cpp +++ b/src/lms/ui/admin/DatabaseSettingsView.cpp @@ -26,9 +26,9 @@ #include #include -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/ScanSettings.hpp" -#include "lmscore/database/Session.hpp" +#include "database/Cluster.hpp" +#include "database/ScanSettings.hpp" +#include "database/Session.hpp" #include "scanner/IScanner.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index b3ba29da..3ca4d184 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -25,8 +25,8 @@ #include #include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index bd12f280..f01cfc71 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -28,8 +28,8 @@ #include #include -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "scanner/IScanner.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index 874442cd..b0f99755 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -28,8 +28,8 @@ #include #include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/database/User.hpp" -#include "lmscore/database/Session.hpp" +#include "database/User.hpp" +#include "database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 358021bf..8f26e470 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -24,8 +24,8 @@ #include #include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/database/User.hpp" -#include "lmscore/database/Session.hpp" +#include "database/User.hpp" +#include "database/Session.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/common/LoginNameValidator.cpp b/src/lms/ui/common/LoginNameValidator.cpp index a7767364..d1574d3f 100644 --- a/src/lms/ui/common/LoginNameValidator.cpp +++ b/src/lms/ui/common/LoginNameValidator.cpp @@ -20,7 +20,7 @@ #include "LoginNameValidator.hpp" #include -#include "lmscore/database/User.hpp" +#include "database/User.hpp" namespace UserInterface { diff --git a/src/lms/ui/common/PasswordValidator.hpp b/src/lms/ui/common/PasswordValidator.hpp index 337341bb..a3efe8e6 100644 --- a/src/lms/ui/common/PasswordValidator.hpp +++ b/src/lms/ui/common/PasswordValidator.hpp @@ -22,6 +22,7 @@ #include #include +#include "database/Types.hpp" #include "lmscore/auth/Types.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 4a698e36..0c73aa3e 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -19,9 +19,9 @@ #include "ArtistCollector.hpp" -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/User.hpp" -#include "lmscore/database/TrackList.hpp" +#include "database/Artist.hpp" +#include "database/User.hpp" +#include "database/TrackList.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ArtistCollector.hpp b/src/lms/ui/explore/ArtistCollector.hpp index 98c6008c..05734af0 100644 --- a/src/lms/ui/explore/ArtistCollector.hpp +++ b/src/lms/ui/explore/ArtistCollector.hpp @@ -24,7 +24,7 @@ #include "DatabaseCollectorBase.hpp" -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ArtistListHelpers.cpp b/src/lms/ui/explore/ArtistListHelpers.cpp index 2187c585..85adb362 100644 --- a/src/lms/ui/explore/ArtistListHelpers.cpp +++ b/src/lms/ui/explore/ArtistListHelpers.cpp @@ -20,7 +20,7 @@ #include -#include "lmscore/database/Artist.hpp" +#include "database/Artist.hpp" #include "LmsApplication.hpp" namespace UserInterface::ArtistListHelpers diff --git a/src/lms/ui/explore/ArtistListHelpers.hpp b/src/lms/ui/explore/ArtistListHelpers.hpp index 3f20d107..02659bf7 100644 --- a/src/lms/ui/explore/ArtistListHelpers.hpp +++ b/src/lms/ui/explore/ArtistListHelpers.hpp @@ -23,7 +23,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 0ea8c110..eb6f1d41 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -25,13 +25,13 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/ScanSettings.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/User.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Release.hpp" +#include "database/ScanSettings.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/User.hpp" #include "recommendation/IEngine.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index bc0b1cac..18b3894c 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -25,7 +25,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index de06b920..3d71b94c 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -21,8 +21,8 @@ #include -#include "lmscore/database/Session.hpp" -#include "lmscore/database/TrackArtistLink.hpp" +#include "database/Session.hpp" +#include "database/TrackArtistLink.hpp" #include "utils/EnumSet.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/explore/ArtistsView.hpp b/src/lms/ui/explore/ArtistsView.hpp index 44ffaea0..f7e61e22 100644 --- a/src/lms/ui/explore/ArtistsView.hpp +++ b/src/lms/ui/explore/ArtistsView.hpp @@ -25,7 +25,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "ArtistCollector.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/DatabaseCollectorBase.hpp b/src/lms/ui/explore/DatabaseCollectorBase.hpp index 677a2746..224f7657 100644 --- a/src/lms/ui/explore/DatabaseCollectorBase.hpp +++ b/src/lms/ui/explore/DatabaseCollectorBase.hpp @@ -25,7 +25,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/explore/Explore.cpp b/src/lms/ui/explore/Explore.cpp index 16dab2e2..8c821fdb 100644 --- a/src/lms/ui/explore/Explore.cpp +++ b/src/lms/ui/explore/Explore.cpp @@ -23,10 +23,10 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "utils/Logger.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/Explore.hpp b/src/lms/ui/explore/Explore.hpp index 7bd4dd8c..66815002 100644 --- a/src/lms/ui/explore/Explore.hpp +++ b/src/lms/ui/explore/Explore.hpp @@ -21,7 +21,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface { diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp index 2749373e..f8ef1c74 100644 --- a/src/lms/ui/explore/Filters.cpp +++ b/src/lms/ui/explore/Filters.cpp @@ -24,8 +24,8 @@ #include #include -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Session.hpp" +#include "database/Cluster.hpp" +#include "database/Session.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/Filters.hpp b/src/lms/ui/explore/Filters.hpp index ce4abab9..079d52c2 100644 --- a/src/lms/ui/explore/Filters.hpp +++ b/src/lms/ui/explore/Filters.hpp @@ -24,7 +24,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index f6939725..c5671f54 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -21,10 +21,10 @@ #include -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" -#include "lmscore/database/TrackList.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" +#include "database/TrackList.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleaseListHelpers.cpp b/src/lms/ui/explore/ReleaseListHelpers.cpp index e374b6fb..71c5daa5 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.cpp +++ b/src/lms/ui/explore/ReleaseListHelpers.cpp @@ -23,8 +23,8 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" #include "resource/CoverResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/ReleaseListHelpers.hpp b/src/lms/ui/explore/ReleaseListHelpers.hpp index 65219f1f..3aad6698 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.hpp +++ b/src/lms/ui/explore/ReleaseListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp index 88482867..7224683f 100644 --- a/src/lms/ui/explore/ReleasePopup.cpp +++ b/src/lms/ui/explore/ReleasePopup.cpp @@ -21,9 +21,9 @@ #include -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/User.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/ReleasePopup.hpp b/src/lms/ui/explore/ReleasePopup.hpp index 393eb072..5ffb4f48 100644 --- a/src/lms/ui/explore/ReleasePopup.hpp +++ b/src/lms/ui/explore/ReleasePopup.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index 2a4cd6d0..15334246 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -25,11 +25,11 @@ #include #include -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/ScanSettings.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Cluster.hpp" +#include "database/Release.hpp" +#include "database/ScanSettings.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "recommendation/IEngine.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/explore/ReleaseView.hpp b/src/lms/ui/explore/ReleaseView.hpp index aba128b1..d3700eaa 100644 --- a/src/lms/ui/explore/ReleaseView.hpp +++ b/src/lms/ui/explore/ReleaseView.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index 71e8a608..93c56428 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -23,7 +23,7 @@ #include #include -#include "lmscore/database/Session.hpp" +#include "database/Session.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "ReleaseListHelpers.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleasesView.hpp b/src/lms/ui/explore/ReleasesView.hpp index f8e9578f..f2a9b91b 100644 --- a/src/lms/ui/explore/ReleasesView.hpp +++ b/src/lms/ui/explore/ReleasesView.hpp @@ -24,7 +24,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" #include "ReleaseCollector.hpp" diff --git a/src/lms/ui/explore/SearchView.cpp b/src/lms/ui/explore/SearchView.cpp index 143f4c7f..90a950e8 100644 --- a/src/lms/ui/explore/SearchView.cpp +++ b/src/lms/ui/explore/SearchView.cpp @@ -25,10 +25,10 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "common/LoadingIndicator.hpp" diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 22eb6013..77106931 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -21,10 +21,10 @@ #include -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/TrackList.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" #include "scrobbling/IScrobbling.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index 1687bd82..ba36cb9d 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -24,9 +24,9 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" +#include "database/Track.hpp" #include "resource/DownloadResource.hpp" #include "resource/CoverResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/TrackListHelpers.hpp b/src/lms/ui/explore/TrackListHelpers.hpp index 94bcc1e6..2e3ea306 100644 --- a/src/lms/ui/explore/TrackListHelpers.hpp +++ b/src/lms/ui/explore/TrackListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/TrackPopup.cpp b/src/lms/ui/explore/TrackPopup.cpp index 143ea92e..8884347c 100644 --- a/src/lms/ui/explore/TrackPopup.cpp +++ b/src/lms/ui/explore/TrackPopup.cpp @@ -21,9 +21,9 @@ #include -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/User.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/TrackPopup.hpp b/src/lms/ui/explore/TrackPopup.hpp index 4b745f73..1597d994 100644 --- a/src/lms/ui/explore/TrackPopup.hpp +++ b/src/lms/ui/explore/TrackPopup.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index 43bd3c0e..daec0fcd 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -23,7 +23,7 @@ #include #include -#include "lmscore/database/Session.hpp" +#include "database/Session.hpp" #include "utils/Logger.hpp" #include "common/InfiniteScrollingContainer.hpp" diff --git a/src/lms/ui/explore/TracksView.hpp b/src/lms/ui/explore/TracksView.hpp index 12fcb897..ac011e01 100644 --- a/src/lms/ui/explore/TracksView.hpp +++ b/src/lms/ui/explore/TracksView.hpp @@ -24,7 +24,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "PlayQueueAction.hpp" #include "TrackCollector.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index ad9d6c91..def318df 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -23,8 +23,8 @@ #include #include "av/IAudioFile.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "utils/FileResourceHandlerCreator.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.hpp b/src/lms/ui/resource/AudioFileResource.hpp index 76fbbe7a..fda116eb 100644 --- a/src/lms/ui/resource/AudioFileResource.hpp +++ b/src/lms/ui/resource/AudioFileResource.hpp @@ -21,7 +21,7 @@ #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/AudioTranscodeResource.cpp b/src/lms/ui/resource/AudioTranscodeResource.cpp index 7845f3c8..66ecac06 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.cpp +++ b/src/lms/ui/resource/AudioTranscodeResource.cpp @@ -25,9 +25,9 @@ #include "av/TranscodeParameters.hpp" #include "av/TranscodeResourceHandlerCreator.hpp" #include "av/Types.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" -#include "lmscore/database/User.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/resource/AudioTranscodeResource.hpp b/src/lms/ui/resource/AudioTranscodeResource.hpp index 4c9a594c..ef516e91 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.hpp +++ b/src/lms/ui/resource/AudioTranscodeResource.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace Database { diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index 4918b3c4..44d1189d 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -23,7 +23,7 @@ #include #include "lmscore/cover/ICoverService.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/resource/CoverResource.hpp b/src/lms/ui/resource/CoverResource.hpp index 2b5d3b84..1e8cb8ad 100644 --- a/src/lms/ui/resource/CoverResource.hpp +++ b/src/lms/ui/resource/CoverResource.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/DownloadResource.cpp b/src/lms/ui/resource/DownloadResource.cpp index 35061ef6..998096a5 100644 --- a/src/lms/ui/resource/DownloadResource.cpp +++ b/src/lms/ui/resource/DownloadResource.cpp @@ -26,10 +26,10 @@ #include #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Zipper.hpp" diff --git a/src/lms/ui/resource/DownloadResource.hpp b/src/lms/ui/resource/DownloadResource.hpp index 20e432cb..581a55ec 100644 --- a/src/lms/ui/resource/DownloadResource.hpp +++ b/src/lms/ui/resource/DownloadResource.hpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/database/Types.hpp" +#include "database/Types.hpp" #include "utils/Zipper.hpp" namespace UserInterface { diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index f6757c8b..8202497c 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -25,10 +25,10 @@ #include #include "lmscore/cover/ICoverService.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/tools/recommendation/CMakeLists.txt b/src/tools/recommendation/CMakeLists.txt index 5fd6b6a8..12f20ef0 100644 --- a/src/tools/recommendation/CMakeLists.txt +++ b/src/tools/recommendation/CMakeLists.txt @@ -4,7 +4,7 @@ add_executable(lms-recommendation ) target_link_libraries(lms-recommendation PRIVATE - lmscore + lmsdatabase lmsrecommendation Boost::program_options ) diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index 23c11d26..c88dcc36 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -24,12 +24,12 @@ #include -#include "lmscore/database/Artist.hpp" -#include "lmscore/database/Cluster.hpp" -#include "lmscore/database/Db.hpp" -#include "lmscore/database/Release.hpp" -#include "lmscore/database/Session.hpp" -#include "lmscore/database/Track.hpp" +#include "database/Artist.hpp" +#include "database/Cluster.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" #include "utils/IConfig.hpp" #include "utils/Service.hpp" #include "utils/StreamLogger.hpp" From ac65aeb0e363308f427a4cd8c872986c5f3026df Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 17 Oct 2021 21:09:04 +0200 Subject: [PATCH 20/49] Revert "Migrated auth service" This reverts commit 0f5c4870842bab897fac19ea8dd1bcd37f6d1c37. --- src/libs/CMakeLists.txt | 1 + src/libs/auth/CMakeLists.txt | 40 +++++++++++++++++++ .../auth => auth/impl}/AuthServiceBase.cpp | 0 .../auth => auth/impl}/AuthServiceBase.hpp | 0 .../auth => auth/impl}/AuthTokenService.cpp | 2 +- .../auth => auth/impl}/AuthTokenService.hpp | 2 +- .../impl/auth => auth/impl}/EnvService.cpp | 5 ++- .../auth => auth/impl}/LoginThrottler.cpp | 0 .../auth => auth/impl}/LoginThrottler.hpp | 0 .../impl}/PasswordServiceBase.cpp | 2 +- .../impl}/PasswordServiceBase.hpp | 4 +- .../http-headers/HttpHeadersEnvService.cpp | 0 .../http-headers/HttpHeadersEnvService.hpp | 4 +- .../internal/InternalPasswordService.cpp | 4 +- .../internal/InternalPasswordService.hpp | 4 +- .../impl}/pam/PAMPasswordService.cpp | 2 +- .../impl}/pam/PAMPasswordService.hpp | 4 +- .../include}/auth/IAuthTokenService.hpp | 0 .../include}/auth/IEnvService.hpp | 0 .../include}/auth/IPasswordService.hpp | 4 +- .../lmscore => auth/include}/auth/Types.hpp | 0 src/libs/lmscore/CMakeLists.txt | 19 +-------- .../lmscore/impl/{cover => }/CoverService.cpp | 0 .../lmscore/impl/{cover => }/CoverService.hpp | 2 +- .../{cover => services}/ICoverService.hpp | 0 src/libs/subsonic/impl/SubsonicResource.cpp | 6 +-- src/lms/main.cpp | 8 ++-- src/lms/ui/Auth.cpp | 4 +- src/lms/ui/LmsApplication.cpp | 6 +-- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 2 +- src/lms/ui/admin/UsersView.cpp | 2 +- src/lms/ui/common/PasswordValidator.cpp | 2 +- src/lms/ui/common/PasswordValidator.hpp | 2 +- src/lms/ui/resource/CoverResource.cpp | 2 +- src/tools/cover/LmsCover.cpp | 2 +- 37 files changed, 84 insertions(+), 55 deletions(-) create mode 100644 src/libs/auth/CMakeLists.txt rename src/libs/{lmscore/impl/auth => auth/impl}/AuthServiceBase.cpp (100%) rename src/libs/{lmscore/impl/auth => auth/impl}/AuthServiceBase.hpp (100%) rename src/libs/{lmscore/impl/auth => auth/impl}/AuthTokenService.cpp (99%) rename src/libs/{lmscore/impl/auth => auth/impl}/AuthTokenService.hpp (97%) rename src/libs/{lmscore/impl/auth => auth/impl}/EnvService.cpp (93%) rename src/libs/{lmscore/impl/auth => auth/impl}/LoginThrottler.cpp (100%) rename src/libs/{lmscore/impl/auth => auth/impl}/LoginThrottler.hpp (100%) rename src/libs/{lmscore/impl/auth => auth/impl}/PasswordServiceBase.cpp (98%) rename src/libs/{lmscore/impl/auth => auth/impl}/PasswordServiceBase.hpp (95%) rename src/libs/{lmscore/impl/auth => auth/impl}/http-headers/HttpHeadersEnvService.cpp (100%) rename src/libs/{lmscore/impl/auth => auth/impl}/http-headers/HttpHeadersEnvService.hpp (93%) rename src/libs/{lmscore/impl/auth => auth/impl}/internal/InternalPasswordService.cpp (98%) rename src/libs/{lmscore/impl/auth => auth/impl}/internal/InternalPasswordService.hpp (95%) rename src/libs/{lmscore/impl/auth => auth/impl}/pam/PAMPasswordService.cpp (99%) rename src/libs/{lmscore/impl/auth => auth/impl}/pam/PAMPasswordService.hpp (95%) rename src/libs/{lmscore/include/lmscore => auth/include}/auth/IAuthTokenService.hpp (100%) rename src/libs/{lmscore/include/lmscore => auth/include}/auth/IEnvService.hpp (100%) rename src/libs/{lmscore/include/lmscore => auth/include}/auth/IPasswordService.hpp (98%) rename src/libs/{lmscore/include/lmscore => auth/include}/auth/Types.hpp (100%) rename src/libs/lmscore/impl/{cover => }/CoverService.cpp (100%) rename src/libs/lmscore/impl/{cover => }/CoverService.hpp (98%) rename src/libs/lmscore/include/lmscore/{cover => services}/ICoverService.hpp (100%) diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 196b2a1b..728cc175 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -1,4 +1,5 @@ +add_subdirectory(auth) add_subdirectory(av) add_subdirectory(database) add_subdirectory(image) diff --git a/src/libs/auth/CMakeLists.txt b/src/libs/auth/CMakeLists.txt new file mode 100644 index 00000000..07400a6b --- /dev/null +++ b/src/libs/auth/CMakeLists.txt @@ -0,0 +1,40 @@ + +add_library(lmsauth SHARED + impl/AuthTokenService.cpp + impl/AuthServiceBase.cpp + impl/EnvService.cpp + impl/LoginThrottler.cpp + impl/PasswordServiceBase.cpp + impl/http-headers/HttpHeadersEnvService.cpp + impl/internal/InternalPasswordService.cpp + ) + +target_include_directories(lmsauth INTERFACE + include + ) + +target_include_directories(lmsauth PRIVATE + include + impl + ) + +target_link_libraries(lmsauth PRIVATE + lmsutils + lmsdatabase + ) + +target_link_libraries(lmsauth PUBLIC + pthread + Boost::system + Wt::Wt + ) + +if (USE_PAM) + target_compile_options(lmsauth PRIVATE "-DLMS_SUPPORT_PAM") + target_sources(lmsauth PRIVATE impl/pam/PAMPasswordService.cpp) + target_include_directories(lmsauth PRIVATE ${PAM_INCLUDE_DIR}) + target_link_libraries(lmsauth PRIVATE ${PAM_LIBRARIES}) +endif (USE_PAM) + +install(TARGETS lmsauth DESTINATION lib) + diff --git a/src/libs/lmscore/impl/auth/AuthServiceBase.cpp b/src/libs/auth/impl/AuthServiceBase.cpp similarity index 100% rename from src/libs/lmscore/impl/auth/AuthServiceBase.cpp rename to src/libs/auth/impl/AuthServiceBase.cpp diff --git a/src/libs/lmscore/impl/auth/AuthServiceBase.hpp b/src/libs/auth/impl/AuthServiceBase.hpp similarity index 100% rename from src/libs/lmscore/impl/auth/AuthServiceBase.hpp rename to src/libs/auth/impl/AuthServiceBase.hpp diff --git a/src/libs/lmscore/impl/auth/AuthTokenService.cpp b/src/libs/auth/impl/AuthTokenService.cpp similarity index 99% rename from src/libs/lmscore/impl/auth/AuthTokenService.cpp rename to src/libs/auth/impl/AuthTokenService.cpp index 05949870..f6f305b6 100644 --- a/src/libs/lmscore/impl/auth/AuthTokenService.cpp +++ b/src/libs/auth/impl/AuthTokenService.cpp @@ -23,7 +23,7 @@ #include #include -#include "lmscore/auth/Types.hpp" +#include "auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/lmscore/impl/auth/AuthTokenService.hpp b/src/libs/auth/impl/AuthTokenService.hpp similarity index 97% rename from src/libs/lmscore/impl/auth/AuthTokenService.hpp rename to src/libs/auth/impl/AuthTokenService.hpp index f1425117..8825a3d3 100644 --- a/src/libs/lmscore/impl/auth/AuthTokenService.hpp +++ b/src/libs/auth/impl/AuthTokenService.hpp @@ -21,7 +21,7 @@ #include -#include "lmscore/auth/IAuthTokenService.hpp" +#include "auth/IAuthTokenService.hpp" #include "AuthServiceBase.hpp" #include "LoginThrottler.hpp" diff --git a/src/libs/lmscore/impl/auth/EnvService.cpp b/src/libs/auth/impl/EnvService.cpp similarity index 93% rename from src/libs/lmscore/impl/auth/EnvService.cpp rename to src/libs/auth/impl/EnvService.cpp index 99046d40..12de9858 100644 --- a/src/libs/lmscore/impl/auth/EnvService.cpp +++ b/src/libs/auth/impl/EnvService.cpp @@ -17,8 +17,9 @@ * along with LMS. If not, see . */ -#include "lmscore/auth/IEnvService.hpp" -#include "lmscore/auth/Types.hpp" +#include "auth/IEnvService.hpp" + +#include "auth/Types.hpp" #include "http-headers/HttpHeadersEnvService.hpp" namespace Auth diff --git a/src/libs/lmscore/impl/auth/LoginThrottler.cpp b/src/libs/auth/impl/LoginThrottler.cpp similarity index 100% rename from src/libs/lmscore/impl/auth/LoginThrottler.cpp rename to src/libs/auth/impl/LoginThrottler.cpp diff --git a/src/libs/lmscore/impl/auth/LoginThrottler.hpp b/src/libs/auth/impl/LoginThrottler.hpp similarity index 100% rename from src/libs/lmscore/impl/auth/LoginThrottler.hpp rename to src/libs/auth/impl/LoginThrottler.hpp diff --git a/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp b/src/libs/auth/impl/PasswordServiceBase.cpp similarity index 98% rename from src/libs/lmscore/impl/auth/PasswordServiceBase.cpp rename to src/libs/auth/impl/PasswordServiceBase.cpp index e80d1b9b..3b230503 100644 --- a/src/libs/lmscore/impl/auth/PasswordServiceBase.cpp +++ b/src/libs/auth/impl/PasswordServiceBase.cpp @@ -27,7 +27,7 @@ #include "pam/PAMPasswordService.hpp" #endif // LMS_SUPPORT_PAM -#include "lmscore/auth/Types.hpp" +#include "auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/lmscore/impl/auth/PasswordServiceBase.hpp b/src/libs/auth/impl/PasswordServiceBase.hpp similarity index 95% rename from src/libs/lmscore/impl/auth/PasswordServiceBase.hpp rename to src/libs/auth/impl/PasswordServiceBase.hpp index 5c922f55..f575e4e1 100644 --- a/src/libs/lmscore/impl/auth/PasswordServiceBase.hpp +++ b/src/libs/auth/impl/PasswordServiceBase.hpp @@ -21,8 +21,8 @@ #include -#include "lmscore/auth/IPasswordService.hpp" -#include "auth/AuthServiceBase.hpp" +#include "auth/IPasswordService.hpp" +#include "AuthServiceBase.hpp" #include "LoginThrottler.hpp" namespace Database diff --git a/src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.cpp b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp similarity index 100% rename from src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.cpp rename to src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp diff --git a/src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.hpp b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp similarity index 93% rename from src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.hpp rename to src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp index 7cad1441..5c44b9fb 100644 --- a/src/libs/lmscore/impl/auth/http-headers/HttpHeadersEnvService.hpp +++ b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp @@ -19,8 +19,8 @@ #pragma once -#include "lmscore/auth/IEnvService.hpp" -#include "auth/AuthServiceBase.hpp" +#include "auth/IEnvService.hpp" +#include "AuthServiceBase.hpp" namespace Auth { diff --git a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp b/src/libs/auth/impl/internal/InternalPasswordService.cpp similarity index 98% rename from src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp rename to src/libs/auth/impl/internal/InternalPasswordService.cpp index 233fb9aa..96a6c34a 100644 --- a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.cpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.cpp @@ -20,8 +20,8 @@ #include "InternalPasswordService.hpp" #include -#include "lmscore/auth/IAuthTokenService.hpp" -#include "lmscore/auth/Types.hpp" +#include "auth/IAuthTokenService.hpp" +#include "auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp b/src/libs/auth/impl/internal/InternalPasswordService.hpp similarity index 95% rename from src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp rename to src/libs/auth/impl/internal/InternalPasswordService.hpp index 42b830f1..6c241f31 100644 --- a/src/libs/lmscore/impl/auth/internal/InternalPasswordService.hpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.hpp @@ -23,8 +23,8 @@ #include #include "database/User.hpp" -#include "auth/PasswordServiceBase.hpp" -#include "auth/LoginThrottler.hpp" +#include "PasswordServiceBase.hpp" +#include "LoginThrottler.hpp" namespace Auth { diff --git a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp b/src/libs/auth/impl/pam/PAMPasswordService.cpp similarity index 99% rename from src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp rename to src/libs/auth/impl/pam/PAMPasswordService.cpp index 708ce457..c5682f42 100644 --- a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.cpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.cpp @@ -26,7 +26,7 @@ #include #include -#include "lmscore/auth/Types.hpp" +#include "auth/Types.hpp" #include "database/Session.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.hpp b/src/libs/auth/impl/pam/PAMPasswordService.hpp similarity index 95% rename from src/libs/lmscore/impl/auth/pam/PAMPasswordService.hpp rename to src/libs/auth/impl/pam/PAMPasswordService.hpp index 9c88c955..2de5bc82 100644 --- a/src/libs/lmscore/impl/auth/pam/PAMPasswordService.hpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.hpp @@ -19,7 +19,9 @@ #pragma once -#include "auth/PasswordServiceBase.hpp" +#include + +#include "PasswordServiceBase.hpp" namespace Auth { diff --git a/src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp b/src/libs/auth/include/auth/IAuthTokenService.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/auth/IAuthTokenService.hpp rename to src/libs/auth/include/auth/IAuthTokenService.hpp diff --git a/src/libs/lmscore/include/lmscore/auth/IEnvService.hpp b/src/libs/auth/include/auth/IEnvService.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/auth/IEnvService.hpp rename to src/libs/auth/include/auth/IEnvService.hpp diff --git a/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp b/src/libs/auth/include/auth/IPasswordService.hpp similarity index 98% rename from src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp rename to src/libs/auth/include/auth/IPasswordService.hpp index 86edc868..f197ae16 100644 --- a/src/libs/lmscore/include/lmscore/auth/IPasswordService.hpp +++ b/src/libs/auth/include/auth/IPasswordService.hpp @@ -21,11 +21,11 @@ #include -#include #include #include -#include "lmscore/auth/Types.hpp" +#include +#include "auth/Types.hpp" #include "database/Types.hpp" namespace Database diff --git a/src/libs/lmscore/include/lmscore/auth/Types.hpp b/src/libs/auth/include/auth/Types.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/auth/Types.hpp rename to src/libs/auth/include/auth/Types.hpp diff --git a/src/libs/lmscore/CMakeLists.txt b/src/libs/lmscore/CMakeLists.txt index 4403380a..358df97a 100644 --- a/src/libs/lmscore/CMakeLists.txt +++ b/src/libs/lmscore/CMakeLists.txt @@ -1,13 +1,6 @@ add_library(lmscore SHARED - impl/auth/AuthTokenService.cpp - impl/auth/AuthServiceBase.cpp - impl/auth/EnvService.cpp - impl/auth/LoginThrottler.cpp - impl/auth/PasswordServiceBase.cpp - impl/auth/http-headers/HttpHeadersEnvService.cpp - impl/auth/internal/InternalPasswordService.cpp - impl/cover/CoverService.cpp + impl/CoverService.cpp ) target_include_directories(lmscore INTERFACE @@ -21,6 +14,7 @@ target_include_directories(lmscore PRIVATE target_link_libraries(lmscore PRIVATE lmsav + lmsimage ) target_link_libraries(lmscore PUBLIC @@ -28,16 +22,7 @@ target_link_libraries(lmscore PUBLIC lmsimage lmsutils std::filesystem - Wt::Wt ) -if (USE_PAM) - target_compile_options(lmscore PRIVATE "-DLMS_SUPPORT_PAM") - target_sources(lmscore PRIVATE impl/auth/pam/PAMPasswordService.cpp) - target_include_directories(lmscore PRIVATE ${PAM_INCLUDE_DIR}) - target_link_libraries(lmscore PRIVATE ${PAM_LIBRARIES}) -endif (USE_PAM) - - install(TARGETS lmscore DESTINATION lib) diff --git a/src/libs/lmscore/impl/cover/CoverService.cpp b/src/libs/lmscore/impl/CoverService.cpp similarity index 100% rename from src/libs/lmscore/impl/cover/CoverService.cpp rename to src/libs/lmscore/impl/CoverService.cpp diff --git a/src/libs/lmscore/impl/cover/CoverService.hpp b/src/libs/lmscore/impl/CoverService.hpp similarity index 98% rename from src/libs/lmscore/impl/cover/CoverService.hpp rename to src/libs/lmscore/impl/CoverService.hpp index f1a845ac..a740673e 100644 --- a/src/libs/lmscore/impl/cover/CoverService.hpp +++ b/src/libs/lmscore/impl/CoverService.hpp @@ -29,7 +29,7 @@ #include #include -#include "lmscore/cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "image/IEncodedImage.hpp" #include "database/Types.hpp" diff --git a/src/libs/lmscore/include/lmscore/cover/ICoverService.hpp b/src/libs/lmscore/include/lmscore/services/ICoverService.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/cover/ICoverService.hpp rename to src/libs/lmscore/include/lmscore/services/ICoverService.hpp diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 866cccf9..888eadf6 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -26,9 +26,9 @@ #include -#include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/auth/IEnvService.hpp" -#include "lmscore/cover/ICoverService.hpp" +#include "auth/IPasswordService.hpp" +#include "auth/IEnvService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 7ae3a787..2d6230cf 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -25,10 +25,10 @@ #include #include -#include "lmscore/auth/IAuthTokenService.hpp" -#include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/auth/IEnvService.hpp" -#include "lmscore/cover/ICoverService.hpp" +#include "auth/IAuthTokenService.hpp" +#include "auth/IPasswordService.hpp" +#include "auth/IEnvService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Db.hpp" #include "database/Session.hpp" #include "scanner/IScanner.hpp" diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 6f406cac..29414b43 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -28,8 +28,8 @@ #include #include -#include "lmscore/auth/IAuthTokenService.hpp" -#include "lmscore/auth/IPasswordService.hpp" +#include "auth/IAuthTokenService.hpp" +#include "auth/IPasswordService.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index c68012fc..8eb688e2 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -28,9 +28,9 @@ #include #include -#include "lmscore/auth/IEnvService.hpp" -#include "lmscore/auth/IPasswordService.hpp" -#include "lmscore/cover/ICoverService.hpp" +#include "auth/IEnvService.hpp" +#include "auth/IPasswordService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index d2cb3a95..66105842 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -34,7 +34,7 @@ #include "common/UUIDValidator.hpp" #include "common/ValueStringModel.hpp" -#include "lmscore/auth/IPasswordService.hpp" +#include "auth/IPasswordService.hpp" #include "database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index 3ca4d184..b37645b3 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -24,7 +24,7 @@ #include #include -#include "lmscore/auth/IPasswordService.hpp" +#include "auth/IPasswordService.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index b0f99755..16374d80 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -27,7 +27,7 @@ #include -#include "lmscore/auth/IPasswordService.hpp" +#include "auth/IPasswordService.hpp" #include "database/User.hpp" #include "database/Session.hpp" #include "utils/IConfig.hpp" diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 8f26e470..50bc58e7 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -23,7 +23,7 @@ #include #include -#include "lmscore/auth/IPasswordService.hpp" +#include "auth/IPasswordService.hpp" #include "database/User.hpp" #include "database/Session.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/common/PasswordValidator.cpp b/src/lms/ui/common/PasswordValidator.cpp index 3a559c71..2df00c3f 100644 --- a/src/lms/ui/common/PasswordValidator.cpp +++ b/src/lms/ui/common/PasswordValidator.cpp @@ -21,7 +21,7 @@ #include -#include "lmscore/auth/IPasswordService.hpp" +#include "auth/IPasswordService.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/common/PasswordValidator.hpp b/src/lms/ui/common/PasswordValidator.hpp index a3efe8e6..d504dbc1 100644 --- a/src/lms/ui/common/PasswordValidator.hpp +++ b/src/lms/ui/common/PasswordValidator.hpp @@ -23,7 +23,7 @@ #include #include "database/Types.hpp" -#include "lmscore/auth/Types.hpp" +#include "auth/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index 44d1189d..c13c5996 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index 8202497c..d2a5e784 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -24,7 +24,7 @@ #include -#include "lmscore/cover/ICoverService.hpp" +#include "lmscore/services/ICoverService.hpp" #include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" From fe298e10d997fce59fa16c05fab5a90153e9c85a Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 17 Oct 2021 21:21:30 +0200 Subject: [PATCH 21/49] Another attempt: regroup lms services --- src/libs/CMakeLists.txt | 2 +- src/libs/lmscore/CMakeLists.txt | 28 ------------------- src/libs/services/CMakeLists.txt | 1 + src/libs/services/cover/CMakeLists.txt | 28 +++++++++++++++++++ .../cover}/impl/CoverService.cpp | 0 .../cover}/impl/CoverService.hpp | 2 +- .../include/services/cover}/ICoverService.hpp | 0 src/libs/subsonic/CMakeLists.txt | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 2 +- src/lms/CMakeLists.txt | 2 +- src/lms/main.cpp | 2 +- src/lms/ui/LmsApplication.cpp | 2 +- src/lms/ui/resource/CoverResource.cpp | 2 +- src/tools/cover/CMakeLists.txt | 2 +- src/tools/cover/LmsCover.cpp | 2 +- 15 files changed, 39 insertions(+), 38 deletions(-) delete mode 100644 src/libs/lmscore/CMakeLists.txt create mode 100644 src/libs/services/CMakeLists.txt create mode 100644 src/libs/services/cover/CMakeLists.txt rename src/libs/{lmscore => services/cover}/impl/CoverService.cpp (100%) rename src/libs/{lmscore => services/cover}/impl/CoverService.hpp (98%) rename src/libs/{lmscore/include/lmscore/services => services/cover/include/services/cover}/ICoverService.hpp (100%) diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 728cc175..6f98e19a 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -3,11 +3,11 @@ add_subdirectory(auth) add_subdirectory(av) add_subdirectory(database) add_subdirectory(image) -add_subdirectory(lmscore) add_subdirectory(metadata) add_subdirectory(recommendation) add_subdirectory(scanner) add_subdirectory(scrobbling) +add_subdirectory(services) add_subdirectory(som) add_subdirectory(subsonic) add_subdirectory(utils) diff --git a/src/libs/lmscore/CMakeLists.txt b/src/libs/lmscore/CMakeLists.txt deleted file mode 100644 index 358df97a..00000000 --- a/src/libs/lmscore/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ - -add_library(lmscore SHARED - impl/CoverService.cpp - ) - -target_include_directories(lmscore INTERFACE - include - ) - -target_include_directories(lmscore PRIVATE - include - impl - ) - -target_link_libraries(lmscore PRIVATE - lmsav - lmsimage - ) - -target_link_libraries(lmscore PUBLIC - lmsdatabase - lmsimage - lmsutils - std::filesystem - ) - -install(TARGETS lmscore DESTINATION lib) - diff --git a/src/libs/services/CMakeLists.txt b/src/libs/services/CMakeLists.txt new file mode 100644 index 00000000..09e78dd4 --- /dev/null +++ b/src/libs/services/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(cover) diff --git a/src/libs/services/cover/CMakeLists.txt b/src/libs/services/cover/CMakeLists.txt new file mode 100644 index 00000000..41169c5d --- /dev/null +++ b/src/libs/services/cover/CMakeLists.txt @@ -0,0 +1,28 @@ + +add_library(lmsservice-cover SHARED + impl/CoverService.cpp + ) + +target_include_directories(lmsservice-cover INTERFACE + include + ) + +target_include_directories(lmsservice-cover PRIVATE + include + impl + ) + +target_link_libraries(lmsservice-cover PRIVATE + lmsav + lmsimage + ) + +target_link_libraries(lmsservice-cover PUBLIC + lmsdatabase + lmsimage + lmsutils + std::filesystem + ) + +install(TARGETS lmsservice-cover DESTINATION lib) + diff --git a/src/libs/lmscore/impl/CoverService.cpp b/src/libs/services/cover/impl/CoverService.cpp similarity index 100% rename from src/libs/lmscore/impl/CoverService.cpp rename to src/libs/services/cover/impl/CoverService.cpp diff --git a/src/libs/lmscore/impl/CoverService.hpp b/src/libs/services/cover/impl/CoverService.hpp similarity index 98% rename from src/libs/lmscore/impl/CoverService.hpp rename to src/libs/services/cover/impl/CoverService.hpp index a740673e..866ed8da 100644 --- a/src/libs/lmscore/impl/CoverService.hpp +++ b/src/libs/services/cover/impl/CoverService.hpp @@ -29,7 +29,7 @@ #include #include -#include "lmscore/services/ICoverService.hpp" +#include "services/cover/ICoverService.hpp" #include "image/IEncodedImage.hpp" #include "database/Types.hpp" diff --git a/src/libs/lmscore/include/lmscore/services/ICoverService.hpp b/src/libs/services/cover/include/services/cover/ICoverService.hpp similarity index 100% rename from src/libs/lmscore/include/lmscore/services/ICoverService.hpp rename to src/libs/services/cover/include/services/cover/ICoverService.hpp diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index aad488ca..d4f4b985 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -19,11 +19,11 @@ target_include_directories(lmssubsonic PRIVATE target_link_libraries(lmssubsonic PRIVATE lmsauth lmsav - lmscore lmsdatabase lmsrecommendation lmsscanner lmsscrobbling + lmsservice-cover lmsutils std::filesystem ) diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 888eadf6..38f811b0 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -28,7 +28,6 @@ #include "auth/IPasswordService.hpp" #include "auth/IEnvService.hpp" -#include "lmscore/services/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" @@ -40,6 +39,7 @@ #include "database/User.hpp" #include "recommendation/IEngine.hpp" #include "scrobbling/IScrobbling.hpp" +#include "services/cover/ICoverService.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt index beff7583..33dad4b1 100644 --- a/src/lms/CMakeLists.txt +++ b/src/lms/CMakeLists.txt @@ -51,11 +51,11 @@ target_include_directories(lms PRIVATE target_link_libraries(lms PRIVATE lmsav lmsauth - lmscore lmsdatabase lmsrecommendation lmsscanner lmsscrobbling + lmsservice-cover lmssubsonic lmsutils Wt::Wt diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 2d6230cf..c30dd110 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -28,7 +28,7 @@ #include "auth/IAuthTokenService.hpp" #include "auth/IPasswordService.hpp" #include "auth/IEnvService.hpp" -#include "lmscore/services/ICoverService.hpp" +#include "services/cover/ICoverService.hpp" #include "database/Db.hpp" #include "database/Session.hpp" #include "scanner/IScanner.hpp" diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 8eb688e2..e14207ca 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -30,7 +30,7 @@ #include "auth/IEnvService.hpp" #include "auth/IPasswordService.hpp" -#include "lmscore/services/ICoverService.hpp" +#include "services/cover/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index c13c5996..69ef9b19 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -22,7 +22,7 @@ #include #include -#include "lmscore/services/ICoverService.hpp" +#include "services/cover/ICoverService.hpp" #include "database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/tools/cover/CMakeLists.txt b/src/tools/cover/CMakeLists.txt index b695300f..b7131b97 100644 --- a/src/tools/cover/CMakeLists.txt +++ b/src/tools/cover/CMakeLists.txt @@ -4,7 +4,7 @@ add_executable(lms-cover ) target_link_libraries(lms-cover PRIVATE - lmscore + lmsservice-cover Boost::program_options ) diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index d2a5e784..a533187f 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -24,11 +24,11 @@ #include -#include "lmscore/services/ICoverService.hpp" #include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" +#include "services/cover/ICoverService.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" From a0489b2d94de40dbf97748a22d4d4de9307ae6c4 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 18 Oct 2021 20:39:47 +0200 Subject: [PATCH 22/49] Migrated scrobbling stuff --- src/libs/CMakeLists.txt | 8 ---- src/libs/services/CMakeLists.txt | 5 +++ src/libs/{ => services}/auth/CMakeLists.txt | 0 .../auth/impl/AuthServiceBase.cpp | 0 .../auth/impl/AuthServiceBase.hpp | 0 .../auth/impl/AuthTokenService.cpp | 0 .../auth/impl/AuthTokenService.hpp | 0 .../{ => services}/auth/impl/EnvService.cpp | 0 .../auth/impl/LoginThrottler.cpp | 0 .../auth/impl/LoginThrottler.hpp | 0 .../auth/impl/PasswordServiceBase.cpp | 0 .../auth/impl/PasswordServiceBase.hpp | 0 .../http-headers/HttpHeadersEnvService.cpp | 0 .../http-headers/HttpHeadersEnvService.hpp | 0 .../impl/internal/InternalPasswordService.cpp | 0 .../impl/internal/InternalPasswordService.hpp | 0 .../auth/impl/pam/PAMPasswordService.cpp | 0 .../auth/impl/pam/PAMPasswordService.hpp | 0 .../auth/include/auth/IAuthTokenService.hpp | 0 .../auth/include/auth/IEnvService.hpp | 0 .../auth/include/auth/IPasswordService.hpp | 0 .../auth/include/auth/Types.hpp | 0 .../{ => services}/database/CMakeLists.txt | 0 .../{ => services}/database/impl/Artist.cpp | 0 .../{ => services}/database/impl/Cluster.cpp | 0 src/libs/{ => services}/database/impl/Db.cpp | 0 .../{ => services}/database/impl/Release.cpp | 0 .../database/impl/ScanSettings.cpp | 0 .../{ => services}/database/impl/Session.cpp | 0 .../{ => services}/database/impl/SqlQuery.cpp | 0 .../{ => services}/database/impl/SqlQuery.hpp | 0 .../database/impl/StringViewTraits.hpp | 0 .../{ => services}/database/impl/Track.cpp | 0 .../database/impl/TrackArtistLink.cpp | 0 .../database/impl/TrackBookmark.cpp | 0 .../database/impl/TrackFeatures.cpp | 0 .../database/impl/TrackList.cpp | 0 .../{ => services}/database/impl/Traits.hpp | 0 .../{ => services}/database/impl/User.cpp | 0 .../{ => services}/database/impl/Utils.cpp | 0 .../{ => services}/database/impl/Utils.hpp | 0 .../database/include/database/Artist.hpp | 0 .../database/include/database/Cluster.hpp | 0 .../database/include/database/Db.hpp | 0 .../database/include/database/Release.hpp | 0 .../include/database/ScanSettings.hpp | 0 .../database/include/database/Session.hpp | 0 .../database/include/database/Track.hpp | 0 .../include/database/TrackArtistLink.hpp | 0 .../include/database/TrackBookmark.hpp | 0 .../include/database/TrackFeatures.hpp | 0 .../database/include/database/TrackList.hpp | 0 .../database/include/database/Types.hpp | 0 .../database/include/database/User.hpp | 0 .../{ => services}/database/test/Artist.cpp | 0 .../database/test/CMakeLists.txt | 0 .../{ => services}/database/test/Cluster.cpp | 0 .../{ => services}/database/test/Common.hpp | 0 .../database/test/DatabaseTest.cpp | 0 .../{ => services}/database/test/Release.cpp | 0 .../{ => services}/database/test/Track.cpp | 0 .../recommendation/CMakeLists.txt | 0 .../impl/ClustersEngineCreator.hpp | 0 .../recommendation/impl/Engine.cpp | 0 .../recommendation/impl/Engine.hpp | 0 .../impl/FeaturesEngineCreator.hpp | 0 .../impl/clusters/ClustersEngine.cpp | 0 .../impl/clusters/ClustersEngine.hpp | 0 .../impl/features/FeaturesDefs.cpp | 0 .../impl/features/FeaturesDefs.hpp | 0 .../impl/features/FeaturesEngine.cpp | 0 .../impl/features/FeaturesEngine.hpp | 0 .../impl/features/FeaturesEngineCache.cpp | 0 .../impl/features/FeaturesEngineCache.hpp | 0 .../include/recommendation/IEngine.hpp | 0 .../{ => services}/scanner/CMakeLists.txt | 0 .../scanner/impl/AcousticBrainzUtils.cpp | 0 .../scanner/impl/AcousticBrainzUtils.hpp | 0 .../{ => services}/scanner/impl/Scanner.cpp | 0 .../{ => services}/scanner/impl/Scanner.hpp | 0 .../scanner/impl/ScannerStats.cpp | 0 .../scanner/include/scanner/IScanner.hpp | 0 .../scanner/include/scanner/ScannerEvents.hpp | 0 .../scanner/include/scanner/ScannerStats.hpp | 0 .../{ => services}/scrobbling/CMakeLists.txt | 2 +- .../scrobbling/impl/IScrobbler.hpp | 2 +- .../scrobbling/impl/ScrobblingService.cpp} | 44 +++++++++---------- .../scrobbling/impl/ScrobblingService.hpp} | 6 +-- .../impl/internal/InternalScrobbler.cpp | 0 .../impl/internal/InternalScrobbler.hpp | 0 .../listenbrainz/FeedbackSynchronizer.hpp | 0 .../listenbrainz/ListenBrainzScrobbler.cpp | 0 .../listenbrainz/ListenBrainzScrobbler.hpp | 0 .../impl/listenbrainz/ListensSynchronizer.cpp | 2 +- .../impl/listenbrainz/ListensSynchronizer.hpp | 2 +- .../scrobbling/impl/listenbrainz/Utils.cpp | 0 .../scrobbling/impl/listenbrainz/Utils.hpp | 0 .../services}/scrobbling/Exception.hpp | 0 .../scrobbling/IScrobblingService.hpp} | 8 ++-- .../include/services}/scrobbling/Listen.hpp | 0 src/libs/subsonic/impl/SubsonicResource.cpp | 10 ++--- src/lms/main.cpp | 4 +- src/lms/ui/LmsApplication.cpp | 6 +-- src/lms/ui/explore/ArtistCollector.cpp | 4 +- src/lms/ui/explore/ReleaseCollector.cpp | 4 +- src/lms/ui/explore/TrackCollector.cpp | 4 +- 106 files changed, 54 insertions(+), 57 deletions(-) rename src/libs/{ => services}/auth/CMakeLists.txt (100%) rename src/libs/{ => services}/auth/impl/AuthServiceBase.cpp (100%) rename src/libs/{ => services}/auth/impl/AuthServiceBase.hpp (100%) rename src/libs/{ => services}/auth/impl/AuthTokenService.cpp (100%) rename src/libs/{ => services}/auth/impl/AuthTokenService.hpp (100%) rename src/libs/{ => services}/auth/impl/EnvService.cpp (100%) rename src/libs/{ => services}/auth/impl/LoginThrottler.cpp (100%) rename src/libs/{ => services}/auth/impl/LoginThrottler.hpp (100%) rename src/libs/{ => services}/auth/impl/PasswordServiceBase.cpp (100%) rename src/libs/{ => services}/auth/impl/PasswordServiceBase.hpp (100%) rename src/libs/{ => services}/auth/impl/http-headers/HttpHeadersEnvService.cpp (100%) rename src/libs/{ => services}/auth/impl/http-headers/HttpHeadersEnvService.hpp (100%) rename src/libs/{ => services}/auth/impl/internal/InternalPasswordService.cpp (100%) rename src/libs/{ => services}/auth/impl/internal/InternalPasswordService.hpp (100%) rename src/libs/{ => services}/auth/impl/pam/PAMPasswordService.cpp (100%) rename src/libs/{ => services}/auth/impl/pam/PAMPasswordService.hpp (100%) rename src/libs/{ => services}/auth/include/auth/IAuthTokenService.hpp (100%) rename src/libs/{ => services}/auth/include/auth/IEnvService.hpp (100%) rename src/libs/{ => services}/auth/include/auth/IPasswordService.hpp (100%) rename src/libs/{ => services}/auth/include/auth/Types.hpp (100%) rename src/libs/{ => services}/database/CMakeLists.txt (100%) rename src/libs/{ => services}/database/impl/Artist.cpp (100%) rename src/libs/{ => services}/database/impl/Cluster.cpp (100%) rename src/libs/{ => services}/database/impl/Db.cpp (100%) rename src/libs/{ => services}/database/impl/Release.cpp (100%) rename src/libs/{ => services}/database/impl/ScanSettings.cpp (100%) rename src/libs/{ => services}/database/impl/Session.cpp (100%) rename src/libs/{ => services}/database/impl/SqlQuery.cpp (100%) rename src/libs/{ => services}/database/impl/SqlQuery.hpp (100%) rename src/libs/{ => services}/database/impl/StringViewTraits.hpp (100%) rename src/libs/{ => services}/database/impl/Track.cpp (100%) rename src/libs/{ => services}/database/impl/TrackArtistLink.cpp (100%) rename src/libs/{ => services}/database/impl/TrackBookmark.cpp (100%) rename src/libs/{ => services}/database/impl/TrackFeatures.cpp (100%) rename src/libs/{ => services}/database/impl/TrackList.cpp (100%) rename src/libs/{ => services}/database/impl/Traits.hpp (100%) rename src/libs/{ => services}/database/impl/User.cpp (100%) rename src/libs/{ => services}/database/impl/Utils.cpp (100%) rename src/libs/{ => services}/database/impl/Utils.hpp (100%) rename src/libs/{ => services}/database/include/database/Artist.hpp (100%) rename src/libs/{ => services}/database/include/database/Cluster.hpp (100%) rename src/libs/{ => services}/database/include/database/Db.hpp (100%) rename src/libs/{ => services}/database/include/database/Release.hpp (100%) rename src/libs/{ => services}/database/include/database/ScanSettings.hpp (100%) rename src/libs/{ => services}/database/include/database/Session.hpp (100%) rename src/libs/{ => services}/database/include/database/Track.hpp (100%) rename src/libs/{ => services}/database/include/database/TrackArtistLink.hpp (100%) rename src/libs/{ => services}/database/include/database/TrackBookmark.hpp (100%) rename src/libs/{ => services}/database/include/database/TrackFeatures.hpp (100%) rename src/libs/{ => services}/database/include/database/TrackList.hpp (100%) rename src/libs/{ => services}/database/include/database/Types.hpp (100%) rename src/libs/{ => services}/database/include/database/User.hpp (100%) rename src/libs/{ => services}/database/test/Artist.cpp (100%) rename src/libs/{ => services}/database/test/CMakeLists.txt (100%) rename src/libs/{ => services}/database/test/Cluster.cpp (100%) rename src/libs/{ => services}/database/test/Common.hpp (100%) rename src/libs/{ => services}/database/test/DatabaseTest.cpp (100%) rename src/libs/{ => services}/database/test/Release.cpp (100%) rename src/libs/{ => services}/database/test/Track.cpp (100%) rename src/libs/{ => services}/recommendation/CMakeLists.txt (100%) rename src/libs/{ => services}/recommendation/impl/ClustersEngineCreator.hpp (100%) rename src/libs/{ => services}/recommendation/impl/Engine.cpp (100%) rename src/libs/{ => services}/recommendation/impl/Engine.hpp (100%) rename src/libs/{ => services}/recommendation/impl/FeaturesEngineCreator.hpp (100%) rename src/libs/{ => services}/recommendation/impl/clusters/ClustersEngine.cpp (100%) rename src/libs/{ => services}/recommendation/impl/clusters/ClustersEngine.hpp (100%) rename src/libs/{ => services}/recommendation/impl/features/FeaturesDefs.cpp (100%) rename src/libs/{ => services}/recommendation/impl/features/FeaturesDefs.hpp (100%) rename src/libs/{ => services}/recommendation/impl/features/FeaturesEngine.cpp (100%) rename src/libs/{ => services}/recommendation/impl/features/FeaturesEngine.hpp (100%) rename src/libs/{ => services}/recommendation/impl/features/FeaturesEngineCache.cpp (100%) rename src/libs/{ => services}/recommendation/impl/features/FeaturesEngineCache.hpp (100%) rename src/libs/{ => services}/recommendation/include/recommendation/IEngine.hpp (100%) rename src/libs/{ => services}/scanner/CMakeLists.txt (100%) rename src/libs/{ => services}/scanner/impl/AcousticBrainzUtils.cpp (100%) rename src/libs/{ => services}/scanner/impl/AcousticBrainzUtils.hpp (100%) rename src/libs/{ => services}/scanner/impl/Scanner.cpp (100%) rename src/libs/{ => services}/scanner/impl/Scanner.hpp (100%) rename src/libs/{ => services}/scanner/impl/ScannerStats.cpp (100%) rename src/libs/{ => services}/scanner/include/scanner/IScanner.hpp (100%) rename src/libs/{ => services}/scanner/include/scanner/ScannerEvents.hpp (100%) rename src/libs/{ => services}/scanner/include/scanner/ScannerStats.hpp (100%) rename src/libs/{ => services}/scrobbling/CMakeLists.txt (94%) rename src/libs/{ => services}/scrobbling/impl/IScrobbler.hpp (97%) rename src/libs/{scrobbling/impl/Scrobbling.cpp => services/scrobbling/impl/ScrobblingService.cpp} (82%) rename src/libs/{scrobbling/impl/Scrobbling.hpp => services/scrobbling/impl/ScrobblingService.hpp} (93%) rename src/libs/{ => services}/scrobbling/impl/internal/InternalScrobbler.cpp (100%) rename src/libs/{ => services}/scrobbling/impl/internal/InternalScrobbler.hpp (100%) rename src/libs/{ => services}/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp (100%) rename src/libs/{ => services}/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp (100%) rename src/libs/{ => services}/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp (100%) rename src/libs/{ => services}/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp (99%) rename src/libs/{ => services}/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp (98%) rename src/libs/{ => services}/scrobbling/impl/listenbrainz/Utils.cpp (100%) rename src/libs/{ => services}/scrobbling/impl/listenbrainz/Utils.hpp (100%) rename src/libs/{scrobbling/include => services/scrobbling/include/services}/scrobbling/Exception.hpp (100%) rename src/libs/{scrobbling/include/scrobbling/IScrobbling.hpp => services/scrobbling/include/services/scrobbling/IScrobblingService.hpp} (93%) rename src/libs/{scrobbling/include => services/scrobbling/include/services}/scrobbling/Listen.hpp (100%) diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 6f98e19a..ca904a17 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -1,15 +1,7 @@ - -add_subdirectory(auth) add_subdirectory(av) -add_subdirectory(database) add_subdirectory(image) add_subdirectory(metadata) -add_subdirectory(recommendation) -add_subdirectory(scanner) -add_subdirectory(scrobbling) add_subdirectory(services) add_subdirectory(som) add_subdirectory(subsonic) add_subdirectory(utils) - - diff --git a/src/libs/services/CMakeLists.txt b/src/libs/services/CMakeLists.txt index 09e78dd4..fd4d3160 100644 --- a/src/libs/services/CMakeLists.txt +++ b/src/libs/services/CMakeLists.txt @@ -1 +1,6 @@ +add_subdirectory(auth) add_subdirectory(cover) +add_subdirectory(database) +add_subdirectory(recommendation) +add_subdirectory(scanner) +add_subdirectory(scrobbling) diff --git a/src/libs/auth/CMakeLists.txt b/src/libs/services/auth/CMakeLists.txt similarity index 100% rename from src/libs/auth/CMakeLists.txt rename to src/libs/services/auth/CMakeLists.txt diff --git a/src/libs/auth/impl/AuthServiceBase.cpp b/src/libs/services/auth/impl/AuthServiceBase.cpp similarity index 100% rename from src/libs/auth/impl/AuthServiceBase.cpp rename to src/libs/services/auth/impl/AuthServiceBase.cpp diff --git a/src/libs/auth/impl/AuthServiceBase.hpp b/src/libs/services/auth/impl/AuthServiceBase.hpp similarity index 100% rename from src/libs/auth/impl/AuthServiceBase.hpp rename to src/libs/services/auth/impl/AuthServiceBase.hpp diff --git a/src/libs/auth/impl/AuthTokenService.cpp b/src/libs/services/auth/impl/AuthTokenService.cpp similarity index 100% rename from src/libs/auth/impl/AuthTokenService.cpp rename to src/libs/services/auth/impl/AuthTokenService.cpp diff --git a/src/libs/auth/impl/AuthTokenService.hpp b/src/libs/services/auth/impl/AuthTokenService.hpp similarity index 100% rename from src/libs/auth/impl/AuthTokenService.hpp rename to src/libs/services/auth/impl/AuthTokenService.hpp diff --git a/src/libs/auth/impl/EnvService.cpp b/src/libs/services/auth/impl/EnvService.cpp similarity index 100% rename from src/libs/auth/impl/EnvService.cpp rename to src/libs/services/auth/impl/EnvService.cpp diff --git a/src/libs/auth/impl/LoginThrottler.cpp b/src/libs/services/auth/impl/LoginThrottler.cpp similarity index 100% rename from src/libs/auth/impl/LoginThrottler.cpp rename to src/libs/services/auth/impl/LoginThrottler.cpp diff --git a/src/libs/auth/impl/LoginThrottler.hpp b/src/libs/services/auth/impl/LoginThrottler.hpp similarity index 100% rename from src/libs/auth/impl/LoginThrottler.hpp rename to src/libs/services/auth/impl/LoginThrottler.hpp diff --git a/src/libs/auth/impl/PasswordServiceBase.cpp b/src/libs/services/auth/impl/PasswordServiceBase.cpp similarity index 100% rename from src/libs/auth/impl/PasswordServiceBase.cpp rename to src/libs/services/auth/impl/PasswordServiceBase.cpp diff --git a/src/libs/auth/impl/PasswordServiceBase.hpp b/src/libs/services/auth/impl/PasswordServiceBase.hpp similarity index 100% rename from src/libs/auth/impl/PasswordServiceBase.hpp rename to src/libs/services/auth/impl/PasswordServiceBase.hpp diff --git a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp b/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.cpp similarity index 100% rename from src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp rename to src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.cpp diff --git a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp b/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.hpp similarity index 100% rename from src/libs/auth/impl/http-headers/HttpHeadersEnvService.hpp rename to src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.hpp diff --git a/src/libs/auth/impl/internal/InternalPasswordService.cpp b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp similarity index 100% rename from src/libs/auth/impl/internal/InternalPasswordService.cpp rename to src/libs/services/auth/impl/internal/InternalPasswordService.cpp diff --git a/src/libs/auth/impl/internal/InternalPasswordService.hpp b/src/libs/services/auth/impl/internal/InternalPasswordService.hpp similarity index 100% rename from src/libs/auth/impl/internal/InternalPasswordService.hpp rename to src/libs/services/auth/impl/internal/InternalPasswordService.hpp diff --git a/src/libs/auth/impl/pam/PAMPasswordService.cpp b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp similarity index 100% rename from src/libs/auth/impl/pam/PAMPasswordService.cpp rename to src/libs/services/auth/impl/pam/PAMPasswordService.cpp diff --git a/src/libs/auth/impl/pam/PAMPasswordService.hpp b/src/libs/services/auth/impl/pam/PAMPasswordService.hpp similarity index 100% rename from src/libs/auth/impl/pam/PAMPasswordService.hpp rename to src/libs/services/auth/impl/pam/PAMPasswordService.hpp diff --git a/src/libs/auth/include/auth/IAuthTokenService.hpp b/src/libs/services/auth/include/auth/IAuthTokenService.hpp similarity index 100% rename from src/libs/auth/include/auth/IAuthTokenService.hpp rename to src/libs/services/auth/include/auth/IAuthTokenService.hpp diff --git a/src/libs/auth/include/auth/IEnvService.hpp b/src/libs/services/auth/include/auth/IEnvService.hpp similarity index 100% rename from src/libs/auth/include/auth/IEnvService.hpp rename to src/libs/services/auth/include/auth/IEnvService.hpp diff --git a/src/libs/auth/include/auth/IPasswordService.hpp b/src/libs/services/auth/include/auth/IPasswordService.hpp similarity index 100% rename from src/libs/auth/include/auth/IPasswordService.hpp rename to src/libs/services/auth/include/auth/IPasswordService.hpp diff --git a/src/libs/auth/include/auth/Types.hpp b/src/libs/services/auth/include/auth/Types.hpp similarity index 100% rename from src/libs/auth/include/auth/Types.hpp rename to src/libs/services/auth/include/auth/Types.hpp diff --git a/src/libs/database/CMakeLists.txt b/src/libs/services/database/CMakeLists.txt similarity index 100% rename from src/libs/database/CMakeLists.txt rename to src/libs/services/database/CMakeLists.txt diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/services/database/impl/Artist.cpp similarity index 100% rename from src/libs/database/impl/Artist.cpp rename to src/libs/services/database/impl/Artist.cpp diff --git a/src/libs/database/impl/Cluster.cpp b/src/libs/services/database/impl/Cluster.cpp similarity index 100% rename from src/libs/database/impl/Cluster.cpp rename to src/libs/services/database/impl/Cluster.cpp diff --git a/src/libs/database/impl/Db.cpp b/src/libs/services/database/impl/Db.cpp similarity index 100% rename from src/libs/database/impl/Db.cpp rename to src/libs/services/database/impl/Db.cpp diff --git a/src/libs/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp similarity index 100% rename from src/libs/database/impl/Release.cpp rename to src/libs/services/database/impl/Release.cpp diff --git a/src/libs/database/impl/ScanSettings.cpp b/src/libs/services/database/impl/ScanSettings.cpp similarity index 100% rename from src/libs/database/impl/ScanSettings.cpp rename to src/libs/services/database/impl/ScanSettings.cpp diff --git a/src/libs/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp similarity index 100% rename from src/libs/database/impl/Session.cpp rename to src/libs/services/database/impl/Session.cpp diff --git a/src/libs/database/impl/SqlQuery.cpp b/src/libs/services/database/impl/SqlQuery.cpp similarity index 100% rename from src/libs/database/impl/SqlQuery.cpp rename to src/libs/services/database/impl/SqlQuery.cpp diff --git a/src/libs/database/impl/SqlQuery.hpp b/src/libs/services/database/impl/SqlQuery.hpp similarity index 100% rename from src/libs/database/impl/SqlQuery.hpp rename to src/libs/services/database/impl/SqlQuery.hpp diff --git a/src/libs/database/impl/StringViewTraits.hpp b/src/libs/services/database/impl/StringViewTraits.hpp similarity index 100% rename from src/libs/database/impl/StringViewTraits.hpp rename to src/libs/services/database/impl/StringViewTraits.hpp diff --git a/src/libs/database/impl/Track.cpp b/src/libs/services/database/impl/Track.cpp similarity index 100% rename from src/libs/database/impl/Track.cpp rename to src/libs/services/database/impl/Track.cpp diff --git a/src/libs/database/impl/TrackArtistLink.cpp b/src/libs/services/database/impl/TrackArtistLink.cpp similarity index 100% rename from src/libs/database/impl/TrackArtistLink.cpp rename to src/libs/services/database/impl/TrackArtistLink.cpp diff --git a/src/libs/database/impl/TrackBookmark.cpp b/src/libs/services/database/impl/TrackBookmark.cpp similarity index 100% rename from src/libs/database/impl/TrackBookmark.cpp rename to src/libs/services/database/impl/TrackBookmark.cpp diff --git a/src/libs/database/impl/TrackFeatures.cpp b/src/libs/services/database/impl/TrackFeatures.cpp similarity index 100% rename from src/libs/database/impl/TrackFeatures.cpp rename to src/libs/services/database/impl/TrackFeatures.cpp diff --git a/src/libs/database/impl/TrackList.cpp b/src/libs/services/database/impl/TrackList.cpp similarity index 100% rename from src/libs/database/impl/TrackList.cpp rename to src/libs/services/database/impl/TrackList.cpp diff --git a/src/libs/database/impl/Traits.hpp b/src/libs/services/database/impl/Traits.hpp similarity index 100% rename from src/libs/database/impl/Traits.hpp rename to src/libs/services/database/impl/Traits.hpp diff --git a/src/libs/database/impl/User.cpp b/src/libs/services/database/impl/User.cpp similarity index 100% rename from src/libs/database/impl/User.cpp rename to src/libs/services/database/impl/User.cpp diff --git a/src/libs/database/impl/Utils.cpp b/src/libs/services/database/impl/Utils.cpp similarity index 100% rename from src/libs/database/impl/Utils.cpp rename to src/libs/services/database/impl/Utils.cpp diff --git a/src/libs/database/impl/Utils.hpp b/src/libs/services/database/impl/Utils.hpp similarity index 100% rename from src/libs/database/impl/Utils.hpp rename to src/libs/services/database/impl/Utils.hpp diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/services/database/include/database/Artist.hpp similarity index 100% rename from src/libs/database/include/database/Artist.hpp rename to src/libs/services/database/include/database/Artist.hpp diff --git a/src/libs/database/include/database/Cluster.hpp b/src/libs/services/database/include/database/Cluster.hpp similarity index 100% rename from src/libs/database/include/database/Cluster.hpp rename to src/libs/services/database/include/database/Cluster.hpp diff --git a/src/libs/database/include/database/Db.hpp b/src/libs/services/database/include/database/Db.hpp similarity index 100% rename from src/libs/database/include/database/Db.hpp rename to src/libs/services/database/include/database/Db.hpp diff --git a/src/libs/database/include/database/Release.hpp b/src/libs/services/database/include/database/Release.hpp similarity index 100% rename from src/libs/database/include/database/Release.hpp rename to src/libs/services/database/include/database/Release.hpp diff --git a/src/libs/database/include/database/ScanSettings.hpp b/src/libs/services/database/include/database/ScanSettings.hpp similarity index 100% rename from src/libs/database/include/database/ScanSettings.hpp rename to src/libs/services/database/include/database/ScanSettings.hpp diff --git a/src/libs/database/include/database/Session.hpp b/src/libs/services/database/include/database/Session.hpp similarity index 100% rename from src/libs/database/include/database/Session.hpp rename to src/libs/services/database/include/database/Session.hpp diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/services/database/include/database/Track.hpp similarity index 100% rename from src/libs/database/include/database/Track.hpp rename to src/libs/services/database/include/database/Track.hpp diff --git a/src/libs/database/include/database/TrackArtistLink.hpp b/src/libs/services/database/include/database/TrackArtistLink.hpp similarity index 100% rename from src/libs/database/include/database/TrackArtistLink.hpp rename to src/libs/services/database/include/database/TrackArtistLink.hpp diff --git a/src/libs/database/include/database/TrackBookmark.hpp b/src/libs/services/database/include/database/TrackBookmark.hpp similarity index 100% rename from src/libs/database/include/database/TrackBookmark.hpp rename to src/libs/services/database/include/database/TrackBookmark.hpp diff --git a/src/libs/database/include/database/TrackFeatures.hpp b/src/libs/services/database/include/database/TrackFeatures.hpp similarity index 100% rename from src/libs/database/include/database/TrackFeatures.hpp rename to src/libs/services/database/include/database/TrackFeatures.hpp diff --git a/src/libs/database/include/database/TrackList.hpp b/src/libs/services/database/include/database/TrackList.hpp similarity index 100% rename from src/libs/database/include/database/TrackList.hpp rename to src/libs/services/database/include/database/TrackList.hpp diff --git a/src/libs/database/include/database/Types.hpp b/src/libs/services/database/include/database/Types.hpp similarity index 100% rename from src/libs/database/include/database/Types.hpp rename to src/libs/services/database/include/database/Types.hpp diff --git a/src/libs/database/include/database/User.hpp b/src/libs/services/database/include/database/User.hpp similarity index 100% rename from src/libs/database/include/database/User.hpp rename to src/libs/services/database/include/database/User.hpp diff --git a/src/libs/database/test/Artist.cpp b/src/libs/services/database/test/Artist.cpp similarity index 100% rename from src/libs/database/test/Artist.cpp rename to src/libs/services/database/test/Artist.cpp diff --git a/src/libs/database/test/CMakeLists.txt b/src/libs/services/database/test/CMakeLists.txt similarity index 100% rename from src/libs/database/test/CMakeLists.txt rename to src/libs/services/database/test/CMakeLists.txt diff --git a/src/libs/database/test/Cluster.cpp b/src/libs/services/database/test/Cluster.cpp similarity index 100% rename from src/libs/database/test/Cluster.cpp rename to src/libs/services/database/test/Cluster.cpp diff --git a/src/libs/database/test/Common.hpp b/src/libs/services/database/test/Common.hpp similarity index 100% rename from src/libs/database/test/Common.hpp rename to src/libs/services/database/test/Common.hpp diff --git a/src/libs/database/test/DatabaseTest.cpp b/src/libs/services/database/test/DatabaseTest.cpp similarity index 100% rename from src/libs/database/test/DatabaseTest.cpp rename to src/libs/services/database/test/DatabaseTest.cpp diff --git a/src/libs/database/test/Release.cpp b/src/libs/services/database/test/Release.cpp similarity index 100% rename from src/libs/database/test/Release.cpp rename to src/libs/services/database/test/Release.cpp diff --git a/src/libs/database/test/Track.cpp b/src/libs/services/database/test/Track.cpp similarity index 100% rename from src/libs/database/test/Track.cpp rename to src/libs/services/database/test/Track.cpp diff --git a/src/libs/recommendation/CMakeLists.txt b/src/libs/services/recommendation/CMakeLists.txt similarity index 100% rename from src/libs/recommendation/CMakeLists.txt rename to src/libs/services/recommendation/CMakeLists.txt diff --git a/src/libs/recommendation/impl/ClustersEngineCreator.hpp b/src/libs/services/recommendation/impl/ClustersEngineCreator.hpp similarity index 100% rename from src/libs/recommendation/impl/ClustersEngineCreator.hpp rename to src/libs/services/recommendation/impl/ClustersEngineCreator.hpp diff --git a/src/libs/recommendation/impl/Engine.cpp b/src/libs/services/recommendation/impl/Engine.cpp similarity index 100% rename from src/libs/recommendation/impl/Engine.cpp rename to src/libs/services/recommendation/impl/Engine.cpp diff --git a/src/libs/recommendation/impl/Engine.hpp b/src/libs/services/recommendation/impl/Engine.hpp similarity index 100% rename from src/libs/recommendation/impl/Engine.hpp rename to src/libs/services/recommendation/impl/Engine.hpp diff --git a/src/libs/recommendation/impl/FeaturesEngineCreator.hpp b/src/libs/services/recommendation/impl/FeaturesEngineCreator.hpp similarity index 100% rename from src/libs/recommendation/impl/FeaturesEngineCreator.hpp rename to src/libs/services/recommendation/impl/FeaturesEngineCreator.hpp diff --git a/src/libs/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp similarity index 100% rename from src/libs/recommendation/impl/clusters/ClustersEngine.cpp rename to src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp diff --git a/src/libs/recommendation/impl/clusters/ClustersEngine.hpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp similarity index 100% rename from src/libs/recommendation/impl/clusters/ClustersEngine.hpp rename to src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp diff --git a/src/libs/recommendation/impl/features/FeaturesDefs.cpp b/src/libs/services/recommendation/impl/features/FeaturesDefs.cpp similarity index 100% rename from src/libs/recommendation/impl/features/FeaturesDefs.cpp rename to src/libs/services/recommendation/impl/features/FeaturesDefs.cpp diff --git a/src/libs/recommendation/impl/features/FeaturesDefs.hpp b/src/libs/services/recommendation/impl/features/FeaturesDefs.hpp similarity index 100% rename from src/libs/recommendation/impl/features/FeaturesDefs.hpp rename to src/libs/services/recommendation/impl/features/FeaturesDefs.hpp diff --git a/src/libs/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp similarity index 100% rename from src/libs/recommendation/impl/features/FeaturesEngine.cpp rename to src/libs/services/recommendation/impl/features/FeaturesEngine.cpp diff --git a/src/libs/recommendation/impl/features/FeaturesEngine.hpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.hpp similarity index 100% rename from src/libs/recommendation/impl/features/FeaturesEngine.hpp rename to src/libs/services/recommendation/impl/features/FeaturesEngine.hpp diff --git a/src/libs/recommendation/impl/features/FeaturesEngineCache.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.cpp similarity index 100% rename from src/libs/recommendation/impl/features/FeaturesEngineCache.cpp rename to src/libs/services/recommendation/impl/features/FeaturesEngineCache.cpp diff --git a/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp similarity index 100% rename from src/libs/recommendation/impl/features/FeaturesEngineCache.hpp rename to src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp diff --git a/src/libs/recommendation/include/recommendation/IEngine.hpp b/src/libs/services/recommendation/include/recommendation/IEngine.hpp similarity index 100% rename from src/libs/recommendation/include/recommendation/IEngine.hpp rename to src/libs/services/recommendation/include/recommendation/IEngine.hpp diff --git a/src/libs/scanner/CMakeLists.txt b/src/libs/services/scanner/CMakeLists.txt similarity index 100% rename from src/libs/scanner/CMakeLists.txt rename to src/libs/services/scanner/CMakeLists.txt diff --git a/src/libs/scanner/impl/AcousticBrainzUtils.cpp b/src/libs/services/scanner/impl/AcousticBrainzUtils.cpp similarity index 100% rename from src/libs/scanner/impl/AcousticBrainzUtils.cpp rename to src/libs/services/scanner/impl/AcousticBrainzUtils.cpp diff --git a/src/libs/scanner/impl/AcousticBrainzUtils.hpp b/src/libs/services/scanner/impl/AcousticBrainzUtils.hpp similarity index 100% rename from src/libs/scanner/impl/AcousticBrainzUtils.hpp rename to src/libs/services/scanner/impl/AcousticBrainzUtils.hpp diff --git a/src/libs/scanner/impl/Scanner.cpp b/src/libs/services/scanner/impl/Scanner.cpp similarity index 100% rename from src/libs/scanner/impl/Scanner.cpp rename to src/libs/services/scanner/impl/Scanner.cpp diff --git a/src/libs/scanner/impl/Scanner.hpp b/src/libs/services/scanner/impl/Scanner.hpp similarity index 100% rename from src/libs/scanner/impl/Scanner.hpp rename to src/libs/services/scanner/impl/Scanner.hpp diff --git a/src/libs/scanner/impl/ScannerStats.cpp b/src/libs/services/scanner/impl/ScannerStats.cpp similarity index 100% rename from src/libs/scanner/impl/ScannerStats.cpp rename to src/libs/services/scanner/impl/ScannerStats.cpp diff --git a/src/libs/scanner/include/scanner/IScanner.hpp b/src/libs/services/scanner/include/scanner/IScanner.hpp similarity index 100% rename from src/libs/scanner/include/scanner/IScanner.hpp rename to src/libs/services/scanner/include/scanner/IScanner.hpp diff --git a/src/libs/scanner/include/scanner/ScannerEvents.hpp b/src/libs/services/scanner/include/scanner/ScannerEvents.hpp similarity index 100% rename from src/libs/scanner/include/scanner/ScannerEvents.hpp rename to src/libs/services/scanner/include/scanner/ScannerEvents.hpp diff --git a/src/libs/scanner/include/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/scanner/ScannerStats.hpp similarity index 100% rename from src/libs/scanner/include/scanner/ScannerStats.hpp rename to src/libs/services/scanner/include/scanner/ScannerStats.hpp diff --git a/src/libs/scrobbling/CMakeLists.txt b/src/libs/services/scrobbling/CMakeLists.txt similarity index 94% rename from src/libs/scrobbling/CMakeLists.txt rename to src/libs/services/scrobbling/CMakeLists.txt index d69a6f5a..2ce1bb21 100644 --- a/src/libs/scrobbling/CMakeLists.txt +++ b/src/libs/services/scrobbling/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(lmsscrobbling SHARED impl/listenbrainz/ListenBrainzScrobbler.cpp impl/listenbrainz/ListensSynchronizer.cpp impl/listenbrainz/Utils.cpp - impl/Scrobbling.cpp + impl/ScrobblingService.cpp ) target_include_directories(lmsscrobbling INTERFACE diff --git a/src/libs/scrobbling/impl/IScrobbler.hpp b/src/libs/services/scrobbling/impl/IScrobbler.hpp similarity index 97% rename from src/libs/scrobbling/impl/IScrobbler.hpp rename to src/libs/services/scrobbling/impl/IScrobbler.hpp index f442e773..cc0a98ca 100644 --- a/src/libs/scrobbling/impl/IScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/IScrobbler.hpp @@ -25,7 +25,7 @@ #include -#include "scrobbling/Listen.hpp" +#include "services/scrobbling/Listen.hpp" namespace Database { diff --git a/src/libs/scrobbling/impl/Scrobbling.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp similarity index 82% rename from src/libs/scrobbling/impl/Scrobbling.cpp rename to src/libs/services/scrobbling/impl/ScrobblingService.cpp index b1a91936..13ddb4d7 100644 --- a/src/libs/scrobbling/impl/Scrobbling.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "Scrobbling.hpp" +#include "ScrobblingService.hpp" #include "database/Artist.hpp" #include "database/Db.hpp" @@ -34,13 +34,13 @@ namespace Scrobbling { using namespace Database; - std::unique_ptr - createScrobbling(boost::asio::io_context& ioContext, Db& db) + std::unique_ptr + createScrobblingService(boost::asio::io_context& ioContext, Db& db) { - return std::make_unique(ioContext, db); + return std::make_unique(ioContext, db); } - Scrobbling::Scrobbling(boost::asio::io_context& ioContext, Db& db) + ScrobblingService::ScrobblingService(boost::asio::io_context& ioContext, Db& db) : _db {db} { _scrobblers.emplace(Database::Scrobbler::Internal, std::make_unique(_db)); @@ -48,28 +48,28 @@ namespace Scrobbling } void - Scrobbling::listenStarted(const Listen& listen) + ScrobblingService::listenStarted(const Listen& listen) { if (std::optional scrobbler {getUserScrobbler(listen.userId)}) _scrobblers[*scrobbler]->listenStarted(listen); } void - Scrobbling::listenFinished(const Listen& listen, std::optional duration) + ScrobblingService::listenFinished(const Listen& listen, std::optional duration) { if (std::optional scrobbler {getUserScrobbler(listen.userId)}) _scrobblers[*scrobbler]->listenFinished(listen, duration); } void - Scrobbling::addTimedListen(const TimedListen& listen) + ScrobblingService::addTimedListen(const TimedListen& listen) { if (std::optional scrobbler {getUserScrobbler(listen.userId)}) _scrobblers[*scrobbler]->addTimedListen(listen); } std::optional - Scrobbling::getUserScrobbler(Database::UserId userId) + ScrobblingService::getUserScrobbler(Database::UserId userId) { std::optional scrobbler; @@ -81,8 +81,8 @@ namespace Scrobbling return scrobbler; } - Scrobbling::ArtistContainer - Scrobbling::getRecentArtists(UserId userId, + ScrobblingService::ArtistContainer + ScrobblingService::getRecentArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, std::optional range, @@ -107,8 +107,8 @@ namespace Scrobbling return res; } - Scrobbling::ReleaseContainer - Scrobbling::getRecentReleases(Database::UserId userId, + ScrobblingService::ReleaseContainer + ScrobblingService::getRecentReleases(Database::UserId userId, const std::vector& clusterIds, std::optional range, bool& moreResults) @@ -132,8 +132,8 @@ namespace Scrobbling return res; } - Scrobbling::TrackContainer - Scrobbling::getRecentTracks(Database::UserId userId, + ScrobblingService::TrackContainer + ScrobblingService::getRecentTracks(Database::UserId userId, const std::vector& clusterIds, std::optional range, bool& moreResults) @@ -159,8 +159,8 @@ namespace Scrobbling // Top - Scrobbling::ArtistContainer - Scrobbling::getTopArtists(UserId userId, + ScrobblingService::ArtistContainer + ScrobblingService::getTopArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, std::optional range, @@ -185,8 +185,8 @@ namespace Scrobbling return res; } - Scrobbling::ReleaseContainer - Scrobbling::getTopReleases(Database::UserId userId, + ScrobblingService::ReleaseContainer + ScrobblingService::getTopReleases(Database::UserId userId, const std::vector& clusterIds, std::optional range, bool& moreResults) @@ -210,8 +210,8 @@ namespace Scrobbling return res; } - Scrobbling::TrackContainer - Scrobbling::getTopTracks(Database::UserId userId, + ScrobblingService::TrackContainer + ScrobblingService::getTopTracks(Database::UserId userId, const std::vector& clusterIds, std::optional range, bool& moreResults) @@ -235,7 +235,7 @@ namespace Scrobbling } Database::ObjectPtr - Scrobbling::getListensTrackList(Session& session, Database::ObjectPtr user) + ScrobblingService::getListensTrackList(Session& session, Database::ObjectPtr user) { return _scrobblers[user->getScrobbler()]->getListensTrackList(session, user); } diff --git a/src/libs/scrobbling/impl/Scrobbling.hpp b/src/libs/services/scrobbling/impl/ScrobblingService.hpp similarity index 93% rename from src/libs/scrobbling/impl/Scrobbling.hpp rename to src/libs/services/scrobbling/impl/ScrobblingService.hpp index dd338dd3..6b1b9959 100644 --- a/src/libs/scrobbling/impl/Scrobbling.hpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.hpp @@ -23,15 +23,15 @@ #include #include -#include "scrobbling/IScrobbling.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "IScrobbler.hpp" namespace Scrobbling { - class Scrobbling : public IScrobbling + class ScrobblingService : public IScrobblingService { public: - Scrobbling(boost::asio::io_context& ioContext, Database::Db& db); + ScrobblingService(boost::asio::io_context& ioContext, Database::Db& db); private: void listenStarted(const Listen& listen) override; diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp similarity index 100% rename from src/libs/scrobbling/impl/internal/InternalScrobbler.cpp rename to src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp similarity index 100% rename from src/libs/scrobbling/impl/internal/InternalScrobbler.hpp rename to src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp diff --git a/src/libs/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp similarity index 100% rename from src/libs/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp rename to src/libs/services/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp similarity index 100% rename from src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp rename to src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp similarity index 100% rename from src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp rename to src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp similarity index 99% rename from src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp rename to src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 8196da8b..62329030 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -32,7 +32,7 @@ #include "database/Track.hpp" #include "database/TrackList.hpp" #include "database/User.hpp" -#include "scrobbling/Exception.hpp" +#include "services/scrobbling/Exception.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp similarity index 98% rename from src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp rename to src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index e71a8ae4..90942990 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -27,7 +27,7 @@ #include #include "database/Types.hpp" -#include "scrobbling/Listen.hpp" +#include "services/scrobbling/Listen.hpp" namespace Database { diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp similarity index 100% rename from src/libs/scrobbling/impl/listenbrainz/Utils.cpp rename to src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp similarity index 100% rename from src/libs/scrobbling/impl/listenbrainz/Utils.hpp rename to src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp diff --git a/src/libs/scrobbling/include/scrobbling/Exception.hpp b/src/libs/services/scrobbling/include/services/scrobbling/Exception.hpp similarity index 100% rename from src/libs/scrobbling/include/scrobbling/Exception.hpp rename to src/libs/services/scrobbling/include/services/scrobbling/Exception.hpp diff --git a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp similarity index 93% rename from src/libs/scrobbling/include/scrobbling/IScrobbling.hpp rename to src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp index cda27ca1..b9ce833e 100644 --- a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp @@ -28,7 +28,7 @@ #include -#include "scrobbling/Listen.hpp" +#include "services/scrobbling/Listen.hpp" #include "database/Types.hpp" namespace Database @@ -44,10 +44,10 @@ namespace Database namespace Scrobbling { - class IScrobbling + class IScrobblingService { public: - virtual ~IScrobbling() = default; + virtual ~IScrobblingService() = default; // Scrobbling virtual void listenStarted(const Listen& listen) = 0; @@ -97,7 +97,7 @@ namespace Scrobbling bool& moreResults) = 0; }; - std::unique_ptr createScrobbling(boost::asio::io_service& ioService, Database::Db& db); + std::unique_ptr createScrobblingService(boost::asio::io_service& ioService, Database::Db& db); } // ns Scrobbling diff --git a/src/libs/scrobbling/include/scrobbling/Listen.hpp b/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp similarity index 100% rename from src/libs/scrobbling/include/scrobbling/Listen.hpp rename to src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 38f811b0..43bbdcca 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -38,7 +38,7 @@ #include "database/TrackList.hpp" #include "database/User.hpp" #include "recommendation/IEngine.hpp" -#include "scrobbling/IScrobbling.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "services/cover/ICoverService.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" @@ -724,7 +724,7 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) const Range range {offset, size}; std::vector releases; - Scrobbling::IScrobbling& scrobbling {*Service::get()}; + Scrobbling::IScrobblingService& scrobbling {*Service::get()}; auto transaction {context.dbSession.createSharedTransaction()}; @@ -1574,13 +1574,13 @@ handleScrobble(RequestContext& context) if (!submission) { - Service::get()->listenStarted({context.userId, ids.front()}); + Service::get()->listenStarted({context.userId, ids.front()}); } else { if (times.empty()) { - Service::get()->listenFinished({context.userId, ids.front()}); + Service::get()->listenFinished({context.userId, ids.front()}); } else { @@ -1588,7 +1588,7 @@ handleScrobble(RequestContext& context) { const TrackId trackId {ids[i]}; const unsigned long time {times[i]}; - Service::get()->addTimedListen({{context.userId, trackId}, Wt::WDateTime::fromTime_t(static_cast(time / 1000))}); + Service::get()->addTimedListen({{context.userId, trackId}, Wt::WDateTime::fromTime_t(static_cast(time / 1000))}); } } } diff --git a/src/lms/main.cpp b/src/lms/main.cpp index c30dd110..467446dc 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -34,7 +34,7 @@ #include "scanner/IScanner.hpp" #include "recommendation/IEngine.hpp" #include "subsonic/SubsonicResource.hpp" -#include "scrobbling/IScrobbling.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "ui/LmsApplication.hpp" #include "ui/LmsApplicationManager.hpp" #include "utils/http/IClient.hpp" @@ -268,7 +268,7 @@ int main(int argc, char* argv[]) coverService->flushCache(); }); - Service scrobblingService {Scrobbling::createScrobbling(ioContext, database)}; + Service scrobblingService {Scrobbling::createScrobblingService(ioContext, database)}; std::unique_ptr subsonicResource; diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index e14207ca..0c9123f7 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -37,7 +37,7 @@ #include "database/Release.hpp" #include "database/Session.hpp" #include "database/User.hpp" -#include "scrobbling/IScrobbling.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" #include "utils/String.hpp" @@ -548,14 +548,14 @@ LmsApplication::createHome() { LMS_LOG(UI, DEBUG) << "Received ScrobbleListenNow from player for trackId = " << trackId.toString(); const Scrobbling::Listen listen {getUserId(), trackId}; - Service::get()->listenStarted(listen); + Service::get()->listenStarted(listen); }); _mediaPlayer->scrobbleListenFinished.connect([this](Database::TrackId trackId, unsigned durationMs) { LMS_LOG(UI, DEBUG) << "Received ScrobbleListenFinished from player for trackId = " << trackId.toString() << ", duration = " << (durationMs / 1000) << "s"; const std::chrono::milliseconds duration {durationMs}; const Scrobbling::Listen listen {getUserId(), trackId}; - Service::get()->listenFinished(listen, std::chrono::duration_cast(duration)); + Service::get()->listenFinished(listen, std::chrono::duration_cast(duration)); }); _mediaPlayer->playbackEnded.connect([this] diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 0c73aa3e..2e2b3522 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -22,7 +22,7 @@ #include "database/Artist.hpp" #include "database/User.hpp" #include "database/TrackList.hpp" -#include "scrobbling/IScrobbling.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "Filters.hpp" #include "LmsApplication.hpp" @@ -34,7 +34,7 @@ namespace UserInterface std::vector> ArtistCollector::get(std::optional range, bool& moreResults) { - Scrobbling::IScrobbling& scrobbling {*Service::get()}; + Scrobbling::IScrobblingService& scrobbling {*Service::get()}; range = getActualRange(range); diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index c5671f54..0bfd0713 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -25,7 +25,7 @@ #include "database/Session.hpp" #include "database/User.hpp" #include "database/TrackList.hpp" -#include "scrobbling/IScrobbling.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "Filters.hpp" #include "LmsApplication.hpp" @@ -37,7 +37,7 @@ namespace UserInterface std::vector ReleaseCollector::get(std::optional range, bool& moreResults) { - Scrobbling::IScrobbling& scrobbling {*Service::get()}; + Scrobbling::IScrobblingService& scrobbling {*Service::get()}; range = getActualRange(range); diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 77106931..d3e8b3db 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -25,7 +25,7 @@ #include "database/Track.hpp" #include "database/TrackList.hpp" #include "database/User.hpp" -#include "scrobbling/IScrobbling.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "Filters.hpp" #include "LmsApplication.hpp" @@ -37,7 +37,7 @@ namespace UserInterface std::vector TrackCollector::get(std::optional range, bool& moreResults) { - Scrobbling::IScrobbling& scrobbling {*Service::get()}; + Scrobbling::IScrobblingService& scrobbling {*Service::get()}; range = getActualRange(range); std::vector tracks; From 7386c89ae7b558fbcfa3df0873c85cce99a6c839 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 18 Oct 2021 21:16:10 +0200 Subject: [PATCH 23/49] Migrated recommendation stuff --- .../services/auth/impl/AuthTokenService.cpp | 2 +- .../services/auth/impl/AuthTokenService.hpp | 2 +- src/libs/services/auth/impl/EnvService.cpp | 4 +- .../auth/impl/PasswordServiceBase.cpp | 2 +- .../auth/impl/PasswordServiceBase.hpp | 2 +- .../http-headers/HttpHeadersEnvService.hpp | 2 +- .../impl/internal/InternalPasswordService.cpp | 4 +- .../auth/impl/pam/PAMPasswordService.cpp | 2 +- .../{ => services}/auth/IAuthTokenService.hpp | 0 .../{ => services}/auth/IEnvService.hpp | 0 .../{ => services}/auth/IPasswordService.hpp | 4 +- .../include/{ => services}/auth/Types.hpp | 0 .../services/recommendation/CMakeLists.txt | 2 +- .../impl/FeaturesEngineCreator.hpp | 2 +- .../recommendation => impl}/IEngine.hpp | 15 +----- .../{Engine.cpp => RecommendationService.cpp} | 36 ++++++------- .../{Engine.hpp => RecommendationService.hpp} | 18 +++---- .../impl/clusters/ClustersEngine.cpp | 8 +-- .../impl/clusters/ClustersEngine.hpp | 3 +- .../impl/features/FeaturesEngine.cpp | 12 ++--- .../impl/features/FeaturesEngine.hpp | 3 +- .../recommendation/IRecommendationService.hpp | 51 +++++++++++++++++++ .../include/services/recommendation/Types.hpp | 22 ++++++++ src/libs/services/scanner/impl/Scanner.cpp | 26 +++++----- src/libs/services/scanner/impl/Scanner.hpp | 8 +-- .../scanner/include/scanner/IScanner.hpp | 4 +- src/libs/subsonic/impl/SubsonicResource.cpp | 10 ++-- src/lms/main.cpp | 12 ++--- src/lms/ui/Auth.cpp | 4 +- src/lms/ui/LmsApplication.cpp | 4 +- src/lms/ui/PlayQueue.cpp | 4 +- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 2 +- src/lms/ui/admin/UsersView.cpp | 2 +- src/lms/ui/common/PasswordValidator.cpp | 2 +- src/lms/ui/common/PasswordValidator.hpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 4 +- src/lms/ui/explore/ReleaseView.cpp | 4 +- .../recommendation/LmsRecommendation.cpp | 32 ++++++------ 40 files changed, 189 insertions(+), 131 deletions(-) rename src/libs/services/auth/include/{ => services}/auth/IAuthTokenService.hpp (100%) rename src/libs/services/auth/include/{ => services}/auth/IEnvService.hpp (100%) rename src/libs/services/auth/include/{ => services}/auth/IPasswordService.hpp (98%) rename src/libs/services/auth/include/{ => services}/auth/Types.hpp (100%) rename src/libs/services/recommendation/{include/recommendation => impl}/IEngine.hpp (77%) rename src/libs/services/recommendation/impl/{Engine.cpp => RecommendationService.cpp} (83%) rename src/libs/services/recommendation/impl/{Engine.hpp => RecommendationService.hpp} (82%) create mode 100644 src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp create mode 100644 src/libs/services/recommendation/include/services/recommendation/Types.hpp diff --git a/src/libs/services/auth/impl/AuthTokenService.cpp b/src/libs/services/auth/impl/AuthTokenService.cpp index f6f305b6..f4104ca0 100644 --- a/src/libs/services/auth/impl/AuthTokenService.cpp +++ b/src/libs/services/auth/impl/AuthTokenService.cpp @@ -23,7 +23,7 @@ #include #include -#include "auth/Types.hpp" +#include "services/auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/services/auth/impl/AuthTokenService.hpp b/src/libs/services/auth/impl/AuthTokenService.hpp index 8825a3d3..e95a8a79 100644 --- a/src/libs/services/auth/impl/AuthTokenService.hpp +++ b/src/libs/services/auth/impl/AuthTokenService.hpp @@ -21,7 +21,7 @@ #include -#include "auth/IAuthTokenService.hpp" +#include "services/auth/IAuthTokenService.hpp" #include "AuthServiceBase.hpp" #include "LoginThrottler.hpp" diff --git a/src/libs/services/auth/impl/EnvService.cpp b/src/libs/services/auth/impl/EnvService.cpp index 12de9858..26f972a6 100644 --- a/src/libs/services/auth/impl/EnvService.cpp +++ b/src/libs/services/auth/impl/EnvService.cpp @@ -17,9 +17,9 @@ * along with LMS. If not, see . */ -#include "auth/IEnvService.hpp" +#include "services/auth/IEnvService.hpp" -#include "auth/Types.hpp" +#include "services/auth/Types.hpp" #include "http-headers/HttpHeadersEnvService.hpp" namespace Auth diff --git a/src/libs/services/auth/impl/PasswordServiceBase.cpp b/src/libs/services/auth/impl/PasswordServiceBase.cpp index 3b230503..bd1901ee 100644 --- a/src/libs/services/auth/impl/PasswordServiceBase.cpp +++ b/src/libs/services/auth/impl/PasswordServiceBase.cpp @@ -27,7 +27,7 @@ #include "pam/PAMPasswordService.hpp" #endif // LMS_SUPPORT_PAM -#include "auth/Types.hpp" +#include "services/auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/services/auth/impl/PasswordServiceBase.hpp b/src/libs/services/auth/impl/PasswordServiceBase.hpp index f575e4e1..9f9575ec 100644 --- a/src/libs/services/auth/impl/PasswordServiceBase.hpp +++ b/src/libs/services/auth/impl/PasswordServiceBase.hpp @@ -21,7 +21,7 @@ #include -#include "auth/IPasswordService.hpp" +#include "services/auth/IPasswordService.hpp" #include "AuthServiceBase.hpp" #include "LoginThrottler.hpp" diff --git a/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.hpp b/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.hpp index 5c44b9fb..02e4f94c 100644 --- a/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.hpp +++ b/src/libs/services/auth/impl/http-headers/HttpHeadersEnvService.hpp @@ -19,7 +19,7 @@ #pragma once -#include "auth/IEnvService.hpp" +#include "services/auth/IEnvService.hpp" #include "AuthServiceBase.hpp" namespace Auth diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp index 96a6c34a..fe162150 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp @@ -20,8 +20,8 @@ #include "InternalPasswordService.hpp" #include -#include "auth/IAuthTokenService.hpp" -#include "auth/Types.hpp" +#include "services/auth/IAuthTokenService.hpp" +#include "services/auth/Types.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp index c5682f42..e24de954 100644 --- a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp @@ -26,7 +26,7 @@ #include #include -#include "auth/Types.hpp" +#include "services/auth/Types.hpp" #include "database/Session.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/services/auth/include/auth/IAuthTokenService.hpp b/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp similarity index 100% rename from src/libs/services/auth/include/auth/IAuthTokenService.hpp rename to src/libs/services/auth/include/services/auth/IAuthTokenService.hpp diff --git a/src/libs/services/auth/include/auth/IEnvService.hpp b/src/libs/services/auth/include/services/auth/IEnvService.hpp similarity index 100% rename from src/libs/services/auth/include/auth/IEnvService.hpp rename to src/libs/services/auth/include/services/auth/IEnvService.hpp diff --git a/src/libs/services/auth/include/auth/IPasswordService.hpp b/src/libs/services/auth/include/services/auth/IPasswordService.hpp similarity index 98% rename from src/libs/services/auth/include/auth/IPasswordService.hpp rename to src/libs/services/auth/include/services/auth/IPasswordService.hpp index f197ae16..6a9cb2bd 100644 --- a/src/libs/services/auth/include/auth/IPasswordService.hpp +++ b/src/libs/services/auth/include/services/auth/IPasswordService.hpp @@ -21,11 +21,11 @@ #include +#include #include #include -#include -#include "auth/Types.hpp" +#include "services/auth/Types.hpp" #include "database/Types.hpp" namespace Database diff --git a/src/libs/services/auth/include/auth/Types.hpp b/src/libs/services/auth/include/services/auth/Types.hpp similarity index 100% rename from src/libs/services/auth/include/auth/Types.hpp rename to src/libs/services/auth/include/services/auth/Types.hpp diff --git a/src/libs/services/recommendation/CMakeLists.txt b/src/libs/services/recommendation/CMakeLists.txt index 4682173d..a001854e 100644 --- a/src/libs/services/recommendation/CMakeLists.txt +++ b/src/libs/services/recommendation/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(lmsrecommendation SHARED impl/features/FeaturesEngineCache.cpp impl/features/FeaturesEngine.cpp impl/features/FeaturesDefs.cpp - impl/Engine.cpp + impl/RecommendationService.cpp ) target_include_directories(lmsrecommendation INTERFACE diff --git a/src/libs/services/recommendation/impl/FeaturesEngineCreator.hpp b/src/libs/services/recommendation/impl/FeaturesEngineCreator.hpp index 1378970f..5cbbde7c 100644 --- a/src/libs/services/recommendation/impl/FeaturesEngineCreator.hpp +++ b/src/libs/services/recommendation/impl/FeaturesEngineCreator.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "recommendation/IEngine.hpp" +#include "IEngine.hpp" namespace Database { diff --git a/src/libs/services/recommendation/include/recommendation/IEngine.hpp b/src/libs/services/recommendation/impl/IEngine.hpp similarity index 77% rename from src/libs/services/recommendation/include/recommendation/IEngine.hpp rename to src/libs/services/recommendation/impl/IEngine.hpp index 26f180bc..8c16b407 100644 --- a/src/libs/services/recommendation/include/recommendation/IEngine.hpp +++ b/src/libs/services/recommendation/impl/IEngine.hpp @@ -24,6 +24,7 @@ #include #include "database/Types.hpp" #include "utils/EnumSet.hpp" +#include "services/recommendation/Types.hpp" namespace Database { @@ -37,23 +38,9 @@ namespace Recommendation public: virtual ~IEngine() = default; - struct Progress - { - std::size_t totalElems {}; - std::size_t processedElems {}; - }; - using ProgressCallback = std::function; virtual void load(bool forceReload, const ProgressCallback& progressCallback = {}) = 0; - virtual void cancelLoad() = 0; // wait for cancel done virtual void requestCancelLoad() = 0; - template - using ResultContainer = std::vector; - - using ArtistContainer = ResultContainer; - using ReleaseContainer = ResultContainer; - using TrackContainer = ResultContainer; - virtual TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0; virtual TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const = 0; virtual ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const = 0; diff --git a/src/libs/services/recommendation/impl/Engine.cpp b/src/libs/services/recommendation/impl/RecommendationService.cpp similarity index 83% rename from src/libs/services/recommendation/impl/Engine.cpp rename to src/libs/services/recommendation/impl/RecommendationService.cpp index b6808ceb..b2b6f235 100644 --- a/src/libs/services/recommendation/impl/Engine.cpp +++ b/src/libs/services/recommendation/impl/RecommendationService.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "Engine.hpp" +#include "RecommendationService.hpp" #include #include @@ -47,19 +47,19 @@ namespace Recommendation throw LmsException {"Internal error"}; } - std::unique_ptr - createEngine(Database::Db& db) + std::unique_ptr + createRecommendationService(Database::Db& db) { - return std::make_unique(db); + return std::make_unique(db); } - Engine::Engine(Database::Db& db) + RecommendationService::RecommendationService(Database::Db& db) : _db {db} { } - Engine::TrackContainer - Engine::getSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const + TrackContainer + RecommendationService::getSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const { TrackContainer res; @@ -78,8 +78,8 @@ namespace Recommendation return res; } - Engine::TrackContainer - Engine::getSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const + TrackContainer + RecommendationService::getSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const { TrackContainer res; @@ -102,8 +102,8 @@ namespace Recommendation return res; } - Engine::ReleaseContainer - Engine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const + ReleaseContainer + RecommendationService::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const { ReleaseContainer res; @@ -128,8 +128,8 @@ namespace Recommendation return res; } - Engine::ArtistContainer - Engine::getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const + ArtistContainer + RecommendationService::getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const { ArtistContainer res; @@ -162,7 +162,7 @@ namespace Recommendation } void - Engine::load(bool forceReload, const ProgressCallback& progressCallback) + RecommendationService::load(bool forceReload, const ProgressCallback& progressCallback) { using namespace Database; @@ -208,18 +208,18 @@ namespace Recommendation } void - Engine::loadPendingEngine(EngineType engineType, std::unique_ptr engine, bool forceReload, const ProgressCallback& progressCallback) + RecommendationService::loadPendingEngine(EngineType engineType, std::unique_ptr engine, bool forceReload, const ProgressCallback& progressCallback) { if (!_loadCancelled) { LMS_LOG(RECOMMENDATION, INFO) << "Initializing engine '" << engineTypeToString(engineType) << "'..."; - auto progress {[&](const IEngine::Progress& progress) + auto progress {[&](const Progress& progress) { progressCallback(progress); }}; - engine->load(forceReload, progressCallback ? progress : IEngine::ProgressCallback {}); + engine->load(forceReload, progressCallback ? progress : ProgressCallback {}); { std::scoped_lock lock {_controlMutex}; @@ -236,7 +236,7 @@ namespace Recommendation } void - Engine::cancelLoad() + RecommendationService::cancelLoad() { LMS_LOG(RECOMMENDATION, DEBUG) << "Cancelling loading..."; diff --git a/src/libs/services/recommendation/impl/Engine.hpp b/src/libs/services/recommendation/impl/RecommendationService.hpp similarity index 82% rename from src/libs/services/recommendation/impl/Engine.hpp rename to src/libs/services/recommendation/impl/RecommendationService.hpp index ac253b5c..22e07217 100644 --- a/src/libs/services/recommendation/impl/Engine.hpp +++ b/src/libs/services/recommendation/impl/RecommendationService.hpp @@ -25,7 +25,8 @@ #include #include -#include "recommendation/IEngine.hpp" +#include "services/recommendation/IRecommendationService.hpp" +#include "IEngine.hpp" namespace Database { @@ -40,21 +41,20 @@ namespace Recommendation Features, }; - class Engine : public IEngine + class RecommendationService : public IRecommendationService { public: - Engine(Database::Db& db); - ~Engine() = default; + RecommendationService(Database::Db& db); + ~RecommendationService() = default; - Engine(const Engine&) = delete; - Engine(Engine&&) = delete; - Engine& operator=(const Engine&) = delete; - Engine& operator=(Engine&&) = delete; + RecommendationService(const RecommendationService&) = delete; + RecommendationService(RecommendationService&&) = delete; + RecommendationService& operator=(const RecommendationService&) = delete; + RecommendationService& operator=(RecommendationService&&) = delete; private: void load(bool forceReload, const ProgressCallback& progressCallback) override; void cancelLoad() override; - void requestCancelLoad() override {}; TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp index b0703a0f..f873443a 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp @@ -34,7 +34,7 @@ std::unique_ptr createClustersEngine(Database::Db& db) return std::make_unique(db); } -IEngine::TrackContainer +TrackContainer ClusterEngine::getSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const { Database::Session& dbSession {_db.getTLSSession()}; @@ -52,7 +52,7 @@ ClusterEngine::getSimilarTracks(const std::vector& trackIds, return res; } -IEngine::ResultContainer +TrackContainer ClusterEngine::getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const { Database::Session& dbSession {_db.getTLSSession()}; @@ -74,7 +74,7 @@ ClusterEngine::getSimilarTracksFromTrackList(Database::TrackListId tracklistId, return res; } -IEngine::ResultContainer +ReleaseContainer ClusterEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const { Database::Session& dbSession {_db.getTLSSession()}; @@ -95,7 +95,7 @@ ClusterEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t max return res; } -IEngine::ResultContainer +ArtistContainer ClusterEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet artistLinkTypes, std::size_t maxCount) const { Database::Session& dbSession {_db.getTLSSession()}; diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp index 47654aac..faff863c 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp @@ -19,7 +19,7 @@ #pragma once -#include "recommendation/IEngine.hpp" +#include "IEngine.hpp" namespace Recommendation { @@ -37,7 +37,6 @@ namespace Recommendation private: void load(bool, const ProgressCallback&) override {} void requestCancelLoad() override {} - void cancelLoad() {} TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp index 7b1f39b6..7610ee75 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp @@ -248,7 +248,7 @@ FeaturesEngine::loadFromCache(FeaturesEngineCache cache) load(std::move(cache._network), cache._trackPositions); } -IEngine::TrackContainer +TrackContainer FeaturesEngine::getSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const { const TrackContainer trackIds {[&] @@ -269,7 +269,7 @@ FeaturesEngine::getSimilarTracksFromTrackList(Database::TrackListId trackListId, return getSimilarTracks(trackIds, maxCount); } -IEngine::TrackContainer +TrackContainer FeaturesEngine::getSimilarTracks(const std::vector& tracksIds, std::size_t maxCount) const { auto similarTrackIds {getSimilarObjects(tracksIds, _trackMatrix, _trackPositions, maxCount)}; @@ -290,7 +290,7 @@ FeaturesEngine::getSimilarTracks(const std::vector& tracksIds return similarTrackIds; } -IEngine::ReleaseContainer +ReleaseContainer FeaturesEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const { auto similarReleaseIds {getSimilarObjects({releaseId}, _releaseMatrix, _releasePositions, maxCount)}; @@ -312,12 +312,12 @@ FeaturesEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t ma return similarReleaseIds; } -std::vector +ArtistContainer FeaturesEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const { auto getSimilarArtistIdsForLinkType {[&] (Database::TrackArtistLinkType linkType) { - std::vector similarArtistIds; + ArtistContainer similarArtistIds; const auto itArtists {_artistMatrix.find(linkType)}; if (itArtists == std::cend(_artistMatrix)) @@ -336,7 +336,7 @@ FeaturesEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet res(std::cbegin(similarArtistIds), std::cend(similarArtistIds)); + ArtistContainer res(std::cbegin(similarArtistIds), std::cend(similarArtistIds)); Database::Session& session {_db.getTLSSession()}; { diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngine.hpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.hpp index d6c1e81b..e29feb93 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngine.hpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngine.hpp @@ -26,10 +26,10 @@ #include #include -#include "recommendation/IEngine.hpp" #include "som/DataNormalizer.hpp" #include "som/Network.hpp" #include "utils/Utils.hpp" +#include "IEngine.hpp" #include "FeaturesEngineCache.hpp" #include "FeaturesDefs.hpp" @@ -62,7 +62,6 @@ class FeaturesEngine : public IEngine private: void load(bool forceReload, const ProgressCallback& progressCallback) override; void requestCancelLoad() override; - void cancelLoad() override {} TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; diff --git a/src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp b/src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp new file mode 100644 index 00000000..900c95a4 --- /dev/null +++ b/src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include "utils/EnumSet.hpp" +#include "services/recommendation/Types.hpp" + +namespace Database +{ + class Db; +} + +namespace Recommendation +{ + class IRecommendationService + { + public: + virtual ~IRecommendationService() = default; + + virtual void load(bool forceReload, const ProgressCallback& progressCallback = {}) = 0; + virtual void cancelLoad() = 0; // wait for cancel done + + virtual TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0; + virtual TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const = 0; + virtual ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const = 0; + virtual ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const = 0; + }; + + std::unique_ptr createRecommendationService(Database::Db& db); + +} // ns Recommendation + diff --git a/src/libs/services/recommendation/include/services/recommendation/Types.hpp b/src/libs/services/recommendation/include/services/recommendation/Types.hpp new file mode 100644 index 00000000..08fa2f90 --- /dev/null +++ b/src/libs/services/recommendation/include/services/recommendation/Types.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "database/Types.hpp" + +namespace Recommendation +{ + struct Progress + { + std::size_t totalElems {}; + std::size_t processedElems {}; + }; + using ProgressCallback = std::function; + + template + using ResultContainer = std::vector; + + using ArtistContainer = ResultContainer; + using ReleaseContainer = ResultContainer; + using TrackContainer = ResultContainer; + +} // namespace Recommendation diff --git a/src/libs/services/scanner/impl/Scanner.cpp b/src/libs/services/scanner/impl/Scanner.cpp index 125324dc..09efc62d 100644 --- a/src/libs/services/scanner/impl/Scanner.cpp +++ b/src/libs/services/scanner/impl/Scanner.cpp @@ -32,7 +32,7 @@ #include "database/TrackArtistLink.hpp" #include "database/TrackFeatures.hpp" #include "metadata/TagLibParser.hpp" -#include "recommendation/IEngine.hpp" +#include "services/recommendation/IRecommendationService.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Path.hpp" @@ -249,13 +249,13 @@ getOrCreateClusters(Session& session, const MetaData::Clusters& clustersNames) namespace Scanner { std::unique_ptr -createScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine) +createScanner(Database::Db& db, Recommendation::IRecommendationService& recommendationService) { - return std::make_unique(db, recommendationEngine); + return std::make_unique(db, recommendationService); } -Scanner::Scanner(Database::Db& db, Recommendation::IEngine& recommendationEngine) -: _recommendationEngine {recommendationEngine} +Scanner::Scanner(Database::Db& db, Recommendation::IRecommendationService& recommendationService) +: _recommendationService {recommendationService} , _dbSession {db} { // For now, always use TagLib @@ -284,8 +284,8 @@ Scanner::start() if (_abortScan) return; - _recommendationEngine.load(false, - [](const Recommendation::IEngine::Progress& progress) + _recommendationService.load(false, + [](const Recommendation::Progress& progress) { LMS_LOG(DBUPDATER, DEBUG) << "Reloading recommendation : " << progress.processedElems << "/" << progress.totalElems; }); @@ -302,7 +302,7 @@ Scanner::stop() _abortScan = true; _scheduleTimer.cancel(); - _recommendationEngine.cancelLoad(); + _recommendationService.cancelLoad(); _ioService.stop(); } @@ -316,7 +316,7 @@ Scanner::abortScan() _abortScan = true; _scheduleTimer.cancel(); - _recommendationEngine.cancelLoad(); + _recommendationService.cancelLoad(); _ioService.stop(); LMS_LOG(DBUPDATER, DEBUG) << "Scan abort done!"; @@ -571,7 +571,7 @@ Scanner::fetchTrackFeatures(Database::TrackId trackId, const UUID& recordingMBID void Scanner::fetchTrackFeatures(ScanStats& stats) { - if (_recommendationEngineType != ScanSettings::RecommendationEngineType::Features) + if (_recommendationServiceType != ScanSettings::RecommendationEngineType::Features) return; ScanStepStats stepStats{stats.startTime, ScanProgressStep::FetchingTrackFeatures}; @@ -638,7 +638,7 @@ Scanner::refreshScanSettings() [](const std::filesystem::path& extension) { return std::filesystem::path{ StringUtils::stringToLower(extension.string()) }; }); } _mediaDirectory = scanSettings->getMediaDirectory(); - _recommendationEngineType = scanSettings->getRecommendationEngineType(); + _recommendationServiceType = scanSettings->getRecommendationEngineType(); const auto clusterTypes = scanSettings->getClusterTypes(); std::set clusterTypeNames; @@ -1037,7 +1037,7 @@ Scanner::reloadSimilarityEngine(ScanStats& stats) { ScanStepStats stepStats {stats.startTime, ScanProgressStep::ReloadingSimilarityEngine}; - auto progressCallback {[&](const Recommendation::IEngine::Progress& progress) + auto progressCallback {[&](const Recommendation::Progress& progress) { stepStats.totalElems = progress.totalElems; stepStats.processedElems = progress.processedElems; @@ -1045,7 +1045,7 @@ Scanner::reloadSimilarityEngine(ScanStats& stats) }}; notifyInProgress(stepStats); - _recommendationEngine.load(stats.nbChanges() > 0, progressCallback); + _recommendationService.load(stats.nbChanges() > 0, progressCallback); notifyInProgress(stepStats); } diff --git a/src/libs/services/scanner/impl/Scanner.hpp b/src/libs/services/scanner/impl/Scanner.hpp index 76aeb819..6999ef9a 100644 --- a/src/libs/services/scanner/impl/Scanner.hpp +++ b/src/libs/services/scanner/impl/Scanner.hpp @@ -41,7 +41,7 @@ class UUID; namespace Recommendation { - class IEngine; + class IRecommendationService; } namespace Scanner { @@ -49,7 +49,7 @@ namespace Scanner { class Scanner : public IScanner { public: - Scanner(Database::Db& db, Recommendation::IEngine& recommendationEngine); + Scanner(Database::Db& db, Recommendation::IRecommendationService& recommendationService); ~Scanner(); Scanner(const Scanner&) = delete; @@ -92,7 +92,7 @@ class Scanner : public IScanner void notifyInProgress(const ScanStepStats& stats); void reloadSimilarityEngine(ScanStats& stats); - Recommendation::IEngine& _recommendationEngine; + Recommendation::IRecommendationService& _recommendationService; std::mutex _controlMutex; std::atomic _abortScan {}; @@ -115,7 +115,7 @@ class Scanner : public IScanner Database::ScanSettings::UpdatePeriod _updatePeriod {Database::ScanSettings::UpdatePeriod::Never}; std::unordered_set _fileExtensions; std::filesystem::path _mediaDirectory; - Database::ScanSettings::RecommendationEngineType _recommendationEngineType; + Database::ScanSettings::RecommendationEngineType _recommendationServiceType; }; } // Scanner diff --git a/src/libs/services/scanner/include/scanner/IScanner.hpp b/src/libs/services/scanner/include/scanner/IScanner.hpp index 018cca98..602a4626 100644 --- a/src/libs/services/scanner/include/scanner/IScanner.hpp +++ b/src/libs/services/scanner/include/scanner/IScanner.hpp @@ -31,7 +31,7 @@ namespace Database namespace Recommendation { - class IEngine; + class IRecommendationService; } namespace Scanner @@ -66,7 +66,7 @@ namespace Scanner virtual Events& getEvents() = 0; }; - std::unique_ptr createScanner(Database::Db& db, Recommendation::IEngine& recommendationEngine); + std::unique_ptr createScanner(Database::Db& db, Recommendation::IRecommendationService& recommendationEngine); } // Scanner diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 43bbdcca..6cbac9a5 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -26,8 +26,8 @@ #include -#include "auth/IPasswordService.hpp" -#include "auth/IEnvService.hpp" +#include "services/auth/IPasswordService.hpp" +#include "services/auth/IEnvService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" @@ -37,7 +37,7 @@ #include "database/TrackBookmark.hpp" #include "database/TrackList.hpp" #include "database/User.hpp" -#include "recommendation/IEngine.hpp" +#include "services/recommendation/IRecommendationService.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "services/cover/ICoverService.hpp" #include "utils/IConfig.hpp" @@ -905,7 +905,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) artistInfoNode.createChild("musicBrainzId").setValue(artistMBID->getAsString()); } - auto similarArtistsId {Service::get()->getSimilarArtists(id, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, count)}; + auto similarArtistsId {Service::get()->getSimilarArtists(id, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, count)}; { auto transaction {context.dbSession.createSharedTransaction()}; @@ -1141,7 +1141,7 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3) // Optional params std::size_t count {getParameterAs(context.parameters, "count").value_or(50)}; - const auto similarArtistIds {Service::get()->getSimilarArtists(artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; + const auto similarArtistIds {Service::get()->getSimilarArtists(artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; auto transaction {context.dbSession.createSharedTransaction()}; diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 467446dc..3d452a67 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -25,15 +25,15 @@ #include #include -#include "auth/IAuthTokenService.hpp" -#include "auth/IPasswordService.hpp" -#include "auth/IEnvService.hpp" +#include "services/auth/IAuthTokenService.hpp" +#include "services/auth/IPasswordService.hpp" +#include "services/auth/IEnvService.hpp" #include "services/cover/ICoverService.hpp" #include "database/Db.hpp" #include "database/Session.hpp" #include "scanner/IScanner.hpp" -#include "recommendation/IEngine.hpp" #include "subsonic/SubsonicResource.hpp" +#include "services/recommendation/IRecommendationService.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "ui/LmsApplication.hpp" #include "ui/LmsApplicationManager.hpp" @@ -258,8 +258,8 @@ int main(int argc, char* argv[]) Service httpClient {Http::createClient(ioContext)}; Service coverService {Cover::createCoverService(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; - Service recommendationEngineService {Recommendation::createEngine(database)}; - Service scannerService {Scanner::createScanner(database, *recommendationEngineService)}; + Service recommendationService {Recommendation::createRecommendationService(database)}; + Service scannerService {Scanner::createScanner(database, *recommendationService)}; scannerService->getEvents().scanComplete.connect([&] { diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 29414b43..b7585247 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -28,8 +28,8 @@ #include #include -#include "auth/IAuthTokenService.hpp" -#include "auth/IPasswordService.hpp" +#include "services/auth/IAuthTokenService.hpp" +#include "services/auth/IPasswordService.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 0c9123f7..b20489d8 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -28,8 +28,8 @@ #include #include -#include "auth/IEnvService.hpp" -#include "auth/IPasswordService.hpp" +#include "services/auth/IEnvService.hpp" +#include "services/auth/IPasswordService.hpp" #include "services/cover/ICoverService.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 554b34d2..550016ee 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -28,7 +28,7 @@ #include "database/Track.hpp" #include "database/TrackList.hpp" #include "database/User.hpp" -#include "recommendation/IEngine.hpp" +#include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" #include "utils/Service.hpp" @@ -520,7 +520,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) void PlayQueue::enqueueRadioTracks() { - const auto similarTrackIds {Service::get()->getSimilarTracksFromTrackList(_tracklistId, 3)}; + const auto similarTrackIds {Service::get()->getSimilarTracksFromTrackList(_tracklistId, 3)}; std::vector trackToAddIds(std::cbegin(similarTrackIds), std::cend(similarTrackIds)); Random::shuffleContainer(trackToAddIds); diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 66105842..c01c7d8e 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -34,7 +34,7 @@ #include "common/UUIDValidator.hpp" #include "common/ValueStringModel.hpp" -#include "auth/IPasswordService.hpp" +#include "services/auth/IPasswordService.hpp" #include "database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index b37645b3..ce7ca58d 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -24,7 +24,7 @@ #include #include -#include "auth/IPasswordService.hpp" +#include "services/auth/IPasswordService.hpp" #include "database/Session.hpp" #include "database/User.hpp" #include "utils/Exception.hpp" diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index 16374d80..e974c967 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -27,7 +27,7 @@ #include -#include "auth/IPasswordService.hpp" +#include "services/auth/IPasswordService.hpp" #include "database/User.hpp" #include "database/Session.hpp" #include "utils/IConfig.hpp" diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 50bc58e7..98160461 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -23,7 +23,7 @@ #include #include -#include "auth/IPasswordService.hpp" +#include "services/auth/IPasswordService.hpp" #include "database/User.hpp" #include "database/Session.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/common/PasswordValidator.cpp b/src/lms/ui/common/PasswordValidator.cpp index 2df00c3f..2979739e 100644 --- a/src/lms/ui/common/PasswordValidator.cpp +++ b/src/lms/ui/common/PasswordValidator.cpp @@ -21,7 +21,7 @@ #include -#include "auth/IPasswordService.hpp" +#include "services/auth/IPasswordService.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/common/PasswordValidator.hpp b/src/lms/ui/common/PasswordValidator.hpp index d504dbc1..40a22679 100644 --- a/src/lms/ui/common/PasswordValidator.hpp +++ b/src/lms/ui/common/PasswordValidator.hpp @@ -23,7 +23,7 @@ #include #include "database/Types.hpp" -#include "auth/Types.hpp" +#include "services/auth/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index eb6f1d41..f44d7ee2 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -32,7 +32,7 @@ #include "database/Session.hpp" #include "database/Track.hpp" #include "database/User.hpp" -#include "recommendation/IEngine.hpp" +#include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" @@ -102,7 +102,7 @@ Artist::refreshView() if (!artistId) throw ArtistNotFoundException {}; - const auto similarArtistIds {Service::get()->getSimilarArtists(*artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; + const auto similarArtistIds {Service::get()->getSimilarArtists(*artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; auto transaction {LmsApp->getDbSession().createSharedTransaction()}; diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index 15334246..facbea1e 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -30,7 +30,7 @@ #include "database/ScanSettings.hpp" #include "database/Session.hpp" #include "database/Track.hpp" -#include "recommendation/IEngine.hpp" +#include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" @@ -101,7 +101,7 @@ Release::refreshView() if (!releaseId) throw ReleaseNotFoundException {}; - auto similarReleasesIds {Service::get()->getSimilarReleases(*releaseId, 6)}; + auto similarReleasesIds {Service::get()->getSimilarReleases(*releaseId, 6)}; auto transaction {LmsApp->getDbSession().createSharedTransaction()}; diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index c88dcc36..54118ffc 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -30,14 +30,14 @@ #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" +#include "services/recommendation/IRecommendationService.hpp" #include "utils/IConfig.hpp" #include "utils/Service.hpp" #include "utils/StreamLogger.hpp" -#include "recommendation/IEngine.hpp" static void -dumpTracksRecommendation(Database::Session session, Recommendation::IEngine& engine, unsigned maxSimilarityCount) +dumpTracksRecommendation(Database::Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) { const std::vector trackIds {[&]() { @@ -66,14 +66,14 @@ dumpTracksRecommendation(Database::Session session, Recommendation::IEngine& eng }; std::cout << "Processing track '" << trackToString(trackId) << std::endl; - for (Database::TrackId similarTrackId : engine.getSimilarTracks({trackId}, maxSimilarityCount)) + for (Database::TrackId similarTrackId : recommendationService.getSimilarTracks({trackId}, maxSimilarityCount)) std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl; } } static void -dumpReleasesRecommendation(Database::Session session, Recommendation::IEngine& engine, unsigned maxSimilarityCount) +dumpReleasesRecommendation(Database::Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) { const std::vector releaseIds = std::invoke([&]() { @@ -93,14 +93,14 @@ dumpReleasesRecommendation(Database::Session session, Recommendation::IEngine& e }; std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl; - for (Database::ReleaseId similarReleaseId : engine.getSimilarReleases(releaseId, maxSimilarityCount)) + for (Database::ReleaseId similarReleaseId : recommendationService.getSimilarReleases(releaseId, maxSimilarityCount)) std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl; } } static void -dumpArtistsRecommendation(Database::Session session, Recommendation::IEngine& engine, unsigned maxSimilarityCount) +dumpArtistsRecommendation(Database::Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) { const std::vector artistIds = std::invoke([&]() { @@ -120,7 +120,7 @@ dumpArtistsRecommendation(Database::Session session, Recommendation::IEngine& en }; std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl; - for (Database::ArtistId similarArtistId : engine.getSimilarArtists(artistId, {Database::TrackArtistLinkType::Artist, Database::TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) + for (Database::ArtistId similarArtistId : recommendationService.getSimilarArtists(artistId, {Database::TrackArtistLinkType::Artist, Database::TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) { std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl; } @@ -161,25 +161,25 @@ int main(int argc, char *argv[]) Database::Db db {config->getPath("working-dir") / "lms.db"}; Database::Session session {db}; - std::cout << "Creating recommendation engine..." << std::endl; - const auto engine {Recommendation::createEngine(db)}; - std::cout << "Recommendation engine created!" << std::endl; + std::cout << "Creating recommendation recommendationService..." << std::endl; + const auto recommendationService {Recommendation::createRecommendationService(db)}; + std::cout << "Recommendation recommendationService created!" << std::endl; - std::cout << "Loading recommendation engine..." << std::endl; - engine->load(false); + std::cout << "Loading recommendation recommendationService..." << std::endl; + recommendationService->load(false); unsigned maxSimilarityCount {vm["max"].as()}; - std::cout << "Recommendation engine loaded!" << std::endl; + std::cout << "Recommendation recommendationService loaded!" << std::endl; if (vm.count("tracks")) - dumpTracksRecommendation(db, *engine, maxSimilarityCount); + dumpTracksRecommendation(db, *recommendationService, maxSimilarityCount); if (vm.count("releases")) - dumpReleasesRecommendation(db, *engine, maxSimilarityCount); + dumpReleasesRecommendation(db, *recommendationService, maxSimilarityCount); if (vm.count("artists")) - dumpArtistsRecommendation(db, *engine, maxSimilarityCount); + dumpArtistsRecommendation(db, *recommendationService, maxSimilarityCount); } catch( std::exception& e) { From 398925588f54955b1358d4041cba1ee8288ee931 Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 20 Oct 2021 20:00:14 +0200 Subject: [PATCH 24/49] Migrated scanner stuff --- src/libs/services/scanner/CMakeLists.txt | 2 +- src/libs/services/scanner/impl/Scanner.hpp | 122 ------------------ .../impl/{Scanner.cpp => ScannerService.cpp} | 56 ++++---- .../services/scanner/impl/ScannerService.hpp | 121 +++++++++++++++++ .../services/scanner/impl/ScannerStats.cpp | 2 +- .../scanner/IScannerService.hpp} | 6 +- .../{ => services}/scanner/ScannerEvents.hpp | 0 .../{ => services}/scanner/ScannerStats.hpp | 0 src/libs/subsonic/impl/Scan.cpp | 10 +- src/lms/main.cpp | 6 +- src/lms/ui/LmsApplication.hpp | 2 +- src/lms/ui/admin/DatabaseSettingsView.cpp | 6 +- src/lms/ui/admin/ScannerController.cpp | 14 +- 13 files changed, 173 insertions(+), 174 deletions(-) delete mode 100644 src/libs/services/scanner/impl/Scanner.hpp rename src/libs/services/scanner/impl/{Scanner.cpp => ScannerService.cpp} (94%) create mode 100644 src/libs/services/scanner/impl/ScannerService.hpp rename src/libs/services/scanner/include/{scanner/IScanner.hpp => services/scanner/IScannerService.hpp} (87%) rename src/libs/services/scanner/include/{ => services}/scanner/ScannerEvents.hpp (100%) rename src/libs/services/scanner/include/{ => services}/scanner/ScannerStats.hpp (100%) diff --git a/src/libs/services/scanner/CMakeLists.txt b/src/libs/services/scanner/CMakeLists.txt index 5f1467b8..9aefadd2 100644 --- a/src/libs/services/scanner/CMakeLists.txt +++ b/src/libs/services/scanner/CMakeLists.txt @@ -1,7 +1,7 @@ add_library(lmsscanner SHARED impl/AcousticBrainzUtils.cpp - impl/Scanner.cpp + impl/ScannerService.cpp impl/ScannerStats.cpp ) diff --git a/src/libs/services/scanner/impl/Scanner.hpp b/src/libs/services/scanner/impl/Scanner.hpp deleted file mode 100644 index 6999ef9a..00000000 --- a/src/libs/services/scanner/impl/Scanner.hpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2013 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "database/Types.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" -#include "metadata/IParser.hpp" -#include "scanner/IScanner.hpp" -#include "utils/Path.hpp" - -class UUID; - -namespace Recommendation -{ - class IRecommendationService; -} - -namespace Scanner { - -class Scanner : public IScanner -{ - public: - Scanner(Database::Db& db, Recommendation::IRecommendationService& recommendationService); - ~Scanner(); - - Scanner(const Scanner&) = delete; - Scanner(Scanner&&) = delete; - Scanner& operator=(const Scanner&) = delete; - Scanner& operator=(Scanner&&) = delete; - - void requestReload() override; - void requestImmediateScan(bool force) override; - - Status getStatus() const override; - Events& getEvents() override { return _events; } - - private: - void start(); - void stop(); - - // Job handling - void scheduleNextScan(); - void scheduleScan(bool force, const Wt::WDateTime& dateTime = {}); - - void abortScan(); - - // Update database (scheduled callback) - void scan(bool force); - - void scanMediaDirectory( const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats); - bool fetchTrackFeatures(Database::TrackId trackId, const UUID& MBID); - void fetchTrackFeatures(ScanStats& stats); - - // Helpers - void refreshScanSettings(); - - void countAllFiles(ScanStats& stats); - void removeMissingTracks(ScanStats& stats); - void removeOrphanEntries(); - void checkDuplicatedAudioFiles(ScanStats& stats); - void scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanStats& stats); - void notifyInProgressIfNeeded(const ScanStepStats& stats); - void notifyInProgress(const ScanStepStats& stats); - void reloadSimilarityEngine(ScanStats& stats); - - Recommendation::IRecommendationService& _recommendationService; - - std::mutex _controlMutex; - std::atomic _abortScan {}; - Wt::WIOService _ioService; - boost::asio::system_timer _scheduleTimer {_ioService}; - Events _events; - std::chrono::system_clock::time_point _lastScanInProgressEmit {}; - Database::Session _dbSession; - std::unique_ptr _metadataParser; - - mutable std::shared_mutex _statusMutex; - State _curState {State::NotScheduled}; - std::optional _lastCompleteScanStats; - std::optional _currentScanStepStats; - Wt::WDateTime _nextScheduledScan; - - // Current scan settings - std::size_t _scanVersion {}; - Wt::WTime _startTime; - Database::ScanSettings::UpdatePeriod _updatePeriod {Database::ScanSettings::UpdatePeriod::Never}; - std::unordered_set _fileExtensions; - std::filesystem::path _mediaDirectory; - Database::ScanSettings::RecommendationEngineType _recommendationServiceType; -}; - -} // Scanner - diff --git a/src/libs/services/scanner/impl/Scanner.cpp b/src/libs/services/scanner/impl/ScannerService.cpp similarity index 94% rename from src/libs/services/scanner/impl/Scanner.cpp rename to src/libs/services/scanner/impl/ScannerService.cpp index 09efc62d..49f2e072 100644 --- a/src/libs/services/scanner/impl/Scanner.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "Scanner.hpp" +#include "ScannerService.hpp" #include #include @@ -248,13 +248,13 @@ getOrCreateClusters(Session& session, const MetaData::Clusters& clustersNames) namespace Scanner { -std::unique_ptr -createScanner(Database::Db& db, Recommendation::IRecommendationService& recommendationService) +std::unique_ptr +createScannerService(Database::Db& db, Recommendation::IRecommendationService& recommendationService) { - return std::make_unique(db, recommendationService); + return std::make_unique(db, recommendationService); } -Scanner::Scanner(Database::Db& db, Recommendation::IRecommendationService& recommendationService) +ScannerService::ScannerService(Database::Db& db, Recommendation::IRecommendationService& recommendationService) : _recommendationService {recommendationService} , _dbSession {db} { @@ -268,14 +268,14 @@ Scanner::Scanner(Database::Db& db, Recommendation::IRecommendationService& recom start(); } -Scanner::~Scanner() +ScannerService::~ScannerService() { LMS_LOG(DBUPDATER, INFO) << "Shutting down Scanner..."; stop(); } void -Scanner::start() +ScannerService::start() { std::scoped_lock lock {_controlMutex}; @@ -296,7 +296,7 @@ Scanner::start() } void -Scanner::stop() +ScannerService::stop() { std::scoped_lock lock {_controlMutex}; @@ -307,7 +307,7 @@ Scanner::stop() } void -Scanner::abortScan() +ScannerService::abortScan() { LMS_LOG(DBUPDATER, DEBUG) << "Aborting scan..."; std::scoped_lock lock {_controlMutex}; @@ -325,7 +325,7 @@ Scanner::abortScan() } void -Scanner::requestImmediateScan(bool force) +ScannerService::requestImmediateScan(bool force) { abortScan(); _ioService.post([=]() @@ -338,7 +338,7 @@ Scanner::requestImmediateScan(bool force) } void -Scanner::requestReload() +ScannerService::requestReload() { abortScan(); _ioService.post([=]() @@ -350,8 +350,8 @@ Scanner::requestReload() }); } -Scanner::Status -Scanner::getStatus() const +ScannerService::Status +ScannerService::getStatus() const { Status res; @@ -366,7 +366,7 @@ Scanner::getStatus() const } void -Scanner::scheduleNextScan() +ScannerService::scheduleNextScan() { LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan"; @@ -420,7 +420,7 @@ Scanner::scheduleNextScan() } void -Scanner::countAllFiles(ScanStats& stats) +ScannerService::countAllFiles(ScanStats& stats) { ScanStepStats stepStats{stats.startTime, ScanProgressStep::DiscoveringFiles}; @@ -445,7 +445,7 @@ Scanner::countAllFiles(ScanStats& stats) } void -Scanner::scheduleScan(bool force, const Wt::WDateTime& dateTime) +ScannerService::scheduleScan(bool force, const Wt::WDateTime& dateTime) { auto cb {[=](boost::system::error_code ec) { @@ -474,7 +474,7 @@ Scanner::scheduleScan(bool force, const Wt::WDateTime& dateTime) } void -Scanner::scan(bool forceScan) +ScannerService::scan(bool forceScan) { _events.scanStarted.emit(); @@ -543,7 +543,7 @@ Scanner::scan(bool forceScan) } bool -Scanner::fetchTrackFeatures(Database::TrackId trackId, const UUID& recordingMBID) +ScannerService::fetchTrackFeatures(Database::TrackId trackId, const UUID& recordingMBID) { std::map features; @@ -569,7 +569,7 @@ Scanner::fetchTrackFeatures(Database::TrackId trackId, const UUID& recordingMBID } void -Scanner::fetchTrackFeatures(ScanStats& stats) +ScannerService::fetchTrackFeatures(ScanStats& stats) { if (_recommendationServiceType != ScanSettings::RecommendationEngineType::Features) return; @@ -619,7 +619,7 @@ Scanner::fetchTrackFeatures(ScanStats& stats) } void -Scanner::refreshScanSettings() +ScannerService::refreshScanSettings() { auto transaction {_dbSession.createSharedTransaction()}; @@ -651,7 +651,7 @@ Scanner::refreshScanSettings() } void -Scanner::notifyInProgress(const ScanStepStats& stepStats) +ScannerService::notifyInProgress(const ScanStepStats& stepStats) { { std::unique_lock lock {_statusMutex}; @@ -664,7 +664,7 @@ Scanner::notifyInProgress(const ScanStepStats& stepStats) } void -Scanner::notifyInProgressIfNeeded(const ScanStepStats& stepStats) +ScannerService::notifyInProgressIfNeeded(const ScanStepStats& stepStats) { std::chrono::system_clock::time_point now {std::chrono::system_clock::now()}; @@ -673,7 +673,7 @@ Scanner::notifyInProgressIfNeeded(const ScanStepStats& stepStats) } void -Scanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanStats& stats) +ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanStats& stats) { Wt::WDateTime lastWriteTime; try @@ -836,7 +836,7 @@ Scanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanSt } void -Scanner::scanMediaDirectory(const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats) +ScannerService::scanMediaDirectory(const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats) { ScanStepStats stepStats{stats.startTime, ScanProgressStep::ScanningFiles}; stepStats.totalElems = stats.filesScanned; @@ -903,7 +903,7 @@ checkFile(const std::filesystem::path& p, const std::filesystem::path& mediaDire } void -Scanner::removeMissingTracks(ScanStats& stats) +ScannerService::removeMissingTracks(ScanStats& stats) { static constexpr std::size_t batchSize {50}; @@ -970,7 +970,7 @@ Scanner::removeMissingTracks(ScanStats& stats) } void -Scanner::removeOrphanEntries() +ScannerService::removeOrphanEntries() { LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan clusters..."; { @@ -1013,7 +1013,7 @@ Scanner::removeOrphanEntries() } void -Scanner::checkDuplicatedAudioFiles(ScanStats& stats) +ScannerService::checkDuplicatedAudioFiles(ScanStats& stats) { LMS_LOG(DBUPDATER, INFO) << "Checking duplicated audio files"; @@ -1033,7 +1033,7 @@ Scanner::checkDuplicatedAudioFiles(ScanStats& stats) } void -Scanner::reloadSimilarityEngine(ScanStats& stats) +ScannerService::reloadSimilarityEngine(ScanStats& stats) { ScanStepStats stepStats {stats.startTime, ScanProgressStep::ReloadingSimilarityEngine}; diff --git a/src/libs/services/scanner/impl/ScannerService.hpp b/src/libs/services/scanner/impl/ScannerService.hpp new file mode 100644 index 00000000..9cb9ce62 --- /dev/null +++ b/src/libs/services/scanner/impl/ScannerService.hpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2013 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "database/Types.hpp" +#include "database/ScanSettings.hpp" +#include "database/Session.hpp" +#include "metadata/IParser.hpp" +#include "services/scanner/IScannerService.hpp" +#include "utils/Path.hpp" + +class UUID; + +namespace Recommendation +{ + class IRecommendationService; +} + +namespace Scanner +{ + class ScannerService : public IScannerService + { + public: + ScannerService(Database::Db& db, Recommendation::IRecommendationService& recommendationService); + ~ScannerService(); + + ScannerService(const ScannerService&) = delete; + ScannerService(ScannerService&&) = delete; + ScannerService& operator=(const ScannerService&) = delete; + ScannerService& operator=(ScannerService&&) = delete; + + void requestReload() override; + void requestImmediateScan(bool force) override; + + Status getStatus() const override; + Events& getEvents() override { return _events; } + + private: + void start(); + void stop(); + + // Job handling + void scheduleNextScan(); + void scheduleScan(bool force, const Wt::WDateTime& dateTime = {}); + + void abortScan(); + + // Update database (scheduled callback) + void scan(bool force); + + void scanMediaDirectory( const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats); + bool fetchTrackFeatures(Database::TrackId trackId, const UUID& MBID); + void fetchTrackFeatures(ScanStats& stats); + + // Helpers + void refreshScanSettings(); + + void countAllFiles(ScanStats& stats); + void removeMissingTracks(ScanStats& stats); + void removeOrphanEntries(); + void checkDuplicatedAudioFiles(ScanStats& stats); + void scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanStats& stats); + void notifyInProgressIfNeeded(const ScanStepStats& stats); + void notifyInProgress(const ScanStepStats& stats); + void reloadSimilarityEngine(ScanStats& stats); + + Recommendation::IRecommendationService& _recommendationService; + + std::mutex _controlMutex; + std::atomic _abortScan {}; + Wt::WIOService _ioService; + boost::asio::system_timer _scheduleTimer {_ioService}; + Events _events; + std::chrono::system_clock::time_point _lastScanInProgressEmit {}; + Database::Session _dbSession; + std::unique_ptr _metadataParser; + + mutable std::shared_mutex _statusMutex; + State _curState {State::NotScheduled}; + std::optional _lastCompleteScanStats; + std::optional _currentScanStepStats; + Wt::WDateTime _nextScheduledScan; + + // Current scan settings + std::size_t _scanVersion {}; + Wt::WTime _startTime; + Database::ScanSettings::UpdatePeriod _updatePeriod {Database::ScanSettings::UpdatePeriod::Never}; + std::unordered_set _fileExtensions; + std::filesystem::path _mediaDirectory; + Database::ScanSettings::RecommendationEngineType _recommendationServiceType; + }; +} // Scanner + diff --git a/src/libs/services/scanner/impl/ScannerStats.cpp b/src/libs/services/scanner/impl/ScannerStats.cpp index afc12f99..e4b71180 100644 --- a/src/libs/services/scanner/impl/ScannerStats.cpp +++ b/src/libs/services/scanner/impl/ScannerStats.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "scanner/ScannerStats.hpp" +#include "services/scanner/ScannerStats.hpp" namespace Scanner { diff --git a/src/libs/services/scanner/include/scanner/IScanner.hpp b/src/libs/services/scanner/include/services/scanner/IScannerService.hpp similarity index 87% rename from src/libs/services/scanner/include/scanner/IScanner.hpp rename to src/libs/services/scanner/include/services/scanner/IScannerService.hpp index 602a4626..6085f6b8 100644 --- a/src/libs/services/scanner/include/scanner/IScanner.hpp +++ b/src/libs/services/scanner/include/services/scanner/IScannerService.hpp @@ -37,10 +37,10 @@ namespace Recommendation namespace Scanner { - class IScanner + class IScannerService { public: - virtual ~IScanner() = default; + virtual ~IScannerService() = default; // Async requests virtual void requestReload() = 0; @@ -66,7 +66,7 @@ namespace Scanner virtual Events& getEvents() = 0; }; - std::unique_ptr createScanner(Database::Db& db, Recommendation::IRecommendationService& recommendationEngine); + std::unique_ptr createScannerService(Database::Db& db, Recommendation::IRecommendationService& recommendationEngine); } // Scanner diff --git a/src/libs/services/scanner/include/scanner/ScannerEvents.hpp b/src/libs/services/scanner/include/services/scanner/ScannerEvents.hpp similarity index 100% rename from src/libs/services/scanner/include/scanner/ScannerEvents.hpp rename to src/libs/services/scanner/include/services/scanner/ScannerEvents.hpp diff --git a/src/libs/services/scanner/include/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp similarity index 100% rename from src/libs/services/scanner/include/scanner/ScannerStats.hpp rename to src/libs/services/scanner/include/services/scanner/ScannerStats.hpp diff --git a/src/libs/subsonic/impl/Scan.cpp b/src/libs/subsonic/impl/Scan.cpp index dee3c082..d9a48d67 100644 --- a/src/libs/subsonic/impl/Scan.cpp +++ b/src/libs/subsonic/impl/Scan.cpp @@ -19,7 +19,7 @@ #include "Scan.hpp" -#include "scanner/IScanner.hpp" +#include "services/scanner/IScannerService.hpp" #include "utils/Service.hpp" namespace API::Subsonic::Scan @@ -32,10 +32,10 @@ namespace API::Subsonic::Scan { Response::Node statusResponse; - const IScanner::Status scanStatus {Service::get()->getStatus()}; + const IScannerService::Status scanStatus {Service::get()->getStatus()}; - statusResponse.setAttribute("scanning", scanStatus.currentState == IScanner::State::InProgress); - if (scanStatus.currentState == IScanner::State::InProgress) + statusResponse.setAttribute("scanning", scanStatus.currentState == IScannerService::State::InProgress); + if (scanStatus.currentState == IScannerService::State::InProgress) { std::size_t count{}; @@ -61,7 +61,7 @@ namespace API::Subsonic::Scan Response handleStartScan(RequestContext& context) { - Service::get()->requestImmediateScan(false); + Service::get()->requestImmediateScan(false); Response response {Response::createOkResponse(context.serverProtocolVersion)}; response.addNode("scanStatus", createStatusResponseNode()); diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 3d452a67..67d221f0 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -31,7 +31,7 @@ #include "services/cover/ICoverService.hpp" #include "database/Db.hpp" #include "database/Session.hpp" -#include "scanner/IScanner.hpp" +#include "services/scanner/IScannerService.hpp" #include "subsonic/SubsonicResource.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "services/scrobbling/IScrobblingService.hpp" @@ -132,7 +132,7 @@ generateWtConfig(std::string execPath) static void -proxyScannerEventsToApplication(Scanner::IScanner& scanner, Wt::WServer& server) +proxyScannerEventsToApplication(Scanner::IScannerService& scanner, Wt::WServer& server) { auto postAll {[](Wt::WServer& server, std::function cb) { @@ -259,7 +259,7 @@ int main(int argc, char* argv[]) Service httpClient {Http::createClient(ioContext)}; Service coverService {Cover::createCoverService(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; Service recommendationService {Recommendation::createRecommendationService(database)}; - Service scannerService {Scanner::createScanner(database, *recommendationService)}; + Service scannerService {Scanner::createScannerService(database, *recommendationService)}; scannerService->getEvents().scanComplete.connect([&] { diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 7f8f9446..6c32fffe 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -23,7 +23,7 @@ #include -#include "scanner/ScannerEvents.hpp" +#include "services/scanner/ScannerEvents.hpp" namespace Database { diff --git a/src/lms/ui/admin/DatabaseSettingsView.cpp b/src/lms/ui/admin/DatabaseSettingsView.cpp index d410b4cd..d73a8241 100644 --- a/src/lms/ui/admin/DatabaseSettingsView.cpp +++ b/src/lms/ui/admin/DatabaseSettingsView.cpp @@ -29,7 +29,7 @@ #include "database/Cluster.hpp" #include "database/ScanSettings.hpp" #include "database/Session.hpp" -#include "scanner/IScanner.hpp" +#include "services/scanner/IScannerService.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" #include "utils/String.hpp" @@ -240,7 +240,7 @@ DatabaseSettingsView::refreshView() { model->saveData(); - Service::get()->requestImmediateScan(false); + Service::get()->requestImmediateScan(false); LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Admin.Database.settings-saved")); } @@ -257,7 +257,7 @@ DatabaseSettingsView::refreshView() immScanBtn->clicked().connect([=] { - Service::get()->requestImmediateScan(false); + Service::get()->requestImmediateScan(false); }); t->updateView(model.get()); diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index f01cfc71..61199f23 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -30,7 +30,7 @@ #include "database/Session.hpp" #include "database/Track.hpp" -#include "scanner/IScanner.hpp" +#include "services/scanner/IScannerService.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" @@ -163,20 +163,20 @@ ScannerController::refreshContents() actionBtn->actionButton()->setText(Wt::WString::tr("Lms.Admin.ScannerController.scan-now")); actionBtn->actionButton()->clicked().connect([] { - Service::get()->requestImmediateScan(false); + Service::get()->requestImmediateScan(false); }); auto popup = std::make_unique(); popup->addItem(Wt::WString::tr("Lms.Admin.ScannerController.force-scan-now")); popup->itemSelected().connect([] { - Service::get()->requestImmediateScan(true); + Service::get()->requestImmediateScan(true); }); actionBtn->dropDownButton()->setMenu(std::move(popup)); actionBtn->dropDownButton()->addStyleClass("btn-primary"); - const IScanner::Status status {Service::get()->getStatus()}; + const IScannerService::Status status {Service::get()->getStatus()}; if (status.lastCompleteScanStats) { bindString("last-scan", Wt::WString::tr("Lms.Admin.ScannerController.last-scan-status") @@ -200,16 +200,16 @@ ScannerController::refreshContents() switch (status.currentState) { - case IScanner::State::NotScheduled: + case IScannerService::State::NotScheduled: bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-not-scheduled")); bindEmpty("step-status"); break; - case IScanner::State::Scheduled: + case IScannerService::State::Scheduled: bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-scheduled") .arg(status.nextScheduledScan.toString())); bindEmpty("step-status"); break; - case IScanner::State::InProgress: + case IScannerService::State::InProgress: bindString("status", Wt::WString::tr("Lms.Admin.ScannerController.status-in-progress") .arg(static_cast(status.currentScanStepStats->currentStep) + 1) .arg(Scanner::ScanProgressStepCount)); From 9adfafc58954b8eb58219d95808c8aaf020675c0 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 21 Oct 2021 13:36:41 +0200 Subject: [PATCH 25/49] Migrated database stuff --- .../services/auth/impl/AuthServiceBase.cpp | 6 +- .../services/auth/impl/AuthServiceBase.hpp | 2 +- .../services/auth/impl/AuthTokenService.cpp | 4 +- .../auth/impl/PasswordServiceBase.cpp | 4 +- .../impl/internal/InternalPasswordService.cpp | 4 +- .../impl/internal/InternalPasswordService.hpp | 2 +- .../auth/impl/pam/PAMPasswordService.cpp | 2 +- .../services/auth/IAuthTokenService.hpp | 2 +- .../include/services/auth/IEnvService.hpp | 2 +- .../services/auth/IPasswordService.hpp | 2 +- .../auth/include/services/auth/Types.hpp | 2 +- src/libs/services/cover/impl/CoverService.cpp | 8 +-- src/libs/services/cover/impl/CoverService.hpp | 2 +- .../include/services/cover/ICoverService.hpp | 2 +- src/libs/services/database/impl/Artist.cpp | 12 ++-- src/libs/services/database/impl/Cluster.cpp | 12 ++-- src/libs/services/database/impl/Db.cpp | 6 +- src/libs/services/database/impl/Release.cpp | 12 ++-- .../services/database/impl/ScanSettings.cpp | 6 +- src/libs/services/database/impl/Session.cpp | 24 +++---- src/libs/services/database/impl/Track.cpp | 16 ++--- .../database/impl/TrackArtistLink.cpp | 8 +-- .../services/database/impl/TrackBookmark.cpp | 8 +-- .../services/database/impl/TrackFeatures.cpp | 6 +- src/libs/services/database/impl/TrackList.cpp | 14 ++-- src/libs/services/database/impl/Traits.hpp | 2 +- src/libs/services/database/impl/User.cpp | 12 ++-- .../{ => services}/database/Artist.hpp | 2 +- .../{ => services}/database/Cluster.hpp | 2 +- .../include/{ => services}/database/Db.hpp | 0 .../{ => services}/database/Release.hpp | 2 +- .../{ => services}/database/ScanSettings.hpp | 2 +- .../{ => services}/database/Session.hpp | 0 .../include/{ => services}/database/Track.hpp | 2 +- .../database/TrackArtistLink.hpp | 2 +- .../{ => services}/database/TrackBookmark.hpp | 2 +- .../{ => services}/database/TrackFeatures.hpp | 2 +- .../{ => services}/database/TrackList.hpp | 2 +- .../include/{ => services}/database/Types.hpp | 0 .../include/{ => services}/database/User.hpp | 2 +- src/libs/services/database/test/Common.hpp | 22 +++--- .../services/recommendation/impl/IEngine.hpp | 2 +- .../impl/RecommendationService.cpp | 6 +- .../impl/clusters/ClustersEngine.cpp | 14 ++-- .../impl/features/FeaturesEngine.cpp | 16 ++--- .../impl/features/FeaturesEngineCache.hpp | 2 +- .../include/services/recommendation/Types.hpp | 2 +- .../services/scanner/impl/ScannerService.cpp | 14 ++-- .../services/scanner/impl/ScannerService.hpp | 6 +- .../include/services/scanner/ScannerStats.hpp | 2 +- .../scrobbling/impl/ScrobblingService.cpp | 14 ++-- .../impl/internal/InternalScrobbler.cpp | 10 +-- .../listenbrainz/ListenBrainzScrobbler.cpp | 14 ++-- .../impl/listenbrainz/ListensSynchronizer.cpp | 14 ++-- .../impl/listenbrainz/ListensSynchronizer.hpp | 2 +- .../scrobbling/impl/listenbrainz/Utils.cpp | 6 +- .../scrobbling/impl/listenbrainz/Utils.hpp | 2 +- .../scrobbling/IScrobblingService.hpp | 2 +- .../include/services/scrobbling/Listen.hpp | 2 +- src/libs/subsonic/impl/ParameterParsing.hpp | 2 +- src/libs/subsonic/impl/RequestContext.hpp | 2 +- src/libs/subsonic/impl/Stream.cpp | 6 +- src/libs/subsonic/impl/SubsonicId.hpp | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 18 ++--- src/libs/subsonic/impl/SubsonicResource.hpp | 2 +- src/lms/main.cpp | 8 +-- src/lms/ui/Auth.cpp | 4 +- src/lms/ui/Auth.hpp | 23 +++--- src/lms/ui/LmsApplication.cpp | 12 ++-- src/lms/ui/LmsApplicationException.hpp | 59 ++++++++------- src/lms/ui/LmsApplicationManager.hpp | 2 +- src/lms/ui/LmsTheme.hpp | 26 ++++--- src/lms/ui/MediaPlayer.cpp | 14 ++-- src/lms/ui/MediaPlayer.hpp | 2 +- src/lms/ui/PlayQueue.cpp | 12 ++-- src/lms/ui/PlayQueue.hpp | 2 +- src/lms/ui/PlayQueueAction.hpp | 3 +- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/DatabaseSettingsView.cpp | 6 +- src/lms/ui/admin/InitWizardView.cpp | 4 +- src/lms/ui/admin/ScannerController.cpp | 4 +- src/lms/ui/admin/UserView.cpp | 4 +- src/lms/ui/admin/UsersView.cpp | 11 ++- src/lms/ui/common/LoginNameValidator.cpp | 2 +- src/lms/ui/common/PasswordValidator.hpp | 2 +- src/lms/ui/explore/ArtistCollector.cpp | 6 +- src/lms/ui/explore/ArtistCollector.hpp | 3 +- src/lms/ui/explore/ArtistListHelpers.cpp | 2 +- src/lms/ui/explore/ArtistListHelpers.hpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 14 ++-- src/lms/ui/explore/ArtistView.hpp | 2 +- src/lms/ui/explore/ArtistsView.cpp | 4 +- src/lms/ui/explore/ArtistsView.hpp | 2 +- src/lms/ui/explore/DatabaseCollectorBase.hpp | 2 +- src/lms/ui/explore/Explore.cpp | 8 +-- src/lms/ui/explore/Explore.hpp | 36 +++++----- src/lms/ui/explore/Filters.cpp | 4 +- src/lms/ui/explore/Filters.hpp | 34 ++++----- src/lms/ui/explore/ReleaseCollector.cpp | 8 +-- src/lms/ui/explore/ReleaseListHelpers.cpp | 6 +- src/lms/ui/explore/ReleaseListHelpers.hpp | 2 +- src/lms/ui/explore/ReleasePopup.cpp | 9 +-- src/lms/ui/explore/ReleasePopup.hpp | 3 +- src/lms/ui/explore/ReleaseView.cpp | 10 +-- src/lms/ui/explore/ReleaseView.hpp | 4 +- src/lms/ui/explore/ReleasesView.cpp | 2 +- src/lms/ui/explore/ReleasesView.hpp | 3 +- src/lms/ui/explore/SearchView.cpp | 12 ++-- src/lms/ui/explore/TrackCollector.cpp | 8 +-- src/lms/ui/explore/TrackListHelpers.cpp | 6 +- src/lms/ui/explore/TrackListHelpers.hpp | 2 +- src/lms/ui/explore/TrackPopup.cpp | 7 +- src/lms/ui/explore/TrackPopup.hpp | 2 +- src/lms/ui/explore/TracksView.cpp | 2 +- src/lms/ui/explore/TracksView.hpp | 2 +- src/lms/ui/resource/AudioFileResource.cpp | 4 +- src/lms/ui/resource/AudioFileResource.hpp | 32 ++++----- .../ui/resource/AudioTranscodeResource.cpp | 7 +- .../ui/resource/AudioTranscodeResource.hpp | 25 ++++--- src/lms/ui/resource/CoverResource.cpp | 2 +- src/lms/ui/resource/CoverResource.hpp | 4 +- src/lms/ui/resource/DownloadResource.cpp | 8 +-- src/lms/ui/resource/DownloadResource.hpp | 72 +++++++++---------- src/tools/cover/LmsCover.cpp | 8 +-- .../recommendation/LmsRecommendation.cpp | 12 ++-- 125 files changed, 464 insertions(+), 505 deletions(-) rename src/libs/services/database/include/{ => services}/database/Artist.hpp (99%) rename src/libs/services/database/include/{ => services}/database/Cluster.hpp (98%) rename src/libs/services/database/include/{ => services}/database/Db.hpp (100%) rename src/libs/services/database/include/{ => services}/database/Release.hpp (99%) rename src/libs/services/database/include/{ => services}/database/ScanSettings.hpp (98%) rename src/libs/services/database/include/{ => services}/database/Session.hpp (100%) rename src/libs/services/database/include/{ => services}/database/Track.hpp (99%) rename src/libs/services/database/include/{ => services}/database/TrackArtistLink.hpp (97%) rename src/libs/services/database/include/{ => services}/database/TrackBookmark.hpp (98%) rename src/libs/services/database/include/{ => services}/database/TrackFeatures.hpp (97%) rename src/libs/services/database/include/{ => services}/database/TrackList.hpp (99%) rename src/libs/services/database/include/{ => services}/database/Types.hpp (100%) rename src/libs/services/database/include/{ => services}/database/User.hpp (99%) diff --git a/src/libs/services/auth/impl/AuthServiceBase.cpp b/src/libs/services/auth/impl/AuthServiceBase.cpp index 6e0d0589..6b3c9a95 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.cpp +++ b/src/libs/services/auth/impl/AuthServiceBase.cpp @@ -19,9 +19,9 @@ #include "AuthServiceBase.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Db.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" namespace Auth diff --git a/src/libs/services/auth/impl/AuthServiceBase.hpp b/src/libs/services/auth/impl/AuthServiceBase.hpp index 81acba59..812e716f 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.hpp +++ b/src/libs/services/auth/impl/AuthServiceBase.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/auth/impl/AuthTokenService.cpp b/src/libs/services/auth/impl/AuthTokenService.cpp index f4104ca0..24b9cee4 100644 --- a/src/libs/services/auth/impl/AuthTokenService.cpp +++ b/src/libs/services/auth/impl/AuthTokenService.cpp @@ -24,8 +24,8 @@ #include #include "services/auth/Types.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/services/auth/impl/PasswordServiceBase.cpp b/src/libs/services/auth/impl/PasswordServiceBase.cpp index bd1901ee..79f57af1 100644 --- a/src/libs/services/auth/impl/PasswordServiceBase.cpp +++ b/src/libs/services/auth/impl/PasswordServiceBase.cpp @@ -28,8 +28,8 @@ #endif // LMS_SUPPORT_PAM #include "services/auth/Types.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp index fe162150..d63de7ed 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp @@ -22,8 +22,8 @@ #include "services/auth/IAuthTokenService.hpp" #include "services/auth/Types.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.hpp b/src/libs/services/auth/impl/internal/InternalPasswordService.hpp index 6c241f31..7754953a 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.hpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/User.hpp" +#include "services/database/User.hpp" #include "PasswordServiceBase.hpp" #include "LoginThrottler.hpp" diff --git a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp index e24de954..80c92644 100644 --- a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp @@ -27,7 +27,7 @@ #include #include "services/auth/Types.hpp" -#include "database/Session.hpp" +#include "services/database/Session.hpp" #include "utils/Logger.hpp" namespace Auth diff --git a/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp b/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp index 5d9dd51b..884dd7d1 100644 --- a/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp +++ b/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp @@ -27,7 +27,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/auth/include/services/auth/IEnvService.hpp b/src/libs/services/auth/include/services/auth/IEnvService.hpp index ecb23349..291bfab6 100644 --- a/src/libs/services/auth/include/services/auth/IEnvService.hpp +++ b/src/libs/services/auth/include/services/auth/IEnvService.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/auth/include/services/auth/IPasswordService.hpp b/src/libs/services/auth/include/services/auth/IPasswordService.hpp index 6a9cb2bd..4958e746 100644 --- a/src/libs/services/auth/include/services/auth/IPasswordService.hpp +++ b/src/libs/services/auth/include/services/auth/IPasswordService.hpp @@ -26,7 +26,7 @@ #include #include "services/auth/Types.hpp" -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/auth/include/services/auth/Types.hpp b/src/libs/services/auth/include/services/auth/Types.hpp index 8b97df6e..df470746 100644 --- a/src/libs/services/auth/include/services/auth/Types.hpp +++ b/src/libs/services/auth/include/services/auth/Types.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/Exception.hpp" namespace Auth diff --git a/src/libs/services/cover/impl/CoverService.cpp b/src/libs/services/cover/impl/CoverService.cpp index a968d5c4..58537ad1 100644 --- a/src/libs/services/cover/impl/CoverService.cpp +++ b/src/libs/services/cover/impl/CoverService.cpp @@ -21,10 +21,10 @@ #include "av/IAudioFile.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "image/Exception.hpp" #include "image/IRawImage.hpp" diff --git a/src/libs/services/cover/impl/CoverService.hpp b/src/libs/services/cover/impl/CoverService.hpp index 866ed8da..bf6e9d6c 100644 --- a/src/libs/services/cover/impl/CoverService.hpp +++ b/src/libs/services/cover/impl/CoverService.hpp @@ -31,7 +31,7 @@ #include "services/cover/ICoverService.hpp" #include "image/IEncodedImage.hpp" -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/cover/include/services/cover/ICoverService.hpp b/src/libs/services/cover/include/services/cover/ICoverService.hpp index 039260ef..392f0dfc 100644 --- a/src/libs/services/cover/include/services/cover/ICoverService.hpp +++ b/src/libs/services/cover/include/services/cover/ICoverService.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "image/IEncodedImage.hpp" namespace Database diff --git a/src/libs/services/database/impl/Artist.cpp b/src/libs/services/database/impl/Artist.cpp index 9ca631fa..9b139381 100644 --- a/src/libs/services/database/impl/Artist.cpp +++ b/src/libs/services/database/impl/Artist.cpp @@ -16,15 +16,15 @@ * You should have received a copy of the GNU General Public License * along with LMS. If not, see . */ -#include "database/Artist.hpp" +#include "services/database/Artist.hpp" #include -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Utils.hpp" diff --git a/src/libs/services/database/impl/Cluster.cpp b/src/libs/services/database/impl/Cluster.cpp index 184a03a1..cf4350cc 100644 --- a/src/libs/services/database/impl/Cluster.cpp +++ b/src/libs/services/database/impl/Cluster.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/Cluster.hpp" +#include "services/database/Cluster.hpp" -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "SqlQuery.hpp" #include "Traits.hpp" diff --git a/src/libs/services/database/impl/Db.cpp b/src/libs/services/database/impl/Db.cpp index 5035466c..7eb7bf7a 100644 --- a/src/libs/services/database/impl/Db.cpp +++ b/src/libs/services/database/impl/Db.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/Db.hpp" +#include "services/database/Db.hpp" #include #include -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" namespace Database { diff --git a/src/libs/services/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp index 310de51e..a25d23db 100644 --- a/src/libs/services/database/impl/Release.cpp +++ b/src/libs/services/database/impl/Release.cpp @@ -17,15 +17,15 @@ * along with LMS. If not, see . */ -#include "database/Release.hpp" +#include "services/database/Release.hpp" #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Traits.hpp" diff --git a/src/libs/services/database/impl/ScanSettings.cpp b/src/libs/services/database/impl/ScanSettings.cpp index 9aafed53..0b509f32 100644 --- a/src/libs/services/database/impl/ScanSettings.cpp +++ b/src/libs/services/database/impl/ScanSettings.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "database/ScanSettings.hpp" +#include "services/database/ScanSettings.hpp" #include @@ -25,8 +25,8 @@ #include "utils/Logger.hpp" #include "utils/String.hpp" -#include "database/Cluster.hpp" -#include "database/Session.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Session.hpp" namespace { diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 56ec77d1..7b219a08 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "database/Session.hpp" +#include "services/database/Session.hpp" #include #include @@ -27,17 +27,17 @@ #include "utils/Exception.hpp" #include "utils/Logger.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Track.hpp" -#include "database/TrackBookmark.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackList.hpp" -#include "database/TrackFeatures.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackBookmark.hpp" +#include "services/database/TrackArtistLink.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/TrackFeatures.hpp" +#include "services/database/User.hpp" namespace Database { diff --git a/src/libs/services/database/impl/Track.cpp b/src/libs/services/database/impl/Track.cpp index 82dc99cf..d1968146 100644 --- a/src/libs/services/database/impl/Track.cpp +++ b/src/libs/services/database/impl/Track.cpp @@ -17,17 +17,17 @@ * along with LMS. If not, see . */ -#include "database/Track.hpp" +#include "services/database/Track.hpp" #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackFeatures.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Release.hpp" +#include "services/database/TrackArtistLink.hpp" +#include "services/database/TrackFeatures.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" diff --git a/src/libs/services/database/impl/TrackArtistLink.cpp b/src/libs/services/database/impl/TrackArtistLink.cpp index f8791dee..03bbbbee 100644 --- a/src/libs/services/database/impl/TrackArtistLink.cpp +++ b/src/libs/services/database/impl/TrackArtistLink.cpp @@ -17,11 +17,11 @@ * along with LMS. If not, see . */ -#include "database/TrackArtistLink.hpp" +#include "services/database/TrackArtistLink.hpp" -#include "database/Artist.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "Traits.hpp" diff --git a/src/libs/services/database/impl/TrackBookmark.cpp b/src/libs/services/database/impl/TrackBookmark.cpp index 91f457bd..5a963c3c 100644 --- a/src/libs/services/database/impl/TrackBookmark.cpp +++ b/src/libs/services/database/impl/TrackBookmark.cpp @@ -17,11 +17,11 @@ * along with LMS. If not, see . */ -#include "database/TrackBookmark.hpp" +#include "services/database/TrackBookmark.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" #include "Traits.hpp" namespace Database { diff --git a/src/libs/services/database/impl/TrackFeatures.cpp b/src/libs/services/database/impl/TrackFeatures.cpp index 5770d0b1..65cc9b7a 100644 --- a/src/libs/services/database/impl/TrackFeatures.cpp +++ b/src/libs/services/database/impl/TrackFeatures.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/TrackFeatures.hpp" +#include "services/database/TrackFeatures.hpp" #include #include -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "utils/Logger.hpp" namespace Database { diff --git a/src/libs/services/database/impl/TrackList.cpp b/src/libs/services/database/impl/TrackList.cpp index a9863d52..8e69434b 100644 --- a/src/libs/services/database/impl/TrackList.cpp +++ b/src/libs/services/database/impl/TrackList.cpp @@ -16,18 +16,18 @@ * You should have received a copy of the GNU General Public License * along with LMS. If not, see . */ -#include "database/TrackList.hpp" +#include "services/database/TrackList.hpp" #include #include "utils/Logger.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" +#include "services/database/Track.hpp" #include "SqlQuery.hpp" #include "StringViewTraits.hpp" #include "Traits.hpp" diff --git a/src/libs/services/database/impl/Traits.hpp b/src/libs/services/database/impl/Traits.hpp index bdfa0045..54b3e8d7 100644 --- a/src/libs/services/database/impl/Traits.hpp +++ b/src/libs/services/database/impl/Traits.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Wt::Dbo { diff --git a/src/libs/services/database/impl/User.cpp b/src/libs/services/database/impl/User.cpp index 44f8c2b1..038c3a55 100644 --- a/src/libs/services/database/impl/User.cpp +++ b/src/libs/services/database/impl/User.cpp @@ -17,13 +17,13 @@ * along with LMS. If not, see . */ -#include "database/User.hpp" +#include "services/database/User.hpp" -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" #include "utils/Logger.hpp" #include "StringViewTraits.hpp" #include "Traits.hpp" diff --git a/src/libs/services/database/include/database/Artist.hpp b/src/libs/services/database/include/services/database/Artist.hpp similarity index 99% rename from src/libs/services/database/include/database/Artist.hpp rename to src/libs/services/database/include/services/database/Artist.hpp index 23ef7812..0f5785bc 100644 --- a/src/libs/services/database/include/database/Artist.hpp +++ b/src/libs/services/database/include/services/database/Artist.hpp @@ -27,7 +27,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" diff --git a/src/libs/services/database/include/database/Cluster.hpp b/src/libs/services/database/include/services/database/Cluster.hpp similarity index 98% rename from src/libs/services/database/include/database/Cluster.hpp rename to src/libs/services/database/include/services/database/Cluster.hpp index 42a11e77..63032f75 100644 --- a/src/libs/services/database/include/database/Cluster.hpp +++ b/src/libs/services/database/include/services/database/Cluster.hpp @@ -26,7 +26,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/database/include/database/Db.hpp b/src/libs/services/database/include/services/database/Db.hpp similarity index 100% rename from src/libs/services/database/include/database/Db.hpp rename to src/libs/services/database/include/services/database/Db.hpp diff --git a/src/libs/services/database/include/database/Release.hpp b/src/libs/services/database/include/services/database/Release.hpp similarity index 99% rename from src/libs/services/database/include/database/Release.hpp rename to src/libs/services/database/include/services/database/Release.hpp index ce45a1a8..6af4e700 100644 --- a/src/libs/services/database/include/database/Release.hpp +++ b/src/libs/services/database/include/services/database/Release.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/UUID.hpp" namespace Database diff --git a/src/libs/services/database/include/database/ScanSettings.hpp b/src/libs/services/database/include/services/database/ScanSettings.hpp similarity index 98% rename from src/libs/services/database/include/database/ScanSettings.hpp rename to src/libs/services/database/include/services/database/ScanSettings.hpp index 9cb8cb5a..0a482956 100644 --- a/src/libs/services/database/include/database/ScanSettings.hpp +++ b/src/libs/services/database/include/services/database/ScanSettings.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/database/include/database/Session.hpp b/src/libs/services/database/include/services/database/Session.hpp similarity index 100% rename from src/libs/services/database/include/database/Session.hpp rename to src/libs/services/database/include/services/database/Session.hpp diff --git a/src/libs/services/database/include/database/Track.hpp b/src/libs/services/database/include/services/database/Track.hpp similarity index 99% rename from src/libs/services/database/include/database/Track.hpp rename to src/libs/services/database/include/services/database/Track.hpp index 3e2c2ced..ccc9bb4e 100644 --- a/src/libs/services/database/include/database/Track.hpp +++ b/src/libs/services/database/include/services/database/Track.hpp @@ -34,7 +34,7 @@ #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/database/include/database/TrackArtistLink.hpp b/src/libs/services/database/include/services/database/TrackArtistLink.hpp similarity index 97% rename from src/libs/services/database/include/database/TrackArtistLink.hpp rename to src/libs/services/database/include/services/database/TrackArtistLink.hpp index 55d3b0c5..7708c69e 100644 --- a/src/libs/services/database/include/database/TrackArtistLink.hpp +++ b/src/libs/services/database/include/services/database/TrackArtistLink.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/EnumSet.hpp" namespace Database diff --git a/src/libs/services/database/include/database/TrackBookmark.hpp b/src/libs/services/database/include/services/database/TrackBookmark.hpp similarity index 98% rename from src/libs/services/database/include/database/TrackBookmark.hpp rename to src/libs/services/database/include/services/database/TrackBookmark.hpp index 2c4af0db..34ddb542 100644 --- a/src/libs/services/database/include/database/TrackBookmark.hpp +++ b/src/libs/services/database/include/services/database/TrackBookmark.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/database/include/database/TrackFeatures.hpp b/src/libs/services/database/include/services/database/TrackFeatures.hpp similarity index 97% rename from src/libs/services/database/include/database/TrackFeatures.hpp rename to src/libs/services/database/include/services/database/TrackFeatures.hpp index 99c36d6d..fd251647 100644 --- a/src/libs/services/database/include/database/TrackFeatures.hpp +++ b/src/libs/services/database/include/services/database/TrackFeatures.hpp @@ -26,7 +26,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/database/include/database/TrackList.hpp b/src/libs/services/database/include/services/database/TrackList.hpp similarity index 99% rename from src/libs/services/database/include/database/TrackList.hpp rename to src/libs/services/database/include/services/database/TrackList.hpp index 03aaca0f..48f56c23 100644 --- a/src/libs/services/database/include/database/TrackList.hpp +++ b/src/libs/services/database/include/services/database/TrackList.hpp @@ -27,7 +27,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/database/include/database/Types.hpp b/src/libs/services/database/include/services/database/Types.hpp similarity index 100% rename from src/libs/services/database/include/database/Types.hpp rename to src/libs/services/database/include/services/database/Types.hpp diff --git a/src/libs/services/database/include/database/User.hpp b/src/libs/services/database/include/services/database/User.hpp similarity index 99% rename from src/libs/services/database/include/database/User.hpp rename to src/libs/services/database/include/services/database/User.hpp index b94d41af..34748567 100644 --- a/src/libs/services/database/include/database/User.hpp +++ b/src/libs/services/database/include/services/database/User.hpp @@ -26,7 +26,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/UUID.hpp" namespace Database { diff --git a/src/libs/services/database/test/Common.hpp b/src/libs/services/database/test/Common.hpp index 8864e681..75aa654d 100644 --- a/src/libs/services/database/test/Common.hpp +++ b/src/libs/services/database/test/Common.hpp @@ -24,17 +24,17 @@ #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackBookmark.hpp" -#include "database/TrackList.hpp" -#include "database/Types.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackArtistLink.hpp" +#include "services/database/TrackBookmark.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/Types.hpp" +#include "services/database/User.hpp" template class ScopedEntity diff --git a/src/libs/services/recommendation/impl/IEngine.hpp b/src/libs/services/recommendation/impl/IEngine.hpp index 8c16b407..b4b9da65 100644 --- a/src/libs/services/recommendation/impl/IEngine.hpp +++ b/src/libs/services/recommendation/impl/IEngine.hpp @@ -22,7 +22,7 @@ #include #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/EnumSet.hpp" #include "services/recommendation/Types.hpp" diff --git a/src/libs/services/recommendation/impl/RecommendationService.cpp b/src/libs/services/recommendation/impl/RecommendationService.cpp index b2b6f235..59a805ec 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.cpp +++ b/src/libs/services/recommendation/impl/RecommendationService.cpp @@ -25,9 +25,9 @@ #include "ClustersEngineCreator.hpp" #include "FeaturesEngineCreator.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" -#include "database/ScanSettings.hpp" +#include "services/database/Db.hpp" +#include "services/database/Session.hpp" +#include "services/database/ScanSettings.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp index f873443a..f2f3d458 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp @@ -19,13 +19,13 @@ #include "ClustersEngine.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" namespace Recommendation { diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp index 7610ee75..bb030bd6 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp @@ -21,14 +21,14 @@ #include -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackFeatures.hpp" -#include "database/TrackList.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackArtistLink.hpp" +#include "services/database/TrackFeatures.hpp" +#include "services/database/TrackList.hpp" #include "som/DataNormalizer.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp index 08372345..348c66af 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp @@ -23,7 +23,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "som/Network.hpp" namespace Recommendation { diff --git a/src/libs/services/recommendation/include/services/recommendation/Types.hpp b/src/libs/services/recommendation/include/services/recommendation/Types.hpp index 08fa2f90..0b258c3f 100644 --- a/src/libs/services/recommendation/include/services/recommendation/Types.hpp +++ b/src/libs/services/recommendation/include/services/recommendation/Types.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Recommendation { diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index 49f2e072..d96663be 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -24,13 +24,13 @@ #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Track.hpp" -#include "database/TrackArtistLink.hpp" -#include "database/TrackFeatures.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Release.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackArtistLink.hpp" +#include "services/database/TrackFeatures.hpp" #include "metadata/TagLibParser.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Exception.hpp" diff --git a/src/libs/services/scanner/impl/ScannerService.hpp b/src/libs/services/scanner/impl/ScannerService.hpp index 9cb9ce62..07adf5c6 100644 --- a/src/libs/services/scanner/impl/ScannerService.hpp +++ b/src/libs/services/scanner/impl/ScannerService.hpp @@ -30,9 +30,9 @@ #include -#include "database/Types.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" +#include "services/database/Types.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Session.hpp" #include "metadata/IParser.hpp" #include "services/scanner/IScannerService.hpp" #include "utils/Path.hpp" diff --git a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp index 5528ce8b..af60143e 100644 --- a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp +++ b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Scanner { diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp index 13ddb4d7..455ccc65 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -19,13 +19,13 @@ #include "ScrobblingService.hpp" -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" #include "internal/InternalScrobbler.hpp" #include "listenbrainz/ListenBrainzScrobbler.hpp" diff --git a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp index e5b23d51..b0dc6964 100644 --- a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp @@ -19,11 +19,11 @@ #include "InternalScrobbler.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Db.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" namespace Scrobbling diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index 08202d9e..20b68fc0 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -24,13 +24,13 @@ #include #include -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" #include "utils/Logger.hpp" diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 62329030..c4216a33 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -25,13 +25,13 @@ #include #include -#include "database/Artist.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" #include "services/scrobbling/Exception.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index 90942990..70efa377 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -26,7 +26,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "services/scrobbling/Listen.hpp" namespace Database diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp index ab293954..a2d9c9e1 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp @@ -21,9 +21,9 @@ #include -#include "database/Session.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" static constexpr std::string_view historyTracklistName {"__scrobbler_listenbrainz_history__"}; diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp index 6b2d1271..aca140c7 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp @@ -22,7 +22,7 @@ #include #include "utils/UUID.hpp" -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp index b9ce833e..cf67d7fc 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp @@ -29,7 +29,7 @@ #include #include "services/scrobbling/Listen.hpp" -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp b/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp index ae80dbac..50029c99 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp @@ -21,7 +21,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Scrobbling { diff --git a/src/libs/subsonic/impl/ParameterParsing.hpp b/src/libs/subsonic/impl/ParameterParsing.hpp index bb8a91d3..2773a08e 100644 --- a/src/libs/subsonic/impl/ParameterParsing.hpp +++ b/src/libs/subsonic/impl/ParameterParsing.hpp @@ -20,7 +20,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/String.hpp" #include "SubsonicResponse.hpp" diff --git a/src/libs/subsonic/impl/RequestContext.hpp b/src/libs/subsonic/impl/RequestContext.hpp index 9d73bcb4..982963ea 100644 --- a/src/libs/subsonic/impl/RequestContext.hpp +++ b/src/libs/subsonic/impl/RequestContext.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "ClientInfo.hpp" #include "ProtocolVersion.hpp" diff --git a/src/libs/subsonic/impl/Stream.cpp b/src/libs/subsonic/impl/Stream.cpp index d3d181ed..fe4218d0 100644 --- a/src/libs/subsonic/impl/Stream.cpp +++ b/src/libs/subsonic/impl/Stream.cpp @@ -22,9 +22,9 @@ #include "av/TranscodeParameters.hpp" #include "av/TranscodeResourceHandlerCreator.hpp" #include "av/Types.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" #include "utils/IResourceHandler.hpp" #include "utils/Logger.hpp" #include "utils/FileResourceHandlerCreator.hpp" diff --git a/src/libs/subsonic/impl/SubsonicId.hpp b/src/libs/subsonic/impl/SubsonicId.hpp index 0ac2a16a..dcc2e50b 100644 --- a/src/libs/subsonic/impl/SubsonicId.hpp +++ b/src/libs/subsonic/impl/SubsonicId.hpp @@ -19,7 +19,7 @@ #pragma once -#include "database/Types.hpp" +#include "services/database/Types.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 6cbac9a5..6778f4b0 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -28,15 +28,15 @@ #include "services/auth/IPasswordService.hpp" #include "services/auth/IEnvService.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackBookmark.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackBookmark.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "services/cover/ICoverService.hpp" diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index 2d549f41..24c0754b 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "ClientInfo.hpp" #include "RequestContext.hpp" diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 67d221f0..e82bc6a3 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -29,12 +29,12 @@ #include "services/auth/IPasswordService.hpp" #include "services/auth/IEnvService.hpp" #include "services/cover/ICoverService.hpp" -#include "database/Db.hpp" -#include "database/Session.hpp" -#include "services/scanner/IScannerService.hpp" -#include "subsonic/SubsonicResource.hpp" +#include "services/database/Db.hpp" +#include "services/database/Session.hpp" #include "services/recommendation/IRecommendationService.hpp" +#include "services/scanner/IScannerService.hpp" #include "services/scrobbling/IScrobblingService.hpp" +#include "subsonic/SubsonicResource.hpp" #include "ui/LmsApplication.hpp" #include "ui/LmsApplicationManager.hpp" #include "utils/http/IClient.hpp" diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index b7585247..b505d8af 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -30,8 +30,8 @@ #include "services/auth/IAuthTokenService.hpp" #include "services/auth/IPasswordService.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/Auth.hpp b/src/lms/ui/Auth.hpp index 5fda238b..87c00228 100644 --- a/src/lms/ui/Auth.hpp +++ b/src/lms/ui/Auth.hpp @@ -21,19 +21,20 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" -namespace UserInterface { - -std::optional -processAuthToken(const Wt::WEnvironment& env); - -class Auth : public Wt::WTemplateFormView +namespace UserInterface { - public: - Auth(); - Wt::Signal userLoggedIn; -}; + std::optional + processAuthToken(const Wt::WEnvironment& env); + + class Auth : public Wt::WTemplateFormView + { + public: + Auth(); + + Wt::Signal userLoggedIn; + }; } // namespace UserInterface diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index b20489d8..a7132699 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -31,12 +31,12 @@ #include "services/auth/IEnvService.hpp" #include "services/auth/IPasswordService.hpp" #include "services/cover/ICoverService.hpp" -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/LmsApplicationException.hpp b/src/lms/ui/LmsApplicationException.hpp index 0671a6c4..463bd899 100644 --- a/src/lms/ui/LmsApplicationException.hpp +++ b/src/lms/ui/LmsApplicationException.hpp @@ -19,41 +19,38 @@ #pragma once -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/Exception.hpp" -namespace UserInterface { - - -class LmsApplicationException : public LmsException +namespace UserInterface { - public: - LmsApplicationException(const Wt::WString& error) : LmsException {error.toUTF8()} {} -}; + class LmsApplicationException : public LmsException + { + public: + LmsApplicationException(const Wt::WString& error) : LmsException {error.toUTF8()} {} + }; -class ArtistNotFoundException : public LmsApplicationException -{ - public: - ArtistNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.artist-not-found")} {} -}; + class ArtistNotFoundException : public LmsApplicationException + { + public: + ArtistNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.artist-not-found")} {} + }; -class ReleaseNotFoundException : public LmsApplicationException -{ - public: - ReleaseNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.release-not-found")} {} -}; + class ReleaseNotFoundException : public LmsApplicationException + { + public: + ReleaseNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.release-not-found")} {} + }; -class UserNotFoundException : public LmsApplicationException -{ - public: - UserNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.user-not-found")} {} -}; - -class UserNotAllowedException : public LmsApplicationException -{ - public: - UserNotAllowedException() : LmsApplicationException {Wt::WString::tr("Lms.Error.user-not-allowed")} {} -}; - -} + class UserNotFoundException : public LmsApplicationException + { + public: + UserNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.user-not-found")} {} + }; + class UserNotAllowedException : public LmsApplicationException + { + public: + UserNotAllowedException() : LmsApplicationException {Wt::WString::tr("Lms.Error.user-not-allowed")} {} + }; +} // ns UserInterface diff --git a/src/lms/ui/LmsApplicationManager.hpp b/src/lms/ui/LmsApplicationManager.hpp index 1d7761f7..f478ae8d 100644 --- a/src/lms/ui/LmsApplicationManager.hpp +++ b/src/lms/ui/LmsApplicationManager.hpp @@ -25,7 +25,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/LmsTheme.hpp b/src/lms/ui/LmsTheme.hpp index 10ee14c2..dce55425 100644 --- a/src/lms/ui/LmsTheme.hpp +++ b/src/lms/ui/LmsTheme.hpp @@ -24,23 +24,21 @@ #include #include -#include "database/User.hpp" +#include "services/database/User.hpp" -namespace UserInterface { - -class LmsTheme : public Wt::WBootstrapTheme +namespace UserInterface { - public: - LmsTheme(Database::User::UITheme theme); + class LmsTheme : public Wt::WBootstrapTheme + { + public: + LmsTheme(Database::User::UITheme theme); - void setTheme(Database::User::UITheme theme); + void setTheme(Database::User::UITheme theme); - private: - - std::vector styleSheets() const override; - static std::vector getStyleSheets(Database::User::UITheme theme); - - Database::User::UITheme _theme; -}; + private: + std::vector styleSheets() const override; + static std::vector getStyleSheets(Database::User::UITheme theme); + Database::User::UITheme _theme; + }; } diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index 4ee65e06..eefd44fc 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -25,13 +25,13 @@ #include "utils/Logger.hpp" -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/Types.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/Types.hpp" +#include "services/database/User.hpp" #include "resource/CoverResource.hpp" #include "resource/AudioTranscodeResource.hpp" diff --git a/src/lms/ui/MediaPlayer.hpp b/src/lms/ui/MediaPlayer.hpp index d4f1700b..542cc081 100644 --- a/src/lms/ui/MediaPlayer.hpp +++ b/src/lms/ui/MediaPlayer.hpp @@ -27,7 +27,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 550016ee..32ab87e7 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -22,12 +22,12 @@ #include #include -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" diff --git a/src/lms/ui/PlayQueue.hpp b/src/lms/ui/PlayQueue.hpp index 1bbb996a..89f15ff4 100644 --- a/src/lms/ui/PlayQueue.hpp +++ b/src/lms/ui/PlayQueue.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Similarity diff --git a/src/lms/ui/PlayQueueAction.hpp b/src/lms/ui/PlayQueueAction.hpp index fffd8133..4fa8b426 100644 --- a/src/lms/ui/PlayQueueAction.hpp +++ b/src/lms/ui/PlayQueueAction.hpp @@ -22,11 +22,10 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace UserInterface { - enum class PlayQueueAction { Play, diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index c01c7d8e..1bc35013 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -35,7 +35,7 @@ #include "common/ValueStringModel.hpp" #include "services/auth/IPasswordService.hpp" -#include "database/Session.hpp" +#include "services/database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/DatabaseSettingsView.cpp b/src/lms/ui/admin/DatabaseSettingsView.cpp index d73a8241..53cec972 100644 --- a/src/lms/ui/admin/DatabaseSettingsView.cpp +++ b/src/lms/ui/admin/DatabaseSettingsView.cpp @@ -26,9 +26,9 @@ #include #include -#include "database/Cluster.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Session.hpp" #include "services/scanner/IScannerService.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index ce7ca58d..4e8186b8 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -25,8 +25,8 @@ #include #include "services/auth/IPasswordService.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index 61199f23..12bf9392 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -28,8 +28,8 @@ #include #include -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "services/scanner/IScannerService.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index e974c967..fd456993 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -28,8 +28,8 @@ #include #include "services/auth/IPasswordService.hpp" -#include "database/User.hpp" -#include "database/Session.hpp" +#include "services/database/User.hpp" +#include "services/database/Session.hpp" #include "utils/IConfig.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index 98160461..ae0279a9 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -24,8 +24,8 @@ #include #include "services/auth/IPasswordService.hpp" -#include "database/User.hpp" -#include "database/Session.hpp" +#include "services/database/User.hpp" +#include "services/database/Session.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" @@ -34,7 +34,7 @@ namespace UserInterface { UsersView::UsersView() - : Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template")) + : Wt::WTemplate {Wt::WString::tr("Lms.Admin.Users.template")} { addFunction("tr", &Wt::WTemplate::Functions::tr); @@ -45,7 +45,7 @@ UsersView::UsersView() setCondition("if-can-create-user", true); Wt::WPushButton* addBtn = bindNew("add-btn", Wt::WString::tr("Lms.Admin.Users.add")); - addBtn->clicked().connect([]() + addBtn->clicked().connect([] { LmsApp->setInternalPath("/admin/user", true); }); @@ -69,8 +69,7 @@ UsersView::refreshView() auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - auto users = Database::User::getAll(LmsApp->getDbSession()); - for (const auto& user : users) + for (const auto& user : Database::User::getAll(LmsApp->getDbSession())) { const Database::UserId userId {user->getId()}; diff --git a/src/lms/ui/common/LoginNameValidator.cpp b/src/lms/ui/common/LoginNameValidator.cpp index d1574d3f..5fc121cf 100644 --- a/src/lms/ui/common/LoginNameValidator.cpp +++ b/src/lms/ui/common/LoginNameValidator.cpp @@ -20,7 +20,7 @@ #include "LoginNameValidator.hpp" #include -#include "database/User.hpp" +#include "services/database/User.hpp" namespace UserInterface { diff --git a/src/lms/ui/common/PasswordValidator.hpp b/src/lms/ui/common/PasswordValidator.hpp index 40a22679..e3b5a708 100644 --- a/src/lms/ui/common/PasswordValidator.hpp +++ b/src/lms/ui/common/PasswordValidator.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "services/auth/Types.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 2e2b3522..0d6d3619 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -19,9 +19,9 @@ #include "ArtistCollector.hpp" -#include "database/Artist.hpp" -#include "database/User.hpp" -#include "database/TrackList.hpp" +#include "services/database/Artist.hpp" +#include "services/database/User.hpp" +#include "services/database/TrackList.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ArtistCollector.hpp b/src/lms/ui/explore/ArtistCollector.hpp index 05734af0..f529b4cc 100644 --- a/src/lms/ui/explore/ArtistCollector.hpp +++ b/src/lms/ui/explore/ArtistCollector.hpp @@ -23,8 +23,7 @@ #include #include "DatabaseCollectorBase.hpp" - -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ArtistListHelpers.cpp b/src/lms/ui/explore/ArtistListHelpers.cpp index 85adb362..e16de3e2 100644 --- a/src/lms/ui/explore/ArtistListHelpers.cpp +++ b/src/lms/ui/explore/ArtistListHelpers.cpp @@ -20,7 +20,7 @@ #include -#include "database/Artist.hpp" +#include "services/database/Artist.hpp" #include "LmsApplication.hpp" namespace UserInterface::ArtistListHelpers diff --git a/src/lms/ui/explore/ArtistListHelpers.hpp b/src/lms/ui/explore/ArtistListHelpers.hpp index 02659bf7..412ad51f 100644 --- a/src/lms/ui/explore/ArtistListHelpers.hpp +++ b/src/lms/ui/explore/ArtistListHelpers.hpp @@ -23,7 +23,7 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index f44d7ee2..f66337ba 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -25,13 +25,13 @@ #include #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Release.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index 18b3894c..3d292760 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index 3d71b94c..b804f453 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -21,8 +21,8 @@ #include -#include "database/Session.hpp" -#include "database/TrackArtistLink.hpp" +#include "services/database/Session.hpp" +#include "services/database/TrackArtistLink.hpp" #include "utils/EnumSet.hpp" #include "utils/Logger.hpp" diff --git a/src/lms/ui/explore/ArtistsView.hpp b/src/lms/ui/explore/ArtistsView.hpp index f7e61e22..350f683d 100644 --- a/src/lms/ui/explore/ArtistsView.hpp +++ b/src/lms/ui/explore/ArtistsView.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "ArtistCollector.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/DatabaseCollectorBase.hpp b/src/lms/ui/explore/DatabaseCollectorBase.hpp index 224f7657..db38fe5b 100644 --- a/src/lms/ui/explore/DatabaseCollectorBase.hpp +++ b/src/lms/ui/explore/DatabaseCollectorBase.hpp @@ -25,7 +25,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/explore/Explore.cpp b/src/lms/ui/explore/Explore.cpp index 8c821fdb..b0ab61a7 100644 --- a/src/lms/ui/explore/Explore.cpp +++ b/src/lms/ui/explore/Explore.cpp @@ -23,10 +23,10 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "utils/Logger.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/Explore.hpp b/src/lms/ui/explore/Explore.hpp index 66815002..9e4c3bb0 100644 --- a/src/lms/ui/explore/Explore.hpp +++ b/src/lms/ui/explore/Explore.hpp @@ -21,32 +21,30 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" -namespace UserInterface { - -class Filters; -class SearchView; - -class Explore : public Wt::WTemplate +namespace UserInterface { - public: - Explore(Filters* filters); + class Filters; + class SearchView; - void search(const Wt::WString& searchText); + class Explore : public Wt::WTemplate + { + public: + Explore(Filters* filters); - PlayQueueActionTrackSignal tracksAction; + void search(const Wt::WString& searchText); - private: + PlayQueueActionTrackSignal tracksAction; - void handleArtistsAction(PlayQueueAction action, const std::vector& artistsId); - void handleReleasesAction(PlayQueueAction action, const std::vector& releasesId); - void handleTracksAction(PlayQueueAction action, const std::vector& tracksId); - - Filters* _filters {}; - SearchView* _search {}; -}; + private: + void handleArtistsAction(PlayQueueAction action, const std::vector& artistsId); + void handleReleasesAction(PlayQueueAction action, const std::vector& releasesId); + void handleTracksAction(PlayQueueAction action, const std::vector& tracksId); + Filters* _filters {}; + SearchView* _search {}; + }; } // namespace UserInterface diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp index f8ef1c74..728c16bf 100644 --- a/src/lms/ui/explore/Filters.cpp +++ b/src/lms/ui/explore/Filters.cpp @@ -24,8 +24,8 @@ #include #include -#include "database/Cluster.hpp" -#include "database/Session.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Session.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/Filters.hpp b/src/lms/ui/explore/Filters.hpp index 079d52c2..585d1472 100644 --- a/src/lms/ui/explore/Filters.hpp +++ b/src/lms/ui/explore/Filters.hpp @@ -24,31 +24,27 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "Filters.hpp" -namespace UserInterface { - -class Filters : public Wt::WTemplate +namespace UserInterface { - public: - Filters(); + class Filters : public Wt::WTemplate + { + public: + Filters(); - void add(Database::ClusterId clusterId); + void add(Database::ClusterId clusterId); + const std::vector& getClusterIds() const { return _clusterIds; } + Wt::Signal<>& updated() { return _sigUpdated; } - const std::vector& getClusterIds() const { return _clusterIds; } - - Wt::Signal<>& updated() { return _sigUpdated; } - - private: - - void showDialog(); - - Wt::WContainerWidget *_filters; - Wt::Signal<> _sigUpdated; - std::vector _clusterIds; -}; + private: + void showDialog(); + Wt::WContainerWidget *_filters; + Wt::Signal<> _sigUpdated; + std::vector _clusterIds; + }; } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index 0bfd0713..e0d0efb5 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -21,10 +21,10 @@ #include -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" -#include "database/TrackList.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" +#include "services/database/TrackList.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleaseListHelpers.cpp b/src/lms/ui/explore/ReleaseListHelpers.cpp index 71c5daa5..e2c40f65 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.cpp +++ b/src/lms/ui/explore/ReleaseListHelpers.cpp @@ -23,8 +23,8 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" #include "resource/CoverResource.hpp" #include "LmsApplication.hpp" @@ -33,7 +33,6 @@ using namespace Database; namespace UserInterface::ReleaseListHelpers { - static std::unique_ptr createEntryInternal(const Release::pointer& release, const std::string& templateKey, const Artist::pointer& artist, const bool showYear) @@ -104,6 +103,5 @@ namespace UserInterface::ReleaseListHelpers { return createEntry(release, artist, true); } - } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseListHelpers.hpp b/src/lms/ui/explore/ReleaseListHelpers.hpp index 3aad6698..fd3b051a 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.hpp +++ b/src/lms/ui/explore/ReleaseListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp index 7224683f..6d924c43 100644 --- a/src/lms/ui/explore/ReleasePopup.cpp +++ b/src/lms/ui/explore/ReleasePopup.cpp @@ -21,15 +21,14 @@ #include -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/User.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" namespace UserInterface { - void displayReleasePopupMenu(Wt::WInteractWidget& target, Database::ReleaseId releaseId, @@ -75,6 +74,4 @@ namespace UserInterface popup->popup(&target); } - } // namespace UserInterface - diff --git a/src/lms/ui/explore/ReleasePopup.hpp b/src/lms/ui/explore/ReleasePopup.hpp index 5ffb4f48..7bf35ad8 100644 --- a/src/lms/ui/explore/ReleasePopup.hpp +++ b/src/lms/ui/explore/ReleasePopup.hpp @@ -20,9 +20,8 @@ #pragma once #include -#include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index facbea1e..199810dc 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -25,11 +25,11 @@ #include #include -#include "database/Cluster.hpp" -#include "database/Release.hpp" -#include "database/ScanSettings.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Release.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/explore/ReleaseView.hpp b/src/lms/ui/explore/ReleaseView.hpp index d3700eaa..855bc044 100644 --- a/src/lms/ui/explore/ReleaseView.hpp +++ b/src/lms/ui/explore/ReleaseView.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database @@ -32,7 +32,6 @@ namespace Database namespace UserInterface { - class Filters; class Release : public Wt::WTemplate { @@ -51,6 +50,5 @@ namespace UserInterface Filters* _filters {}; }; - } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index 93c56428..8e47fe4e 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -23,7 +23,7 @@ #include #include -#include "database/Session.hpp" +#include "services/database/Session.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "ReleaseListHelpers.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleasesView.hpp b/src/lms/ui/explore/ReleasesView.hpp index f2a9b91b..9661678f 100644 --- a/src/lms/ui/explore/ReleasesView.hpp +++ b/src/lms/ui/explore/ReleasesView.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" #include "ReleaseCollector.hpp" @@ -41,7 +41,6 @@ namespace UserInterface PlayQueueActionReleaseSignal releasesAction; private: - void refreshView(); void refreshView(ReleaseCollector::Mode mode); diff --git a/src/lms/ui/explore/SearchView.cpp b/src/lms/ui/explore/SearchView.cpp index 90a950e8..b82ff3a0 100644 --- a/src/lms/ui/explore/SearchView.cpp +++ b/src/lms/ui/explore/SearchView.cpp @@ -25,10 +25,10 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "common/LoadingIndicator.hpp" @@ -40,7 +40,6 @@ namespace UserInterface { - SearchView::SearchView(Filters* filters) : Wt::WTemplate {Wt::WString::tr("Lms.Explore.Search.template")} , _filters {filters} @@ -186,8 +185,5 @@ namespace UserInterface getItemMenu(Mode::Track).setDisabled(results.getCount() == 0); } - - - } // namespace UserInterface diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index d3e8b3db..13be14fc 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -21,10 +21,10 @@ #include -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/TrackList.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/User.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index ba36cb9d..69055806 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -24,9 +24,9 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Track.hpp" #include "resource/DownloadResource.hpp" #include "resource/CoverResource.hpp" #include "LmsApplication.hpp" diff --git a/src/lms/ui/explore/TrackListHelpers.hpp b/src/lms/ui/explore/TrackListHelpers.hpp index 2e3ea306..2ba93246 100644 --- a/src/lms/ui/explore/TrackListHelpers.hpp +++ b/src/lms/ui/explore/TrackListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/TrackPopup.cpp b/src/lms/ui/explore/TrackPopup.cpp index 8884347c..d1b1bdea 100644 --- a/src/lms/ui/explore/TrackPopup.cpp +++ b/src/lms/ui/explore/TrackPopup.cpp @@ -21,15 +21,14 @@ #include -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" namespace UserInterface { - void displayTrackPopupMenu(Wt::WInteractWidget& target, Database::TrackId trackId, diff --git a/src/lms/ui/explore/TrackPopup.hpp b/src/lms/ui/explore/TrackPopup.hpp index 1597d994..9ffe7474 100644 --- a/src/lms/ui/explore/TrackPopup.hpp +++ b/src/lms/ui/explore/TrackPopup.hpp @@ -22,7 +22,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" namespace UserInterface diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index daec0fcd..639046de 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -23,7 +23,7 @@ #include #include -#include "database/Session.hpp" +#include "services/database/Session.hpp" #include "utils/Logger.hpp" #include "common/InfiniteScrollingContainer.hpp" diff --git a/src/lms/ui/explore/TracksView.hpp b/src/lms/ui/explore/TracksView.hpp index ac011e01..37e7b4c5 100644 --- a/src/lms/ui/explore/TracksView.hpp +++ b/src/lms/ui/explore/TracksView.hpp @@ -24,7 +24,7 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "PlayQueueAction.hpp" #include "TrackCollector.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index def318df..42ff45b6 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -23,8 +23,8 @@ #include #include "av/IAudioFile.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "utils/FileResourceHandlerCreator.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.hpp b/src/lms/ui/resource/AudioFileResource.hpp index fda116eb..046f7c55 100644 --- a/src/lms/ui/resource/AudioFileResource.hpp +++ b/src/lms/ui/resource/AudioFileResource.hpp @@ -21,29 +21,21 @@ #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" - -namespace UserInterface { - -class AudioFileResource : public Wt::WResource +namespace UserInterface { - public: - ~AudioFileResource(); + class AudioFileResource : public Wt::WResource + { + public: + ~AudioFileResource(); - std::string getUrl(Database::TrackId trackId) const; + std::string getUrl(Database::TrackId trackId) const; - private: - - static constexpr std::size_t _chunkSize {262144}; - - void handleRequest(const Wt::Http::Request& request, - Wt::Http::Response& response) override; - -}; + private: + static constexpr std::size_t _chunkSize {262144}; + void handleRequest(const Wt::Http::Request& request, + Wt::Http::Response& response) override; + }; } // namespace UserInterface - - - - diff --git a/src/lms/ui/resource/AudioTranscodeResource.cpp b/src/lms/ui/resource/AudioTranscodeResource.cpp index 66ecac06..acc04918 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.cpp +++ b/src/lms/ui/resource/AudioTranscodeResource.cpp @@ -25,9 +25,9 @@ #include "av/TranscodeParameters.hpp" #include "av/TranscodeResourceHandlerCreator.hpp" #include "av/Types.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "database/User.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" @@ -41,7 +41,6 @@ namespace StringUtils std::optional readAs(std::string_view str) { - auto encodedFormat {readAs(str)}; if (!encodedFormat) return std::nullopt; diff --git a/src/lms/ui/resource/AudioTranscodeResource.hpp b/src/lms/ui/resource/AudioTranscodeResource.hpp index ef516e91..dbd1d31a 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.hpp +++ b/src/lms/ui/resource/AudioTranscodeResource.hpp @@ -22,29 +22,28 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace Database { class User; } -namespace UserInterface { - -class AudioTranscodeResource : public Wt::WResource +namespace UserInterface { - public: - ~AudioTranscodeResource(); + class AudioTranscodeResource : public Wt::WResource + { + public: + ~AudioTranscodeResource(); - // Url depends on the user since settings are used in parameters - std::string getUrl(Database::TrackId trackId) const; + // Url depends on the user since settings are used in parameters + std::string getUrl(Database::TrackId trackId) const; - void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response); - - private: - static constexpr std::size_t _chunkSize {262144}; -}; + void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response); + private: + static constexpr std::size_t _chunkSize {262144}; + }; } // namespace UserInterface diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index 69ef9b19..6b4c5a42 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -23,7 +23,7 @@ #include #include "services/cover/ICoverService.hpp" -#include "database/Track.hpp" +#include "services/database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" diff --git a/src/lms/ui/resource/CoverResource.hpp b/src/lms/ui/resource/CoverResource.hpp index 1e8cb8ad..7ab2a1fe 100644 --- a/src/lms/ui/resource/CoverResource.hpp +++ b/src/lms/ui/resource/CoverResource.hpp @@ -20,11 +20,10 @@ #pragma once #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" namespace UserInterface { - class CoverResource : public Wt::WResource { public: @@ -45,6 +44,5 @@ namespace UserInterface private: void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; }; - } // namespace UserInterface diff --git a/src/lms/ui/resource/DownloadResource.cpp b/src/lms/ui/resource/DownloadResource.cpp index 998096a5..7dd75d79 100644 --- a/src/lms/ui/resource/DownloadResource.cpp +++ b/src/lms/ui/resource/DownloadResource.cpp @@ -26,10 +26,10 @@ #include #include -#include "database/Artist.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "utils/Exception.hpp" #include "utils/Logger.hpp" #include "utils/Zipper.hpp" diff --git a/src/lms/ui/resource/DownloadResource.hpp b/src/lms/ui/resource/DownloadResource.hpp index 581a55ec..80d02994 100644 --- a/src/lms/ui/resource/DownloadResource.hpp +++ b/src/lms/ui/resource/DownloadResource.hpp @@ -22,53 +22,51 @@ #include #include -#include "database/Types.hpp" +#include "services/database/Types.hpp" #include "utils/Zipper.hpp" -namespace UserInterface { - -class DownloadResource : public Wt::WResource +namespace UserInterface { - public: - static constexpr std::size_t bufferSize {32768}; + class DownloadResource : public Wt::WResource + { + public: + static constexpr std::size_t bufferSize {32768}; - ~DownloadResource(); + ~DownloadResource(); - private: + private: + void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; + virtual std::unique_ptr createZipper() = 0; + }; - void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; - virtual std::unique_ptr createZipper() = 0; -}; + class DownloadArtistResource : public DownloadResource + { + public: + DownloadArtistResource(Database::ArtistId artistId); -class DownloadArtistResource : public DownloadResource -{ - public: - DownloadArtistResource(Database::ArtistId artistId); + private: + std::unique_ptr createZipper() override; + Database::ArtistId _artistId; + }; - private: - std::unique_ptr createZipper() override; - Database::ArtistId _artistId; -}; + class DownloadReleaseResource : public DownloadResource + { + public: + DownloadReleaseResource(Database::ReleaseId releaseId); -class DownloadReleaseResource : public DownloadResource -{ - public: - DownloadReleaseResource(Database::ReleaseId releaseId); + private: + std::unique_ptr createZipper() override; + Database::ReleaseId _releaseId; + }; - private: - std::unique_ptr createZipper() override; - Database::ReleaseId _releaseId; -}; - -class DownloadTrackResource : public DownloadResource -{ - public: - DownloadTrackResource(Database::TrackId trackId); - - private: - std::unique_ptr createZipper() override; - Database::TrackId _trackId; -}; + class DownloadTrackResource : public DownloadResource + { + public: + DownloadTrackResource(Database::TrackId trackId); + private: + std::unique_ptr createZipper() override; + Database::TrackId _trackId; + }; } // namespace UserInterface diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index a533187f..7d72fd09 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -24,10 +24,10 @@ #include -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "services/cover/ICoverService.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index 54118ffc..98666179 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -24,12 +24,12 @@ #include -#include "database/Artist.hpp" -#include "database/Cluster.hpp" -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Db.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/IConfig.hpp" #include "utils/Service.hpp" From 97aba4ae82a7d6ed2d933b08b623510bca188aff Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 24 Oct 2021 16:31:04 +0200 Subject: [PATCH 26/49] Introduced Feedback service --- src/libs/services/CMakeLists.txt | 1 + src/libs/services/database/impl/User.cpp | 18 +- .../include/services/database/User.hpp | 18 +- src/libs/services/database/test/Artist.cpp | 33 ++++ .../services/database/test/DatabaseTest.cpp | 94 --------- src/libs/services/database/test/Release.cpp | 33 ++++ src/libs/services/database/test/Track.cpp | 31 +++ src/libs/services/feedback/CMakeLists.txt | 19 ++ .../feedback/impl/FeedbackService.cpp | 183 ++++++++++++++++++ .../feedback/impl/FeedbackService.hpp | 59 ++++++ .../services/feedback/IFeedbackService.hpp | 51 +++++ .../include/services/scanner/ScannerStats.hpp | 7 +- src/libs/subsonic/CMakeLists.txt | 1 + src/libs/subsonic/impl/SubsonicResource.cpp | 19 +- src/lms/CMakeLists.txt | 1 + src/lms/main.cpp | 3 +- src/lms/ui/PlayQueue.cpp | 20 +- src/lms/ui/explore/ArtistView.cpp | 19 +- src/lms/ui/explore/ReleasePopup.cpp | 21 +- src/lms/ui/explore/TrackPopup.cpp | 14 +- 20 files changed, 463 insertions(+), 182 deletions(-) create mode 100644 src/libs/services/feedback/CMakeLists.txt create mode 100644 src/libs/services/feedback/impl/FeedbackService.cpp create mode 100644 src/libs/services/feedback/impl/FeedbackService.hpp create mode 100644 src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp diff --git a/src/libs/services/CMakeLists.txt b/src/libs/services/CMakeLists.txt index fd4d3160..70f360ed 100644 --- a/src/libs/services/CMakeLists.txt +++ b/src/libs/services/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(auth) add_subdirectory(cover) add_subdirectory(database) +add_subdirectory(feedback) add_subdirectory(recommendation) add_subdirectory(scanner) add_subdirectory(scrobbling) diff --git a/src/libs/services/database/impl/User.cpp b/src/libs/services/database/impl/User.cpp index 038c3a55..75c69adf 100644 --- a/src/libs/services/database/impl/User.cpp +++ b/src/libs/services/database/impl/User.cpp @@ -161,61 +161,61 @@ User::getQueuedTrackList(Session& session) const } void -User::starArtist(ObjectPtr artist) +User::star(ObjectPtr artist) { if (_starredArtists.count(getDboPtr(artist)) == 0) _starredArtists.insert(getDboPtr(artist)); } void -User::unstarArtist(ObjectPtr artist) +User::unstar(ObjectPtr artist) { if (_starredArtists.count(getDboPtr(artist)) != 0) _starredArtists.erase(getDboPtr(artist)); } bool -User::hasStarredArtist(ObjectPtr artist) const +User::isStarred(ObjectPtr artist) const { return _starredArtists.count(getDboPtr(artist)) != 0; } void -User::starRelease(ObjectPtr release) +User::star(ObjectPtr release) { if (_starredReleases.count(getDboPtr(release)) == 0) _starredReleases.insert(getDboPtr(release)); } void -User::unstarRelease(ObjectPtr release) +User::unstar(ObjectPtr release) { if (_starredReleases.count(getDboPtr(release)) != 0) _starredReleases.erase(getDboPtr(release)); } bool -User::hasStarredRelease(ObjectPtr release) const +User::isStarred(ObjectPtr release) const { return _starredReleases.count(getDboPtr(release)) != 0; } void -User::starTrack(ObjectPtr track) +User::star(ObjectPtr track) { if (_starredTracks.count(getDboPtr(track)) == 0) _starredTracks.insert(getDboPtr(track)); } void -User::unstarTrack(ObjectPtr track) +User::unstar(ObjectPtr track) { if (_starredTracks.count(getDboPtr(track)) != 0) _starredTracks.erase(getDboPtr(track)); } bool -User::hasStarredTrack(ObjectPtr track) const +User::isStarred(ObjectPtr track) const { return _starredTracks.count(getDboPtr(track)) != 0; } diff --git a/src/libs/services/database/include/services/database/User.hpp b/src/libs/services/database/include/services/database/User.hpp index 34748567..34bfac2c 100644 --- a/src/libs/services/database/include/services/database/User.hpp +++ b/src/libs/services/database/include/services/database/User.hpp @@ -167,18 +167,18 @@ class User : public Object ObjectPtr getQueuedTrackList(Session& session) const; - void starArtist(ObjectPtr artist); - void unstarArtist(ObjectPtr artist); - bool hasStarredArtist(ObjectPtr artist) const; + void star(ObjectPtr artist); + void unstar(ObjectPtr artist); + bool isStarred(ObjectPtr artist) const; - void starRelease(ObjectPtr release); - void unstarRelease(ObjectPtr release); - bool hasStarredRelease(ObjectPtr release) const; + void star(ObjectPtr release); + void unstar(ObjectPtr release); + bool isStarred(ObjectPtr release) const; // Stars - void starTrack(ObjectPtr track); - void unstarTrack(ObjectPtr track); - bool hasStarredTrack(ObjectPtr track) const; + void star(ObjectPtr track); + void unstar(ObjectPtr track); + bool isStarred(ObjectPtr track) const; template void persist(Action& a) diff --git a/src/libs/services/database/test/Artist.cpp b/src/libs/services/database/test/Artist.cpp index dcace44e..d94c6daf 100644 --- a/src/libs/services/database/test/Artist.cpp +++ b/src/libs/services/database/test/Artist.cpp @@ -366,3 +366,36 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) EXPECT_EQ(tracks.front()->getId(), track2.getId()); } } + +TEST_F(DatabaseFixture, SingleStarredArtist) +{ + ScopedArtist artist {session, "MyArtist"}; + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + + { + auto transaction {session.createUniqueTransaction()}; + + EXPECT_FALSE(user->isStarred(artist.get())); + } + + { + auto transaction {session.createUniqueTransaction()}; + + auto trackArtistLink {TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist)}; + user.get().modify()->star(artist.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_TRUE(user->isStarred(artist.get())); + + bool hasMore {}; + auto artists {Artist::getStarred(session, user.get(), {}, std::nullopt, Artist::SortMethod::BySortName, std::nullopt, hasMore)}; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist.getId()); + EXPECT_FALSE(hasMore); + } +} + diff --git a/src/libs/services/database/test/DatabaseTest.cpp b/src/libs/services/database/test/DatabaseTest.cpp index 1bca6150..2e0a386a 100644 --- a/src/libs/services/database/test/DatabaseTest.cpp +++ b/src/libs/services/database/test/DatabaseTest.cpp @@ -104,100 +104,6 @@ TEST_F(DatabaseFixture, SingleUser) } } -TEST_F(DatabaseFixture, SingleStarredArtist) -{ - ScopedArtist artist {session, "MyArtist"}; - ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - - { - auto transaction {session.createUniqueTransaction()}; - - EXPECT_FALSE(user->hasStarredArtist(artist.get())); - } - - { - auto transaction {session.createUniqueTransaction()}; - - auto trackArtistLink {TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist)}; - user.get().modify()->starArtist(artist.get()); - } - - { - auto transaction {session.createSharedTransaction()}; - - EXPECT_TRUE(user->hasStarredArtist(artist.get())); - - bool hasMore {}; - auto artists {Artist::getStarred(session, user.get(), {}, std::nullopt, Artist::SortMethod::BySortName, std::nullopt, hasMore)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); - EXPECT_FALSE(hasMore); - } -} - -TEST_F(DatabaseFixture, SingleStarredRelease) -{ - ScopedRelease release {session, "MyRelease"}; - ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - - { - auto transaction {session.createSharedTransaction()}; - - EXPECT_FALSE(user->hasStarredRelease(release.get())); - } - - { - auto transaction {session.createUniqueTransaction()}; - - track.get().modify()->setRelease(release.get()); - user.get().modify()->starRelease(release.get()); - } - - { - auto transaction {session.createSharedTransaction()}; - - EXPECT_TRUE(user->hasStarredRelease(release.get())); - - bool hasMore {}; - auto releases {Release::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); - EXPECT_FALSE(hasMore); - } -} - -TEST_F(DatabaseFixture, SingleStarredTrack) -{ - ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - - { - auto transaction {session.createUniqueTransaction()}; - - EXPECT_FALSE(user->hasStarredTrack(track.get())); - } - - { - auto transaction {session.createUniqueTransaction()}; - - user.get().modify()->starTrack(track.get()); - } - - { - auto transaction {session.createUniqueTransaction()}; - - EXPECT_TRUE(user->hasStarredTrack(track.get())); - - bool hasMore {}; - auto tracks {Track::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track.getId()); - EXPECT_FALSE(hasMore); - } -} - TEST_F(DatabaseFixture, SingleTrackList) { ScopedUser user {session, "MyUser"}; diff --git a/src/libs/services/database/test/Release.cpp b/src/libs/services/database/test/Release.cpp index 801fd9bf..84f765f4 100644 --- a/src/libs/services/database/test/Release.cpp +++ b/src/libs/services/database/test/Release.cpp @@ -361,3 +361,36 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseDate) } } +TEST_F(DatabaseFixture, SingleStarredRelease) +{ + ScopedRelease release {session, "MyRelease"}; + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_FALSE(user->isStarred(release.get())); + } + + { + auto transaction {session.createUniqueTransaction()}; + + track.get().modify()->setRelease(release.get()); + user.get().modify()->star(release.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_TRUE(user->isStarred(release.get())); + + bool hasMore {}; + auto releases {Release::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release.getId()); + EXPECT_FALSE(hasMore); + } +} + + diff --git a/src/libs/services/database/test/Track.cpp b/src/libs/services/database/test/Track.cpp index bdc54855..a4b22d61 100644 --- a/src/libs/services/database/test/Track.cpp +++ b/src/libs/services/database/test/Track.cpp @@ -115,5 +115,36 @@ TEST_F(DatabaseFixture, SingleTrackDate) } } +TEST_F(DatabaseFixture, SingleStarredTrack) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + + { + auto transaction {session.createUniqueTransaction()}; + + EXPECT_FALSE(user->isStarred(track.get())); + } + + { + auto transaction {session.createUniqueTransaction()}; + + user.get().modify()->star(track.get()); + } + + { + auto transaction {session.createUniqueTransaction()}; + + EXPECT_TRUE(user->isStarred(track.get())); + + bool hasMore {}; + auto tracks {Track::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; + ASSERT_EQ(tracks.size(), 1); + EXPECT_EQ(tracks.front()->getId(), track.getId()); + EXPECT_FALSE(hasMore); + } +} + + diff --git a/src/libs/services/feedback/CMakeLists.txt b/src/libs/services/feedback/CMakeLists.txt new file mode 100644 index 00000000..3049f60f --- /dev/null +++ b/src/libs/services/feedback/CMakeLists.txt @@ -0,0 +1,19 @@ + +add_library(lmsfeedback SHARED + impl/FeedbackService.cpp + ) + +target_include_directories(lmsfeedback INTERFACE + include + ) + +target_include_directories(lmsfeedback PRIVATE + include + ) + +target_link_libraries(lmsfeedback PRIVATE + lmsdatabase + ) + +install(TARGETS lmsfeedback DESTINATION lib) + diff --git a/src/libs/services/feedback/impl/FeedbackService.cpp b/src/libs/services/feedback/impl/FeedbackService.cpp new file mode 100644 index 00000000..d4c0a04f --- /dev/null +++ b/src/libs/services/feedback/impl/FeedbackService.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2021 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 "FeedbackService.hpp" +#include "services/database/Db.hpp" +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" + +namespace +{ + using namespace Database; + + void + exec(Db& db, UserId userId, ArtistId artistId, std::function func) + { + Session& dbSession {db.getTLSSession()}; + auto transaction {dbSession.createSharedTransaction()}; + + Database::User::pointer user {Database::User::getById(dbSession, userId)}; + if (!user) + return; + + Database::Artist::pointer artist {Database::Artist::getById(dbSession, artistId)}; + if (!artist) + return; + + func(user, artist); + } + + void + exec(Db& db, UserId userId, ReleaseId releaseId, std::function func) + { + Session& dbSession {db.getTLSSession()}; + auto transaction {dbSession.createSharedTransaction()}; + + Database::User::pointer user {Database::User::getById(dbSession, userId)}; + if (!user) + return; + + Database::Release::pointer release {Database::Release::getById(dbSession, releaseId)}; + if (!release) + return; + + func(user, release); + } + + void + exec(Db& db, UserId userId, TrackId trackId, std::function func) + { + Session& dbSession {db.getTLSSession()}; + auto transaction {dbSession.createSharedTransaction()}; + + Database::User::pointer user {Database::User::getById(dbSession, userId)}; + if (!user) + return; + + Database::Track::pointer track {Database::Track::getById(dbSession, trackId)}; + if (!track) + return; + + func(user, track); + } +} + +namespace Feedback +{ + std::unique_ptr + createFeedbackService(Database::Db& db) + { + return std::make_unique(db); + } + + FeedbackService::FeedbackService(Database::Db& db) + : _db {db} + { + } + + void + FeedbackService::star(Database::UserId userId, Database::ArtistId artistId) + { + starObject(userId, artistId); + } + + void + FeedbackService::unstar(Database::UserId userId, Database::ArtistId artistId) + { + unstarObject(userId, artistId); + } + + bool + FeedbackService::isStarred(Database::UserId userId, Database::ArtistId artistId) + { + return isObjectStarred(userId, artistId); + } + + void + FeedbackService::star(Database::UserId userId, Database::ReleaseId releaseId) + { + starObject(userId, releaseId); + } + + void + FeedbackService::unstar(Database::UserId userId, Database::ReleaseId releaseId) + { + unstarObject(userId, releaseId); + } + + bool + FeedbackService::isStarred(Database::UserId userId, Database::ReleaseId releaseId) + { + return isObjectStarred(userId, releaseId); + } + + void + FeedbackService::star(Database::UserId userId, Database::TrackId trackId) + { + starObject(userId, trackId); + } + + void + FeedbackService::unstar(Database::UserId userId, Database::TrackId trackId) + { + unstarObject(userId, trackId); + } + + bool + FeedbackService::isStarred(Database::UserId userId, Database::TrackId trackId) + { + return isObjectStarred(userId, trackId); + } + + template + void + FeedbackService::starObject(Database::UserId userId, DatabaseId objectId) + { + exec(_db, userId, objectId, [](Database::User::pointer user, auto object) + { + user.modify()->star(object); + }); + } + + template + void + FeedbackService::unstarObject(Database::UserId userId, DatabaseId objectId) + { + exec(_db, userId, objectId, [](Database::User::pointer user, auto object) + { + user.modify()->unstar(object); + }); + } + + template + bool + FeedbackService::isObjectStarred(Database::UserId userId, DatabaseId objectId) + { + bool res {}; + exec(_db, userId, objectId, [&res](Database::User::pointer user, auto object) + { + res = user->isStarred(object); + }); + return res; + } +} // ns Feedback + diff --git a/src/libs/services/feedback/impl/FeedbackService.hpp b/src/libs/services/feedback/impl/FeedbackService.hpp new file mode 100644 index 00000000..c2b37b13 --- /dev/null +++ b/src/libs/services/feedback/impl/FeedbackService.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 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 "services/feedback/IFeedbackService.hpp" + +namespace Database +{ + class Db; +} + +namespace Feedback +{ + class FeedbackService : public IFeedbackService + { + public: + FeedbackService(Database::Db& db); + + private: + void star(Database::UserId userId, Database::ArtistId artistId) override; + void unstar(Database::UserId userId, Database::ArtistId artistId) override; + bool isStarred(Database::UserId userId, Database::ArtistId artistId) override; + + void star(Database::UserId userId, Database::ReleaseId releaseId) override; + void unstar(Database::UserId userId, Database::ReleaseId releaseId) override; + bool isStarred(Database::UserId userId, Database::ReleaseId artistId) override; + + void star(Database::UserId userId, Database::TrackId trackId) override; + void unstar(Database::UserId userId, Database::TrackId trackId) override; + bool isStarred(Database::UserId userId, Database::TrackId trackId) override; + + template + void starObject(Database::UserId userId, DatabaseId objectId); + template + void unstarObject(Database::UserId userId, DatabaseId objectId); + template + bool isObjectStarred(Database::UserId userId, DatabaseId objectId); + + Database::Db& _db; + }; +} // ns Feedback + diff --git a/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp b/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp new file mode 100644 index 00000000..67b89d62 --- /dev/null +++ b/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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 "services/database/Types.hpp" + +namespace Database +{ + class Db; +} + +namespace Feedback +{ + class IFeedbackService + { + public: + virtual ~IFeedbackService() = default; + + virtual void star(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual void unstar(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual bool isStarred(Database::UserId userId, Database::ArtistId artistId) = 0; + + virtual void star(Database::UserId userId, Database::ReleaseId releaseId) = 0; + virtual void unstar(Database::UserId userId, Database::ReleaseId releaseId) = 0; + virtual bool isStarred(Database::UserId userId, Database::ReleaseId artistId) = 0; + + virtual void star(Database::UserId userId, Database::TrackId trackId) = 0; + virtual void unstar(Database::UserId userId, Database::TrackId trackId) = 0; + virtual bool isStarred(Database::UserId userId, Database::TrackId artistId) = 0; + }; + + std::unique_ptr createFeedbackService(Database::Db& db); +} // ns Feedback + diff --git a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp index af60143e..530a946e 100644 --- a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp +++ b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp @@ -26,8 +26,8 @@ #include "services/database/Types.hpp" -namespace Scanner { - +namespace Scanner +{ enum class ScanErrorType { CannotReadFile, // cannot read file @@ -102,6 +102,5 @@ namespace Scanner { std::size_t nbFiles() const; std::size_t nbChanges() const; }; - -} +} // namespace Scanner diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index d4f4b985..c36bf1ba 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -20,6 +20,7 @@ target_link_libraries(lmssubsonic PRIVATE lmsauth lmsav lmsdatabase + lmsfeedback lmsrecommendation lmsscanner lmsscrobbling diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 6778f4b0..dc6440f9 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -37,6 +37,7 @@ #include "services/database/TrackBookmark.hpp" #include "services/database/TrackList.hpp" #include "services/database/User.hpp" +#include "services/feedback/IFeedbackService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "services/cover/ICoverService.hpp" @@ -322,7 +323,7 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User: trackResponse.setAttribute("type", "music"); trackResponse.setAttribute("created", dateTimeToCreatedString(track->getLastWritten())); - if (user->hasStarredTrack(track)) + if (Service::get()->isStarred(user->getId(), track->getId())) trackResponse.setAttribute("starred", reportedStarredDate); // Report the first GENRE for this track @@ -413,7 +414,7 @@ releaseToResponseNode(const Release::pointer& release, Session& dbSession, const } } - if (user->hasStarredRelease(release)) + if (Service::get()->isStarred(user->getId(), release->getId())) albumNode.setAttribute("starred", reportedStarredDate); return albumNode; @@ -431,7 +432,7 @@ artistToResponseNode(const User::pointer& user, const Artist::pointer& artist, b if (id3) artistNode.setAttribute("albumCount", artist->getReleaseCount()); - if (user->hasStarredArtist(artist)) + if (Service::get()->isStarred(user->getId(), artist->getId())) artistNode.setAttribute("starred", reportedStarredDate); return artistNode; @@ -1471,7 +1472,7 @@ handleStarRequest(RequestContext& context) if (!artist) continue; - user.modify()->starArtist(artist); + Service::get()->star(user->getId(), artist->getId()); } for (const ReleaseId id : params.releaseIds) @@ -1480,7 +1481,7 @@ handleStarRequest(RequestContext& context) if (!release) continue; - user.modify()->starRelease(release); + Service::get()->star(user->getId(), release->getId()); } for (const TrackId id : params.trackIds) @@ -1489,7 +1490,7 @@ handleStarRequest(RequestContext& context) if (!track) continue; - user.modify()->starTrack(track); + Service::get()->star(user->getId(), track->getId()); } return Response::createOkResponse(context.serverProtocolVersion); @@ -1527,7 +1528,7 @@ handleUnstarRequest(RequestContext& context) if (!artist) continue; - user.modify()->unstarArtist(artist); + Service::get()->unstar(user->getId(), artist->getId()); } for (const ReleaseId id : params.releaseIds) @@ -1536,7 +1537,7 @@ handleUnstarRequest(RequestContext& context) if (!release) continue; - user.modify()->unstarRelease(release); + Service::get()->unstar(user->getId(), release->getId()); } for (const TrackId id : params.trackIds) @@ -1545,7 +1546,7 @@ handleUnstarRequest(RequestContext& context) if (!track) continue; - user.modify()->unstarTrack(track); + Service::get()->unstar(user->getId(), track->getId()); } diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt index 33dad4b1..83bead84 100644 --- a/src/lms/CMakeLists.txt +++ b/src/lms/CMakeLists.txt @@ -52,6 +52,7 @@ target_link_libraries(lms PRIVATE lmsav lmsauth lmsdatabase + lmsfeedback lmsrecommendation lmsscanner lmsscrobbling diff --git a/src/lms/main.cpp b/src/lms/main.cpp index e82bc6a3..8329f6d6 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -31,6 +31,7 @@ #include "services/cover/ICoverService.hpp" #include "services/database/Db.hpp" #include "services/database/Session.hpp" +#include "services/feedback/IFeedbackService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "services/scanner/IScannerService.hpp" #include "services/scrobbling/IScrobblingService.hpp" @@ -238,7 +239,7 @@ int main(int argc, char* argv[]) // Service initialization order is important (reverse-order for deinit) Service childProcessManagerService {createChildProcessManager(ioContext)}; - + Service feedbackService {Feedback::createFeedbackService(database)}; Service authTokenService; Service authPasswordService; Service authEnvService; diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 32ab87e7..fd4f42b2 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -28,6 +28,7 @@ #include "services/database/Track.hpp" #include "services/database/TrackList.hpp" #include "services/database/User.hpp" +#include "services/feedback/IFeedbackService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" @@ -488,27 +489,14 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) { Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; - bool isStarred {}; - { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - if (auto track {Database::Track::getById(LmsApp->getDbSession(), trackId)}) - isStarred = LmsApp->getUser()->hasStarredTrack(track); - } - + const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), trackId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(moreBtn, [=] { - auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; - - auto track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; - if (!track) - return; - if (isStarred) - LmsApp->getUser().modify()->unstarTrack(track); + Service::get()->unstar(LmsApp->getUserId(), trackId); else - LmsApp->getUser().modify()->starTrack(track); + Service::get()->star(LmsApp->getUserId(), trackId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(trackId)}); diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index f66337ba..e4c65a01 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -32,6 +32,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" +#include "services/feedback/IFeedbackService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" @@ -165,26 +166,14 @@ Artist::refreshView() artistsAction.emit(PlayQueueAction::PlayLast, {_artistId}); }); - bool isStarred {}; - { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - if (auto artist {Database::Artist::getById(LmsApp->getDbSession(), *artistId)}) - isStarred = LmsApp->getUser()->hasStarredArtist(artist); - } + const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), _artistId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(this, [=] { - auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; - - auto artist {Database::Artist::getById(LmsApp->getDbSession(), *artistId)}; - if (!artist) - return; - if (isStarred) - LmsApp->getUser().modify()->unstarArtist(artist); + Service::get()->unstar(LmsApp->getUserId(), _artistId); else - LmsApp->getUser().modify()->starArtist(artist); + Service::get()->star(LmsApp->getUserId(), _artistId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(*artistId)}); diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp index 6d924c43..cc970b0f 100644 --- a/src/lms/ui/explore/ReleasePopup.cpp +++ b/src/lms/ui/explore/ReleasePopup.cpp @@ -24,7 +24,9 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/User.hpp" +#include "services/feedback/IFeedbackService.hpp" #include "resource/DownloadResource.hpp" +#include "utils/Service.hpp" #include "LmsApplication.hpp" namespace UserInterface @@ -47,27 +49,14 @@ namespace UserInterface releasesAction.emit(PlayQueueAction::PlayLast, {releaseId}); }); - bool isStarred {}; - { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - if (auto release {Database::Release::getById(LmsApp->getDbSession(), releaseId)}) - isStarred = LmsApp->getUser()->hasStarredRelease(release); - } - + const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), releaseId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(&target, [=] { - auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; - - auto release {Database::Release::getById(LmsApp->getDbSession(), releaseId)}; - if (!release) - return; - if (isStarred) - LmsApp->getUser().modify()->unstarRelease(release); + Service::get()->unstar(LmsApp->getUserId(), releaseId); else - LmsApp->getUser().modify()->starRelease(release); + Service::get()->star(LmsApp->getUserId(), releaseId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(releaseId)}); diff --git a/src/lms/ui/explore/TrackPopup.cpp b/src/lms/ui/explore/TrackPopup.cpp index d1b1bdea..f95e5825 100644 --- a/src/lms/ui/explore/TrackPopup.cpp +++ b/src/lms/ui/explore/TrackPopup.cpp @@ -24,6 +24,8 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" +#include "services/feedback/IFeedbackService.hpp" +#include "utils/Service.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" @@ -42,13 +44,7 @@ namespace UserInterface tracksAction.emit(PlayQueueAction::PlayLast, {trackId}); }); - bool isStarred {}; - { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - if (auto track {Database::Track::getById(LmsApp->getDbSession(), trackId)}) - isStarred = LmsApp->getUser()->hasStarredTrack(track); - } + bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), trackId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(&target, [=] { @@ -59,9 +55,9 @@ namespace UserInterface return; if (isStarred) - LmsApp->getUser().modify()->unstarTrack(track); + Service::get()->unstar(LmsApp->getUserId(), trackId); else - LmsApp->getUser().modify()->starTrack(track); + Service::get()->star(LmsApp->getUserId(), trackId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(trackId)}); From 0cf6b2159e5fa6b5e087d11f5c4f02cb65dfbe29 Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 4 Dec 2021 15:25:09 +0100 Subject: [PATCH 27/49] Databsae service refactoring. Warning, loses stars and listens stats --- src/libs/services/CMakeLists.txt | 1 - .../services/auth/impl/AuthServiceBase.cpp | 4 +- .../services/auth/impl/AuthServiceBase.hpp | 2 +- .../services/auth/impl/AuthTokenService.cpp | 7 +- .../impl/internal/InternalPasswordService.cpp | 4 +- .../services/auth/IAuthTokenService.hpp | 2 +- .../include/services/auth/IEnvService.hpp | 2 +- .../services/auth/IPasswordService.hpp | 2 +- src/libs/services/cover/impl/CoverService.cpp | 4 +- .../include/services/cover/ICoverService.hpp | 3 +- src/libs/services/database/CMakeLists.txt | 6 + src/libs/services/database/impl/Artist.cpp | 367 ++----- src/libs/services/database/impl/AuthToken.cpp | 67 ++ src/libs/services/database/impl/Cluster.cpp | 104 +- .../impl/{Traits.hpp => IdTypeTraits.hpp} | 0 src/libs/services/database/impl/Listen.cpp | 285 ++++++ src/libs/services/database/impl/Release.cpp | 291 ++---- .../services/database/impl/ScanSettings.cpp | 2 +- src/libs/services/database/impl/Session.cpp | 71 +- .../services/database/impl/StarredArtist.cpp | 79 ++ .../services/database/impl/StarredRelease.cpp | 79 ++ .../services/database/impl/StarredTrack.cpp | 79 ++ src/libs/services/database/impl/Track.cpp | 355 ++----- .../database/impl/TrackArtistLink.cpp | 4 +- .../services/database/impl/TrackBookmark.cpp | 41 +- .../services/database/impl/TrackFeatures.cpp | 55 +- src/libs/services/database/impl/TrackList.cpp | 190 +++- src/libs/services/database/impl/Types.cpp | 52 + src/libs/services/database/impl/User.cpp | 167 +--- src/libs/services/database/impl/Utils.cpp | 7 + src/libs/services/database/impl/Utils.hpp | 57 +- .../include/services/database/Artist.hpp | 79 +- .../include/services/database/ArtistId.hpp | 25 + .../include/services/database/AuthToken.hpp | 66 ++ .../include/services/database/AuthTokenId.hpp | 24 + .../include/services/database/Cluster.hpp | 38 +- .../include/services/database/ClusterId.hpp | 26 + .../include/services/database/IdType.hpp | 72 ++ .../include/services/database/Listen.hpp | 111 +++ .../include/services/database/Object.hpp | 67 ++ .../include/services/database/Release.hpp | 62 +- .../include/services/database/ReleaseId.hpp | 25 + .../services/database/ScanSettings.hpp | 5 +- .../services/database/StarredArtist.hpp | 81 ++ .../services/database/StarredRelease.hpp | 81 ++ .../services/database/StarredTrack.hpp | 81 ++ .../include/services/database/Track.hpp | 82 +- .../services/database/TrackArtistLink.hpp | 9 +- .../services/database/TrackBookmark.hpp | 20 +- .../services/database/TrackFeatures.hpp | 17 +- .../include/services/database/TrackId.hpp | 25 + .../include/services/database/TrackList.hpp | 54 +- .../include/services/database/TrackListId.hpp | 26 + .../include/services/database/Types.hpp | 218 ++--- .../include/services/database/User.hpp | 117 +-- .../include/services/database/UserId.hpp | 25 + src/libs/services/database/test/Artist.cpp | 184 ++-- .../services/database/test/CMakeLists.txt | 8 + src/libs/services/database/test/Cluster.cpp | 371 ++++---- src/libs/services/database/test/Common.cpp | 211 +++++ src/libs/services/database/test/Common.hpp | 60 +- .../services/database/test/DatabaseTest.cpp | 218 +---- src/libs/services/database/test/Listen.cpp | 892 ++++++++++++++++++ src/libs/services/database/test/Release.cpp | 133 +-- .../services/database/test/StarredArtist.cpp | 109 +++ .../services/database/test/StarredRelease.cpp | 109 +++ .../services/database/test/StarredTrack.cpp | 109 +++ src/libs/services/database/test/Track.cpp | 71 +- .../services/database/test/TrackBookmark.cpp | 66 ++ .../services/database/test/TrackFeatures.cpp | 48 + src/libs/services/database/test/TrackList.cpp | 366 +++++++ src/libs/services/feedback/CMakeLists.txt | 19 - .../feedback/impl/FeedbackService.cpp | 183 ---- .../feedback/impl/FeedbackService.hpp | 59 -- .../services/feedback/IFeedbackService.hpp | 51 - .../services/recommendation/impl/IEngine.hpp | 9 +- .../impl/RecommendationService.cpp | 10 +- .../impl/RecommendationService.hpp | 4 +- .../impl/clusters/ClustersEngine.cpp | 55 +- .../impl/clusters/ClustersEngine.hpp | 4 +- .../impl/features/FeaturesEngine.cpp | 118 +-- .../impl/features/FeaturesEngine.hpp | 11 +- .../impl/features/FeaturesEngineCache.hpp | 3 +- .../recommendation/IRecommendationService.hpp | 7 +- .../include/services/recommendation/Types.hpp | 4 +- .../services/scanner/impl/ScannerService.cpp | 85 +- .../include/services/scanner/ScannerStats.hpp | 2 +- .../services/scrobbling/impl/IScrobbler.hpp | 19 +- .../scrobbling/impl/ScrobblerBase.cpp | 72 ++ .../scrobbling/impl/ScrobblingService.cpp | 272 +++--- .../scrobbling/impl/ScrobblingService.hpp | 44 +- .../impl/ScrobblingService.impl.hpp | 93 ++ .../impl/internal/InternalScrobbler.cpp | 34 +- .../impl/internal/InternalScrobbler.hpp | 6 +- .../listenbrainz/ListenBrainzScrobbler.cpp | 10 +- .../listenbrainz/ListenBrainzScrobbler.hpp | 2 - .../impl/listenbrainz/ListensSynchronizer.cpp | 70 +- .../impl/listenbrainz/ListensSynchronizer.hpp | 1 - .../impl/listenbrainz/LovesSynchronizer.hpp | 92 ++ .../scrobbling/impl/listenbrainz/Utils.cpp | 19 +- .../scrobbling/impl/listenbrainz/Utils.hpp | 5 +- .../scrobbling/IScrobblingService.hpp | 59 +- .../include/services/scrobbling/Listen.hpp | 3 +- src/libs/subsonic/CMakeLists.txt | 1 - src/libs/subsonic/impl/RequestContext.hpp | 2 +- src/libs/subsonic/impl/Stream.cpp | 6 +- src/libs/subsonic/impl/SubsonicId.hpp | 5 +- src/libs/subsonic/impl/SubsonicResource.cpp | 382 ++++---- src/lms/CMakeLists.txt | 1 - src/lms/main.cpp | 2 - src/lms/ui/Auth.cpp | 4 +- src/lms/ui/Auth.hpp | 3 +- src/lms/ui/LmsApplication.cpp | 4 +- src/lms/ui/LmsApplication.hpp | 3 + src/lms/ui/LmsApplicationManager.hpp | 2 +- src/lms/ui/LmsTheme.cpp | 10 +- src/lms/ui/LmsTheme.hpp | 10 +- src/lms/ui/MediaPlayer.cpp | 8 +- src/lms/ui/MediaPlayer.hpp | 1 + src/lms/ui/PlayQueue.cpp | 22 +- src/lms/ui/PlayQueue.hpp | 3 +- src/lms/ui/PlayQueueAction.hpp | 4 +- src/lms/ui/SettingsView.cpp | 19 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/ScannerController.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 28 +- src/lms/ui/admin/UsersView.cpp | 8 +- src/lms/ui/explore/ArtistCollector.cpp | 130 ++- src/lms/ui/explore/ArtistCollector.hpp | 10 +- src/lms/ui/explore/ArtistListHelpers.hpp | 2 +- src/lms/ui/explore/ArtistView.cpp | 55 +- src/lms/ui/explore/ArtistView.hpp | 2 +- src/lms/ui/explore/ArtistsView.cpp | 22 +- src/lms/ui/explore/DatabaseCollectorBase.cpp | 17 +- src/lms/ui/explore/DatabaseCollectorBase.hpp | 11 +- src/lms/ui/explore/Explore.cpp | 4 +- src/lms/ui/explore/Filters.cpp | 35 +- src/lms/ui/explore/Filters.hpp | 2 +- src/lms/ui/explore/ReleaseCollector.cpp | 123 ++- src/lms/ui/explore/ReleaseCollector.hpp | 13 +- src/lms/ui/explore/ReleaseListHelpers.hpp | 2 +- src/lms/ui/explore/ReleasePopup.cpp | 8 +- src/lms/ui/explore/ReleaseView.cpp | 20 +- src/lms/ui/explore/ReleaseView.hpp | 2 +- src/lms/ui/explore/ReleasesView.cpp | 20 +- src/lms/ui/explore/SearchView.cpp | 53 +- src/lms/ui/explore/TrackCollector.cpp | 121 ++- src/lms/ui/explore/TrackCollector.hpp | 11 +- src/lms/ui/explore/TrackListHelpers.hpp | 2 +- src/lms/ui/explore/TrackPopup.cpp | 10 +- src/lms/ui/explore/TracksView.cpp | 20 +- src/lms/ui/resource/AudioFileResource.cpp | 2 +- src/lms/ui/resource/AudioFileResource.hpp | 2 +- .../ui/resource/AudioTranscodeResource.cpp | 4 +- .../ui/resource/AudioTranscodeResource.hpp | 2 +- src/lms/ui/resource/CoverResource.hpp | 3 +- src/lms/ui/resource/DownloadResource.cpp | 12 +- src/lms/ui/resource/DownloadResource.hpp | 4 +- src/tools/cover/LmsCover.cpp | 8 +- .../recommendation/LmsRecommendation.cpp | 55 +- 160 files changed, 6047 insertions(+), 3477 deletions(-) create mode 100644 src/libs/services/database/impl/AuthToken.cpp rename src/libs/services/database/impl/{Traits.hpp => IdTypeTraits.hpp} (100%) create mode 100644 src/libs/services/database/impl/Listen.cpp create mode 100644 src/libs/services/database/impl/StarredArtist.cpp create mode 100644 src/libs/services/database/impl/StarredRelease.cpp create mode 100644 src/libs/services/database/impl/StarredTrack.cpp create mode 100644 src/libs/services/database/impl/Types.cpp create mode 100644 src/libs/services/database/include/services/database/ArtistId.hpp create mode 100644 src/libs/services/database/include/services/database/AuthToken.hpp create mode 100644 src/libs/services/database/include/services/database/AuthTokenId.hpp create mode 100644 src/libs/services/database/include/services/database/ClusterId.hpp create mode 100644 src/libs/services/database/include/services/database/IdType.hpp create mode 100644 src/libs/services/database/include/services/database/Listen.hpp create mode 100644 src/libs/services/database/include/services/database/Object.hpp create mode 100644 src/libs/services/database/include/services/database/ReleaseId.hpp create mode 100644 src/libs/services/database/include/services/database/StarredArtist.hpp create mode 100644 src/libs/services/database/include/services/database/StarredRelease.hpp create mode 100644 src/libs/services/database/include/services/database/StarredTrack.hpp create mode 100644 src/libs/services/database/include/services/database/TrackId.hpp create mode 100644 src/libs/services/database/include/services/database/TrackListId.hpp create mode 100644 src/libs/services/database/include/services/database/UserId.hpp create mode 100644 src/libs/services/database/test/Common.cpp create mode 100644 src/libs/services/database/test/Listen.cpp create mode 100644 src/libs/services/database/test/StarredArtist.cpp create mode 100644 src/libs/services/database/test/StarredRelease.cpp create mode 100644 src/libs/services/database/test/StarredTrack.cpp create mode 100644 src/libs/services/database/test/TrackBookmark.cpp create mode 100644 src/libs/services/database/test/TrackFeatures.cpp create mode 100644 src/libs/services/database/test/TrackList.cpp delete mode 100644 src/libs/services/feedback/CMakeLists.txt delete mode 100644 src/libs/services/feedback/impl/FeedbackService.cpp delete mode 100644 src/libs/services/feedback/impl/FeedbackService.hpp delete mode 100644 src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp create mode 100644 src/libs/services/scrobbling/impl/ScrobblerBase.cpp create mode 100644 src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp create mode 100644 src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp diff --git a/src/libs/services/CMakeLists.txt b/src/libs/services/CMakeLists.txt index 70f360ed..fd4d3160 100644 --- a/src/libs/services/CMakeLists.txt +++ b/src/libs/services/CMakeLists.txt @@ -1,7 +1,6 @@ add_subdirectory(auth) add_subdirectory(cover) add_subdirectory(database) -add_subdirectory(feedback) add_subdirectory(recommendation) add_subdirectory(scanner) add_subdirectory(scrobbling) diff --git a/src/libs/services/auth/impl/AuthServiceBase.cpp b/src/libs/services/auth/impl/AuthServiceBase.cpp index 6b3c9a95..01b87a0a 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.cpp +++ b/src/libs/services/auth/impl/AuthServiceBase.cpp @@ -38,7 +38,7 @@ namespace Auth Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; - User::pointer user {User::getByLoginName(session, loginName)}; + User::pointer user {User::find(session, loginName)}; if (!user) { const UserType type {User::getCount(session) == 0 ? UserType::ADMIN : UserType::REGULAR}; @@ -58,7 +58,7 @@ namespace Auth Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; - User::pointer user {User::getById(session, userId)}; + User::pointer user {User::find(session, userId)}; if (user) user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); } diff --git a/src/libs/services/auth/impl/AuthServiceBase.hpp b/src/libs/services/auth/impl/AuthServiceBase.hpp index 812e716f..50879896 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.hpp +++ b/src/libs/services/auth/impl/AuthServiceBase.hpp @@ -20,7 +20,7 @@ #pragma once #include -#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace Database { diff --git a/src/libs/services/auth/impl/AuthTokenService.cpp b/src/libs/services/auth/impl/AuthTokenService.cpp index 24b9cee4..01ac9c87 100644 --- a/src/libs/services/auth/impl/AuthTokenService.cpp +++ b/src/libs/services/auth/impl/AuthTokenService.cpp @@ -24,6 +24,7 @@ #include #include "services/auth/Types.hpp" +#include "services/database/AuthToken.hpp" #include "services/database/Session.hpp" #include "services/database/User.hpp" #include "utils/Exception.hpp" @@ -55,7 +56,7 @@ namespace Auth auto transaction {session.createUniqueTransaction()}; - Database::User::pointer user {Database::User::getById(session, userId)}; + Database::User::pointer user {Database::User::find(session, userId)}; if (!user) throw Exception {"User deleted"}; @@ -77,7 +78,7 @@ namespace Auth Database::Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; - Database::AuthToken::pointer authToken {Database::AuthToken::getByValue(session, secretHash)}; + Database::AuthToken::pointer authToken {Database::AuthToken::find(session, secretHash)}; if (!authToken) return std::nullopt; @@ -132,7 +133,7 @@ namespace Auth auto transaction {session.createUniqueTransaction()}; - Database::User::pointer user {Database::User::getById(session, userId)}; + Database::User::pointer user {Database::User::find(session, userId)}; if (!user) throw Exception {"User deleted"}; diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp index d63de7ed..ebc55158 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.cpp @@ -51,7 +51,7 @@ namespace Auth Database::Session& session {getDbSession()}; auto transaction {session.createSharedTransaction()}; - const Database::User::pointer user {Database::User::getByLoginName(session, loginName)}; + const Database::User::pointer user {Database::User::find(session, loginName)}; if (!user) { LMS_LOG(AUTH, DEBUG) << "hashing random stuff"; @@ -102,7 +102,7 @@ namespace Auth Database::Session& session {getDbSession()}; auto transaction {session.createUniqueTransaction()}; - Database::User::pointer user {Database::User::getById(session, userId)}; + Database::User::pointer user {Database::User::find(session, userId)}; if (!user) throw Exception {"User not found!"}; diff --git a/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp b/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp index 884dd7d1..bd5754de 100644 --- a/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp +++ b/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp @@ -27,7 +27,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace Database { diff --git a/src/libs/services/auth/include/services/auth/IEnvService.hpp b/src/libs/services/auth/include/services/auth/IEnvService.hpp index 291bfab6..ad9dda2c 100644 --- a/src/libs/services/auth/include/services/auth/IEnvService.hpp +++ b/src/libs/services/auth/include/services/auth/IEnvService.hpp @@ -22,7 +22,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace Database { diff --git a/src/libs/services/auth/include/services/auth/IPasswordService.hpp b/src/libs/services/auth/include/services/auth/IPasswordService.hpp index 4958e746..18319106 100644 --- a/src/libs/services/auth/include/services/auth/IPasswordService.hpp +++ b/src/libs/services/auth/include/services/auth/IPasswordService.hpp @@ -26,7 +26,7 @@ #include #include "services/auth/Types.hpp" -#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace Database { diff --git a/src/libs/services/cover/impl/CoverService.cpp b/src/libs/services/cover/impl/CoverService.cpp index 58537ad1..a9c2b8e4 100644 --- a/src/libs/services/cover/impl/CoverService.cpp +++ b/src/libs/services/cover/impl/CoverService.cpp @@ -50,7 +50,7 @@ namespace auto transaction {dbSession.createSharedTransaction()}; - const Database::Track::pointer track {Database::Track::getById(dbSession, trackId)}; + const Database::Track::pointer track {Database::Track::find(dbSession, trackId)}; if (!track) return res; @@ -374,7 +374,7 @@ CoverService::getFromRelease(Database::ReleaseId releaseId, ImageSize width) auto transaction {session.createSharedTransaction()}; - if (const Database::Release::pointer release {Database::Release::getById(session, releaseId)}) + if (const Database::Release::pointer release {Database::Release::find(session, releaseId)}) { if (const auto firstTrack {release->getFirstTrack()}) { diff --git a/src/libs/services/cover/include/services/cover/ICoverService.hpp b/src/libs/services/cover/include/services/cover/ICoverService.hpp index 392f0dfc..0421f589 100644 --- a/src/libs/services/cover/include/services/cover/ICoverService.hpp +++ b/src/libs/services/cover/include/services/cover/ICoverService.hpp @@ -22,7 +22,8 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" #include "image/IEncodedImage.hpp" namespace Database diff --git a/src/libs/services/database/CMakeLists.txt b/src/libs/services/database/CMakeLists.txt index 38b13e75..8e8b9fe3 100644 --- a/src/libs/services/database/CMakeLists.txt +++ b/src/libs/services/database/CMakeLists.txt @@ -1,16 +1,22 @@ add_library(lmsdatabase SHARED impl/Artist.cpp + impl/AuthToken.cpp impl/Cluster.cpp impl/Db.cpp + impl/Listen.cpp impl/TrackArtistLink.cpp impl/TrackFeatures.cpp impl/TrackList.cpp impl/Release.cpp impl/ScanSettings.cpp impl/Session.cpp + impl/StarredArtist.cpp + impl/StarredRelease.cpp + impl/StarredTrack.cpp impl/SqlQuery.cpp impl/Track.cpp impl/TrackBookmark.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 9b139381..6a7428a0 100644 --- a/src/libs/services/database/impl/Artist.cpp +++ b/src/libs/services/database/impl/Artist.cpp @@ -28,7 +28,7 @@ #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Utils.hpp" -#include "Traits.hpp" +#include "IdTypeTraits.hpp" namespace Database { @@ -40,8 +40,16 @@ _MBID {MBID ? MBID->getAsString() : ""} { } +std::size_t +Artist::getCount(Session& session) +{ + session.checkSharedLocked(); + + return session.getDboSession().query("SELECT COUNT(*) FROM artist"); +} + std::vector -Artist::getByName(Session& session, const std::string& name) +Artist::find(Session& session, const std::string& name) { session.checkSharedLocked(); @@ -53,14 +61,14 @@ Artist::getByName(Session& session, const std::string& name) } Artist::pointer -Artist::getByMBID(Session& session, const UUID& mbid) +Artist::find(Session& session, const UUID& mbid) { session.checkSharedLocked(); return session.getDboSession().find().where("mbid = ?").bind(std::string {mbid.getAsString()}).resultValue(); } Artist::pointer -Artist::getById(Session& session, ArtistId id) +Artist::find(Session& session, ArtistId id) { session.checkSharedLocked(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); @@ -84,36 +92,37 @@ Artist::create(Session& session, const std::string& name, const std::optional static -Wt::Dbo::Query -createQuery(Session& session, - const std::string& queryStr, - const std::vector& clusterIds, - const std::vector& keywords, - std::optional linkType) +Wt::Dbo::Query +createQuery(Session& session, const Artist::FindParameters& params) { session.checkSharedLocked(); - auto query {session.getDboSession().query(queryStr)}; - query.join("track t ON t.id = t_a_l.track_id"); - query.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id"); + auto query {session.getDboSession().query("SELECT DISTINCT a.id FROM artist a")}; + if (params.sortMethod == ArtistSortMethod::LastWritten || params.writtenAfter.isValid() || params.linkType) + { + query.join("track t ON t.id = t_a_l.track_id"); + query.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id"); + } - if (linkType) - query.where("t_a_l.type = ?").bind(*linkType); + if (params.linkType) + query.where("t_a_l.type = ?").bind(*params.linkType); - if (!keywords.empty()) + if (params.writtenAfter.isValid()) + query.where("t.file_last_write > ?").bind(params.writtenAfter); + + if (!params.keywords.empty()) { std::vector clauses; std::vector sortClauses; - for (std::string_view keyword : keywords) + for (std::string_view keyword : params.keywords) { clauses.push_back("a.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'"); query.bind("%" + escapeLikeKeyword(keyword) + "%"); } - for (std::string_view keyword : keywords) + for (std::string_view keyword : params.keywords) { sortClauses.push_back("a.sort_name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'"); query.bind("%" + escapeLikeKeyword(keyword) + "%"); @@ -122,7 +131,15 @@ createQuery(Session& session, query.where("(" + StringUtils::joinStrings(clauses, " AND ") + ") OR (" + StringUtils::joinStrings(sortClauses, " AND ") + ")"); } - if (!clusterIds.empty()) + if (params.starringUser.isValid()) + { + assert(params.scrobbler); + query.join("starred_artist s_a ON s_a.artist_id = a.id") + .where("s_a.user_id = ?").bind(params.starringUser) + .where("s_a.scrobbler = ?").bind(*params.scrobbler); + } + + if (!params.clusters.empty()) { std::ostringstream oss; oss << "a.id IN (SELECT DISTINCT a.id FROM artist a" @@ -132,268 +149,59 @@ createQuery(Session& session, " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"; WhereClause clusterClause; - for (const ClusterId clusterId : clusterIds) + for (const ClusterId clusterId : params.clusters) { clusterClause.Or(WhereClause("c.id = ?")); query.bind(clusterId); } oss << " " << clusterClause.get(); - oss << " GROUP BY t.id,a.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size() << ")"; + oss << " GROUP BY t.id,a.id HAVING COUNT(DISTINCT c.id) = " << params.clusters.size() << ")"; query.where(oss.str()); } + switch (params.sortMethod) + { + case ArtistSortMethod::None: + break; + case ArtistSortMethod::ByName: + query.orderBy("a.name COLLATE NOCASE"); + break; + case ArtistSortMethod::BySortName: + query.orderBy("a.sort_name COLLATE NOCASE"); + break; + case ArtistSortMethod::Random: + query.orderBy("RANDOM()"); + break; + case ArtistSortMethod::LastWritten: + query.orderBy("t.file_last_write DESC"); + break; + case ArtistSortMethod::StarredDateDesc: + assert(params.starringUser.isValid()); + query.orderBy("s_a.date_time DESC"); + break; + } + return query; } -std::vector -Artist::getAll(Session& session) +RangeResults +Artist::findAllOrphans(Session& session, Range range) { session.checkSharedLocked(); + 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)")}; - Wt::Dbo::collection> res = session.getDboSession().find(); - return std::vector(res.begin(), res.end()); + return execQuery(query, range); } -std::vector -Artist::getAll(Session& session, SortMethod sortMethod) +RangeResults +Artist::find(Session& session, const FindParameters& params) { session.checkSharedLocked(); - auto query {session.getDboSession().find()}; - switch (sortMethod) - { - case Artist::SortMethod::None: - break; - case Artist::SortMethod::ByName: - query.orderBy("name COLLATE NOCASE"); - break; - case Artist::SortMethod::BySortName: - query.orderBy("sort_name COLLATE NOCASE"); - break; - } - - Wt::Dbo::collection> res = query; - return std::vector(res.begin(), res.end()); -} - - -std::vector -Artist::getAll(Session& session, SortMethod sortMethod, std::optional range, bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT a FROM Artist a", {}, {}, std::nullopt)}; - - switch (sortMethod) - { - case Artist::SortMethod::None: - break; - case Artist::SortMethod::ByName: - query.orderBy("a.name COLLATE NOCASE"); - break; - case Artist::SortMethod::BySortName: - query.orderBy("a.sort_name COLLATE NOCASE"); - break; - } - - Wt::Dbo::collection> collection = query - .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1); - - std::vector res (collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; -} - -std::vector -Artist::getAllIds(Session& session) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().query("SELECT id FROM artist"); - return std::vector(res.begin(), res.end()); -} - -std::vector -Artist::getAllIdsRandom(Session& session, const std::vector& clusters, std::optional linkType, std::optional size) -{ - session.checkSharedLocked(); - - auto query {createQuery(session, "SELECT DISTINCT a.id from artist a", clusters, {}, linkType)}; - - Wt::Dbo::collection res = query - .orderBy("RANDOM()") - .limit(size ? static_cast(*size) : -1); - - return std::vector(res.begin(), res.end()); - -} - -std::vector -Artist::getAllOrphans(Session& session) -{ - session.checkSharedLocked(); - Wt::Dbo::collection> res {session.getDboSession().query>("SELECT DISTINCT a 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 std::vector(res.begin(), res.end()); -} - -std::vector -Artist::getAllIdsWithClusters(Session& session, std::optional limit) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().query - ("SELECT DISTINCT a.id FROM artist a" - " INNER JOIN track t ON t.id = t_a_l.track_id INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id" - " INNER JOIN track_cluster t_c ON t_c.track_id = t.id") - .limit(limit ? static_cast(*limit) : -1); - - return std::vector(res.begin(), res.end()); -} - -std::vector -Artist::getByClusters(Session& session, const std::vector& clusters, SortMethod sortMethod) -{ - assert(!clusters.empty()); - - session.checkSharedLocked(); - bool more{}; - return getByFilter(session, clusters, {}, std::nullopt, sortMethod, std::nullopt, more); -} - -std::vector -Artist::getByFilter(Session& session, - const std::vector& clusters, - const std::vector& keywords, - std::optional linkType, - SortMethod sortMethod, - std::optional range, - bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT DISTINCT a from artist a", clusters, keywords, linkType)}; - switch (sortMethod) - { - case Artist::SortMethod::None: - break; - case Artist::SortMethod::ByName: - query.orderBy("a.name COLLATE NOCASE"); - break; - case Artist::SortMethod::BySortName: - query.orderBy("a.sort_name COLLATE NOCASE"); - break; - } - - Wt::Dbo::collection> collection = query - .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1); - - std::vector res (collection.begin(), collection.end()); - - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; -} - -std::vector -Artist::getLastWritten(Session& session, - std::optional after, - const std::vector& clusters, - std::optional linkType, - std::optional range, bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT DISTINCT a from artist a", clusters, {}, linkType)}; - - if (after) - query.where("t.file_last_write > ?").bind(*after); - - Wt::Dbo::collection> collection = query - .orderBy("t.file_last_write DESC") - .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1); - - std::vector res (collection.begin(), collection.end()); - - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return std::vector(res.begin(), res.end()); -} - -std::vector -Artist::getStarred(Session& session, - User::pointer user, - const std::vector& clusters, - std::optional linkType, - SortMethod sortMethod, - std::optional range, bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT DISTINCT a from artist a", clusters, {}, linkType)}; - - { - std::ostringstream oss; - oss << "a.id IN (SELECT DISTINCT a.id FROM artist a" - " INNER JOIN user_artist_starred uas ON uas.artist_id = a.id" - " INNER JOIN user u ON u.id = uas.user_id WHERE u.id = ?)"; - - query.bind(user->getId()); - query.where(oss.str()); - } - - switch (sortMethod) - { - case Artist::SortMethod::None: - break; - case Artist::SortMethod::ByName: - query.orderBy("name COLLATE NOCASE"); - break; - case Artist::SortMethod::BySortName: - query.orderBy("sort_name COLLATE NOCASE"); - break; - } - - Wt::Dbo::collection> collection = query - .groupBy("a.id") - .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1); - - std::vector res (collection.begin(), collection.end()); - - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return std::vector(res.begin(), res.end()); + auto query {createQuery(session, params)}; + return execQuery(query, params.range); } std::vector @@ -463,32 +271,19 @@ Artist::getTracks(std::optional linkType) const return std::vector(tracks.begin(), tracks.end()); } -std::vector -Artist::getNonReleaseTracks(std::optional linkType, std::optional range, bool& moreResults) const +RangeResults +Artist::getNonReleaseTracks(std::optional linkType, Range range) const { assert(session()); auto query {session()->query>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id") .where("a.id = ?").bind(getId()) .where("t.release_id is NULL") - .orderBy("t.name") - .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1)}; - + .orderBy("t.name")}; if (linkType) query.where("t_a_l.type = ?").bind(*linkType); - Wt::Dbo::collection> tracks {query.resultList()}; - std::vector res(tracks.begin(), tracks.end()); - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; + return execQuery(query, range); } bool @@ -518,14 +313,14 @@ Artist::getRandomTracks(std::optional count) const return std::vector(tracks.begin(), tracks.end()); } -std::vector -Artist::getSimilarArtists(EnumSet artistLinkTypes, std::optional range) const +RangeResults +Artist::findSimilarArtists(EnumSet artistLinkTypes, Range range) const { assert(session()); std::ostringstream oss; oss << - "SELECT a FROM artist a" + "SELECT a.id FROM artist a" " INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id" " INNER JOIN track t ON t.id = t_a_l.track_id" " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" @@ -554,19 +349,15 @@ Artist::getSimilarArtists(EnumSet artistLinkTypes, std::opt oss << ")"; } - Wt::Dbo::Query> query {session()->query>(oss.str()) + auto query {session()->query(oss.str()) .bind(getId()) .bind(getId()) .groupBy("a.id") - .orderBy("COUNT(*) DESC, RANDOM()") - .limit(range ? static_cast(range->limit) : -1) - .offset(range ? static_cast(range->offset) : -1)}; - + .orderBy("COUNT(*) DESC, RANDOM()")}; for (TrackArtistLinkType type : artistLinkTypes) query.bind(type); - Wt::Dbo::collection> res {query.resultList()}; - return std::vector(res.begin(), res.end()); + return execQuery(query, range); } std::vector> diff --git a/src/libs/services/database/impl/AuthToken.cpp b/src/libs/services/database/impl/AuthToken.cpp new file mode 100644 index 00000000..52efdace --- /dev/null +++ b/src/libs/services/database/impl/AuthToken.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include "services/database/AuthToken.hpp" + +#include +#include "services/database/Session.hpp" +#include "services/database/User.hpp" +#include "StringViewTraits.hpp" +#include "IdTypeTraits.hpp" + +namespace Database +{ + + AuthToken::AuthToken(std::string_view value, const Wt::WDateTime& expiry, ObjectPtr user) + : _value {value} + , _expiry {expiry} + , _user {getDboPtr(user)} + { + + } + + AuthToken::pointer + AuthToken::create(Session& session, std::string_view value, const Wt::WDateTime& expiry, ObjectPtr user) + { + session.checkUniqueLocked(); + + AuthToken::pointer res {session.getDboSession().add(std::make_unique(value, expiry, user))}; + session.getDboSession().flush(); + + return res; + } + + void + AuthToken::removeExpiredTokens(Session& session, const Wt::WDateTime& now) + { + session.checkUniqueLocked(); + + session.getDboSession().execute("DELETE FROM auth_token WHERE expiry < ?").bind(now); + } + + AuthToken::pointer + AuthToken::find(Session& session, std::string_view value) + { + session.checkSharedLocked(); + + return session.getDboSession().find() + .where("value = ?").bind(value) + .resultValue(); + } +} diff --git a/src/libs/services/database/impl/Cluster.cpp b/src/libs/services/database/impl/Cluster.cpp index cf4350cc..87ec28b1 100644 --- a/src/libs/services/database/impl/Cluster.cpp +++ b/src/libs/services/database/impl/Cluster.cpp @@ -24,8 +24,9 @@ #include "services/database/ScanSettings.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" +#include "IdTypeTraits.hpp" #include "SqlQuery.hpp" -#include "Traits.hpp" +#include "Utils.hpp" namespace Database { @@ -35,6 +36,14 @@ Cluster::Cluster(ObjectPtr type, std::string_view name) { } +std::size_t +Cluster::getCount(Session& session) +{ + session.checkSharedLocked(); + + return session.getDboSession().query("SELECT COUNT(*) FROM cluster"); +} + Cluster::pointer Cluster::create(Session& session, ObjectPtr type, std::string_view name) { @@ -46,25 +55,26 @@ Cluster::create(Session& session, ObjectPtr type, std::string_view return res; } -std::vector -Cluster::getAll(Session& session) +RangeResults +Cluster::find(Session& session, Range range) { session.checkSharedLocked(); + auto query {session.getDboSession().query("SELECT id FROM cluster")}; - Wt::Dbo::collection> res {session.getDboSession().find()}; - return std::vector(res.begin(), res.end()); + return execQuery(query, range); } -std::vector -Cluster::getAllOrphans(Session& session) +RangeResults +Cluster::findOrphans(Session& session, Range range) { session.checkSharedLocked(); - auto res {session.getDboSession().query>("SELECT DISTINCT c FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)").resultList()}; - return std::vector(res.begin(), res.end()); + 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 execQuery(query, range); } Cluster::pointer -Cluster::getById(Session& session, ClusterId id) +Cluster::find(Session& session, ClusterId id) { session.checkSharedLocked(); @@ -77,29 +87,15 @@ Cluster::addTrack(ObjectPtr track) _tracks.insert(getDboPtr(track)); } -std::vector -Cluster::getTracks(std::optional offset, std::optional limit) const +RangeResults +Cluster::getTracks(Range range) const { assert(session()); - auto res {session()->query>("SELECT t FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") - .where("c.id = ?").bind(getId()) - .offset(offset ? static_cast(*offset) : -1) - .limit(limit ? static_cast(*limit) : -1) - .resultList()}; + auto query {session()->query("SELECT t.id FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") + .where("c.id = ?").bind(getId())}; - return std::vector(res.begin(), res.end()); -} - -std::vector -Cluster::getTrackIds() const -{ - assert(session()); - - Wt::Dbo::collection res = session()->query("SELECT t_c.track_id FROM track_cluster t_c INNER JOIN cluster c ON c.id = t_c.cluster_id") - .where("c.id = ?").bind(getId()); - - return std::vector(res.begin(), res.end()); + return execQuery(query, range); } std::size_t @@ -117,33 +113,42 @@ ClusterType::ClusterType(std::string_view name) { } -std::vector -ClusterType::getAllOrphans(Session& session) +std::size_t +ClusterType::getCount(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection> res = session.getDboSession().query>( - "SELECT c_t from cluster_type c_t" - " LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id") - .where("c.id IS NULL"); - - return std::vector(res.begin(), res.end()); + return session.getDboSession().query("SELECT COUNT(*) FROM cluster_type"); } -std::vector -ClusterType::getAllUsed(Session& session) + +RangeResults +ClusterType::findOrphans(Session& session, Range range) { session.checkSharedLocked(); - Wt::Dbo::collection> res = session.getDboSession().query>( - "SELECT DISTINCT c_t from cluster_type c_t") - .join("cluster c ON c_t.id = c.cluster_type_id"); + auto query {session.getDboSession().query( + "SELECT c_t.id from cluster_type c_t" + " LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id") + .where("c.id IS NULL")}; - return std::vector(res.begin(), res.end()); + return execQuery(query, range); +} + +RangeResults +ClusterType::findUsed(Session& session, Range range) +{ + session.checkSharedLocked(); + + auto query {session.getDboSession().query( + "SELECT DISTINCT c_t.id from cluster_type c_t") + .join("cluster c ON c_t.id = c.cluster_type_id")}; + + return execQuery(query, range); } ClusterType::pointer -ClusterType::getByName(Session& session, const std::string& name) +ClusterType::find(Session& session, const std::string& name) { session.checkSharedLocked(); @@ -151,20 +156,21 @@ ClusterType::getByName(Session& session, const std::string& name) } ClusterType::pointer -ClusterType::getById(Session& session, ClusterTypeId id) +ClusterType::find(Session& session, ClusterTypeId id) { session.checkSharedLocked(); return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } -std::vector -ClusterType::getAll(Session& session) +RangeResults +ClusterType::find(Session& session, Range range) { session.checkSharedLocked(); - auto res {session.getDboSession().find().resultList()}; - return std::vector(res.begin(), res.end()); + auto query {session.getDboSession().query("SELECT id from cluster_type")}; + + return execQuery(query, range); } ClusterType::pointer diff --git a/src/libs/services/database/impl/Traits.hpp b/src/libs/services/database/impl/IdTypeTraits.hpp similarity index 100% rename from src/libs/services/database/impl/Traits.hpp rename to src/libs/services/database/impl/IdTypeTraits.hpp diff --git a/src/libs/services/database/impl/Listen.cpp b/src/libs/services/database/impl/Listen.cpp new file mode 100644 index 00000000..fde20d8c --- /dev/null +++ b/src/libs/services/database/impl/Listen.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2021 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/Listen.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" +#include "IdTypeTraits.hpp" +#include "SqlQuery.hpp" +#include "Utils.hpp" + +namespace +{ + using namespace Database; + + Wt::Dbo::Query + createArtistsQuery(Wt::Dbo::Session& session, UserId userId, Scrobbler scrobbler, const std::vector& clusterIds, std::optional linkType) + { + auto query {session.query("SELECT a.id from artist a") + .join("track t ON t.id = t_a_l.track_id") + .join("track_artist_link t_a_l ON t_a_l.artist_id = a.id") + .join("listen l ON l.track_id = t.id") + .where("l.user_id = ?").bind(userId) + .where("l.scrobbler = ?").bind(scrobbler)}; + + if (linkType) + query.where("t_a_l.type = ?").bind(*linkType); + + if (!clusterIds.empty()) + { + std::ostringstream oss; + oss << "a.id IN (SELECT DISTINCT a.id FROM artist a" + " INNER JOIN track t ON t.id = t_a_l.track_id" + " INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id" + " INNER JOIN cluster c ON c.id = t_c.cluster_id" + " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"; + + WhereClause clusterClause; + for (auto id : clusterIds) + { + clusterClause.Or(WhereClause("c.id = ?")); + query.bind(id); + } + + oss << " " << clusterClause.get(); + oss << " GROUP BY t.id,a.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size() << ")"; + + query.where(oss.str()); + } + + return query; + } + + Wt::Dbo::Query + createReleasesQuery(Wt::Dbo::Session& session, UserId userId, Scrobbler scrobbler, const std::vector& clusterIds) + { + auto query {session.query("SELECT r.id from release r") + .join("track t ON t.release_id = r.id") + .join("listen l ON l.track_id = t.id") + .where("l.user_id = ?").bind(userId) + .where("l.scrobbler = ?").bind(scrobbler)}; + + if (!clusterIds.empty()) + { + std::ostringstream oss; + oss << "r.id IN (SELECT DISTINCT r.id FROM release r" + " INNER JOIN track t ON t.release_id = r.id" + " INNER JOIN cluster c ON c.id = t_c.cluster_id" + " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"; + + WhereClause clusterClause; + for (ClusterId id : clusterIds) + { + clusterClause.Or(WhereClause("c.id = ?")); + query.bind(id); + } + + oss << " " << clusterClause.get(); + oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size() << ")"; + + query.where(oss.str()); + } + + return query; + } + + Wt::Dbo::Query + createTracksQuery(Wt::Dbo::Session& session, UserId userId, Scrobbler scrobbler, const std::vector& clusterIds) + { + auto query {session.query("SELECT t.id from track t") + .join("listen l ON l.track_id = t.id") + .where("l.user_id = ?").bind(userId) + .where("l.scrobbler = ?").bind(scrobbler)}; + + if (!clusterIds.empty()) + { + std::ostringstream oss; + oss << "t.id IN (SELECT DISTINCT t.id FROM track t" + " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" + " INNER JOIN cluster c ON c.id = t_c.cluster_id"; + + WhereClause clusterClause; + for (auto id : clusterIds) + { + clusterClause.Or(WhereClause("c.id = ?")).bind(id.toString()); + query.bind(id); + } + + oss << " " << clusterClause.get(); + oss << " GROUP BY t.id HAVING COUNT(*) = " << clusterIds.size() << ")"; + + query.where(oss.str()); + } + + return query; + } +} + +namespace Database +{ + Listen::Listen(ObjectPtr user, ObjectPtr track, Scrobbler scrobbler, const Wt::WDateTime& dateTime) + : _dateTime {Wt::WDateTime::fromTime_t(dateTime.toTime_t())} + , _scrobbler {scrobbler} + , _user {getDboPtr(user)} + , _track {getDboPtr(track)} + {} + + std::size_t + Listen::getCount(Session& session) + { + session.checkSharedLocked(); + return session.getDboSession().query("SELECT COUNT(*) FROM listen"); + } + + Listen::pointer + Listen::find(Session& session, ListenId id) + { + session.checkSharedLocked(); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); + } + + RangeResults + Listen::find(Session& session, UserId userId, Scrobbler scrobbler, Range range) + { + session.checkSharedLocked(); + + auto query {session.getDboSession().find() + .where("user_id = ?").bind(userId) + .where("scrobbler = ?").bind(scrobbler) + .orderBy("date_time")}; + + return execQuery(query, range); + } + + Listen::pointer + Listen::find(Session& session, UserId userId, TrackId trackId, Scrobbler scrobbler, const Wt::WDateTime& dateTime) + { + session.checkSharedLocked(); + + return session.getDboSession().find() + .where("user_id = ?").bind(userId) + .where("track_id = ?").bind(trackId) + .where("scrobbler = ?").bind(scrobbler) + .where("date_time = ?").bind(Wt::WDateTime::fromTime_t(dateTime.toTime_t())) + .resultValue(); + } + + Listen::pointer + Listen::create(Session& session, ObjectPtr user, ObjectPtr track, Scrobbler scrobbler, const Wt::WDateTime& dateTime) + { + session.checkUniqueLocked(); + + Listen::pointer res {session.getDboSession().add(std::make_unique(user, track, scrobbler, dateTime))}; + session.getDboSession().flush(); + + return res; + } + + RangeResults + Listen::getTopArtists(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + std::optional linkType, + Range range) + { + auto query {createArtistsQuery(session.getDboSession(), userId, scrobbler, clusterIds, linkType)}; + + auto collection {query + .orderBy("COUNT(a.id) DESC") + .groupBy("a.id")}; + + return execQuery(query, range); + } + + RangeResults + Listen::getTopReleases(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range) + { + auto query {createReleasesQuery(session.getDboSession(), userId, scrobbler, clusterIds) + .orderBy("COUNT(r.id) DESC") + .groupBy("r.id")}; + + return execQuery(query, range); + } + + RangeResults + Listen::getTopTracks(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range) + { + auto query {createTracksQuery(session.getDboSession(), userId, scrobbler, clusterIds) + .orderBy("COUNT(t.id) DESC") + .groupBy("t.id")}; + + return execQuery(query, range); + } + + RangeResults + Listen::getRecentArtists(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + std::optional linkType, + Range range) + { + auto query {createArtistsQuery(session.getDboSession(), userId, scrobbler, clusterIds, linkType) + .groupBy("a.id").having("l.date_time = MAX(l.date_time)") + .orderBy("l.date_time DESC")}; + + return execQuery(query, range); + } + + RangeResults + Listen::getRecentReleases(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range) + { + auto query {createReleasesQuery(session.getDboSession(), userId, scrobbler, clusterIds) + .groupBy("r.id").having("l.date_time = MAX(l.date_time)") + .orderBy("l.date_time DESC")}; + + return execQuery(query, range); + } + + RangeResults + Listen::getRecentTracks(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range) + { + auto query {createTracksQuery(session.getDboSession(), userId, scrobbler, clusterIds) + .groupBy("t.id").having("l.date_time = MAX(l.date_time)") + .orderBy("l.date_time DESC")}; + + return execQuery(query, range); + } + + +} // namespace Database + diff --git a/src/libs/services/database/impl/Release.cpp b/src/libs/services/database/impl/Release.cpp index a25d23db..c268ef7c 100644 --- a/src/libs/services/database/impl/Release.cpp +++ b/src/libs/services/database/impl/Release.cpp @@ -28,28 +28,45 @@ #include "services/database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" -#include "Traits.hpp" +#include "IdTypeTraits.hpp" #include "Utils.hpp" namespace Database { -template -static -Wt::Dbo::Query -createQuery(Session& session, - const std::string& queryStr, - const std::vector& clusterIds, - const std::vector& keywords) +Wt::Dbo::Query +createQuery(Session& session, const Release::FindParameters& params) { + auto query {session.getDboSession().query("SELECT DISTINCT r.id from release r")}; - auto query {session.getDboSession().query(queryStr)}; - query.join("track t ON t.release_id = r.id"); + if (params.sortMethod == ReleaseSortMethod::LastWritten + || params.writtenAfter.isValid() + || params.dateRange) + { + query.join("track t ON t.release_id = r.id"); + } - for (std::string_view keyword : keywords) + if (params.writtenAfter.isValid()) + query.where("t.file_last_write > ?").bind(params.writtenAfter); + + if (params.dateRange) + { + query.where("t.date >= ?").bind(params.dateRange->begin); + query.where("t.date <= ?").bind(params.dateRange->end); + } + + for (std::string_view keyword : params.keywords) query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + escapeLikeKeyword(keyword) + "%"); - if (!clusterIds.empty()) + if (params.starringUser.isValid()) + { + assert(params.scrobbler); + query.join("starred_release s_r ON s_r.release_id = r.id") + .where("s_r.user_id = ?").bind(params.starringUser) + .where("s_r.scrobbler = ?").bind(*params.scrobbler); + } + + if (!params.clusters.empty()) { std::ostringstream oss; oss << "r.id IN (SELECT DISTINCT r.id FROM release r" @@ -58,18 +75,40 @@ createQuery(Session& session, " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"; WhereClause clusterClause; - for (const ClusterId clusterId : clusterIds) + for (const ClusterId clusterId : params.clusters) { clusterClause.Or(WhereClause("c.id = ?")); query.bind(clusterId); } oss << " " << clusterClause.get(); - oss << " GROUP BY t.id HAVING COUNT(*) = " << clusterIds.size() << ")"; + oss << " GROUP BY t.id HAVING COUNT(*) = " << params.clusters.size() << ")"; query.where(oss.str()); } + switch (params.sortMethod) + { + case ReleaseSortMethod::None: + break; + case ReleaseSortMethod::Name: + query.orderBy("r.name COLLATE NOCASE"); + break; + case ReleaseSortMethod::Random: + query.orderBy("RANDOM()"); + break; + case ReleaseSortMethod::LastWritten: + query.orderBy("t.file_last_write DESC"); + break; + case ReleaseSortMethod::Date: + query.orderBy("t.date, r.name COLLATE NOCASE"); + break; + case ReleaseSortMethod::StarredDateDesc: + assert(params.starringUser.isValid()); + query.orderBy("s_r.date_time DESC"); + break; + } + return query; } @@ -80,7 +119,7 @@ _MBID {MBID ? MBID->getAsString() : ""} } std::vector -Release::getByName(Session& session, const std::string& name) +Release::find(Session& session, const std::string& name) { session.checkUniqueLocked(); @@ -93,7 +132,7 @@ Release::getByName(Session& session, const std::string& name) } Release::pointer -Release::getByMBID(Session& session, const UUID& mbid) +Release::find(Session& session, const UUID& mbid) { session.checkSharedLocked(); @@ -104,7 +143,7 @@ Release::getByMBID(Session& session, const UUID& mbid) } Release::pointer -Release::getById(Session& session, ReleaseId id) +Release::find(Session& session, ReleaseId id) { session.checkSharedLocked(); @@ -137,230 +176,44 @@ Release::getCount(Session& session) { session.checkSharedLocked(); - return session.getDboSession().find().resultList().size(); + return session.getDboSession().query("SELECT COUNT(*) FROM release"); } -std::vector -Release::getAll(Session& session, std::optional range) +RangeResults +Release::findOrderedByArtist(Session& session, Range range) { session.checkSharedLocked(); - auto res {session.getDboSession().find() - .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) : -1) - .orderBy("name COLLATE NOCASE") - .resultList()}; - - return std::vector(res.begin(), res.end()); -} - -std::vector -Release::getAllIds(Session& session) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().query("SELECT id FROM release"); - return std::vector(res.begin(), res.end()); -} - -std::vector -Release::getAllOrderedByArtist(Session& session, std::optional offset, std::optional size) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().query>( - "SELECT DISTINCT r FROM release r" + // TODO merge with execQuery + auto query {session.getDboSession().query( + "SELECT DISTINCT r.id FROM release r" " INNER JOIN track t ON r.id = t.release_id" " INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id" " INNER JOIN artist a ON t_a_l.artist_id = a.id") - .offset(offset ? static_cast(*offset) : -1) - .limit(size ? static_cast(*size) : -1) - .orderBy("a.name COLLATE NOCASE, r.name COLLATE NOCASE") - .resultList()}; + .orderBy("a.name COLLATE NOCASE, r.name COLLATE NOCASE")}; - return std::vector(res.begin(), res.end()); + return execQuery(query, range); } -std::vector -Release::getAllRandom(Session& session, const std::vector& clusterIds, std::optional size) +RangeResults +Release::findOrphans(Session& session, Range range) { session.checkSharedLocked(); - auto query {createQuery>(session, "SELECT DISTINCT r from release r", clusterIds, {})}; - auto res {query - .orderBy("RANDOM()") - .limit(size ? static_cast(*size) : -1) - .resultList()}; - - return std::vector(res.begin(), res.end()); + 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 execQuery(query, range); } -std::vector -Release::getAllIdsRandom(Session& session, const std::vector& clusterIds, std::optional size) +RangeResults +Release::find(Session& session, const FindParameters& params) { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT DISTINCT r.id from release r", clusterIds, {})}; + auto query {createQuery(session, params)}; - Wt::Dbo::collection res = query - .orderBy("RANDOM()") - .limit(size ? static_cast(*size) : -1); - - return std::vector(res.begin(), res.end()); + return execQuery(query, params.range); } - -std::vector -Release::getAllOrphans(Session& session) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().query>("select r from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL").resultList()}; - return std::vector(res.begin(), res.end()); -} - -std::vector -Release::getLastWritten(Session& session, - std::optional after, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT r from release r", clusterIds, {})}; - if (after) - query.where("t.file_last_write > ?").bind(after); - - auto collection {query - .orderBy("t.file_last_write DESC") - .groupBy("r.id") - .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) + 1: -1) - .resultList()}; - - std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; -} - -std::vector -Release::getByYear(Session& session, int yearFrom, int yearTo, std::optional range) -{ - auto res {session.getDboSession().query> - ("SELECT DISTINCT r from release r INNER JOIN track t ON r.id = t.release_id") - .where("t.date >= ?").bind(Wt::WDate {yearFrom, 1, 1}) - .where("t.date <= ?").bind(Wt::WDate {yearTo, 12, 31}) - .orderBy("t.date, r.name COLLATE NOCASE") - .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) : -1) - .resultList()}; - - return std::vector(res.begin(), res.end()); -} - -std::vector -Release::getStarred(Session& session, - User::pointer user, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT r from release r", clusterIds, {})}; - { - std::ostringstream oss; - oss << "r.id IN (SELECT DISTINCT r.id FROM release r" - " INNER JOIN user_release_starred urs ON urs.release_id = r.id" - " INNER JOIN user u ON u.id = urs.user_id WHERE u.id = ?)"; - - query.bind(user->getId()); - query.where(oss.str()); - } - - auto collection {query - .groupBy("r.id") - .orderBy("r.name COLLATE NOCASE") - .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) + 1: -1) - .resultList()}; - - std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; - - -} - -std::vector -Release::getByClusters(Session& session, const std::vector& clusters) -{ - assert(!clusters.empty()); - - session.checkSharedLocked(); - - bool moreResults; - return getByFilter(session, clusters, {}, std::nullopt, moreResults); -} - -std::vector -Release::getByFilter(Session& session, - const std::vector& clusterIds, - const std::vector& keywords, - std::optional range, - bool& moreResults) -{ - session.checkSharedLocked(); - - auto collection {createQuery>(session, "SELECT r from release r", clusterIds, keywords) - .groupBy("r.id") - .orderBy("r.name COLLATE NOCASE") - .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1) - .resultList()}; - - std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; -} - -std::vector -Release::getAllIdsWithClusters(Session& session, std::optional limit) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().query - ("SELECT 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") - .limit(limit ? static_cast(*limit) : -1); - - return std::vector(res.begin(), res.end()); -} - - std::optional Release::getTotalTrack(void) const { diff --git a/src/libs/services/database/impl/ScanSettings.cpp b/src/libs/services/database/impl/ScanSettings.cpp index 0b509f32..70f08b72 100644 --- a/src/libs/services/database/impl/ScanSettings.cpp +++ b/src/libs/services/database/impl/ScanSettings.cpp @@ -111,7 +111,7 @@ ScanSettings::setClusterTypes(Session& session, const std::set& clu // Create any missing cluster type for (const std::string& clusterTypeName : clusterTypeNames) { - ClusterType::pointer clusterType {ClusterType::getByName(session, clusterTypeName)}; + ClusterType::pointer clusterType {ClusterType::find(session, clusterTypeName)}; if (!clusterType) { LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName; diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 7b219a08..76e0396c 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -28,10 +28,15 @@ #include "utils/Logger.hpp" #include "services/database/Artist.hpp" +#include "services/database/AuthToken.hpp" #include "services/database/Cluster.hpp" #include "services/database/Db.hpp" +#include "services/database/Listen.hpp" #include "services/database/Release.hpp" #include "services/database/ScanSettings.hpp" +#include "services/database/StarredArtist.hpp" +#include "services/database/StarredRelease.hpp" +#include "services/database/StarredTrack.hpp" #include "services/database/Track.hpp" #include "services/database/TrackBookmark.hpp" #include "services/database/TrackArtistLink.hpp" @@ -43,7 +48,7 @@ namespace Database { using Version = std::size_t; - static constexpr Version LMS_DATABASE_VERSION {31}; + static constexpr Version LMS_DATABASE_VERSION {32}; class VersionInfo { @@ -360,6 +365,61 @@ CREATE TABLE "track_backup" ( // Just increment the scan version of the settings to make the next scheduled scan rescan everything ScanSettings::get(*this).modify()->incScanVersion(); } + else if (version == 31) + { + // new star system, using dedicated ObjectSets per scrobbler + _session.execute("DROP TABLE user_artist_starred"); + _session.execute("DROP TABLE user_release_starred"); + _session.execute("DROP TABLE user_track_starred"); + + _session.execute(R"( +CREATE TABLE IF NOT EXISTS "starred_artist" ( + "id" integer primary key autoincrement, + "version" integer not null, + "scrobbler" integer not null, + "date_time" text, + "artist_id" bigint, + "user_id" bigint, + constraint "fk_starred_artist_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred, + constraint "fk_starred_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + + _session.execute(R"( +CREATE TABLE IF NOT EXISTS "starred_release" ( + "id" integer primary key autoincrement, + "version" integer not null, + "scrobbler" integer not null, + "date_time" text, + "release_id" bigint, + "user_id" bigint, + constraint "fk_starred_release_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred, + constraint "fk_starred_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + + _session.execute(R"( +CREATE TABLE IF NOT EXISTS "starred_track" ( + "id" integer primary key autoincrement, + "version" integer not null, + "scrobbler" integer not null, + "date_time" text, + "track_id" bigint, + "user_id" bigint, + constraint "fk_starred_track_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, + constraint "fk_starred_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + + _session.execute(R"( +CREATE TABLE IF NOT EXISTS "listen" ( + "id" integer primary key autoincrement, + "version" integer not null, + "date_time" text, + "scrobbler" integer not null, + "track_id" bigint, + "user_id" bigint, + constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, + constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + } else { LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; @@ -380,8 +440,12 @@ Session::Session(Db& db) _session.mapClass("auth_token"); _session.mapClass("cluster"); _session.mapClass("cluster_type"); + _session.mapClass("listen"); _session.mapClass("release"); _session.mapClass("scan_settings"); + _session.mapClass("starred_artist"); + _session.mapClass("starred_release"); + _session.mapClass("starred_track"); _session.mapClass("track"); _session.mapClass("track_bookmark"); _session.mapClass("track_artist_link"); @@ -484,6 +548,11 @@ Session::prepareTables() _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_type_idx ON track_artist_link(type)"); _session.execute("CREATE INDEX IF NOT EXISTS track_bookmark_user_idx ON track_bookmark(user_id)"); _session.execute("CREATE INDEX IF NOT EXISTS track_bookmark_user_track_idx ON track_bookmark(user_id,track_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS listen_scrobbler_idx ON listen(scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS listen_user_scrobbler_idx ON listen(user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_artist_user_scrobbler_idx ON starred_artist(user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_release_user_scrobbler_idx ON starred_release(user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_track_user_scrobbler_idx ON starred_track(user_id,scrobbler)"); } // Initial settings tables diff --git a/src/libs/services/database/impl/StarredArtist.cpp b/src/libs/services/database/impl/StarredArtist.cpp new file mode 100644 index 00000000..ed617665 --- /dev/null +++ b/src/libs/services/database/impl/StarredArtist.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 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/StarredArtist.hpp" + +#include + +#include "services/database/Artist.hpp" +#include "services/database/User.hpp" +#include "IdTypeTraits.hpp" +#include "Utils.hpp" + +namespace Database +{ + StarredArtist::StarredArtist(ObjectPtr artist, ObjectPtr user, Scrobbler scrobbler) + : _scrobbler {scrobbler} + , _artist {getDboPtr(artist)} + , _user {getDboPtr(user)} + { + } + + std::size_t + StarredArtist::getCount(Session& session) + { + session.checkSharedLocked(); + return session.getDboSession().query("SELECT COUNT(*) FROM starred_artist"); + } + + StarredArtist::pointer + StarredArtist::find(Session& session, StarredArtistId id) + { + session.checkSharedLocked(); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); + } + + StarredArtist::pointer + StarredArtist::find(Session& session, ArtistId artistId, UserId userId, Scrobbler scrobbler) + { + session.checkSharedLocked(); + return session.getDboSession().find() + .where("artist_id = ?").bind(artistId) + .where("user_id = ?").bind(userId) + .where("scrobbler = ?").bind(scrobbler) + .resultValue(); + } + + StarredArtist::pointer + StarredArtist::create(Session& session, ObjectPtr artist, ObjectPtr user, Scrobbler scrobbler) + { + session.checkUniqueLocked(); + + StarredArtist::pointer res {session.getDboSession().add(std::make_unique(artist, user, scrobbler))}; + session.getDboSession().flush(); + + return res; + } + + void + StarredArtist::setDateTime(const Wt::WDateTime& dateTime) + { + _dateTime = normalizeDateTime(dateTime); + } +} diff --git a/src/libs/services/database/impl/StarredRelease.cpp b/src/libs/services/database/impl/StarredRelease.cpp new file mode 100644 index 00000000..c72331d5 --- /dev/null +++ b/src/libs/services/database/impl/StarredRelease.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 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/StarredRelease.hpp" + +#include + +#include "services/database/Release.hpp" +#include "services/database/User.hpp" +#include "IdTypeTraits.hpp" +#include "Utils.hpp" + +namespace Database +{ + StarredRelease::StarredRelease(ObjectPtr release, ObjectPtr user, Scrobbler scrobbler) + : _scrobbler {scrobbler} + , _release {getDboPtr(release)} + , _user {getDboPtr(user)} + { + } + + std::size_t + StarredRelease::getCount(Session& session) + { + session.checkSharedLocked(); + return session.getDboSession().query("SELECT COUNT(*) FROM starred_release"); + } + + StarredRelease::pointer + StarredRelease::find(Session& session, StarredReleaseId id) + { + session.checkSharedLocked(); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); + } + + StarredRelease::pointer + StarredRelease::find(Session& session, ReleaseId releaseId, UserId userId, Scrobbler scrobbler) + { + session.checkSharedLocked(); + return session.getDboSession().find() + .where("release_id = ?").bind(releaseId) + .where("user_id = ?").bind(userId) + .where("scrobbler = ?").bind(scrobbler) + .resultValue(); + } + + StarredRelease::pointer + StarredRelease::create(Session& session, ObjectPtr release, ObjectPtr user, Scrobbler scrobbler) + { + session.checkUniqueLocked(); + + StarredRelease::pointer res {session.getDboSession().add(std::make_unique(release, user, scrobbler))}; + session.getDboSession().flush(); + + return res; + } + + void + StarredRelease::setDateTime(const Wt::WDateTime& dateTime) + { + _dateTime = normalizeDateTime(dateTime); + } +} diff --git a/src/libs/services/database/impl/StarredTrack.cpp b/src/libs/services/database/impl/StarredTrack.cpp new file mode 100644 index 00000000..6543654f --- /dev/null +++ b/src/libs/services/database/impl/StarredTrack.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 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/StarredTrack.hpp" + +#include + +#include "services/database/Track.hpp" +#include "services/database/User.hpp" +#include "IdTypeTraits.hpp" +#include "Utils.hpp" + +namespace Database +{ + StarredTrack::StarredTrack(ObjectPtr track, ObjectPtr user, Scrobbler scrobbler) + : _scrobbler {scrobbler} + , _track {getDboPtr(track)} + , _user {getDboPtr(user)} + { + } + + std::size_t + StarredTrack::getCount(Session& session) + { + session.checkSharedLocked(); + return session.getDboSession().query("SELECT COUNT(*) FROM starred_track"); + } + + StarredTrack::pointer + StarredTrack::find(Session& session, StarredTrackId id) + { + session.checkSharedLocked(); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); + } + + StarredTrack::pointer + StarredTrack::find(Session& session, TrackId trackId, UserId userId, Scrobbler scrobbler) + { + session.checkSharedLocked(); + return session.getDboSession().find() + .where("track_id = ?").bind(trackId) + .where("user_id = ?").bind(userId) + .where("scrobbler = ?").bind(scrobbler) + .resultValue(); + } + + StarredTrack::pointer + StarredTrack::create(Session& session, ObjectPtr track, ObjectPtr user, Scrobbler scrobbler) + { + session.checkUniqueLocked(); + + StarredTrack::pointer res {session.getDboSession().add(std::make_unique(track, user, scrobbler))}; + session.getDboSession().flush(); + + return res; + } + + void + StarredTrack::setDateTime(const Wt::WDateTime& dateTime) + { + _dateTime = normalizeDateTime(dateTime); + } +} diff --git a/src/libs/services/database/impl/Track.cpp b/src/libs/services/database/impl/Track.cpp index d1968146..a93f2339 100644 --- a/src/libs/services/database/impl/Track.cpp +++ b/src/libs/services/database/impl/Track.cpp @@ -30,29 +30,36 @@ #include "services/database/User.hpp" #include "utils/Logger.hpp" +#include "IdTypeTraits.hpp" #include "SqlQuery.hpp" #include "StringViewTraits.hpp" -#include "Traits.hpp" #include "Utils.hpp" namespace Database { -template static -Wt::Dbo::Query -createQuery(Session& session, - const std::string& queryStr, - const std::vector& clusterIds, - const std::vector& keywords) +Wt::Dbo::Query +createQuery(Session& session, const Track::FindParameters& params) { session.checkSharedLocked(); - auto query {session.getDboSession().query(queryStr)}; + auto query {session.getDboSession().query("SELECT t.id from track t")}; - for (std::string_view keyword : keywords) + for (std::string_view keyword : params.keywords) query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + escapeLikeKeyword(keyword) + "%"); - if (!clusterIds.empty()) + if (params.writtenAfter.isValid()) + query.where("t.file_last_write > ?").bind(params.writtenAfter); + + if (params.starringUser.isValid()) + { + assert(params.scrobbler); + query.join("starred_track s_t ON s_t.track_id = t.id") + .where("s_t.user_id = ?").bind(params.starringUser) + .where("s_t.scrobbler = ?").bind(*params.scrobbler); + } + + if (!params.clusters.empty()) { std::ostringstream oss; oss << "t.id IN (SELECT DISTINCT t.id FROM track t" @@ -60,18 +67,34 @@ createQuery(Session& session, " INNER JOIN cluster c ON c.id = t_c.cluster_id"; WhereClause clusterClause; - for (const ClusterId clusterId : clusterIds) + for (const ClusterId clusterId : params.clusters) { clusterClause.Or(WhereClause("c.id = ?")); query.bind(clusterId); } oss << " " << clusterClause.get(); - oss << " GROUP BY t.id HAVING COUNT(*) = " << clusterIds.size() << ")"; + oss << " GROUP BY t.id HAVING COUNT(*) = " << params.clusters.size() << ")"; query.where(oss.str()); } + switch (params.sortMethod) + { + case TrackSortMethod::None: + break; + case TrackSortMethod::LastWritten: + query.orderBy("t.file_last_write DESC"); + break; + case TrackSortMethod::Random: + query.orderBy("RANDOM()"); + break; + case TrackSortMethod::StarredDateDesc: + assert(params.starringUser.isValid()); + query.orderBy("s_t.date_time DESC"); + break; + } + return query; } @@ -80,6 +103,17 @@ Track::Track(const std::filesystem::path& p) { } +Track::pointer +Track::create(Session& session, const std::filesystem::path& p) +{ + session.checkUniqueLocked(); + + Track::pointer res {session.getDboSession().add(std::make_unique(p))}; + session.getDboSession().flush(); + + return res; +} + std::size_t Track::getCount(Session& session) { @@ -88,58 +122,8 @@ Track::getCount(Session& session) return session.getDboSession().query("SELECT COUNT(*) FROM track"); } -std::vector -Track::getAll(Session& session, std::optional limit) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().find() - .limit(limit ? static_cast(*limit) : -1) - .resultList()}; - - return std::vector(std::cbegin(res), std::cend(res)); -} - -std::vector -Track::getAllRandom(Session& session, const std::vector& clusterIds, std::optional limit) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT t from track t", clusterIds, {})}; - auto collection {query - .orderBy("RANDOM()") - .limit(limit ? static_cast(*limit) + 1: -1) - .resultList()}; - - return std::vector(collection.begin(), collection.end()); -} - -std::vector -Track::getAllIdsRandom(Session& session, const std::vector& clusterIds, std::optional limit) -{ - session.checkSharedLocked(); - - auto query {createQuery(session, "SELECT t.id from track t", clusterIds, {})}; - - Wt::Dbo::collection collection = query - .orderBy("RANDOM()") - .limit(limit ? static_cast(*limit) + 1: -1); - - return std::vector(collection.begin(), collection.end()); -} - - -std::vector -Track::getAllIds(Session& session) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().query("SELECT id FROM track"); - return std::vector(res.begin(), res.end()); -} - Track::pointer -Track::getByPath(Session& session, const std::filesystem::path& p) +Track::findByPath(Session& session, const std::filesystem::path& p) { session.checkSharedLocked(); @@ -147,7 +131,7 @@ Track::getByPath(Session& session, const std::filesystem::path& p) } Track::pointer -Track::getById(Session& session, TrackId id) +Track::find(Session& session, TrackId id) { session.checkSharedLocked(); @@ -165,7 +149,7 @@ Track::exists(Session& session, TrackId id) } std::vector -Track::getByRecordingMBID(Session& session, const UUID& mbid) +Track::findByRecordingMBID(Session& session, const UUID& mbid) { session.checkSharedLocked(); @@ -176,151 +160,52 @@ Track::getByRecordingMBID(Session& session, const UUID& mbid) return std::vector(res.begin(), res.end()); } -Track::pointer -Track::create(Session& session, const std::filesystem::path& p) -{ - session.checkUniqueLocked(); - - Track::pointer res {session.getDboSession().add(std::make_unique(p))}; - session.getDboSession().flush(); - - return res; -} - -std::vector> -Track::getAllPaths(Session& session, std::optional offset, std::optional size) +RangeResults +Track::findPaths(Session& session, Range range) { using QueryResultType = std::tuple; session.checkSharedLocked(); - Wt::Dbo::collection queryRes = session.getDboSession().query("SELECT id,file_path FROM track") - .limit(size ? static_cast(*size) + 1 : -1) - .offset(offset ? static_cast(*offset) : -1); + // TODO Dbo traits on filesystem + auto query {session.getDboSession().query("SELECT id, file_path FROM track")}; - std::vector> result; - result.reserve(queryRes.size()); + RangeResults queryResults {execQuery(query, range)}; - std::transform(std::begin(queryRes), std::end(queryRes), std::back_inserter(result), + RangeResults res; + res.range = queryResults.range; + res.moreResults = queryResults.moreResults; + res.results.reserve(queryResults.results.size()); + + std::transform(std::cbegin(queryResults.results), std::cend(queryResults.results), std::back_inserter(res.results), [](const QueryResultType& queryResult) { - return std::make_pair(std::get<0>(queryResult), std::get<1>(queryResult)); + return PathResult {std::get<0>(queryResult), std::get<1>(queryResult)}; }); - return result; -} - -std::vector -Track::getMBIDDuplicates(Session& session) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().query>( "SELECT track 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") - .resultList()}; - return std::vector(res.begin(), res.end()); -} - -std::vector -Track::getLastWritten(Session& session, std::optional after, const std::vector& clusterIds, std::optional range, bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT t from track t", clusterIds, {})}; - if (after) - query.where("t.file_last_write > ?").bind(after); - - auto collection {query - .orderBy("t.file_last_write DESC") - .groupBy("t.id") - .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) + 1: -1) - .resultList()}; - - std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - return res; } -std::vector -Track::getAllWithRecordingMBIDAndMissingFeatures(Session& session) +RangeResults +Track::findMBIDDuplicates(Session& session, Range range) { session.checkSharedLocked(); - auto res {session.getDboSession().query> - ("SELECT t FROM track t") + 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")}; + + return execQuery(query, range); +} + +RangeResults +Track::findWithRecordingMBIDAndMissingFeatures(Session& session, Range range) +{ + session.checkSharedLocked(); + + auto query {session.getDboSession().query("SELECT t.id FROM track t") .where("LENGTH(t.recording_mbid) > 0") - .where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)") - .resultList()}; - return std::vector(res.begin(), res.end()); -} + .where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)")}; -std::vector -Track::getAllIdsWithFeatures(Session& session, std::optional limit) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().query - ("SELECT t.id FROM track t") - .where("EXISTS (SELECT * from track_features t_f WHERE t_f.track_id = t.id)") - .limit(limit ? static_cast(*limit) : -1); - - return std::vector(res.begin(), res.end()); -} - -std::vector -Track::getAllIdsWithClusters(Session& session, std::optional limit) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().query - ("SELECT DISTINCT t.id FROM track t" - " INNER JOIN track_cluster t_c ON t_c.track_id = t.id") - .limit(limit ? static_cast(*limit) : -1); - - return std::vector(res.begin(), res.end()); -} - -std::vector -Track::getStarred(Session& session, - ObjectPtr user, - const std::vector& clusterIds, - std::optional range, bool& moreResults) -{ - session.checkSharedLocked(); - - auto query {createQuery>(session, "SELECT t from track t", clusterIds, {})}; - { - std::ostringstream oss; - oss << "t.id IN (SELECT DISTINCT t.id FROM track t" - " INNER JOIN user_track_starred uts ON uts.track_id = t.id" - " INNER JOIN user u ON u.id = uts.user_id WHERE u.id = ?)"; - - query.bind(user->getId().toString()); - query.where(oss.str()); - } - - auto collection {query - .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) + 1: -1) - .resultList()}; - - std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; + return execQuery(query, range); } std::vector @@ -342,56 +227,31 @@ Track::getClusterIds() const return std::vector(res.begin(), res.end()); } -bool -Track::hasTrackFeatures() const -{ - return (_trackFeatures.lock() != Wt::Dbo::ptr {}); -} - -std::vector -Track::getByFilter(Session& session, - const std::vector& clusterIds, - const std::vector& keywords, - std::optional range, - bool& moreResults) +RangeResults +Track::find(Session& session, const FindParameters& parameters) { session.checkSharedLocked(); - auto collection {createQuery>(session, "SELECT t from track t", clusterIds, keywords) - .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1) - .resultList()}; + auto query {createQuery(session, parameters)}; - std::vector res(collection.begin(), collection.end()); - if (range && (res.size() == static_cast(range->limit) + 1)) - { - moreResults = true; - res.pop_back(); - } - else - moreResults = false; - - return res; + return execQuery(query, parameters.range); } -std::vector -Track::getByNameAndReleaseName(Session& session, std::string_view trackName, std::string_view releaseName) +RangeResults +Track::findByNameAndReleaseName(Session& session, std::string_view trackName, std::string_view releaseName) { session.checkSharedLocked(); - auto res {session.getDboSession().query>("SELECT t from track t") + auto query {session.getDboSession().query("SELECT t.id from track t") .join("release r ON t.release_id = r.id") .where("t.name = ?").bind(trackName) - .where("r.name = ?").bind(releaseName) - .resultList()}; - return std::vector(res.begin(), res.end()); + .where("r.name = ?").bind(releaseName)}; + + return execQuery(query, Range {}); } -std::vector -Track::getSimilarTracks(Session& session, - const std::vector& tracks, - std::optional offset, - std::optional size) +RangeResults +Track::findSimilarTracks(Session& session, const std::vector& tracks, Range range) { assert(!tracks.empty()); session.checkSharedLocked(); @@ -404,15 +264,13 @@ Track::getSimilarTracks(Session& session, oss << "?"; } - auto query {session.getDboSession().query>( - "SELECT t FROM track t" + auto query {session.getDboSession().query( + "SELECT t.id FROM track t" " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" " AND t_c.cluster_id IN (SELECT c.id FROM cluster c INNER JOIN track_cluster t_c ON t_c.cluster_id = c.id WHERE t_c.track_id IN (" + oss.str() + "))" " AND t.id NOT IN (" + oss.str() + ")") .groupBy("t.id") - .orderBy("COUNT(*) DESC, RANDOM()") - .limit(size ? static_cast(*size) : -1) - .offset(offset ? static_cast(*offset) : -1)}; + .orderBy("COUNT(*) DESC, RANDOM()")}; for (TrackId trackId : tracks) query.bind(trackId); @@ -420,22 +278,7 @@ Track::getSimilarTracks(Session& session, for (TrackId trackId : tracks) query.bind(trackId); - auto res {query.resultList()}; - return std::vector(res.begin(), res.end()); -} - -std::vector -Track::getByClusters(Session& session, const std::vector& clusters) -{ - assert(!clusters.empty()); - session.checkSharedLocked(); - - bool moreResults; - return getByFilter(session, - clusters, - {}, // keywords - std::nullopt, // range - moreResults); + return execQuery(query, range); } void @@ -458,12 +301,6 @@ Track::setClusters(const std::vector>& clusters) _clusters.insert(getDboPtr(cluster)); } -void -Track::setFeatures(const ObjectPtr& features) -{ - _trackFeatures = getDboPtr(features); -} - std::optional Track::getTrackNumber() const { @@ -593,12 +430,6 @@ Track::getArtistLinks() const return std::vector(_trackArtistLinks.begin(), _trackArtistLinks.end()); } -ObjectPtr -Track::getTrackFeatures() const -{ - return _trackFeatures.lock(); -} - std::vector> Track::getClusterGroups(const std::vector& clusterTypes, std::size_t size) const { diff --git a/src/libs/services/database/impl/TrackArtistLink.cpp b/src/libs/services/database/impl/TrackArtistLink.cpp index 03bbbbee..d54339e8 100644 --- a/src/libs/services/database/impl/TrackArtistLink.cpp +++ b/src/libs/services/database/impl/TrackArtistLink.cpp @@ -23,7 +23,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "Traits.hpp" +#include "IdTypeTraits.hpp" namespace Database { @@ -46,7 +46,7 @@ TrackArtistLink::create(Session& session, ObjectPtr track, ObjectPtr -TrackArtistLink::getUsedTypes(Session& session) +TrackArtistLink::findUsedTypes(Session& session) { session.checkSharedLocked(); diff --git a/src/libs/services/database/impl/TrackBookmark.cpp b/src/libs/services/database/impl/TrackBookmark.cpp index 5a963c3c..28e169ca 100644 --- a/src/libs/services/database/impl/TrackBookmark.cpp +++ b/src/libs/services/database/impl/TrackBookmark.cpp @@ -22,7 +22,8 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" -#include "Traits.hpp" +#include "IdTypeTraits.hpp" +#include "Utils.hpp" namespace Database { @@ -32,6 +33,15 @@ _track {getDboPtr(track)} { } +std::size_t +TrackBookmark::getCount(Session& session) +{ + session.checkSharedLocked(); + + return session.getDboSession().query("SELECT COUNT(*) FROM track_bookmark"); +} + + TrackBookmark::pointer TrackBookmark::create(Session& session, ObjectPtr user, ObjectPtr track) { @@ -43,40 +53,30 @@ TrackBookmark::create(Session& session, ObjectPtr user, ObjectPtr t return res; } -std::vector -TrackBookmark::getAll(Session& session) +RangeResults +TrackBookmark::find(Session& session, UserId userId, Range range) { session.checkSharedLocked(); - auto res {session.getDboSession().find().resultList()}; - return std::vector(std::cbegin(res), std::cend(res)); -} + auto query {session.getDboSession().query("SELECT id from track_bookmark") + .where("user_id = ?").bind(userId)}; -std::vector -TrackBookmark::getByUser(Session& session, User::pointer user) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().find() - .where("user_id = ?").bind(user->getId()) - .resultList()}; - - return std::vector(std::cbegin(res), std::cend(res)); + return execQuery(query, range); } TrackBookmark::pointer -TrackBookmark::getByUser(Session& session, ObjectPtr user, ObjectPtr track) +TrackBookmark::find(Session& session, UserId userId, TrackId trackId) { session.checkSharedLocked(); return session.getDboSession().find() - .where("user_id = ?").bind(user->getId()) - .where("track_id = ?").bind(track->getId()) + .where("user_id = ?").bind(userId) + .where("track_id = ?").bind(trackId) .resultValue(); } TrackBookmark::pointer -TrackBookmark::getById(Session& session, TrackBookmarkId id) +TrackBookmark::find(Session& session, TrackBookmarkId id) { session.checkSharedLocked(); @@ -85,6 +85,5 @@ TrackBookmark::getById(Session& session, TrackBookmarkId id) .resultValue(); } - } // namespace Database diff --git a/src/libs/services/database/impl/TrackFeatures.cpp b/src/libs/services/database/impl/TrackFeatures.cpp index 65cc9b7a..628c10c7 100644 --- a/src/libs/services/database/impl/TrackFeatures.cpp +++ b/src/libs/services/database/impl/TrackFeatures.cpp @@ -25,6 +25,8 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "utils/Logger.hpp" +#include "IdTypeTraits.hpp" +#include "Utils.hpp" namespace Database { @@ -34,11 +36,53 @@ _track {getDboPtr(track)} { } +std::size_t +TrackFeatures::getCount(Session& session) +{ + session.checkSharedLocked(); + + return session.getDboSession().query("SELECT COUNT(*) FROM track_features"); +} + +TrackFeatures::pointer +TrackFeatures::find(Session& session, TrackFeaturesId id) +{ + session.checkSharedLocked(); + + return session.getDboSession().find() + .where("id = ?").bind(id) + .resultValue(); +} + +TrackFeatures::pointer +TrackFeatures::find(Session& session, TrackId trackId) +{ + session.checkSharedLocked(); + + return session.getDboSession().find() + .where("track_id = ?").bind(trackId) + .resultValue(); +} + +RangeResults +TrackFeatures::find(Session& session, Range range) +{ + session.checkSharedLocked(); + + auto query {session.getDboSession().query("SELECT id from track_features")}; + + return execQuery(query, range); +} + TrackFeatures::pointer TrackFeatures::create(Session& session, ObjectPtr track, const std::string& jsonEncodedFeatures) { session.checkUniqueLocked(); - return session.getDboSession().add(std::make_unique(track, jsonEncodedFeatures)); + + TrackFeatures::pointer res {session.getDboSession().add(std::make_unique(track, jsonEncodedFeatures))}; + session.getDboSession().flush(); + + return res; } FeatureValues @@ -51,6 +95,8 @@ TrackFeatures::getFeatureValues(const FeatureName& featureNode) const FeatureValuesMap TrackFeatures::getFeatureValuesMap(const std::unordered_set& featureNames) const { + FeatureValuesMap res; + try { std::istringstream iss {_data}; @@ -58,7 +104,6 @@ TrackFeatures::getFeatureValuesMap(const std::unordered_set& featur boost::property_tree::read_json(iss, root); - FeatureValuesMap res; for (const FeatureName& featureName : featureNames) { FeatureValues& featureValues {res[featureName]}; @@ -75,14 +120,14 @@ TrackFeatures::getFeatureValuesMap(const std::unordered_set& featur if (!hasChildren) featureValues.push_back(node.get_value()); } - - return res; } catch (boost::property_tree::ptree_error& error) { LMS_LOG(DB, ERROR) << "Track " << _track.id() << ": ptree exception: " << error.what(); - return {}; + res.clear(); } + + return res; } } // namespace Database diff --git a/src/libs/services/database/impl/TrackList.cpp b/src/libs/services/database/impl/TrackList.cpp index 8e69434b..c91826fd 100644 --- a/src/libs/services/database/impl/TrackList.cpp +++ b/src/libs/services/database/impl/TrackList.cpp @@ -30,7 +30,8 @@ #include "services/database/Track.hpp" #include "SqlQuery.hpp" #include "StringViewTraits.hpp" -#include "Traits.hpp" +#include "IdTypeTraits.hpp" +#include "Utils.hpp" namespace Database { @@ -55,56 +56,54 @@ TrackList::create(Session& session, std::string_view name, Type type, bool isPub return res; } -TrackList::pointer -TrackList::get(Session& session, std::string_view name, Type type, ObjectPtr user) +std::size_t +TrackList::getCount(Session& session) { session.checkSharedLocked(); - assert(user); + + return session.getDboSession().query("SELECT COUNT(*) FROM tracklist"); +} + + +TrackList::pointer +TrackList::find(Session& session, std::string_view name, Type type, UserId userId) +{ + session.checkSharedLocked(); + assert(userId.isValid()); return session.getDboSession().find() .where("name = ?").bind(name) .where("type = ?").bind(type) - .where("user_id = ?").bind(user->getId()).resultValue(); + .where("user_id = ?").bind(userId).resultValue(); } -std::vector -TrackList::getAll(Session& session) +RangeResults +TrackList::find(Session& session, UserId userId, Range range) { session.checkSharedLocked(); - auto res = session.getDboSession().find().resultList(); - return std::vector(res.begin(), res.end()); + auto query {session.getDboSession().query("SELECT id FROM tracklist") + .where("user_id = ?").bind(userId) + .orderBy("name COLLATE NOCASE")}; + + return execQuery(query, range); } -std::vector -TrackList::getAll(Session& session, ObjectPtr user) +RangeResults +TrackList::find(Session& session, UserId userId, Type type, Range range) { session.checkSharedLocked(); - auto res {session.getDboSession().find() - .where("user_id = ?").bind(user->getId()) - .orderBy("name COLLATE NOCASE") - .resultList()}; - - return std::vector(res.begin(), res.end()); -} - -std::vector -TrackList::getAll(Session& session, ObjectPtr user, Type type) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().find() - .where("user_id = ?").bind(user->getId()) + auto query {session.getDboSession().query("SELECT id FROM tracklist") + .where("user_id = ?").bind(userId) .where("type = ?").bind(type) - .orderBy("name COLLATE NOCASE") - .resultList()}; + .orderBy("name COLLATE NOCASE")}; - return std::vector(res.begin(), res.end()); + return execQuery(query, range); } TrackList::pointer -TrackList::getById(Session& session, TrackListId id) +TrackList::find(Session& session, TrackListId id) { session.checkSharedLocked(); @@ -159,7 +158,7 @@ TrackList::getEntryByTrackAndDateTime(ObjectPtr track, const Wt::WDateTim return session()->find() .where("tracklist_id = ?").bind(getId()) .where("track_id = ?").bind(track->getId()) - .where("date_time = ?").bind(Wt::WDateTime::fromTime_t(dateTime.toTime_t())) + .where("date_time = ?").bind(normalizeDateTime(dateTime)) .resultValue(); } @@ -270,19 +269,109 @@ createTracksQuery(Wt::Dbo::Session& session, TrackListId tracklistId, const std: } std::vector -TrackList::getArtistsReverse(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const +TrackList::getArtists(const std::vector& clusterIds, std::optional linkType, ArtistSortMethod sortMethod, std::optional range, bool& moreResults) const +{ + assert(session()); + + auto query {createArtistsQuery(*session(), "SELECT a from artist a", getId(), clusterIds, linkType) + .groupBy("a.id").having("p_e.date_time = MAX(p_e.date_time)") + .limit(range ? static_cast(range->size) + 1 : -1) + .offset(range ? static_cast(range->offset) : -1)}; + + switch (sortMethod) + { + case ArtistSortMethod::None: + break; + case ArtistSortMethod::ByName: + query.orderBy("a.name COLLATE NOCASE"); + break; + case ArtistSortMethod::BySortName: + query.orderBy("a.sort_name COLLATE NOCASE"); + break; + case ArtistSortMethod::Random: + query.orderBy("RANDOM()"); + break; + case ArtistSortMethod::LastWritten: + case ArtistSortMethod::StarredDateDesc: + assert(false); // Not implemented! + break; + } + + Wt::Dbo::collection> collection {query.resultList()}; + + auto res {std::vector(collection.begin(), collection.end())}; + if (range && res.size() == static_cast(range->size) + 1) + { + moreResults = true; + res.pop_back(); + } + else + moreResults = false; + + return res; +} + + +std::vector> +TrackList::getReleases(const std::vector& clusterIds, std::optional range, bool& moreResults) const +{ + assert(session()); + + auto collection {createReleasesQuery(*session(), "SELECT r from release r", getId(), clusterIds) + .groupBy("r.id").having("p_e.date_time = MAX(p_e.date_time)") + .limit(range ? static_cast(range->size) + 1 : -1) + .offset(range ? static_cast(range->offset) : -1) + .resultList()}; + + std::vector res(collection.begin(), collection.end()); + if (range && res.size() == static_cast(range->size) + 1) + { + moreResults = true; + res.pop_back(); + } + else + moreResults = false; + + return res; +} + +std::vector> +TrackList::getTracks(const std::vector& clusterIds, std::optional range, bool& moreResults) const +{ + assert(session()); + + auto collection {createTracksQuery(*session(), getId(), clusterIds) + .groupBy("t.id").having("p_e.date_time = MAX(p_e.date_time)") + .limit(range ? static_cast(range->size) + 1 : -1) + .offset(range ? static_cast(range->offset) : -1) + .resultList()}; + + std::vector res(collection.begin(), collection.end()); + if (range && res.size() == static_cast(range->size) + 1) + { + moreResults = true; + res.pop_back(); + } + else + moreResults = false; + + return res; +} + +std::vector +TrackList::getArtistsOrderedByRecentFirst(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const { assert(session()); auto collection {createArtistsQuery(*session(), "SELECT a from artist a", getId(), clusterIds, linkType) .groupBy("a.id").having("p_e.date_time = MAX(p_e.date_time)") - .orderBy("p_e.date_time DESC") - .limit(range ? static_cast(range->limit) + 1 : -1) + .orderBy("p_e.date_time DESC, p_e.id DESC") + .limit(range ? static_cast(range->size) + 1 : -1) .offset(range ? static_cast(range->offset) : -1) .resultList()}; auto res {std::vector(collection.begin(), collection.end())}; - if (range && res.size() == static_cast(range->limit) + 1) + if (range && res.size() == static_cast(range->size) + 1) { moreResults = true; res.pop_back(); @@ -294,19 +383,19 @@ TrackList::getArtistsReverse(const std::vector& clusterIds, std::opti } std::vector -TrackList::getReleasesReverse(const std::vector& clusterIds, std::optional range, bool& moreResults) const +TrackList::getReleasesOrderedByRecentFirst(const std::vector& clusterIds, std::optional range, bool& moreResults) const { assert(session()); auto collection {createReleasesQuery(*session(), "SELECT r from release r", getId(), clusterIds) .groupBy("r.id").having("p_e.date_time = MAX(p_e.date_time)") - .orderBy("p_e.date_time DESC") - .limit(range ? static_cast(range->limit) + 1 : -1) + .orderBy("p_e.date_time DESC, p_e.id DESC") + .limit(range ? static_cast(range->size) + 1 : -1) .offset(range ? static_cast(range->offset) : -1) .resultList()}; std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) + if (range && res.size() == static_cast(range->size) + 1) { moreResults = true; res.pop_back(); @@ -318,19 +407,19 @@ TrackList::getReleasesReverse(const std::vector& clusterIds, std::opt } std::vector -TrackList::getTracksReverse(const std::vector& clusterIds, std::optional range, bool& moreResults) const +TrackList::getTracksOrderedByRecentFirst(const std::vector& clusterIds, std::optional range, bool& moreResults) const { assert(session()); auto collection {createTracksQuery(*session(), getId(), clusterIds) .groupBy("t.id").having("p_e.date_time = MAX(p_e.date_time)") - .orderBy("p_e.date_time DESC") - .limit(range ? static_cast(range->limit) + 1 : -1) + .orderBy("p_e.date_time DESC, p_e.id DESC") + .limit(range ? static_cast(range->size) + 1 : -1) .offset(range ? static_cast(range->offset) : -1) .resultList()}; std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) + if (range && res.size() == static_cast(range->size) + 1) { moreResults = true; res.pop_back(); @@ -424,13 +513,13 @@ TrackList::getTopArtists(const std::vector& clusterIds, std::optional auto collection {query .orderBy("COUNT(a.id) DESC") .groupBy("a.id") - .limit(range ? static_cast(range->limit) + 1 : -1) + .limit(range ? static_cast(range->size) + 1 : -1) .offset(range ? static_cast(range->offset) : -1) .resultList()}; std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) + if (range && res.size() == static_cast(range->size) + 1) { moreResults = true; res.pop_back(); @@ -450,12 +539,12 @@ TrackList::getTopReleases(const std::vector& clusterIds, std::optiona auto collection {query .orderBy("COUNT(r.id) DESC") .groupBy("r.id") - .limit(range ? static_cast(range->limit) + 1 : -1) + .limit(range ? static_cast(range->size) + 1 : -1) .offset(range ? static_cast(range->offset) : -1) .resultList()}; std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) + if (range && res.size() == static_cast(range->size) + 1) { moreResults = true; res.pop_back(); @@ -475,12 +564,12 @@ TrackList::getTopTracks(const std::vector& clusterIds, std::optional< auto collection {query .orderBy("COUNT(t.id) DESC") .groupBy("t.id") - .limit(range ? static_cast(range->limit) + 1 : -1) + .limit(range ? static_cast(range->size) + 1 : -1) .offset(range ? static_cast(range->offset) : -1) .resultList()}; std::vector res(collection.begin(), collection.end()); - if (range && res.size() == static_cast(range->limit) + 1) + if (range && res.size() == static_cast(range->size) + 1) { moreResults = true; res.pop_back(); @@ -492,11 +581,10 @@ TrackList::getTopTracks(const std::vector& clusterIds, std::optional< } TrackListEntry::TrackListEntry(ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime) -: _dateTime {Wt::WDateTime::fromTime_t(dateTime.toTime_t())} // force second resolution +: _dateTime {normalizeDateTime(dateTime)} , _track {getDboPtr(track)} , _tracklist {getDboPtr(tracklist)} { - assert(_dateTime.isValid()); } TrackListEntry::pointer diff --git a/src/libs/services/database/impl/Types.cpp b/src/libs/services/database/impl/Types.cpp new file mode 100644 index 00000000..ef02556d --- /dev/null +++ b/src/libs/services/database/impl/Types.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 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/Types.hpp" + +#include + +namespace Database +{ + static const std::set allowedAudioBitrates + { + 64000, + 96000, + 128000, + 192000, + 320000, + }; + + void visitAllowedAudioBitrates(std::function func) + { + for (Bitrate bitrate : allowedAudioBitrates) + func(bitrate); + } + + bool isAudioBitrateAllowed(Bitrate bitrate) + { + return allowedAudioBitrates.find(bitrate) != std::cend(allowedAudioBitrates); + } + + DateRange + DateRange::fromYearRange(int from, int to) + { + return DateRange {{from, 1, 1}, {to, 12, 31}}; + } +} + diff --git a/src/libs/services/database/impl/User.cpp b/src/libs/services/database/impl/User.cpp index 75c69adf..a8abea66 100644 --- a/src/libs/services/database/impl/User.cpp +++ b/src/libs/services/database/impl/User.cpp @@ -23,85 +23,18 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "services/database/TrackList.hpp" #include "utils/Logger.hpp" +#include "IdTypeTraits.hpp" #include "StringViewTraits.hpp" -#include "Traits.hpp" +#include "Utils.hpp" namespace Database { - -AuthToken::AuthToken(const std::string& value, const Wt::WDateTime& expiry, ObjectPtr user) -: _value {value} -, _expiry {expiry} -, _user {getDboPtr(user)} -{ - -} - -AuthToken::pointer -AuthToken::create(Session& session, const std::string& value, const Wt::WDateTime& expiry, ObjectPtr user) -{ - session.checkUniqueLocked(); - - AuthToken::pointer res {session.getDboSession().add(std::make_unique(value, expiry, user))}; - session.getDboSession().flush(); - - return res; -} - -void -AuthToken::removeExpiredTokens(Session& session, const Wt::WDateTime& now) -{ - session.checkUniqueLocked(); - - session.getDboSession().execute - ("DELETE FROM auth_token WHERE expiry < ?").bind(now); -} - -AuthToken::pointer -AuthToken::getByValue(Session& session, const std::string& value) -{ - session.checkSharedLocked(); - - return session.getDboSession().find() - .where("value = ?").bind(value) - .resultValue(); -} - -static const std::string queuedListName {"__queued_tracks__"}; - User::User(std::string_view loginName) : _loginName {loginName} { } -std::vector -User::getAll(Session& session) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().find().resultList()}; - return std::vector(res.begin(), res.end()); -} - -std::vector -User::getAllIds(Session& session) -{ - session.checkSharedLocked(); - - auto res {session.getDboSession().query("SELECT id FROM user").resultList()}; - return std::vector(res.begin(), res.end()); -} - -User::pointer -User::getDemo(Session& session) -{ - session.checkSharedLocked(); - - return session.getDboSession().find().where("type = ?").bind(UserType::DEMO).resultValue(); -} - std::size_t User::getCount(Session& session) { @@ -110,28 +43,43 @@ User::getCount(Session& session) return session.getDboSession().query("SELECT COUNT(*) FROM user"); } +RangeResults +User::find(Session& session, Range range) +{ + session.checkSharedLocked(); + + auto query {session.getDboSession().query("SELECT id FROM user")}; + + return execQuery(query, range); +} + +User::pointer +User::findDemoUser(Session& session) +{ + session.checkSharedLocked(); + + return session.getDboSession().find().where("type = ?").bind(UserType::DEMO).resultValue(); +} + User::pointer User::create(Session& session, std::string_view loginName) { session.checkUniqueLocked(); User::pointer user {session.getDboSession().add(std::make_unique(loginName))}; - - TrackList::create(session, queuedListName, TrackList::Type::Internal, false, user); - session.getDboSession().flush(); return user; } User::pointer -User::getById(Session& session, UserId id) +User::find(Session& session, UserId id) { return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } User::pointer -User::getByLoginName(Session& session, std::string_view name) +User::find(Session& session, std::string_view name) { return session.getDboSession().find() .where("login_name = ?").bind(name) @@ -141,7 +89,7 @@ User::getByLoginName(Session& session, std::string_view name) void User::setSubsonicTranscodeBitrate(Bitrate bitrate) { - assert(audioTranscodeAllowedBitrates.find(bitrate) != audioTranscodeAllowedBitrates.cend()); + assert(isAudioBitrateAllowed(bitrate)); _subsonicTranscodeBitrate = bitrate; } @@ -151,75 +99,6 @@ User::clearAuthTokens() _authTokens.clear(); } -TrackList::pointer -User::getQueuedTrackList(Session& session) const -{ - assert(self()); - session.checkSharedLocked(); - - return TrackList::get(session, queuedListName, TrackList::Type::Internal, self()); -} - -void -User::star(ObjectPtr artist) -{ - if (_starredArtists.count(getDboPtr(artist)) == 0) - _starredArtists.insert(getDboPtr(artist)); -} - -void -User::unstar(ObjectPtr artist) -{ - if (_starredArtists.count(getDboPtr(artist)) != 0) - _starredArtists.erase(getDboPtr(artist)); -} - -bool -User::isStarred(ObjectPtr artist) const -{ - return _starredArtists.count(getDboPtr(artist)) != 0; -} - -void -User::star(ObjectPtr release) -{ - if (_starredReleases.count(getDboPtr(release)) == 0) - _starredReleases.insert(getDboPtr(release)); -} - -void -User::unstar(ObjectPtr release) -{ - if (_starredReleases.count(getDboPtr(release)) != 0) - _starredReleases.erase(getDboPtr(release)); -} - -bool -User::isStarred(ObjectPtr release) const -{ - return _starredReleases.count(getDboPtr(release)) != 0; -} - -void -User::star(ObjectPtr track) -{ - if (_starredTracks.count(getDboPtr(track)) == 0) - _starredTracks.insert(getDboPtr(track)); -} - -void -User::unstar(ObjectPtr track) -{ - if (_starredTracks.count(getDboPtr(track)) != 0) - _starredTracks.erase(getDboPtr(track)); -} - -bool -User::isStarred(ObjectPtr track) const -{ - return _starredTracks.count(getDboPtr(track)) != 0; -} - } // namespace Database diff --git a/src/libs/services/database/impl/Utils.cpp b/src/libs/services/database/impl/Utils.cpp index 39849db1..e0994090 100644 --- a/src/libs/services/database/impl/Utils.cpp +++ b/src/libs/services/database/impl/Utils.cpp @@ -29,5 +29,12 @@ namespace Database return StringUtils::escapeString(keyword, "%_", escapeChar); } + Wt::WDateTime + normalizeDateTime(const Wt::WDateTime& dateTime) + { + // force second resolution + return Wt::WDateTime::fromTime_t(dateTime.toTime_t()); + } + } // namespace Database diff --git a/src/libs/services/database/impl/Utils.hpp b/src/libs/services/database/impl/Utils.hpp index 547c7995..b92b12c3 100644 --- a/src/libs/services/database/impl/Utils.hpp +++ b/src/libs/services/database/impl/Utils.hpp @@ -21,13 +21,66 @@ #include #include -#include + +#include +#include + +#include "services/database/Types.hpp" namespace Database { #define ESCAPE_CHAR_STR "\\" - static constexpr char escapeChar {'\\'}; + static inline constexpr char escapeChar {'\\'}; std::string escapeLikeKeyword(std::string_view keywords); + template + RangeResults + execQuery(Wt::Dbo::Query& query, Range range) + { + RangeResults res; + + auto collection {query.limit(range.size ? static_cast(range.size) + 1 : -1) + .offset(range.offset ? static_cast(range.offset) : -1) + .resultList()}; + + res.results.assign(collection.begin(), collection.end()); + if (range.size && res.results.size() == static_cast(range.size) + 1) + { + res.moreResults = true; + res.results.pop_back(); + } + else + res.moreResults = false; + + res.range.offset = range.offset; + res.range.size = res.results.size(); + return res; + } + + template + RangeResults + execQuery(Wt::Dbo::Query>& query, Range range) + { + RangeResults res; + + auto collection {query.limit(range.size ? static_cast(range.size) + 1 : -1) + .offset(range.offset ? static_cast(range.offset) : -1) + .resultList()}; + + res.results.assign(collection.begin(), collection.end()); + if (range.size && res.results.size() == static_cast(range.size) + 1) + { + res.moreResults = true; + res.results.pop_back(); + } + else + res.moreResults = false; + + res.range.offset = range.offset; + res.range.size = res.results.size(); + return res; + } + + Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime); } // namespace Database diff --git a/src/libs/services/database/include/services/database/Artist.hpp b/src/libs/services/database/include/services/database/Artist.hpp index 0f5785bc..7f28e2fa 100644 --- a/src/libs/services/database/include/services/database/Artist.hpp +++ b/src/libs/services/database/include/services/database/Artist.hpp @@ -27,7 +27,11 @@ #include #include +#include "services/database/ArtistId.hpp" +#include "services/database/ClusterId.hpp" +#include "services/database/Object.hpp" #include "services/database/Types.hpp" +#include "services/database/UserId.hpp" #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" @@ -38,6 +42,7 @@ class Cluster; class ClusterType; class Release; class Session; +class StarredArtist; class Track; class TrackArtistLink; class User; @@ -45,67 +50,53 @@ class User; class Artist : public Object { public: - enum class SortMethod + struct FindParameters { - None, - ByName, - BySortName, + std::vector clusters; // if non empty, at least one artist that belongs to these clusters + std::vector keywords; // if non empty, name must match all of these keywords (on either name field OR sort name field) + std::optional linkType; // if set, only artists that have produced at least one track with this link type + ArtistSortMethod sortMethod {ArtistSortMethod::None}; + Range range; + Wt::WDateTime writtenAfter; + UserId starringUser; // only artists starred by this user + std::optional scrobbler; // and for this scrobbler + + FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; } + FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; } + FindParameters& setLinkType(std::optional _linkType) { linkType = _linkType; return *this; } + FindParameters& setSortMethod(ArtistSortMethod _sortMethod) {sortMethod = _sortMethod; return *this; } + FindParameters& setRange(Range _range) {range = _range; return *this; } + FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; } + FindParameters& setStarringUser(UserId _user, Scrobbler _scrobbler) { starringUser = _user; scrobbler = _scrobbler; return *this; } }; Artist() = default; Artist(const std::string& name, const std::optional& MBID = {}); // Accessors - static pointer getByMBID(Session& session, const UUID& MBID); - static pointer getById(Session& session, ArtistId id); - static bool exists(Session& session, ArtistId id); - static std::vector getByName(Session& session, const std::string& name); // exact match on name field - static std::vector getByClusters(Session& session, - const std::vector& clusters, // at least one track that belongs to these clusters - SortMethod sortMethod - ); - static std::vector getByFilter(Session& session, - const std::vector& clusters, // if non empty, at least one artist that belongs to these clusters - const std::vector& keywords, // if non empty, name must match all of these keywords (name + sort name fields) - std::optional linkType, // if set, only artists that have produced at least one track with this link type - SortMethod sortMethod, - std::optional range, - bool& moreExpected); + static std::size_t getCount(Session& session); + static pointer find(Session& session, const UUID& MBID); + static pointer find(Session& session, ArtistId id); + static std::vector find(Session& session, const std::string& name); // exact match on name field + static RangeResults find(Session& session, const FindParameters& parameters); + static RangeResults findAllOrphans(Session& session, Range range); // No track related + static bool exists(Session& session, ArtistId id); - static std::vector getAll(Session& session); - static std::vector getAll(Session& session, SortMethod sortMethod); - static std::vector getAll(Session& session, SortMethod sortMethod, std::optional range, bool& moreResults); - static std::vector getAllIds(Session& session); - static std::vector getAllIdsRandom(Session& session, const std::vector& clusters, std::optional linkType, std::optional size = {}); - static std::vector getAllOrphans(Session& session); // No track related - static std::vector getLastWritten(Session& session, - std::optional after, - const std::vector& clusters, - std::optional linkType, // if set, only artists that have produced at least one track with this link type - std::optional, - bool& moreResults); - static std::vector getAllIdsWithClusters(Session& session, std::optional limit = {}); - static std::vector getStarred(Session& session, - ObjectPtr user, - const std::vector& clusters, - std::optional linkType, // if set, only artists that have produced at least one track with this link type - SortMethod sortMethod, - std::optional, bool& moreResults); // Accessors const std::string& getName() const { return _name; } const std::string& getSortName() const { return _sortName; } std::optional getMBID() const { return UUID::fromString(_MBID); } - std::vector> getReleases(const std::vector& clusterIds = {}) const; // if non empty, get the releases that match all these clusters + std::vector> getReleases(const std::vector& clusterIds = {}) const; // if non empty, get the releases that match all these clusters std::size_t getReleaseCount() const; std::vector> getTracks(std::optional linkType = {}) const; bool hasNonReleaseTracks(std::optional linkType = std::nullopt) const; - std::vector> getNonReleaseTracks(std::optional linkType, std::optional range, bool& moreResults) const; + RangeResults> getNonReleaseTracks(std::optional linkType, Range range) const; std::vector> getRandomTracks(std::optional count) const; // No artistLinkTypes means get them all - std::vector getSimilarArtists(EnumSet artistLinkTypes = {}, std::optional range = std::nullopt) const; + RangeResults findSimilarArtists(EnumSet artistLinkTypes = {}, Range range = {}) const; // Get the cluster of the tracks made by this artist // Each clusters are grouped by cluster type, sorted by the number of occurence @@ -127,7 +118,7 @@ class Artist : public Object Wt::Dbo::field(a, _MBID, "mbid"); Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "artist"); - Wt::Dbo::hasMany(a, _starringUsers, Wt::Dbo::ManyToMany, "user_release_starred", "", Wt::Dbo::OnDeleteCascade); + Wt::Dbo::hasMany(a, _starredArtists, Wt::Dbo::ManyToMany, "user_starred_artists", "", Wt::Dbo::OnDeleteCascade); } private: @@ -138,8 +129,8 @@ class Artist : public Object std::string _sortName; std::string _MBID; // Musicbrainz Identifier - Wt::Dbo::collection> _trackArtistLinks; // Tracks involving this artist - Wt::Dbo::collection> _starringUsers; // Users that starred this artist + Wt::Dbo::collection> _trackArtistLinks; // Tracks involving this artist + Wt::Dbo::collection> _starredArtists; // starred entries for this artist }; } // namespace Database diff --git a/src/libs/services/database/include/services/database/ArtistId.hpp b/src/libs/services/database/include/services/database/ArtistId.hpp new file mode 100644 index 00000000..01461d90 --- /dev/null +++ b/src/libs/services/database/include/services/database/ArtistId.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(ArtistId) + diff --git a/src/libs/services/database/include/services/database/AuthToken.hpp b/src/libs/services/database/include/services/database/AuthToken.hpp new file mode 100644 index 00000000..46467dc0 --- /dev/null +++ b/src/libs/services/database/include/services/database/AuthToken.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include + +#include +#include + +#include "services/database/AuthTokenId.hpp" +#include "services/database/Object.hpp" + +namespace Database +{ + class Session; + + class User; + class AuthToken : public Object + { + public: + AuthToken() = default; + AuthToken(std::string_view value, const Wt::WDateTime& expiry, ObjectPtr user); + + // Utility + static pointer create(Session& session, std::string_view value, const Wt::WDateTime&expiry, ObjectPtr user); + static void removeExpiredTokens(Session& session, const Wt::WDateTime& now); + static pointer find(Session& session, std::string_view value); + + // Accessors + const Wt::WDateTime& getExpiry() const { return _expiry; } + ObjectPtr getUser() const { return _user; } + const std::string& getValue() const { return _value; } + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _value, "value"); + Wt::Dbo::field(a, _expiry, "expiry"); + Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); + } + + private: + + std::string _value; + Wt::WDateTime _expiry; + Wt::Dbo::ptr _user; + }; +} // namespace Databas' + diff --git a/src/libs/services/database/include/services/database/AuthTokenId.hpp b/src/libs/services/database/include/services/database/AuthTokenId.hpp new file mode 100644 index 00000000..86de8770 --- /dev/null +++ b/src/libs/services/database/include/services/database/AuthTokenId.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(AuthTokenId) diff --git a/src/libs/services/database/include/services/database/Cluster.hpp b/src/libs/services/database/include/services/database/Cluster.hpp index 63032f75..59560f74 100644 --- a/src/libs/services/database/include/services/database/Cluster.hpp +++ b/src/libs/services/database/include/services/database/Cluster.hpp @@ -26,6 +26,9 @@ #include #include +#include "services/database/Object.hpp" +#include "services/database/ClusterId.hpp" +#include "services/database/TrackId.hpp" #include "services/database/Types.hpp" namespace Database { @@ -42,20 +45,20 @@ class Cluster : public Object Cluster(ObjectPtr type, std::string_view name); // Find utility - static std::vector getAll(Session& session); - static std::vector getAllOrphans(Session& session); - static pointer getById(Session& session, ClusterId id); + static std::size_t getCount(Session& session); + static RangeResults find(Session& session, Range range); + static pointer find(Session& session, ClusterId id); + static RangeResults findOrphans(Session& session, Range range); // Create utility static pointer create(Session& session, ObjectPtr type, std::string_view name); // Accessors - const std::string& getName() const { return _name; } - ObjectPtr getType() const { return _clusterType; } - std::size_t getTracksCount() const { return _tracks.size(); } - std::vector> getTracks(std::optional offset = {}, std::optional limit = {}) const; - std::vector getTrackIds() const; - std::size_t getReleasesCount() const; + const std::string& getName() const { return _name; } + ObjectPtr getType() const { return _clusterType; } + std::size_t getTracksCount() const { return _tracks.size(); } + RangeResults getTracks(Range range) const; + std::size_t getReleasesCount() const; void addTrack(ObjectPtr track); @@ -85,19 +88,20 @@ class ClusterType : public Object ClusterType(std::string_view name); // Getters - static std::vector getAllOrphans(Session& session); - static std::vector getAllUsed(Session& session); - static pointer getByName(Session& session, const std::string& name); - static pointer getById(Session& session, ClusterTypeId id); - static std::vector getAll(Session& session); + static std::size_t getCount(Session& session); + static RangeResults find(Session& session, Range range); + static pointer find(Session& session, const std::string& name); + static pointer find(Session& session, ClusterTypeId id); + static RangeResults findOrphans(Session& session, Range range); + static RangeResults findUsed(Session& session, Range range); static pointer create(Session& session, const std::string& name); static void remove(Session& session, const std::string& name); // Accessors - const std::string& getName(void) const { return _name; } - std::vector getClusters() const; - Cluster::pointer getCluster(const std::string& name) const; + const std::string& getName() const { return _name; } + std::vector getClusters() const; + Cluster::pointer getCluster(const std::string& name) const; template void persist(Action& a) diff --git a/src/libs/services/database/include/services/database/ClusterId.hpp b/src/libs/services/database/include/services/database/ClusterId.hpp new file mode 100644 index 00000000..d14a88c0 --- /dev/null +++ b/src/libs/services/database/include/services/database/ClusterId.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(ClusterId) +LMS_DECLARE_IDTYPE(ClusterTypeId) + diff --git a/src/libs/services/database/include/services/database/IdType.hpp b/src/libs/services/database/include/services/database/IdType.hpp new file mode 100644 index 00000000..04aaa2e4 --- /dev/null +++ b/src/libs/services/database/include/services/database/IdType.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + + +#include +#include +#include + +namespace Database +{ + class IdType + { + public: + using ValueType = Wt::Dbo::dbo_default_traits::IdType; + + IdType() = default; + IdType(ValueType id) : _id {id} { assert(isValid()); } + + bool isValid() const { return _id != Wt::Dbo::dbo_default_traits::invalidId(); } + std::string toString() const { assert(isValid()); return std::to_string(_id); } + + ValueType getValue() const { return _id; } + + bool operator==(IdType other) const { return other._id == _id; } + bool operator!=(IdType other) const { return !(*this == other); } + bool operator<(IdType other) const { return _id < other._id; } + bool operator>(IdType other) const { return _id > other._id; } + + private: + Wt::Dbo::dbo_default_traits::IdType _id {Wt::Dbo::dbo_default_traits::invalidId()}; + }; + +#define LMS_DECLARE_IDTYPE(name) \ + namespace Database { \ + class name : public IdType \ + { \ + public: \ + using IdType::IdType; \ + };\ + } \ + namespace std \ + { \ + template<> \ + class hash \ + { \ + public: \ + size_t operator()(Database::name id) const \ + { \ + return std::hash()(id.getValue()); \ + } \ + }; \ + } // ns std +} // namespace Database + diff --git a/src/libs/services/database/include/services/database/Listen.hpp b/src/libs/services/database/include/services/database/Listen.hpp new file mode 100644 index 00000000..32c02294 --- /dev/null +++ b/src/libs/services/database/include/services/database/Listen.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include + +#include "services/database/ArtistId.hpp" +#include "services/database/ClusterId.hpp" +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" +#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" + +LMS_DECLARE_IDTYPE(ListenId) + +namespace Database +{ + +class Session; +class Track; +class User; + +class Listen : public Object +{ + public: + Listen() = default; + Listen(ObjectPtr user, ObjectPtr track, Scrobbler scrobbler, const Wt::WDateTime& dateTime); + + // Accessors + static std::size_t getCount(Session& session); + static pointer find(Session& session, ListenId id); + static RangeResults find(Session& session, UserId userId, Scrobbler scrobbler, Range = {}); + static pointer find(Session& session, UserId userId, TrackId trackId, Scrobbler scrobbler, const Wt::WDateTime& dateTime); + + // Create + static pointer create(Session& session, ObjectPtr user, ObjectPtr track, Scrobbler scrobbler, const Wt::WDateTime& dateTime); + + // Stats + static RangeResults getTopArtists(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + std::optional linkType, + Range range = {}); + static RangeResults getTopReleases(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range = {}); + static RangeResults getTopTracks(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range = {}); + + static RangeResults getRecentArtists(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + std::optional linkType, + Range range = {}); + static RangeResults getRecentReleases(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range = {}); + static RangeResults getRecentTracks(Session& session, + UserId userId, + Scrobbler scrobbler, + const std::vector& clusterIds, + Range range = {}); + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _dateTime, "date_time"); + Wt::Dbo::field(a, _scrobbler, "scrobbler"); + + Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade); + Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); + } + + private: + Wt::WDateTime _dateTime; + Scrobbler _scrobbler; + Wt::Dbo::ptr _user; + Wt::Dbo::ptr _track; +}; + +} // namespace Database + diff --git a/src/libs/services/database/include/services/database/Object.hpp b/src/libs/services/database/include/services/database/Object.hpp new file mode 100644 index 00000000..3c9f189f --- /dev/null +++ b/src/libs/services/database/include/services/database/Object.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 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 "services/database/IdType.hpp" + +namespace Database +{ + template + class ObjectPtr + { + public: + ObjectPtr() = default; + ObjectPtr(Wt::Dbo::ptr obj) : _obj {obj} {} + + const T* operator->() const { return _obj.get(); } + operator bool() const { return _obj.get(); } + bool operator!() const { return !_obj.get(); } + + auto modify() { return _obj.modify(); } + void remove() { _obj.remove(); } + + private: + template friend class Object; + Wt::Dbo::ptr _obj; + }; + + template + class Object : public Wt::Dbo::Dbo + { + static_assert(std::is_base_of_v); + static_assert(!std::is_same_v); + + public: + using pointer = ObjectPtr; + using IdType = ObjectIdType; + + IdType getId() const { return Wt::Dbo::Dbo::self()->Wt::Dbo::template Dbo::id(); } + + // catch some misuses + typename Wt::Dbo::dbo_traits::IdType id() const = delete; + + protected: + // Can get raw dbo ptr only from Objects + template + static + Wt::Dbo::ptr getDboPtr(ObjectPtr ptr) { return ptr._obj; } + }; +} diff --git a/src/libs/services/database/include/services/database/Release.hpp b/src/libs/services/database/include/services/database/Release.hpp index 6af4e700..999899a6 100644 --- a/src/libs/services/database/include/services/database/Release.hpp +++ b/src/libs/services/database/include/services/database/Release.hpp @@ -25,7 +25,11 @@ #include #include +#include "services/database/ClusterId.hpp" +#include "services/database/Object.hpp" +#include "services/database/ReleaseId.hpp" #include "services/database/Types.hpp" +#include "services/database/UserId.hpp" #include "utils/UUID.hpp" namespace Database @@ -42,36 +46,42 @@ class User; class Release : public Object { public: + struct FindParameters + { + std::vector clusters; // if non empty, releases that belong to these clusters + std::vector keywords; // if non empty, name must match all of these keywords + ReleaseSortMethod sortMethod {ReleaseSortMethod::None}; + Range range; + Wt::WDateTime writtenAfter; + std::optional dateRange; + UserId starringUser; // only releases starred by this user + std::optional scrobbler; // and for this scrobbler + + 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; } + FindParameters& setRange(Range _range) {range = _range; return *this; } + FindParameters& setWrittenAfter(const Wt::WDateTime& _after) {writtenAfter = _after; return *this; } + FindParameters& setDateRange(const std::optional& _dateRange) {dateRange = _dateRange; return *this; } + FindParameters& setStarringUser(UserId _user, Scrobbler _scrobbler) { starringUser = _user; scrobbler = _scrobbler; return *this; } + }; + Release() = default; Release(const std::string& name, const std::optional& MBID = {}); // Accessors - static std::size_t getCount(Session& session); - static pointer getByMBID(Session& session, const UUID& MBID); - static std::vector getByName(Session& session, const std::string& name); - static pointer getById(Session& session, ReleaseId id); - static bool exists(Session& session, ReleaseId id); - static std::vector getAllOrphans(Session& session); // no track related - static std::vector getAll(Session& session, std::optional range = std::nullopt); - static std::vector getAllIds(Session& session); - static std::vector getAllOrderedByArtist(Session& session, std::optional offset = {}, std::optional size = {}); - static std::vector getAllRandom(Session& session, const std::vector& clusters, std::optional size = {}); - static std::vector getAllIdsRandom(Session& session, const std::vector& clusters, std::optional size = {}); - static std::vector getLastWritten(Session& session, std::optional after, const std::vector& clusters, std::optional range, bool& moreResults); - static std::vector getByYear(Session& session, int yearFrom, int yearTo, std::optional range = std::nullopt); - static std::vector getStarred(Session& session, ObjectPtr user, const std::vector& clusters, std::optional range, bool& moreResults); + 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 pointer find(Session& session, ReleaseId id); + static RangeResults find(Session& session, const FindParameters& parameters); + static RangeResults findOrphans(Session& session, Range range); // no track related + static RangeResults findOrderedByArtist(Session& session, Range range); - static std::vector getByClusters(Session& session, const std::vector& clusters); - static std::vector getByFilter(Session& session, - const std::vector& clusters, // if non empty, at least one release that belongs to these clusters - const std::vector& keywords, // if non empty, name must match all of these keywords - std::optional range, - bool& moreExpected); - static std::vector getAllIdsWithClusters(Session& session, std::optional limit = {}); - - std::vector> getTracks(const std::vector& clusters = {}) const; - std::size_t getTracksCount() const; - ObjectPtr getFirstTrack() const; + std::vector> getTracks(const std::vector& clusters = {}) const; + std::size_t getTracksCount() const; + ObjectPtr getFirstTrack() const; // Get the cluster of the tracks that belong to this release // Each clusters are grouped by cluster type, sorted by the number of occurence (max to min) @@ -110,7 +120,6 @@ class Release : public Object Wt::Dbo::field(a, _MBID, "mbid"); Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToOne, "release"); - Wt::Dbo::hasMany(a, _starringUsers, Wt::Dbo::ManyToMany, "user_release_starred", "", Wt::Dbo::OnDeleteCascade); } private: @@ -120,7 +129,6 @@ class Release : public Object std::string _MBID; Wt::Dbo::collection> _tracks; // Tracks in the release - Wt::Dbo::collection> _starringUsers; // Users that starred this release }; } // namespace Database diff --git a/src/libs/services/database/include/services/database/ReleaseId.hpp b/src/libs/services/database/include/services/database/ReleaseId.hpp new file mode 100644 index 00000000..c9c07c7a --- /dev/null +++ b/src/libs/services/database/include/services/database/ReleaseId.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(ReleaseId) + diff --git a/src/libs/services/database/include/services/database/ScanSettings.hpp b/src/libs/services/database/include/services/database/ScanSettings.hpp index 0a482956..75fa4a58 100644 --- a/src/libs/services/database/include/services/database/ScanSettings.hpp +++ b/src/libs/services/database/include/services/database/ScanSettings.hpp @@ -25,7 +25,10 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" + +LMS_DECLARE_IDTYPE(ScanSettingsId) namespace Database { diff --git a/src/libs/services/database/include/services/database/StarredArtist.hpp b/src/libs/services/database/include/services/database/StarredArtist.hpp new file mode 100644 index 00000000..0abb51e0 --- /dev/null +++ b/src/libs/services/database/include/services/database/StarredArtist.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include + +#include "services/database/ArtistId.hpp" +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" +#include "services/database/Session.hpp" +#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" + +LMS_DECLARE_IDTYPE(StarredArtistId) + +namespace Database +{ + class Artist; + class Session; + class User; + + class StarredArtist : public Object + { + public: + StarredArtist() = default; + StarredArtist(ObjectPtr artist, ObjectPtr user, Scrobbler scrobbler); + + // 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, Scrobbler scrobbler); + + // Create utility + static pointer create(Session& session, ObjectPtr artist, ObjectPtr user, Scrobbler scrobbler); + + // Accessors + ObjectPtr getArtist() const { return _artist; } + ObjectPtr getUser() const { return _user; } + Scrobbler getScrobbler() const { return _scrobbler; } + const Wt::WDateTime& getDateTime() const { return _dateTime; } + + // Setters + void setDateTime(const Wt::WDateTime& dateTime); + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _scrobbler, "scrobbler"); + Wt::Dbo::field(a, _dateTime, "date_time"); + + Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade); + Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); + } + + private: + Scrobbler _scrobbler; // for which scrobbler + Wt::WDateTime _dateTime; // when it was starred + + Wt::Dbo::ptr _artist; + Wt::Dbo::ptr _user; + }; +} // namespace Database + diff --git a/src/libs/services/database/include/services/database/StarredRelease.hpp b/src/libs/services/database/include/services/database/StarredRelease.hpp new file mode 100644 index 00000000..e7a1ad1e --- /dev/null +++ b/src/libs/services/database/include/services/database/StarredRelease.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include + +#include "services/database/ReleaseId.hpp" +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" +#include "services/database/Session.hpp" +#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" + +LMS_DECLARE_IDTYPE(StarredReleaseId) + +namespace Database +{ + class Release; + class Session; + class User; + + class StarredRelease : public Object + { + public: + StarredRelease() = default; + StarredRelease(ObjectPtr release, ObjectPtr user, Scrobbler scrobbler); + + // 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, Scrobbler scrobbler); + + // Create utility + static pointer create(Session& session, ObjectPtr release, ObjectPtr user, Scrobbler scrobbler); + + // Accessors + ObjectPtr getRelease() const { return _release; } + ObjectPtr getUser() const { return _user; } + Scrobbler getScrobbler() const { return _scrobbler; } + const Wt::WDateTime& getDateTime() const { return _dateTime; } + + // Setters + void setDateTime(const Wt::WDateTime& dateTime); + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _scrobbler, "scrobbler"); + Wt::Dbo::field(a, _dateTime, "date_time"); + + Wt::Dbo::belongsTo(a, _release, "release", Wt::Dbo::OnDeleteCascade); + Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); + } + + private: + Scrobbler _scrobbler; // for which scrobbler + Wt::WDateTime _dateTime; // when it was starred + + Wt::Dbo::ptr _release; + Wt::Dbo::ptr _user; + }; +} // namespace Database + diff --git a/src/libs/services/database/include/services/database/StarredTrack.hpp b/src/libs/services/database/include/services/database/StarredTrack.hpp new file mode 100644 index 00000000..ab4cf1d1 --- /dev/null +++ b/src/libs/services/database/include/services/database/StarredTrack.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include + +#include "services/database/TrackId.hpp" +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" +#include "services/database/Session.hpp" +#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" + +LMS_DECLARE_IDTYPE(StarredTrackId) + +namespace Database +{ + class Track; + class Session; + class User; + + class StarredTrack : public Object + { + public: + StarredTrack() = default; + StarredTrack(ObjectPtr track, ObjectPtr user, Scrobbler scrobbler); + + // 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, Scrobbler scrobbler); + + // Create utility + static pointer create(Session& session, ObjectPtr track, ObjectPtr user, Scrobbler scrobbler); + + // Accessors + ObjectPtr getTrack() const { return _track; } + ObjectPtr getUser() const { return _user; } + Scrobbler getScrobbler() const { return _scrobbler; } + const Wt::WDateTime& getDateTime() const { return _dateTime; } + + // Setters + void setDateTime(const Wt::WDateTime& dateTime); + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _scrobbler, "scrobbler"); + Wt::Dbo::field(a, _dateTime, "date_time"); + + Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade); + Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); + } + + private: + Scrobbler _scrobbler; // for which scrobbler + Wt::WDateTime _dateTime; // when it was starred + + Wt::Dbo::ptr _track; + Wt::Dbo::ptr _user; + }; +} // namespace Database + diff --git a/src/libs/services/database/include/services/database/Track.hpp b/src/libs/services/database/include/services/database/Track.hpp index ccc9bb4e..38297cb3 100644 --- a/src/libs/services/database/include/services/database/Track.hpp +++ b/src/libs/services/database/include/services/database/Track.hpp @@ -34,7 +34,12 @@ #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" +#include "services/database/ArtistId.hpp" +#include "services/database/ClusterId.hpp" +#include "services/database/Object.hpp" +#include "services/database/TrackId.hpp" #include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace Database { @@ -44,50 +49,51 @@ class ClusterType; class Release; class Session; class TrackArtistLink; -class TrackFeatures; -class TrackListEntry; class TrackStats; class User; class Track : public Object { public: + struct FindParameters + { + std::vector clusters; // if non empty, tracks that belong to these clusters + std::vector keywords; // if non empty, name must match all of these keywords + TrackSortMethod sortMethod {TrackSortMethod::None}; + Range range; + Wt::WDateTime writtenAfter; + UserId starringUser; // only tracks starred by this user + std::optional scrobbler; // and for this scrobbler + + FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; } + FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; } + FindParameters& setSortMethod(TrackSortMethod _method) { sortMethod = _method; return *this; } + FindParameters& setRange(Range _range) { range = _range; return *this; } + FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; } + FindParameters& setStarringUser(UserId _user, Scrobbler _scrobbler) { starringUser = _user; scrobbler = _scrobbler; return *this; } + }; + struct PathResult + { + TrackId trackId; + std::filesystem::path path; + }; + Track() = default; Track(const std::filesystem::path& p); // Find utility functions - static std::size_t getCount(Session& session); - static pointer getByPath(Session& session, const std::filesystem::path& p); - static pointer getById(Session& session, TrackId id); - static bool exists(Session& session, TrackId id); - static std::vector getByRecordingMBID(Session& session, const UUID& MBID); - static std::vector getSimilarTracks(Session& session, - const std::vector& trackIds, - std::optional offset = {}, - std::optional size = {}); - static std::vector getByClusters(Session& session, - const std::vector& clusters); // tracks that belong to these clusters - static std::vector getByFilter(Session& session, - const std::vector& clusters, // if non empty, tracks that belong to these clusters - const std::vector& keywords, // if non empty, name must match all of these keywords - std::optional range, - bool& moreExpected); - static std::vector getByNameAndReleaseName(Session& session, std::string_view trackName, std::string_view releaseName); + static std::size_t getCount(Session& session); + static pointer findByPath(Session& session, const std::filesystem::path& p); + static pointer find(Session& session, TrackId id); + static bool exists(Session& session, TrackId id); + static std::vector findByRecordingMBID(Session& session, const UUID& MBID); + static RangeResults findSimilarTracks(Session& session, const std::vector& trackIds, Range range); - static std::vector getAll(Session& session, std::optional limit = std::nullopt); - static std::vector getAllRandom(Session& session, const std::vector& clusters, std::optional limit = std::nullopt); - static std::vector getAllIdsRandom(Session& session, const std::vector& clusters, std::optional limit = std::nullopt); - static std::vector getAllIds(Session& session); - static std::vector> getAllPaths(Session& session, std::optional offset = std::nullopt, std::optional size = std::nullopt); - static std::vector getMBIDDuplicates(Session& session); - static std::vector getLastWritten(Session& session, std::optional after, const std::vector& clusters, std::optional range, bool& moreResults); - static std::vector getAllWithRecordingMBIDAndMissingFeatures(Session& session); - static std::vector getAllIdsWithFeatures(Session& session, std::optional limit = {}); - static std::vector getAllIdsWithClusters(Session& session, std::optional limit = {}); - static std::vector getStarred(Session& session, - ObjectPtr user, - const std::vector& clusters, - std::optional range, bool& hasMore); + static RangeResults find(Session& session, const FindParameters& parameters); + static RangeResults findByNameAndReleaseName(Session& session, std::string_view trackName, std::string_view releaseName); + static RangeResults findPaths(Session& session, Range range); + static RangeResults findMBIDDuplicates(Session& session, Range range); + static RangeResults findWithRecordingMBIDAndMissingFeatures(Session& session, Range range); // Create utility static pointer create(Session& session, const std::filesystem::path& p); @@ -116,7 +122,6 @@ class Track : public Object void addArtistLink(const ObjectPtr& artistLink); void setRelease(ObjectPtr release) { _release = getDboPtr(release); } void setClusters(const std::vector>& clusters ); - void setFeatures(const ObjectPtr& features); std::size_t getScanVersion() const { return _scanVersion; } std::optional getTrackNumber() const; @@ -147,8 +152,6 @@ class Track : public Object ObjectPtr getRelease() const { return _release; } std::vector> getClusters() const; std::vector getClusterIds() const; - bool hasTrackFeatures() const; - ObjectPtr getTrackFeatures() const; std::vector>> getClusterGroups(const std::vector>& clusterTypes, std::size_t size) const; @@ -178,9 +181,6 @@ class Track : public Object Wt::Dbo::belongsTo(a, _release, "release", Wt::Dbo::OnDeleteCascade); Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "track"); Wt::Dbo::hasMany(a, _clusters, Wt::Dbo::ManyToMany, "track_cluster", "", Wt::Dbo::OnDeleteCascade); - Wt::Dbo::hasMany(a, _playlistEntries, Wt::Dbo::ManyToOne, "track"); - Wt::Dbo::hasMany(a, _starringUsers, Wt::Dbo::ManyToMany, "user_track_starred", "", Wt::Dbo::OnDeleteCascade); - Wt::Dbo::hasOne(a, _trackFeatures); } private: @@ -215,10 +215,6 @@ class Track : public Object Wt::Dbo::ptr _release; Wt::Dbo::collection> _trackArtistLinks; Wt::Dbo::collection> _clusters; - Wt::Dbo::collection> _playlistEntries; - Wt::Dbo::collection> _starringUsers; - Wt::Dbo::weak_ptr _trackFeatures; - }; } // namespace database diff --git a/src/libs/services/database/include/services/database/TrackArtistLink.hpp b/src/libs/services/database/include/services/database/TrackArtistLink.hpp index 7708c69e..8a1ba0b2 100644 --- a/src/libs/services/database/include/services/database/TrackArtistLink.hpp +++ b/src/libs/services/database/include/services/database/TrackArtistLink.hpp @@ -23,9 +23,13 @@ #include +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" #include "services/database/Types.hpp" #include "utils/EnumSet.hpp" +LMS_DECLARE_IDTYPE(TrackArtistLinkId) + namespace Database { @@ -40,11 +44,10 @@ namespace Database TrackArtistLink(ObjectPtr track, ObjectPtr artist, TrackArtistLinkType type); static pointer create(Session& session, ObjectPtr track, ObjectPtr artist, TrackArtistLinkType type); - - static EnumSet getUsedTypes(Session& session); + static EnumSet findUsedTypes(Session& session); ObjectPtr getTrack() const { return _track; } - ObjectPtr getArtist() const { return _artist; } + ObjectPtr getArtist() const { return _artist; } TrackArtistLinkType getType() const { return _type; } template diff --git a/src/libs/services/database/include/services/database/TrackBookmark.hpp b/src/libs/services/database/include/services/database/TrackBookmark.hpp index 34ddb542..b5fb2985 100644 --- a/src/libs/services/database/include/services/database/TrackBookmark.hpp +++ b/src/libs/services/database/include/services/database/TrackBookmark.hpp @@ -23,7 +23,13 @@ #include +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" +#include "services/database/TrackId.hpp" #include "services/database/Types.hpp" +#include "services/database/UserId.hpp" + +LMS_DECLARE_IDTYPE(TrackBookmarkId) namespace Database { @@ -41,10 +47,10 @@ class TrackBookmark : public Object static pointer create(Session& session, ObjectPtr user, ObjectPtr track); // Find utility functions - static std::vector getAll(Session& session); - static std::vector getByUser(Session& session, ObjectPtr user); - static pointer getByUser(Session& session, ObjectPtr user, ObjectPtr track); - static pointer getById(Session& session, TrackBookmarkId id); + static std::size_t getCount(Session& session); + static pointer find(Session& session, TrackBookmarkId id); + static RangeResults find(Session& session, UserId userId, Range range); + static pointer find(Session& session, UserId userId, TrackId trackId); // Setters void setOffset(std::chrono::milliseconds offset) { _offset = offset; } @@ -52,9 +58,9 @@ class TrackBookmark : public Object // Getters std::chrono::milliseconds getOffset() const { return _offset; } - std::string_view getComment() const { return _comment; } - ObjectPtr getTrack() const { return _track; } - ObjectPtr getUser() const { return _user; } + std::string_view getComment() const { return _comment; } + ObjectPtr getTrack() const { return _track; } + ObjectPtr getUser() const { return _user; } template void persist(Action& a) diff --git a/src/libs/services/database/include/services/database/TrackFeatures.hpp b/src/libs/services/database/include/services/database/TrackFeatures.hpp index fd251647..1c1251fa 100644 --- a/src/libs/services/database/include/services/database/TrackFeatures.hpp +++ b/src/libs/services/database/include/services/database/TrackFeatures.hpp @@ -26,8 +26,13 @@ #include +#include "services/database/IdType.hpp" +#include "services/database/Object.hpp" +#include "services/database/TrackId.hpp" #include "services/database/Types.hpp" +LMS_DECLARE_IDTYPE(TrackFeaturesId) + namespace Database { class Session; @@ -43,12 +48,21 @@ class TrackFeatures : public Object TrackFeatures() = default; TrackFeatures(ObjectPtr track, const std::string& jsonEncodedFeatures); + // Find utilities + static std::size_t getCount(Session& session); + static pointer find(Session& session, TrackFeaturesId id); + static pointer find(Session& session, TrackId trackId); + static RangeResults find(Session& session, Range range); + // Create utility - static pointer create(Session& session, ObjectPtr track, const std::string& jsonEncodedFeatures); + static pointer create(Session& session, ObjectPtr track, const std::string& jsonEncodedFeatures); FeatureValues getFeatureValues(const FeatureName& feature) const; FeatureValuesMap getFeatureValuesMap(const std::unordered_set& featureNames) const; + // Accessors + Wt::Dbo::ptr getTrack() const { return _track; } + template void persist(Action& a) { @@ -57,7 +71,6 @@ class TrackFeatures : public Object } private: - std::string _data; Wt::Dbo::ptr _track; }; diff --git a/src/libs/services/database/include/services/database/TrackId.hpp b/src/libs/services/database/include/services/database/TrackId.hpp new file mode 100644 index 00000000..40684e3e --- /dev/null +++ b/src/libs/services/database/include/services/database/TrackId.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(TrackId) + diff --git a/src/libs/services/database/include/services/database/TrackList.hpp b/src/libs/services/database/include/services/database/TrackList.hpp index 48f56c23..b168ad5d 100644 --- a/src/libs/services/database/include/services/database/TrackList.hpp +++ b/src/libs/services/database/include/services/database/TrackList.hpp @@ -21,13 +21,18 @@ #include #include -#include +#include #include #include #include +#include "services/database/ClusterId.hpp" +#include "services/database/Object.hpp" +#include "services/database/TrackId.hpp" +#include "services/database/TrackListId.hpp" #include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace Database { @@ -52,25 +57,25 @@ class TrackList : public Object TrackList(std::string_view name, Type type, bool isPublic, ObjectPtr user); // Stats utility - std::vector> getTopArtists(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const; - std::vector> getTopReleases(const std::vector& clusterIds, std::optional range, bool& moreResults) const; - std::vector> getTopTracks(const std::vector& clusterIds, std::optional range, bool& moreResults) const; + std::vector> getTopArtists(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const; + std::vector> getTopReleases(const std::vector& clusterIds, std::optional range, bool& moreResults) const; + std::vector> getTopTracks(const std::vector& clusterIds, std::optional range, bool& moreResults) const; // Search utility - static pointer get(Session& session, std::string_view name, Type type, ObjectPtr user); - static pointer getById(Session& session, TrackListId tracklistId); - static std::vector getAll(Session& session); - static std::vector getAll(Session& session, ObjectPtr user); - static std::vector getAll(Session& session, ObjectPtr user, Type type); + static std::size_t getCount(Session& session); + static pointer find(Session& session, std::string_view name, Type type, UserId userId); + static pointer find(Session& session, TrackListId tracklistId); + static RangeResults find(Session& session, UserId userId, Range range); + static RangeResults find(Session& session, UserId userId, Type type, Range range); // Create utility static pointer create(Session& session, std::string_view name, Type type, bool isPublic, ObjectPtr user); // Accessors - std::string getName() const { return _name; } - bool isPublic() const { return _isPublic; } - Type getType() const { return _type; } - ObjectPtr getUser() const { return _user; } + std::string_view getName() const { return _name; } + bool isPublic() const { return _isPublic; } + Type getType() const { return _type; } + ObjectPtr getUser() const { return _user; } // Modifiers void setName(const std::string& name) { _name = name; } @@ -81,18 +86,20 @@ class TrackList : public Object bool isEmpty() const; std::size_t getCount() const; ObjectPtr getEntry(std::size_t pos) const; - std::vector> getEntries(std::optional offset = {}, std::optional size = {}) const; + std::vector> getEntries(std::optional offset = {}, std::optional size = {}) const; ObjectPtr getEntryByTrackAndDateTime(ObjectPtr track, const Wt::WDateTime& dateTime) const; - // Get track bya + std::vector> getArtists(const std::vector& clusters, std::optional linkType, ArtistSortMethod sortMethod, std::optional range, bool& moreResults) const; + std::vector> getReleases(const std::vector& clusters, std::optional range, bool& moreResults) const; + std::vector> getTracks(const std::vector& clusters, std::optional range, bool& moreResults) const; - std::vector> getArtistsReverse(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const; - std::vector> getReleasesReverse(const std::vector& clusterIds, std::optional range, bool& moreResults) const; - std::vector> getTracksReverse(const std::vector& clusterIds, std::optional range, bool& moreResults) const; + // Sorted by date time + std::vector> getArtistsOrderedByRecentFirst(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const; + std::vector> getReleasesOrderedByRecentFirst(const std::vector& clusterIds, std::optional range, bool& moreResults) const; + std::vector> getTracksOrderedByRecentFirst(const std::vector& clusterIds, std::optional range, bool& moreResults) const; - std::vector getTrackIds() const; - - std::chrono::milliseconds getDuration() const; + std::vector getTrackIds() const; + std::chrono::milliseconds getDuration() const; // Get clusters, order by occurence std::vector> getClusters() const; @@ -129,12 +136,13 @@ class TrackListEntry : public Object public: TrackListEntry() = default; TrackListEntry(ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime); + TrackListEntry(ObjectPtr track, ObjectPtr tracklist); // find utility static pointer getById(Session& session, TrackListEntryId id); // Create utility - static pointer create(Session& session, ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime = Wt::WDateTime::currentDateTime()); + static pointer create(Session& session, ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime = {}); // Accessors ObjectPtr getTrack() const { return _track; } @@ -151,7 +159,7 @@ class TrackListEntry : public Object private: - Wt::WDateTime _dateTime; + Wt::WDateTime _dateTime; // optional date time Wt::Dbo::ptr _track; Wt::Dbo::ptr _tracklist; }; diff --git a/src/libs/services/database/include/services/database/TrackListId.hpp b/src/libs/services/database/include/services/database/TrackListId.hpp new file mode 100644 index 00000000..3dc35d07 --- /dev/null +++ b/src/libs/services/database/include/services/database/TrackListId.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(TrackListId) +LMS_DECLARE_IDTYPE(TrackListEntryId) + diff --git a/src/libs/services/database/include/services/database/Types.hpp b/src/libs/services/database/include/services/database/Types.hpp index c2fb4146..c1c0021b 100644 --- a/src/libs/services/database/include/services/database/Types.hpp +++ b/src/libs/services/database/include/services/database/Types.hpp @@ -22,63 +22,126 @@ #include #include #include -#include +#include namespace Database { - class IdType - { - public: - using ValueType = Wt::Dbo::dbo_default_traits::IdType; - - IdType() = default; - IdType(ValueType id) : _id {id} { assert(isValid()); } - - bool isValid() const { return _id != Wt::Dbo::dbo_default_traits::invalidId(); } - std::string toString() const { assert(isValid()); return std::to_string(_id); } - - ValueType getValue() const { return _id; } - - bool operator==(IdType other) const { return other._id == _id; } - bool operator!=(IdType other) const { return !(*this == other); } - bool operator<(IdType other) const { return other._id < _id; } - - private: - Wt::Dbo::dbo_default_traits::IdType _id {Wt::Dbo::dbo_default_traits::invalidId()}; - }; + // Caution: do not change enum values if they are set! + // Request: + // size = 0 => no size limit! + // Response (via RangeResults) + // size => results size struct Range { std::size_t offset {}; - std::size_t limit {}; + std::size_t size {}; + + // TODO remove this + operator bool() const { return size != 0; } }; + template + struct RangeResults + { + Range range; + std::vector results; + bool moreResults; + + RangeResults getSubRange(Range subRange) + { + assert(subRange.offset >= range.offset); + + if (!subRange.size) + subRange.size = range.size - (subRange.offset - range.offset); + + subRange.offset = std::min(subRange.offset, range.offset + range.size); + subRange.size = std::min(subRange.size, range.offset + range.size - subRange.offset); + + RangeResults subResults; + + auto itBegin {std::cbegin(results) + subRange.offset - range.offset}; + auto itEnd {itBegin + subRange.size}; + subResults.results.reserve(std::distance(itBegin, itEnd)); + std::copy(itBegin, itEnd, std::back_inserter(subResults.results)); + + subResults.range = subRange; + if (subRange.offset + subRange.size == range.offset + range.size) + subResults.moreResults = moreResults; + else + subResults.moreResults = true; + + return subResults; + } + }; + + struct DateRange + { + Wt::WDate begin; + Wt::WDate end; + + static DateRange fromYearRange(int from, int to); + }; + + enum class ArtistSortMethod + { + None, + ByName, + BySortName, + Random, + LastWritten, + StarredDateDesc, + }; + + enum class ReleaseSortMethod + { + None, + Name, + Date, + Random, + LastWritten, + StarredDateDesc, + }; + + enum class TrackSortMethod + { + None, + Random, + LastWritten, + StarredDateDesc, + }; + + // Do not change enum values! enum class TrackArtistLinkType { - Artist, // regular artist - Arranger, - Composer, - Conductor, - Lyricist, - Mixer, - Performer, - Producer, - ReleaseArtist, - Remixer, - Writer, + Artist = 0, // regular track artist + Arranger = 1, + Composer = 2, + Conductor = 3, + Lyricist = 4, + Mixer = 5, + Performer = 6, + Producer = 7, + ReleaseArtist = 8, + Remixer = 9, + Writer = 10, }; // User selectable audio file formats // Do not change values enum class AudioFormat { - MP3 = 1, - OGG_OPUS = 2, - OGG_VORBIS = 3, - WEBM_VORBIS = 4, + MP3 = 1, + OGG_OPUS = 2, + OGG_VORBIS = 3, + WEBM_VORBIS = 4, MATROSKA_OPUS = 5, }; + using Bitrate = std::uint32_t; + // Do not remove values! + void visitAllowedAudioBitrates(std::function); + bool isAudioBitrateAllowed(Bitrate bitrate); // Do not change enum values! enum class Scrobbler @@ -95,82 +158,19 @@ namespace Database DEMO = 2, }; - template - class ObjectPtr + // Do not change enum values! + enum class UITheme { - public: - ObjectPtr() = default; - ObjectPtr(Wt::Dbo::ptr obj) : _obj {obj} {} - - const T* operator->() const { return _obj.get(); } - operator bool() const { return _obj.get(); } - bool operator!() const { return !_obj.get(); } - - auto modify() { return _obj.modify(); } - void remove() { _obj.remove(); } - - private: - template friend class Object; - Wt::Dbo::ptr _obj; + Light = 0, + Dark = 1, }; - template - class Object : public Wt::Dbo::Dbo + // Do not change enum values! + enum class SubsonicArtistListMode { - static_assert(std::is_base_of_v); - static_assert(!std::is_same_v); - - public: - using pointer = ObjectPtr; - using IdType = ObjectIdType; - - IdType getId() const { return Wt::Dbo::Dbo::self()->Wt::Dbo::template Dbo::id(); } - - // catch some misuses - typename Wt::Dbo::dbo_traits::IdType id() const = delete; - - protected: - // Can get raw dbo ptr only from Objects - template - static - Wt::Dbo::ptr getDboPtr(ObjectPtr ptr) { return ptr._obj; } + AllArtists = 0, + ReleaseArtists = 1, + TrackArtists = 2, }; } -// TODO factorize hash with std::enable_if -#define LMS_DECLARE_IDTYPE(name) \ -namespace Database { \ - class name : public IdType \ - { \ - public: \ - using IdType::IdType; \ - };\ -} \ -namespace std \ -{ \ - template<> \ - class hash \ - { \ - public: \ - size_t operator()(Database::name id) const \ - { \ - return std::hash()(id.getValue()); \ - } \ - }; \ -} // ns std - -LMS_DECLARE_IDTYPE(ArtistId) -LMS_DECLARE_IDTYPE(AuthTokenId) -LMS_DECLARE_IDTYPE(ClusterId) -LMS_DECLARE_IDTYPE(ClusterTypeId) -LMS_DECLARE_IDTYPE(ReleaseId) -LMS_DECLARE_IDTYPE(ScanSettingsId) -LMS_DECLARE_IDTYPE(TrackArtistLinkId) -LMS_DECLARE_IDTYPE(TrackBookmarkId) -LMS_DECLARE_IDTYPE(TrackFeaturesId) -LMS_DECLARE_IDTYPE(TrackId) -LMS_DECLARE_IDTYPE(TrackListId) -LMS_DECLARE_IDTYPE(TrackListEntryId) -LMS_DECLARE_IDTYPE(UserId) - - diff --git a/src/libs/services/database/include/services/database/User.hpp b/src/libs/services/database/include/services/database/User.hpp index 34bfac2c..e42a2181 100644 --- a/src/libs/services/database/include/services/database/User.hpp +++ b/src/libs/services/database/include/services/database/User.hpp @@ -26,51 +26,15 @@ #include #include +#include "services/database/Object.hpp" #include "services/database/Types.hpp" +#include "services/database/UserId.hpp" #include "utils/UUID.hpp" namespace Database { - -class Artist; -class Release; +class AuthToken; class Session; -class TrackList; -class Track; - -class User; -class AuthToken : public Object -{ - public: - AuthToken() = default; - AuthToken(const std::string& value, const Wt::WDateTime& expiry, ObjectPtr user); - - // Utility - static pointer create(Session& session, const std::string& value, const Wt::WDateTime&expiry, ObjectPtr user); - static void removeExpiredTokens(Session& session, const Wt::WDateTime& now); - static pointer getByValue(Session& session, const std::string& value); - static pointer getById(Session& session, AuthTokenId tokenId); - - // Accessors - const Wt::WDateTime& getExpiry() const { return _expiry; } - ObjectPtr getUser() const { return _user; } - const std::string& getValue() const { return _value; } - - template - void persist(Action& a) - { - Wt::Dbo::field(a, _value, "value"); - Wt::Dbo::field(a, _expiry, "expiry"); - Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); - } - - private: - - std::string _value; - Wt::WDateTime _expiry; - - Wt::Dbo::ptr _user; -}; class User : public Object { @@ -81,35 +45,10 @@ class User : public Object std::string hash; }; - // Do not change enum values! - enum class UITheme - { - Light = 0, - Dark = 1, - }; - - // Do not remove values! - static inline const std::set audioTranscodeAllowedBitrates - { - 64000, - 96000, - 128000, - 192000, - 320000, - }; - - // Do not change enum values! - enum class SubsonicArtistListMode - { - AllArtists = 0, - ReleaseArtists = 1, - TrackArtists = 2, - }; - static inline const std::size_t MinNameLength {3}; static inline const std::size_t MaxNameLength {15}; static inline const bool defaultSubsonicTranscodeEnable {true}; - static inline const AudioFormat defaultSubsonicTranscodeFormat {AudioFormat::OGG_OPUS}; + static inline const AudioFormat defaultSubsonicTranscodeFormat {AudioFormat::OGG_OPUS}; static inline const Bitrate defaultSubsonicTranscodeBitrate {128000}; static inline const UITheme defaultUITheme {UITheme::Dark}; static inline const SubsonicArtistListMode defaultSubsonicArtistListMode {SubsonicArtistListMode::AllArtists}; @@ -121,18 +60,17 @@ class User : public Object // utility static pointer create(Session& session, std::string_view loginName); - static pointer getById(Session& session, UserId id); - static pointer getByLoginName(Session& session, std::string_view loginName); - static std::vector getAll(Session& session); - static std::vector getAllIds(Session& session); - static pointer getDemo(Session& session); - static std::size_t getCount(Session& session); + static std::size_t getCount(Session& session); + static pointer find(Session& session, UserId id); + static pointer find(Session& session, std::string_view loginName); + static RangeResults find(Session& session, Range range); + static pointer findDemoUser(Session& session); // accessors - const std::string& getLoginName() const { return _loginName; } - PasswordHash getPasswordHash() const { return PasswordHash {_passwordSalt, _passwordHash}; } - Wt::WDateTime getLastLogin() const { return _lastLogin; } - std::size_t getAuthTokensCount() const { return _authTokens.size(); } + const std::string& getLoginName() const { return _loginName; } + PasswordHash getPasswordHash() const { return PasswordHash {_passwordSalt, _passwordHash}; } + const Wt::WDateTime& getLastLogin() const { return _lastLogin; } + std::size_t getAuthTokensCount() const { return _authTokens.size(); } // write void setLastLogin(const Wt::WDateTime& dateTime) { _lastLogin = dateTime; } @@ -165,21 +103,6 @@ class User : public Object Scrobbler getScrobbler() const { return _scrobbler; } std::optional getListenBrainzToken() const { return UUID::fromString(_listenbrainzToken); } - ObjectPtr getQueuedTrackList(Session& session) const; - - void star(ObjectPtr artist); - void unstar(ObjectPtr artist); - bool isStarred(ObjectPtr artist) const; - - void star(ObjectPtr release); - void unstar(ObjectPtr release); - bool isStarred(ObjectPtr release) const; - - // Stars - void star(ObjectPtr track); - void unstar(ObjectPtr track); - bool isStarred(ObjectPtr track) const; - template void persist(Action& a) { @@ -196,20 +119,15 @@ class User : public Object Wt::Dbo::field(a, _scrobbler, "scrobbler"); Wt::Dbo::field(a, _listenbrainzToken, "listenbrainz_token"); - // UI settings + // UI player settings Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos"); Wt::Dbo::field(a, _repeatAll, "repeat_all"); Wt::Dbo::field(a, _radio, "radio"); - Wt::Dbo::hasMany(a, _tracklists, Wt::Dbo::ManyToOne, "user"); - Wt::Dbo::hasMany(a, _starredArtists, Wt::Dbo::ManyToMany, "user_artist_starred", "", Wt::Dbo::OnDeleteCascade); - Wt::Dbo::hasMany(a, _starredReleases, Wt::Dbo::ManyToMany, "user_release_starred", "", Wt::Dbo::OnDeleteCascade); - Wt::Dbo::hasMany(a, _starredTracks, Wt::Dbo::ManyToMany, "user_track_starred", "", Wt::Dbo::OnDeleteCascade); Wt::Dbo::hasMany(a, _authTokens, Wt::Dbo::ManyToOne, "user"); } private: - std::string _loginName; std::string _passwordSalt; std::string _passwordHash; @@ -228,16 +146,11 @@ class User : public Object int _subsonicTranscodeBitrate {defaultSubsonicTranscodeBitrate}; // User's dynamic data (UI) - int _curPlayingTrackPos {}; // Current track position in queue + int _curPlayingTrackPos {}; // Current track position in queue bool _repeatAll {}; bool _radio {}; - Wt::Dbo::collection> _tracklists; - Wt::Dbo::collection> _starredArtists; - Wt::Dbo::collection> _starredReleases; - Wt::Dbo::collection> _starredTracks; Wt::Dbo::collection> _authTokens; - }; } // namespace Databas' diff --git a/src/libs/services/database/include/services/database/UserId.hpp b/src/libs/services/database/include/services/database/UserId.hpp new file mode 100644 index 00000000..c337f8fa --- /dev/null +++ b/src/libs/services/database/include/services/database/UserId.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(UserId) + diff --git a/src/libs/services/database/test/Artist.cpp b/src/libs/services/database/test/Artist.cpp index d94c6daf..81dd3771 100644 --- a/src/libs/services/database/test/Artist.cpp +++ b/src/libs/services/database/test/Artist.cpp @@ -21,13 +21,14 @@ using namespace Database; -TEST_F(DatabaseFixture, SingleArtist) +TEST_F(DatabaseFixture, Artist) { { auto transaction {session.createSharedTransaction()}; EXPECT_FALSE(Artist::exists(session, 35)); EXPECT_FALSE(Artist::exists(session, 0)); EXPECT_FALSE(Artist::exists(session, 1)); + EXPECT_EQ(Artist::getCount(session), 0); } ScopedArtist artist {session, "MyArtist"}; @@ -40,22 +41,23 @@ TEST_F(DatabaseFixture, SingleArtist) EXPECT_EQ(artist.get()->getId(), artist.getId()); EXPECT_TRUE(Artist::exists(session, artist.getId())); + EXPECT_EQ(Artist::getCount(session), 1); } { auto transaction {session.createSharedTransaction()}; - auto artists {Artist::getAll(session, Artist::SortMethod::ByName)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + auto artists {Artist::find(session, Artist::FindParameters {})}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - artists = Artist::getAllOrphans(session); - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + artists = Artist::findAllOrphans(session, Range {}); + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); } } -TEST_F(DatabaseFixture, SingleTrackSingleArtist) +TEST_F(DatabaseFixture, Artist_singleTrack) { ScopedTrack track {session, "MyTrack"}; ScopedArtist artist {session, "MyArtist"}; @@ -68,7 +70,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtist) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); } { @@ -102,7 +104,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtist) } } -TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRoles) +TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles) { ScopedTrack track {session, "MyTrack"}; ScopedArtist artist {session, "MyArtist"}; @@ -116,17 +118,16 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRoles) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); } { auto transaction {session.createSharedTransaction()}; - bool hasMore{}; - EXPECT_EQ(Artist::getByFilter(session, {}, {}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, hasMore).size(), 1); - EXPECT_EQ(Artist::getByFilter(session, {}, {}, TrackArtistLinkType::Artist, Artist::SortMethod::ByName, std::nullopt, hasMore).size(), 1); - EXPECT_EQ(Artist::getByFilter(session, {}, {}, TrackArtistLinkType::ReleaseArtist, Artist::SortMethod::ByName, std::nullopt, hasMore).size(), 1); - EXPECT_EQ(Artist::getByFilter(session, {}, {}, TrackArtistLinkType::Writer, Artist::SortMethod::ByName, std::nullopt, hasMore).size(), 1); - EXPECT_TRUE(Artist::getByFilter(session, {}, {}, TrackArtistLinkType::Composer, Artist::SortMethod::ByName, std::nullopt, hasMore).empty()); + EXPECT_EQ(Artist::find(session, Artist::FindParameters {}).results.size(), 1); + EXPECT_EQ(Artist::find(session, Artist::FindParameters {}.setLinkType(TrackArtistLinkType::Artist)).results.size(), 1); + EXPECT_EQ(Artist::find(session, Artist::FindParameters {}.setLinkType(TrackArtistLinkType::ReleaseArtist)).results.size(), 1); + EXPECT_EQ(Artist::find(session, Artist::FindParameters {}.setLinkType(TrackArtistLinkType::Writer)).results.size(), 1); + EXPECT_EQ(Artist::find(session, Artist::FindParameters {}.setLinkType(TrackArtistLinkType::Composer)).results.size(), 0); } { @@ -149,7 +150,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRoles) } } -TEST_F(DatabaseFixture,SingleTrackMultiArtists) +TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists) { ScopedTrack track {session, "track"}; ScopedArtist artist1 {session, "artist1"}; @@ -165,7 +166,7 @@ TEST_F(DatabaseFixture,SingleTrackMultiArtists) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); } { @@ -179,8 +180,8 @@ TEST_F(DatabaseFixture,SingleTrackMultiArtists) EXPECT_EQ(track->getArtists({}).size(), 2); EXPECT_EQ(track->getArtists({TrackArtistLinkType::Artist}).size(), 2); EXPECT_TRUE(track->getArtists({TrackArtistLinkType::ReleaseArtist}).empty()); - EXPECT_EQ(Artist::getAll(session, Artist::SortMethod::ByName).size(), 2); - EXPECT_EQ(Artist::getAllIds(session).size(), 2); + EXPECT_EQ(Artist::find(session, Artist::FindParameters {}).results.size(), 2); + EXPECT_EQ(Artist::find(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::Random)).results.size(), 2); } { @@ -196,7 +197,7 @@ TEST_F(DatabaseFixture,SingleTrackMultiArtists) } } -TEST_F(DatabaseFixture, SingleArtistSearchByName) +TEST_F(DatabaseFixture, Artist_findByName) { ScopedArtist artist {session, "AAA"}; ScopedTrack track {session, "MyTrack"}; // filters does not work on orphans @@ -210,22 +211,22 @@ TEST_F(DatabaseFixture, SingleArtistSearchByName) { auto transaction {session.createSharedTransaction()}; - bool more {}; - EXPECT_TRUE(Artist::getByFilter(session, {}, {"N"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more).empty()); + EXPECT_TRUE(Artist::find(session, Artist::FindParameters {}.setKeywords({"N"})).results.empty()); - const auto artistsByAAA {Artist::Artist::getByFilter(session, {}, {"A"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more)}; - ASSERT_EQ(artistsByAAA.size(), 1); - EXPECT_EQ(artistsByAAA.front()->getId(), artist.getId()); + const auto artistsByAAA {Artist::find(session, Artist::FindParameters {}.setKeywords({"A"}))}; + ASSERT_EQ(artistsByAAA.results.size(), 1); + EXPECT_EQ(artistsByAAA.results.front(), artist.getId()); - const auto artistsByZZZ {Artist::Artist::getByFilter(session, {}, {"Z"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more)}; - ASSERT_EQ(artistsByZZZ.size(), 1); - EXPECT_EQ(artistsByZZZ.front()->getId(), artist.getId()); + const auto artistsByZZZ {Artist::Artist::find(session, Artist::FindParameters {}.setKeywords({"Z"}))}; + ASSERT_EQ(artistsByZZZ.results.size(), 1); + EXPECT_EQ(artistsByZZZ.results.front(), artist.getId()); - EXPECT_TRUE(Artist::getByName(session, "NNN").empty()); + EXPECT_TRUE(Artist::find(session, "NNN").empty()); + EXPECT_EQ(Artist::find(session, "AAA").size(), 1); } } -TEST_F(DatabaseFixture, MultipleArtistsSearchByNameEscaped) +TEST_F(DatabaseFixture, Artist_findByNameEscaped) { ScopedArtist artist1 {session, "MyArtist%"}; ScopedArtist artist2 {session, "%MyArtist"}; @@ -238,70 +239,55 @@ TEST_F(DatabaseFixture, MultipleArtistsSearchByNameEscaped) { auto transaction {session.createSharedTransaction()}; { - const auto artists {Artist::getByName(session, "MyArtist%")}; + const auto artists {Artist::find(session, "MyArtist%")}; ASSERT_TRUE(artists.size() == 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); - EXPECT_TRUE(Artist::getByName(session, "MyArtistFoo").empty()); + EXPECT_TRUE(Artist::find(session, "MyArtistFoo").empty()); } { - const auto artists {Artist::getByName(session, "%MyArtist")}; + const auto artists {Artist::find(session, "%MyArtist")}; ASSERT_TRUE(artists.size() == 1); EXPECT_EQ(artists.front()->getId(), artist2.getId()); - EXPECT_TRUE(Artist::getByName(session, "FooMyArtist").empty()); + EXPECT_TRUE(Artist::find(session, "FooMyArtist").empty()); } { - const auto artists {Artist::getByName(session, "%_MyArtist")}; + const auto artists {Artist::find(session, "%_MyArtist")}; ASSERT_TRUE(artists.size() == 1); ASSERT_EQ(artists.front()->getId(), artist3.getId()); - EXPECT_TRUE(Artist::getByName(session, "%CMyArtist").empty()); + EXPECT_TRUE(Artist::find(session, "%CMyArtist").empty()); } } - // get by filter only works with tracks links... - ScopedTrack track {session, "MyTrack"}; // filters does not work on orphans - - { - auto transaction {session.createUniqueTransaction()}; - - TrackArtistLink::create(session, track.get(), artist1.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track.get(), artist2.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track.get(), artist3.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track.get(), artist4.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track.get(), artist5.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track.get(), artist6.get(), TrackArtistLinkType::Artist); - } - { auto transaction {session.createSharedTransaction()}; - bool more; { - const auto artists {Artist::getByFilter(session, {}, {"MyArtist"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more)}; - EXPECT_EQ(artists.size(), 6); + const auto artists {Artist::find(session, Artist::FindParameters {}.setKeywords({"MyArtist"}))}; + EXPECT_EQ(artists.results.size(), 6); } { - const auto artists {Artist::getByFilter(session, {}, {"MyArtist%"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist1.getId()); - EXPECT_EQ(artists[1]->getId(), artist4.getId()); + const auto artists {Artist::find(session, Artist::FindParameters {}.setKeywords({"MyArtist%"}).setSortMethod(ArtistSortMethod::ByName))}; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], artist1.getId()); + EXPECT_EQ(artists.results[1], artist4.getId()); } { - const auto artists {Artist::getByFilter(session, {}, {"%MyArtist"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist2.getId()); - EXPECT_EQ(artists[1]->getId(), artist5.getId()); + const auto artists {Artist::find(session, Artist::FindParameters {}.setKeywords({"%MyArtist"}).setSortMethod(ArtistSortMethod::ByName))}; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], artist2.getId()); + EXPECT_EQ(artists.results[1], artist5.getId()); } { - const auto artists {Artist::getByFilter(session, {}, {"_MyArtist"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists[0]->getId(), artist3.getId()); + const auto artists {Artist::find(session, Artist::FindParameters {}.setKeywords({"_MyArtist"}).setSortMethod(ArtistSortMethod::ByName))}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results[0], artist3.getId()); } } } -TEST_F(DatabaseFixture, MultiArtistsSortMethod) +TEST_F(DatabaseFixture, Artist_sortMethod) { ScopedArtist artistA {session, "artistA"}; ScopedArtist artistB {session, "artistB"}; @@ -316,20 +302,20 @@ TEST_F(DatabaseFixture, MultiArtistsSortMethod) { auto transaction {session.createSharedTransaction()}; - auto allArtistsByName {Artist::getAll(session, Artist::SortMethod::ByName)}; - auto allArtistsBySortName {Artist::getAll(session, Artist::SortMethod::BySortName)}; + auto allArtistsByName {Artist::find(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::ByName))}; + auto allArtistsBySortName {Artist::find(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName))}; - ASSERT_EQ(allArtistsByName.size(), 2); - EXPECT_EQ(allArtistsByName.front()->getId(), artistA.getId()); - EXPECT_EQ(allArtistsByName.back()->getId(), artistB.getId()); + ASSERT_EQ(allArtistsByName.results.size(), 2); + EXPECT_EQ(allArtistsByName.results.front(), artistA.getId()); + EXPECT_EQ(allArtistsByName.results.back(), artistB.getId()); - ASSERT_EQ(allArtistsBySortName.size(), 2); - EXPECT_EQ(allArtistsBySortName.front()->getId(), artistB.getId()); - EXPECT_EQ(allArtistsBySortName.back()->getId(), artistA.getId()); + ASSERT_EQ(allArtistsBySortName.results.size(), 2); + EXPECT_EQ(allArtistsBySortName.results.front(), artistB.getId()); + EXPECT_EQ(allArtistsBySortName.results.back(), artistA.getId()); } } -TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) +TEST_F(DatabaseFixture, Artist_nonReleaseTracks) { ScopedArtist artist {session, "artist"}; ScopedTrack track1 {session, "MyTrack1"}; @@ -340,9 +326,8 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) auto transaction {session.createSharedTransaction()}; EXPECT_FALSE(artist->hasNonReleaseTracks(std::nullopt)); - bool moreResults; - const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )}; - EXPECT_EQ(tracks.size(), 0); + const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {})}; + EXPECT_EQ(tracks.results.size(), 0); } { @@ -358,44 +343,11 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) { auto transaction {session.createSharedTransaction()}; - bool moreResults; - const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )}; + const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {})}; EXPECT_TRUE(artist->hasNonReleaseTracks(std::nullopt)); - EXPECT_FALSE(moreResults); - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track2.getId()); - } -} - -TEST_F(DatabaseFixture, SingleStarredArtist) -{ - ScopedArtist artist {session, "MyArtist"}; - ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - - { - auto transaction {session.createUniqueTransaction()}; - - EXPECT_FALSE(user->isStarred(artist.get())); - } - - { - auto transaction {session.createUniqueTransaction()}; - - auto trackArtistLink {TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist)}; - user.get().modify()->star(artist.get()); - } - - { - auto transaction {session.createSharedTransaction()}; - - EXPECT_TRUE(user->isStarred(artist.get())); - - bool hasMore {}; - auto artists {Artist::getStarred(session, user.get(), {}, std::nullopt, Artist::SortMethod::BySortName, std::nullopt, hasMore)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); - EXPECT_FALSE(hasMore); + EXPECT_FALSE(tracks.moreResults); + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results.front()->getId(), track2.getId()); } } diff --git a/src/libs/services/database/test/CMakeLists.txt b/src/libs/services/database/test/CMakeLists.txt index 605c3af5..99bbf7b7 100644 --- a/src/libs/services/database/test/CMakeLists.txt +++ b/src/libs/services/database/test/CMakeLists.txt @@ -2,9 +2,17 @@ add_executable(test-database Artist.cpp Cluster.cpp + Common.cpp DatabaseTest.cpp + Listen.cpp Release.cpp + StarredArtist.cpp + StarredRelease.cpp + StarredTrack.cpp Track.cpp + TrackBookmark.cpp + TrackFeatures.cpp + TrackList.cpp ) target_link_libraries(test-database PRIVATE diff --git a/src/libs/services/database/test/Cluster.cpp b/src/libs/services/database/test/Cluster.cpp index aba28a41..df6c677d 100644 --- a/src/libs/services/database/test/Cluster.cpp +++ b/src/libs/services/database/test/Cluster.cpp @@ -24,60 +24,73 @@ using namespace Database; -TEST_F(DatabaseFixture, SingleCluster) +TEST_F(DatabaseFixture, Cluster) { + { + auto transaction {session.createUniqueTransaction()}; + EXPECT_EQ(Cluster::getCount(session), 0); + EXPECT_EQ(ClusterType::getCount(session), 0); + } + ScopedClusterType clusterType {session, "MyType"}; + { + auto transaction {session.createUniqueTransaction()}; + EXPECT_EQ(ClusterType::getCount(session), 1); + } + { ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; { auto transaction {session.createUniqueTransaction()}; - auto clusters {Cluster::getAll(session)}; - ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front()->getId(), cluster.getId()); - EXPECT_EQ(clusters.front()->getType()->getId(), clusterType.getId()); + EXPECT_EQ(Cluster::getCount(session), 1); + EXPECT_EQ(cluster->getType()->getId(), clusterType.getId()); - clusters = Cluster::getAllOrphans(session); - ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front()->getId(), cluster.getId()); + auto clusters {Cluster::find(session, Range {})}; + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), cluster.getId()); - auto clusterTypes {ClusterType::getAll(session)}; - ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); + clusters = Cluster::findOrphans(session, Range {}); + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), cluster.getId()); - clusterTypes = ClusterType::getAllUsed(session); - ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); + auto clusterTypes {ClusterType::find(session, Range {})}; + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); - clusterTypes = ClusterType::getAllOrphans(session); - EXPECT_TRUE(clusterTypes.empty()); + clusterTypes = ClusterType::findUsed(session, Range {}); + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); + + clusterTypes = ClusterType::findOrphans(session, Range {}); + EXPECT_TRUE(clusterTypes.results.empty()); } } { auto transaction {session.createUniqueTransaction()}; - auto clusterTypes {ClusterType::getAllOrphans(session)}; - ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); + auto clusterTypes {ClusterType::findOrphans(session, Range {})}; + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); - ASSERT_TRUE(ClusterType::getAllUsed(session).empty()); + ASSERT_TRUE(ClusterType::findUsed(session, Range {}).results.empty()); } } -TEST_F(DatabaseFixture, SingleTrackSingleCluster) +TEST_F(DatabaseFixture, Cluster_singleTrack) { ScopedTrack track {session, "MyTrack"}; ScopedClusterType clusterType {session, "MyClusterType"}; { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::getAllOrphans(session).empty()); - auto clusterTypes {ClusterType::getAllOrphans(session)}; - ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); + EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); + auto clusterTypes {ClusterType::findOrphans(session, Range {})}; + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); } ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; @@ -85,17 +98,12 @@ TEST_F(DatabaseFixture, SingleTrackSingleCluster) { auto transaction {session.createSharedTransaction()}; - auto clusters {Cluster::getAllOrphans(session)}; - EXPECT_EQ(clusters.size(), 2); + auto clusters {Cluster::findOrphans(session, Range {})}; + EXPECT_EQ(clusters.results.size(), 2); EXPECT_TRUE(track->getClusters().empty()); EXPECT_TRUE(track->getClusterIds().empty()); } - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Track::getAllIdsWithClusters(session).empty()); - } - { auto transaction {session.createUniqueTransaction()}; @@ -104,29 +112,22 @@ TEST_F(DatabaseFixture, SingleTrackSingleCluster) { auto transaction {session.createSharedTransaction()}; - auto tracks {Track::getAllIdsWithClusters(session)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front(), track.getId()); - } + auto clusters {Cluster::findOrphans(session, Range {})}; + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), cluster2.getId()); - { - auto transaction {session.createSharedTransaction()}; - auto clusters {Cluster::getAllOrphans(session)}; - ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front()->getId(), cluster2.getId()); - - EXPECT_TRUE(ClusterType::getAllOrphans(session).empty()); + EXPECT_TRUE(ClusterType::findOrphans(session, Range {}).results.empty()); } { auto transaction {session.createSharedTransaction()}; - auto tracks {Track::getByClusters(session, {cluster1.getId()})}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track.getId()); + auto tracks {Track::find(session, Track::FindParameters {}.setClusters({cluster1.getId()}))}; + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results.front(), track.getId()); - tracks = Track::getByClusters(session, {cluster2.getId()}); - EXPECT_TRUE(tracks.empty()); + tracks = Track::find(session, Track::FindParameters {}.setClusters({cluster2.getId()})); + EXPECT_TRUE(tracks.results.empty()); } { @@ -142,7 +143,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleCluster) } } -TEST_F(DatabaseFixture, MultipleTracksSingleCluster) +TEST_F(DatabaseFixture, Cluster_multiTracks) { std::list tracks; ScopedClusterType clusterType {session, "MyClusterType"}; @@ -160,19 +161,19 @@ TEST_F(DatabaseFixture, MultipleTracksSingleCluster) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::getAllOrphans(session).empty()); + EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); EXPECT_EQ(cluster->getTracksCount(), tracks.size()); - for (auto trackCluster : cluster->getTracks()) + for (TrackId trackId : cluster->getTracks(Range {}).results) { - auto it {std::find_if(std::cbegin(tracks), std::cend(tracks), [&](const ScopedTrack& track) { return trackCluster->getId() == track.getId(); })}; + auto it {std::find_if(std::cbegin(tracks), std::cend(tracks), [&](const ScopedTrack& track) { return trackId == track.getId(); })}; EXPECT_TRUE(it != std::cend(tracks)); } } } -TEST_F(DatabaseFixture, MultipleTracksMultipleClustersTopRelease) +TEST_F(DatabaseFixture, Cluster_multiTracksMultipleClustersTopRelease) { ScopedClusterType clusterType {session, "ClusterType"}; ScopedCluster cluster1 {session, clusterType.lockAndGet(), "Cluster1"}; @@ -268,16 +269,25 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersTopRelease) } } -TEST_F(DatabaseFixture,SingleTrackSingleReleaseSingleCluster) +TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) { ScopedTrack track {session, "MyTrackFile"}; ScopedRelease release {session, "MyRelease"}; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster {session, clusterType .lockAndGet(), "MyCluster"}; { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Release::getAllIdsWithClusters(session).empty()); + EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); + } + + ScopedClusterType clusterType {session, "MyClusterType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + ScopedCluster unusedCluster {session, clusterType.lockAndGet(), "MyClusterUnused"}; + + { + auto transaction {session.createSharedTransaction()}; + ASSERT_EQ(Cluster::findOrphans(session, Range {}).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); } { @@ -289,24 +299,27 @@ TEST_F(DatabaseFixture,SingleTrackSingleReleaseSingleCluster) { auto transaction {session.createSharedTransaction()}; - auto releases {Release::getAllIdsWithClusters(session)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front(), release.getId()); + + { + auto clusters {Cluster::findOrphans(session, Range {})}; + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), unusedCluster.getId()); + } } { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::getAllOrphans(session).empty()); - EXPECT_TRUE(Release::getAllOrphans(session).empty()); + auto releases {Release::find(session, Release::FindParameters {}.setClusters({cluster.getId()}))}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); } { auto transaction {session.createSharedTransaction()}; - auto releases {Release::getByClusters(session, {cluster.getId()})}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + auto releases {Release::find(session, Release::FindParameters {}.setClusters({unusedCluster.getId()}))}; + EXPECT_EQ(releases.results.size(), 0); } { @@ -314,6 +327,8 @@ TEST_F(DatabaseFixture,SingleTrackSingleReleaseSingleCluster) EXPECT_EQ(cluster->getReleasesCount(), 1); EXPECT_EQ(cluster->getTracksCount(), 1); + EXPECT_EQ(unusedCluster->getReleasesCount(), 0); + EXPECT_EQ(unusedCluster->getTracksCount(), 0); } } @@ -334,10 +349,10 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(ClusterType::getAllOrphans(session).empty()); - EXPECT_EQ(Cluster::getAllOrphans(session).size(), 2); - EXPECT_TRUE(Release::getAllOrphans(session).empty()); - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); + EXPECT_TRUE(ClusterType::findOrphans(session, Range {}).results.empty()); + EXPECT_EQ(Cluster::findOrphans(session, Range {}).results.size(), 2); + EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); } { @@ -349,12 +364,12 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { auto transaction {session.createSharedTransaction()}; - auto artists {Artist::getByClusters(session, {cluster1.getId()}, Artist::SortMethod::ByName)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()}))}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - EXPECT_TRUE(Artist::getByClusters(session, {cluster2.getId()}, Artist::SortMethod::ByName).empty()); - EXPECT_TRUE(Artist::getByClusters(session, {cluster3.getId()}, Artist::SortMethod::ByName).empty()); + EXPECT_TRUE(Artist::find(session, Artist::FindParameters {}.setClusters({cluster2.getId()})).results.empty()); + EXPECT_TRUE(Artist::find(session, Artist::FindParameters {}.setClusters({cluster3.getId()})).results.empty()); cluster2.get().modify()->addTrack(track.get()); } @@ -362,19 +377,19 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { auto transaction {session.createSharedTransaction()}; - auto artists {Artist::getByClusters(session, {cluster1.getId()}, Artist::SortMethod::ByName)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()}))}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - artists = Artist::getByClusters(session, {cluster2.getId()}, Artist::SortMethod::ByName); - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + artists = Artist::find(session, Artist::FindParameters {}.setClusters({cluster2.getId()})); + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - artists = Artist::getByClusters(session, {cluster1.getId(), cluster2.getId()}, Artist::SortMethod::ByName); - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + artists = Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()})); + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - EXPECT_TRUE(Artist::getByClusters(session, {cluster3.getId()}, Artist::SortMethod::ByName).empty()); + EXPECT_TRUE(Artist::find(session, Artist::FindParameters {}.setClusters({cluster3.getId()})).results.empty()); } } @@ -395,17 +410,17 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::getAllOrphans(session).empty()); - EXPECT_TRUE(Release::getAllOrphans(session).empty()); - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); + EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); } { auto transaction {session.createSharedTransaction()}; - auto artists {Artist::getByClusters(session, {cluster.getId()}, Artist::SortMethod::ByName)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()}))}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); } } @@ -435,8 +450,8 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::getAllOrphans(session).empty()); - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); + EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); } { @@ -445,9 +460,9 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) std::vector clusterIds; std::transform(std::cbegin(clusters), std::cend(clusters), std::back_inserter(clusterIds), [](const ScopedCluster& cluster) { return cluster.getId(); }); - auto artists {Artist::getByClusters(session, clusterIds, Artist::SortMethod::ByName)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + auto artists {Artist::find(session, Artist::FindParameters {}.setClusters(clusterIds))}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); } } @@ -471,15 +486,11 @@ TEST_F(DatabaseFixture, MultipleTracksSingleClusterSimilarity) { auto transaction {session.createSharedTransaction()}; - const auto similarTracks {Track::getSimilarTracks(session, {tracks.front().getId()})}; - EXPECT_EQ(similarTracks.size(), tracks.size() - 1); - for (auto similarTrack : similarTracks) + const auto similarTracks {Track::findSimilarTracks(session, {tracks.front().getId()}, Range {})}; + EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); + for (const TrackId similarTrackId : similarTracks.results) { - EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), - [&](const ScopedTrack& track) - { - return similarTrack->getId() == track.getId(); - }) != std::cend(tracks)); + EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); } } } @@ -516,17 +527,17 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersSimilarity) auto transaction {session.createSharedTransaction()}; { - auto similarTracks {Track::getSimilarTracks(session, {tracks.back().getId()}, 0, 4)}; - EXPECT_EQ(similarTracks.size(), 4); - for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 5), std::next(std::cend(tracks), -1), [&](const ScopedTrack& track) { return similarTrack->getId() == track.getId(); }) != std::cend(tracks)); + auto similarTracks {Track::findSimilarTracks(session, {tracks.back().getId()}, Range {0, 4})}; + EXPECT_EQ(similarTracks.results.size(), 4); + for (const TrackId similarTrackId : similarTracks.results) + EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 5), std::next(std::cend(tracks), -1), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); } { - auto similarTracks {Track::getSimilarTracks(session, {tracks.front().getId()})}; - EXPECT_EQ(similarTracks.size(), tracks.size() - 1); - for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const ScopedTrack& track) { return similarTrack->getId() == track.getId(); }) != std::cend(tracks)); + auto similarTracks {Track::findSimilarTracks(session, {tracks.front().getId()}, Range {})}; + EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); + for (const TrackId similarTrackId : similarTracks.results) + EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); } } } @@ -539,11 +550,6 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) ScopedClusterType clusterType {session, "MyType"}; ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Artist::getAllIdsWithClusters(session).empty()); - } - { auto transaction {session.createUniqueTransaction()}; @@ -555,25 +561,18 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::getAllOrphans(session).empty()); - EXPECT_TRUE(ClusterType::getAllOrphans(session).empty()); - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); - EXPECT_TRUE(Release::getAllOrphans(session).empty()); - } - - { - auto transaction {session.createSharedTransaction()}; - auto artists {Artist::getAllIdsWithClusters(session)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front(), artist.getId()); + EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(ClusterType::findOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); } { auto transaction {session.createSharedTransaction()}; - auto artists {Artist::getByClusters(session, {cluster.getId()}, Artist::SortMethod::ByName)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist.getId()); + auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()}))}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); auto releases {artist->getReleases()}; ASSERT_EQ(releases.size(), 1); @@ -735,9 +734,9 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - EXPECT_TRUE(trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getReleasesReverse({}, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getTracksReverse({}, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); } { @@ -750,15 +749,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size() , 1); EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); EXPECT_EQ(tracks.front()->getId(), track1.getId()); } @@ -767,15 +766,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesReverse({cluster1.getId()}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksReverse({cluster1.getId()}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); EXPECT_EQ(tracks.front()->getId(), track1.getId()); } @@ -784,15 +783,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesReverse({cluster3.getId()}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksReverse({cluster3.getId()}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); EXPECT_EQ(tracks.front()->getId(), track1.getId()); } @@ -801,7 +800,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({cluster1.getId()}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); } @@ -810,7 +809,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); } @@ -819,11 +818,11 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - EXPECT_TRUE(trackList->getArtistsReverse({cluster2.getId()}, std::nullopt, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getReleasesReverse({cluster2.getId()}, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getTracksReverse({cluster2.getId()}, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({cluster2.getId()}, std::nullopt, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getReleasesOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getTracksOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getArtistsReverse({}, TrackArtistLinkType::ReleaseArtist, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({}, TrackArtistLinkType::ReleaseArtist, std::nullopt, moreResults).empty()); } { @@ -836,17 +835,17 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); EXPECT_EQ(artists[0]->getId(), artist2.getId()); EXPECT_EQ(artists[1]->getId(), artist1.getId()); - const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 2); EXPECT_EQ(releases[0]->getId(), release2.getId()); EXPECT_EQ(releases[1]->getId(), release1.getId()); - const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 2); EXPECT_EQ(tracks[0]->getId(), track2.getId()); EXPECT_EQ(tracks[1]->getId(),track1.getId()); @@ -856,17 +855,17 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); EXPECT_EQ(artists[0]->getId(), artist2.getId()); EXPECT_EQ(artists[1]->getId(), artist1.getId()); - const auto releases {trackList->getReleasesReverse({cluster3.getId()}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 2); EXPECT_EQ(releases[0]->getId(), release2.getId()); EXPECT_EQ(releases[1]->getId(), release1.getId()); - const auto tracks {trackList->getTracksReverse({cluster3.getId()}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 2); EXPECT_EQ(tracks[0]->getId(), track2.getId()); EXPECT_EQ(tracks[1]->getId(), track1.getId()); @@ -876,15 +875,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesReverse({cluster1.getId()}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksReverse({cluster1.getId()}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); EXPECT_EQ(tracks.front()->getId(), track1.getId()); } @@ -893,15 +892,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({cluster2.getId()}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster2.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); EXPECT_EQ(artists.front()->getId(), artist2.getId()); - const auto releases {trackList->getReleasesReverse({cluster2.getId()}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); EXPECT_EQ(releases.front()->getId(), release2.getId()); - const auto tracks {trackList->getTracksReverse({cluster2.getId()}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); EXPECT_EQ(tracks.front()->getId(), track2.getId()); } @@ -916,17 +915,17 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); EXPECT_EQ(artists[0]->getId(), artist1.getId()); EXPECT_EQ(artists[1]->getId(), artist2.getId()); - const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 2); EXPECT_EQ(releases[0]->getId(), release1.getId()); EXPECT_EQ(releases[1]->getId(), release2.getId()); - const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 2); EXPECT_EQ(tracks[0]->getId(), track1.getId()); EXPECT_EQ(tracks[1]->getId(), track2.getId()); @@ -936,17 +935,17 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) auto transaction {session.createSharedTransaction()}; bool moreResults {}; - const auto artists {trackList->getArtistsReverse({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); EXPECT_EQ(artists[0]->getId(), artist1.getId()); EXPECT_EQ(artists[1]->getId(), artist2.getId()); - const auto releases {trackList->getReleasesReverse({cluster3.getId()}, std::nullopt, moreResults)}; + const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 2); EXPECT_EQ(releases[0]->getId(), release1.getId()); EXPECT_EQ(releases[1]->getId(), release2.getId()); - const auto tracks {trackList->getTracksReverse({cluster3.getId()}, std::nullopt, moreResults)}; + const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 2); EXPECT_EQ(tracks[0]->getId(), track1.getId()); EXPECT_EQ(tracks[1]->getId(), track2.getId()); @@ -964,9 +963,9 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(artist1->getSimilarArtists().empty()); - EXPECT_TRUE(artist2->getSimilarArtists().empty()); - EXPECT_TRUE(artist3->getSimilarArtists().empty()); + EXPECT_TRUE(artist1->findSimilarArtists().results.empty()); + EXPECT_TRUE(artist2->findSimilarArtists().results.empty()); + EXPECT_TRUE(artist3->findSimilarArtists().results.empty()); } std::list tracks; @@ -998,38 +997,38 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) auto transaction {session.createSharedTransaction()}; { - auto artists {artist1->getSimilarArtists()}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist2.getId()); + auto artists {artist1->findSimilarArtists()}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist2.getId()); } { - auto artists {artist1->getSimilarArtists({TrackArtistLinkType::Artist})}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist2.getId()); + auto artists {artist1->findSimilarArtists({TrackArtistLinkType::Artist})}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist2.getId()); } { - auto artists {artist1->getSimilarArtists({TrackArtistLinkType::ReleaseArtist})}; - EXPECT_EQ(artists.empty(), 1); + auto artists {artist1->findSimilarArtists({TrackArtistLinkType::ReleaseArtist})}; + EXPECT_EQ(artists.results.empty(), 1); } { - auto artists {artist1->getSimilarArtists({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist})}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist2.getId()); + auto artists {artist1->findSimilarArtists({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist})}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist2.getId()); } { - auto artists {artist1->getSimilarArtists({TrackArtistLinkType::Composer})}; - EXPECT_TRUE(artists.empty()); + auto artists {artist1->findSimilarArtists({TrackArtistLinkType::Composer})}; + EXPECT_TRUE(artists.results.empty()); } { - auto artists {artist2->getSimilarArtists()}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist1.getId()); - EXPECT_EQ(artists[1]->getId(), artist3.getId()); + auto artists {artist2->findSimilarArtists()}; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], artist1.getId()); + EXPECT_EQ(artists.results[1], artist3.getId()); } } } diff --git a/src/libs/services/database/test/Common.cpp b/src/libs/services/database/test/Common.cpp new file mode 100644 index 00000000..033aa754 --- /dev/null +++ b/src/libs/services/database/test/Common.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2021 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 "Common.hpp" + +#include "services/database/Artist.hpp" +#include "services/database/Cluster.hpp" +#include "services/database/Db.hpp" +#include "services/database/Listen.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/StarredArtist.hpp" +#include "services/database/StarredRelease.hpp" +#include "services/database/StarredTrack.hpp" +#include "services/database/Track.hpp" +#include "services/database/TrackArtistLink.hpp" +#include "services/database/TrackBookmark.hpp" +#include "services/database/TrackList.hpp" +#include "services/database/Types.hpp" +#include "services/database/User.hpp" + +TmpDatabase::TmpDatabase() +: _tmpFile {std::tmpnam(nullptr)} +, _fileDeleter {_tmpFile} +, _db {_tmpFile} +{ +} + +Database::Db& +TmpDatabase::getDb() +{ + return _db; +} + +DatabaseFixture::~DatabaseFixture() +{ + testDatabaseEmpty(); +} + +void +DatabaseFixture::SetUpTestCase() +{ + _tmpDb = std::make_unique(); + { + Database::Session s {_tmpDb->getDb()}; + s.prepareTables(); + s.optimize(); + + // remove default created entries + { + auto transaction {s.createUniqueTransaction()}; + auto clusterTypes {Database::ClusterType::find(s, Database::Range {})}; + for (auto clusterTypeId : clusterTypes.results) + { + auto clusterType {Database::ClusterType::find(s, clusterTypeId)}; + clusterType.remove(); + } + } + } +} + +void +DatabaseFixture::TearDownTestCase() +{ + _tmpDb.reset(); +} + +void +DatabaseFixture::testDatabaseEmpty() +{ + using namespace Database; + + auto uniqueTransaction {session.createUniqueTransaction()}; + + EXPECT_EQ(Artist::getCount(session), 0); + EXPECT_EQ(Cluster::getCount(session), 0); + EXPECT_EQ(ClusterType::getCount(session), 0); + EXPECT_EQ(Listen::getCount(session), 0); + EXPECT_EQ(Release::getCount(session), 0); + EXPECT_EQ(StarredArtist::getCount(session), 0); + EXPECT_EQ(StarredRelease::getCount(session), 0); + EXPECT_EQ(StarredTrack::getCount(session), 0); + EXPECT_EQ(Track::getCount(session), 0); + EXPECT_EQ(TrackBookmark::getCount(session), 0); + EXPECT_EQ(TrackList::getCount(session), 0); + EXPECT_EQ(User::getCount(session), 0); +} + +TEST_F(DatabaseFixture, Common_subRangeEmpty) +{ + using namespace Database; + + RangeResults results; + results.range = Range {0, 0}; + results.results = {}; + results.moreResults = false; + + { + auto subRange {results.getSubRange(Range {0, 0})}; + EXPECT_FALSE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 0); + EXPECT_EQ(subRange.range, Range {}); + } + { + auto subRange {results.getSubRange(Range {0, 1})}; + EXPECT_FALSE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, Common_IdType) +{ + using namespace Database; + + { + const IdType id{}; + EXPECT_FALSE(id.isValid()); + } + + { + const IdType id{0}; + EXPECT_TRUE(id.isValid()); + } + + { + const IdType id1{0}; + const IdType id2{0}; + EXPECT_EQ(id1, id2); + } + + { + const IdType id1{0}; + const IdType id2{1}; + EXPECT_NE(id1, id2); + EXPECT_LT(id1, id2); + EXPECT_GT(id2, id1); + } +} + +TEST_F(DatabaseFixture, Common_subRange) +{ + using namespace Database; + + RangeResults results; + results.range = Range {0, 2}; + results.results = {5, 6}; + results.moreResults = false; + + { + auto subRange {results.getSubRange(Range {0, 1})}; + EXPECT_TRUE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 1); + EXPECT_EQ(subRange.results.front(), 5); + } + { + auto subRange {results.getSubRange(Range {1, 1})}; + EXPECT_FALSE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 1); + EXPECT_EQ(subRange.results.front(), 6); + } + { + auto subRange {results.getSubRange(Range {0, 2})}; + EXPECT_FALSE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 2); + EXPECT_EQ(subRange.results.front(), 5); + EXPECT_EQ(subRange.results.back(), 6); + } + { + auto subRange {results.getSubRange(Range {})}; + EXPECT_FALSE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 2); + EXPECT_EQ(subRange.results.front(), 5); + EXPECT_EQ(subRange.results.back(), 6); + EXPECT_EQ(subRange.range, results.range); + } + + { + auto subRange {results.getSubRange(Range {1, 0})}; + EXPECT_FALSE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 1); + EXPECT_EQ(subRange.results.front(), 6); + const Range expectedRange {1, 1}; + EXPECT_EQ(subRange.range, expectedRange); + } + { + auto subRange {results.getSubRange(Range {3, 2})}; + EXPECT_FALSE(subRange.moreResults); + ASSERT_EQ(subRange.results.size(), 0); + const Range expectedRange {2, 0}; + EXPECT_EQ(subRange.range, expectedRange); + } +} + + + diff --git a/src/libs/services/database/test/Common.hpp b/src/libs/services/database/test/Common.hpp index 75aa654d..25c47de6 100644 --- a/src/libs/services/database/test/Common.hpp +++ b/src/libs/services/database/test/Common.hpp @@ -27,11 +27,13 @@ #include "services/database/Artist.hpp" #include "services/database/Cluster.hpp" #include "services/database/Db.hpp" +#include "services/database/Listen.hpp" #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/TrackArtistLink.hpp" #include "services/database/TrackBookmark.hpp" +#include "services/database/TrackFeatures.hpp" #include "services/database/TrackList.hpp" #include "services/database/Types.hpp" #include "services/database/User.hpp" @@ -57,7 +59,7 @@ class ScopedEntity { auto transaction {_session.createUniqueTransaction()}; - auto entity {T::getById(_session, _id)}; + auto entity {T::find(_session, _id)}; entity.remove(); } @@ -76,7 +78,7 @@ class ScopedEntity { _session.checkSharedLocked(); - auto entity {T::getById(_session, _id)}; + auto entity {T::find(_session, _id)}; EXPECT_TRUE(entity); return entity; } @@ -98,7 +100,6 @@ using ScopedCluster = ScopedEntity; using ScopedClusterType = ScopedEntity; using ScopedRelease = ScopedEntity; using ScopedTrack = ScopedEntity; -using ScopedTrackBookmark = ScopedEntity; using ScopedTrackList = ScopedEntity; using ScopedUser = ScopedEntity; @@ -120,60 +121,27 @@ class ScopedFileDeleter final class TmpDatabase final { public: - Database::Db& getDb() { return _db; } + TmpDatabase (); + + Database::Db& getDb(); private: - const std::filesystem::path _tmpFile {std::tmpnam(nullptr)}; - ScopedFileDeleter fileDeleter {_tmpFile}; - Database::Db _db {_tmpFile}; + const std::filesystem::path _tmpFile; + ScopedFileDeleter _fileDeleter; + Database::Db _db; }; class DatabaseFixture : public ::testing::Test { public: - ~DatabaseFixture() - { - testDatabaseEmpty(); - } + ~DatabaseFixture(); public: - static void SetUpTestCase() - { - _tmpDb = std::make_unique(); - { - Database::Session s {_tmpDb->getDb()}; - s.prepareTables(); - s.optimize(); - - // remove default created entries - { - auto transaction {s.createUniqueTransaction()}; - auto clusterTypes {Database::ClusterType::getAll(s)}; - for (auto& clusterType : clusterTypes) - clusterType.remove(); - } - } - } - - static void TearDownTestCase() - { - _tmpDb.reset(); - } + static void SetUpTestCase(); + static void TearDownTestCase(); private: - void testDatabaseEmpty() - { - auto uniqueTransaction {session.createUniqueTransaction()}; - - EXPECT_TRUE(Database::Artist::getAll(session, Database::Artist::SortMethod::ByName).empty()); - EXPECT_TRUE(Database::Cluster::getAll(session).empty()); - EXPECT_TRUE(Database::ClusterType::getAll(session).empty()); - EXPECT_TRUE(Database::Release::getAll(session).empty()); - EXPECT_TRUE(Database::Track::getAll(session).empty()); - EXPECT_TRUE(Database::TrackBookmark::getAll(session).empty()); - EXPECT_TRUE(Database::TrackList::getAll(session).empty()); - EXPECT_TRUE(Database::User::getAll(session).empty()); - } + void testDatabaseEmpty(); static inline std::unique_ptr _tmpDb {}; diff --git a/src/libs/services/database/test/DatabaseTest.cpp b/src/libs/services/database/test/DatabaseTest.cpp index 2e0a386a..f106e06a 100644 --- a/src/libs/services/database/test/DatabaseTest.cpp +++ b/src/libs/services/database/test/DatabaseTest.cpp @@ -42,8 +42,8 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Release::getAllOrphans(session).empty()); - EXPECT_TRUE(Artist::getAllOrphans(session).empty()); + EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); } { @@ -89,8 +89,8 @@ TEST_F(DatabaseFixture, SingleUser) { { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(User::getAll(session).empty()); - EXPECT_TRUE(User::getAllIds(session).empty()); + EXPECT_TRUE(User::find(session, Range {}).results.empty()); + EXPECT_EQ(User::getCount(session), 0); } ScopedUser user {session, "MyUser"}; @@ -98,214 +98,8 @@ TEST_F(DatabaseFixture, SingleUser) { auto transaction {session.createSharedTransaction()}; - EXPECT_EQ(user->getQueuedTrackList(session)->getCount(), 0); - EXPECT_EQ(User::getAll(session).size(), 1); - EXPECT_EQ(User::getAllIds(session).size(), 1); - } -} - -TEST_F(DatabaseFixture, SingleTrackList) -{ - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; - - { - auto transaction {session.createSharedTransaction()}; - - auto trackLists {TrackList::getAll(session, user.get(), TrackList::Type::Playlist)}; - ASSERT_EQ(trackLists.size(), 1); - EXPECT_EQ(trackLists.front()->getId(), trackList.getId()); - } -} - -TEST_F(DatabaseFixture, SingleTrackListMultipleTrack) -{ - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; - std::list tracks; - - for (std::size_t i {}; i < 10; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - - auto transaction {session.createUniqueTransaction()}; - TrackListEntry::create(session, tracks.back().get(), trackList.get()); - } - - { - auto transaction {session.createSharedTransaction()}; - - ASSERT_EQ(trackList->getCount(), tracks.size()); - const auto trackIds {trackList->getTrackIds()}; - for (auto trackId : trackIds) - EXPECT_TRUE(std::any_of(std::cbegin(tracks), std::cend(tracks), [trackId](const ScopedTrack& track) { return track.getId() == trackId; })); - } -} - -TEST_F(DatabaseFixture, SingleTrackListMultipleTrackDateTime) -{ - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; - ScopedTrack track1 {session, "MyTrack1"}; - ScopedTrack track2 {session, "MyTrack2"}; - ScopedTrack track3 {session, "MyTrack3"}; - - { - Wt::WDateTime now {Wt::WDateTime::currentDateTime()}; - auto transaction {session.createUniqueTransaction()}; - TrackListEntry::create(session, track1.get(), trackList.get(), now); - TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(-1)); - TrackListEntry::create(session, track3.get(), trackList.get(), now.addSecs(1)); - } - - { - auto transaction {session.createSharedTransaction()}; - - bool moreResults; - const auto tracks {trackList.get()->getTracksReverse({}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 3); - EXPECT_EQ(tracks.front()->getId(), track3.getId()); - EXPECT_EQ(tracks.back()->getId(), track2.getId()); - } -} - -TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed) -{ - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "MyTrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; - ScopedTrack track1 {session, "MyTrack1"}; - ScopedTrack track2 {session, "MyTrack1"}; - ScopedArtist artist1 {session, "MyArtist1"}; - ScopedArtist artist2 {session, "MyArtist2"}; - ScopedRelease release1 {session, "MyRelease1"}; - ScopedRelease release2 {session, "MyRelease2"}; - - const Wt::WDateTime now {Wt::WDateTime::currentDateTime()}; - - { - auto transaction {session.createUniqueTransaction()}; - - track1.get().modify()->setRelease(release1.get()); - track2.get().modify()->setRelease(release2.get()); - TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); - } - { - - auto transaction {session.createSharedTransaction()}; - - bool moreResults {}; - EXPECT_TRUE(trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getReleasesReverse({}, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getTracksReverse({}, std::nullopt, moreResults).empty()); - } - - { - auto transaction {session.createUniqueTransaction()}; - - TrackListEntry::create(session, track1.get(), trackList.get(), now); - } - - { - auto transaction {session.createSharedTransaction()}; - - bool moreResults {}; - const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist1.getId()); - - const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release1.getId()); - - const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; - EXPECT_EQ(tracks.size(), 1); - } - - { - auto transaction {session.createUniqueTransaction()}; - - TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(1)); - } - - { - auto transaction {session.createSharedTransaction()}; - - bool moreResults {}; - const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist2.getId()); - EXPECT_EQ(artists[1]->getId(), artist1.getId()); - - const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release2.getId()); - EXPECT_EQ(releases[1]->getId(), release1.getId()); - - const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track2.getId()); - EXPECT_EQ(tracks[1]->getId(), track1.getId()); - } - - { - auto transaction {session.createUniqueTransaction()}; - - TrackListEntry::create(session, track1.get(), trackList.get(), now.addSecs(2)); - } - - { - auto transaction {session.createSharedTransaction()}; - - bool moreResults {}; - const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist1.getId()); - EXPECT_EQ(artists[1]->getId(), artist2.getId()); - - const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release1.getId()); - EXPECT_EQ(releases[1]->getId(), release2.getId()); - - const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track1.getId()); - EXPECT_EQ(tracks[1]->getId(), track2.getId()); - } -} - -TEST_F(DatabaseFixture, SingleTrackSingleUserSingleBookmark) -{ - ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - ScopedTrackBookmark bookmark {session, user.lockAndGet(), track.lockAndGet()}; - - { - auto transaction {session.createUniqueTransaction()}; - - bookmark.get().modify()->setComment("MyComment"); - bookmark.get().modify()->setOffset(std::chrono::milliseconds {5}); - } - - { - auto transaction {session.createSharedTransaction()}; - - EXPECT_EQ(TrackBookmark::getAll(session).size(), 1); - - const auto bookmarks {TrackBookmark::getByUser(session, user.get())}; - ASSERT_EQ(bookmarks.size(), 1); - EXPECT_EQ(bookmarks.back(), bookmark.get()); - } - { - auto transaction {session.createSharedTransaction()}; - - auto userBookmark {TrackBookmark::getByUser(session, user.get(), track.get())}; - ASSERT_TRUE(userBookmark); - EXPECT_EQ(userBookmark, bookmark.get()); - - EXPECT_EQ(userBookmark->getOffset(), std::chrono::milliseconds {5}); - EXPECT_EQ(userBookmark->getComment(), "MyComment"); + EXPECT_EQ(User::find(session, Range {}).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 new file mode 100644 index 00000000..1b5b12b9 --- /dev/null +++ b/src/libs/services/database/test/Listen.cpp @@ -0,0 +1,892 @@ +/* + * Copyright (C) 2021 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 "Common.hpp" +#include "services/database/Listen.hpp" + +using namespace Database; + +using ScopedListen = ScopedEntity; + +TEST_F(DatabaseFixture, Listen_getAll) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(Listen::getCount(session), 0); + } + + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 1}}}; + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(Listen::getCount(session), 1); + } + + { + auto transaction {session.createUniqueTransaction()}; + ScopedListen listen2 {session, user.get(), track.get(), Scrobbler::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{13, 0, 1}}}; + + EXPECT_EQ(Listen::getCount(session), 2); + } + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(Listen::getCount(session), 1); + } +} + +TEST_F(DatabaseFixture, Listen_get) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 1}}}; + + { + auto transaction {session.createSharedTransaction()}; + + auto listens {Listen::find(session, user->getId(), Scrobbler::ListenBrainz)}; + EXPECT_EQ(listens.results.size(), 0); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto listens {Listen::find(session, user->getId(), Scrobbler::Internal)}; + EXPECT_EQ(listens.moreResults, false); + ASSERT_EQ(listens.results.size(), 1); + EXPECT_EQ(listens.results.front()->getId(), listen->getId()); + } +} + +TEST_F(DatabaseFixture, Listen_get_multi) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedListen listen3 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 3}}}; + ScopedListen listen1 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 1}}}; + ScopedListen listen2 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, Wt::WDateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12, 0, 2}}}; + + { + auto transaction {session.createSharedTransaction()}; + + auto listens {Listen::find(session, user->getId(), Scrobbler::Internal)}; + ASSERT_EQ(listens.results.size(), 3); + EXPECT_EQ(listens.results[0]->getId(), listen1.getId()); + EXPECT_EQ(listens.results[1]->getId(), listen2.getId()); + EXPECT_EQ(listens.results[2]->getId(), listen3.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_get_byDateTime) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime1 {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + const Wt::WDateTime dateTime2 {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 2}}; + ASSERT_GT(dateTime2, dateTime1); + + ScopedListen listen1 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime1}; + ScopedListen listen2 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime2}; + + { + auto transaction {session.createSharedTransaction()}; + + { + Listen::pointer listen {Listen::find(session, user.getId(), track.getId(), Scrobbler::Internal, dateTime1)}; + ASSERT_TRUE(listen); + EXPECT_EQ(listen->getId(), listen1.getId()); + } + + { + Listen::pointer listen {Listen::find(session, user.getId(), track.getId(), Scrobbler::Internal, dateTime2)}; + ASSERT_TRUE(listen); + EXPECT_EQ(listen->getId(), listen2.getId()); + } + + { + Listen::pointer listen {Listen::find(session, user.getId(), track.getId(), Scrobbler::Internal, dateTime2.addSecs(56))}; + EXPECT_FALSE(listen); + } + } +} + +TEST_F(DatabaseFixture, Listen_getTopArtists) +{ + ScopedTrack track1 {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime1 {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime1}; + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + EXPECT_EQ(artists.results.size(), 0); + EXPECT_EQ(artists.moreResults, false); + } + + ScopedTrack track2 {session, "MyTrack2"}; + ScopedArtist artist1 {session, "MyArtist1"}; + ScopedListen listen2 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime1.addSecs(1)}; + + { + auto transaction {session.createUniqueTransaction()}; + + TrackArtistLink::create(session, track2.get(), artist1.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results[0], artist1->getId()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::ListenBrainz, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 0); + } + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {}, TrackArtistLinkType::Producer)}; + EXPECT_EQ(artists.results.size(), 0); + } + { + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {cluster->getId()}, std::nullopt)}; + EXPECT_EQ(artists.results.size(), 0); + } + } +} + +TEST_F(DatabaseFixture, Listen_getTopArtists_multi) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrack track1 {session, "MyTrack1"}; + ScopedArtist artist1 {session, "MyArtist1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedArtist artist2 {session, "MyArtist2"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + + { + auto transaction {session.createUniqueTransaction()}; + TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + EXPECT_EQ(artists.results.size(), 0); + } + + ScopedListen listen1 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(1)}; + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results[0], artist1->getId()); + } + ScopedListen listen2 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(2)}; + ScopedListen listen3 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(3)}; + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], artist2->getId()); + EXPECT_EQ(artists.results[1], artist1->getId()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt, Range {0, 1})}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.moreResults, true); + EXPECT_EQ(artists.results[0], artist2->getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getTopArtists_cluster) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedArtist artist {session, "MyArtist"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + + { + auto transaction {session.createUniqueTransaction()}; + TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {cluster.getId()}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 0); + } + { + auto transaction {session.createUniqueTransaction()}; + cluster.get().modify()->addTrack(track.get()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getTopArtists(session, user->getId(), Scrobbler::Internal, {cluster.getId()}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results[0], artist.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getTopReleases) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedRelease release {session, "MyRelease"}; + { + auto transaction {session.createSharedTransaction()}; + track.get().modify()->setRelease(release.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + EXPECT_EQ(releases.results.size(), 0); + } + + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release.getId()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::ListenBrainz, {})}; + EXPECT_EQ(releases.moreResults, false); + EXPECT_EQ(releases.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, Listen_getTopReleases_multi) +{ + ScopedTrack track1 {session, "MyTrack"}; + ScopedTrack track2 {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedRelease release1 {session, "MyRelease1"}; + ScopedRelease release2 {session, "MyRelease2"}; + + { + auto transaction {session.createSharedTransaction()}; + track1.get().modify()->setRelease(release1.get()); + track2.get().modify()->setRelease(release2.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release1.getId()); + } + ScopedListen listen2 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedListen listen3 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime}; + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], release2.getId()); + EXPECT_EQ(releases.results[1], release1.getId()); + } + ScopedListen listen4 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedListen listen5 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime}; + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], release1.getId()); + EXPECT_EQ(releases.results[1], release2.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getTopReleases_cluster) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + ScopedRelease release {session, "MyRelease"}; + + { + auto transaction {session.createSharedTransaction()}; + track.get().modify()->setRelease(release.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(releases.results.size(), 0); + } + { + auto transaction {session.createUniqueTransaction()}; + cluster.get().modify()->addTrack(track.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getTopReleases(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getTopTracks) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 0); + } + + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results[0], track.getId()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::ListenBrainz, {})}; + EXPECT_EQ(tracks.moreResults, false); + EXPECT_EQ(tracks.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, Listen_getTopTrack_multi) +{ + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results[0], track1.getId()); + } + ScopedListen listen2 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedListen listen3 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime}; + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 2); + EXPECT_EQ(tracks.results[0], track2.getId()); + EXPECT_EQ(tracks.results[1], track1.getId()); + } + ScopedListen listen4 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedListen listen5 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime}; + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 2); + EXPECT_EQ(tracks.results[0], track1.getId()); + EXPECT_EQ(tracks.results[1], track2.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getTopTracks_cluster) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(tracks.results.size(), 0); + } + { + auto transaction {session.createUniqueTransaction()}; + cluster.get().modify()->addTrack(track.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getTopTracks(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results[0], track.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentArtists) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedArtist artist {session, "MyArtist"}; + + { + auto transaction {session.createUniqueTransaction()}; + TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + EXPECT_EQ(artists.results.size(), 0); + EXPECT_EQ(artists.moreResults, false); + } + + const Wt::WDateTime dateTime {Wt::WDate {2000, 1, 2}, Wt::WTime {12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results[0], artist->getId()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::ListenBrainz, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 0); + } + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {}, TrackArtistLinkType::Producer)}; + EXPECT_EQ(artists.results.size(), 0); + } + { + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {cluster->getId()}, std::nullopt)}; + EXPECT_EQ(artists.results.size(), 0); + } + } +} + +TEST_F(DatabaseFixture, Listen_getRecentArtists_multi) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrack track1 {session, "MyTrack1"}; + ScopedArtist artist1 {session, "MyArtist1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedArtist artist2 {session, "MyArtist2"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + + { + auto transaction {session.createUniqueTransaction()}; + TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + EXPECT_EQ(artists.results.size(), 0); + } + + ScopedListen listen1 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(1)}; + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results[0], artist1->getId()); + } + ScopedListen listen2 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(2)}; + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], artist2->getId()); + EXPECT_EQ(artists.results[1], artist1->getId()); + } + ScopedListen listen3 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(-1)}; + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {}, std::nullopt, Range {0, 1})}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.moreResults, true); + EXPECT_EQ(artists.results[0], artist2->getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentArtists_cluster) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedArtist artist {session, "MyArtist"}; + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + + { + auto transaction {session.createUniqueTransaction()}; + TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {cluster.getId()}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 0); + } + { + auto transaction {session.createUniqueTransaction()}; + cluster.get().modify()->addTrack(track.get()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Listen::getRecentArtists(session, user->getId(), Scrobbler::Internal, {cluster.getId()}, std::nullopt)}; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results[0], artist.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentReleases) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedRelease release {session, "MyRelease"}; + + { + auto transaction {session.createSharedTransaction()}; + track.get().modify()->setRelease(release.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 0); + } + + const Wt::WDateTime dateTime {Wt::WDate {2000, 1, 2}, Wt::WTime {12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release.getId()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::ListenBrainz, {})}; + EXPECT_EQ(releases.moreResults, false); + EXPECT_EQ(releases.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentReleases_multi) +{ + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedUser user {session, "MyUser"}; + ScopedRelease release1 {session, "MyRelease1"}; + ScopedRelease release2 {session, "MyRelease2"}; + + { + auto transaction {session.createSharedTransaction()}; + track1.get().modify()->setRelease(release1.get()); + track2.get().modify()->setRelease(release2.get()); + } + + const Wt::WDateTime dateTime {Wt::WDate {2000, 1, 2}, Wt::WTime {12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release2.getId()); + } + + ScopedListen listen2 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(1)}; + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], release1.getId()); + EXPECT_EQ(releases.results[1], release2.getId()); + } + + ScopedListen listen3 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(2)}; + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], release2.getId()); + EXPECT_EQ(releases.results[1], release1.getId()); + } + + ScopedListen listen4 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(-1)}; + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(releases.moreResults, false); + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], release2.getId()); + EXPECT_EQ(releases.results[1], release1.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentReleases_cluster) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + ScopedRelease release {session, "MyRelease"}; + + { + auto transaction {session.createSharedTransaction()}; + track.get().modify()->setRelease(release.get()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(releases.results.size(), 0); + } + + const Wt::WDateTime dateTime {Wt::WDate{2000, 1, 2}, Wt::WTime{12,0, 1}}; + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(releases.results.size(), 0); + } + + { + auto transaction {session.createUniqueTransaction()}; + cluster.get().modify()->addTrack(track.get()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Listen::getRecentReleases(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentTracks) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 0); + } + + const Wt::WDateTime dateTime {Wt::WDate {2000, 1, 2}, Wt::WTime {12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results[0], track.getId()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::ListenBrainz, {})}; + EXPECT_EQ(tracks.moreResults, false); + EXPECT_EQ(tracks.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentTracks_multi) +{ + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedUser user {session, "MyUser"}; + + const Wt::WDateTime dateTime {Wt::WDate {2000, 1, 2}, Wt::WTime {12,0, 1}}; + ScopedListen listen1 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results[0], track2.getId()); + } + + ScopedListen listen2 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(1)}; + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 2); + EXPECT_EQ(tracks.results[0], track1.getId()); + EXPECT_EQ(tracks.results[1], track2.getId()); + } + + ScopedListen listen3 {session, user.lockAndGet(), track2.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(2)}; + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 2); + EXPECT_EQ(tracks.results[0], track2.getId()); + EXPECT_EQ(tracks.results[1], track1.getId()); + } + + ScopedListen listen4 {session, user.lockAndGet(), track1.lockAndGet(), Scrobbler::Internal, dateTime.addSecs(-1)}; + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {})}; + EXPECT_EQ(tracks.moreResults, false); + ASSERT_EQ(tracks.results.size(), 2); + EXPECT_EQ(tracks.results[0], track2.getId()); + EXPECT_EQ(tracks.results[1], track1.getId()); + } +} + +TEST_F(DatabaseFixture, Listen_getRecentTracks_cluster) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + const Wt::WDateTime dateTime {Wt::WDate {2000, 1, 2}, Wt::WTime {12,0, 1}}; + ScopedListen listen {session, user.lockAndGet(), track.lockAndGet(), Scrobbler::Internal, dateTime}; + ScopedClusterType clusterType {session, "MyType"}; + ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(tracks.results.size(), 0); + } + { + auto transaction {session.createUniqueTransaction()}; + cluster.get().modify()->addTrack(track.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Listen::getRecentTracks(session, user->getId(), Scrobbler::Internal, {cluster.getId()})}; + EXPECT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results[0], track.getId()); + } +} + diff --git a/src/libs/services/database/test/Release.cpp b/src/libs/services/database/test/Release.cpp index 84f765f4..9ff1db24 100644 --- a/src/libs/services/database/test/Release.cpp +++ b/src/libs/services/database/test/Release.cpp @@ -21,11 +21,12 @@ using namespace Database; -TEST_F(DatabaseFixture, SingleRelease) +TEST_F(DatabaseFixture, Release) { { auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(Release::getCount(session), 0); EXPECT_FALSE(Release::exists(session, 0)); EXPECT_FALSE(Release::exists(session, 1)); } @@ -35,20 +36,23 @@ TEST_F(DatabaseFixture, SingleRelease) { auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(Release::getCount(session), 1); EXPECT_TRUE(Release::exists(session, release.getId())); - auto releases {Release::getAllOrphans(session)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + auto releases {Release::findOrphans(session, Range {})}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); - releases = Release::getAll(session); - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); - EXPECT_EQ(release->getDuration(), std::chrono::seconds {0}); + { + auto releases {Release::find(session, Release::FindParameters {})}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); + EXPECT_EQ(release->getDuration(), std::chrono::seconds {0}); + } } } -TEST_F(DatabaseFixture, SingleTrackSingleRelease) +TEST_F(DatabaseFixture, Release_singleTrack) { ScopedRelease release {session, "MyRelease"}; @@ -64,7 +68,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleRelease) { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Release::getAllOrphans(session).empty()); + EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); EXPECT_EQ(release->getTracksCount(), 1); ASSERT_EQ(release->getTracks().size(), 1); @@ -80,19 +84,19 @@ TEST_F(DatabaseFixture, SingleTrackSingleRelease) { auto transaction {session.createUniqueTransaction()}; - auto tracks {Track::getByNameAndReleaseName(session, "MyTrackName", "MyReleaseName")}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track.getId()); + auto tracks {Track::findByNameAndReleaseName(session, "MyTrackName", "MyReleaseName")}; + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results.front(), track.getId()); } { auto transaction {session.createUniqueTransaction()}; - auto tracks {Track::getByNameAndReleaseName(session, "MyTrackName", "MyReleaseFoo")}; - EXPECT_EQ(tracks.size(), 0); + auto tracks {Track::findByNameAndReleaseName(session, "MyTrackName", "MyReleaseFoo")}; + EXPECT_EQ(tracks.results.size(), 0); } { auto transaction {session.createUniqueTransaction()}; - auto tracks {Track::getByNameAndReleaseName(session, "MyTrackFoo", "MyReleaseName")}; - EXPECT_EQ(tracks.size(), 0); + auto tracks {Track::findByNameAndReleaseName(session, "MyTrackFoo", "MyReleaseName")}; + EXPECT_EQ(tracks.results.size(), 0); } } @@ -101,9 +105,9 @@ TEST_F(DatabaseFixture, SingleTrackSingleRelease) EXPECT_TRUE(release->getTracks().empty()); - auto releases {Release::getAllOrphans(session)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + auto releases {Release::findOrphans(session, Range {})}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); } } @@ -138,38 +142,37 @@ TEST_F(DatabaseFixture, MulitpleReleaseSearchByName) { auto transaction {session.createSharedTransaction()}; - bool more; { - const auto releases {Release::getByFilter(session, {}, {"Release"}, std::nullopt, more)}; - EXPECT_EQ(releases.size(), 6); + const auto releases {Release::find(session, Release::FindParameters {}.setKeywords({"Release"}))}; + EXPECT_EQ(releases.results.size(), 6); } { - const auto releases {Release::getByFilter(session, {}, {"MyRelease"}, std::nullopt, more)}; - EXPECT_EQ(releases.size(), 5); - EXPECT_TRUE(std::none_of(std::cbegin(releases), std::cend(releases), [&](const Release::pointer& release) { return release->getId() == release6.getId(); })); + const auto releases {Release::find(session, Release::FindParameters {}.setKeywords({"MyRelease"}))}; + ASSERT_EQ(releases.results.size(), 5); + EXPECT_TRUE(std::none_of(std::cbegin(releases.results), std::cend(releases.results), [&](const ReleaseId releaseId) { return releaseId == release6.getId(); })); } { - const auto releases {Release::getByFilter(session, {}, {"MyRelease%"}, std::nullopt, more)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release2.getId()); - EXPECT_EQ(releases[1]->getId(), release4.getId()); + const auto releases {Release::find(session, Release::FindParameters {}.setKeywords({"MyRelease%"}))}; + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], release2.getId()); + EXPECT_EQ(releases.results[1], release4.getId()); } { - const auto releases {Release::getByFilter(session, {}, {"%MyRelease"}, std::nullopt, more)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release3.getId()); - EXPECT_EQ(releases[1]->getId(), release5.getId()); + const auto releases {Release::find(session, Release::FindParameters {}.setKeywords({"%MyRelease"}))}; + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], release3.getId()); + EXPECT_EQ(releases.results[1], release5.getId()); } { - const auto releases {Release::getByFilter(session, {}, {"Foo%MyRelease"}, std::nullopt, more)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0]->getId(), release5.getId()); + const auto releases {Release::find(session, Release::FindParameters {}.setKeywords({"Foo%MyRelease"}))}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release5.getId()); } { - const auto releases {Release::getByFilter(session, {}, {"MyRelease%Foo"}, std::nullopt, more)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0]->getId(), release4.getId()); + const auto releases {Release::find(session, Release::FindParameters {}.setKeywords({"MyRelease%Foo"}))}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results[0], release4.getId()); } } } @@ -323,8 +326,8 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseDate) { auto transaction {session.createSharedTransaction()}; - const auto releases {Release::getByYear(session, 0, 3000)}; - EXPECT_EQ(releases.size(), 0); + const auto releases {Release::find(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(0, 3000)))}; + EXPECT_EQ(releases.results.size(), 0); } { @@ -348,48 +351,48 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseDate) { auto transaction {session.createSharedTransaction()}; - auto releases {Release::getByYear(session, 1950, 2000)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release1.getId()); + auto releases {Release::find(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(1950, 2000)))}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release1.getId()); - releases = Release::getByYear(session, 1994, 1994); - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release1.getId()); + releases = Release::find(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(1994, 1994))); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release1.getId()); - releases = Release::getByYear(session, 1993, 1993); - ASSERT_EQ(releases.size(), 0); + releases = Release::find(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(1993, 1993))); + ASSERT_EQ(releases.results.size(), 0); } } -TEST_F(DatabaseFixture, SingleStarredRelease) +TEST_F(DatabaseFixture, Release_writtenAfter) { ScopedRelease release {session, "MyRelease"}; ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; - { - auto transaction {session.createSharedTransaction()}; - - EXPECT_FALSE(user->isStarred(release.get())); - } + const Wt::WDateTime dateTime {Wt::WDate {1950, 1, 1}, Wt::WTime {12, 30, 20}}; { auto transaction {session.createUniqueTransaction()}; - + track.get().modify()->setLastWriteTime(dateTime); track.get().modify()->setRelease(release.get()); - user.get().modify()->star(release.get()); } { auto transaction {session.createSharedTransaction()}; + const auto releases {Release::find(session, Release::FindParameters {})}; + EXPECT_EQ(releases.results.size(), 1); + } - EXPECT_TRUE(user->isStarred(release.get())); + { + auto transaction {session.createSharedTransaction()}; + const auto releases {Release::find(session, Release::FindParameters {}.setWrittenAfter(dateTime.addSecs(-1)))}; + EXPECT_EQ(releases.results.size(), 1); + } - bool hasMore {}; - auto releases {Release::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); - EXPECT_FALSE(hasMore); + { + auto transaction {session.createSharedTransaction()}; + const auto releases {Release::find(session, Release::FindParameters {}.setWrittenAfter(dateTime.addSecs(+1)))}; + EXPECT_EQ(releases.results.size(), 0); } } diff --git a/src/libs/services/database/test/StarredArtist.cpp b/src/libs/services/database/test/StarredArtist.cpp new file mode 100644 index 00000000..22f11f8e --- /dev/null +++ b/src/libs/services/database/test/StarredArtist.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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 "Common.hpp" +#include "services/database/StarredArtist.hpp" + +using namespace Database; + +using ScopedStarredArtist = ScopedEntity; + +TEST_F(DatabaseFixture, StarredArtist) +{ + ScopedArtist artist {session, "MyArtist"}; + ScopedUser user {session, "MyUser"}; + ScopedUser user2 {session, "MyUser2"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto starredArtist {StarredArtist::find(session, artist->getId(), user->getId(), Scrobbler::Internal)}; + EXPECT_FALSE(starredArtist); + EXPECT_EQ(StarredArtist::getCount(session), 0); + + auto artists {Artist::find(session, Artist::FindParameters {})}; + EXPECT_EQ(artists.results.size(), 1); + } + + ScopedStarredArtist starredArtist {session, artist.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + { + auto transaction {session.createSharedTransaction()}; + + auto gotArtist {StarredArtist::find(session, artist->getId(), user->getId(), Scrobbler::Internal)}; + EXPECT_EQ(gotArtist->getId(), starredArtist->getId()); + EXPECT_EQ(StarredArtist::getCount(session), 1); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Artist::find(session, Artist::FindParameters {})}; + EXPECT_EQ(artists.results.size(), 1); + + artists = Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal)); + EXPECT_EQ(artists.results.size(), 1); + + artists = Artist::find(session, Artist::FindParameters {}.setStarringUser(user2.getId(), Scrobbler::Internal)); + EXPECT_EQ(artists.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, StarredArtist_dateTime) +{ + ScopedArtist artist1 {session, "MyArtist1"}; + ScopedArtist artist2 {session, "MyArtist2"}; + ScopedUser user {session, "MyUser"}; + + ScopedStarredArtist starredArtist1 {session, artist1.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + ScopedStarredArtist starredArtist2 {session, artist2.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + + const Wt::WDateTime dateTime {Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1}}; + + { + auto transaction {session.createSharedTransaction()}; + + auto artists {Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal))}; + EXPECT_EQ(artists.results.size(), 2); + } + + { + auto transaction {session.createUniqueTransaction()}; + + starredArtist1.get().modify()->setDateTime(dateTime); + starredArtist2.get().modify()->setDateTime(dateTime.addSecs(-1)); + + auto artists {Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal) + .setSortMethod(ArtistSortMethod::StarredDateDesc))}; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], starredArtist1->getArtist()->getId()); + EXPECT_EQ(artists.results[1], starredArtist2->getArtist()->getId()); + } + { + auto transaction {session.createUniqueTransaction()}; + + starredArtist1.get().modify()->setDateTime(dateTime); + starredArtist2.get().modify()->setDateTime(dateTime.addSecs(1)); + + auto artists {Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal) + .setSortMethod(ArtistSortMethod::StarredDateDesc))}; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], starredArtist2->getArtist()->getId()); + EXPECT_EQ(artists.results[1], starredArtist1->getArtist()->getId()); + } +} diff --git a/src/libs/services/database/test/StarredRelease.cpp b/src/libs/services/database/test/StarredRelease.cpp new file mode 100644 index 00000000..2059a785 --- /dev/null +++ b/src/libs/services/database/test/StarredRelease.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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 "Common.hpp" +#include "services/database/StarredRelease.hpp" + +using namespace Database; + +using ScopedStarredRelease = ScopedEntity; + +TEST_F(DatabaseFixture, StarredRelease) +{ + ScopedRelease release {session, "MyRelease"}; + ScopedUser user {session, "MyUser"}; + ScopedUser user2 {session, "MyUser2"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto starredRelease {StarredRelease::find(session, release->getId(), user->getId(), Scrobbler::Internal)}; + EXPECT_FALSE(starredRelease); + EXPECT_EQ(StarredRelease::getCount(session), 0); + + auto releases {Release::find(session, Release::FindParameters {})}; + EXPECT_EQ(releases.results.size(), 1); + } + + ScopedStarredRelease starredRelease {session, release.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + { + auto transaction {session.createSharedTransaction()}; + + auto gotRelease {StarredRelease::find(session, release->getId(), user->getId(), Scrobbler::Internal)}; + EXPECT_EQ(gotRelease->getId(), starredRelease->getId()); + EXPECT_EQ(StarredRelease::getCount(session), 1); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Release::find(session, Release::FindParameters {})}; + EXPECT_EQ(releases.results.size(), 1); + + releases = Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal)); + EXPECT_EQ(releases.results.size(), 1); + + releases = Release::find(session, Release::FindParameters {}.setStarringUser(user2.getId(), Scrobbler::Internal)); + EXPECT_EQ(releases.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, StarredRelease_dateTime) +{ + ScopedRelease release1 {session, "MyRelease1"}; + ScopedRelease release2 {session, "MyRelease2"}; + ScopedUser user {session, "MyUser"}; + + ScopedStarredRelease starredRelease1 {session, release1.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + ScopedStarredRelease starredRelease2 {session, release2.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + + const Wt::WDateTime dateTime {Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1}}; + + { + auto transaction {session.createSharedTransaction()}; + + auto releases {Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal))}; + EXPECT_EQ(releases.results.size(), 2); + } + + { + auto transaction {session.createUniqueTransaction()}; + + starredRelease1.get().modify()->setDateTime(dateTime); + starredRelease2.get().modify()->setDateTime(dateTime.addSecs(-1)); + + auto releases {Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal) + .setSortMethod(ReleaseSortMethod::StarredDateDesc))}; + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], starredRelease1->getRelease()->getId()); + EXPECT_EQ(releases.results[1], starredRelease2->getRelease()->getId()); + } + { + auto transaction {session.createUniqueTransaction()}; + + starredRelease1.get().modify()->setDateTime(dateTime); + starredRelease2.get().modify()->setDateTime(dateTime.addSecs(1)); + + auto releases {Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal) + .setSortMethod(ReleaseSortMethod::StarredDateDesc))}; + ASSERT_EQ(releases.results.size(), 2); + EXPECT_EQ(releases.results[0], starredRelease2->getRelease()->getId()); + EXPECT_EQ(releases.results[1], starredRelease1->getRelease()->getId()); + } +} diff --git a/src/libs/services/database/test/StarredTrack.cpp b/src/libs/services/database/test/StarredTrack.cpp new file mode 100644 index 00000000..6bd00cb0 --- /dev/null +++ b/src/libs/services/database/test/StarredTrack.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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 "Common.hpp" +#include "services/database/StarredTrack.hpp" + +using namespace Database; + +using ScopedStarredTrack = ScopedEntity; + +TEST_F(DatabaseFixture, StarredTrack) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + ScopedUser user2 {session, "MyUser2"}; + + { + auto transaction {session.createSharedTransaction()}; + + auto starredTrack {StarredTrack::find(session, track->getId(), user->getId(), Scrobbler::Internal)}; + EXPECT_FALSE(starredTrack); + EXPECT_EQ(StarredTrack::getCount(session), 0); + + auto tracks {Track::find(session, Track::FindParameters {})}; + EXPECT_EQ(tracks.results.size(), 1); + } + + ScopedStarredTrack starredTrack {session, track.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + { + auto transaction {session.createSharedTransaction()}; + + auto gotTrack {StarredTrack::find(session, track->getId(), user->getId(), Scrobbler::Internal)}; + EXPECT_EQ(gotTrack->getId(), starredTrack->getId()); + EXPECT_EQ(StarredTrack::getCount(session), 1); + } + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Track::find(session, Track::FindParameters {})}; + EXPECT_EQ(tracks.results.size(), 1); + + tracks = Track::find(session, Track::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal)); + EXPECT_EQ(tracks.results.size(), 1); + + tracks = Track::find(session, Track::FindParameters {}.setStarringUser(user2.getId(), Scrobbler::Internal)); + EXPECT_EQ(tracks.results.size(), 0); + } +} + +TEST_F(DatabaseFixture, StarredTrack_dateTime) +{ + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedUser user {session, "MyUser"}; + + ScopedStarredTrack starredTrack1 {session, track1.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + ScopedStarredTrack starredTrack2 {session, track2.lockAndGet(), user.lockAndGet(), Scrobbler::Internal}; + + const Wt::WDateTime dateTime {Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1}}; + + { + auto transaction {session.createSharedTransaction()}; + + auto tracks {Track::find(session, Track::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal))}; + EXPECT_EQ(tracks.results.size(), 2); + } + + { + auto transaction {session.createUniqueTransaction()}; + + starredTrack1.get().modify()->setDateTime(dateTime); + starredTrack2.get().modify()->setDateTime(dateTime.addSecs(-1)); + + auto tracks {Track::find(session, Track::FindParameters {}.setStarringUser(user.getId(), Scrobbler::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()}; + + starredTrack1.get().modify()->setDateTime(dateTime); + starredTrack2.get().modify()->setDateTime(dateTime.addSecs(1)); + + auto tracks {Track::find(session, Track::FindParameters {}.setStarringUser(user.getId(), Scrobbler::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/database/test/Track.cpp b/src/libs/services/database/test/Track.cpp index a4b22d61..3868225d 100644 --- a/src/libs/services/database/test/Track.cpp +++ b/src/libs/services/database/test/Track.cpp @@ -23,10 +23,11 @@ using namespace Database; -TEST_F(DatabaseFixture, SingleTrack) +TEST_F(DatabaseFixture, Track) { { auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(Track::find(session, Track::FindParameters {}).results.size(), 0); EXPECT_EQ(Track::getCount(session), 0); EXPECT_FALSE(Track::exists(session, 0)); } @@ -36,16 +37,16 @@ TEST_F(DatabaseFixture, SingleTrack) { auto transaction {session.createSharedTransaction()}; - EXPECT_EQ(Track::getAll(session).size(), 1); + EXPECT_EQ(Track::find(session, Track::FindParameters {}).results.size(), 1); EXPECT_EQ(Track::getCount(session), 1); EXPECT_TRUE(Track::exists(session, track.getId())); - auto myTrack {Track::getById(session, track.getId())}; + auto myTrack {Track::find(session, track.getId())}; ASSERT_TRUE(myTrack); EXPECT_EQ(myTrack->getId(), track.getId()); } } -TEST_F(DatabaseFixture, MultipleTracksSearchByFilter) +TEST_F(DatabaseFixture, Track_findByKeywords) { ScopedTrack track1 {session, ""}; ScopedTrack track2 {session, ""}; @@ -67,32 +68,31 @@ TEST_F(DatabaseFixture, MultipleTracksSearchByFilter) { auto transaction {session.createSharedTransaction()}; - bool more; { - const auto tracks {Track::getByFilter(session, {}, {"Track"}, std::nullopt, more)}; - EXPECT_EQ(tracks.size(), 6); + const auto tracks {Track::find(session, Track::FindParameters {}.setKeywords({"Track"}))}; + EXPECT_EQ(tracks.results.size(), 6); } { - const auto tracks {Track::getByFilter(session, {}, {"MyTrack"}, std::nullopt, more)}; - EXPECT_EQ(tracks.size(), 5); - EXPECT_TRUE(std::none_of(std::cbegin(tracks), std::cend(tracks), [&](const Track::pointer& track) { return track->getId() == track6.getId(); })); + const auto tracks {Track::find(session, Track::FindParameters {}.setKeywords({"MyTrack"}))}; + EXPECT_EQ(tracks.results.size(), 5); + EXPECT_TRUE(std::none_of(std::cbegin(tracks.results), std::cend(tracks.results), [&](const TrackId trackId) { return trackId == track6.getId(); })); } { - const auto tracks {Track::getByFilter(session, {}, {"MyTrack%"}, std::nullopt, more)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track2.getId()); - EXPECT_EQ(tracks[1]->getId(), track3.getId()); + const auto tracks {Track::find(session, Track::FindParameters {}.setKeywords({"MyTrack%"}))}; + ASSERT_EQ(tracks.results.size(), 2); + EXPECT_EQ(tracks.results[0], track2.getId()); + EXPECT_EQ(tracks.results[1], track3.getId()); } { - const auto tracks {Track::getByFilter(session, {}, {"%MyTrack"}, std::nullopt, more)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track4.getId()); - EXPECT_EQ(tracks[1]->getId(), track5.getId()); + const auto tracks {Track::find(session, Track::FindParameters {}.setKeywords({"%MyTrack"}))}; + ASSERT_EQ(tracks.results.size(), 2); + EXPECT_EQ(tracks.results[0], track4.getId()); + EXPECT_EQ(tracks.results[1], track5.getId()); } } } -TEST_F(DatabaseFixture, SingleTrackDate) +TEST_F(DatabaseFixture, Track_date) { ScopedTrack track {session, "MyTrack"}; @@ -115,36 +115,33 @@ TEST_F(DatabaseFixture, SingleTrackDate) } } -TEST_F(DatabaseFixture, SingleStarredTrack) +TEST_F(DatabaseFixture, Track_writtenAfter) { ScopedTrack track {session, "MyTrack"}; - ScopedUser user {session, "MyUser"}; + + const Wt::WDateTime dateTime {Wt::WDate {1950, 1, 1}, Wt::WTime {12, 30, 20}}; { auto transaction {session.createUniqueTransaction()}; - - EXPECT_FALSE(user->isStarred(track.get())); + track.get().modify()->setLastWriteTime(dateTime); } { - auto transaction {session.createUniqueTransaction()}; - - user.get().modify()->star(track.get()); + auto transaction {session.createSharedTransaction()}; + const auto tracks {Track::find(session, Track::FindParameters {})}; + EXPECT_EQ(tracks.results.size(), 1); } { - auto transaction {session.createUniqueTransaction()}; + auto transaction {session.createSharedTransaction()}; + const auto tracks {Track::find(session, Track::FindParameters {}.setWrittenAfter(dateTime.addSecs(-1)))}; + EXPECT_EQ(tracks.results.size(), 1); + } - EXPECT_TRUE(user->isStarred(track.get())); - - bool hasMore {}; - auto tracks {Track::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track.getId()); - EXPECT_FALSE(hasMore); + { + auto transaction {session.createSharedTransaction()}; + const auto tracks {Track::find(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 new file mode 100644 index 00000000..d99b0bd5 --- /dev/null +++ b/src/libs/services/database/test/TrackBookmark.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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 "Common.hpp" + +#include "services/database/TrackBookmark.hpp" + +using ScopedTrackBookmark = ScopedEntity; + +using namespace Database; + +TEST_F(DatabaseFixture, TrackBookmark) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + + { + auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(TrackBookmark::getCount(session), 0); + } + + ScopedTrackBookmark bookmark {session, user.lockAndGet(), track.lockAndGet()}; + + { + auto transaction {session.createUniqueTransaction()}; + + bookmark.get().modify()->setComment("MyComment"); + bookmark.get().modify()->setOffset(std::chrono::milliseconds {5}); + } + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(TrackBookmark::getCount(session), 1); + + const auto bookmarks {TrackBookmark::find(session, user.getId(), Range {})}; + ASSERT_EQ(bookmarks.results.size(), 1); + EXPECT_EQ(bookmarks.results.front(), bookmark.getId()); + } + { + auto transaction {session.createSharedTransaction()}; + + auto userBookmark {TrackBookmark::find(session, user.getId(), track.getId())}; + ASSERT_TRUE(userBookmark); + EXPECT_EQ(userBookmark, bookmark.get()); + + EXPECT_EQ(userBookmark->getOffset(), std::chrono::milliseconds {5}); + EXPECT_EQ(userBookmark->getComment(), "MyComment"); + } +} diff --git a/src/libs/services/database/test/TrackFeatures.cpp b/src/libs/services/database/test/TrackFeatures.cpp new file mode 100644 index 00000000..5d919a40 --- /dev/null +++ b/src/libs/services/database/test/TrackFeatures.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 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 "Common.hpp" + +#include "services/database/TrackFeatures.hpp" + +using ScopedTrackFeatures = ScopedEntity; + +using namespace Database; + +TEST_F(DatabaseFixture, TrackFeatures) +{ + ScopedTrack track {session, "MyTrack"}; + ScopedUser user {session, "MyUser"}; + + { + auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(TrackFeatures::getCount(session), 0); + } + + ScopedTrackFeatures trackFeatures {session, track.lockAndGet(), ""}; + + { + auto transaction {session.createUniqueTransaction()}; + EXPECT_EQ(TrackFeatures::getCount(session), 1); + + auto allTrackFeatures {TrackFeatures::find(session, Range {})}; + ASSERT_EQ(allTrackFeatures.results.size(), 1); + EXPECT_EQ(allTrackFeatures.results.front(), trackFeatures.getId()); + } +} diff --git a/src/libs/services/database/test/TrackList.cpp b/src/libs/services/database/test/TrackList.cpp new file mode 100644 index 00000000..9bdb96ec --- /dev/null +++ b/src/libs/services/database/test/TrackList.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2019 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 + +#include "Common.hpp" + +using namespace Database; + +TEST_F(DatabaseFixture, SingleTrackList) +{ + ScopedUser user {session, "MyUser"}; + { + auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(TrackList::getCount(session), 0); + } + + ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; + + { + auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(TrackList::getCount(session), 1); + } +} + +TEST_F(DatabaseFixture, SingleTrackListMultipleTrack) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; + std::list tracks; + + for (std::size_t i {}; i < 10; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + + auto transaction {session.createUniqueTransaction()}; + TrackListEntry::create(session, tracks.back().get(), trackList.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + ASSERT_EQ(trackList->getCount(), tracks.size()); + const auto trackIds {trackList->getTrackIds()}; + ASSERT_EQ(trackIds.size(), tracks.size()); + + // Same order + std::size_t i {}; + for (const ScopedTrack& track : tracks) + EXPECT_EQ(track.getId(), trackIds[i++]); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto reverseTracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; + ASSERT_EQ(reverseTracks.size(), tracks.size()); + + // Reverse order + ASSERT_TRUE(tracks.size() > 0); + std::size_t i {tracks.size() - 1}; + for (const ScopedTrack& track : tracks) + EXPECT_EQ(track.getId(), reverseTracks[i--]->getId()); + } +} + +TEST_F(DatabaseFixture, SingleTrackListMultipleTrackDateTime) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedTrack track3 {session, "MyTrack3"}; + + { + Wt::WDateTime now {Wt::WDateTime::currentDateTime()}; + auto transaction {session.createUniqueTransaction()}; + TrackListEntry::create(session, track1.get(), trackList.get(), now); + TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(-1)); + TrackListEntry::create(session, track3.get(), trackList.get(), now.addSecs(1)); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults; + const auto tracks {trackList.get()->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; + ASSERT_EQ(tracks.size(), 3); + EXPECT_EQ(tracks.front()->getId(), track3.getId()); + EXPECT_EQ(tracks.back()->getId(), track2.getId()); + } +} + +TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrackList trackList {session, "MyTrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack1"}; + ScopedArtist artist1 {session, "MyArtist1"}; + ScopedArtist artist2 {session, "MyArtist2"}; + ScopedRelease release1 {session, "MyRelease1"}; + ScopedRelease release2 {session, "MyRelease2"}; + + const Wt::WDateTime now {Wt::WDateTime::currentDateTime()}; + + { + auto transaction {session.createUniqueTransaction()}; + + track1.get().modify()->setRelease(release1.get()); + track2.get().modify()->setRelease(release2.get()); + TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); + } + { + + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); + } + + { + auto transaction {session.createUniqueTransaction()}; + + TrackListEntry::create(session, track1.get(), trackList.get(), now); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); + + const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release1.getId()); + + const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; + EXPECT_EQ(tracks.size(), 1); + } + + { + auto transaction {session.createUniqueTransaction()}; + + TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(1)); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists[0]->getId(), artist2.getId()); + EXPECT_EQ(artists[1]->getId(), artist1.getId()); + + const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release2.getId()); + EXPECT_EQ(releases[1]->getId(), release1.getId()); + + const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; + ASSERT_EQ(tracks.size(), 2); + EXPECT_EQ(tracks[0]->getId(), track2.getId()); + EXPECT_EQ(tracks[1]->getId(), track1.getId()); + } + + { + auto transaction {session.createUniqueTransaction()}; + + TrackListEntry::create(session, track1.get(), trackList.get(), now.addSecs(2)); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists[0]->getId(), artist1.getId()); + EXPECT_EQ(artists[1]->getId(), artist2.getId()); + + const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release1.getId()); + EXPECT_EQ(releases[1]->getId(), release2.getId()); + + const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; + ASSERT_EQ(tracks.size(), 2); + EXPECT_EQ(tracks[0]->getId(), track1.getId()); + EXPECT_EQ(tracks[1]->getId(), track2.getId()); + } +} + +TEST_F(DatabaseFixture, SingleTrackList_getArtists) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack2"}; + ScopedRelease release {session, "MyRelease"}; + ScopedArtist artist1 {session, "MyArtist1"}; + ScopedArtist artist2 {session, "MyArtist2"}; + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(trackList->getCount(), 0); + bool moreResults {}; + const auto artists {trackList->getArtists({} /*clusters*/, std::nullopt /* linkType */, ArtistSortMethod::ByName, std::nullopt /* range */, moreResults)}; + ASSERT_TRUE(artists.empty()); + } + + { + auto transaction {session.createUniqueTransaction()}; + TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); + TrackListEntry::create(session, track1.get(), trackList.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(trackList->getCount(), 1); + bool moreResults {}; + const auto artists {trackList->getArtists({} /*clusters*/, std::nullopt /* linkType */, ArtistSortMethod::ByName, std::nullopt /* range */, moreResults)}; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto artists {trackList->getArtists({} /*clusters*/, TrackArtistLinkType::ReleaseArtist, ArtistSortMethod::ByName, std::nullopt /* range */, moreResults)}; + EXPECT_TRUE(artists.empty()); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto artists {trackList->getArtists({} /*clusters*/, TrackArtistLinkType::Artist, ArtistSortMethod::ByName, std::nullopt /* range */, moreResults)}; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); + } + + { + auto transaction {session.createUniqueTransaction()}; + TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); + TrackListEntry::create(session, track2.get(), trackList.get()); + artist1.get().modify()->setSortName("ZZZ"); + artist2.get().modify()->setSortName("AAA"); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto artists {trackList->getArtists({} /*clusters*/, TrackArtistLinkType::Artist, ArtistSortMethod::ByName, std::nullopt /* range */, moreResults)}; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); + EXPECT_EQ(artists.back()->getId(), artist2.getId()); + } + + { + auto transaction {session.createSharedTransaction()}; + + bool moreResults {}; + const auto artists {trackList->getArtists({} /*clusters*/, TrackArtistLinkType::Artist, ArtistSortMethod::BySortName, std::nullopt /* range */, moreResults)}; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists.front()->getId(), artist2.getId()); + EXPECT_EQ(artists.back()->getId(), artist1.getId()); + } +} + +TEST_F(DatabaseFixture, SingleTrackList_getReleases) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; + ScopedTrack track {session, "MyTrack"}; + ScopedRelease release {session, "MyRelease"}; + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(trackList->getCount(), 0); + bool moreResults {}; + const auto releases {trackList->getReleases({} /*clusters*/, std::nullopt /* range */, moreResults)}; + ASSERT_TRUE(releases.empty()); + } + + { + auto transaction {session.createUniqueTransaction()}; + + track.get().modify()->setRelease(release.get()); + + TrackListEntry::create(session, track.get(), trackList.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + ASSERT_EQ(trackList->getCount(), 1); + bool moreResults {}; + const auto releases {trackList->getReleases({} /*clusters*/, std::nullopt /* range */, moreResults)}; + ASSERT_EQ(releases.size(), 1); + ASSERT_EQ(releases.front()->getId(), release->getId()); + } +} + +TEST_F(DatabaseFixture, SingleTrackList_getTracks) +{ + ScopedUser user {session, "MyUser"}; + ScopedTrackList trackList {session, "MytrackList", TrackList::Type::Playlist, false, user.lockAndGet()}; + ScopedTrack track1 {session, "MyTrack1"}; + ScopedTrack track2 {session, "MyTrack1"}; + + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_EQ(trackList->getCount(), 0); + bool moreResults {}; + const auto tracks {trackList->getTracks({} /*clusters*/, std::nullopt /* range */, moreResults)}; + ASSERT_TRUE(tracks.empty()); + } + + { + auto transaction {session.createUniqueTransaction()}; + TrackListEntry::create(session, track1.get(), trackList.get()); + TrackListEntry::create(session, track2.get(), trackList.get()); + TrackListEntry::create(session, track1.get(), trackList.get()); + } + + { + auto transaction {session.createSharedTransaction()}; + + ASSERT_EQ(trackList->getCount(), 3); + bool moreResults {}; + const auto tracks {trackList->getTracks({} /*clusters*/, std::nullopt /* range */, moreResults)}; + ASSERT_EQ(tracks.size(), 2); + ASSERT_EQ(tracks[0]->getId(), track1->getId()); + ASSERT_EQ(tracks[1]->getId(), track2->getId()); + } +} diff --git a/src/libs/services/feedback/CMakeLists.txt b/src/libs/services/feedback/CMakeLists.txt deleted file mode 100644 index 3049f60f..00000000 --- a/src/libs/services/feedback/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ - -add_library(lmsfeedback SHARED - impl/FeedbackService.cpp - ) - -target_include_directories(lmsfeedback INTERFACE - include - ) - -target_include_directories(lmsfeedback PRIVATE - include - ) - -target_link_libraries(lmsfeedback PRIVATE - lmsdatabase - ) - -install(TARGETS lmsfeedback DESTINATION lib) - diff --git a/src/libs/services/feedback/impl/FeedbackService.cpp b/src/libs/services/feedback/impl/FeedbackService.cpp deleted file mode 100644 index d4c0a04f..00000000 --- a/src/libs/services/feedback/impl/FeedbackService.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2021 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 "FeedbackService.hpp" -#include "services/database/Db.hpp" -#include "services/database/Artist.hpp" -#include "services/database/Release.hpp" -#include "services/database/Session.hpp" -#include "services/database/Track.hpp" -#include "services/database/User.hpp" - -namespace -{ - using namespace Database; - - void - exec(Db& db, UserId userId, ArtistId artistId, std::function func) - { - Session& dbSession {db.getTLSSession()}; - auto transaction {dbSession.createSharedTransaction()}; - - Database::User::pointer user {Database::User::getById(dbSession, userId)}; - if (!user) - return; - - Database::Artist::pointer artist {Database::Artist::getById(dbSession, artistId)}; - if (!artist) - return; - - func(user, artist); - } - - void - exec(Db& db, UserId userId, ReleaseId releaseId, std::function func) - { - Session& dbSession {db.getTLSSession()}; - auto transaction {dbSession.createSharedTransaction()}; - - Database::User::pointer user {Database::User::getById(dbSession, userId)}; - if (!user) - return; - - Database::Release::pointer release {Database::Release::getById(dbSession, releaseId)}; - if (!release) - return; - - func(user, release); - } - - void - exec(Db& db, UserId userId, TrackId trackId, std::function func) - { - Session& dbSession {db.getTLSSession()}; - auto transaction {dbSession.createSharedTransaction()}; - - Database::User::pointer user {Database::User::getById(dbSession, userId)}; - if (!user) - return; - - Database::Track::pointer track {Database::Track::getById(dbSession, trackId)}; - if (!track) - return; - - func(user, track); - } -} - -namespace Feedback -{ - std::unique_ptr - createFeedbackService(Database::Db& db) - { - return std::make_unique(db); - } - - FeedbackService::FeedbackService(Database::Db& db) - : _db {db} - { - } - - void - FeedbackService::star(Database::UserId userId, Database::ArtistId artistId) - { - starObject(userId, artistId); - } - - void - FeedbackService::unstar(Database::UserId userId, Database::ArtistId artistId) - { - unstarObject(userId, artistId); - } - - bool - FeedbackService::isStarred(Database::UserId userId, Database::ArtistId artistId) - { - return isObjectStarred(userId, artistId); - } - - void - FeedbackService::star(Database::UserId userId, Database::ReleaseId releaseId) - { - starObject(userId, releaseId); - } - - void - FeedbackService::unstar(Database::UserId userId, Database::ReleaseId releaseId) - { - unstarObject(userId, releaseId); - } - - bool - FeedbackService::isStarred(Database::UserId userId, Database::ReleaseId releaseId) - { - return isObjectStarred(userId, releaseId); - } - - void - FeedbackService::star(Database::UserId userId, Database::TrackId trackId) - { - starObject(userId, trackId); - } - - void - FeedbackService::unstar(Database::UserId userId, Database::TrackId trackId) - { - unstarObject(userId, trackId); - } - - bool - FeedbackService::isStarred(Database::UserId userId, Database::TrackId trackId) - { - return isObjectStarred(userId, trackId); - } - - template - void - FeedbackService::starObject(Database::UserId userId, DatabaseId objectId) - { - exec(_db, userId, objectId, [](Database::User::pointer user, auto object) - { - user.modify()->star(object); - }); - } - - template - void - FeedbackService::unstarObject(Database::UserId userId, DatabaseId objectId) - { - exec(_db, userId, objectId, [](Database::User::pointer user, auto object) - { - user.modify()->unstar(object); - }); - } - - template - bool - FeedbackService::isObjectStarred(Database::UserId userId, DatabaseId objectId) - { - bool res {}; - exec(_db, userId, objectId, [&res](Database::User::pointer user, auto object) - { - res = user->isStarred(object); - }); - return res; - } -} // ns Feedback - diff --git a/src/libs/services/feedback/impl/FeedbackService.hpp b/src/libs/services/feedback/impl/FeedbackService.hpp deleted file mode 100644 index c2b37b13..00000000 --- a/src/libs/services/feedback/impl/FeedbackService.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2021 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 "services/feedback/IFeedbackService.hpp" - -namespace Database -{ - class Db; -} - -namespace Feedback -{ - class FeedbackService : public IFeedbackService - { - public: - FeedbackService(Database::Db& db); - - private: - void star(Database::UserId userId, Database::ArtistId artistId) override; - void unstar(Database::UserId userId, Database::ArtistId artistId) override; - bool isStarred(Database::UserId userId, Database::ArtistId artistId) override; - - void star(Database::UserId userId, Database::ReleaseId releaseId) override; - void unstar(Database::UserId userId, Database::ReleaseId releaseId) override; - bool isStarred(Database::UserId userId, Database::ReleaseId artistId) override; - - void star(Database::UserId userId, Database::TrackId trackId) override; - void unstar(Database::UserId userId, Database::TrackId trackId) override; - bool isStarred(Database::UserId userId, Database::TrackId trackId) override; - - template - void starObject(Database::UserId userId, DatabaseId objectId); - template - void unstarObject(Database::UserId userId, DatabaseId objectId); - template - bool isObjectStarred(Database::UserId userId, DatabaseId objectId); - - Database::Db& _db; - }; -} // ns Feedback - diff --git a/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp b/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp deleted file mode 100644 index 67b89d62..00000000 --- a/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2021 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 "services/database/Types.hpp" - -namespace Database -{ - class Db; -} - -namespace Feedback -{ - class IFeedbackService - { - public: - virtual ~IFeedbackService() = default; - - virtual void star(Database::UserId userId, Database::ArtistId artistId) = 0; - virtual void unstar(Database::UserId userId, Database::ArtistId artistId) = 0; - virtual bool isStarred(Database::UserId userId, Database::ArtistId artistId) = 0; - - virtual void star(Database::UserId userId, Database::ReleaseId releaseId) = 0; - virtual void unstar(Database::UserId userId, Database::ReleaseId releaseId) = 0; - virtual bool isStarred(Database::UserId userId, Database::ReleaseId artistId) = 0; - - virtual void star(Database::UserId userId, Database::TrackId trackId) = 0; - virtual void unstar(Database::UserId userId, Database::TrackId trackId) = 0; - virtual bool isStarred(Database::UserId userId, Database::TrackId artistId) = 0; - }; - - std::unique_ptr createFeedbackService(Database::Db& db); -} // ns Feedback - diff --git a/src/libs/services/recommendation/impl/IEngine.hpp b/src/libs/services/recommendation/impl/IEngine.hpp index b4b9da65..789ef7cf 100644 --- a/src/libs/services/recommendation/impl/IEngine.hpp +++ b/src/libs/services/recommendation/impl/IEngine.hpp @@ -19,12 +19,11 @@ #pragma once -#include #include -#include #include "services/database/Types.hpp" -#include "utils/EnumSet.hpp" +#include "services/database/TrackListId.hpp" #include "services/recommendation/Types.hpp" +#include "utils/EnumSet.hpp" namespace Database { @@ -41,8 +40,8 @@ namespace Recommendation virtual void load(bool forceReload, const ProgressCallback& progressCallback = {}) = 0; virtual void requestCancelLoad() = 0; - virtual TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0; - virtual TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const = 0; + virtual TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0; + virtual TrackContainer findSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const = 0; virtual ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const = 0; virtual ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const = 0; }; diff --git a/src/libs/services/recommendation/impl/RecommendationService.cpp b/src/libs/services/recommendation/impl/RecommendationService.cpp index 59a805ec..76e4e693 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.cpp +++ b/src/libs/services/recommendation/impl/RecommendationService.cpp @@ -59,7 +59,7 @@ namespace Recommendation } TrackContainer - RecommendationService::getSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const + RecommendationService::findSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const { TrackContainer res; @@ -70,7 +70,7 @@ namespace Recommendation if (itEngine == std::cend(_engines)) continue; - res = itEngine->second->getSimilarTracksFromTrackList(trackListId, maxCount); + res = itEngine->second->findSimilarTracksFromTrackList(trackListId, maxCount); if (!res.empty()) break; } @@ -79,7 +79,7 @@ namespace Recommendation } TrackContainer - RecommendationService::getSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const + RecommendationService::findSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const { TrackContainer res; @@ -91,7 +91,7 @@ namespace Recommendation continue; const IEngine& engine {*itEngine->second}; - res = engine.getSimilarTracks(trackIds, maxCount); + res = engine.findSimilarTracks(trackIds, maxCount); if (!res.empty()) { LMS_LOG(RECOMMENDATION, DEBUG) << "Got " << res.size() << " similar tracks using engine '" << engineTypeToString(engineType) << "'"; @@ -140,6 +140,8 @@ namespace Recommendation if (itEngine == std::cend(_engines)) continue; + LMS_LOG(RECOMMENDATION, DEBUG) << "Trying engine '" << engineTypeToString(engineType) << "'"; + const IEngine& engine {*itEngine->second}; res = engine.getSimilarArtists(artistId, linkTypes, maxCount); if (!res.empty()) diff --git a/src/libs/services/recommendation/impl/RecommendationService.hpp b/src/libs/services/recommendation/impl/RecommendationService.hpp index 22e07217..25e6b5d4 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.hpp +++ b/src/libs/services/recommendation/impl/RecommendationService.hpp @@ -56,8 +56,8 @@ namespace Recommendation void load(bool forceReload, const ProgressCallback& progressCallback) override; void cancelLoad() override; - TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; - TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; + TrackContainer findSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override; ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const override; diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp index f2f3d458..9a714ff7 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp @@ -29,40 +29,35 @@ namespace Recommendation { -std::unique_ptr createClustersEngine(Database::Db& db) +using namespace Database; + +std::unique_ptr createClustersEngine(Db& db) { return std::make_unique(db); } TrackContainer -ClusterEngine::getSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const +ClusterEngine::findSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const { - Database::Session& dbSession {_db.getTLSSession()}; + Session& dbSession {_db.getTLSSession()}; - TrackContainer res; + auto transaction {dbSession.createSharedTransaction()}; - { - auto transaction {dbSession.createSharedTransaction()}; - - const auto tracks {Database::Track::getSimilarTracks(dbSession, trackIds, 0, maxCount)}; - res.reserve(tracks.size()); - std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const auto& track) { return track->getId(); }); - } - - return res; + const auto similarTrackIds {Track::findSimilarTracks(dbSession, trackIds, Range {0, maxCount})}; + return std::move(similarTrackIds.results); } TrackContainer -ClusterEngine::getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const +ClusterEngine::findSimilarTracksFromTrackList(TrackListId tracklistId, std::size_t maxCount) const { - Database::Session& dbSession {_db.getTLSSession()}; + Session& dbSession {_db.getTLSSession()}; TrackContainer res; { auto transaction {dbSession.createSharedTransaction()}; - const Database::TrackList::pointer trackList {Database::TrackList::getById(dbSession, tracklistId)}; + const TrackList::pointer trackList {TrackList::find(dbSession, tracklistId)}; if (!trackList) return res; @@ -75,15 +70,15 @@ ClusterEngine::getSimilarTracksFromTrackList(Database::TrackListId tracklistId, } ReleaseContainer -ClusterEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const +ClusterEngine::getSimilarReleases(ReleaseId releaseId, std::size_t maxCount) const { - Database::Session& dbSession {_db.getTLSSession()}; + Session& dbSession {_db.getTLSSession()}; ReleaseContainer res; { auto transaction {dbSession.createSharedTransaction()}; - auto release {Database::Release::getById(dbSession, releaseId)}; + auto release {Release::find(dbSession, releaseId)}; if (!release) return res; @@ -96,24 +91,18 @@ ClusterEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t max } ArtistContainer -ClusterEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet artistLinkTypes, std::size_t maxCount) const +ClusterEngine::getSimilarArtists(ArtistId artistId, EnumSet artistLinkTypes, std::size_t maxCount) const { - Database::Session& dbSession {_db.getTLSSession()}; + Session& dbSession {_db.getTLSSession()}; - ResultContainer res; - { - auto transaction {dbSession.createSharedTransaction()}; + auto transaction {dbSession.createSharedTransaction()}; - auto artist {Database::Artist::getById(dbSession, artistId)}; - if (!artist) - return res; + auto artist {Artist::find(dbSession, artistId)}; + if (!artist) + return {}; - const auto artists {artist->getSimilarArtists(artistLinkTypes, Database::Range {0, maxCount})}; - res.reserve(artists.size()); - std::transform(std::cbegin(artists), std::cend(artists), std::back_inserter(res), [](const auto& artist) { return artist->getId(); }); - } - - return res; + const auto similarArtistIds {artist->findSimilarArtists(artistLinkTypes, Range {0, maxCount})}; + return std::move(similarArtistIds.results); } } // namespace Recommendation diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp index faff863c..09c14072 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp @@ -38,8 +38,8 @@ namespace Recommendation void load(bool, const ProgressCallback&) override {} void requestCancelLoad() override {} - TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; - TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; + TrackContainer findSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override; ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const override; diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp index bb030bd6..daee3d4b 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngine.cpp @@ -36,7 +36,9 @@ namespace Recommendation { -std::unique_ptr createFeaturesEngine(Database::Db& db) +using namespace Database; + +std::unique_ptr createFeaturesEngine(Db& db) { return std::make_unique(db); } @@ -49,44 +51,13 @@ FeaturesEngine::getDefaultTrainFeatureSettings() { "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}}, + { "lowlevel.erbbands.mean", {1}}, + { "lowlevel.gfcc.mean", {1}}, }; return defaultTrainFeatureSettings; } -static -std::optional -getTrackFeatureValues(FeaturesEngine::FeaturesFetchFunc func, Database::TrackId trackId, const std::unordered_set& featureNames) -{ - return func(trackId, featureNames); -} - -static -std::optional -getTrackFeatureValuesFromDb(Database::Session& session, Database::TrackId trackId, const std::unordered_set& featureNames) -{ - auto func = [&](Database::TrackId trackId, const std::unordered_set& featureNames) - { - std::optional res; - - auto transaction {session.createSharedTransaction()}; - - Database::Track::pointer track {Database::Track::getById(session, trackId)}; - if (!track) - return res; - - res = track->getTrackFeatures()->getFeatureValuesMap(featureNames); - if (res->empty()) - res.reset(); - - return res; - }; - - return getTrackFeatureValues(func, trackId, featureNames); -} - static std::optional convertFeatureValuesMapToInputVector(const FeatureValuesMap& featureValuesMap, std::size_t nbDimensions) @@ -142,45 +113,46 @@ FeaturesEngine::loadFromTraining(const TrainSettings& trainSettings, const Progr LMS_LOG(RECOMMENDATION, DEBUG) << "Features dimension = " << nbDimensions; - Database::Session& session {_db.getTLSSession()}; + Session& session {_db.getTLSSession()}; - std::vector trackIds; + RangeResults trackFeaturesIds; { auto transaction {session.createSharedTransaction()}; - LMS_LOG(RECOMMENDATION, DEBUG) << "Getting Tracks with features..."; - trackIds = Database::Track::getAllIdsWithFeatures(session); - LMS_LOG(RECOMMENDATION, DEBUG) << "Getting Tracks with features DONE (found " << trackIds.size() << " tracks)"; + LMS_LOG(RECOMMENDATION, DEBUG) << "Getting Track features..."; + trackFeaturesIds = TrackFeatures::find(session, Range {}); + LMS_LOG(RECOMMENDATION, DEBUG) << "Getting Track features DONE (found " << trackFeaturesIds.results.size() << " track features)"; } std::vector samples; - std::vector samplesTrackIds; + std::vector samplesTrackIds; - samples.reserve(trackIds.size()); - samplesTrackIds.reserve(trackIds.size()); + samples.reserve(trackFeaturesIds.results.size()); + samplesTrackIds.reserve(trackFeaturesIds.results.size()); LMS_LOG(RECOMMENDATION, DEBUG) << "Extracting features..."; - for (Database::TrackId trackId : trackIds) + // TODO handle errors using exceptions + for (const TrackFeaturesId trackFeaturesId : trackFeaturesIds.results) { if (_loadCancelled) return; - std::optional featureValuesMap; + auto transaction {session.createSharedTransaction()}; - if (_featuresFetchFunc) - featureValuesMap = getTrackFeatureValues(_featuresFetchFunc, trackId, featureNames); - else - featureValuesMap = getTrackFeatureValuesFromDb(session, trackId, featureNames); - - if (!featureValuesMap) + TrackFeatures::pointer trackFeatures {TrackFeatures::find(session, trackFeaturesId)}; + if (!trackFeatures) continue; - std::optional inputVector {convertFeatureValuesMapToInputVector(*featureValuesMap, nbDimensions)}; + 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(trackId); + samplesTrackIds.emplace_back(trackFeatures->getTrack()->getId()); } LMS_LOG(RECOMMENDATION, DEBUG) << "Extracting features DONE"; @@ -249,41 +221,41 @@ FeaturesEngine::loadFromCache(FeaturesEngineCache cache) } TrackContainer -FeaturesEngine::getSimilarTracksFromTrackList(Database::TrackListId trackListId, std::size_t maxCount) const +FeaturesEngine::findSimilarTracksFromTrackList(TrackListId trackListId, std::size_t maxCount) const { const TrackContainer trackIds {[&] { TrackContainer res; - Database::Session& session {_db.getTLSSession()}; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - const Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)}; + const TrackList::pointer trackList {TrackList::find(session, trackListId)}; if (trackList) res = trackList->getTrackIds(); return res; }()}; - return getSimilarTracks(trackIds, maxCount); + return findSimilarTracks(trackIds, maxCount); } TrackContainer -FeaturesEngine::getSimilarTracks(const std::vector& tracksIds, std::size_t maxCount) const +FeaturesEngine::findSimilarTracks(const std::vector& tracksIds, std::size_t maxCount) const { auto similarTrackIds {getSimilarObjects(tracksIds, _trackMatrix, _trackPositions, maxCount)}; - Database::Session& session {_db.getTLSSession()}; + 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.createSharedTransaction()}; similarTrackIds.erase(std::remove_if(std::begin(similarTrackIds), std::end(similarTrackIds), - [&](Database::TrackId trackId) + [&](TrackId trackId) { - return !Database::Track::exists(session, trackId); + return !Track::exists(session, trackId); }), std::end(similarTrackIds)); } @@ -291,11 +263,11 @@ FeaturesEngine::getSimilarTracks(const std::vector& tracksIds } ReleaseContainer -FeaturesEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const +FeaturesEngine::getSimilarReleases(ReleaseId releaseId, std::size_t maxCount) const { auto similarReleaseIds {getSimilarObjects({releaseId}, _releaseMatrix, _releasePositions, maxCount)}; - Database::Session& session {_db.getTLSSession()}; + Session& session {_db.getTLSSession()}; if (!similarReleaseIds.empty()) { @@ -303,9 +275,9 @@ FeaturesEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t ma auto transaction {session.createSharedTransaction()}; similarReleaseIds.erase(std::remove_if(std::begin(similarReleaseIds), std::end(similarReleaseIds), - [&](Database::ReleaseId releaseId) + [&](ReleaseId releaseId) { - return !Database::Release::exists(session, releaseId); + return !Release::exists(session, releaseId); }), std::end(similarReleaseIds)); } @@ -313,9 +285,9 @@ FeaturesEngine::getSimilarReleases(Database::ReleaseId releaseId, std::size_t ma } ArtistContainer -FeaturesEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const +FeaturesEngine::getSimilarArtists(ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const { - auto getSimilarArtistIdsForLinkType {[&] (Database::TrackArtistLinkType linkType) + auto getSimilarArtistIdsForLinkType {[&] (TrackArtistLinkType linkType) { ArtistContainer similarArtistIds; @@ -328,9 +300,9 @@ FeaturesEngine::getSimilarArtists(Database::ArtistId artistId, EnumSetsecond, _artistPositions, maxCount); }}; - std::unordered_set similarArtistIds; + std::unordered_set similarArtistIds; - for (Database::TrackArtistLinkType linkType : linkTypes) + for (TrackArtistLinkType linkType : linkTypes) { const auto similarArtistIdsForLinkType {getSimilarArtistIdsForLinkType(linkType)}; similarArtistIds.insert(std::begin(similarArtistIdsForLinkType), std::end(similarArtistIdsForLinkType)); @@ -338,15 +310,15 @@ FeaturesEngine::getSimilarArtists(Database::ArtistId artistId, EnumSet>>(Database::TrackId, const std::unordered_set& /*features*/)>; - // Default is to retrieve the features from the database (may be slow). - // Use this only if you want to train different searchers with some cached data - static void setFeaturesFetchFunc(FeaturesFetchFunc func) { _featuresFetchFunc = func; } - static const FeatureSettingsMap& getDefaultTrainFeatureSettings(); private: void load(bool forceReload, const ProgressCallback& progressCallback) override; void requestCancelLoad() override; - TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; - TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override; + TrackContainer findSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override; ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const override; @@ -121,8 +116,6 @@ class FeaturesEngine : public IEngine TrackPositions _trackPositions; TrackMatrix _trackMatrix; - - static inline FeaturesFetchFunc _featuresFetchFunc; }; template diff --git a/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp index 348c66af..a27712db 100644 --- a/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp +++ b/src/libs/services/recommendation/impl/features/FeaturesEngineCache.hpp @@ -21,9 +21,8 @@ #include #include -#include -#include "services/database/Types.hpp" +#include "services/database/TrackId.hpp" #include "som/Network.hpp" namespace Recommendation { diff --git a/src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp b/src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp index 900c95a4..45704ac9 100644 --- a/src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp +++ b/src/libs/services/recommendation/include/services/recommendation/IRecommendationService.hpp @@ -20,8 +20,9 @@ #pragma once #include -#include #include "utils/EnumSet.hpp" +#include "services/database/TrackListId.hpp" +#include "services/database/Types.hpp" #include "services/recommendation/Types.hpp" namespace Database @@ -39,8 +40,8 @@ namespace Recommendation virtual void load(bool forceReload, const ProgressCallback& progressCallback = {}) = 0; virtual void cancelLoad() = 0; // wait for cancel done - virtual TrackContainer getSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0; - virtual TrackContainer getSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const = 0; + virtual TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0; + virtual TrackContainer findSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const = 0; virtual ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const = 0; virtual ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const = 0; }; diff --git a/src/libs/services/recommendation/include/services/recommendation/Types.hpp b/src/libs/services/recommendation/include/services/recommendation/Types.hpp index 0b258c3f..bfdaa67f 100644 --- a/src/libs/services/recommendation/include/services/recommendation/Types.hpp +++ b/src/libs/services/recommendation/include/services/recommendation/Types.hpp @@ -1,7 +1,9 @@ #pragma once #include -#include "services/database/Types.hpp" +#include "services/database/ArtistId.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" namespace Recommendation { diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index d96663be..d00c97ff 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -138,7 +138,7 @@ getOrCreateArtists(Session& session, const std::vector& artist // First try to get by MBID if (artistInfo.musicBrainzArtistID) { - artist = Artist::getByMBID(session, *artistInfo.musicBrainzArtistID); + artist = Artist::find(session, *artistInfo.musicBrainzArtistID); if (!artist) artist = createArtist(session, artistInfo); else @@ -151,7 +151,7 @@ getOrCreateArtists(Session& session, const std::vector& artist // Fall back on artist name (collisions may occur) if (!artistInfo.name.empty()) { - for (const Artist::pointer& sameNamedArtist : Artist::getByName(session, artistInfo.name)) + for (const Artist::pointer& sameNamedArtist : Artist::find(session, artistInfo.name)) { // Do not fallback on artist that is correctly tagged if (!allowFallbackOnMBIDEntries && sameNamedArtist->getMBID()) @@ -183,7 +183,7 @@ getOrCreateRelease(Session& session, const MetaData::Album& album) // First try to get by MBID if (album.musicBrainzAlbumID) { - release = Release::getByMBID(session, *album.musicBrainzAlbumID); + release = Release::find(session, *album.musicBrainzAlbumID); if (!release) { release = Release::create(session, album.name, album.musicBrainzAlbumID); @@ -200,7 +200,7 @@ getOrCreateRelease(Session& session, const MetaData::Album& album) // Fall back on release name (collisions may occur) if (!album.name.empty()) { - for (const Release::pointer& sameNamedRelease : Release::getByName(session, album.name)) + for (const Release::pointer& sameNamedRelease : Release::find(session, album.name)) { // do not fallback on properly tagged releases if (!sameNamedRelease->getMBID()) @@ -227,7 +227,7 @@ getOrCreateClusters(Session& session, const MetaData::Clusters& clustersNames) for (auto clusterNames : clustersNames) { - auto clusterType = ClusterType::getByName(session, clusterNames.first); + auto clusterType = ClusterType::find(session, clusterNames.first); if (!clusterType) continue; @@ -249,12 +249,12 @@ getOrCreateClusters(Session& session, const MetaData::Clusters& clustersNames) namespace Scanner { std::unique_ptr -createScannerService(Database::Db& db, Recommendation::IRecommendationService& recommendationService) +createScannerService(Db& db, Recommendation::IRecommendationService& recommendationService) { return std::make_unique(db, recommendationService); } -ScannerService::ScannerService(Database::Db& db, Recommendation::IRecommendationService& recommendationService) +ScannerService::ScannerService(Db& db, Recommendation::IRecommendationService& recommendationService) : _recommendationService {recommendationService} , _dbSession {db} { @@ -543,7 +543,7 @@ ScannerService::scan(bool forceScan) } bool -ScannerService::fetchTrackFeatures(Database::TrackId trackId, const UUID& recordingMBID) +ScannerService::fetchTrackFeatures(TrackId trackId, const UUID& recordingMBID) { std::map features; @@ -558,11 +558,11 @@ ScannerService::fetchTrackFeatures(Database::TrackId trackId, const UUID& record { auto uniqueTransaction {_dbSession.createUniqueTransaction()}; - Database::Track::pointer track {Database::Track::getById(_dbSession, trackId)}; + Track::pointer track {Track::find(_dbSession, trackId)}; if (!track) return false; - Database::TrackFeatures::create(_dbSession, track, data); + TrackFeatures::create(_dbSession, track, data); } return true; @@ -580,7 +580,7 @@ ScannerService::fetchTrackFeatures(ScanStats& stats) struct TrackInfo { - Database::TrackId id; + TrackId id; UUID recordingMBID; }; @@ -590,9 +590,12 @@ ScannerService::fetchTrackFeatures(ScanStats& stats) auto transaction {_dbSession.createSharedTransaction()}; - auto tracks {Database::Track::getAllWithRecordingMBIDAndMissingFeatures(_dbSession)}; - for (const auto& track : tracks) + auto trackIds {Track::findWithRecordingMBIDAndMissingFeatures(_dbSession, Range {})}; + for (const TrackId trackId : trackIds.results) + { + const Track::pointer track {Track::find(_dbSession, trackId)}; res.emplace_back(TrackInfo {track->getId(), *track->getRecordingMBID()}); + } return res; }()}; @@ -692,7 +695,7 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan, // Skip file if last write is the same auto transaction {_dbSession.createSharedTransaction()}; - const Track::pointer track {Track::getByPath(_dbSession, file)}; + const Track::pointer track {Track::findByPath(_dbSession, file)}; if (track && track->getLastWriteTime().toTime_t() == lastWriteTime.toTime_t() && track->getScanVersion() == _scanVersion) @@ -713,7 +716,7 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan, auto uniqueTransaction {_dbSession.createUniqueTransaction()}; - Track::pointer track {Track::getByPath(_dbSession, file) }; + Track::pointer track {Track::findByPath(_dbSession, file) }; // We estimate this is an audio file if: // - we found a least one audio stream @@ -778,30 +781,30 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan, track.modify()->clearArtistLinks(); // Do not fallback on artists with the same name but having a MBID for artist and releaseArtists, as it may be corrected by properly tagging files for (const Artist::pointer& artist : getOrCreateArtists(_dbSession, trackInfo->artists, false)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, artist, Database::TrackArtistLinkType::Artist)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, artist, TrackArtistLinkType::Artist)); for (const Artist::pointer& releaseArtist : getOrCreateArtists(_dbSession, trackInfo->albumArtists, false)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, releaseArtist, Database::TrackArtistLinkType::ReleaseArtist)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, releaseArtist, TrackArtistLinkType::ReleaseArtist)); // Allow fallbacks on artists with the same name even if they have MBID, since there is no tag to indicate the MBID of these artists // We could ask MusicBrainz to get all the information, but that would heavily slow down the import process for (const Artist::pointer& conductor : getOrCreateArtists(_dbSession, trackInfo->conductorArtists, true)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, conductor, Database::TrackArtistLinkType::Conductor)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, conductor, TrackArtistLinkType::Conductor)); for (const Artist::pointer& composer : getOrCreateArtists(_dbSession, trackInfo->composerArtists, true)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, composer, Database::TrackArtistLinkType::Composer)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, composer, TrackArtistLinkType::Composer)); for (const Artist::pointer& lyricist : getOrCreateArtists(_dbSession, trackInfo->lyricistArtists, true)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, lyricist, Database::TrackArtistLinkType::Lyricist)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, lyricist, TrackArtistLinkType::Lyricist)); for (const Artist::pointer& mixer : getOrCreateArtists(_dbSession, trackInfo->mixerArtists, true)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, mixer, Database::TrackArtistLinkType::Mixer)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, mixer, TrackArtistLinkType::Mixer)); for (const Artist::pointer& producer : getOrCreateArtists(_dbSession, trackInfo->producerArtists, true)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, producer, Database::TrackArtistLinkType::Producer)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, producer, TrackArtistLinkType::Producer)); for (const Artist::pointer& remixer : getOrCreateArtists(_dbSession, trackInfo->remixerArtists, true)) - track.modify()->addArtistLink(Database::TrackArtistLink::create(_dbSession, track, remixer, Database::TrackArtistLinkType::Remixer)); + track.modify()->addArtistLink(TrackArtistLink::create(_dbSession, track, remixer, TrackArtistLinkType::Remixer)); track.modify()->setScanVersion(_scanVersion); if (trackInfo->album) @@ -827,7 +830,8 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan, track.modify()->setRecordingMBID(trackInfo->recordingMBID); track.modify()->setTrackMBID(trackInfo->trackMBID); - track.modify()->setFeatures({}); // TODO: only if MBID changed? + if (auto trackFeatures {TrackFeatures::find(_dbSession, track->getId())}) + trackFeatures.remove(); // TODO: only if MBID changed? track.modify()->setHasCover(trackInfo->hasCover); track.modify()->setCopyright(trackInfo->copyright); track.modify()->setCopyrightURL(trackInfo->copyrightURL); @@ -921,26 +925,25 @@ ScannerService::removeMissingTracks(ScanStats& stats) stepStats.totalElems = trackCount; notifyInProgress(stepStats); - std::vector> trackPaths; + RangeResults trackPaths; std::vector tracksToRemove; for (std::size_t i {trackCount < batchSize ? 0 : trackCount - batchSize}; ; i -= (i > batchSize ? batchSize : i)) { - trackPaths.clear(); tracksToRemove.clear(); { auto transaction {_dbSession.createSharedTransaction()}; - trackPaths = Track::getAllPaths(_dbSession, i, batchSize); + trackPaths = Track::findPaths(_dbSession, Range {i, batchSize}); } - for (const auto& [trackId, trackPath] : trackPaths) + for (const Track::PathResult& trackPath : trackPaths.results) { if (_abortScan) return; - if (!checkFile(trackPath, _mediaDirectory, _fileExtensions)) - tracksToRemove.push_back(trackId); + if (!checkFile(trackPath.path, _mediaDirectory, _fileExtensions)) + tracksToRemove.push_back(trackPath.trackId); stepStats.processedElems++; } @@ -951,7 +954,7 @@ ScannerService::removeMissingTracks(ScanStats& stats) for (const TrackId trackId : tracksToRemove) { - Track::pointer track {Track::getById(_dbSession, trackId)}; + Track::pointer track {Track::find(_dbSession, trackId)}; if (track) { track.remove(); @@ -977,9 +980,10 @@ ScannerService::removeOrphanEntries() auto transaction {_dbSession.createUniqueTransaction()}; // Now process orphan Cluster (no track) - auto clusters {Cluster::getAllOrphans(_dbSession)}; - for (auto& cluster : clusters) + auto clusterIds {Cluster::findOrphans(_dbSession, Range {})}; + for (ClusterId clusterId : clusterIds.results) { + Cluster::pointer cluster {Cluster::find(_dbSession, clusterId)}; LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan cluster '" << cluster->getName() << "'"; cluster.remove(); } @@ -989,9 +993,10 @@ ScannerService::removeOrphanEntries() { auto transaction {_dbSession.createUniqueTransaction()}; - auto artists {Artist::getAllOrphans(_dbSession)}; - for (auto& artist : artists) + auto artistIds {Artist::findAllOrphans(_dbSession, Range {})}; + for (const ArtistId artistId : artistIds.results) { + Artist::pointer artist {Artist::find(_dbSession, artistId)}; LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan artist '" << artist->getName() << "'"; artist.remove(); } @@ -1001,9 +1006,10 @@ ScannerService::removeOrphanEntries() { auto transaction {_dbSession.createUniqueTransaction()}; - auto releases {Release::getAllOrphans(_dbSession)}; - for (auto& release : releases) + auto releases {Release::findOrphans(_dbSession, Range {})}; + for (const ReleaseId releaseId : releases.results) { + Release::pointer release {Release::find(_dbSession, releaseId)}; LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan release '" << release->getName() << "'"; release.remove(); } @@ -1019,9 +1025,10 @@ ScannerService::checkDuplicatedAudioFiles(ScanStats& stats) auto transaction {_dbSession.createSharedTransaction()}; - const std::vector tracks = Database::Track::getMBIDDuplicates(_dbSession); - for (const Track::pointer& track : tracks) + const RangeResults tracks = Track::findMBIDDuplicates(_dbSession, Range {}); + for (const TrackId trackId : tracks.results) { + const Track::pointer track {Track::find(_dbSession, trackId)}; if (auto trackMBID {track->getTrackMBID()}) { LMS_LOG(DBUPDATER, INFO) << "Found duplicated Track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); diff --git a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp index 530a946e..1b5e3de4 100644 --- a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp +++ b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp @@ -24,7 +24,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/TrackId.hpp" namespace Scanner { diff --git a/src/libs/services/scrobbling/impl/IScrobbler.hpp b/src/libs/services/scrobbling/impl/IScrobbler.hpp index cc0a98ca..3a41242d 100644 --- a/src/libs/services/scrobbling/impl/IScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/IScrobbler.hpp @@ -23,8 +23,9 @@ #include #include -#include - +#include "services/database/ArtistId.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackListId.hpp" #include "services/scrobbling/Listen.hpp" namespace Database @@ -36,18 +37,26 @@ namespace Database namespace Scrobbling { - class IScrobbler { public: virtual ~IScrobbler() = default; + // Listens virtual void listenStarted(const Listen& listen) = 0; virtual void listenFinished(const Listen& listen, std::optional duration) = 0; - virtual void addTimedListen(const TimedListen& listen) = 0; - virtual Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user) = 0; + // Feedback + virtual void onStarred(Database::UserId, Database::ArtistId) {}; + virtual void onUnstarred(Database::UserId, Database::ArtistId) {}; + virtual void onStarred(Database::UserId, Database::ReleaseId) {}; + virtual void onUnstarred(Database::UserId, Database::ReleaseId) {}; + virtual void onStarred(Database::UserId, Database::TrackId) {}; + virtual void onUnstarred(Database::UserId, Database::TrackId) {}; + +// virtual void star(Database::TrackId trackId) = 0; +// virtual void unstar(Database::TrackId trackId) = 0; }; std::unique_ptr createScrobbler(std::string_view backendName); diff --git a/src/libs/services/scrobbling/impl/ScrobblerBase.cpp b/src/libs/services/scrobbling/impl/ScrobblerBase.cpp new file mode 100644 index 00000000..a21d312b --- /dev/null +++ b/src/libs/services/scrobbling/impl/ScrobblerBase.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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 "services/scrobbling/Listen.hpp" + +namespace Scrobbling +{ + ScrobblerBase::ScrobblerBase(Database::Db& db) + : _db {db} + { + } + + Database::TrackListId + ScrobberBase::getListensTrackList(Database::UserId userId, std::string_view trackListName) + { + std::string_view listensTrackListName {getListensTrackListName()}; + Database::Session& session {_db.getTLSSession()}; + + { + auto transaction {session.createSharedTransaction()}; + + if (auto trackList {Database::TrackList::get(session, listensTrackListName, Database::TrackList::Type::Internal, userId)}) + return trackList->getId(); + } + + { + auto transaction {session.createUniqueTransaction()}; + if (auto trackList {Database::TrackList::get(session, listensTrackListName, Database::TrackList::Type::Internal, userId)}) + return trackList->getId(); + + const Database::User::pointer user {Database::User::getById(session, userId)}; + if (!user) + return {}; + + Database::TrackList::pointer trackList {Database::TrackList::create(session, listensTrackListName, Database::TrackList::Type::Internal, false, user)}; + return trackList->getId(); + } + } + + bool + ScrobberBase::saveTimedListen(const TimedListen& listen, std::string_view trackListName) + { + const Database::TrackListId trackListId {getListensTrackList(listen.userId)}; + if (!trackListId) + return false; + + { + + return true; + } + } +} // ns Scrobbling + diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp index 455ccc65..607c1f1e 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -18,13 +18,17 @@ */ #include "ScrobblingService.hpp" +#include "ScrobblingService.impl.hpp" #include "services/database/Artist.hpp" #include "services/database/Db.hpp" +#include "services/database/Listen.hpp" #include "services/database/Release.hpp" #include "services/database/Session.hpp" +#include "services/database/StarredArtist.hpp" +#include "services/database/StarredRelease.hpp" +#include "services/database/StarredTrack.hpp" #include "services/database/Track.hpp" -#include "services/database/TrackList.hpp" #include "services/database/User.hpp" #include "internal/InternalScrobbler.hpp" @@ -43,202 +47,248 @@ namespace Scrobbling ScrobblingService::ScrobblingService(boost::asio::io_context& ioContext, Db& db) : _db {db} { - _scrobblers.emplace(Database::Scrobbler::Internal, std::make_unique(_db)); - _scrobblers.emplace(Database::Scrobbler::ListenBrainz, std::make_unique(ioContext, _db)); + _scrobblers.emplace(Scrobbler::Internal, std::make_unique(_db)); + _scrobblers.emplace(Scrobbler::ListenBrainz, std::make_unique(ioContext, _db)); } void ScrobblingService::listenStarted(const Listen& listen) { - if (std::optional scrobbler {getUserScrobbler(listen.userId)}) + if (std::optional scrobbler {getUserScrobbler(listen.userId)}) _scrobblers[*scrobbler]->listenStarted(listen); } void ScrobblingService::listenFinished(const Listen& listen, std::optional duration) { - if (std::optional scrobbler {getUserScrobbler(listen.userId)}) + if (std::optional scrobbler {getUserScrobbler(listen.userId)}) _scrobblers[*scrobbler]->listenFinished(listen, duration); } void ScrobblingService::addTimedListen(const TimedListen& listen) { - if (std::optional scrobbler {getUserScrobbler(listen.userId)}) + if (std::optional scrobbler {getUserScrobbler(listen.userId)}) _scrobblers[*scrobbler]->addTimedListen(listen); } - std::optional - ScrobblingService::getUserScrobbler(Database::UserId userId) + std::optional + ScrobblingService::getUserScrobbler(UserId userId) { - std::optional scrobbler; + std::optional scrobbler; Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - if (const User::pointer user {User::getById(session, userId)}) + if (const User::pointer user {User::find(session, userId)}) scrobbler = user->getScrobbler(); return scrobbler; } ScrobblingService::ArtistContainer - ScrobblingService::getRecentArtists(UserId userId, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) + ScrobblingService::getRecentArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, Range range) { ArtistContainer res; + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return res; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - const User::pointer user {User::getById(session, userId)}; - if (!user) - return res; - - const ObjectPtr history {getListensTrackList(session, user)}; - if (history) - { - for (const Artist::pointer& artist : history->getArtistsReverse(clusterIds, linkType, range, moreResults)) - res.push_back(artist->getId()); - } - - return res; + return Database::Listen::getRecentArtists(session, userId, *scrobbler, clusterIds, linkType, range); } ScrobblingService::ReleaseContainer - ScrobblingService::getRecentReleases(Database::UserId userId, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) + ScrobblingService::getRecentReleases(UserId userId, const std::vector& clusterIds, Range range) { ReleaseContainer res; + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return res; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - const User::pointer user {User::getById(session, userId)}; - if (!user) - return res; - - const ObjectPtr history {getListensTrackList(session, user)}; - if (history) - { - for (const Release::pointer& release : history->getReleasesReverse(clusterIds, range, moreResults)) - res.push_back(release->getId()); - } - - return res; + return Database::Listen::getRecentReleases(session, userId, *scrobbler, clusterIds, range); } ScrobblingService::TrackContainer - ScrobblingService::getRecentTracks(Database::UserId userId, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) + ScrobblingService::getRecentTracks(UserId userId, const std::vector& clusterIds, Range range) { TrackContainer res; + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return res; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - const User::pointer user {User::getById(session, userId)}; - if (!user) - return res; - - const ObjectPtr history {getListensTrackList(session, user)}; - if (history) - { - for (const Track::pointer& track : history->getTracksReverse(clusterIds, range, moreResults)) - res.push_back(track->getId()); - } - - return res; + return Database::Listen::getRecentTracks(session, userId, *scrobbler, clusterIds, range); } - // Top ScrobblingService::ArtistContainer - ScrobblingService::getTopArtists(UserId userId, - const std::vector& clusterIds, - std::optional linkType, - std::optional range, - bool& moreResults) + ScrobblingService::getTopArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, Range range) { ArtistContainer res; + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return res; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - const User::pointer user {User::getById(session, userId)}; - if (!user) - return res; - - const ObjectPtr history {getListensTrackList(session, user)}; - if (history) - { - for (const Artist::pointer& artist : history->getTopArtists(clusterIds, linkType, range, moreResults)) - res.push_back(artist->getId()); - } - - return res; + return Database::Listen::getTopArtists(session, userId, *scrobbler, clusterIds, linkType, range); } ScrobblingService::ReleaseContainer - ScrobblingService::getTopReleases(Database::UserId userId, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) + ScrobblingService::getTopReleases(UserId userId, const std::vector& clusterIds, Range range) { ReleaseContainer res; + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return res; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - const User::pointer user {User::getById(session, userId)}; - if (!user) - return res; - - const ObjectPtr history {getListensTrackList(session, user)}; - if (history) - { - for (const Release::pointer& release : history->getTopReleases(clusterIds, range, moreResults)) - res.push_back(release->getId()); - } - - return res; + return Database::Listen::getTopReleases(session, userId, *scrobbler, clusterIds, range); } ScrobblingService::TrackContainer - ScrobblingService::getTopTracks(Database::UserId userId, - const std::vector& clusterIds, - std::optional range, - bool& moreResults) + ScrobblingService::getTopTracks(UserId userId, const std::vector& clusterIds, Range range) { TrackContainer res; + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return res; + Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - const User::pointer user {User::getById(session, userId)}; - if (!user) - return res; - - if (const ObjectPtr history {getListensTrackList(session, user)}) - { - for (const Track::pointer& track : history->getTopTracks(clusterIds, range, moreResults)) - res.push_back(track->getId()); - } - - return res; + return Database::Listen::getTopTracks(session, userId, *scrobbler, clusterIds, range); } - Database::ObjectPtr - ScrobblingService::getListensTrackList(Session& session, Database::ObjectPtr user) + void + ScrobblingService::star(UserId userId, ArtistId artistId) { - return _scrobblers[user->getScrobbler()]->getListensTrackList(session, user); + star(userId, artistId); } + void + ScrobblingService::unstar(UserId userId, ArtistId artistId) + { + unstar(userId, artistId); + } + + bool + ScrobblingService::isStarred(UserId userId, ArtistId artistId) + { + return isStarred(userId, artistId); + } + + ScrobblingService::ArtistContainer + ScrobblingService::getStarredArtists(UserId userId, const std::vector& clusterIds, + std::optional linkType, + ArtistSortMethod sortMethod, + Range range) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return {}; + + Artist::FindParameters params; + params.setStarringUser(userId, *scrobbler); + params.setClusters(clusterIds); + params.setLinkType(linkType); + params.setSortMethod(sortMethod); + params.setRange(range); + + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + return Artist::find(session, params); + } + + void + ScrobblingService::star(UserId userId, ReleaseId releaseId) + { + star(userId, releaseId); + } + + void + ScrobblingService::unstar(UserId userId, ReleaseId releaseId) + { + unstar(userId, releaseId); + } + + bool + ScrobblingService::isStarred(UserId userId, ReleaseId releaseId) + { + return isStarred(userId, releaseId); + } + + ScrobblingService::ReleaseContainer + ScrobblingService::getStarredReleases(UserId userId, const std::vector& clusterIds, Range range) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return {}; + + Release::FindParameters params; + params.setStarringUser(userId, *scrobbler); + params.setClusters(clusterIds); + params.setSortMethod(ReleaseSortMethod::StarredDateDesc); + params.setRange(range); + + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + return Release::find(session, params); + } + + void + ScrobblingService::star(UserId userId, TrackId trackId) + { + star(userId, trackId); + } + + void + ScrobblingService::unstar(UserId userId, TrackId trackId) + { + unstar(userId, trackId); + } + + bool + ScrobblingService::isStarred(UserId userId, TrackId trackId) + { + return isStarred(userId, trackId); + } + + ScrobblingService::TrackContainer + ScrobblingService::getStarredTracks(UserId userId, const std::vector& clusterIds, Range range) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return {}; + + Track::FindParameters params; + params.setStarringUser(userId, *scrobbler); + params.setClusters(clusterIds); + params.setSortMethod(TrackSortMethod::StarredDateDesc); + params.setRange(range); + + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + return Track::find(session, params); + } } // ns Scrobbling diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.hpp b/src/libs/services/scrobbling/impl/ScrobblingService.hpp index 6b1b9959..63377ad6 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.hpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.hpp @@ -41,39 +41,57 @@ namespace Scrobbling ArtistContainer getRecentArtists(Database::UserId userId, const std::vector& clusterIds, std::optional linkType, - std::optional range, - bool& moreResults) override; + Database::Range range) override; ReleaseContainer getRecentReleases(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + Database::Range range) override; TrackContainer getRecentTracks(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + Database::Range range) override; ArtistContainer getTopArtists(Database::UserId userId, const std::vector& clusterIds, std::optional linkType, - std::optional range, - bool& moreResults) override; + Database::Range range) override; ReleaseContainer getTopReleases(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + Database::Range range) override; TrackContainer getTopTracks(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) override; + Database::Range range) override; - Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user); + void star(Database::UserId userId, Database::ArtistId artistId) override; + void unstar(Database::UserId userId, Database::ArtistId artistId) override; + bool isStarred(Database::UserId userId, Database::ArtistId artistId) override; + ArtistContainer getStarredArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::ArtistSortMethod sortMethod, + Database::Range range) override; + + void star(Database::UserId userId, Database::ReleaseId releaseId) override; + void unstar(Database::UserId userId, Database::ReleaseId releaseId) override; + bool isStarred(Database::UserId userId, Database::ReleaseId artistId) override; + ReleaseContainer getStarredReleases(Database::UserId userId, const std::vector& clusterIds, Database::Range range) override; + + void star(Database::UserId userId, Database::TrackId trackId) override; + void unstar(Database::UserId userId, Database::TrackId trackId) override; + bool isStarred(Database::UserId userId, Database::TrackId trackId) override; + TrackContainer getStarredTracks(Database::UserId userId, const std::vector& clusterIds, Database::Range range) override; std::optional getUserScrobbler(Database::UserId userId); + template + void star(Database::UserId userId, ObjIdType id); + template + void unstar(Database::UserId userId, ObjIdType id); + template + bool isStarred(Database::UserId userId, ObjIdType id); + Database::Db& _db; std::unordered_map> _scrobblers; }; diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp b/src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp new file mode 100644 index 00000000..1b7ef03d --- /dev/null +++ b/src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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 "services/database/Db.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" + +namespace Scrobbling +{ + using namespace Database; + + template + void + ScrobblingService::star(UserId userId, ObjIdType objId) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return; + + { + Session& session {_db.getTLSSession()}; + auto transaction {session.createUniqueTransaction()}; + + typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; + if (!starredObj) + { + const typename ObjType::pointer obj {ObjType::find(session, objId)}; + if (!obj) + return; + + const User::pointer user {User::find(session, userId)}; + if (!user) + return; + + starredObj = StarredObjType::create(session, obj, user, *scrobbler); + } + starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime()); + } + _scrobblers[*scrobbler]->onStarred(userId, objId); + } + + template + void + ScrobblingService::unstar(UserId userId, ObjIdType objId) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return; + + { + Session& session {_db.getTLSSession()}; + auto transaction {session.createUniqueTransaction()}; + + if (typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}) + starredObj.remove(); + } + _scrobblers[*scrobbler]->onUnstarred(userId, objId); + } + + template + bool + ScrobblingService::isStarred(UserId userId, ObjIdType objId) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return false; + + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + return StarredObjType::find(session, objId, userId, *scrobbler); + } + +} // ns Scrobbling + diff --git a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp index b0dc6964..3c2f17d4 100644 --- a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp @@ -20,22 +20,19 @@ #include "InternalScrobbler.hpp" #include "services/database/Db.hpp" +#include "services/database/Listen.hpp" #include "services/database/Session.hpp" -#include "services/database/Track.hpp" -#include "services/database/TrackList.hpp" #include "services/database/User.hpp" -#include "utils/Logger.hpp" +#include "services/database/Track.hpp" namespace Scrobbling { - static const std::string historyTracklistName {"__scrobbler_internal_history__"}; - InternalScrobbler::InternalScrobbler(Database::Db& db) - : _db {db} + : _db {db} {} void - InternalScrobbler::listenStarted(const Listen& /*listen*/) + InternalScrobbler::listenStarted(const Listen&) { // nothing to do } @@ -43,7 +40,7 @@ namespace Scrobbling void InternalScrobbler::listenFinished(const Listen& listen, std::optional duration) { - // record tracks that have been played for at least of few seconds... + // only record tracks that have been played for at least of few seconds... if (duration && *duration < std::chrono::seconds {5}) return; @@ -54,29 +51,20 @@ namespace Scrobbling InternalScrobbler::addTimedListen(const TimedListen& listen) { Database::Session& session {_db.getTLSSession()}; - auto transaction {session.createUniqueTransaction()}; - const Database::User::pointer user {Database::User::getById(session, listen.userId)}; + if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::Internal, listen.listenedAt)) + return; + + const Database::User::pointer user {Database::User::find(session, listen.userId)}; if (!user) return; - Database::TrackList::pointer tracklist {getListensTrackList(session, user)}; - if (!tracklist) - tracklist = Database::TrackList::create(session, historyTracklistName, Database::TrackList::Type::Internal, false, user); - - const Database::Track::pointer track {Database::Track::getById(session, listen.trackId)}; + const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; if (!track) return; - Database::TrackListEntry::create(session, track, getListensTrackList(session, user), listen.listenedAt); + Database::Listen::create(session, user, track, Database::Scrobbler::Internal, listen.listenedAt); } - - Database::TrackList::pointer - InternalScrobbler::getListensTrackList(Database::Session& session, Database::ObjectPtr user) - { - return Database::TrackList::get(session, historyTracklistName, Database::TrackList::Type::Internal, user); - } - } // Scrobbling diff --git a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp index 45c067da..1c4d7df4 100644 --- a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp @@ -34,14 +34,12 @@ namespace Scrobbling InternalScrobbler(Database::Db& db); private: + // IScrobbler void listenStarted(const Listen& listen) override; void listenFinished(const Listen& listen, std::optional duration) override; - void addTimedListen(const TimedListen& listen) override; - Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user) override; - - Database::Db& _db; + Database::Db& _db; }; } // Scrobbling diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index 20b68fc0..db614b18 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -46,7 +46,7 @@ namespace { auto transaction {session.createSharedTransaction()}; - const Database::Track::pointer track {Database::Track::getById(session, trackId)}; + const Database::Track::pointer track {Database::Track::find(session, trackId)}; if (!track) return false; @@ -62,7 +62,7 @@ namespace { auto transaction {session.createSharedTransaction()}; - const Database::Track::pointer track {Database::Track::getById(session, listen.trackId)}; + const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; if (!track) return std::nullopt; @@ -179,12 +179,6 @@ namespace Scrobbling::ListenBrainz enqueListen(listen, listen.listenedAt); } - Database::TrackList::pointer - Scrobbler::getListensTrackList(Database::Session& session, Database::User::pointer user) - { - return Utils::getListensTrackList(session, user); - } - void Scrobbler::enqueListen(const Listen& listen, const Wt::WDateTime& timePoint) { diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp index c1b2406e..8134ac3c 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp @@ -49,11 +49,9 @@ namespace Scrobbling::ListenBrainz void listenStarted(const Listen& listen) override; void listenFinished(const Listen& listen, std::optional duration) override; void addTimedListen(const TimedListen& listen) override; - Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user) override; // Submit listens void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); - //std::optional createSubmitListenRequestData(const Listen& listen, const Wt::WDateTime& timePoint); boost::asio::io_context& _ioContext; Database::Db& _db; diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index c4216a33..644172a0 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -27,6 +27,7 @@ #include "services/database/Artist.hpp" #include "services/database/Db.hpp" +#include "services/database/Listen.hpp" #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" @@ -87,10 +88,10 @@ namespace } } - Database::Track::pointer + Database::TrackId tryMatchListen(Database::Session& session, const Wt::Json::Object& metadata) { - Database::Track::pointer track; + using namespace Database; // first try to get the associated track using MBIDs, and then fallback on names if (metadata.type("additional_info") == Wt::Json::Type::Object) @@ -98,30 +99,29 @@ namespace const Wt::Json::Object& additionalInfo = metadata.get("additional_info"); if (std::optional recordingMBID {UUID::fromString(additionalInfo.get("recording_mbid").orIfNull(""))}) { - const auto tracks {Database::Track::getByRecordingMBID(session, *recordingMBID)}; + const auto tracks {Track::findByRecordingMBID(session, *recordingMBID)}; // if duplicated files, do not record it (let the user correct its database) if (tracks.size() == 1) - track = tracks.front(); + return tracks.front()->getId(); } } - if (track) - return track; - // these fields are mandatory const std::string trackName {static_cast(metadata.get("track_name"))}; const std::string releaseName {static_cast(metadata.get("release_name"))}; - auto tracks {Database::Track::getByNameAndReleaseName(session, trackName, releaseName)}; - if (tracks.size() > 1) + auto tracks {Track::findByNameAndReleaseName(session, trackName, releaseName)}; + if (tracks.results.size() > 1) { - tracks.erase(std::remove_if(std::begin(tracks), std::end(tracks), - [&](const Database::Track::pointer track) + tracks.results.erase(std::remove_if(std::begin(tracks.results), std::end(tracks.results), + [&](const TrackId trackId) { + const Track::pointer track {Track::find(session, trackId)}; + if (std::string artistName {metadata.get("artist_name").orIfNull("")}; !artistName.empty()) { - const auto& artists {track->getArtists({Database::TrackArtistLinkType::Artist})}; - if (std::none_of(std::begin(artists), std::end(artists), [&](const Database::Artist::pointer& artist) { return artist->getName() == artistName; })) + const auto& artists {track->getArtists({TrackArtistLinkType::Artist})}; + if (std::none_of(std::begin(artists), std::end(artists), [&](const Artist::pointer& artist) { return artist->getName() == artistName; })) return true; } if (metadata.type("additional_info") == Wt::Json::Type::Object) @@ -145,13 +145,13 @@ namespace } return false; - }), std::end(tracks)); + }), std::end(tracks.results)); } - if (tracks.size() == 1) - track = tracks.front(); + if (tracks.results.size() == 1) + return tracks.results.front(); - return track; + return {}; } struct ParseGetListensResult @@ -198,8 +198,8 @@ namespace else if (listenedAt < result.oldestEntry) result.oldestEntry = listenedAt; - if (const Database::Track::pointer track {tryMatchListen(session, metadata)}) - result.matchedListens.emplace_back(Scrobbling::TimedListen {{userId, track->getId()}, listenedAt}); + if (Database::TrackId trackId {tryMatchListen(session, metadata)}; trackId.isValid()) + result.matchedListens.emplace_back(Scrobbling::TimedListen {{userId, trackId}, listenedAt}); } } catch (const Wt::WException& error) @@ -234,15 +234,18 @@ namespace Scrobbling::ListenBrainz auto transaction {session.createUniqueTransaction()}; - const Database::User::pointer user {Database::User::getById(session, listen.userId)}; + if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::ListenBrainz, listen.listenedAt)) + return; + + const Database::User::pointer user {Database::User::find(session, listen.userId)}; if (!user) return; - const Database::Track::pointer track {Database::Track::getById(session, listen.trackId)}; + const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; if (!track) return; - Database::TrackListEntry::create(session, track, Utils::getOrCreateListensTrackList(session, user), listen.listenedAt); + Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt); UserContext& context {getUserContext(listen.userId)}; if (context.listenCount) @@ -304,14 +307,14 @@ namespace Scrobbling::ListenBrainz assert(!isFetching()); - std::vector userIds; + Database::RangeResults userIds; { Database::Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - userIds = Database::User::getAllIds(_db.getTLSSession()); + userIds = Database::User::find(_db.getTLSSession(), Database::Range {}); } - for (const Database::UserId userId : userIds) + for (const Database::UserId userId : userIds.results) { if (Utils::getListenBrainzToken(_db.getTLSSession(), userId)) startGetListens(getUserContext(userId)); @@ -458,25 +461,22 @@ namespace Scrobbling::ListenBrainz auto transaction {session.createUniqueTransaction()}; - Database::User::pointer user {Database::User::getById(session, context.userId)}; + Database::User::pointer user {Database::User::find(session, context.userId)}; if (!user) return; - Database::TrackList::pointer tracklist {Utils::getOrCreateListensTrackList(session, user)}; - for (const TimedListen& listen : parseResult.matchedListens) { - const Database::Track::pointer track {Database::Track::getById(session, listen.trackId)}; + const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; if (!track) continue; - if (!tracklist->getEntryByTrackAndDateTime(track, listen.listenedAt)) - { - context.importedListenCount++; - Database::TrackListEntry::create(session, track, tracklist, listen.listenedAt); - } + if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::ListenBrainz, listen.listenedAt)) + continue; + + Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt); + context.importedListenCount++; } } - } // namespace Scrobbling::ListenBrainz diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index 70efa377..84ed3a6a 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "services/database/Types.hpp" #include "services/scrobbling/Listen.hpp" diff --git a/src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp new file mode 100644 index 00000000..0f7bf48f --- /dev/null +++ b/src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "services/database/Types.hpp" +#include "services/scrobbling/Listen.hpp" + +namespace Database +{ + class Db; + class Session; + class TrackList; + class User; +} + +namespace Scrobbling::ListenBrainz +{ + class ListensSynchronizer + { + public: + ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, std::string_view baseAPIUrl); + + void saveListen(const TimedListen& listen); + + private: + struct UserContext + { + UserContext(Database::UserId id) : userId {id} {} + + UserContext(const UserContext&) = delete; + UserContext(UserContext&&) = delete; + UserContext& operator=(const UserContext&) = delete; + UserContext& operator=(UserContext&&) = delete; + + const Database::UserId userId; + bool fetching {}; + + // resetted at each fetch + std::string listenBrainzUserName; // need to be resolved first + Wt::WDateTime maxDateTime; + std::size_t fetchedLoveCount{}; + std::size_t matchedLoveCount{}; + std::size_t importedLoveCount{}; + }; + + UserContext& getUserContext(Database::UserId userId); + bool isFetching() const; + void scheduleGetListens(std::chrono::seconds fromNow); + void startGetListens(); + void startGetListens(UserContext& context); + void onGetListensEnded(UserContext& context); + void enqueValidateToken(UserContext& context); + void enqueGetListenCount(UserContext& context); + void enqueGetListens(UserContext& context); + void processGetListensResponse(std::string_view body, UserContext& context); + + boost::asio::io_context& _ioContext; + boost::asio::io_context::strand _strand {_ioContext}; + Database::Db& _db; + std::string _baseAPIUrl; + boost::asio::steady_timer _getListensTimer {_ioContext}; + + std::unordered_map _userContexts; + + const std::size_t _maxSyncListenCount; + const std::chrono::hours _syncListensPeriod; + }; +} // Scrobbling::ListenBrainz + diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp index a2d9c9e1..e956370f 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp @@ -34,7 +34,7 @@ namespace Scrobbling::ListenBrainz::Utils { auto transaction {session.createSharedTransaction()}; - const Database::User::pointer user {Database::User::getById(session, userId)}; + const Database::User::pointer user {Database::User::find(session, userId)}; if (!user) return std::nullopt; @@ -43,21 +43,4 @@ namespace Scrobbling::ListenBrainz::Utils return user->getListenBrainzToken(); } - - Database::TrackList::pointer - getListensTrackList(Database::Session& session, Database::User::pointer user) - { - return Database::TrackList::get(session, historyTracklistName, Database::TrackList::Type::Internal, user); - } - - Database::TrackList::pointer - getOrCreateListensTrackList(Database::Session& session, Database::User::pointer user) - { - Database::TrackList::pointer tracklist {getListensTrackList(session, user)}; - if (!tracklist) - tracklist = Database::TrackList::create(session, historyTracklistName, Database::TrackList::Type::Internal, false, user); - - return tracklist; - } - } diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp index aca140c7..4a4897c3 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp @@ -19,21 +19,18 @@ #pragma once -#include #include "utils/UUID.hpp" #include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace Database { class Session; - class TrackList; class User; } namespace Scrobbling::ListenBrainz::Utils { std::optional getListenBrainzToken(Database::Session& session, Database::UserId userId); - Database::ObjectPtr getOrCreateListensTrackList(Database::Session& session, Database::ObjectPtr user); - Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user); } diff --git a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp index cf67d7fc..18b00129 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp @@ -24,21 +24,17 @@ #include #include #include -#include - -#include #include "services/scrobbling/Listen.hpp" +#include "services/database/ArtistId.hpp" +#include "services/database/ClusterId.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" #include "services/database/Types.hpp" namespace Database { - class Artist; class Db; - class Release; - class Session; - class Track; - class User; } namespace Scrobbling @@ -56,45 +52,56 @@ namespace Scrobbling virtual void addTimedListen(const TimedListen& listen) = 0; // Stats - template - using ResultContainer = std::vector; - - using ArtistContainer = ResultContainer; - using ReleaseContainer = ResultContainer; - using TrackContainer = ResultContainer; + using ArtistContainer = Database::RangeResults; + using ReleaseContainer = Database::RangeResults; + using TrackContainer = Database::RangeResults; // From most recent to oldest virtual ArtistContainer getRecentArtists(Database::UserId userId, const std::vector& clusterIds, std::optional linkType, - std::optional range, - bool& moreResults) = 0; + Database::Range range) = 0; virtual ReleaseContainer getRecentReleases(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + Database::Range range) = 0; virtual TrackContainer getRecentTracks(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + Database::Range range) = 0; // Top virtual ArtistContainer getTopArtists(Database::UserId userId, const std::vector& clusterIds, std::optional linkType, - std::optional range, - bool& moreResults) = 0; + Database::Range) = 0; virtual ReleaseContainer getTopReleases(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + Database::Range range) = 0; virtual TrackContainer getTopTracks(Database::UserId userId, const std::vector& clusterIds, - std::optional range, - bool& moreResults) = 0; + Database::Range range) = 0; + + // Star + virtual void star(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual void unstar(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual bool isStarred(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual ArtistContainer getStarredArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::ArtistSortMethod sortMethod, + Database::Range range) = 0; + + virtual void star(Database::UserId userId, Database::ReleaseId releaseId) = 0; + virtual void unstar(Database::UserId userId, Database::ReleaseId releaseId) = 0; + virtual bool isStarred(Database::UserId userId, Database::ReleaseId artistId) = 0; + virtual ReleaseContainer getStarredReleases(Database::UserId userId, const std::vector& clusterIds, Database::Range range) = 0; + + virtual void star(Database::UserId userId, Database::TrackId trackId) = 0; + virtual void unstar(Database::UserId userId, Database::TrackId trackId) = 0; + virtual bool isStarred(Database::UserId userId, Database::TrackId artistId) = 0; + virtual TrackContainer getStarredTracks(Database::UserId userId, const std::vector& clusterIds, Database::Range range) = 0; }; std::unique_ptr createScrobblingService(boost::asio::io_service& ioService, Database::Db& db); diff --git a/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp b/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp index 50029c99..a42ab649 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp @@ -21,7 +21,8 @@ #include -#include "services/database/Types.hpp" +#include "services/database/TrackId.hpp" +#include "services/database/UserId.hpp" namespace Scrobbling { diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index c36bf1ba..d4f4b985 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -20,7 +20,6 @@ target_link_libraries(lmssubsonic PRIVATE lmsauth lmsav lmsdatabase - lmsfeedback lmsrecommendation lmsscanner lmsscrobbling diff --git a/src/libs/subsonic/impl/RequestContext.hpp b/src/libs/subsonic/impl/RequestContext.hpp index 982963ea..01930783 100644 --- a/src/libs/subsonic/impl/RequestContext.hpp +++ b/src/libs/subsonic/impl/RequestContext.hpp @@ -23,7 +23,7 @@ #include -#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" #include "ClientInfo.hpp" #include "ProtocolVersion.hpp" diff --git a/src/libs/subsonic/impl/Stream.cpp b/src/libs/subsonic/impl/Stream.cpp index fe4218d0..8089779c 100644 --- a/src/libs/subsonic/impl/Stream.cpp +++ b/src/libs/subsonic/impl/Stream.cpp @@ -74,7 +74,7 @@ getStreamParameters(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; { - auto track {Track::getById(context.dbSession, id)}; + auto track {Track::find(context.dbSession, id)}; if (!track) throw RequestedDataNotFoundError {}; @@ -82,7 +82,7 @@ getStreamParameters(RequestContext& context) } { - const User::pointer user {User::getById(context.dbSession, context.userId)}; + const User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; @@ -124,7 +124,7 @@ handleDownload(RequestContext& context, const Wt::Http::Request& request, Wt::Ht { auto transaction {context.dbSession.createSharedTransaction()}; - auto track {Track::getById(context.dbSession, id)}; + auto track {Track::find(context.dbSession, id)}; if (!track) throw RequestedDataNotFoundError {}; diff --git a/src/libs/subsonic/impl/SubsonicId.hpp b/src/libs/subsonic/impl/SubsonicId.hpp index dcc2e50b..32b45715 100644 --- a/src/libs/subsonic/impl/SubsonicId.hpp +++ b/src/libs/subsonic/impl/SubsonicId.hpp @@ -19,7 +19,10 @@ #pragma once -#include "services/database/Types.hpp" +#include "services/database/ArtistId.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" +#include "services/database/TrackListId.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 dc6440f9..54e53642 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -37,7 +37,6 @@ #include "services/database/TrackBookmark.hpp" #include "services/database/TrackList.hpp" #include "services/database/User.hpp" -#include "services/feedback/IFeedbackService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "services/cover/ICoverService.hpp" @@ -164,7 +163,7 @@ static void checkUserIsMySelfOrAdmin(RequestContext& context, const std::string& username) { - User::pointer currentUser {User::getById(context.dbSession, context.userId)}; + User::pointer currentUser {User::find(context.dbSession, context.userId)}; if (!currentUser) throw RequestedDataNotFoundError {}; @@ -178,7 +177,7 @@ checkUserTypeIsAllowed(RequestContext& context, EnumSet allo { auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer currentUser {User::getById(context.dbSession, context.userId)}; + User::pointer currentUser {User::find(context.dbSession, context.userId)}; if (!currentUser) throw RequestedDataNotFoundError {}; @@ -323,11 +322,11 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User: trackResponse.setAttribute("type", "music"); trackResponse.setAttribute("created", dateTimeToCreatedString(track->getLastWritten())); - if (Service::get()->isStarred(user->getId(), track->getId())) + if (Service::get()->isStarred(user->getId(), track->getId())) trackResponse.setAttribute("starred", reportedStarredDate); // Report the first GENRE for this track - ClusterType::pointer clusterType {ClusterType::getByName(dbSession, genreClusterName)}; + ClusterType::pointer clusterType {ClusterType::find(dbSession, genreClusterName)}; if (clusterType) { auto clusters {track->getClusterGroups({clusterType}, 1)}; @@ -405,7 +404,7 @@ releaseToResponseNode(const Release::pointer& release, Session& dbSession, const if (id3) { // Report the first GENRE for this track - ClusterType::pointer clusterType {ClusterType::getByName(dbSession, genreClusterName)}; + ClusterType::pointer clusterType {ClusterType::find(dbSession, genreClusterName)}; if (clusterType) { auto clusters {release->getClusterGroups({clusterType}, 1)}; @@ -414,7 +413,7 @@ releaseToResponseNode(const Release::pointer& release, Session& dbSession, const } } - if (Service::get()->isStarred(user->getId(), release->getId())) + if (Service::get()->isStarred(user->getId(), release->getId())) albumNode.setAttribute("starred", reportedStarredDate); return albumNode; @@ -432,7 +431,7 @@ artistToResponseNode(const User::pointer& user, const Artist::pointer& artist, b if (id3) artistNode.setAttribute("albumCount", artist->getReleaseCount()); - if (Service::get()->isStarred(user->getId(), artist->getId())) + if (Service::get()->isStarred(user->getId(), artist->getId())) artistNode.setAttribute("starred", reportedStarredDate); return artistNode; @@ -500,7 +499,7 @@ handleChangePassword(RequestContext& context) checkUserIsMySelfOrAdmin(context, username); - User::pointer user {User::getByLoginName(context.dbSession, username)}; + User::pointer user {User::find(context.dbSession, username)}; if (!user) throw UserNotAuthorizedError {}; @@ -540,14 +539,14 @@ handleCreatePlaylistRequest(RequestContext& context) auto transaction {context.dbSession.createUniqueTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; TrackList::pointer tracklist; if (id) { - tracklist = TrackList::getById(context.dbSession, *id); + tracklist = TrackList::find(context.dbSession, *id); if (!tracklist || tracklist->getUser() != user || tracklist->getType() != TrackList::Type::Playlist) @@ -565,11 +564,11 @@ handleCreatePlaylistRequest(RequestContext& context) for (const TrackId trackId : trackIds) { - Track::pointer track {Track::getById(context.dbSession, trackId)}; + Track::pointer track {Track::find(context.dbSession, trackId)}; if (!track) continue; - TrackListEntry::create(context.dbSession, track, tracklist ); + TrackListEntry::create(context.dbSession, track, tracklist); } return Response::createOkResponse(context.serverProtocolVersion); @@ -587,7 +586,7 @@ handleCreateUserRequest(RequestContext& context) { auto transaction {context.dbSession.createUniqueTransaction()}; - User::pointer user {User::getByLoginName(context.dbSession, username)}; + User::pointer user {User::find(context.dbSession, username)}; if (user) throw UserAlreadyExistsGenericError {}; @@ -598,7 +597,7 @@ handleCreateUserRequest(RequestContext& context) auto removeCreatedUser {[&]() { auto transaction {context.dbSession.createUniqueTransaction()}; - User::pointer user {User::getById(context.dbSession, userId)}; + User::pointer user {User::find(context.dbSession, userId)}; if (user) user.remove(); }}; @@ -634,11 +633,11 @@ handleDeletePlaylistRequest(RequestContext& context) auto transaction {context.dbSession.createUniqueTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - TrackList::pointer tracklist {TrackList::getById(context.dbSession, id)}; + TrackList::pointer tracklist {TrackList::find(context.dbSession, id)}; if (!tracklist || tracklist->getUser() != user || tracklist->getType() != TrackList::Type::Playlist) @@ -659,7 +658,7 @@ handleDeleteUserRequest(RequestContext& context) auto transaction {context.dbSession.createUniqueTransaction()}; - User::pointer user {User::getByLoginName(context.dbSession, username)}; + User::pointer user {User::find(context.dbSession, username)}; if (!user) throw RequestedDataNotFoundError {}; @@ -696,17 +695,20 @@ handleGetRandomSongsRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - auto tracks {Track::getAllRandom(context.dbSession, {}, size)}; + const auto trackIds {Track::find(context.dbSession, Track::FindParameters {}.setSortMethod(TrackSortMethod::Random).setRange({0, size}))}; Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& randomSongsNode {response.createNode("randomSongs")}; - for (const Track::pointer& track : tracks) + for (const TrackId trackId : trackIds.results) + { + const Track::pointer track {Track::find(context.dbSession, trackId)}; randomSongsNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user)); + } return response; } @@ -724,78 +726,86 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) const Range range {offset, size}; - std::vector releases; + RangeResults releases; Scrobbling::IScrobblingService& scrobbling {*Service::get()}; auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; if (type == "alphabeticalByName") { - releases = Release::getAll(context.dbSession, range); + Release::FindParameters params; + params.setSortMethod(ReleaseSortMethod::Name); + params.setRange(range); + + releases = Release::find(context.dbSession, params); } else if (type == "alphabeticalByArtist") { - releases = Release::getAllOrderedByArtist(context.dbSession, offset, size); + releases = Release::findOrderedByArtist(context.dbSession, range); } else if (type == "byGenre") { // Mandatory param - std::string genre {getMandatoryParameterAs(context.parameters, "genre")}; + const std::string genre {getMandatoryParameterAs(context.parameters, "genre")}; - ClusterType::pointer clusterType {ClusterType::getByName(context.dbSession, genreClusterName)}; - if (clusterType) + if (const ClusterType::pointer clusterType {ClusterType::find(context.dbSession, genreClusterName)}) { - Cluster::pointer cluster {clusterType->getCluster(genre)}; - if (cluster) + if (const Cluster::pointer cluster {clusterType->getCluster(genre)}) { - bool more; - releases = Release::getByFilter(context.dbSession, {cluster->getId()}, {}, range, more); + Release::FindParameters params; + params.setClusters({cluster->getId()}); + params.setSortMethod(ReleaseSortMethod::Name); + params.setRange(range); + + releases = Release::find(context.dbSession, params); } } } else if (type == "byYear") { - int fromYear {getMandatoryParameterAs(context.parameters, "fromYear")}; - int toYear {getMandatoryParameterAs(context.parameters, "toYear")}; + const int fromYear {getMandatoryParameterAs(context.parameters, "fromYear")}; + const int toYear {getMandatoryParameterAs(context.parameters, "toYear")}; - releases = Release::getByYear(context.dbSession, fromYear, toYear, range); + Release::FindParameters params; + params.setSortMethod(ReleaseSortMethod::Date); + params.setRange(range); + params.setDateRange(DateRange::fromYearRange(fromYear, toYear)); + + releases = Release::find(context.dbSession, params); } else if (type == "frequent") { - bool moreResults {}; - for (ReleaseId releaseId : scrobbling.getTopReleases(context.userId, {}, range, moreResults)) - { - if (Release::pointer release {Release::getById(context.dbSession, releaseId)}) - releases.push_back(release ); - } + releases = scrobbling.getTopReleases(context.userId, {}, range); } else if (type == "newest") { - bool moreResults {}; - releases = Release::getLastWritten(context.dbSession, std::nullopt, {}, range, moreResults); + Release::FindParameters params; + params.setSortMethod(ReleaseSortMethod::LastWritten); + params.setRange(range); + + releases = Release::find(context.dbSession, params); } else if (type == "random") { // Random results are paginated, but there is no acceptable way to handle the pagination params without repeating some albums - releases = Release::getAllRandom(context.dbSession, {}, size); + // (no seed provided by subsonic, ot it would require to store some kind of context for each user/client when iterating over the random albums) + Release::FindParameters params; + params.setSortMethod(ReleaseSortMethod::Random); + params.setRange({0, size}); + + releases = Release::find(context.dbSession, params); } else if (type == "recent") { - bool moreResults {}; - for (ReleaseId releaseId : scrobbling.getRecentReleases(context.userId, {}, range, moreResults)) - { - if (Release::pointer release {Release::getById(context.dbSession, releaseId)}) - releases.push_back(release ); - } + releases = scrobbling.getRecentReleases(context.userId, {}, range); } else if (type == "starred") { - bool moreResults {}; - releases = Release::getStarred(context.dbSession, user, {}, range, moreResults); + releases = scrobbling.getStarredReleases(context.userId, {}, range); } else throw NotImplementedGenericError {}; @@ -803,8 +813,11 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& albumListNode {response.createNode(id3 ? "albumList2" : "albumList")}; - for (const Release::pointer& release : releases) + for (const ReleaseId releaseId : releases.results) + { + const Release::pointer release {Release::find(context.dbSession, releaseId)}; albumListNode.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, id3)); + } return response; } @@ -832,11 +845,11 @@ handleGetAlbumRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - Release::pointer release {Release::getById(context.dbSession, id)}; + Release::pointer release {Release::find(context.dbSession, id)}; if (!release) throw RequestedDataNotFoundError {}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; @@ -861,11 +874,11 @@ handleGetArtistRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - Artist::pointer artist {Artist::getById(context.dbSession, id)}; + const Artist::pointer artist {Artist::find(context.dbSession, id)}; if (!artist) throw RequestedDataNotFoundError {}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; @@ -897,7 +910,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) { auto transaction {context.dbSession.createSharedTransaction()}; - Artist::pointer artist {Artist::getById(context.dbSession, id)}; + const Artist::pointer artist {Artist::find(context.dbSession, id)}; if (!artist) throw RequestedDataNotFoundError {}; @@ -911,13 +924,13 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) { auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - for ( const ArtistId similarArtistId : similarArtistsId ) + for (const ArtistId similarArtistId : similarArtistsId) { - Artist::pointer similarArtist {Artist::getById(context.dbSession, similarArtistId)}; + const Artist::pointer similarArtist {Artist::find(context.dbSession, similarArtistId)}; if (similarArtist) artistInfoNode.addArrayChild("similarArtist", artistToResponseNode(user, similarArtist, id3)); } @@ -955,32 +968,31 @@ handleGetArtistsRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - std::optional linkType; + Artist::FindParameters findParameters; + + findParameters.setSortMethod(ArtistSortMethod::BySortName); switch (user->getSubsonicArtistListMode()) { - case User::SubsonicArtistListMode::AllArtists: + case SubsonicArtistListMode::AllArtists: break; - case User::SubsonicArtistListMode::ReleaseArtists: - linkType = TrackArtistLinkType::ReleaseArtist; + case SubsonicArtistListMode::ReleaseArtists: + findParameters.setLinkType(TrackArtistLinkType::ReleaseArtist); break; - case User::SubsonicArtistListMode::TrackArtists: - linkType = TrackArtistLinkType::Artist; + case SubsonicArtistListMode::TrackArtists: + findParameters.setLinkType(TrackArtistLinkType::Artist); break; } - bool more {}; - const std::vector artists {Artist::getByFilter(context.dbSession, - {}, - {}, - linkType, - Artist::SortMethod::BySortName, - std::nullopt, more)}; - for (const Artist::pointer& artist : artists) + const RangeResults artists {Artist::find(context.dbSession, findParameters)}; + for (const ArtistId artistId : artists.results) + { + const auto artist {Artist::find(context.dbSession, artistId)}; indexNode.addArrayChild("artist", artistToResponseNode(user, artist, true /* id3 */)); + } return response; } @@ -1003,7 +1015,7 @@ handleGetMusicDirectoryRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; @@ -1012,16 +1024,18 @@ handleGetMusicDirectoryRequest(RequestContext& context) directoryNode.setAttribute("id", idToString(RootId {})); directoryNode.setAttribute("name", "Music"); - bool moreResults{}; - auto artists {Artist::getAll(context.dbSession, Artist::SortMethod::BySortName, std::nullopt, moreResults)}; - for (const Artist::pointer& artist : artists) + auto artistIds {Artist::find(context.dbSession, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName))}; + for (const ArtistId artistId : artistIds.results) + { + const Artist::pointer artist {Artist::find(context.dbSession, artistId)}; directoryNode.addArrayChild("child", artistToResponseNode(user, artist, false /* no id3 */)); + } } else if (artistId) { directoryNode.setAttribute("id", idToString(*artistId)); - auto artist {Artist::getById(context.dbSession, *artistId)}; + auto artist {Artist::find(context.dbSession, *artistId)}; if (!artist) throw RequestedDataNotFoundError {}; @@ -1035,7 +1049,7 @@ handleGetMusicDirectoryRequest(RequestContext& context) { directoryNode.setAttribute("id", idToString(*releaseId)); - auto release {Release::getById(context.dbSession, *releaseId)}; + auto release {Release::find(context.dbSession, *releaseId)}; if (!release) throw RequestedDataNotFoundError {}; @@ -1075,7 +1089,7 @@ handleGetGenresRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - const ClusterType::pointer clusterType {ClusterType::getByName(context.dbSession, genreClusterName)}; + const ClusterType::pointer clusterType {ClusterType::find(context.dbSession, genreClusterName)}; if (clusterType) { const auto clusters {clusterType->getClusters()}; @@ -1102,32 +1116,30 @@ handleGetIndexesRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - std::optional linkType; + Artist::FindParameters parameters; + parameters.setSortMethod(ArtistSortMethod::BySortName); switch (user->getSubsonicArtistListMode()) { - case User::SubsonicArtistListMode::AllArtists: + case SubsonicArtistListMode::AllArtists: break; - case User::SubsonicArtistListMode::ReleaseArtists: - linkType = TrackArtistLinkType::ReleaseArtist; + case SubsonicArtistListMode::ReleaseArtists: + parameters.setLinkType(TrackArtistLinkType::ReleaseArtist); break; - case User::SubsonicArtistListMode::TrackArtists: - linkType = TrackArtistLinkType::Artist; + case SubsonicArtistListMode::TrackArtists: + parameters.setLinkType(TrackArtistLinkType::Artist); break; } - bool more {}; - const std::vector artists {Artist::getByFilter(context.dbSession, - {}, - {}, - linkType, - Artist::SortMethod::BySortName, - std::nullopt, more)}; - for (const Artist::pointer& artist : artists) + const RangeResults artists {Artist::find(context.dbSession, parameters)}; + for (const ArtistId artistId : artists.results) + { + const Artist::pointer artist {Artist::find(context.dbSession, artistId)}; indexNode.addArrayChild("artist", artistToResponseNode(user, artist, false /* no id3 */)); + } return response; } @@ -1146,11 +1158,11 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3) auto transaction {context.dbSession.createSharedTransaction()}; - const Artist::pointer artist {Artist::getById(context.dbSession, artistId)}; + const Artist::pointer artist {Artist::find(context.dbSession, artistId)}; if (!artist) throw RequestedDataNotFoundError {}; - const User::pointer user {User::getById(context.dbSession, context.userId)}; + const User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; @@ -1158,7 +1170,7 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3) auto tracks {artist->getRandomTracks(count / 2)}; for (const ArtistId similarArtistId : similarArtistIds) { - const Artist::pointer similarArtist {Artist::getById(context.dbSession, similarArtistId)}; + const Artist::pointer similarArtist {Artist::find(context.dbSession, similarArtistId)}; if (!similarArtist) continue; @@ -1199,31 +1211,30 @@ handleGetStarredRequestCommon(RequestContext& context, bool id3) { auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& starredNode {response.createNode(id3 ? "starred2" : "starred")}; + Scrobbling::IScrobblingService& scrobbling {*Service::get()}; + + for (const ArtistId artistId : scrobbling.getStarredArtists(context.userId, {} /* clusters */, std::nullopt /* linkType */, ArtistSortMethod::BySortName, Range {}).results) { - bool moreResults {}; - const auto artists {Artist::getStarred(context.dbSession, user, {}, std::nullopt, Artist::SortMethod::BySortName, std::nullopt, moreResults)}; - for (const Artist::pointer& artist : artists) + if (auto artist {Artist::find(context.dbSession, artistId)}) starredNode.addArrayChild("artist", artistToResponseNode(user, artist, id3)); } + for (const ReleaseId releaseId : scrobbling.getStarredReleases(context.userId, {} /* clusters */, Range {}).results) { - bool moreResults {}; - const auto releases {Release::getStarred(context.dbSession, user, {}, std::nullopt, moreResults)}; - for (const Release::pointer& release : releases) + if (auto release {Release::find(context.dbSession, releaseId)}) starredNode.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, id3)); } + for (const TrackId trackId : scrobbling.getStarredTracks(context.userId, {} /* clusters */, Range {}).results) { - bool moreResults {}; - const auto tracks {Track::getStarred(context.dbSession, user, {}, std::nullopt, moreResults)}; - for (const Track::pointer& track : tracks) + if (auto track {Track::find(context.dbSession, trackId)}) starredNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user)); } @@ -1271,11 +1282,11 @@ handleGetPlaylistRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - TrackList::pointer tracklist {TrackList::getById(context.dbSession, trackListId)}; + TrackList::pointer tracklist {TrackList::find(context.dbSession, trackListId)}; if (!tracklist) throw RequestedDataNotFoundError {}; @@ -1297,16 +1308,15 @@ handleGetPlaylistsRequest(RequestContext& context) { auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; - if (!user) - throw UserNotAuthorizedError {}; - Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& playlistsNode {response.createNode("playlists")}; - auto tracklists {TrackList::getAll(context.dbSession, user, TrackList::Type::Playlist)}; - for (const TrackList::pointer& tracklist : tracklists) - playlistsNode.addArrayChild("playlist", tracklistToResponseNode(tracklist, context.dbSession)); + auto tracklistIds {TrackList::find(context.dbSession, context.userId, TrackList::Type::Playlist, Range {})}; + for (const TrackListId trackListId : tracklistIds.results) + { + const TrackList::pointer trackList {TrackList::find(context.dbSession, trackListId)}; + playlistsNode.addArrayChild("playlist", tracklistToResponseNode(trackList, context.dbSession)); + } return response; } @@ -1326,7 +1336,7 @@ handleGetSongsByGenreRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - auto clusterType {ClusterType::getByName(context.dbSession, genreClusterName)}; + auto clusterType {ClusterType::find(context.dbSession, genreClusterName)}; if (!clusterType) throw RequestedDataNotFoundError {}; @@ -1334,17 +1344,23 @@ handleGetSongsByGenreRequest(RequestContext& context) if (!cluster) throw RequestedDataNotFoundError {}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& songsByGenreNode {response.createNode("songsByGenre")}; - bool more; - auto tracks {Track::getByFilter(context.dbSession, {cluster->getId()}, {}, Range {offset, size}, more)}; - for (const Track::pointer& track : tracks) + Track::FindParameters params; + params.setClusters({cluster->getId()}); + params.setRange({offset, size}); + + auto trackIds {Track::find(context.dbSession, params)}; + for (const TrackId trackId : trackIds.results) + { + const Track::pointer track {Track::find(context.dbSession, trackId)}; songsByGenreNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user)); + } return response; } @@ -1359,7 +1375,7 @@ handleGetUserRequest(RequestContext& context) checkUserIsMySelfOrAdmin(context, username); - const User::pointer user {User::getByLoginName(context.dbSession, username)}; + const User::pointer user {User::find(context.dbSession, username)}; if (!user) throw RequestedDataNotFoundError {}; @@ -1378,9 +1394,12 @@ handleGetUsersRequest(RequestContext& context) Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& usersNode {response.createNode("users")}; - const auto users {User::getAll(context.dbSession)}; - for (const User::pointer& user : users) + const auto userIds {User::find(context.dbSession, Range {})}; + for (const UserId userId : userIds.results) + { + const User::pointer user {User::find(context.dbSession, userId)}; usersNode.addArrayChild("user", userToResponseNode(user)); + } return response; } @@ -1404,30 +1423,52 @@ handleSearchRequestCommon(RequestContext& context, bool id3) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& searchResult2Node {response.createNode(id3 ? "searchResult3" : "searchResult2")}; - bool more; { - auto artists {Artist::getByFilter(context.dbSession, {}, keywords, std::nullopt, Artist::SortMethod::BySortName, Range {artistOffset, artistCount}, more)}; - for (const Artist::pointer& artist : artists) + Artist::FindParameters params; + params.setKeywords(keywords); + params.setSortMethod(ArtistSortMethod::BySortName); + params.setRange({artistOffset, artistCount}); + + RangeResults artistIds {Artist::find(context.dbSession, params)}; + for (const ArtistId artistId : artistIds.results) + { + const auto artist {Artist::find(context.dbSession, artistId)}; searchResult2Node.addArrayChild("artist", artistToResponseNode(user, artist, id3)); + } } { - auto releases {Release::getByFilter(context.dbSession, {}, keywords, Range {albumOffset, albumCount}, more)}; - for (const Release::pointer& release : releases) + Release::FindParameters params; + params.setKeywords(keywords); + params.setSortMethod(ReleaseSortMethod::Name); + params.setRange({albumOffset, albumCount}); + + RangeResults releaseIds {Release::find(context.dbSession, params)}; + for (const ReleaseId releaseId : releaseIds.results) + { + const auto release {Release::find(context.dbSession, releaseId)}; searchResult2Node.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, id3)); + } } { - auto tracks {Track::getByFilter(context.dbSession, {}, keywords, Range {songOffset, songCount}, more)}; - for (const Track::pointer& track : tracks) + Track::FindParameters params; + params.setKeywords(keywords); + params.setRange({songOffset, songCount}); + + RangeResults trackIds {Track::find(context.dbSession, params)}; + for (const TrackId trackId : trackIds.results) + { + const auto track {Track::find(context.dbSession, trackId)}; searchResult2Node.addArrayChild("song", trackToResponseNode(track, context.dbSession, user)); + } } return response; @@ -1462,35 +1503,35 @@ handleStarRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; for (const ArtistId id : params.artistIds) { - Artist::pointer artist {Artist::getById(context.dbSession, id)}; + Artist::pointer artist {Artist::find(context.dbSession, id)}; if (!artist) continue; - Service::get()->star(user->getId(), artist->getId()); + Service::get()->star(user->getId(), artist->getId()); } for (const ReleaseId id : params.releaseIds) { - Release::pointer release {Release::getById(context.dbSession, id)}; + Release::pointer release {Release::find(context.dbSession, id)}; if (!release) continue; - Service::get()->star(user->getId(), release->getId()); + Service::get()->star(user->getId(), release->getId()); } for (const TrackId id : params.trackIds) { - Track::pointer track {Track::getById(context.dbSession, id)}; + Track::pointer track {Track::find(context.dbSession, id)}; if (!track) continue; - Service::get()->star(user->getId(), track->getId()); + Service::get()->star(user->getId(), track->getId()); } return Response::createOkResponse(context.serverProtocolVersion); @@ -1518,35 +1559,29 @@ handleUnstarRequest(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw RequestedDataNotFoundError {}; - for (const ArtistId id : params.artistIds) - { - Artist::pointer artist {Artist::getById(context.dbSession, id)}; - if (!artist) - continue; - - Service::get()->unstar(user->getId(), artist->getId()); - } + for (const ArtistId artistId : params.artistIds) + Service::get()->unstar(context.userId, artistId); for (const ReleaseId id : params.releaseIds) { - Release::pointer release {Release::getById(context.dbSession, id)}; + Release::pointer release {Release::find(context.dbSession, id)}; if (!release) continue; - Service::get()->unstar(user->getId(), release->getId()); + Service::get()->unstar(user->getId(), release->getId()); } for (const TrackId id : params.trackIds) { - Track::pointer track {Track::getById(context.dbSession, id)}; + Track::pointer track {Track::find(context.dbSession, id)}; if (!track) continue; - Service::get()->unstar(user->getId(), track->getId()); + Service::get()->unstar(user->getId(), track->getId()); } @@ -1608,7 +1643,7 @@ handleUpdateUserRequest(RequestContext& context) { auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getByLoginName(context.dbSession, username)}; + User::pointer user {User::find(context.dbSession, username)}; if (!user) throw RequestedDataNotFoundError {}; @@ -1656,11 +1691,11 @@ handleUpdatePlaylistRequest(RequestContext& context) auto transaction {context.dbSession.createUniqueTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - TrackList::pointer tracklist {TrackList::getById(context.dbSession, id)}; + TrackList::pointer tracklist {TrackList::find(context.dbSession, id)}; if (!tracklist || tracklist->getUser() != user || tracklist->getType() != TrackList::Type::Playlist) @@ -1689,7 +1724,7 @@ handleUpdatePlaylistRequest(RequestContext& context) // Add tracks for (const TrackId trackIdToAdd : trackIdsToAdd) { - Track::pointer track {Track::getById(context.dbSession, trackIdToAdd)}; + Track::pointer track {Track::find(context.dbSession, trackIdToAdd)}; if (!track) continue; @@ -1705,17 +1740,18 @@ handleGetBookmarks(RequestContext& context) { auto transaction {context.dbSession.createSharedTransaction()}; - User::pointer user {User::getById(context.dbSession, context.userId)}; + User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - const auto bookmarks {TrackBookmark::getByUser(context.dbSession, user)}; + const auto bookmarkIds {TrackBookmark::find(context.dbSession, user->getId(), Range {})}; Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& bookmarksNode {response.createNode("bookmarks")}; - for (const TrackBookmark::pointer& bookmark : bookmarks) + for (const TrackBookmarkId bookmarkId : bookmarkIds.results) { + const TrackBookmark::pointer bookmark {TrackBookmark::find(context.dbSession, bookmarkId)}; Response::Node bookmarkNode {trackBookmarkToResponseNode(bookmark)}; bookmarkNode.addArrayChild("entry", trackToResponseNode(bookmark->getTrack(), context.dbSession, user)); @@ -1730,22 +1766,22 @@ Response handleCreateBookmark(RequestContext& context) { // Mandatory params - TrackId id {getMandatoryParameterAs(context.parameters, "id")}; + TrackId trackId {getMandatoryParameterAs(context.parameters, "id")}; unsigned long position {getMandatoryParameterAs(context.parameters, "position")}; const std::optional comment {getParameterAs(context.parameters, "comment")}; auto transaction {context.dbSession.createUniqueTransaction()}; - const User::pointer user {User::getById(context.dbSession, context.userId)}; + const User::pointer user {User::find(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - const Track::pointer track {Track::getById(context.dbSession, id)}; + const Track::pointer track {Track::find(context.dbSession, trackId)}; if (!track) throw RequestedDataNotFoundError {}; // Replace any existing bookmark - auto bookmark {TrackBookmark::getByUser(context.dbSession, user, track)}; + auto bookmark {TrackBookmark::find(context.dbSession, user->getId(), trackId)}; if (!bookmark) bookmark = TrackBookmark::create(context.dbSession, user, track); @@ -1761,19 +1797,11 @@ Response handleDeleteBookmark(RequestContext& context) { // Mandatory params - TrackId id {getMandatoryParameterAs(context.parameters, "id")}; + TrackId trackId {getMandatoryParameterAs(context.parameters, "id")}; auto transaction {context.dbSession.createUniqueTransaction()}; - const User::pointer user {User::getById(context.dbSession, context.userId)}; - if (!user) - throw UserNotAuthorizedError {}; - - const Track::pointer track {Track::getById(context.dbSession, id)}; - if (!track) - throw RequestedDataNotFoundError {}; - - auto bookmark {TrackBookmark::getByUser(context.dbSession, user, track)}; + auto bookmark {TrackBookmark::find(context.dbSession, context.userId, trackId)}; if (!bookmark) throw RequestedDataNotFoundError {}; diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt index 83bead84..33dad4b1 100644 --- a/src/lms/CMakeLists.txt +++ b/src/lms/CMakeLists.txt @@ -52,7 +52,6 @@ target_link_libraries(lms PRIVATE lmsav lmsauth lmsdatabase - lmsfeedback lmsrecommendation lmsscanner lmsscrobbling diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 8329f6d6..c269f7a8 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -31,7 +31,6 @@ #include "services/cover/ICoverService.hpp" #include "services/database/Db.hpp" #include "services/database/Session.hpp" -#include "services/feedback/IFeedbackService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "services/scanner/IScannerService.hpp" #include "services/scrobbling/IScrobblingService.hpp" @@ -239,7 +238,6 @@ int main(int argc, char* argv[]) // Service initialization order is important (reverse-order for deinit) Service childProcessManagerService {createChildProcessManager(ioContext)}; - Service feedbackService {Feedback::createFeedbackService(database)}; Service authTokenService; Service authPasswordService; Service authEnvService; diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index b505d8af..2db329bc 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -109,7 +109,7 @@ class AuthModel : public Wt::WFormModel { auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; - Database::User::pointer user {Database::User::getByLoginName(LmsApp->getDbSession(), valueText(LoginNameField).toUTF8())}; + Database::User::pointer user {Database::User::find(LmsApp->getDbSession(), valueText(LoginNameField).toUTF8())}; user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); _userId = user->getId(); @@ -205,7 +205,7 @@ Auth::Auth() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - Database::User::pointer demoUser {Database::User::getDemo(LmsApp->getDbSession())}; + Database::User::pointer demoUser {Database::User::findDemoUser(LmsApp->getDbSession())}; if (demoUser) { model->setValue(AuthModel::LoginNameField, demoUser->getLoginName()); diff --git a/src/lms/ui/Auth.hpp b/src/lms/ui/Auth.hpp index 87c00228..305e9a4d 100644 --- a/src/lms/ui/Auth.hpp +++ b/src/lms/ui/Auth.hpp @@ -19,9 +19,10 @@ #pragma once +#include #include -#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace UserInterface { diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index a7132699..c15f05f1 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -101,7 +101,7 @@ LmsApplication::getUser() if (!_authenticatedUser) return {}; - return Database::User::getById(getDbSession(), _authenticatedUser->userId); + return Database::User::find(getDbSession(), _authenticatedUser->userId); } Database::UserId @@ -247,7 +247,7 @@ LmsApplication::processPasswordAuth() void LmsApplication::setTheme() { - Database::User::UITheme theme {Database::User::defaultUITheme}; + Database::UITheme theme {Database::User::defaultUITheme}; { auto transaction {getDbSession().createSharedTransaction()}; if (const auto user {getUser()}) diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 6c32fffe..080c03f9 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -23,6 +23,9 @@ #include +#include "services/database/Object.hpp" +#include "services/database/UserId.hpp" +#include "services/database/Types.hpp" #include "services/scanner/ScannerEvents.hpp" namespace Database diff --git a/src/lms/ui/LmsApplicationManager.hpp b/src/lms/ui/LmsApplicationManager.hpp index f478ae8d..5a81aec1 100644 --- a/src/lms/ui/LmsApplicationManager.hpp +++ b/src/lms/ui/LmsApplicationManager.hpp @@ -25,7 +25,7 @@ #include -#include "services/database/Types.hpp" +#include "services/database/UserId.hpp" namespace UserInterface { diff --git a/src/lms/ui/LmsTheme.cpp b/src/lms/ui/LmsTheme.cpp index d56cbe5a..e5d9f3cc 100644 --- a/src/lms/ui/LmsTheme.cpp +++ b/src/lms/ui/LmsTheme.cpp @@ -24,7 +24,7 @@ namespace UserInterface { -LmsTheme::LmsTheme(Database::User::UITheme theme) +LmsTheme::LmsTheme(Database::UITheme theme) : _theme {theme} { setVersion(Wt::BootstrapVersion::v3); @@ -32,7 +32,7 @@ LmsTheme::LmsTheme(Database::User::UITheme theme) } void -LmsTheme::setTheme(Database::User::UITheme theme) +LmsTheme::setTheme(Database::UITheme theme) { if (theme == _theme) return; @@ -59,11 +59,11 @@ LmsTheme::styleSheets() const } std::vector -LmsTheme::getStyleSheets(Database::User::UITheme theme) +LmsTheme::getStyleSheets(Database::UITheme theme) { switch (theme) { - case Database::User::UITheme::Dark: + case Database::UITheme::Dark: return { {"css/fonts.css"}, @@ -73,7 +73,7 @@ LmsTheme::getStyleSheets(Database::User::UITheme theme) {"css/lms-darkly.css"}, }; - case Database::User::UITheme::Light: + case Database::UITheme::Light: return { {"css/fonts.css"}, diff --git a/src/lms/ui/LmsTheme.hpp b/src/lms/ui/LmsTheme.hpp index dce55425..912137a6 100644 --- a/src/lms/ui/LmsTheme.hpp +++ b/src/lms/ui/LmsTheme.hpp @@ -24,21 +24,21 @@ #include #include -#include "services/database/User.hpp" +#include "services/database/Types.hpp" namespace UserInterface { class LmsTheme : public Wt::WBootstrapTheme { public: - LmsTheme(Database::User::UITheme theme); + LmsTheme(Database::UITheme theme); - void setTheme(Database::User::UITheme theme); + void setTheme(Database::UITheme theme); private: std::vector styleSheets() const override; - static std::vector getStyleSheets(Database::User::UITheme theme); + static std::vector getStyleSheets(Database::UITheme theme); - Database::User::UITheme _theme; + Database::UITheme _theme; }; } diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index eefd44fc..43f37e76 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -119,10 +119,10 @@ bitrateFromString(const std::string& str) if (!value) return std::nullopt; - if (Database::User::audioTranscodeAllowedBitrates.find(*value) != std::cend(Database::User::audioTranscodeAllowedBitrates)) - return *value; + if (!Database::isAudioBitrateAllowed(*value)) + return std::nullopt; - return std::nullopt; + return *value; } static @@ -241,7 +241,7 @@ MediaPlayer::loadTrack(Database::TrackId trackId, bool play, float replayGain) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const auto track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; + const auto track {Database::Track::find(LmsApp->getDbSession(), trackId)}; if (!track) return; diff --git a/src/lms/ui/MediaPlayer.hpp b/src/lms/ui/MediaPlayer.hpp index 542cc081..40307c4b 100644 --- a/src/lms/ui/MediaPlayer.hpp +++ b/src/lms/ui/MediaPlayer.hpp @@ -27,6 +27,7 @@ #include #include +#include "services/database/TrackId.hpp" #include "services/database/Types.hpp" namespace UserInterface { diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index fd4f42b2..0e88bd84 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -28,7 +28,7 @@ #include "services/database/Track.hpp" #include "services/database/TrackList.hpp" #include "services/database/User.hpp" -#include "services/feedback/IFeedbackService.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/Random.hpp" @@ -132,7 +132,7 @@ PlayQueue::PlayQueue() if (LmsApp->getUser()->isDemo()) { LMS_LOG(UI, DEBUG) << "Removing tracklist id " << _tracklistId.toString(); - auto tracklist = Database::TrackList::getById(LmsApp->getDbSession(), _tracklistId); + auto tracklist = Database::TrackList::find(LmsApp->getDbSession(), _tracklistId); if (tracklist) tracklist.remove(); } @@ -161,7 +161,11 @@ PlayQueue::PlayQueue() loadTrack(trackPos, false); }); - trackList = LmsApp->getUser()->getQueuedTrackList(LmsApp->getDbSession()); + + static const std::string queuedListName {"__queued_tracks__"}; + trackList = Database::TrackList::find(LmsApp->getDbSession(), queuedListName, Database::TrackList::Type::Internal, LmsApp->getUserId()); + if (!trackList) + trackList = Database::TrackList::create(LmsApp->getDbSession(), queuedListName, Database::TrackList::Type::Internal, false, LmsApp->getUser()); } else { @@ -193,7 +197,7 @@ PlayQueue::updateRadioBtn() Database::TrackList::pointer PlayQueue::getTrackList() const { - return Database::TrackList::getById(LmsApp->getDbSession(), _tracklistId); + return Database::TrackList::find(LmsApp->getDbSession(), _tracklistId); } bool @@ -327,7 +331,7 @@ PlayQueue::enqueueTracks(const std::vector& trackIds) std::size_t nbTracksToEnqueue {tracklist->getCount() + trackIds.size() > _nbMaxEntries ? _nbMaxEntries - tracklist->getCount() : trackIds.size()}; for (const Database::TrackId trackId : trackIds) { - Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; + Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), trackId)}; if (!track) continue; @@ -489,14 +493,14 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) { Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; - const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), trackId)}; + const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), trackId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(moreBtn, [=] { if (isStarred) - Service::get()->unstar(LmsApp->getUserId(), trackId); + Service::get()->unstar(LmsApp->getUserId(), trackId); else - Service::get()->star(LmsApp->getUserId(), trackId); + Service::get()->star(LmsApp->getUserId(), trackId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(trackId)}); @@ -508,7 +512,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) void PlayQueue::enqueueRadioTracks() { - const auto similarTrackIds {Service::get()->getSimilarTracksFromTrackList(_tracklistId, 3)}; + const auto similarTrackIds {Service::get()->findSimilarTracksFromTrackList(_tracklistId, 3)}; std::vector trackToAddIds(std::cbegin(similarTrackIds), std::cend(similarTrackIds)); Random::shuffleContainer(trackToAddIds); diff --git a/src/lms/ui/PlayQueue.hpp b/src/lms/ui/PlayQueue.hpp index 89f15ff4..b90b28ce 100644 --- a/src/lms/ui/PlayQueue.hpp +++ b/src/lms/ui/PlayQueue.hpp @@ -25,7 +25,8 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/Object.hpp" +#include "services/database/TrackListId.hpp" #include "PlayQueueAction.hpp" namespace Similarity diff --git a/src/lms/ui/PlayQueueAction.hpp b/src/lms/ui/PlayQueueAction.hpp index 4fa8b426..c2a45b34 100644 --- a/src/lms/ui/PlayQueueAction.hpp +++ b/src/lms/ui/PlayQueueAction.hpp @@ -22,7 +22,9 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/ArtistId.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" namespace UserInterface { diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 1bc35013..7bbc5672 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -36,6 +36,7 @@ #include "services/auth/IPasswordService.hpp" #include "services/database/Session.hpp" +#include "services/database/User.hpp" #include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Service.hpp" @@ -139,7 +140,7 @@ class SettingsModel : public Wt::WFormModel User::pointer user {LmsApp->getUser()}; { - const User::UITheme newTheme {Wt::asNumber(value(DarkModeField)) ? User::UITheme::Dark : User::UITheme::Light}; + const UITheme newTheme {Wt::asNumber(value(DarkModeField)) ? UITheme::Dark : UITheme::Light}; LmsTheme* lmsTheme {static_cast(LmsApp->theme().get())}; lmsTheme->setTheme(newTheme); @@ -208,7 +209,7 @@ class SettingsModel : public Wt::WFormModel User::pointer user {LmsApp->getUser()}; - setValue(DarkModeField, user->getUITheme() == User::UITheme::Dark); + setValue(DarkModeField, user->getUITheme() == UITheme::Dark); { const auto settings {*LmsApp->getMediaPlayer().getSettings()}; @@ -333,10 +334,10 @@ class SettingsModel : public Wt::WFormModel _transcodeModeModel->add(Wt::WString::tr("Lms.Settings.transcode-mode.if-format-not-supported"), MediaPlayer::Settings::Transcode::Mode::IfFormatNotSupported); _transcodeBitrateModel = std::make_shared>(); - for (const Bitrate bitrate : User::audioTranscodeAllowedBitrates) + visitAllowedAudioBitrates([&](const Bitrate bitrate) { _transcodeBitrateModel->add(Wt::WString::fromUTF8(std::to_string(bitrate / 1000)), bitrate); - } + }); _transcodeFormatModel = std::make_shared>(); _transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.mp3"), AudioFormat::MP3); @@ -351,10 +352,10 @@ class SettingsModel : public Wt::WFormModel _replayGainModeModel->add(Wt::WString::tr("Lms.Settings.replaygain-mode.track"), MediaPlayer::Settings::ReplayGain::Mode::Track); _replayGainModeModel->add(Wt::WString::tr("Lms.Settings.replaygain-mode.release"), MediaPlayer::Settings::ReplayGain::Mode::Release); - _subsonicArtistListModeModel = std::make_shared>(); - _subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.all-artists"), User::SubsonicArtistListMode::AllArtists); - _subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.release-artists"), User::SubsonicArtistListMode::ReleaseArtists); - _subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.track-artists"), User::SubsonicArtistListMode::TrackArtists); + _subsonicArtistListModeModel = std::make_shared>(); + _subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.all-artists"), SubsonicArtistListMode::AllArtists); + _subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.release-artists"), SubsonicArtistListMode::ReleaseArtists); + _subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.track-artists"), SubsonicArtistListMode::TrackArtists); _scrobblerModel = std::make_shared>(); _scrobblerModel->add(Wt::WString::tr("Lms.Settings.scrobbling.scrobbler.internal"), Scrobbler::Internal); @@ -368,7 +369,7 @@ class SettingsModel : public Wt::WFormModel std::shared_ptr> _transcodeBitrateModel; std::shared_ptr> _transcodeFormatModel; std::shared_ptr _replayGainModeModel; - std::shared_ptr> _subsonicArtistListModeModel; + std::shared_ptr> _subsonicArtistListModeModel; std::shared_ptr _scrobblerModel; }; diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index 4e8186b8..efb14db4 100644 --- a/src/lms/ui/admin/InitWizardView.cpp +++ b/src/lms/ui/admin/InitWizardView.cpp @@ -65,7 +65,7 @@ class InitWizardModel : public Wt::WFormModel // Check if a user already exist // If it's the case, just do nothing - if (!Database::User::getAll(LmsApp->getDbSession()).empty()) + if (Database::User::getCount(LmsApp->getDbSession()) > 0) throw LmsException {"Admin user already created"}; Database::User::pointer user {Database::User::create(LmsApp->getDbSession(), valueText(AdminLoginField).toUTF8())}; diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index 12bf9392..764d6bb8 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -90,7 +90,7 @@ class ReportResource : public Wt::WResource for (const auto& duplicate : _stats.duplicates) { - const auto& track {Database::Track::getById(LmsApp->getDbSession(), duplicate.trackId)}; + const auto& track {Database::Track::find(LmsApp->getDbSession(), duplicate.trackId)}; if (!track) continue; diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index fd456993..218526f1 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -53,7 +53,7 @@ class UserModel : public Wt::WFormModel static inline const Field PasswordField {"password"}; static inline const Field DemoField {"demo"}; - UserModel(std::optional userId, ::Auth::IPasswordService* authPasswordService) + UserModel(std::optional userId, ::Auth::IPasswordService* authPasswordService) : _userId {userId} , _authPasswordService {authPasswordService} { @@ -82,7 +82,7 @@ class UserModel : public Wt::WFormModel if (_userId) { // Update user - Database::User::pointer user {Database::User::getById(LmsApp->getDbSession(), *_userId)}; + User::pointer user {User::find(LmsApp->getDbSession(), *_userId)}; if (!user) throw UserNotFoundException {}; @@ -92,15 +92,15 @@ class UserModel : public Wt::WFormModel else { // Check races with other endpoints (subsonic API...) - Database::User::pointer user {Database::User::getByLoginName(LmsApp->getDbSession(), valueText(LoginField).toUTF8())}; + User::pointer user {User::find(LmsApp->getDbSession(), valueText(LoginField).toUTF8())}; if (user) throw UserNotAllowedException {}; // Create user - user = Database::User::create(LmsApp->getDbSession(), valueText(LoginField).toUTF8()); + user = User::create(LmsApp->getDbSession(), valueText(LoginField).toUTF8()); if (Wt::asNumber(value(DemoField))) - user.modify()->setType(Database::UserType::DEMO); + user.modify()->setType(UserType::DEMO); if (_authPasswordService) _authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8()); @@ -115,20 +115,20 @@ class UserModel : public Wt::WFormModel auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::User::pointer user {Database::User::getById(LmsApp->getDbSession(), *_userId)}; + const User::pointer user {User::find(LmsApp->getDbSession(), *_userId)}; if (!user) throw UserNotFoundException {}; else if (user == LmsApp->getUser()) throw UserNotAllowedException {}; } - Database::UserType getUserType() const + UserType getUserType() const { if (_userId) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::User::pointer user {Database::User::getById(LmsApp->getDbSession(), *_userId)}; + const User::pointer user {User::find(LmsApp->getDbSession(), *_userId)}; return user->getType(); } @@ -141,7 +141,7 @@ class UserModel : public Wt::WFormModel { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::User::pointer user {Database::User::getById(LmsApp->getDbSession(), *_userId)}; + const User::pointer user {User::find(LmsApp->getDbSession(), *_userId)}; return user->getLoginName(); } @@ -156,7 +156,7 @@ class UserModel : public Wt::WFormModel { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::User::pointer user {Database::User::getByLoginName(LmsApp->getDbSession(), valueText(LoginField).toUTF8())}; + const User::pointer user {User::find(LmsApp->getDbSession(), valueText(LoginField).toUTF8())}; if (user) error = Wt::WString::tr("Lms.Admin.User.user-already-exists"); } @@ -164,7 +164,7 @@ class UserModel : public Wt::WFormModel { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - if (Wt::asNumber(value(DemoField)) && Database::User::getDemo(LmsApp->getDbSession())) + if (Wt::asNumber(value(DemoField)) && User::findDemoUser(LmsApp->getDbSession())) error = Wt::WString::tr("Lms.Admin.User.demo-account-already-exists"); } @@ -176,7 +176,7 @@ class UserModel : public Wt::WFormModel return false; } - std::optional _userId; + std::optional _userId; ::Auth::IPasswordService* _authPasswordService {}; }; @@ -196,7 +196,7 @@ UserView::refreshView() if (!wApp->internalPathMatches("/admin/user")) return; - const std::optional userId {StringUtils::readAs(wApp->internalPathNextPart("/admin/user/"))}; + const std::optional userId {StringUtils::readAs(wApp->internalPathNextPart("/admin/user/"))}; clear(); @@ -212,7 +212,7 @@ UserView::refreshView() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::User::pointer user {Database::User::getById(LmsApp->getDbSession(), *userId)}; + const User::pointer user {User::find(LmsApp->getDbSession(), *userId)}; if (!user) throw UserNotFoundException {}; diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index ae0279a9..a5daed2f 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -33,6 +33,8 @@ namespace UserInterface { +using namespace Database; + UsersView::UsersView() : Wt::WTemplate {Wt::WString::tr("Lms.Admin.Users.template")} { @@ -69,9 +71,9 @@ UsersView::refreshView() auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - for (const auto& user : Database::User::getAll(LmsApp->getDbSession())) + for (const UserId userId : User::find(LmsApp->getDbSession(), Range {}).results) { - const Database::UserId userId {user->getId()}; + const User::pointer user {User::find(LmsApp->getDbSession(), userId)}; Wt::WTemplate* entry {_container->addNew(Wt::WString::tr("Lms.Admin.Users.template.entry"))}; @@ -109,7 +111,7 @@ UsersView::refreshView() { auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; - Database::User::pointer user {Database::User::getById(LmsApp->getDbSession(), userId)}; + User::pointer user {User::find(LmsApp->getDbSession(), userId)}; if (user) user.remove(); diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 0d6d3619..f1df91a8 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -31,104 +31,92 @@ namespace UserInterface { using namespace Database; - std::vector> - ArtistCollector::get(std::optional range, bool& moreResults) + RangeResults + ArtistCollector::get(Database::Range range) { Scrobbling::IScrobblingService& scrobbling {*Service::get()}; range = getActualRange(range); - std::vector artists; + RangeResults artists; switch (getMode()) { case Mode::Random: - artists = getRandomArtists(range, moreResults); + artists = getRandomArtists(range); break; case Mode::Starred: - artists = Artist::getStarred(LmsApp->getDbSession(), - LmsApp->getUser(), - getFilters().getClusterIds(), - _linkType, - Artist::SortMethod::BySortName, - range, moreResults); + artists = scrobbling.getStarredArtists(LmsApp->getUserId(), getFilters().getClusterIds(), _linkType, ArtistSortMethod::StarredDateDesc, range); break; case Mode::RecentlyPlayed: - for (const ArtistId artistId : scrobbling.getRecentArtists(LmsApp->getUserId(), getFilters().getClusterIds(), _linkType, range, moreResults)) - { - if (Artist::pointer artist {Artist::getById(LmsApp->getDbSession(), artistId)}) - artists.push_back(artist); - } + artists = scrobbling.getRecentArtists(LmsApp->getUserId(), getFilters().getClusterIds(), _linkType, range); break; case Mode::MostPlayed: - for (const ArtistId artistId : scrobbling.getTopArtists(LmsApp->getUserId(), getFilters().getClusterIds(), _linkType, range, moreResults)) - { - if (Artist::pointer artist {Artist::getById(LmsApp->getDbSession(), artistId)}) - artists.push_back(artist); - } + artists = scrobbling.getTopArtists(LmsApp->getUserId(), getFilters().getClusterIds(), _linkType, range); break; case Mode::RecentlyAdded: - artists = Artist::getLastWritten(LmsApp->getDbSession(), - std::nullopt, // after - getFilters().getClusterIds(), - _linkType, - range, moreResults); - break; - - case Mode::Search: - artists = Database::Artist::getByFilter(LmsApp->getDbSession(), - getFilters().getClusterIds(), - getSearchKeywords(), - std::nullopt, // no link - Database::Artist::SortMethod::BySortName, - range, moreResults); - break; - - case Mode::All: - artists = Artist::getByFilter(LmsApp->getDbSession(), - getFilters().getClusterIds(), - {}, - _linkType, - Artist::SortMethod::BySortName, - range, moreResults); - break; - } - - if (range && getMaxCount() && (range->offset + range->limit == *getMaxCount())) - moreResults = false; - - return artists; - } - - std::vector - ArtistCollector::getRandomArtists(std::optional range, bool& moreResults) - { - std::vector artists; - - assert(getMode() == Mode::Random); - - if (_randomArtists.empty()) - _randomArtists = Artist::getAllIdsRandom(LmsApp->getDbSession(), getFilters().getClusterIds(), _linkType, getMaxCount()); - - { - auto itBegin {std::cbegin(_randomArtists) + std::min(range ? range->offset : 0, _randomArtists.size())}; - auto itEnd {std::cbegin(_randomArtists) + std::min(range ? range->offset + range->limit : _randomArtists.size(), _randomArtists.size())}; - - for (auto it {itBegin}; it != itEnd; ++it) { - Artist::pointer artist {Artist::getById(LmsApp->getDbSession(), *it)}; - if (artist) - artists.push_back(artist); + Artist::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setLinkType(_linkType); + params.setSortMethod(ArtistSortMethod::LastWritten); + params.setRange(range); + + artists = Artist::find(LmsApp->getDbSession(), params); + break; } - moreResults = (itEnd != std::cend(_randomArtists)); + case Mode::Search: + { + Artist::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setKeywords(getSearchKeywords()); + params.setSortMethod(ArtistSortMethod::BySortName); + params.setRange(range); + + artists = Artist::find(LmsApp->getDbSession(), params); + break; + } + + case Mode::All: + { + Artist::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setLinkType(_linkType); + params.setSortMethod(ArtistSortMethod::BySortName); + params.setRange(range); + + artists = Artist::find(LmsApp->getDbSession(), params); + break; + } } + if (range.offset + range.size == getMaxCount()) + artists.moreResults = false; + return artists; } + RangeResults + ArtistCollector::getRandomArtists(Range range) + { + assert(getMode() == Mode::Random); + + if (!_randomArtists) + { + Artist::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setLinkType(_linkType); + params.setSortMethod(ArtistSortMethod::Random); + params.setRange(Range {0, getMaxCount()}); + + _randomArtists = Artist::find(LmsApp->getDbSession(), params); + } + + return _randomArtists->getSubRange(range); + } } // ns UserInterface diff --git a/src/lms/ui/explore/ArtistCollector.hpp b/src/lms/ui/explore/ArtistCollector.hpp index f529b4cc..ab9d43fc 100644 --- a/src/lms/ui/explore/ArtistCollector.hpp +++ b/src/lms/ui/explore/ArtistCollector.hpp @@ -20,9 +20,9 @@ #pragma once #include -#include #include "DatabaseCollectorBase.hpp" +#include "services/database/ArtistId.hpp" #include "services/database/Types.hpp" namespace Database @@ -37,13 +37,13 @@ namespace UserInterface public: using DatabaseCollectorBase::DatabaseCollectorBase; - std::vector> get(std::optional range, bool& moreResults); - void reset() { _randomArtists.clear(); } + Database::RangeResults get(Database::Range range); + void reset() { _randomArtists.reset(); } void setArtistLinkType(std::optional linkType) { _linkType = linkType; } private: - std::vector> getRandomArtists(std::optional range, bool& moreResults); - std::vector _randomArtists; + Database::RangeResults getRandomArtists(Range range); + std::optional> _randomArtists; std::optional _linkType; }; } // ns UserInterface diff --git a/src/lms/ui/explore/ArtistListHelpers.hpp b/src/lms/ui/explore/ArtistListHelpers.hpp index 412ad51f..d26a5182 100644 --- a/src/lms/ui/explore/ArtistListHelpers.hpp +++ b/src/lms/ui/explore/ArtistListHelpers.hpp @@ -23,7 +23,7 @@ #include -#include "services/database/Types.hpp" +#include "services/database/Object.hpp" namespace Database { diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index e4c65a01..2c14fead 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -32,7 +32,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" -#include "services/feedback/IFeedbackService.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" @@ -79,14 +79,14 @@ extractArtistIdFromInternalPath() if (mbid) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - if (const Database::Artist::pointer artist {Database::Artist::getByMBID(LmsApp->getDbSession(), *mbid)}) + if (const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), *mbid)}) return artist->getId(); } return std::nullopt; } - return StringUtils::readAs(wApp->internalPathNextPart("/artist/")); + return StringUtils::readAs(wApp->internalPathNextPart("/artist/")); } void @@ -107,7 +107,7 @@ Artist::refreshView() auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Artist::pointer artist {Database::Artist::getById(LmsApp->getDbSession(), *artistId)}; + const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), *artistId)}; if (!artist) throw ArtistNotFoundException {}; @@ -166,14 +166,14 @@ Artist::refreshView() artistsAction.emit(PlayQueueAction::PlayLast, {_artistId}); }); - const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), _artistId)}; + const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), _artistId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(this, [=] { if (isStarred) - Service::get()->unstar(LmsApp->getUserId(), _artistId); + Service::get()->unstar(LmsApp->getUserId(), _artistId); else - Service::get()->star(LmsApp->getUserId(), _artistId); + Service::get()->star(LmsApp->getUserId(), _artistId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(*artistId)}); @@ -184,7 +184,7 @@ Artist::refreshView() } void -Artist::refreshReleases(const Database::ObjectPtr& artist) +Artist::refreshReleases(const ObjectPtr& artist) { const auto releases {artist->getReleases(_filters->getClusterIds())}; if (releases.empty()) @@ -200,7 +200,7 @@ Artist::refreshReleases(const Database::ObjectPtr& artist) } void -Artist::refreshNonReleaseTracks(const Database::ObjectPtr& artist) +Artist::refreshNonReleaseTracks(const ObjectPtr& artist) { if (!artist->hasNonReleaseTracks()) return; @@ -216,7 +216,7 @@ Artist::refreshNonReleaseTracks(const Database::ObjectPtr& art } void -Artist::refreshSimilarArtists(const std::vector& similarArtistsId) +Artist::refreshSimilarArtists(const std::vector& similarArtistsId) { if (similarArtistsId.empty()) return; @@ -224,9 +224,9 @@ Artist::refreshSimilarArtists(const std::vector& similarArti setCondition("if-has-similar-artists", true); Wt::WContainerWidget* similarArtistsContainer {bindNew("similar-artists")}; - for (const Database::ArtistId artistId : similarArtistsId) + for (const ArtistId artistId : similarArtistsId) { - const Database::Artist::pointer similarArtist{Database::Artist::getById(LmsApp->getDbSession(), artistId)}; + const Database::Artist::pointer similarArtist {Database::Artist::find(LmsApp->getDbSession(), artistId)}; if (!similarArtist) continue; @@ -252,27 +252,24 @@ Artist::refreshLinks(const Database::Artist::pointer& artist) void Artist::addSomeNonReleaseTracks() { - bool moreResults {}; + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)}; + if (!artist) + return; + + const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {static_cast(_trackContainer->getCount()), _tracksBatchSize})}; + bool moreResults {tracks.moreResults}; + + for (const Track::pointer& track : tracks.results) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - const Database::Artist::pointer artist {Database::Artist::getById(LmsApp->getDbSession(), _artistId)}; - if (!artist) - return; - - const auto tracks {artist->getNonReleaseTracks(std::nullopt, Database::Range {static_cast(_trackContainer->getCount()), _tracksBatchSize}, moreResults)}; - - for (const auto& track : tracks) + if (_trackContainer->getCount() == _tracksMaxCount) { - if (_trackContainer->getCount() == _tracksMaxCount) - { - moreResults = false; - break; - } - - _trackContainer->add(TrackListHelpers::createEntry(track, tracksAction)); + moreResults = false; + break; } + + _trackContainer->add(TrackListHelpers::createEntry(track, tracksAction)); } _trackContainer->setHasMore(moreResults); diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index 3d292760..26255a6a 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -25,7 +25,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/Object.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index b804f453..34fc0966 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -21,6 +21,7 @@ #include +#include "services/database/Artist.hpp" #include "services/database/Session.hpp" #include "services/database/TrackArtistLink.hpp" #include "utils/EnumSet.hpp" @@ -109,7 +110,7 @@ Artists::refreshView(ArtistCollector::Mode mode) } void -Artists::refreshView(std::optional linkType) +Artists::refreshView(std::optional linkType) { _artistCollector.setArtistLinkType(linkType); refreshView(); @@ -120,13 +121,13 @@ Artists::refreshArtistLinkTypes() { std::shared_ptr linkTypeModel {std::static_pointer_cast(_linkType->model())}; - EnumSet usedLinkTypes; + EnumSet usedLinkTypes; { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - usedLinkTypes = Database::TrackArtistLink::getUsedTypes(LmsApp->getDbSession()); + usedLinkTypes = TrackArtistLink::findUsedTypes(LmsApp->getDbSession()); } - auto addTypeIfUsed {[&](Database::TrackArtistLinkType linkType, std::string_view stringKey) + auto addTypeIfUsed {[&](TrackArtistLinkType linkType, std::string_view stringKey) { if (!usedLinkTypes.contains(linkType)) return; @@ -151,17 +152,16 @@ Artists::refreshArtistLinkTypes() void Artists::addSome() { - bool moreResults {}; + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + const auto artistIds {_artistCollector.get(Range {static_cast(_container->getCount()), _batchSize})}; + for (const ArtistId artistId : artistIds.results) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - const auto artists {_artistCollector.get(Range {static_cast(_container->getCount()), _batchSize}, moreResults)}; - for (const auto& artist : artists) - _container->add(ArtistListHelpers::createEntry(artist)); + const auto artist {Artist::find(LmsApp->getDbSession(), artistId)}; + _container->add(ArtistListHelpers::createEntry(artist)); } - _container->setHasMore(moreResults); + _container->setHasMore(artistIds.moreResults); } } // namespace UserInterface diff --git a/src/lms/ui/explore/DatabaseCollectorBase.cpp b/src/lms/ui/explore/DatabaseCollectorBase.cpp index a4e9cdfc..7a161fd7 100644 --- a/src/lms/ui/explore/DatabaseCollectorBase.cpp +++ b/src/lms/ui/explore/DatabaseCollectorBase.cpp @@ -23,28 +23,23 @@ namespace UserInterface { - DatabaseCollectorBase::DatabaseCollectorBase(Filters& filters, Mode defaultMode, std::optional maxCount) + DatabaseCollectorBase::DatabaseCollectorBase(Filters& filters, Mode defaultMode, std::size_t maxCount) : _filters {filters} , _mode {defaultMode} , _maxCount {maxCount} { } - std::optional - DatabaseCollectorBase::getActualRange(std::optional range) const + DatabaseCollectorBase::Range + DatabaseCollectorBase::getActualRange(Range range) const { - if (std::optional maxCount {getMaxCount()}) - { - if (range) - range->limit = std::min(*maxCount - range->offset, range->limit); - else - range = Range {0, *maxCount}; - } + assert(range.offset < _maxCount); + range.size = std::min(_maxCount - range.offset, range.size); return range; } - std::optional + std::size_t DatabaseCollectorBase::getMaxCount() const { return _maxCount; diff --git a/src/lms/ui/explore/DatabaseCollectorBase.hpp b/src/lms/ui/explore/DatabaseCollectorBase.hpp index db38fe5b..66732c32 100644 --- a/src/lms/ui/explore/DatabaseCollectorBase.hpp +++ b/src/lms/ui/explore/DatabaseCollectorBase.hpp @@ -49,17 +49,16 @@ namespace UserInterface All }; - DatabaseCollectorBase(Filters& filters, Mode defaultMode, std::optional maxCount = std::nullopt); + DatabaseCollectorBase(Filters& filters, Mode defaultMode, std::size_t maxCount); Mode getMode() const { return _mode; } void setMode(Mode mode) { _mode = mode; } - void setMaxCount(std::size_t maxCount) { _maxCount = maxCount; } void setSearch(std::string_view search); protected: - std::optional getActualRange(std::optional range) const; - std::optional getMaxCount() const; - Filters& getFilters() { return _filters; } + Range getActualRange(Range range) const; + std::size_t getMaxCount() const; + Filters& getFilters() { return _filters; } const std::vector& getSearchKeywords() const { return _searchKeywords; } private: @@ -67,7 +66,7 @@ namespace UserInterface std::string _searchText; std::vector _searchKeywords; Mode _mode; - std::optional _maxCount; + std::size_t _maxCount; }; } // ns UserInterface diff --git a/src/lms/ui/explore/Explore.cpp b/src/lms/ui/explore/Explore.cpp index b0ab61a7..bf9b1239 100644 --- a/src/lms/ui/explore/Explore.cpp +++ b/src/lms/ui/explore/Explore.cpp @@ -138,7 +138,7 @@ getArtistsTracks(Database::Session& session, const std::vectorgetDbSession().createSharedTransaction()}; - const auto types {Database::ClusterType::getAllUsed(LmsApp->getDbSession())}; - for (const Database::ClusterType::pointer& type : types) - typeCombo->addItem(Wt::WString::fromUTF8(type->getName())); - - if (!types.empty()) + const auto clusterTypesIds {ClusterType::findUsed(LmsApp->getDbSession(), Range {})}; + for (const ClusterTypeId clusterTypeId : clusterTypesIds.results) { - for (const Database::Cluster::pointer& cluster : types.front()->getClusters()) + const auto clusterType {ClusterType::find(LmsApp->getDbSession(), clusterTypeId)}; + typeCombo->addItem(Wt::WString::fromUTF8(clusterType->getName())); + } + + if (!clusterTypesIds.results.empty()) + { + const auto clusterType {ClusterType::find(LmsApp->getDbSession(), clusterTypesIds.results.front())}; + + for (const Cluster::pointer cluster : clusterType->getClusters()) { if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), cluster->getId()) == _clusterIds.end()) valueCombo->addItem(Wt::WString::fromUTF8(cluster->getName())); @@ -74,9 +81,9 @@ Filters::showDialog() auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - auto clusterType = Database::ClusterType::getByName(LmsApp->getDbSession(), name); + auto clusterType = ClusterType::find(LmsApp->getDbSession(), name); - for (const Database::Cluster::pointer& cluster : clusterType->getClusters()) + for (const Cluster::pointer& cluster : clusterType->getClusters()) { if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), cluster->getId()) == _clusterIds.end()) valueCombo->addItem(Wt::WString::fromUTF8(cluster->getName())); @@ -98,16 +105,16 @@ Filters::showDialog() const std::string value {valueCombo->valueText().toUTF8()}; // TODO use a model to store the cluster.id() values - Database::ClusterId clusterId {}; + ClusterId clusterId {}; { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - Database::ClusterType::pointer clusterType {Database::ClusterType::getByName(LmsApp->getDbSession(), type)}; + ClusterType::pointer clusterType {ClusterType::find(LmsApp->getDbSession(), type)}; if (!clusterType) return; - Database::Cluster::pointer cluster {clusterType->getCluster(value)}; + Cluster::pointer cluster {clusterType->getCluster(value)}; if (!cluster) return; @@ -121,7 +128,7 @@ Filters::showDialog() } void -Filters::add(Database::ClusterId clusterId) +Filters::add(ClusterId clusterId) { Wt::WInteractWidget* filter {}; @@ -129,7 +136,7 @@ Filters::add(Database::ClusterId clusterId) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - Database::Cluster::pointer cluster {Database::Cluster::getById(LmsApp->getDbSession(), clusterId)}; + Cluster::pointer cluster {Cluster::find(LmsApp->getDbSession(), clusterId)}; if (!cluster) return; @@ -144,7 +151,7 @@ Filters::add(Database::ClusterId clusterId) filter->clicked().connect([=] { _filters->removeWidget(filter); - _clusterIds.erase(std::remove_if(std::begin(_clusterIds), std::end(_clusterIds), [clusterId](Database::ClusterId id) { return id == clusterId; }), std::end(_clusterIds)); + _clusterIds.erase(std::remove_if(std::begin(_clusterIds), std::end(_clusterIds), [clusterId](ClusterId id) { return id == clusterId; }), std::end(_clusterIds)); _sigUpdated.emit(); }); diff --git a/src/lms/ui/explore/Filters.hpp b/src/lms/ui/explore/Filters.hpp index 585d1472..0f96398a 100644 --- a/src/lms/ui/explore/Filters.hpp +++ b/src/lms/ui/explore/Filters.hpp @@ -24,7 +24,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/ClusterId.hpp" #include "Filters.hpp" diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index e0d0efb5..e109f038 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -34,100 +34,89 @@ namespace UserInterface { using namespace Database; - std::vector - ReleaseCollector::get(std::optional range, bool& moreResults) + RangeResults + ReleaseCollector::get(Database::Range range) { Scrobbling::IScrobblingService& scrobbling {*Service::get()}; range = getActualRange(range); - std::vector releases; + RangeResults releases; switch (getMode()) { case Mode::Random: - releases = getRandomReleases(range, moreResults); + releases = getRandomReleases(range); break; case Mode::Starred: - releases = Release::getStarred(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); + releases = scrobbling.getStarredReleases(LmsApp->getUserId(), getFilters().getClusterIds(), range); break; case ReleaseCollector::Mode::RecentlyPlayed: - for (const ReleaseId releaseId : scrobbling.getRecentReleases(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) - { - if (const Release::pointer release {Release::getById(LmsApp->getDbSession(), releaseId)}) - releases.push_back(release); - } + releases = scrobbling.getRecentReleases(LmsApp->getUserId(), getFilters().getClusterIds(), range); break; case Mode::MostPlayed: - for (const ReleaseId releaseId : scrobbling.getTopReleases(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) - { - if (const Release::pointer release {Release::getById(LmsApp->getDbSession(), releaseId)}) - releases.push_back(release); - } + releases = scrobbling.getTopReleases(LmsApp->getUserId(), getFilters().getClusterIds(), range); break; case Mode::RecentlyAdded: - releases = Release::getLastWritten(LmsApp->getDbSession(), std::nullopt, getFilters().getClusterIds(), range, moreResults); - break; - - case Mode::Search: - releases = Release::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), getSearchKeywords(), range, moreResults); - break; - - case Mode::All: - releases = Release::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), {}, range, moreResults); - break; - } - - if (range && getMaxCount() && (range->offset + range->limit == *getMaxCount())) - moreResults = false; - - return releases; - } - - std::vector - ReleaseCollector::getAll() - { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - bool moreResults; - const auto releases {get(std::nullopt, moreResults)}; - - std::vector res; - res.reserve(releases.size()); - std::transform(std::cbegin(releases), std::cend(releases), std::back_inserter(res), [](const Release::pointer& release) { return release->getId(); }); - - return res; - } - - std::vector - ReleaseCollector::getRandomReleases(std::optional range, bool& moreResults) - { - std::vector releases; - - assert(getMode() == Mode::Random); - - if (_randomReleases.empty()) - _randomReleases = Release::getAllIdsRandom(LmsApp->getDbSession(), getFilters().getClusterIds(), getMaxCount()); - - { - auto itBegin {std::cbegin(_randomReleases) + std::min(range ? range->offset : 0, _randomReleases.size())}; - auto itEnd {std::cbegin(_randomReleases) + std::min(range ? range->offset + range->limit : _randomReleases.size(), _randomReleases.size())}; - - for (auto it {itBegin}; it != itEnd; ++it) { - Release::pointer release {Release::getById(LmsApp->getDbSession(), *it)}; - if (release) - releases.push_back(release); + Release::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setSortMethod(ReleaseSortMethod::LastWritten); + params.setRange(range); + + releases = Release::find(LmsApp->getDbSession(), params); + break; } - moreResults = (itEnd != std::cend(_randomReleases)); + case Mode::Search: + { + Release::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setKeywords(getSearchKeywords()); + params.setSortMethod(ReleaseSortMethod::Name); + params.setRange(range); + + releases = Release::find(LmsApp->getDbSession(), params); + break; + } + + case Mode::All: + { + Release::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setSortMethod(ReleaseSortMethod::Name); + params.setRange(range); + + releases = Release::find(LmsApp->getDbSession(), params); + break; + } } + if (range.offset + range.size == getMaxCount()) + releases.moreResults = false; + return releases; } + RangeResults + ReleaseCollector::getRandomReleases(Range range) + { + assert(getMode() == Mode::Random); + + if (!_randomReleases) + { + Release::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setSortMethod(ReleaseSortMethod::Random); + params.setRange({0, getMaxCount()}); + _randomReleases = Release::find(LmsApp->getDbSession(), params); + } + + return _randomReleases->getSubRange(range); + } + } // ns UserInterface diff --git a/src/lms/ui/explore/ReleaseCollector.hpp b/src/lms/ui/explore/ReleaseCollector.hpp index 3882cc80..924aabe6 100644 --- a/src/lms/ui/explore/ReleaseCollector.hpp +++ b/src/lms/ui/explore/ReleaseCollector.hpp @@ -23,6 +23,10 @@ #include "DatabaseCollectorBase.hpp" +#include "services/database/Object.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/Types.hpp" + namespace Database { class Release; @@ -35,13 +39,12 @@ namespace UserInterface public: using DatabaseCollectorBase::DatabaseCollectorBase; - std::vector> get(std::optional range, bool& moreResults); - std::vector getAll(); - void reset() { _randomReleases.clear(); } + Database::RangeResults get(Database::Range range); + void reset() { _randomReleases.reset(); } private: - std::vector> getRandomReleases(std::optional range, bool& moreResults); - std::vector _randomReleases; + Database::RangeResults getRandomReleases(Range range); + std::optional> _randomReleases; }; } // ns UserInterface diff --git a/src/lms/ui/explore/ReleaseListHelpers.hpp b/src/lms/ui/explore/ReleaseListHelpers.hpp index fd3b051a..41550b85 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.hpp +++ b/src/lms/ui/explore/ReleaseListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/Object.hpp" namespace Database { diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp index cc970b0f..ae8aefcb 100644 --- a/src/lms/ui/explore/ReleasePopup.cpp +++ b/src/lms/ui/explore/ReleasePopup.cpp @@ -24,7 +24,7 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/User.hpp" -#include "services/feedback/IFeedbackService.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "resource/DownloadResource.hpp" #include "utils/Service.hpp" #include "LmsApplication.hpp" @@ -49,14 +49,14 @@ namespace UserInterface releasesAction.emit(PlayQueueAction::PlayLast, {releaseId}); }); - const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), releaseId)}; + const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), releaseId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(&target, [=] { if (isStarred) - Service::get()->unstar(LmsApp->getUserId(), releaseId); + Service::get()->unstar(LmsApp->getUserId(), releaseId); else - Service::get()->star(LmsApp->getUserId(), releaseId); + Service::get()->star(LmsApp->getUserId(), releaseId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(releaseId)}); diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index 199810dc..2e3f2deb 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -78,14 +78,14 @@ extractReleaseIdFromInternalPath() if (mbid) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - if (const Database::Release::pointer release {Database::Release::getByMBID(LmsApp->getDbSession(), *mbid)}) + if (const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), *mbid)}) return release->getId(); } return std::nullopt; } - return StringUtils::readAs(wApp->internalPathNextPart("/release/")); + return StringUtils::readAs(wApp->internalPathNextPart("/release/")); } @@ -105,7 +105,7 @@ Release::refreshView() auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Release::pointer release {Database::Release::getById(LmsApp->getDbSession(), *releaseId)}; + const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), *releaseId)}; if (!release) throw ReleaseNotFoundException {}; @@ -218,7 +218,7 @@ Release::refreshView() entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain); - const auto artists {track->getArtists({Database::TrackArtistLinkType::Artist})}; + const auto artists {track->getArtists({TrackArtistLinkType::Artist})}; if (variousArtists && !artists.empty()) { entry->setCondition("if-has-artists", true); @@ -252,7 +252,7 @@ Release::refreshView() entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain); - LmsApp->getMediaPlayer().trackLoaded.connect(entry, [=] (Database::TrackId loadedTrackId) + LmsApp->getMediaPlayer().trackLoaded.connect(entry, [=] (TrackId loadedTrackId) { entry->bindString("is-playing", loadedTrackId == trackId ? "Lms-entry-playing" : ""); }); @@ -270,12 +270,12 @@ Release::refreshView() void Release::refreshReleaseArtists(const Database::Release::pointer& release) { - std::vector> artists; + std::vector> artists; artists = release->getReleaseArtists(); if (artists.empty()) { - artists = release->getArtists(Database::TrackArtistLinkType::Artist); + artists = release->getArtists(TrackArtistLinkType::Artist); if (artists.size() > 1) { setCondition("if-has-various-release-artists", true); @@ -340,7 +340,7 @@ Release::refreshLinks(const Database::Release::pointer& release) } void -Release::refreshSimilarReleases(const std::vector& similarReleasesId) +Release::refreshSimilarReleases(const std::vector& similarReleasesId) { if (similarReleasesId.empty()) return; @@ -348,9 +348,9 @@ Release::refreshSimilarReleases(const std::vector& similarR setCondition("if-has-similar-releases", true); auto* similarReleasesContainer {bindNew("similar-releases")}; - for (const Database::ReleaseId id : similarReleasesId) + for (const ReleaseId id : similarReleasesId) { - const Database::Release::pointer similarRelease{Database::Release::getById(LmsApp->getDbSession(), id)}; + const Database::Release::pointer similarRelease {Database::Release::find(LmsApp->getDbSession(), id)}; if (!similarRelease) continue; diff --git a/src/lms/ui/explore/ReleaseView.hpp b/src/lms/ui/explore/ReleaseView.hpp index 855bc044..41f9fbf6 100644 --- a/src/lms/ui/explore/ReleaseView.hpp +++ b/src/lms/ui/explore/ReleaseView.hpp @@ -22,7 +22,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/Object.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index 8e47fe4e..6fc358da 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -23,6 +23,7 @@ #include #include +#include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "ReleaseListHelpers.hpp" @@ -115,23 +116,24 @@ Releases::refreshView(ReleaseCollector::Mode mode) void Releases::addSome() { - bool moreResults {}; + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + const auto releaseIds {_releaseCollector.get(Range {static_cast(_container->getCount()), _batchSize})}; + for (const ReleaseId releaseId : releaseIds.results) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - const auto releases {_releaseCollector.get(Range {static_cast(_container->getCount()), _batchSize}, moreResults)}; - for (const auto& release : releases) - _container->add(ReleaseListHelpers::createEntry(release)); + const Release::pointer release {Release::find(LmsApp->getDbSession(), releaseId)}; + _container->add(ReleaseListHelpers::createEntry(release)); } - _container->setHasMore(moreResults); + _container->setHasMore(releaseIds.moreResults); } -std::vector +std::vector Releases::getAllReleases() { - return _releaseCollector.getAll(); + RangeResults releaseIds {_releaseCollector.get(Range {})}; + + return std::move(releaseIds.results); } } // namespace UserInterface diff --git a/src/lms/ui/explore/SearchView.cpp b/src/lms/ui/explore/SearchView.cpp index b82ff3a0..ac573799 100644 --- a/src/lms/ui/explore/SearchView.cpp +++ b/src/lms/ui/explore/SearchView.cpp @@ -38,6 +38,8 @@ #include "ReleaseListHelpers.hpp" #include "TrackListHelpers.hpp" +using namespace Database; + namespace UserInterface { SearchView::SearchView(Filters* filters) @@ -133,17 +135,22 @@ namespace UserInterface SearchView::addSomeArtists() { InfiniteScrollingContainer& results {getResultContainer(Mode::Artist)}; - bool moreResults {}; { + using namespace Database; + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Range range {results.getCount(), getBatchSize(Mode::Artist)}; - for (const auto& artist : _artistCollector.get(range, moreResults)) + const Range range {results.getCount(), getBatchSize(Mode::Artist)}; + const RangeResults artistIds {_artistCollector.get(range)}; + for (const ArtistId artistId : artistIds.results) + { + const Artist::pointer artist {Artist::find(LmsApp->getDbSession(), artistId)}; results.add(ArtistListHelpers::createEntry(artist)); - } + } - results.setHasMore(moreResults); + results.setHasMore(artistIds.moreResults); + } getItemMenu(Mode::Artist).setDisabled(results.getCount() == 0); } @@ -155,14 +162,21 @@ namespace UserInterface bool moreResults {}; { + using namespace Database; + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Range range {results.getCount(), getBatchSize(Mode::Release)}; - for (const auto& release : _releaseCollector.get(range, moreResults)) - results.add(ReleaseListHelpers::createEntry(release)); - } + const Range range {results.getCount(), getBatchSize(Mode::Release)}; + const RangeResults releaseIds {_releaseCollector.get(range)}; - results.setHasMore(moreResults); + for (const ReleaseId releaseId : releaseIds.results) + { + const Release::pointer release {Release::find(LmsApp->getDbSession(), releaseId)}; + results.add(ReleaseListHelpers::createEntry(release)); + } + + results.setHasMore(moreResults); + } getItemMenu(Mode::Release).setDisabled(results.getCount() == 0); } @@ -171,17 +185,22 @@ namespace UserInterface SearchView::addSomeTracks() { InfiniteScrollingContainer& results {getResultContainer(Mode::Track)}; - bool moreResults {}; - { + using namespace Database; + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Range range {results.getCount(), getBatchSize(Mode::Track)}; - for (const auto& track : _trackCollector.get(range, moreResults)) - results.add(TrackListHelpers::createEntry(track, tracksAction)); - } + const Range range {results.getCount(), getBatchSize(Mode::Track)}; + const RangeResults trackIds {_trackCollector.get(range)}; - results.setHasMore(moreResults); + for (const TrackId trackId : trackIds.results) + { + const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)}; + results.add(TrackListHelpers::createEntry(track, tracksAction)); + } + + results.setHasMore(trackIds.moreResults); + } getItemMenu(Mode::Track).setDisabled(results.getCount() == 0); } diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 13be14fc..bbefe4cd 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -34,99 +34,86 @@ namespace UserInterface { using namespace Database; - std::vector - TrackCollector::get(std::optional range, bool& moreResults) + RangeResults + TrackCollector::get(Range range) { Scrobbling::IScrobblingService& scrobbling {*Service::get()}; range = getActualRange(range); - std::vector tracks; + RangeResults tracks; switch (getMode()) { case Mode::Random: - tracks = getRandomTracks(range, moreResults); + tracks = getRandomTracks(range); break; case Mode::Starred: - tracks = Track::getStarred(LmsApp->getDbSession(), LmsApp->getUser(), getFilters().getClusterIds(), range, moreResults); + tracks = scrobbling.getStarredTracks(LmsApp->getUserId(), getFilters().getClusterIds(), range); break; case TrackCollector::Mode::RecentlyPlayed: - for (const TrackId trackId : scrobbling.getRecentTracks(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) - { - if (const Track::pointer track {Track::getById(LmsApp->getDbSession(), trackId)}) - tracks.push_back(track); - } + tracks = scrobbling.getRecentTracks(LmsApp->getUserId(), getFilters().getClusterIds(), range); break; case Mode::MostPlayed: - for (const TrackId trackId : scrobbling.getTopTracks(LmsApp->getUserId(), getFilters().getClusterIds(), range, moreResults)) - { - if (const Track::pointer track {Track::getById(LmsApp->getDbSession(), trackId)}) - tracks.push_back(track); - } + tracks = scrobbling.getTopTracks(LmsApp->getUserId(), getFilters().getClusterIds(), range); break; case Mode::RecentlyAdded: - tracks = Track::getLastWritten(LmsApp->getDbSession(), std::nullopt, getFilters().getClusterIds(), range, moreResults); - break; - - case Mode::Search: - tracks = Track::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), getSearchKeywords(), range, moreResults); - break; - - case Mode::All: - tracks = Track::getByFilter(LmsApp->getDbSession(), getFilters().getClusterIds(), {}, range, moreResults); - break; - } - - if (range && getMaxCount() && (range->offset + range->limit == *getMaxCount())) - moreResults = false; - - return tracks; - } - - std::vector - TrackCollector::getAll() - { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - bool moreResults; - const auto tracks {get(std::nullopt, moreResults)}; - - std::vector res; - res.reserve(tracks.size()); - std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const Track::pointer& track) { return track->getId(); }); - - return res; - } - - std::vector - TrackCollector::getRandomTracks(std::optional range, bool& moreResults) - { - std::vector tracks; - - assert(getMode() == Mode::Random); - - if (_randomTracks.empty()) - _randomTracks = Track::getAllIdsRandom(LmsApp->getDbSession(), getFilters().getClusterIds(), getMaxCount()); - - { - auto itBegin {std::cbegin(_randomTracks) + std::min(range ? range->offset : 0, _randomTracks.size())}; - auto itEnd {std::cbegin(_randomTracks) + std::min(range ? range->offset + range->limit : _randomTracks.size(), _randomTracks.size())}; - - for (auto it {itBegin}; it != itEnd; ++it) { - Track::pointer track {Track::getById(LmsApp->getDbSession(), *it)}; - if (track) - tracks.push_back(track); + Track::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setSortMethod(TrackSortMethod::LastWritten); + params.setRange(range); + + tracks = Track::find(LmsApp->getDbSession(), params); + break; } - moreResults = (itEnd != std::cend(_randomTracks)); + case Mode::Search: + { + Track::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setKeywords(getSearchKeywords()); + params.setRange(range); + + tracks = Track::find(LmsApp->getDbSession(), params); + break; + } + + case Mode::All: + { + Track::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setRange(range); + + tracks = Track::find(LmsApp->getDbSession(), params); + break; + } } + if (range.offset + range.size == getMaxCount()) + tracks.moreResults = false; + return tracks; } + RangeResults + TrackCollector::getRandomTracks(Range range) + { + assert(getMode() == Mode::Random); + + if (!_randomTracks) + { + Track::FindParameters params; + params.setClusters(getFilters().getClusterIds()); + params.setSortMethod(TrackSortMethod::Random); + params.setRange({0, getMaxCount()}); + _randomTracks = Track::find(LmsApp->getDbSession(), params); + } + + return _randomTracks->getSubRange(range); + } + } // ns UserInterface diff --git a/src/lms/ui/explore/TrackCollector.hpp b/src/lms/ui/explore/TrackCollector.hpp index 79011adf..a87f9759 100644 --- a/src/lms/ui/explore/TrackCollector.hpp +++ b/src/lms/ui/explore/TrackCollector.hpp @@ -21,6 +21,8 @@ #include +#include "services/database/Object.hpp" +#include "services/database/TrackId.hpp" #include "DatabaseCollectorBase.hpp" namespace Database @@ -35,13 +37,12 @@ namespace UserInterface public: using DatabaseCollectorBase::DatabaseCollectorBase; - std::vector> get(std::optional range, bool& moreResults); - std::vector getAll(); - void reset() { _randomTracks.clear(); } + Database::RangeResults get(Database::Range range); + void reset() { _randomTracks.reset(); } private: - std::vector> getRandomTracks(std::optional range, bool& moreResults); - std::vector _randomTracks; + Database::RangeResults getRandomTracks(Range range); + std::optional> _randomTracks; }; } // ns UserInterface diff --git a/src/lms/ui/explore/TrackListHelpers.hpp b/src/lms/ui/explore/TrackListHelpers.hpp index 2ba93246..00b322f9 100644 --- a/src/lms/ui/explore/TrackListHelpers.hpp +++ b/src/lms/ui/explore/TrackListHelpers.hpp @@ -22,7 +22,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/Object.hpp" #include "PlayQueueAction.hpp" namespace Database diff --git a/src/lms/ui/explore/TrackPopup.cpp b/src/lms/ui/explore/TrackPopup.cpp index f95e5825..7020d2d4 100644 --- a/src/lms/ui/explore/TrackPopup.cpp +++ b/src/lms/ui/explore/TrackPopup.cpp @@ -24,7 +24,7 @@ #include "services/database/Session.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" -#include "services/feedback/IFeedbackService.hpp" +#include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "resource/DownloadResource.hpp" #include "LmsApplication.hpp" @@ -44,20 +44,20 @@ namespace UserInterface tracksAction.emit(PlayQueueAction::PlayLast, {trackId}); }); - bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), trackId)}; + const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), trackId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) ->triggered().connect(&target, [=] { auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; - auto track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; + auto track {Database::Track::find(LmsApp->getDbSession(), trackId)}; if (!track) return; if (isStarred) - Service::get()->unstar(LmsApp->getUserId(), trackId); + Service::get()->unstar(LmsApp->getUserId(), trackId); else - Service::get()->star(LmsApp->getUserId(), trackId); + Service::get()->star(LmsApp->getUserId(), trackId); }); popup->addItem(Wt::WString::tr("Lms.Explore.download")) ->setLink(Wt::WLink {std::make_unique(trackId)}); diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index 639046de..c93e9a18 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -24,6 +24,7 @@ #include #include "services/database/Session.hpp" +#include "services/database/Track.hpp" #include "utils/Logger.hpp" #include "common/InfiniteScrollingContainer.hpp" @@ -118,24 +119,25 @@ Tracks::refreshView(TrackCollector::Mode mode) void Tracks::addSome() { - bool moreResults {}; + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + const auto trackIds {_trackCollector.get(Range {static_cast(_container->getCount()), _batchSize})}; + + for (const TrackId trackId : trackIds.results) { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - - const auto tracks {_trackCollector.get(Range {static_cast(_container->getCount()), _batchSize}, moreResults)}; - - for (const auto& track : tracks) - _container->add(TrackListHelpers::createEntry(track, tracksAction)); + const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)}; + _container->add(TrackListHelpers::createEntry(track, tracksAction)); } - _container->setHasMore(moreResults); + _container->setHasMore(trackIds.moreResults); } std::vector Tracks::getAllTracks() { - return _trackCollector.getAll(); + RangeResults trackIds {_trackCollector.get(Range {})}; + + return std::move(trackIds.results); } diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index 42ff45b6..792128c0 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -51,7 +51,7 @@ getTrackPathFromTrackId(Database::TrackId trackId) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; + const Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), trackId)}; if (!track) { LOG(ERROR) << "Missing track"; diff --git a/src/lms/ui/resource/AudioFileResource.hpp b/src/lms/ui/resource/AudioFileResource.hpp index 046f7c55..589ac51e 100644 --- a/src/lms/ui/resource/AudioFileResource.hpp +++ b/src/lms/ui/resource/AudioFileResource.hpp @@ -21,7 +21,7 @@ #include -#include "services/database/Types.hpp" +#include "services/database/TrackId.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/AudioTranscodeResource.cpp b/src/lms/ui/resource/AudioTranscodeResource.cpp index acc04918..c8982fbd 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.cpp +++ b/src/lms/ui/resource/AudioTranscodeResource.cpp @@ -149,7 +149,7 @@ readTranscodeParameters(const Wt::Http::Request& request) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), *trackId)}; + const Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), *trackId)}; if (!track) { LOG(ERROR) << "Missing track"; @@ -158,7 +158,7 @@ readTranscodeParameters(const Wt::Http::Request& request) parameters.file = track->getPath(); - if (Database::User::audioTranscodeAllowedBitrates.find(*bitrate) == std::cend(Database::User::audioTranscodeAllowedBitrates)) + if (!Database::isAudioBitrateAllowed(*bitrate)) { LOG(ERROR) << "Bitrate '" << *bitrate << "' is not allowed"; return std::nullopt; diff --git a/src/lms/ui/resource/AudioTranscodeResource.hpp b/src/lms/ui/resource/AudioTranscodeResource.hpp index dbd1d31a..a4240b15 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.hpp +++ b/src/lms/ui/resource/AudioTranscodeResource.hpp @@ -22,7 +22,7 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/TrackId.hpp" namespace Database { diff --git a/src/lms/ui/resource/CoverResource.hpp b/src/lms/ui/resource/CoverResource.hpp index 7ab2a1fe..533a96f1 100644 --- a/src/lms/ui/resource/CoverResource.hpp +++ b/src/lms/ui/resource/CoverResource.hpp @@ -20,7 +20,8 @@ #pragma once #include -#include "services/database/Types.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" namespace UserInterface { diff --git a/src/lms/ui/resource/DownloadResource.cpp b/src/lms/ui/resource/DownloadResource.cpp index 7dd75d79..f87d4645 100644 --- a/src/lms/ui/resource/DownloadResource.cpp +++ b/src/lms/ui/resource/DownloadResource.cpp @@ -176,7 +176,7 @@ DownloadArtistResource::DownloadArtistResource(Database::ArtistId artistId) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - Database::Artist::pointer artist {Database::Artist::getById(LmsApp->getDbSession(), artistId)}; + Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), artistId)}; if (artist) suggestFileName(getArtistPathName(artist) + ".zip"); } @@ -186,7 +186,7 @@ DownloadArtistResource::createZipper() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Artist::pointer artist {Database::Artist::getById(LmsApp->getDbSession(), _artistId)}; + const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)}; if (!artist) { LOG(DEBUG) << "Cannot find artist"; @@ -201,7 +201,7 @@ DownloadReleaseResource::DownloadReleaseResource(Database::ReleaseId releaseId) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - Database::Release::pointer release {Database::Release::getById(LmsApp->getDbSession(), releaseId)}; + Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseId)}; if (release) suggestFileName(getReleasePathName(release) + ".zip"); } @@ -212,7 +212,7 @@ DownloadReleaseResource::createZipper() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Release::pointer release {Database::Release::getById(LmsApp->getDbSession(), _releaseId)}; + const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), _releaseId)}; if (!release) { LOG(DEBUG) << "Cannot find release"; @@ -227,7 +227,7 @@ DownloadTrackResource::DownloadTrackResource(Database::TrackId trackId) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; + Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), trackId)}; if (track) suggestFileName(getTrackPathName(track) + ".zip"); } @@ -237,7 +237,7 @@ DownloadTrackResource::createZipper() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - const Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), _trackId)}; + const Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), _trackId)}; if (!track) { LOG(DEBUG) << "Cannot find track"; diff --git a/src/lms/ui/resource/DownloadResource.hpp b/src/lms/ui/resource/DownloadResource.hpp index 80d02994..9203d0af 100644 --- a/src/lms/ui/resource/DownloadResource.hpp +++ b/src/lms/ui/resource/DownloadResource.hpp @@ -22,7 +22,9 @@ #include #include -#include "services/database/Types.hpp" +#include "services/database/ArtistId.hpp" +#include "services/database/ReleaseId.hpp" +#include "services/database/TrackId.hpp" #include "utils/Zipper.hpp" namespace UserInterface diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index 7d72fd09..36291e5b 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -38,13 +38,15 @@ static void dumpTrackCovers(Database::Session& session, Image::ImageSize width) { - std::vector trackIds; + using namespace Database; + + RangeResults trackIds; { auto transaction {session.createSharedTransaction()}; - trackIds = Database::Track::getAllIds(session); + trackIds = Database::Track::find(session, Database::Track::FindParameters {}); } - for (const Database::TrackId trackId : trackIds) + for (const Database::TrackId trackId : trackIds.results) { std::cout << "Getting cover for track id " << trackId.toString() << std::endl; Service::get()->getFromTrack(trackId, width); diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index 98666179..5dec1cb3 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -30,34 +30,37 @@ #include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" +#include "services/database/Types.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/IConfig.hpp" #include "utils/Service.hpp" #include "utils/StreamLogger.hpp" +using namespace Database; + static void -dumpTracksRecommendation(Database::Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) +dumpTracksRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) { - const std::vector trackIds {[&]() + const RangeResults trackIds {[&]() { auto transaction {session.createSharedTransaction()}; - return Database::Track::getAllIds(session); + return Track::find(session, Track::FindParameters {}); }()}; - std::cout << "*** Tracks (" << trackIds.size() << ") ***" << std::endl; - for (Database::TrackId trackId : trackIds) + std::cout << "*** Tracks (" << trackIds.results.size() << ") ***" << std::endl; + for (const TrackId trackId : trackIds.results) { - auto trackToString = [&](Database::TrackId trackId) + auto trackToString = [&](const TrackId trackId) { std::string res; auto transaction {session.createSharedTransaction()}; - Database::Track::pointer track {Database::Track::getById(session, trackId)}; + const Track::pointer track {Track::find(session, trackId)}; res += track->getName(); if (track->getRelease()) res += " [" + track->getRelease()->getName() + "]"; - for (auto artist : track->getArtists({Database::TrackArtistLinkType::Artist})) + for (auto artist : track->getArtists({TrackArtistLinkType::Artist})) res += " - " + artist->getName(); for (auto cluster : track->getClusters()) res += " {" + cluster->getType()->getName() + "-"+ cluster->getName() + "}"; @@ -66,61 +69,61 @@ dumpTracksRecommendation(Database::Session session, Recommendation::IRecommendat }; std::cout << "Processing track '" << trackToString(trackId) << std::endl; - for (Database::TrackId similarTrackId : recommendationService.getSimilarTracks({trackId}, maxSimilarityCount)) + for (TrackId similarTrackId : recommendationService.findSimilarTracks({trackId}, maxSimilarityCount)) std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl; } } static void -dumpReleasesRecommendation(Database::Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) +dumpReleasesRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) { - const std::vector releaseIds = std::invoke([&]() + const RangeResults releaseIds {std::invoke([&]() { auto transaction {session.createSharedTransaction()}; - return Database::Release::getAllIds(session); - }); + return Release::find(session, Release::FindParameters {}); + })}; std::cout << "*** Releases ***" << std::endl; - for (Database::ReleaseId releaseId : releaseIds) + for (ReleaseId releaseId : releaseIds.results) { - auto releaseToString = [&](Database::ReleaseId releaseId) -> std::string + auto releaseToString = [&](ReleaseId releaseId) -> std::string { auto transaction {session.createSharedTransaction()}; - Database::Release::pointer release {Database::Release::getById(session, releaseId)}; + Release::pointer release {Release::find(session, releaseId)}; return release->getName(); }; std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl; - for (Database::ReleaseId similarReleaseId : recommendationService.getSimilarReleases(releaseId, maxSimilarityCount)) + for (ReleaseId similarReleaseId : recommendationService.getSimilarReleases(releaseId, maxSimilarityCount)) std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl; } } static void -dumpArtistsRecommendation(Database::Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) +dumpArtistsRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount) { - const std::vector artistIds = std::invoke([&]() + const RangeResults artistIds = std::invoke([&]() { auto transaction {session.createSharedTransaction()}; - return Database::Artist::getAllIds(session); + return Artist::find(session, Artist::FindParameters {}); }); std::cout << "*** Artists ***" << std::endl; - for (Database::ArtistId artistId : artistIds) + for (ArtistId artistId : artistIds.results) { - auto artistToString = [&](Database::ArtistId artistId) + auto artistToString = [&](ArtistId artistId) { auto transaction {session.createSharedTransaction()}; - Database::Artist::pointer artist {Database::Artist::getById(session, artistId)}; + Artist::pointer artist {Artist::find(session, artistId)}; return artist->getName(); }; std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl; - for (Database::ArtistId similarArtistId : recommendationService.getSimilarArtists(artistId, {Database::TrackArtistLinkType::Artist, Database::TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) + for (ArtistId similarArtistId : recommendationService.getSimilarArtists(artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) { std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl; } @@ -158,8 +161,8 @@ 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}; + Db db {config->getPath("working-dir") / "lms.db"}; + Session session {db}; std::cout << "Creating recommendation recommendationService..." << std::endl; const auto recommendationService {Recommendation::createRecommendationService(db)}; From 63ed28e38f6a15ab43f0647c63eb3db4da0096d9 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 5 Dec 2021 15:20:07 +0100 Subject: [PATCH 28/49] Fixed regression that prevents app to quit if another process is already running --- .../recommendation/impl/RecommendationService.cpp | 11 +++++++---- src/libs/services/scanner/impl/ScannerService.cpp | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libs/services/recommendation/impl/RecommendationService.cpp b/src/libs/services/recommendation/impl/RecommendationService.cpp index 76e4e693..67445959 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.cpp +++ b/src/libs/services/recommendation/impl/RecommendationService.cpp @@ -223,13 +223,14 @@ namespace Recommendation engine->load(forceReload, progressCallback ? progress : ProgressCallback {}); - { - std::scoped_lock lock {_controlMutex}; - _pendingEngines.erase(std::find(std::begin(_pendingEngines), std::end(_pendingEngines), engine.get())); - } LMS_LOG(RECOMMENDATION, INFO) << "Initializing engine '" << engineTypeToString(engineType) << "': " << (_loadCancelled ? "aborted" : "complete"); } + { + std::scoped_lock lock {_controlMutex}; + _pendingEngines.erase(std::find(std::begin(_pendingEngines), std::end(_pendingEngines), engine.get())); + } + if (!_loadCancelled) { std::unique_lock lock {_enginesMutex}; @@ -250,7 +251,9 @@ namespace Recommendation LMS_LOG(RECOMMENDATION, DEBUG) << "Still " << _pendingEngines.size() << " pending engines!"; for (IEngine* engine : _pendingEngines) + { engine->requestCancelLoad(); + } _pendingEnginesCondvar.wait(controlLock, [this] {return _pendingEngines.empty();}); _loadCancelled = false; diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index d00c97ff..abac1180 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -270,8 +270,9 @@ ScannerService::ScannerService(Db& db, Recommendation::IRecommendationService& r ScannerService::~ScannerService() { - LMS_LOG(DBUPDATER, INFO) << "Shutting down Scanner..."; + LMS_LOG(DBUPDATER, INFO) << "Stopping service..."; stop(); + LMS_LOG(DBUPDATER, INFO) << "Service stopped!"; } void From c6338771cf147cde87939baa7709810cb2bf9f65 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 5 Dec 2021 15:20:57 +0100 Subject: [PATCH 29/49] Changed http client to be instanciated by services --- .../scrobbling/impl/ScrobblingService.cpp | 8 ++++ .../scrobbling/impl/ScrobblingService.hpp | 1 + .../listenbrainz/ListenBrainzScrobbler.cpp | 7 ++-- .../listenbrainz/ListenBrainzScrobbler.hpp | 9 +++-- .../impl/listenbrainz/ListensSynchronizer.cpp | 16 ++++---- .../impl/listenbrainz/ListensSynchronizer.hpp | 9 ++++- src/libs/utils/impl/IOContextRunner.cpp | 6 +-- src/libs/utils/impl/http/Client.cpp | 38 ++----------------- src/libs/utils/impl/http/Client.hpp | 12 +++--- src/libs/utils/impl/http/SendQueue.cpp | 10 +++-- src/libs/utils/impl/http/SendQueue.hpp | 3 +- .../utils/http/ClientRequestParameters.hpp | 2 +- src/libs/utils/include/utils/http/IClient.hpp | 2 +- src/lms/main.cpp | 2 - 14 files changed, 56 insertions(+), 69 deletions(-) diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp index 607c1f1e..a89ca613 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -30,6 +30,7 @@ #include "services/database/StarredTrack.hpp" #include "services/database/Track.hpp" #include "services/database/User.hpp" +#include "utils/Logger.hpp" #include "internal/InternalScrobbler.hpp" #include "listenbrainz/ListenBrainzScrobbler.hpp" @@ -47,8 +48,15 @@ namespace Scrobbling ScrobblingService::ScrobblingService(boost::asio::io_context& ioContext, Db& db) : _db {db} { + LMS_LOG(SCROBBLING, INFO) << "Starting service..."; _scrobblers.emplace(Scrobbler::Internal, std::make_unique(_db)); _scrobblers.emplace(Scrobbler::ListenBrainz, std::make_unique(ioContext, _db)); + LMS_LOG(SCROBBLING, INFO) << "Service started!"; + } + + ScrobblingService::~ScrobblingService() + { + LMS_LOG(SCROBBLING, INFO) << "Service stopped!"; } void diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.hpp b/src/libs/services/scrobbling/impl/ScrobblingService.hpp index 63377ad6..ce9efaa5 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.hpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.hpp @@ -32,6 +32,7 @@ namespace Scrobbling { public: ScrobblingService(boost::asio::io_context& ioContext, Database::Db& db); + ~ScrobblingService(); private: void listenStarted(const Listen& listen) override; diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index db614b18..81c95b88 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -144,7 +144,8 @@ namespace Scrobbling::ListenBrainz : _ioContext {ioContext} , _db {db} , _baseAPIUrl {Service::get()->getString("listenbrainz-api-base-url", "https://api.listenbrainz.org")} - , _listensSynchronizer {_ioContext, db, _baseAPIUrl} + , _client {Http::createClient(_ioContext, _baseAPIUrl)} + , _listensSynchronizer {_ioContext, db, *_client} { LOG(INFO) << "Starting ListenBrainz scrobbler... API endpoint = '" << _baseAPIUrl; } @@ -183,7 +184,7 @@ namespace Scrobbling::ListenBrainz Scrobbler::enqueListen(const Listen& listen, const Wt::WDateTime& timePoint) { Http::ClientPOSTRequestParameters request; - request.url = _baseAPIUrl + "/1/submit-listens"; + request.relativeUrl = "/1/submit-listens"; if (timePoint.isValid()) { @@ -213,7 +214,7 @@ namespace Scrobbling::ListenBrainz request.message.addBodyText(bodyText); request.message.addHeader("Authorization", "Token " + std::string {listenBrainzToken->getAsString()}); request.message.addHeader("Content-Type", "application/json"); - Service::get()->sendPOSTRequest(std::move(request)); + _client->sendPOSTRequest(std::move(request)); } } // namespace Scrobbling::ListenBrainz diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp index 8134ac3c..7863ce74 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp @@ -53,10 +53,11 @@ namespace Scrobbling::ListenBrainz // Submit listens void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); - boost::asio::io_context& _ioContext; - Database::Db& _db; - std::string _baseAPIUrl; - ListensSynchronizer _listensSynchronizer; + boost::asio::io_context& _ioContext; + Database::Db& _db; + std::string _baseAPIUrl; + std::unique_ptr _client; + ListensSynchronizer _listensSynchronizer; }; } // Scrobbling::ListenBrainz diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 644172a0..f3b603b8 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -213,10 +213,10 @@ namespace namespace Scrobbling::ListenBrainz { - ListensSynchronizer::ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, std::string_view baseAPIUrl) + ListensSynchronizer::ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, Http::IClient& client) : _ioContext {ioContext} , _db {db} - , _baseAPIUrl {baseAPIUrl} + , _client {client} , _maxSyncListenCount {Service::get()->getULong("listenbrainz-max-sync-listen-count", 1000)} , _syncListensPeriod {Service::get()->getULong("listenbrainz-sync-listens-period-hours", 1)} { @@ -364,7 +364,7 @@ namespace Scrobbling::ListenBrainz Http::ClientGETRequestParameters request; request.priority = Http::ClientRequestParameters::Priority::Low; - request.url = _baseAPIUrl + "/1/validate-token"; + request.relativeUrl = "/1/validate-token"; request.headers = { {"Authorization", "Token " + std::string {listenBrainzToken->getAsString()}} }; request.onSuccessFunc = [this, &context] (std::string_view msgBody) { @@ -381,7 +381,7 @@ namespace Scrobbling::ListenBrainz onGetListensEnded(context); }; - Service::get()->sendGETRequest(std::move(request)); + _client.sendGETRequest(std::move(request)); } void @@ -390,7 +390,7 @@ namespace Scrobbling::ListenBrainz assert(!context.listenBrainzUserName.empty()); Http::ClientGETRequestParameters request; - request.url = _baseAPIUrl + "/1/user/" + std::string {context.listenBrainzUserName} + "/listen-count"; + request.relativeUrl = "/1/user/" + std::string {context.listenBrainzUserName} + "/listen-count"; request.priority = Http::ClientRequestParameters::Priority::Low; request.onSuccessFunc = [=, &context] (std::string_view msgBody) { @@ -415,7 +415,7 @@ namespace Scrobbling::ListenBrainz onGetListensEnded(context); }; - Service::get()->sendGETRequest(std::move(request)); + _client.sendGETRequest(std::move(request)); } void @@ -424,7 +424,7 @@ namespace Scrobbling::ListenBrainz assert(!context.listenBrainzUserName.empty()); Http::ClientGETRequestParameters request; - request.url = _baseAPIUrl + "/1/user/" + context.listenBrainzUserName + "/listens?max_ts=" + std::to_string(context.maxDateTime.toTime_t()); + request.relativeUrl = "/1/user/" + context.listenBrainzUserName + "/listens?max_ts=" + std::to_string(context.maxDateTime.toTime_t()); request.priority = Http::ClientRequestParameters::Priority::Low; request.onSuccessFunc = [=, &context] (std::string_view msgBody) { @@ -442,7 +442,7 @@ namespace Scrobbling::ListenBrainz onGetListensEnded(context); }; - Service::get()->sendGETRequest(std::move(request)); + _client.sendGETRequest(std::move(request)); } void diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index 84ed3a6a..0b95365d 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -36,12 +36,17 @@ namespace Database class User; } +namespace Http +{ + class IClient; +} + namespace Scrobbling::ListenBrainz { class ListensSynchronizer { public: - ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, std::string_view baseAPIUrl); + ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, Http::IClient& client); void saveListen(const TimedListen& listen); @@ -81,8 +86,8 @@ namespace Scrobbling::ListenBrainz boost::asio::io_context& _ioContext; boost::asio::io_context::strand _strand {_ioContext}; Database::Db& _db; - std::string _baseAPIUrl; boost::asio::steady_timer _getListensTimer {_ioContext}; + Http::IClient& _client; std::unordered_map _userContexts; diff --git a/src/libs/utils/impl/IOContextRunner.cpp b/src/libs/utils/impl/IOContextRunner.cpp index 934b3b64..8cd1fd14 100644 --- a/src/libs/utils/impl/IOContextRunner.cpp +++ b/src/libs/utils/impl/IOContextRunner.cpp @@ -27,7 +27,7 @@ IOContextRunner::IOContextRunner(boost::asio::io_service& ioService, std::size_t : _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([&] @@ -48,10 +48,10 @@ IOContextRunner::IOContextRunner(boost::asio::io_service& ioService, std::size_t void IOContextRunner::stop() { - LMS_LOG(UTILS, INFO) << "Stopping IO Context"; + LMS_LOG(UTILS, DEBUG) << "Stopping IO context..."; _work.reset(); _ioService.stop(); - LMS_LOG(UTILS, INFO) << "Stopped IO Context"; + LMS_LOG(UTILS, DEBUG) << "IO context stopped!"; } IOContextRunner::~IOContextRunner() diff --git a/src/libs/utils/impl/http/Client.cpp b/src/libs/utils/impl/http/Client.cpp index e4a12a49..ecc9d817 100644 --- a/src/libs/utils/impl/http/Client.cpp +++ b/src/libs/utils/impl/http/Client.cpp @@ -23,51 +23,21 @@ namespace Http { std::unique_ptr - createClient(boost::asio::io_context& ioContext) + createClient(boost::asio::io_context& ioContext, std::string_view baseUrl) { - return std::make_unique(ioContext); + return std::make_unique(ioContext, baseUrl); } void Client::sendGETRequest(ClientGETRequestParameters&& GETParams) { - SendQueue& sendQueue {getOrCreateSendQueue(GETParams.url)}; - sendQueue.sendRequest(std::make_unique(std::move(GETParams))); + _sendQueue.sendRequest(std::make_unique(std::move(GETParams))); } void Client::sendPOSTRequest(ClientPOSTRequestParameters&& POSTParams) { - SendQueue& sendQueue {getOrCreateSendQueue(POSTParams.url)}; - sendQueue.sendRequest(std::make_unique(std::move(POSTParams))); + _sendQueue.sendRequest(std::make_unique(std::move(POSTParams))); } - - SendQueue& - Client::getOrCreateSendQueue(const std::string& url) - { - Wt::Http::Client::URL parsedURL; - if (!Wt::Http::Client::parseUrl(url, parsedURL)) - throw LmsException {"Cannot parse URL '" + url + "'"}; - - { - std::shared_lock lock {_sendQueuesMutex}; - - if (auto it = _sendQueues.find(parsedURL.host); it != std::cend(_sendQueues)) - return it->second; - } - - { - std::unique_lock lock {_sendQueuesMutex}; - - if (auto it = _sendQueues.find(parsedURL.host); it != std::cend(_sendQueues)) - return it->second; - - auto [it, inserted] {_sendQueues.emplace(parsedURL.host, _ioContext)}; - assert(inserted); - - return it->second; - } - } - } // namespace Http diff --git a/src/libs/utils/impl/http/Client.hpp b/src/libs/utils/impl/http/Client.hpp index d6bff7f4..f6a42b40 100644 --- a/src/libs/utils/impl/http/Client.hpp +++ b/src/libs/utils/impl/http/Client.hpp @@ -31,17 +31,17 @@ namespace Http class Client final : public IClient { public: - Client(boost::asio::io_context& ioContext) : _ioContext {ioContext} {} + Client(boost::asio::io_context& ioContext, std::string_view baseUrl) + : _ioContext {ioContext} + , _sendQueue {ioContext, baseUrl} + {} private: void sendGETRequest(ClientGETRequestParameters&& request) override; void sendPOSTRequest(ClientPOSTRequestParameters&& request) override; - SendQueue& getOrCreateSendQueue(const std::string& host); - - boost::asio::io_context& _ioContext; - std::shared_mutex _sendQueuesMutex; - std::unordered_map _sendQueues; + boost::asio::io_context& _ioContext; + SendQueue _sendQueue; }; } // namespace Http diff --git a/src/libs/utils/impl/http/SendQueue.cpp b/src/libs/utils/impl/http/SendQueue.cpp index de5a0722..d689179e 100644 --- a/src/libs/utils/impl/http/SendQueue.cpp +++ b/src/libs/utils/impl/http/SendQueue.cpp @@ -60,8 +60,9 @@ namespace namespace Http { - SendQueue::SendQueue(boost::asio::io_context& ioContext) + SendQueue::SendQueue(boost::asio::io_context& ioContext, std::string_view baseUrl) : _ioContext {ioContext} + , _baseUrl {baseUrl} { _client.done().connect([this](Wt::AsioWrapper::error_code ec, const Wt::Http::Message& msg) { @@ -116,17 +117,18 @@ namespace Http bool SendQueue::sendRequest(const ClientRequest& request) { - LOG(DEBUG) << "Sending request to url '" << request.getParameters().url << "'"; + std::string url {_baseUrl + request.getParameters().relativeUrl}; + LOG(DEBUG) << "Sending request to url '" << url << "'"; bool res {}; switch (request.getType()) { case ClientRequest::Type::GET: - res = _client.get(request.getParameters().url, request.getGETParameters().headers); + res = _client.get(url, request.getGETParameters().headers); break; case ClientRequest::Type::POST: - res = _client.post(request.getParameters().url, request.getPOSTParameters().message); + res = _client.post(url, request.getPOSTParameters().message); break; } diff --git a/src/libs/utils/impl/http/SendQueue.hpp b/src/libs/utils/impl/http/SendQueue.hpp index 0e3bba24..f309cb2c 100644 --- a/src/libs/utils/impl/http/SendQueue.hpp +++ b/src/libs/utils/impl/http/SendQueue.hpp @@ -35,7 +35,7 @@ namespace Http class SendQueue { public: - SendQueue(boost::asio::io_context& ioContext); + SendQueue(boost::asio::io_context& ioContext, std::string_view baseUrl); ~SendQueue(); SendQueue(const SendQueue&) = delete; @@ -62,6 +62,7 @@ namespace Http boost::asio::io_context& _ioContext; boost::asio::io_context::strand _strand {_ioContext}; boost::asio::steady_timer _throttleTimer {_ioContext}; + std::string _baseUrl; enum class State { diff --git a/src/libs/utils/include/utils/http/ClientRequestParameters.hpp b/src/libs/utils/include/utils/http/ClientRequestParameters.hpp index 2e11f183..94fc1cdd 100644 --- a/src/libs/utils/include/utils/http/ClientRequestParameters.hpp +++ b/src/libs/utils/include/utils/http/ClientRequestParameters.hpp @@ -37,7 +37,7 @@ namespace Http }; Priority priority {Priority::Normal}; - std::string url; + std::string relativeUrl; // relative to baseUrl used by the client using OnSuccessFunc = std::function; OnSuccessFunc onSuccessFunc; diff --git a/src/libs/utils/include/utils/http/IClient.hpp b/src/libs/utils/include/utils/http/IClient.hpp index 0feb33df..09df5ee5 100644 --- a/src/libs/utils/include/utils/http/IClient.hpp +++ b/src/libs/utils/include/utils/http/IClient.hpp @@ -35,6 +35,6 @@ namespace Http virtual void sendPOSTRequest(ClientPOSTRequestParameters&& request) = 0; }; - std::unique_ptr createClient(boost::asio::io_context& ioContext); + std::unique_ptr createClient(boost::asio::io_context& ioContext, std::string_view baseUrl); } // namespace Http diff --git a/src/lms/main.cpp b/src/lms/main.cpp index c269f7a8..34f82676 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -37,7 +37,6 @@ #include "subsonic/SubsonicResource.hpp" #include "ui/LmsApplication.hpp" #include "ui/LmsApplicationManager.hpp" -#include "utils/http/IClient.hpp" #include "utils/IChildProcessManager.hpp" #include "utils/IConfig.hpp" #include "utils/IOContextRunner.hpp" @@ -255,7 +254,6 @@ int main(int argc, char* argv[]) else throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"}; - Service httpClient {Http::createClient(ioContext)}; Service coverService {Cover::createCoverService(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; Service recommendationService {Recommendation::createRecommendationService(database)}; Service scannerService {Scanner::createScannerService(database, *recommendationService)}; From 37de73a9d0f807e573b0d7598bdf6d60ad69651a Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 19 Jan 2022 13:58:42 +0100 Subject: [PATCH 30/49] Fixed the transcoding info wrong green color --- approot/mediaplayer.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/approot/mediaplayer.xml b/approot/mediaplayer.xml index f7b70da6..46f1078f 100644 --- a/approot/mediaplayer.xml +++ b/approot/mediaplayer.xml @@ -35,7 +35,7 @@ From aac119f34c507953360148cba2b8408d9fb4cb3e Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 20 Jan 2022 19:52:10 +0100 Subject: [PATCH 31/49] Removed unused files --- .../listenbrainz/FeedbackSynchronizer.hpp | 92 ------------------- .../impl/listenbrainz/LovesSynchronizer.hpp | 92 ------------------- 2 files changed, 184 deletions(-) delete mode 100644 src/libs/services/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp delete mode 100644 src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp diff --git a/src/libs/services/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp deleted file mode 100644 index 0547921a..00000000 --- a/src/libs/services/scrobbling/impl/listenbrainz/FeedbackSynchronizer.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2021 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#pragma once - -#include -#include -#include -#include - -#include "SendQueue.hpp" - -namespace Database -{ - class Db; - class Session; - class User; -} - -namespace Scrobbling::ListenBrainz -{ - class ListensSynchronizer - { - public: - FeedbackSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, SendQueue& sendQueue); - - // void updateFeedback(const TimedListen& listen); - - private: - struct UserContext - { - UserContext(Database::UserId id) : userId {id} {} - - UserContext(const UserContext&) = delete; - UserContext(UserContext&&) = delete; - UserContext& operator=(const UserContext&) = delete; - UserContext& operator=(UserContext&&) = delete; - - const Database::UserId userId; - bool fetching {}; - std::optional listenCount {}; - - // resetted at each fetch - std::string listenBrainzUserName; // need to be resolved first - Wt::WDateTime maxDateTime; - std::size_t fetchedListenCount{}; - std::size_t matchedListenCount{}; - std::size_t importedListenCount{}; - }; - - UserContext& getUserContext(Database::UserId userId); - bool isFetching() const; - void scheduleGetListens(std::chrono::seconds fromNow); - void startGetListens(); - void startGetListens(UserContext& context); - void onGetListensEnded(UserContext& context); - void enqueValidateToken(UserContext& context); - void enqueGetListenCount(UserContext& context); - void enqueGetListens(UserContext& context); - std::optional createValidateTokenRequestData(Database::UserId userId); - std::optional createGetListensRequestData(std::string_view listenBrainzUserName, const Wt::WDateTime& maxDateTime); - void processGetListensResponse(std::string_view body, UserContext& context); - - boost::asio::io_context& _ioContext; - boost::asio::io_context::strand _strand {_ioContext}; - Database::Db& _db; - SendQueue& _sendQueue; - boost::asio::steady_timer _getListensTimer {_ioContext}; - - std::unordered_map _userContexts; - - const std::size_t _maxSyncFeedbackCount; - const std::chrono::hours _syncFeedbackPeriod; - }; -} // Scrobbling::ListenBrainz - diff --git a/src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp deleted file mode 100644 index 0f7bf48f..00000000 --- a/src/libs/services/scrobbling/impl/listenbrainz/LovesSynchronizer.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2021 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include "services/database/Types.hpp" -#include "services/scrobbling/Listen.hpp" - -namespace Database -{ - class Db; - class Session; - class TrackList; - class User; -} - -namespace Scrobbling::ListenBrainz -{ - class ListensSynchronizer - { - public: - ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, std::string_view baseAPIUrl); - - void saveListen(const TimedListen& listen); - - private: - struct UserContext - { - UserContext(Database::UserId id) : userId {id} {} - - UserContext(const UserContext&) = delete; - UserContext(UserContext&&) = delete; - UserContext& operator=(const UserContext&) = delete; - UserContext& operator=(UserContext&&) = delete; - - const Database::UserId userId; - bool fetching {}; - - // resetted at each fetch - std::string listenBrainzUserName; // need to be resolved first - Wt::WDateTime maxDateTime; - std::size_t fetchedLoveCount{}; - std::size_t matchedLoveCount{}; - std::size_t importedLoveCount{}; - }; - - UserContext& getUserContext(Database::UserId userId); - bool isFetching() const; - void scheduleGetListens(std::chrono::seconds fromNow); - void startGetListens(); - void startGetListens(UserContext& context); - void onGetListensEnded(UserContext& context); - void enqueValidateToken(UserContext& context); - void enqueGetListenCount(UserContext& context); - void enqueGetListens(UserContext& context); - void processGetListensResponse(std::string_view body, UserContext& context); - - boost::asio::io_context& _ioContext; - boost::asio::io_context::strand _strand {_ioContext}; - Database::Db& _db; - std::string _baseAPIUrl; - boost::asio::steady_timer _getListensTimer {_ioContext}; - - std::unordered_map _userContexts; - - const std::size_t _maxSyncListenCount; - const std::chrono::hours _syncListensPeriod; - }; -} // Scrobbling::ListenBrainz - From 9e64287383d4fe52d3b200e9b2a66e4a7157d5a4 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 20 Jan 2022 19:56:42 +0100 Subject: [PATCH 32/49] Added scrobbling state for listens --- src/libs/services/database/impl/Session.cpp | 1 + .../database/include/services/database/Listen.hpp | 3 +++ .../database/include/services/database/Types.hpp | 13 +++++++------ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 76e0396c..57f7f1e4 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -414,6 +414,7 @@ CREATE TABLE IF NOT EXISTS "listen" ( "version" integer not null, "date_time" text, "scrobbler" integer not null, + "scrobbling_state" integer not null, "track_id" bigint, "user_id" bigint, constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, diff --git a/src/libs/services/database/include/services/database/Listen.hpp b/src/libs/services/database/include/services/database/Listen.hpp index 32c02294..29d3394d 100644 --- a/src/libs/services/database/include/services/database/Listen.hpp +++ b/src/libs/services/database/include/services/database/Listen.hpp @@ -95,6 +95,7 @@ class Listen : public Object { Wt::Dbo::field(a, _dateTime, "date_time"); Wt::Dbo::field(a, _scrobbler, "scrobbler"); + Wt::Dbo::field(a, _scrobblingState, "scrobbling_state"); Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade); Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); @@ -103,6 +104,8 @@ class Listen : public Object private: Wt::WDateTime _dateTime; Scrobbler _scrobbler; + ScrobblingState _scrobblingState {ScrobblingState::PendingAdd}; + Wt::Dbo::ptr _user; Wt::Dbo::ptr _track; }; diff --git a/src/libs/services/database/include/services/database/Types.hpp b/src/libs/services/database/include/services/database/Types.hpp index c1c0021b..4333be45 100644 --- a/src/libs/services/database/include/services/database/Types.hpp +++ b/src/libs/services/database/include/services/database/Types.hpp @@ -111,7 +111,6 @@ namespace Database StarredDateDesc, }; - // Do not change enum values! enum class TrackArtistLinkType { Artist = 0, // regular track artist @@ -128,7 +127,6 @@ namespace Database }; // User selectable audio file formats - // Do not change values enum class AudioFormat { MP3 = 1, @@ -143,14 +141,19 @@ namespace Database void visitAllowedAudioBitrates(std::function); bool isAudioBitrateAllowed(Bitrate bitrate); - // Do not change enum values! enum class Scrobbler { Internal = 0, ListenBrainz = 1, }; - // Do not change enum values! + enum class ScrobblingState + { + PendingAdd = 0, + Synchronized = 1, + PendingRemove = 2, + }; + enum class UserType { REGULAR = 0, @@ -158,14 +161,12 @@ namespace Database DEMO = 2, }; - // Do not change enum values! enum class UITheme { Light = 0, Dark = 1, }; - // Do not change enum values! enum class SubsonicArtistListMode { AllArtists = 0, From d52cc7166730708df5f4aa6e48186e878e124e10 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 13 Dec 2021 13:49:07 +0100 Subject: [PATCH 33/49] Restored thread checks on database accesses --- src/libs/services/database/impl/Session.cpp | 12 +----- .../include/services/database/Session.hpp | 2 - src/libs/utils/impl/RecursiveSharedMutex.cpp | 43 +++++++++++++++---- .../include/utils/RecursiveSharedMutex.hpp | 4 ++ 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 57f7f1e4..1fbdb2cb 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -456,14 +456,6 @@ Session::Session(Db& db) _session.mapClass("user"); } - -enum class OwnedLock -{ - None, - Shared, - Unique, -}; - UniqueTransaction::UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) : _lock {mutex}, _transaction {session} @@ -479,13 +471,13 @@ SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Sessi void Session::checkUniqueLocked() { -// assert(lockDebug[&_db.getMutex()] == OwnedLock::Unique); + assert(_db.getMutex().isUniqueLocked()); } void Session::checkSharedLocked() { -// assert(lockDebug[&_db.getMutex()] != OwnedLock::None); + assert(_db.getMutex().isSharedLocked()); } UniqueTransaction diff --git a/src/libs/services/database/include/services/database/Session.hpp b/src/libs/services/database/include/services/database/Session.hpp index 6378c0a3..7871a2e8 100644 --- a/src/libs/services/database/include/services/database/Session.hpp +++ b/src/libs/services/database/include/services/database/Session.hpp @@ -74,8 +74,6 @@ namespace Database Wt::Dbo::Session& getDboSession() { return _session; } private: - Session(std::shared_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool); - void doDatabaseMigrationIfNeeded(); Db& _db; diff --git a/src/libs/utils/impl/RecursiveSharedMutex.cpp b/src/libs/utils/impl/RecursiveSharedMutex.cpp index 4adaa18a..9dec11de 100644 --- a/src/libs/utils/impl/RecursiveSharedMutex.cpp +++ b/src/libs/utils/impl/RecursiveSharedMutex.cpp @@ -24,7 +24,9 @@ void RecursiveSharedMutex::lock() { - if (_uniqueOwner == std::this_thread::get_id()) + const auto thisThreadId {std::this_thread::get_id()}; + + if (_uniqueOwner == thisThreadId) { // already locked _uniqueCount++; @@ -32,7 +34,7 @@ RecursiveSharedMutex::lock() else { _mutex.lock(); - _uniqueOwner = std::this_thread::get_id(); + _uniqueOwner = thisThreadId; assert(_uniqueCount == 0); _uniqueCount = 1; } @@ -53,10 +55,12 @@ RecursiveSharedMutex::unlock() void RecursiveSharedMutex::lock_shared() { - if (_uniqueOwner == std::this_thread::get_id()) + const auto thisThreadId {std::this_thread::get_id()}; + + if (_uniqueOwner == thisThreadId ) { // alone here, no need to lock - _sharedCounts[std::this_thread::get_id()]++; + _sharedCounts[thisThreadId]++; return; } @@ -66,7 +70,7 @@ RecursiveSharedMutex::lock_shared() { std::scoped_lock lock {_sharedCountMutex}; - auto& sharedCount {_sharedCounts[std::this_thread::get_id()]}; + auto& sharedCount {_sharedCounts[thisThreadId]}; if (sharedCount == 0) needLock = true; else @@ -80,17 +84,19 @@ RecursiveSharedMutex::lock_shared() assert(_uniqueOwner == std::thread::id {}); std::scoped_lock lock {_sharedCountMutex}; - _sharedCounts[std::this_thread::get_id()]++; + _sharedCounts[thisThreadId]++; } } void RecursiveSharedMutex::unlock_shared() { - if (_uniqueOwner == std::this_thread::get_id()) + const auto thisThreadId {std::this_thread::get_id()}; + + if (_uniqueOwner == thisThreadId ) { // alone here, no need to lock - auto& sharedCount {_sharedCounts[std::this_thread::get_id()]}; + auto& sharedCount {_sharedCounts[thisThreadId ]}; assert(sharedCount > 0); --sharedCount; @@ -102,7 +108,7 @@ RecursiveSharedMutex::unlock_shared() { std::scoped_lock lock {_sharedCountMutex}; - auto& sharedCount {_sharedCounts[std::this_thread::get_id()]}; + auto& sharedCount {_sharedCounts[thisThreadId]}; assert(sharedCount > 0); needUnlock = (--sharedCount == 0); } @@ -110,3 +116,22 @@ RecursiveSharedMutex::unlock_shared() if (needUnlock) _mutex.unlock_shared(); } + +#ifndef NDEBUG +bool +RecursiveSharedMutex::isUniqueLocked() +{ + return _uniqueOwner == std::this_thread::get_id(); +} + +bool +RecursiveSharedMutex::isSharedLocked() +{ + const auto thisThreadId {std::this_thread::get_id()}; + if (_uniqueOwner == thisThreadId ) + return true; + + std::scoped_lock lock {_sharedCountMutex}; + return _sharedCounts[thisThreadId] > 0; +} +#endif diff --git a/src/libs/utils/include/utils/RecursiveSharedMutex.hpp b/src/libs/utils/include/utils/RecursiveSharedMutex.hpp index 84f05453..57f6631e 100644 --- a/src/libs/utils/include/utils/RecursiveSharedMutex.hpp +++ b/src/libs/utils/include/utils/RecursiveSharedMutex.hpp @@ -34,6 +34,10 @@ class RecursiveSharedMutex void lock_shared(); void unlock_shared(); +#ifndef NDEBUG + bool isSharedLocked(); + bool isUniqueLocked(); +#endif // NDEBUG private: std::shared_mutex _mutex; std::thread::id _uniqueOwner; From 4851581975f4e2980898bf19bec8300d8a69e353 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 20 Jan 2022 20:57:59 +0100 Subject: [PATCH 34/49] Isolated DB migraiton stuff --- src/libs/services/database/CMakeLists.txt | 1 + src/libs/services/database/impl/Migration.cpp | 421 ++++++++++++++++++ src/libs/services/database/impl/Migration.hpp | 55 +++ src/libs/services/database/impl/Session.cpp | 397 +---------------- .../database/include/services/database/Db.hpp | 24 +- .../include/services/database/Session.hpp | 4 +- 6 files changed, 486 insertions(+), 416 deletions(-) create mode 100644 src/libs/services/database/impl/Migration.cpp create mode 100644 src/libs/services/database/impl/Migration.hpp diff --git a/src/libs/services/database/CMakeLists.txt b/src/libs/services/database/CMakeLists.txt index 8e8b9fe3..dc1a11f5 100644 --- a/src/libs/services/database/CMakeLists.txt +++ b/src/libs/services/database/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(lmsdatabase SHARED impl/Cluster.cpp impl/Db.cpp impl/Listen.cpp + impl/Migration.cpp impl/TrackArtistLink.cpp impl/TrackFeatures.cpp impl/TrackList.cpp diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp new file mode 100644 index 00000000..e179bfa0 --- /dev/null +++ b/src/libs/services/database/impl/Migration.cpp @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include "Migration.hpp" + +#include "services/database/Db.hpp" +#include "services/database/ScanSettings.hpp" +#include "services/database/Session.hpp" +#include "services/database/User.hpp" +#include "utils/Exception.hpp" +#include "utils/Logger.hpp" + +namespace Database +{ + VersionInfo::pointer + VersionInfo::getOrCreate(Session& session) + { + session.checkUniqueLocked(); + + pointer versionInfo {session.getDboSession().find()}; + if (!versionInfo) + return session.getDboSession().add(std::make_unique()); + + return versionInfo; + } + + VersionInfo::pointer + VersionInfo::get(Session& session) + { + session.checkSharedLocked(); + + return session.getDboSession().find(); + } + +} + +namespace Database::Migration +{ + class ScopedNoForeignKeys + { + public: + ScopedNoForeignKeys(Db& db) : _db {db} + { + _db.executeSql("PRAGMA foreign_keys=OFF"); + } + ~ScopedNoForeignKeys() + { + _db.executeSql("PRAGMA foreign_keys=ON"); + } + + ScopedNoForeignKeys(const ScopedNoForeignKeys&) = delete; + ScopedNoForeignKeys(ScopedNoForeignKeys&&) = delete; + ScopedNoForeignKeys& operator=(const ScopedNoForeignKeys&) = delete; + ScopedNoForeignKeys& operator=(ScopedNoForeignKeys&&) = delete; + + private: + Db& _db; + }; + + void + doDbMigration(Session& session) + { + static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"}; + + ScopedNoForeignKeys noPragmaKeys {session.getDb()}; + + while (1) + { + auto uniqueTransaction {session.createUniqueTransaction()}; + + Version version; + try + { + version = VersionInfo::getOrCreate(session)->getVersion(); + LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION; + if (version == LMS_DATABASE_VERSION) + { + LMS_LOG(DB, DEBUG) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!"; + return; + } + } + catch (std::exception& e) + { + LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what(); + throw LmsException {outdatedMsg}; + } + + LMS_LOG(DB, INFO) << "Migrating database from version " << version << "..."; + + if (version == 5) + { + session.getDboSession().execute("DELETE FROM auth_token"); // format has changed + } + else if (version == 6) + { + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 7) + { + session.getDboSession().execute("DROP TABLE similarity_settings"); + session.getDboSession().execute("DROP TABLE similarity_settings_feature"); + session.getDboSession().execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(ScanSettings::RecommendationEngineType::Clusters)) + ")"); + } + else if (version == 8) + { + // Better cover handling, need to rescan the whole files + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 9) + { + session.getDboSession().execute(R"( +CREATE TABLE IF NOT EXISTS "track_bookmark" ( + "id" integer primary key autoincrement, + "version" integer not null, + "offset" integer, + "comment" text not null, + "track_id" bigint, + "user_id" bigint, + constraint "fk_track_bookmark_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, + constraint "fk_track_bookmark_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +);)"); + } + else if (version == 10) + { + ScanSettings::get(session).modify()->addAudioFileExtension(".m4b"); + ScanSettings::get(session).modify()->addAudioFileExtension(".alac"); + } + else if (version == 11) + { + // Sanitize bad MBID, need to rescan the whole files + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 12) + { + // Artist and release that have a badly parsed name but a MBID had no chance to updat the name + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 13) + { + // Always store UUID in lower case + better WMA parsing + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 14) + { + // SortName now set from metadata + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 15) + { + session.getDboSession().execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultUITheme)) + ")"); + } + else if (version == 16) + { + session.getDboSession().execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)"); + session.getDboSession().execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 17) + { + // Drop colums total_disc/total_track from release + session.getDboSession().execute(R"( +CREATE TABLE "release_backup" ( + "id" integer primary key autoincrement, + "version" integer not null, + "name" text not null, + "mbid" text not null +))"); + session.getDboSession().execute("INSERT INTO release_backup SELECT id,version,name,mbid FROM release"); + session.getDboSession().execute("DROP TABLE release"); + session.getDboSession().execute("ALTER TABLE release_backup RENAME TO release"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 18) + { + session.getDboSession().execute(R"( +CREATE TABLE IF NOT EXISTS "subsonic_settings" ( + "id" integer primary key autoincrement, + "version" integer not null, + "api_enabled" boolean not null, + "artist_list_mode" integer not null +))"); + } + else if (version == 19) + { + session.getDboSession().execute(R"( +CREATE TABLE "user_backup" ( + "id" integer primary key autoincrement, + "version" integer not null, + "type" integer not null, + "login_name" text not null, + "password_salt" text not null, + "password_hash" text not null, + "last_login" text, + "subsonic_transcode_enable" boolean not null, + "subsonic_transcode_format" integer not null, + "subsonic_transcode_bitrate" integer not null, + "subsonic_artist_list_mode" integer not null, + "ui_theme" integer not null, + "cur_playing_track_pos" integer not null, + "repeat_all" boolean not null, + "radio" boolean not null +))"); + session.getDboSession().execute(std::string {"INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, "} + + (User::defaultSubsonicTranscodeEnable ? "1" : "0") + + ", " + std::to_string(static_cast(User::defaultSubsonicTranscodeFormat)) + + ", " + std::to_string(User::defaultSubsonicTranscodeBitrate) + + ", " + std::to_string(static_cast(User::defaultSubsonicArtistListMode)) + + ", ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); + session.getDboSession().execute("DROP TABLE user"); + session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); + } + else if (version == 20) + { + session.getDboSession().execute("DROP TABLE subsonic_settings"); + } + else if (version == 21) + { + session.getDboSession().execute("ALTER TABLE track ADD track_replay_gain REAL"); + session.getDboSession().execute("ALTER TABLE track ADD release_replay_gain REAL"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 22) + { + session.getDboSession().execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 23) + { + // Better cover detection + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 24) + { + // User's AuthMode + session.getDboSession().execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(/*User::defaultAuthMode*/0)) + ")"); + } + else if (version == 25) + { + // Better cover detection + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 26) + { + // Composer, mixer, etc. support + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 27) + { + // Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 28) + { + // Drop Auth mode + session.getDboSession().execute(R"( +CREATE TABLE "user_backup" ( + "id" integer primary key autoincrement, + "version" integer not null, + "type" integer not null, + "login_name" text not null, + "password_salt" text not null, + "password_hash" text not null, + "last_login" text, + "subsonic_transcode_enable" boolean not null, + "subsonic_transcode_format" integer not null, + "subsonic_transcode_bitrate" integer not null, + "subsonic_artist_list_mode" integer not null, + "ui_theme" integer not null, + "cur_playing_track_pos" integer not null, + "repeat_all" boolean not null, + "radio" boolean not null +))"); + session.getDboSession().execute("INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, subsonic_transcode_enable, subsonic_transcode_format, subsonic_transcode_bitrate, subsonic_artist_list_mode, ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); + session.getDboSession().execute("DROP TABLE user"); + session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); + } + else if (version == 29) + { + session.getDboSession().execute("ALTER TABLE tracklist_entry ADD date_time TEXT"); + session.getDboSession().execute("ALTER TABLE user ADD listenbrainz_token TEXT"); + session.getDboSession().execute("ALTER TABLE user ADD scrobbler INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultScrobbler)) + ")"); + session.getDboSession().execute("ALTER TABLE track ADD recording_mbid TEXT"); + + session.getDboSession().execute("DELETE from tracklist WHERE name = ?").bind("__played_tracks__"); + + // MBID changes + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 30) + { + // drop "year" and "original_year" (rescan needed to convert them into dates) + session.getDboSession().execute(R"( +CREATE TABLE "track_backup" ( + "id" integer primary key autoincrement, + "version" integer not null, + "scan_version" integer not null, + "track_number" integer not null, + "disc_number" integer not null, + "name" text not null, + "duration" integer, + "date" integer text, + "original_date" integer text, + "file_path" text not null, + "file_last_write" text, + "file_added" text, + "has_cover" boolean not null, + "mbid" text not null, + "copyright" text not null, + "copyright_url" text not null, + "release_id" bigint, total_disc INTEGER NOT NULL DEFAULT(0), total_track INTEGER NOT NULL DEFAULT(0), track_replay_gain REAL, release_replay_gain REAL, disc_subtitle TEXT NOT NULL DEFAULT '', recording_mbid TEXT, + constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred +))"); + session.getDboSession().execute("INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, name, duration, \"1900-01-01\", \"1900-01-01\", file_path, file_last_write, file_added, has_cover, mbid, copyright, copyright_url, release_id, total_disc, total_track, track_replay_gain, release_replay_gain, disc_subtitle, recording_mbid FROM track"); + session.getDboSession().execute("DROP TABLE track"); + session.getDboSession().execute("ALTER TABLE track_backup RENAME TO track"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + else if (version == 31) + { + // new star system, using dedicated ObjectSets per scrobbler + session.getDboSession().execute("DROP TABLE user_artist_starred"); + session.getDboSession().execute("DROP TABLE user_release_starred"); + session.getDboSession().execute("DROP TABLE user_track_starred"); + + session.getDboSession().execute(R"( +CREATE TABLE IF NOT EXISTS "starred_artist" ( + "id" integer primary key autoincrement, + "version" integer not null, + "scrobbler" integer not null, + "date_time" text, + "artist_id" bigint, + "user_id" bigint, + constraint "fk_starred_artist_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred, + constraint "fk_starred_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + + session.getDboSession().execute(R"( +CREATE TABLE IF NOT EXISTS "starred_release" ( + "id" integer primary key autoincrement, + "version" integer not null, + "scrobbler" integer not null, + "date_time" text, + "release_id" bigint, + "user_id" bigint, + constraint "fk_starred_release_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred, + constraint "fk_starred_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + + session.getDboSession().execute(R"( +CREATE TABLE IF NOT EXISTS "starred_track" ( + "id" integer primary key autoincrement, + "version" integer not null, + "scrobbler" integer not null, + "date_time" text, + "track_id" bigint, + "user_id" bigint, + constraint "fk_starred_track_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, + constraint "fk_starred_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + + // new listen system, no longer using tracklists + session.getDboSession().execute(R"( +CREATE TABLE IF NOT EXISTS "listen" ( + "id" integer primary key autoincrement, + "version" integer not null, + "date_time" text, + "scrobbler" integer not null, + "scrobbling_state" integer not null, + "track_id" bigint, + "user_id" bigint, + constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, + constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + } + else + { + LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; + throw LmsException { LMS_DATABASE_VERSION > version ? outdatedMsg : "Server binary outdated, please upgrade it to handle this database"}; + } + + VersionInfo::get(session).modify()->setVersion(++version); + } + } +} diff --git a/src/libs/services/database/impl/Migration.hpp b/src/libs/services/database/impl/Migration.hpp new file mode 100644 index 00000000..ce5ae962 --- /dev/null +++ b/src/libs/services/database/impl/Migration.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include + +namespace Database +{ + class Session; + + using Version = std::size_t; + static constexpr Version LMS_DATABASE_VERSION {32}; + class VersionInfo + { + public: + using pointer = Wt::Dbo::ptr; + + static VersionInfo::pointer getOrCreate(Session& session); + static VersionInfo::pointer get(Session& session); + + Version getVersion() const { return _version; } + void setVersion(Version version) { _version = static_cast(version); } + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _version, "db_version"); + } + + private: + int _version {LMS_DATABASE_VERSION}; + }; + + namespace Migration + { + void doDbMigration(Session& session); + } +} diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 1fbdb2cb..660e7082 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -19,10 +19,7 @@ #include "services/database/Session.hpp" -#include -#include -#include -#include +#include #include "utils/Exception.hpp" #include "utils/Logger.hpp" @@ -43,394 +40,11 @@ #include "services/database/TrackList.hpp" #include "services/database/TrackFeatures.hpp" #include "services/database/User.hpp" +#include "Migration.hpp" namespace Database { - using Version = std::size_t; - static constexpr Version LMS_DATABASE_VERSION {32}; - - class VersionInfo - { - public: - using pointer = Wt::Dbo::ptr; - - static VersionInfo::pointer getOrCreate(Session& session) - { - session.checkUniqueLocked(); - - pointer versionInfo {session.getDboSession().find()}; - if (!versionInfo) - return session.getDboSession().add(std::make_unique()); - - return versionInfo; - } - - static VersionInfo::pointer get(Session& session) - { - session.checkSharedLocked(); - - return session.getDboSession().find(); - } - - Version getVersion() const { return _version; } - void setVersion(Version version) { _version = static_cast(version); } - - template - void persist(Action& a) - { - Wt::Dbo::field(a, _version, "db_version"); - } - - private: - int _version {LMS_DATABASE_VERSION}; - }; - -void -Session::doDatabaseMigrationIfNeeded() -{ - static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"}; - - Db::ScopedNoForeignKeys noPragmaKeys {_db}; - - while (1) - { - auto uniqueTransaction {createUniqueTransaction()}; - - Version version; - try - { - version = VersionInfo::getOrCreate(*this)->getVersion(); - LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION; - if (version == LMS_DATABASE_VERSION) - { - LMS_LOG(DB, DEBUG) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!"; - return; - } - } - catch (std::exception& e) - { - LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what(); - throw LmsException {outdatedMsg}; - } - - LMS_LOG(DB, INFO) << "Migrating database from version " << version << "..."; - - if (version == 5) - { - _session.execute("DELETE FROM auth_token"); // format has changed - } - else if (version == 6) - { - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 7) - { - _session.execute("DROP TABLE similarity_settings"); - _session.execute("DROP TABLE similarity_settings_feature"); - _session.execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(ScanSettings::RecommendationEngineType::Clusters)) + ")"); - } - else if (version == 8) - { - // Better cover handling, need to rescan the whole files - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 9) - { - _session.execute(R"( -CREATE TABLE IF NOT EXISTS "track_bookmark" ( - "id" integer primary key autoincrement, - "version" integer not null, - "offset" integer, - "comment" text not null, - "track_id" bigint, - "user_id" bigint, - constraint "fk_track_bookmark_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, - constraint "fk_track_bookmark_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred -);)"); - } - else if (version == 10) - { - ScanSettings::get(*this).modify()->addAudioFileExtension(".m4b"); - ScanSettings::get(*this).modify()->addAudioFileExtension(".alac"); - } - else if (version == 11) - { - // Sanitize bad MBID, need to rescan the whole files - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 12) - { - // Artist and release that have a badly parsed name but a MBID had no chance to updat the name - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 13) - { - // Always store UUID in lower case + better WMA parsing - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 14) - { - // SortName now set from metadata - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 15) - { - _session.execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultUITheme)) + ")"); - } - else if (version == 16) - { - _session.execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)"); - _session.execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)"); - - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 17) - { - // Drop colums total_disc/total_track from release - _session.execute(R"( -CREATE TABLE "release_backup" ( - "id" integer primary key autoincrement, - "version" integer not null, - "name" text not null, - "mbid" text not null -))"); - _session.execute("INSERT INTO release_backup SELECT id,version,name,mbid FROM release"); - _session.execute("DROP TABLE release"); - _session.execute("ALTER TABLE release_backup RENAME TO release"); - - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 18) - { - _session.execute(R"( -CREATE TABLE IF NOT EXISTS "subsonic_settings" ( - "id" integer primary key autoincrement, - "version" integer not null, - "api_enabled" boolean not null, - "artist_list_mode" integer not null -))"); - } - else if (version == 19) - { - _session.execute(R"( -CREATE TABLE "user_backup" ( - "id" integer primary key autoincrement, - "version" integer not null, - "type" integer not null, - "login_name" text not null, - "password_salt" text not null, - "password_hash" text not null, - "last_login" text, - "subsonic_transcode_enable" boolean not null, - "subsonic_transcode_format" integer not null, - "subsonic_transcode_bitrate" integer not null, - "subsonic_artist_list_mode" integer not null, - "ui_theme" integer not null, - "cur_playing_track_pos" integer not null, - "repeat_all" boolean not null, - "radio" boolean not null -))"); - _session.execute(std::string {"INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, "} - + (User::defaultSubsonicTranscodeEnable ? "1" : "0") - + ", " + std::to_string(static_cast(User::defaultSubsonicTranscodeFormat)) - + ", " + std::to_string(User::defaultSubsonicTranscodeBitrate) - + ", " + std::to_string(static_cast(User::defaultSubsonicArtistListMode)) - + ", ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); - _session.execute("DROP TABLE user"); - _session.execute("ALTER TABLE user_backup RENAME TO user"); - } - else if (version == 20) - { - _session.execute("DROP TABLE subsonic_settings"); - } - else if (version == 21) - { - _session.execute("ALTER TABLE track ADD track_replay_gain REAL"); - _session.execute("ALTER TABLE track ADD release_replay_gain REAL"); - - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 22) - { - _session.execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''"); - - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 23) - { - // Better cover detection - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 24) - { - // User's AuthMode - _session.execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(/*User::defaultAuthMode*/0)) + ")"); - } - else if (version == 25) - { - // Better cover detection - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 26) - { - // Composer, mixer, etc. support - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 27) - { - // Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 28) - { - // Drop Auth mode - _session.execute(R"( -CREATE TABLE "user_backup" ( - "id" integer primary key autoincrement, - "version" integer not null, - "type" integer not null, - "login_name" text not null, - "password_salt" text not null, - "password_hash" text not null, - "last_login" text, - "subsonic_transcode_enable" boolean not null, - "subsonic_transcode_format" integer not null, - "subsonic_transcode_bitrate" integer not null, - "subsonic_artist_list_mode" integer not null, - "ui_theme" integer not null, - "cur_playing_track_pos" integer not null, - "repeat_all" boolean not null, - "radio" boolean not null -))"); - _session.execute("INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, subsonic_transcode_enable, subsonic_transcode_format, subsonic_transcode_bitrate, subsonic_artist_list_mode, ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); - _session.execute("DROP TABLE user"); - _session.execute("ALTER TABLE user_backup RENAME TO user"); - } - else if (version == 29) - { - _session.execute("ALTER TABLE tracklist_entry ADD date_time TEXT"); - _session.execute("ALTER TABLE user ADD listenbrainz_token TEXT"); - _session.execute("ALTER TABLE user ADD scrobbler INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultScrobbler)) + ")"); - _session.execute("ALTER TABLE track ADD recording_mbid TEXT"); - - _session.execute("DELETE from tracklist WHERE name = ?").bind("__played_tracks__"); - - // MBID changes - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 30) - { - // drop "year" and "original_year" (rescan needed to convert them into dates) - _session.execute(R"( -CREATE TABLE "track_backup" ( - "id" integer primary key autoincrement, - "version" integer not null, - "scan_version" integer not null, - "track_number" integer not null, - "disc_number" integer not null, - "name" text not null, - "duration" integer, - "date" integer text, - "original_date" integer text, - "file_path" text not null, - "file_last_write" text, - "file_added" text, - "has_cover" boolean not null, - "mbid" text not null, - "copyright" text not null, - "copyright_url" text not null, - "release_id" bigint, total_disc INTEGER NOT NULL DEFAULT(0), total_track INTEGER NOT NULL DEFAULT(0), track_replay_gain REAL, release_replay_gain REAL, disc_subtitle TEXT NOT NULL DEFAULT '', recording_mbid TEXT, - constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred -))"); - _session.execute("INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, name, duration, \"1900-01-01\", \"1900-01-01\", file_path, file_last_write, file_added, has_cover, mbid, copyright, copyright_url, release_id, total_disc, total_track, track_replay_gain, release_replay_gain, disc_subtitle, recording_mbid FROM track"); - _session.execute("DROP TABLE track"); - _session.execute("ALTER TABLE track_backup RENAME TO track"); - - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(*this).modify()->incScanVersion(); - } - else if (version == 31) - { - // new star system, using dedicated ObjectSets per scrobbler - _session.execute("DROP TABLE user_artist_starred"); - _session.execute("DROP TABLE user_release_starred"); - _session.execute("DROP TABLE user_track_starred"); - - _session.execute(R"( -CREATE TABLE IF NOT EXISTS "starred_artist" ( - "id" integer primary key autoincrement, - "version" integer not null, - "scrobbler" integer not null, - "date_time" text, - "artist_id" bigint, - "user_id" bigint, - constraint "fk_starred_artist_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred, - constraint "fk_starred_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred -))"); - - _session.execute(R"( -CREATE TABLE IF NOT EXISTS "starred_release" ( - "id" integer primary key autoincrement, - "version" integer not null, - "scrobbler" integer not null, - "date_time" text, - "release_id" bigint, - "user_id" bigint, - constraint "fk_starred_release_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred, - constraint "fk_starred_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred -))"); - - _session.execute(R"( -CREATE TABLE IF NOT EXISTS "starred_track" ( - "id" integer primary key autoincrement, - "version" integer not null, - "scrobbler" integer not null, - "date_time" text, - "track_id" bigint, - "user_id" bigint, - constraint "fk_starred_track_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, - constraint "fk_starred_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred -))"); - - _session.execute(R"( -CREATE TABLE IF NOT EXISTS "listen" ( - "id" integer primary key autoincrement, - "version" integer not null, - "date_time" text, - "scrobbler" integer not null, - "scrobbling_state" integer not null, - "track_id" bigint, - "user_id" bigint, - constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, - constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred -))"); - } - else - { - LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; - throw LmsException { LMS_DATABASE_VERSION > version ? outdatedMsg : "Server binary outdated, please upgrade it to handle this database"}; - } - - VersionInfo::get(*this).modify()->setVersion(++version); - } -} - Session::Session(Db& db) : _db {db} { @@ -496,8 +110,9 @@ void Session::prepareTables() { // Creation case - try { - _session.createTables(); + try + { + _session.createTables(); LMS_LOG(DB, INFO) << "Tables created"; } @@ -506,7 +121,7 @@ Session::prepareTables() LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what(); } - doDatabaseMigrationIfNeeded(); + Migration::doDbMigration(*this); // Indexes { diff --git a/src/libs/services/database/include/services/database/Db.hpp b/src/libs/services/database/include/services/database/Db.hpp index 5effb856..0fea5b24 100644 --- a/src/libs/services/database/include/services/database/Db.hpp +++ b/src/libs/services/database/include/services/database/Db.hpp @@ -42,6 +42,8 @@ class Db Session& getTLSSession(); + void executeSql(const std::string& sql); + private: friend class Session; @@ -66,28 +68,6 @@ class Db std::unique_ptr _connection; }; - class ScopedNoForeignKeys - { - public: - ScopedNoForeignKeys(Db& db) : _db {db} - { - _db.executeSql("PRAGMA foreign_keys=OFF"); - } - ~ScopedNoForeignKeys() - { - _db.executeSql("PRAGMA foreign_keys=ON"); - } - - ScopedNoForeignKeys(const ScopedNoForeignKeys&) = delete; - ScopedNoForeignKeys(ScopedNoForeignKeys&&) = delete; - ScopedNoForeignKeys& operator=(const ScopedNoForeignKeys&) = delete; - ScopedNoForeignKeys& operator=(ScopedNoForeignKeys&&) = delete; - - private: - Db& _db; - }; - - void executeSql(const std::string& sql); RecursiveSharedMutex _sharedMutex; std::unique_ptr _connectionPool; diff --git a/src/libs/services/database/include/services/database/Session.hpp b/src/libs/services/database/include/services/database/Session.hpp index 7871a2e8..1aab2d66 100644 --- a/src/libs/services/database/include/services/database/Session.hpp +++ b/src/libs/services/database/include/services/database/Session.hpp @@ -19,7 +19,6 @@ #pragma once -#include #include #include @@ -72,10 +71,9 @@ namespace Database void prepareTables(); // need to run only once at startup Wt::Dbo::Session& getDboSession() { return _session; } + Db& getDb() { return _db; } private: - void doDatabaseMigrationIfNeeded(); - Db& _db; Wt::Dbo::Session _session; }; From 9b8e2737e47f079a2b69bc1f1a65e5bbf05fb460 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 23 Jan 2022 20:19:27 +0100 Subject: [PATCH 35/49] Added starred entries migration --- src/libs/services/database/impl/Migration.cpp | 584 +++++++++++------- 1 file changed, 376 insertions(+), 208 deletions(-) diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index e179bfa0..3588b379 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -73,60 +73,45 @@ namespace Database::Migration Db& _db; }; + static void - doDbMigration(Session& session) + migrateFromV5(Session& session) { - static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"}; + session.getDboSession().execute("DELETE FROM auth_token"); // format has changed + } - ScopedNoForeignKeys noPragmaKeys {session.getDb()}; + static + void + migrateFromV6(Session& session) + { + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } - while (1) - { - auto uniqueTransaction {session.createUniqueTransaction()}; - Version version; - try - { - version = VersionInfo::getOrCreate(session)->getVersion(); - LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION; - if (version == LMS_DATABASE_VERSION) - { - LMS_LOG(DB, DEBUG) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!"; - return; - } - } - catch (std::exception& e) - { - LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what(); - throw LmsException {outdatedMsg}; - } + static + void + migrateFromV7(Session& session) + { + session.getDboSession().execute("DROP TABLE similarity_settings"); + session.getDboSession().execute("DROP TABLE similarity_settings_feature"); + session.getDboSession().execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(ScanSettings::RecommendationEngineType::Clusters)) + ")"); + } - LMS_LOG(DB, INFO) << "Migrating database from version " << version << "..."; + static + void + migrateFromV8(Session& session) + { + // Better cover handling, need to rescan the whole files + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } - if (version == 5) - { - session.getDboSession().execute("DELETE FROM auth_token"); // format has changed - } - else if (version == 6) - { - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 7) - { - session.getDboSession().execute("DROP TABLE similarity_settings"); - session.getDboSession().execute("DROP TABLE similarity_settings_feature"); - session.getDboSession().execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(ScanSettings::RecommendationEngineType::Clusters)) + ")"); - } - else if (version == 8) - { - // Better cover handling, need to rescan the whole files - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 9) - { - session.getDboSession().execute(R"( + static + void + migrateFromV9(Session& session) + { + session.getDboSession().execute(R"( CREATE TABLE IF NOT EXISTS "track_bookmark" ( "id" integer primary key autoincrement, "version" integer not null, @@ -137,78 +122,109 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" ( constraint "fk_track_bookmark_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, constraint "fk_track_bookmark_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred );)"); - } - else if (version == 10) - { - ScanSettings::get(session).modify()->addAudioFileExtension(".m4b"); - ScanSettings::get(session).modify()->addAudioFileExtension(".alac"); - } - else if (version == 11) - { - // Sanitize bad MBID, need to rescan the whole files - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 12) - { - // Artist and release that have a badly parsed name but a MBID had no chance to updat the name - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 13) - { - // Always store UUID in lower case + better WMA parsing - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 14) - { - // SortName now set from metadata - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 15) - { - session.getDboSession().execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultUITheme)) + ")"); - } - else if (version == 16) - { - session.getDboSession().execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)"); - session.getDboSession().execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)"); + } - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 17) - { - // Drop colums total_disc/total_track from release - session.getDboSession().execute(R"( + static + void + migrateFromV10(Session& session) + { + ScanSettings::get(session).modify()->addAudioFileExtension(".m4b"); + ScanSettings::get(session).modify()->addAudioFileExtension(".alac"); + } + + static + void + migrateFromV11(Session& session) + { + // Sanitize bad MBID, need to rescan the whole files + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV12(Session& session) + { + // Artist and release that have a badly parsed name but a MBID had no chance to updat the name + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV13(Session& session) + { + // Always store UUID in lower case + better WMA parsing + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV14(Session& session) + { + // SortName now set from metadata + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV15(Session& session) + { + session.getDboSession().execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultUITheme)) + ")"); + } + + static + void + migrateFromV16(Session& session) + { + session.getDboSession().execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)"); + session.getDboSession().execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV17(Session& session) + { + // Drop colums total_disc/total_track from release + session.getDboSession().execute(R"( CREATE TABLE "release_backup" ( "id" integer primary key autoincrement, "version" integer not null, "name" text not null, "mbid" text not null ))"); - session.getDboSession().execute("INSERT INTO release_backup SELECT id,version,name,mbid FROM release"); - session.getDboSession().execute("DROP TABLE release"); - session.getDboSession().execute("ALTER TABLE release_backup RENAME TO release"); + session.getDboSession().execute("INSERT INTO release_backup SELECT id,version,name,mbid FROM release"); + session.getDboSession().execute("DROP TABLE release"); + session.getDboSession().execute("ALTER TABLE release_backup RENAME TO release"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 18) - { - session.getDboSession().execute(R"( + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + + static + void + migrateFromV18(Session& session) + { + session.getDboSession().execute(R"( CREATE TABLE IF NOT EXISTS "subsonic_settings" ( "id" integer primary key autoincrement, "version" integer not null, "api_enabled" boolean not null, "artist_list_mode" integer not null ))"); - } - else if (version == 19) - { - session.getDboSession().execute(R"( + } + + static + void + migrateFromV19(Session& session) + { + session.getDboSession().execute(R"( CREATE TABLE "user_backup" ( "id" integer primary key autoincrement, "version" integer not null, @@ -226,67 +242,96 @@ CREATE TABLE "user_backup" ( "repeat_all" boolean not null, "radio" boolean not null ))"); - session.getDboSession().execute(std::string {"INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, "} - + (User::defaultSubsonicTranscodeEnable ? "1" : "0") - + ", " + std::to_string(static_cast(User::defaultSubsonicTranscodeFormat)) - + ", " + std::to_string(User::defaultSubsonicTranscodeBitrate) - + ", " + std::to_string(static_cast(User::defaultSubsonicArtistListMode)) - + ", ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); - session.getDboSession().execute("DROP TABLE user"); - session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); - } - else if (version == 20) - { - session.getDboSession().execute("DROP TABLE subsonic_settings"); - } - else if (version == 21) - { - session.getDboSession().execute("ALTER TABLE track ADD track_replay_gain REAL"); - session.getDboSession().execute("ALTER TABLE track ADD release_replay_gain REAL"); + session.getDboSession().execute(std::string {"INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, "} + + (User::defaultSubsonicTranscodeEnable ? "1" : "0") + + ", " + std::to_string(static_cast(User::defaultSubsonicTranscodeFormat)) + + ", " + std::to_string(User::defaultSubsonicTranscodeBitrate) + + ", " + std::to_string(static_cast(User::defaultSubsonicArtistListMode)) + + ", ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); + session.getDboSession().execute("DROP TABLE user"); + session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); + } - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 22) - { - session.getDboSession().execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''"); + static + void + migrateFromV20(Session& session) + { + session.getDboSession().execute("DROP TABLE subsonic_settings"); + } - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 23) - { - // Better cover detection - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 24) - { - // User's AuthMode - session.getDboSession().execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(/*User::defaultAuthMode*/0)) + ")"); - } - else if (version == 25) - { - // Better cover detection - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 26) - { - // Composer, mixer, etc. support - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 27) - { - // Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 28) - { - // Drop Auth mode - session.getDboSession().execute(R"( + static + void + migrateFromV21(Session& session) + { + session.getDboSession().execute("ALTER TABLE track ADD track_replay_gain REAL"); + session.getDboSession().execute("ALTER TABLE track ADD release_replay_gain REAL"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + + static + void + migrateFromV22(Session& session) + { + session.getDboSession().execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''"); + + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + + static + void + migrateFromV23(Session& session) + { + // Better cover detection + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV24(Session& session) + { + // User's AuthMode + session.getDboSession().execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(/*User::defaultAuthMode*/0)) + ")"); + } + + static + void + migrateFromV25(Session& session) + { + // Better cover detection + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV26(Session& session) + { + // Composer, mixer, etc. support + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV27(Session& session) + { + // Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV28(Session& session) + { + // Drop Auth mode + session.getDboSession().execute(R"( CREATE TABLE "user_backup" ( "id" integer primary key autoincrement, "version" integer not null, @@ -304,27 +349,33 @@ CREATE TABLE "user_backup" ( "repeat_all" boolean not null, "radio" boolean not null ))"); - session.getDboSession().execute("INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, subsonic_transcode_enable, subsonic_transcode_format, subsonic_transcode_bitrate, subsonic_artist_list_mode, ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); - session.getDboSession().execute("DROP TABLE user"); - session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); - } - else if (version == 29) - { - session.getDboSession().execute("ALTER TABLE tracklist_entry ADD date_time TEXT"); - session.getDboSession().execute("ALTER TABLE user ADD listenbrainz_token TEXT"); - session.getDboSession().execute("ALTER TABLE user ADD scrobbler INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultScrobbler)) + ")"); - session.getDboSession().execute("ALTER TABLE track ADD recording_mbid TEXT"); + session.getDboSession().execute("INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, subsonic_transcode_enable, subsonic_transcode_format, subsonic_transcode_bitrate, subsonic_artist_list_mode, ui_theme, cur_playing_track_pos, repeat_all, radio FROM user"); + session.getDboSession().execute("DROP TABLE user"); + session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); + } - session.getDboSession().execute("DELETE from tracklist WHERE name = ?").bind("__played_tracks__"); + static + void + migrateFromV29(Session& session) + { + session.getDboSession().execute("ALTER TABLE tracklist_entry ADD date_time TEXT"); + session.getDboSession().execute("ALTER TABLE user ADD listenbrainz_token TEXT"); + session.getDboSession().execute("ALTER TABLE user ADD scrobbler INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast(User::defaultScrobbler)) + ")"); + session.getDboSession().execute("ALTER TABLE track ADD recording_mbid TEXT"); - // MBID changes - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 30) - { - // drop "year" and "original_year" (rescan needed to convert them into dates) - session.getDboSession().execute(R"( + session.getDboSession().execute("DELETE from tracklist WHERE name = ?").bind("__played_tracks__"); + + // MBID changes + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } + + static + void + migrateFromV30(Session& session) + { + // drop "year" and "original_year" (rescan needed to convert them into dates) + session.getDboSession().execute(R"( CREATE TABLE "track_backup" ( "id" integer primary key autoincrement, "version" integer not null, @@ -345,22 +396,21 @@ CREATE TABLE "track_backup" ( "release_id" bigint, total_disc INTEGER NOT NULL DEFAULT(0), total_track INTEGER NOT NULL DEFAULT(0), track_replay_gain REAL, release_replay_gain REAL, disc_subtitle TEXT NOT NULL DEFAULT '', recording_mbid TEXT, constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred ))"); - session.getDboSession().execute("INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, name, duration, \"1900-01-01\", \"1900-01-01\", file_path, file_last_write, file_added, has_cover, mbid, copyright, copyright_url, release_id, total_disc, total_track, track_replay_gain, release_replay_gain, disc_subtitle, recording_mbid FROM track"); - session.getDboSession().execute("DROP TABLE track"); - session.getDboSession().execute("ALTER TABLE track_backup RENAME TO track"); + session.getDboSession().execute("INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, name, duration, \"1900-01-01\", \"1900-01-01\", file_path, file_last_write, file_added, has_cover, mbid, copyright, copyright_url, release_id, total_disc, total_track, track_replay_gain, release_replay_gain, disc_subtitle, recording_mbid FROM track"); + session.getDboSession().execute("DROP TABLE track"); + session.getDboSession().execute("ALTER TABLE track_backup RENAME TO track"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything - ScanSettings::get(session).modify()->incScanVersion(); - } - else if (version == 31) - { - // new star system, using dedicated ObjectSets per scrobbler - session.getDboSession().execute("DROP TABLE user_artist_starred"); - session.getDboSession().execute("DROP TABLE user_release_starred"); - session.getDboSession().execute("DROP TABLE user_track_starred"); + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(session).modify()->incScanVersion(); + } - session.getDboSession().execute(R"( -CREATE TABLE IF NOT EXISTS "starred_artist" ( + static + void + migrateFromV31(Session& session) + { + // new star system, using dedicated entries per scrobbler and date time + session.getDboSession().execute(R"( +CREATE TABLE "starred_artist" ( "id" integer primary key autoincrement, "version" integer not null, "scrobbler" integer not null, @@ -371,8 +421,8 @@ CREATE TABLE IF NOT EXISTS "starred_artist" ( constraint "fk_starred_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred ))"); - session.getDboSession().execute(R"( -CREATE TABLE IF NOT EXISTS "starred_release" ( + session.getDboSession().execute(R"( +CREATE TABLE "starred_release" ( "id" integer primary key autoincrement, "version" integer not null, "scrobbler" integer not null, @@ -383,8 +433,8 @@ CREATE TABLE IF NOT EXISTS "starred_release" ( constraint "fk_starred_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred ))"); - session.getDboSession().execute(R"( -CREATE TABLE IF NOT EXISTS "starred_track" ( + session.getDboSession().execute(R"( +CREATE TABLE "starred_track" ( "id" integer primary key autoincrement, "version" integer not null, "scrobbler" integer not null, @@ -395,9 +445,57 @@ CREATE TABLE IF NOT EXISTS "starred_track" ( constraint "fk_starred_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred ))"); - // new listen system, no longer using tracklists - session.getDboSession().execute(R"( -CREATE TABLE IF NOT EXISTS "listen" ( + // Can't migrate using class mapping as mapping may evolve in the future + + // use time_t to avoid rounding issues later + const Wt::WDateTime now {Wt::WDateTime::fromTime_t(Wt::WDateTime::currentDateTime().toTime_t())}; + + std::map userScrobblers; + auto getScrobbler {[&](IdType::ValueType userId) + { + auto itScrobbler {userScrobblers.find(userId)}; + if (itScrobbler != std::cend(userScrobblers)) + return itScrobbler->second; + + auto query {session.getDboSession().query("SELECT scrobbler FROM user WHERE id = ?").bind(userId)}; + auto [itInserted, inserted] {userScrobblers.emplace(userId, query.resultValue())}; + assert(inserted); + return itInserted->second; + }}; + + auto migrateStarEntries = [&session, &getScrobbler, &now](const std::string& colName, const std::string& oldTableName, const std::string& newTableName) + { + using UserIdObjectId = std::tuple; + + std::vector starredEntries; + auto query {session.getDboSession().query("SELECT user_id, " + colName + " from " + oldTableName)}; + auto results {query.resultList()}; + starredEntries.reserve(results.size()); + for (const auto& entry : results) + starredEntries.push_back(entry); + + LMS_LOG(DB, INFO) << "Found " << starredEntries.size() << " " << colName << " to migrate"; + + for (const auto [userId, entryId] : starredEntries) + { + session.getDboSession().execute("INSERT INTO " + newTableName + " ('version', 'scrobbler', 'date_time', '" + colName + "', 'user_id') VALUES (?, ?, ?, ?, ?)") + .bind(0) + .bind(getScrobbler(userId)) + .bind(now.toString().toUTF8()) + .bind(entryId) + .bind(userId); + } + + session.getDboSession().execute("DROP TABLE " + oldTableName); + }; + + migrateStarEntries("artist_id","user_artist_starred", "starred_artist"); + migrateStarEntries("release_id","user_release_starred", "starred_release"); + migrateStarEntries("track_id","user_track_starred", "starred_track"); + + // new listen system, no longer using tracklists + session.getDboSession().execute(R"( +CREATE TABLE "listen" ( "id" integer primary key autoincrement, "version" integer not null, "date_time" text, @@ -408,12 +506,82 @@ CREATE TABLE IF NOT EXISTS "listen" ( constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred ))"); - } - else + } + + void + doDbMigration(Session& session) + { + static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"}; + + ScopedNoForeignKeys noPragmaKeys {session.getDb()}; + + using MigrationFunction = std::function; + + const std::map migrationFunctions + { + {5, migrateFromV5}, + {6, migrateFromV6}, + {7, migrateFromV7}, + {8, migrateFromV8}, + {9, migrateFromV9}, + {10, migrateFromV10}, + {11, migrateFromV11}, + {12, migrateFromV12}, + {13, migrateFromV13}, + {14, migrateFromV14}, + {15, migrateFromV15}, + {16, migrateFromV16}, + {17, migrateFromV17}, + {18, migrateFromV18}, + {19, migrateFromV19}, + {20, migrateFromV20}, + {21, migrateFromV21}, + {22, migrateFromV22}, + {23, migrateFromV23}, + {24, migrateFromV24}, + {25, migrateFromV25}, + {26, migrateFromV26}, + {27, migrateFromV27}, + {28, migrateFromV28}, + {29, migrateFromV29}, + {30, migrateFromV30}, + {31, migrateFromV31}, + }; + + while (1) + { + auto uniqueTransaction {session.createUniqueTransaction()}; + + Version version; + try { - LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; - throw LmsException { LMS_DATABASE_VERSION > version ? outdatedMsg : "Server binary outdated, please upgrade it to handle this database"}; + version = VersionInfo::getOrCreate(session)->getVersion(); + 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(); + throw LmsException {outdatedMsg}; + } + + if (version == LMS_DATABASE_VERSION) + { + LMS_LOG(DB, DEBUG) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!"; + return; + } + else if (version > LMS_DATABASE_VERSION) + { + throw LmsException {"Server binary outdated, please upgrade it to handle this database"}; + } + + if (version < migrationFunctions.begin()->first) + throw LmsException {outdatedMsg}; + + LMS_LOG(DB, INFO) << "Migrating database from version " << version << "..."; + + auto itMigrationFunc {migrationFunctions.find(version)}; + assert(itMigrationFunc != std::cend(migrationFunctions)); + itMigrationFunc->second(session); VersionInfo::get(session).modify()->setVersion(++version); } From 64b2495bac85fddf8fde50eb8d02d6f798ea4e0a Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 23 Jan 2022 20:59:37 +0100 Subject: [PATCH 36/49] Added listens migration --- src/libs/services/database/impl/Migration.cpp | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index 3588b379..0c2fdcec 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -19,6 +19,8 @@ #include "Migration.hpp" +#include + #include "services/database/Db.hpp" #include "services/database/ScanSettings.hpp" #include "services/database/Session.hpp" @@ -463,20 +465,17 @@ CREATE TABLE "starred_track" ( return itInserted->second; }}; - auto migrateStarEntries = [&session, &getScrobbler, &now](const std::string& colName, const std::string& oldTableName, const std::string& newTableName) + auto migrateStarEntries {[&session, &getScrobbler, &now](const std::string& colName, const std::string& oldTableName, const std::string& newTableName) { - using UserIdObjectId = std::tuple; + using UserIdObjectId = std::tuple; std::vector starredEntries; auto query {session.getDboSession().query("SELECT user_id, " + colName + " from " + oldTableName)}; auto results {query.resultList()}; - starredEntries.reserve(results.size()); - for (const auto& entry : results) - starredEntries.push_back(entry); - LMS_LOG(DB, INFO) << "Found " << starredEntries.size() << " " << colName << " to migrate"; + LMS_LOG(DB, INFO) << "Found " << results.size() << " " << colName << " to migrate"; - for (const auto [userId, entryId] : starredEntries) + for (const auto& [userId, entryId] : results) { session.getDboSession().execute("INSERT INTO " + newTableName + " ('version', 'scrobbler', 'date_time', '" + colName + "', 'user_id') VALUES (?, ?, ?, ?, ?)") .bind(0) @@ -487,7 +486,7 @@ CREATE TABLE "starred_track" ( } session.getDboSession().execute("DROP TABLE " + oldTableName); - }; + }}; migrateStarEntries("artist_id","user_artist_starred", "starred_artist"); migrateStarEntries("release_id","user_release_starred", "starred_release"); @@ -506,6 +505,34 @@ CREATE TABLE "listen" ( constraint "fk_listen_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred, constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred ))"); + + auto migrateListens {[&session, &getScrobbler](const std::string& trackListName, Scrobbler scrobbler) + { + using UserIdObjectId = std::tuple; + + std::vector listens; + auto query {session.getDboSession().query("SELECT t_l.user_id, t_l_e.track_id, t_l_e.date_time FROM tracklist t_l") + .join("tracklist_entry t_l_e ON t_l_e.tracklist_id = t_l.id") + .where("t_l.name = ?").bind(trackListName)}; + auto results {query.resultList()}; + listens.reserve(results.size()); + + LMS_LOG(DB, INFO) << "Found " << results.size() << " listens in " << trackListName; + + for (const auto& [userId, trackId, dateTime] : results) + { + session.getDboSession().execute("INSERT INTO listen ('version', 'date_time', 'scrobbler', 'scrobbling_state', 'track_id', 'user_id') VALUES (?, ?, ?, ?, ?, ?)") + .bind(0) + .bind(dateTime.toString().toUTF8()) + .bind(getScrobbler(userId)) + .bind(ScrobblingState::Synchronized) // consider sync is done to avoid duplicate submissions + .bind(trackId) + .bind(userId); + } + }}; + + migrateListens("__scrobbler_internal_history__", Scrobbler::Internal); + migrateListens("__scrobbler_listenbrainz_history__", Scrobbler::ListenBrainz); } void From fba20fcb7d8450ceeb078e00231ef29ecceb3dc9 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 23 Jan 2022 21:02:07 +0100 Subject: [PATCH 37/49] Fixed correct scrobbler for listens migration --- src/libs/services/database/impl/Migration.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index 0c2fdcec..73c9e2c0 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -506,7 +506,7 @@ CREATE TABLE "listen" ( constraint "fk_listen_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred ))"); - auto migrateListens {[&session, &getScrobbler](const std::string& trackListName, Scrobbler scrobbler) + auto migrateListens {[&session](const std::string& trackListName, Scrobbler scrobbler) { using UserIdObjectId = std::tuple; @@ -524,7 +524,7 @@ CREATE TABLE "listen" ( session.getDboSession().execute("INSERT INTO listen ('version', 'date_time', 'scrobbler', 'scrobbling_state', 'track_id', 'user_id') VALUES (?, ?, ?, ?, ?, ?)") .bind(0) .bind(dateTime.toString().toUTF8()) - .bind(getScrobbler(userId)) + .bind(scrobbler) .bind(ScrobblingState::Synchronized) // consider sync is done to avoid duplicate submissions .bind(trackId) .bind(userId); From 86b16266b616da7a1f1c5a1184cf802204b0e23a Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 24 Jan 2022 21:28:28 +0100 Subject: [PATCH 38/49] Listenbrainz: save listen even if send failed --- .../include/services/database/Listen.hpp | 9 +- .../include/services/database/ListenId.hpp | 25 +++ .../listenbrainz/ListenBrainzScrobbler.cpp | 122 +----------- .../impl/listenbrainz/ListensSynchronizer.cpp | 175 ++++++++++++++++-- .../impl/listenbrainz/ListensSynchronizer.hpp | 7 +- 5 files changed, 196 insertions(+), 142 deletions(-) create mode 100644 src/libs/services/database/include/services/database/ListenId.hpp diff --git a/src/libs/services/database/include/services/database/Listen.hpp b/src/libs/services/database/include/services/database/Listen.hpp index 29d3394d..80164cdc 100644 --- a/src/libs/services/database/include/services/database/Listen.hpp +++ b/src/libs/services/database/include/services/database/Listen.hpp @@ -24,15 +24,13 @@ #include "services/database/ArtistId.hpp" #include "services/database/ClusterId.hpp" -#include "services/database/IdType.hpp" +#include "services/database/ListenId.hpp" #include "services/database/Object.hpp" #include "services/database/ReleaseId.hpp" #include "services/database/TrackId.hpp" #include "services/database/Types.hpp" #include "services/database/UserId.hpp" -LMS_DECLARE_IDTYPE(ListenId) - namespace Database { @@ -90,6 +88,11 @@ class Listen : public Object const std::vector& clusterIds, Range range = {}); + ScrobblingState getScrobblingState() const { return _scrobblingState; } + ObjectPtr getUser() const { return _user; } + + void setScrobblingState(ScrobblingState state) { _scrobblingState = state; } + template void persist(Action& a) { diff --git a/src/libs/services/database/include/services/database/ListenId.hpp b/src/libs/services/database/include/services/database/ListenId.hpp new file mode 100644 index 00000000..e19019ee --- /dev/null +++ b/src/libs/services/database/include/services/database/ListenId.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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 "services/database/IdType.hpp" + +LMS_DECLARE_IDTYPE(ListenId) + diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index 81c95b88..b0365e95 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -19,18 +19,9 @@ #include "ListenBrainzScrobbler.hpp" -#include -#include -#include -#include - -#include "services/database/Artist.hpp" #include "services/database/Db.hpp" -#include "services/database/Release.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "services/database/TrackList.hpp" -#include "services/database/User.hpp" #include "utils/IConfig.hpp" #include "utils/http/IClient.hpp" #include "utils/Logger.hpp" @@ -56,86 +47,6 @@ namespace return res; } - - std::optional - listenToJsonPayload(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint) - { - auto transaction {session.createSharedTransaction()}; - - const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; - if (!track) - return std::nullopt; - - auto artists {track->getArtists({Database::TrackArtistLinkType::Artist})}; - if (artists.empty()) - artists = track->getArtists({Database::TrackArtistLinkType::ReleaseArtist}); - - if (artists.empty()) - { - LOG(DEBUG) << "Track cannot be scrobbled since it does not have any artist"; - return std::nullopt; - } - - Wt::Json::Object additionalInfo; - additionalInfo["listening_from"] = "LMS"; - if (track->getRelease()) - { - if (auto MBID {track->getRelease()->getMBID()}) - additionalInfo["release_mbid"] = Wt::Json::Value {std::string {MBID->getAsString()}}; - } - - { - Wt::Json::Array artistMBIDs; - for (const Database::Artist::pointer& artist : artists) - { - if (auto MBID {artist->getMBID()}) - artistMBIDs.push_back(Wt::Json::Value {std::string {MBID->getAsString()}}); - } - - if (!artistMBIDs.empty()) - additionalInfo["artist_mbids"] = std::move(artistMBIDs); - } - - if (auto MBID {track->getTrackMBID()}) - additionalInfo["track_mbid"] = Wt::Json::Value {std::string {MBID->getAsString()}}; - - if (auto MBID {track->getRecordingMBID()}) - additionalInfo["recording_mbid"] = Wt::Json::Value {std::string {MBID->getAsString()}}; - - if (const std::optional trackNumber {track->getTrackNumber()}) - additionalInfo["tracknumber"] = Wt::Json::Value {static_cast(*trackNumber)}; - - Wt::Json::Object trackMetadata; - trackMetadata["additional_info"] = std::move(additionalInfo); - trackMetadata["artist_name"] = Wt::Json::Value {artists.front()->getName()}; - trackMetadata["track_name"] = Wt::Json::Value {track->getName()}; - if (track->getRelease()) - trackMetadata["release_name"] = Wt::Json::Value {track->getRelease()->getName()}; - - Wt::Json::Object payload; - payload["track_metadata"] = std::move(trackMetadata); - if (timePoint.isValid()) - payload["listened_at"] = Wt::Json::Value {static_cast(timePoint.toTime_t())}; - - return payload; - } - - std::string - listenToJsonString(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint, std::string_view listenType) - { - std::string res; - - std::optional payload {listenToJsonPayload(session, listen, timePoint)}; - if (!payload) - return res; - - Wt::Json::Object root; - root["listen_type"] = Wt::Json::Value {std::string {listenType}}; - root["payload"] = Wt::Json::Array {std::move(*payload)}; - - res = Wt::Json::serialize(root); - return res; - } } namespace Scrobbling::ListenBrainz @@ -183,38 +94,7 @@ namespace Scrobbling::ListenBrainz void Scrobbler::enqueListen(const Listen& listen, const Wt::WDateTime& timePoint) { - Http::ClientPOSTRequestParameters request; - request.relativeUrl = "/1/submit-listens"; - - if (timePoint.isValid()) - { - request.priority = Http::ClientRequestParameters::Priority::Normal; - request.onSuccessFunc = [=](std::string_view) - { - _listensSynchronizer.saveListen(TimedListen {listen, timePoint}); - }; - } - else - { - // We want "listen now" to appear as soon as possible - request.priority = Http::ClientRequestParameters::Priority::High; - } - - std::string bodyText {listenToJsonString(_db.getTLSSession(), listen, timePoint, timePoint.isValid() ? "single" : "playing_now")}; - if (bodyText.empty()) - { - LOG(DEBUG) << "Cannot convert listen to json: skipping"; - return; - } - - const std::optional listenBrainzToken {Utils::getListenBrainzToken(_db.getTLSSession(), listen.userId)}; - if (!listenBrainzToken) - return; - - request.message.addBodyText(bodyText); - request.message.addHeader("Authorization", "Token " + std::string {listenBrainzToken->getAsString()}); - request.message.addHeader("Content-Type", "application/json"); - _client->sendPOSTRequest(std::move(request)); + _listensSynchronizer.enqueListen(listen, timePoint); } } // namespace Scrobbling::ListenBrainz diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index f3b603b8..68c8d8e4 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -47,6 +47,86 @@ namespace { using namespace Scrobbling::ListenBrainz; + std::optional + listenToJsonPayload(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint) + { + auto transaction {session.createSharedTransaction()}; + + const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; + if (!track) + return std::nullopt; + + auto artists {track->getArtists({Database::TrackArtistLinkType::Artist})}; + if (artists.empty()) + artists = track->getArtists({Database::TrackArtistLinkType::ReleaseArtist}); + + if (artists.empty()) + { + LOG(DEBUG) << "Track cannot be scrobbled since it does not have any artist"; + return std::nullopt; + } + + Wt::Json::Object additionalInfo; + additionalInfo["listening_from"] = "LMS"; + if (track->getRelease()) + { + if (auto MBID {track->getRelease()->getMBID()}) + additionalInfo["release_mbid"] = Wt::Json::Value {std::string {MBID->getAsString()}}; + } + + { + Wt::Json::Array artistMBIDs; + for (const Database::Artist::pointer& artist : artists) + { + if (auto MBID {artist->getMBID()}) + artistMBIDs.push_back(Wt::Json::Value {std::string {MBID->getAsString()}}); + } + + if (!artistMBIDs.empty()) + additionalInfo["artist_mbids"] = std::move(artistMBIDs); + } + + if (auto MBID {track->getTrackMBID()}) + additionalInfo["track_mbid"] = Wt::Json::Value {std::string {MBID->getAsString()}}; + + if (auto MBID {track->getRecordingMBID()}) + additionalInfo["recording_mbid"] = Wt::Json::Value {std::string {MBID->getAsString()}}; + + if (const std::optional trackNumber {track->getTrackNumber()}) + additionalInfo["tracknumber"] = Wt::Json::Value {static_cast(*trackNumber)}; + + Wt::Json::Object trackMetadata; + trackMetadata["additional_info"] = std::move(additionalInfo); + trackMetadata["artist_name"] = Wt::Json::Value {artists.front()->getName()}; + trackMetadata["track_name"] = Wt::Json::Value {track->getName()}; + if (track->getRelease()) + trackMetadata["release_name"] = Wt::Json::Value {track->getRelease()->getName()}; + + Wt::Json::Object payload; + payload["track_metadata"] = std::move(trackMetadata); + if (timePoint.isValid()) + payload["listened_at"] = Wt::Json::Value {static_cast(timePoint.toTime_t())}; + + return payload; + } + + std::string + listenToJsonString(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint, std::string_view listenType) + { + std::string res; + + std::optional payload {listenToJsonPayload(session, listen, timePoint)}; + if (!payload) + return res; + + Wt::Json::Object root; + root["listen_type"] = Wt::Json::Value {std::string {listenType}}; + root["payload"] = Wt::Json::Array {std::move(*payload)}; + + res = Wt::Json::serialize(root); + return res; + } + std::string parseValidateToken(std::string_view msgBody) { @@ -226,30 +306,91 @@ namespace Scrobbling::ListenBrainz } void + ListensSynchronizer::enqueListen(const Listen& listen, const Wt::WDateTime& timePoint) + { + Http::ClientPOSTRequestParameters request; + request.relativeUrl = "/1/submit-listens"; + + if (timePoint.isValid()) + { + // We want the listen to be sent again later in case of failure, so we just save it as pending send + const Database::ListenId listenId {saveListen(TimedListen {listen, timePoint})}; + if (!listenId.isValid()) + return; + + request.priority = Http::ClientRequestParameters::Priority::Normal; + request.onSuccessFunc = [=](std::string_view) + { + onListenSent(listenId); + }; + // on failure, this listen will be sent during the next sync + } + else + { + // We want "listen now" to appear as soon as possible + request.priority = Http::ClientRequestParameters::Priority::High; + // don't retry on failure + } + + std::string bodyText {listenToJsonString(_db.getTLSSession(), listen, timePoint, timePoint.isValid() ? "single" : "playing_now")}; + if (bodyText.empty()) + { + LOG(DEBUG) << "Cannot convert listen to json: skipping"; + return; + } + + const std::optional listenBrainzToken {Utils::getListenBrainzToken(_db.getTLSSession(), listen.userId)}; + if (!listenBrainzToken) + return; + + request.message.addBodyText(bodyText); + request.message.addHeader("Authorization", "Token " + std::string {listenBrainzToken->getAsString()}); + request.message.addHeader("Content-Type", "application/json"); + _client.sendPOSTRequest(std::move(request)); + + } + + Database::ListenId ListensSynchronizer::saveListen(const TimedListen& listen) + { + using namespace Database; + + Session& session {_db.getTLSSession()}; + auto transaction {session.createUniqueTransaction()}; + + if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::ListenBrainz, listen.listenedAt)) + return {}; + + const User::pointer user {User::find(session, listen.userId)}; + if (!user) + return {}; + + const Track::pointer track {Track::find(session, listen.trackId)}; + if (!track) + return {}; + + const auto dbListen {Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt)}; + assert(dbListen->getScrobblingState() == Database::ScrobblingState::PendingAdd); + + return dbListen->getId(); + } + + void + ListensSynchronizer::onListenSent(Database::ListenId listenId) { _strand.dispatch([=] { Database::Session& session {_db.getTLSSession()}; - auto transaction {session.createUniqueTransaction()}; - if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::ListenBrainz, listen.listenedAt)) - return; + if (Database::Listen::pointer listen {Database::Listen::find(session, listenId)}) + { + listen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized); - const Database::User::pointer user {Database::User::find(session, listen.userId)}; - if (!user) - return; - - const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; - if (!track) - return; - - Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt); - - UserContext& context {getUserContext(listen.userId)}; - if (context.listenCount) - (*context.listenCount)++; + UserContext& context {getUserContext(listen->getUser()->getId())}; + if (context.listenCount) + (*context.listenCount)++; + } }); } @@ -303,7 +444,7 @@ namespace Scrobbling::ListenBrainz void ListensSynchronizer::startGetListens() { - LOG(DEBUG) << "GetListens started!!!"; + LOG(DEBUG) << "GetListens started!"; assert(!isFetching()); diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index 0b95365d..258942a0 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -26,6 +26,8 @@ #include #include "services/database/Types.hpp" +#include "services/database/ListenId.hpp" +#include "services/database/UserId.hpp" #include "services/scrobbling/Listen.hpp" namespace Database @@ -48,9 +50,12 @@ namespace Scrobbling::ListenBrainz public: ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, Http::IClient& client); - void saveListen(const TimedListen& listen); + void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); private: + Database::ListenId saveListen(const TimedListen& listen); + void onListenSent(Database::ListenId listenId); + struct UserContext { UserContext(Database::UserId id) : userId {id} {} From 97c2571a547e0e71f7650725b963ad514e832371 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 24 Jan 2022 21:30:25 +0100 Subject: [PATCH 39/49] Internal scrobling: set listen state to be synchronized (actually not used) --- .../services/scrobbling/impl/internal/InternalScrobbler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp index 3c2f17d4..2a7572ad 100644 --- a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp @@ -64,7 +64,8 @@ namespace Scrobbling if (!track) return; - Database::Listen::create(session, user, track, Database::Scrobbler::Internal, listen.listenedAt); + auto dbListen {Database::Listen::create(session, user, track, Database::Scrobbler::Internal, listen.listenedAt)}; + dbListen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized); } } // Scrobbling From 4177354afe26063d86b205f56a01aa66957ecc70 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 30 Jan 2022 14:18:22 +0100 Subject: [PATCH 40/49] Send pending listens that have not been successfully sent --- src/libs/services/database/impl/Listen.cpp | 21 +- src/libs/services/database/impl/Migration.cpp | 13 +- src/libs/services/database/impl/User.cpp | 7 +- .../include/services/database/Listen.hpp | 21 +- .../include/services/database/User.hpp | 11 +- .../services/database/test/DatabaseTest.cpp | 4 +- src/libs/services/database/test/Listen.cpp | 29 ++- .../scrobbling/impl/ScrobblingService.cpp | 18 +- .../listenbrainz/ListenBrainzScrobbler.cpp | 19 +- .../listenbrainz/ListenBrainzScrobbler.hpp | 3 - .../impl/listenbrainz/ListensSynchronizer.cpp | 190 +++++++++++------- .../impl/listenbrainz/ListensSynchronizer.hpp | 27 +-- .../scrobbling/impl/listenbrainz/Utils.cpp | 8 - .../scrobbling/impl/listenbrainz/Utils.hpp | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 2 +- src/libs/utils/impl/http/SendQueue.cpp | 6 +- src/libs/utils/include/utils/Logger.hpp | 3 +- src/lms/ui/admin/UsersView.cpp | 2 +- 18 files changed, 232 insertions(+), 154 deletions(-) diff --git a/src/libs/services/database/impl/Listen.cpp b/src/libs/services/database/impl/Listen.cpp index fde20d8c..c9a2cb45 100644 --- a/src/libs/services/database/impl/Listen.cpp +++ b/src/libs/services/database/impl/Listen.cpp @@ -155,17 +155,24 @@ namespace Database return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } - RangeResults - Listen::find(Session& session, UserId userId, Scrobbler scrobbler, Range range) + RangeResults + Listen::find(Session& session, const FindParameters& parameters) { session.checkSharedLocked(); - auto query {session.getDboSession().find() - .where("user_id = ?").bind(userId) - .where("scrobbler = ?").bind(scrobbler) - .orderBy("date_time")}; + auto query {session.getDboSession().query("SELECT id FROM listen") + .orderBy("date_time")}; - return execQuery(query, range); + if (parameters.user.isValid()) + query.where("user_id = ?").bind(parameters.user); + + if (parameters.scrobbler) + query.where("scrobbler = ?").bind(*parameters.scrobbler); + + if (parameters.scrobblingState) + query.where("scrobbling_state = ?").bind(*parameters.scrobblingState); + + return execQuery(query, parameters.range); } Listen::pointer diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index 73c9e2c0..64198a88 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -75,6 +75,13 @@ namespace Database::Migration Db& _db; }; + static + std::string + dateTimeToDbFormat(const Wt::WDateTime& dateTime) + { + return dateTime.toString("yyyy'-'MM'-'dd'T'hh':'mm':'ss'.000'", false).toUTF8(); + } + static void migrateFromV5(Session& session) @@ -450,7 +457,7 @@ CREATE TABLE "starred_track" ( // Can't migrate using class mapping as mapping may evolve in the future // use time_t to avoid rounding issues later - const Wt::WDateTime now {Wt::WDateTime::fromTime_t(Wt::WDateTime::currentDateTime().toTime_t())}; + const std::string now {dateTimeToDbFormat(Wt::WDateTime::fromTime_t(Wt::WDateTime::currentDateTime().toTime_t()))}; std::map userScrobblers; auto getScrobbler {[&](IdType::ValueType userId) @@ -480,7 +487,7 @@ CREATE TABLE "starred_track" ( session.getDboSession().execute("INSERT INTO " + newTableName + " ('version', 'scrobbler', 'date_time', '" + colName + "', 'user_id') VALUES (?, ?, ?, ?, ?)") .bind(0) .bind(getScrobbler(userId)) - .bind(now.toString().toUTF8()) + .bind(now) .bind(entryId) .bind(userId); } @@ -523,7 +530,7 @@ CREATE TABLE "listen" ( { session.getDboSession().execute("INSERT INTO listen ('version', 'date_time', 'scrobbler', 'scrobbling_state', 'track_id', 'user_id') VALUES (?, ?, ?, ?, ?, ?)") .bind(0) - .bind(dateTime.toString().toUTF8()) + .bind(dateTimeToDbFormat(dateTime)) .bind(scrobbler) .bind(ScrobblingState::Synchronized) // consider sync is done to avoid duplicate submissions .bind(trackId) diff --git a/src/libs/services/database/impl/User.cpp b/src/libs/services/database/impl/User.cpp index a8abea66..31728e06 100644 --- a/src/libs/services/database/impl/User.cpp +++ b/src/libs/services/database/impl/User.cpp @@ -44,13 +44,16 @@ User::getCount(Session& session) } RangeResults -User::find(Session& session, Range range) +User::find(Session& session, const FindParameters& params) { session.checkSharedLocked(); auto query {session.getDboSession().query("SELECT id FROM user")}; - return execQuery(query, range); + if (params.scrobbler) + query.where("scrobbler = ?").bind(*params.scrobbler); + + return execQuery(query, params.range); } User::pointer diff --git a/src/libs/services/database/include/services/database/Listen.hpp b/src/libs/services/database/include/services/database/Listen.hpp index 80164cdc..edbb218b 100644 --- a/src/libs/services/database/include/services/database/Listen.hpp +++ b/src/libs/services/database/include/services/database/Listen.hpp @@ -44,11 +44,24 @@ class Listen : public Object Listen() = default; Listen(ObjectPtr user, ObjectPtr track, Scrobbler scrobbler, const Wt::WDateTime& dateTime); + struct FindParameters + { + UserId user; + std::optional scrobbler; + std::optional scrobblingState; + Range range; + + FindParameters& setUser(UserId _user) { user = _user; return *this; } + FindParameters& setScrobbler(Scrobbler _scrobbler) { scrobbler = _scrobbler; return *this; } + FindParameters& setScrobblingState(ScrobblingState _scrobblingState) { scrobblingState = _scrobblingState; return *this; } + FindParameters& setRange(Range _range) {range = _range; return *this; } + }; + // Accessors static std::size_t getCount(Session& session); static pointer find(Session& session, ListenId id); - static RangeResults find(Session& session, UserId userId, Scrobbler scrobbler, Range = {}); static pointer find(Session& session, UserId userId, TrackId trackId, Scrobbler scrobbler, const Wt::WDateTime& dateTime); + static RangeResults find(Session& session, const FindParameters& parameters); // Create static pointer create(Session& session, ObjectPtr user, ObjectPtr track, Scrobbler scrobbler, const Wt::WDateTime& dateTime); @@ -88,8 +101,10 @@ class Listen : public Object const std::vector& clusterIds, Range range = {}); - ScrobblingState getScrobblingState() const { return _scrobblingState; } - ObjectPtr getUser() const { return _user; } + ScrobblingState getScrobblingState() const { return _scrobblingState; } + ObjectPtr getUser() const { return _user; } + ObjectPtr getTrack() const { return _track; } + const Wt::WDateTime& getDateTime() const { return _dateTime; } void setScrobblingState(ScrobblingState state) { _scrobblingState = state; } diff --git a/src/libs/services/database/include/services/database/User.hpp b/src/libs/services/database/include/services/database/User.hpp index e42a2181..4a446296 100644 --- a/src/libs/services/database/include/services/database/User.hpp +++ b/src/libs/services/database/include/services/database/User.hpp @@ -45,6 +45,15 @@ class User : public Object std::string hash; }; + struct FindParameters + { + std::optional scrobbler; + Range range; + + FindParameters& setScrobbler(Scrobbler _scrobbler) { scrobbler = _scrobbler; return *this; } + FindParameters& setRange(Range _range) {range = _range; return *this; } + }; + static inline const std::size_t MinNameLength {3}; static inline const std::size_t MaxNameLength {15}; static inline const bool defaultSubsonicTranscodeEnable {true}; @@ -63,7 +72,7 @@ class User : public Object static std::size_t getCount(Session& session); static pointer find(Session& session, UserId id); static pointer find(Session& session, std::string_view loginName); - static RangeResults find(Session& session, Range range); + static RangeResults find(Session& session, const FindParameters& params); static pointer findDemoUser(Session& session); // accessors diff --git a/src/libs/services/database/test/DatabaseTest.cpp b/src/libs/services/database/test/DatabaseTest.cpp index f106e06a..918d519a 100644 --- a/src/libs/services/database/test/DatabaseTest.cpp +++ b/src/libs/services/database/test/DatabaseTest.cpp @@ -89,7 +89,7 @@ TEST_F(DatabaseFixture, SingleUser) { { auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(User::find(session, Range {}).results.empty()); + EXPECT_TRUE(User::find(session, User::FindParameters {}).results.empty()); EXPECT_EQ(User::getCount(session), 0); } @@ -98,7 +98,7 @@ TEST_F(DatabaseFixture, SingleUser) { auto transaction {session.createSharedTransaction()}; - EXPECT_EQ(User::find(session, Range {}).results.size(), 1); + 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 1b5b12b9..d43f7ad0 100644 --- a/src/libs/services/database/test/Listen.cpp +++ b/src/libs/services/database/test/Listen.cpp @@ -66,17 +66,28 @@ TEST_F(DatabaseFixture, Listen_get) { auto transaction {session.createSharedTransaction()}; - auto listens {Listen::find(session, user->getId(), Scrobbler::ListenBrainz)}; + auto listens {Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobbler(Scrobbler::ListenBrainz))}; EXPECT_EQ(listens.results.size(), 0); } { auto transaction {session.createSharedTransaction()}; - auto listens {Listen::find(session, user->getId(), Scrobbler::Internal)}; - EXPECT_EQ(listens.moreResults, false); - ASSERT_EQ(listens.results.size(), 1); - EXPECT_EQ(listens.results.front()->getId(), listen->getId()); + { + auto listens {Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobbler(Scrobbler::Internal))}; + EXPECT_EQ(listens.moreResults, false); + ASSERT_EQ(listens.results.size(), 1); + EXPECT_EQ(listens.results.front(), listen->getId()); + } + + { + auto listens {Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobbler(Scrobbler::Internal).setScrobblingState(ScrobblingState::PendingAdd))}; + EXPECT_EQ(listens.results.size(), 1); + } + { + auto listens {Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobbler(Scrobbler::Internal).setScrobblingState(ScrobblingState::Synchronized))}; + EXPECT_EQ(listens.results.size(), 0); + } } } @@ -91,11 +102,11 @@ TEST_F(DatabaseFixture, Listen_get_multi) { auto transaction {session.createSharedTransaction()}; - auto listens {Listen::find(session, user->getId(), Scrobbler::Internal)}; + auto listens {Listen::find(session, Listen::FindParameters{}.setUser(user->getId()).setScrobbler(Scrobbler::Internal))}; ASSERT_EQ(listens.results.size(), 3); - EXPECT_EQ(listens.results[0]->getId(), listen1.getId()); - EXPECT_EQ(listens.results[1]->getId(), listen2.getId()); - EXPECT_EQ(listens.results[2]->getId(), listen3.getId()); + EXPECT_EQ(listens.results[0], listen1.getId()); + EXPECT_EQ(listens.results[1], listen2.getId()); + EXPECT_EQ(listens.results[2], listen3.getId()); } } diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp index a89ca613..ac24670d 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -105,7 +105,8 @@ namespace Scrobbling Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - return Database::Listen::getRecentArtists(session, userId, *scrobbler, clusterIds, linkType, range); + res = Database::Listen::getRecentArtists(session, userId, *scrobbler, clusterIds, linkType, range); + return res; } ScrobblingService::ReleaseContainer @@ -120,7 +121,8 @@ namespace Scrobbling Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - return Database::Listen::getRecentReleases(session, userId, *scrobbler, clusterIds, range); + res = Database::Listen::getRecentReleases(session, userId, *scrobbler, clusterIds, range); + return res; } ScrobblingService::TrackContainer @@ -135,7 +137,8 @@ namespace Scrobbling Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - return Database::Listen::getRecentTracks(session, userId, *scrobbler, clusterIds, range); + res = Database::Listen::getRecentTracks(session, userId, *scrobbler, clusterIds, range); + return res; } // Top @@ -151,7 +154,8 @@ namespace Scrobbling Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - return Database::Listen::getTopArtists(session, userId, *scrobbler, clusterIds, linkType, range); + res = Database::Listen::getTopArtists(session, userId, *scrobbler, clusterIds, linkType, range); + return res; } ScrobblingService::ReleaseContainer @@ -166,7 +170,8 @@ namespace Scrobbling Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - return Database::Listen::getTopReleases(session, userId, *scrobbler, clusterIds, range); + res = Database::Listen::getTopReleases(session, userId, *scrobbler, clusterIds, range); + return res; } ScrobblingService::TrackContainer @@ -181,7 +186,8 @@ namespace Scrobbling Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - return Database::Listen::getTopTracks(session, userId, *scrobbler, clusterIds, range); + res = Database::Listen::getTopTracks(session, userId, *scrobbler, clusterIds, range); + return res; } void diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index b0365e95..62a57ce0 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -69,7 +69,7 @@ namespace Scrobbling::ListenBrainz void Scrobbler::listenStarted(const Listen& listen) { - enqueListen(listen, Wt::WDateTime {}); + _listensSynchronizer.enqueListenNow(listen); } void @@ -78,23 +78,14 @@ namespace Scrobbling::ListenBrainz if (duration && !canBeScrobbled(_db.getTLSSession(), listen.trackId, *duration)) return; - const Listen timedListen {listen}; - const Wt::WDateTime now {Wt::WDateTime::currentDateTime()}; - - enqueListen(timedListen, now); + const TimedListen timedListen {listen, Wt::WDateTime::currentDateTime()}; + _listensSynchronizer.enqueListen(timedListen); } void - Scrobbler::addTimedListen(const TimedListen& listen) + Scrobbler::addTimedListen(const TimedListen& timedListen) { - assert(listen.listenedAt.isValid()); - enqueListen(listen, listen.listenedAt); - } - - void - Scrobbler::enqueListen(const Listen& listen, const Wt::WDateTime& timePoint) - { - _listensSynchronizer.enqueListen(listen, timePoint); + _listensSynchronizer.enqueListen(timedListen); } } // namespace Scrobbling::ListenBrainz diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp index 7863ce74..4a7dc77c 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp @@ -50,9 +50,6 @@ namespace Scrobbling::ListenBrainz void listenFinished(const Listen& listen, std::optional duration) override; void addTimedListen(const TimedListen& listen) override; - // Submit listens - void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); - boost::asio::io_context& _ioContext; Database::Db& _db; std::string _baseAPIUrl; diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 68c8d8e4..329a19bb 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -42,6 +42,7 @@ #include "Utils.hpp" #define LOG(sev) LMS_LOG(SCROBBLING, sev) << "[listenbrainz Synchronizer] - " +#define LOG_EX(sev) LMS_LOG_EX(Module::SCROBBLING, sev) << "[listenbrainz Synchronizer] - " namespace { @@ -173,6 +174,8 @@ namespace { using namespace Database; + //LOG(DEBUG) << "Trying to match track' " << Wt::Json::serialize(metadata) << "'"; + // first try to get the associated track using MBIDs, and then fallback on names if (metadata.type("additional_info") == Wt::Json::Type::Object) { @@ -302,7 +305,20 @@ namespace Scrobbling::ListenBrainz { LOG(INFO) << "Starting Listens synchronizer, maxSyncListenCount = " << _maxSyncListenCount << ", _syncListensPeriod = " << _syncListensPeriod.count() << " hours"; - scheduleGetListens(std::chrono::seconds {30}); + scheduleSync(std::chrono::seconds {30}); + } + + void + ListensSynchronizer::enqueListen(const TimedListen& listen) + { + assert(listen.listenedAt.isValid()); + enqueListen(listen, listen.listenedAt); + } + + void + ListensSynchronizer::enqueListenNow(const Listen& listen) + { + enqueListen(listen, {}); } void @@ -313,15 +329,22 @@ namespace Scrobbling::ListenBrainz if (timePoint.isValid()) { + const TimedListen timedListen {listen, timePoint}; // We want the listen to be sent again later in case of failure, so we just save it as pending send - const Database::ListenId listenId {saveListen(TimedListen {listen, timePoint})}; - if (!listenId.isValid()) - return; + saveListen(timedListen, Database::ScrobblingState::PendingAdd); request.priority = Http::ClientRequestParameters::Priority::Normal; request.onSuccessFunc = [=](std::string_view) { - onListenSent(listenId); + _strand.dispatch([=] + { + if (saveListen(timedListen, Database::ScrobblingState::Synchronized)) + { + UserContext& context {getUserContext(listen.userId)}; + if (context.listenCount) + (*context.listenCount)++; + } + }); }; // on failure, this listen will be sent during the next sync } @@ -341,62 +364,93 @@ namespace Scrobbling::ListenBrainz const std::optional listenBrainzToken {Utils::getListenBrainzToken(_db.getTLSSession(), listen.userId)}; if (!listenBrainzToken) + { + LOG(DEBUG) << "No listenbrainz token found: skipping"; return; + } request.message.addBodyText(bodyText); request.message.addHeader("Authorization", "Token " + std::string {listenBrainzToken->getAsString()}); request.message.addHeader("Content-Type", "application/json"); _client.sendPOSTRequest(std::move(request)); - } - Database::ListenId - ListensSynchronizer::saveListen(const TimedListen& listen) + bool + ListensSynchronizer::saveListen(const TimedListen& listen, Database::ScrobblingState scrobblingState) { using namespace Database; Session& session {_db.getTLSSession()}; auto transaction {session.createUniqueTransaction()}; - if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::ListenBrainz, listen.listenedAt)) - return {}; + Database::Listen::pointer dbListen {Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::ListenBrainz, listen.listenedAt)}; + if (!dbListen) + { + const User::pointer user {User::find(session, listen.userId)}; + if (!user) + return false; - const User::pointer user {User::find(session, listen.userId)}; - if (!user) - return {}; + const Track::pointer track {Track::find(session, listen.trackId)}; + if (!track) + return false; - const Track::pointer track {Track::find(session, listen.trackId)}; - if (!track) - return {}; + dbListen = Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt); + dbListen.modify()->setScrobblingState(scrobblingState); - const auto dbListen {Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt)}; - assert(dbListen->getScrobblingState() == Database::ScrobblingState::PendingAdd); + LOG(DEBUG) << "LISTEN CREATED for user " << user->getLoginName() << ", track '" << track->getName() << "' AT " << listen.listenedAt.toString(); - return dbListen->getId(); + return true; + } + + if (dbListen->getScrobblingState() == scrobblingState) + return false; + + dbListen.modify()->setScrobblingState(scrobblingState); + return true; } void - ListensSynchronizer::onListenSent(Database::ListenId listenId) + ListensSynchronizer::enquePendingListens() { - _strand.dispatch([=] + std::vector pendingListens; + { Database::Session& session {_db.getTLSSession()}; + auto transaction {session.createUniqueTransaction()}; - if (Database::Listen::pointer listen {Database::Listen::find(session, listenId)}) - { - listen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized); + Database::Listen::FindParameters params; + params.setScrobbler(Database::Scrobbler::ListenBrainz) + .setScrobblingState(Database::ScrobblingState::PendingAdd) + .setRange(Database::Range {0, 100}); // don't flood too much? - UserContext& context {getUserContext(listen->getUser()->getId())}; - if (context.listenCount) - (*context.listenCount)++; + const Database::RangeResults results {Database::Listen::find(session, params)}; + pendingListens.reserve(results.results.size()); + + for (Database::ListenId listenId : results.results) + { + const Database::Listen::pointer listen {Database::Listen::find(session, listenId)}; + + TimedListen timedListen; + timedListen.listenedAt = listen->getDateTime(); + timedListen.userId = listen->getUser()->getId(); + timedListen.trackId = listen->getTrack()->getId(); + + pendingListens.push_back(std::move(timedListen)); } - }); + } + + LOG(DEBUG) << "Queing " << pendingListens.size() << " pending listen"; + + for (const TimedListen& pendingListen : pendingListens) + enqueListen(pendingListen); } ListensSynchronizer::UserContext& ListensSynchronizer::getUserContext(Database::UserId userId) { + assert(_strand.running_in_this_thread()); + auto itContext {_userContexts.find(userId)}; if (itContext == std::cend(_userContexts)) { @@ -408,24 +462,24 @@ namespace Scrobbling::ListenBrainz } bool - ListensSynchronizer::isFetching() const + ListensSynchronizer::isSyncing() const { return std::any_of(std::cbegin(_userContexts), std::cend(_userContexts), [](const auto& contextEntry) { const auto& [userId, context] {contextEntry}; - return context.fetching; + return context.syncing; }); } void - ListensSynchronizer::scheduleGetListens(std::chrono::seconds fromNow) + ListensSynchronizer::scheduleSync(std::chrono::seconds fromNow) { if (_syncListensPeriod.count() == 0 || _maxSyncListenCount == 0) return; LOG(DEBUG) << "Scheduled sync in " << fromNow.count() << " seconds..."; - _getListensTimer.expires_after(fromNow); - _getListensTimer.async_wait(boost::asio::bind_executor(_strand, [this] (const boost::system::error_code& ec) + _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) { @@ -437,38 +491,37 @@ namespace Scrobbling::ListenBrainz throw Exception {"GetListens timer failure: " + std::string {ec.message()} }; } - startGetListens(); + startSync(); })); } void - ListensSynchronizer::startGetListens() + ListensSynchronizer::startSync() { - LOG(DEBUG) << "GetListens started!"; + LOG(DEBUG) << "Starting sync!"; - assert(!isFetching()); + assert(!isSyncing()); + + enquePendingListens(); Database::RangeResults userIds; { Database::Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; - userIds = Database::User::find(_db.getTLSSession(), Database::Range {}); + userIds = Database::User::find(_db.getTLSSession(), Database::User::FindParameters{}.setScrobbler(Database::Scrobbler::ListenBrainz)); } for (const Database::UserId userId : userIds.results) - { - if (Utils::getListenBrainzToken(_db.getTLSSession(), userId)) - startGetListens(getUserContext(userId)); - } + startSync(getUserContext(userId)); - if (!isFetching()) - scheduleGetListens(_syncListensPeriod); + if (!isSyncing()) + scheduleSync(_syncListensPeriod); } void - ListensSynchronizer::startGetListens(UserContext& context) + ListensSynchronizer::startSync(UserContext& context) { - context.fetching = true; + context.syncing = true; context.listenBrainzUserName = ""; context.maxDateTime = {}; context.fetchedListenCount = 0; @@ -479,15 +532,15 @@ namespace Scrobbling::ListenBrainz } void - ListensSynchronizer::onGetListensEnded(UserContext& context) + ListensSynchronizer::onSyncEnded(UserContext& context) { _strand.dispatch([this, &context] { - LOG(DEBUG) << "Fetch done for user " << context.userId.getValue() << ", fetched: " << context.fetchedListenCount << ", matched: " << context.matchedListenCount << ", imported: " << context.importedListenCount; - context.fetching = false; + LOG_EX(context.importedListenCount > 0 ? Severity::INFO : Severity::DEBUG) << "Sync done for user '" << context.listenBrainzUserName << "', fetched: " << context.fetchedListenCount << ", matched: " << context.matchedListenCount << ", imported: " << context.importedListenCount; + context.syncing = false; - if (!isFetching()) - scheduleGetListens(_syncListensPeriod); + if (!isSyncing()) + scheduleSync(_syncListensPeriod); }); } @@ -499,7 +552,7 @@ namespace Scrobbling::ListenBrainz const std::optional listenBrainzToken {Utils::getListenBrainzToken(_db.getTLSSession(), context.userId)}; if (!listenBrainzToken) { - onGetListensEnded(context); + onSyncEnded(context); return; } @@ -512,14 +565,14 @@ namespace Scrobbling::ListenBrainz context.listenBrainzUserName = parseValidateToken(msgBody); if (context.listenBrainzUserName.empty()) { - onGetListensEnded(context); + onSyncEnded(context); return; } enqueGetListenCount(context); }; request.onFailureFunc = [this, &context] { - onGetListensEnded(context); + onSyncEnded(context); }; _client.sendGETRequest(std::move(request)); @@ -544,7 +597,7 @@ namespace Scrobbling::ListenBrainz if (!needSync) { - onGetListensEnded(context); + onSyncEnded(context); return; } @@ -553,7 +606,7 @@ namespace Scrobbling::ListenBrainz }; request.onFailureFunc = [this, &context] { - onGetListensEnded(context); + onSyncEnded(context); }; _client.sendGETRequest(std::move(request)); @@ -572,7 +625,7 @@ namespace Scrobbling::ListenBrainz processGetListensResponse(msgBody, context); if (context.fetchedListenCount >= _maxSyncListenCount || !context.maxDateTime.isValid()) { - onGetListensEnded(context); + onSyncEnded(context); return; } @@ -580,7 +633,7 @@ namespace Scrobbling::ListenBrainz }; request.onFailureFunc = [=, &context] { - onGetListensEnded(context); + onSyncEnded(context); }; _client.sendGETRequest(std::move(request)); @@ -597,27 +650,10 @@ namespace Scrobbling::ListenBrainz context.matchedListenCount += parseResult.matchedListens.size(); context.maxDateTime = parseResult.oldestEntry; - if (parseResult.matchedListens.empty()) - return; - - auto transaction {session.createUniqueTransaction()}; - - Database::User::pointer user {Database::User::find(session, context.userId)}; - if (!user) - return; - for (const TimedListen& listen : parseResult.matchedListens) { - const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; - if (!track) - continue; - - if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::ListenBrainz, listen.listenedAt)) - continue; - - Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt); - context.importedListenCount++; + if (saveListen(listen, Database::ScrobblingState::Synchronized)) + context.importedListenCount++; } } } // namespace Scrobbling::ListenBrainz - diff --git a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index 258942a0..45fef878 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -50,11 +50,14 @@ namespace Scrobbling::ListenBrainz public: ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, Http::IClient& client); - void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); + void enqueListen(const TimedListen& listen); + void enqueListenNow(const Listen& listen); private: - Database::ListenId saveListen(const TimedListen& listen); - void onListenSent(Database::ListenId listenId); + void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); + bool saveListen(const TimedListen& listen, Database::ScrobblingState scrobblinState); + + void enquePendingListens(); struct UserContext { @@ -66,10 +69,10 @@ namespace Scrobbling::ListenBrainz UserContext& operator=(UserContext&&) = delete; const Database::UserId userId; - bool fetching {}; + bool syncing {}; std::optional listenCount {}; - // resetted at each fetch + // resetted at each sync std::string listenBrainzUserName; // need to be resolved first Wt::WDateTime maxDateTime; std::size_t fetchedListenCount{}; @@ -78,20 +81,20 @@ namespace Scrobbling::ListenBrainz }; UserContext& getUserContext(Database::UserId userId); - bool isFetching() const; - void scheduleGetListens(std::chrono::seconds fromNow); - void startGetListens(); - void startGetListens(UserContext& context); - void onGetListensEnded(UserContext& context); + bool isSyncing() const; + void scheduleSync(std::chrono::seconds fromNow); + void startSync(); + void startSync(UserContext& context); + void onSyncEnded(UserContext& context); void enqueValidateToken(UserContext& context); void enqueGetListenCount(UserContext& context); void enqueGetListens(UserContext& context); - void processGetListensResponse(std::string_view body, UserContext& context); + void processGetListensResponse(std::string_view body, UserContext& context); boost::asio::io_context& _ioContext; boost::asio::io_context::strand _strand {_ioContext}; Database::Db& _db; - boost::asio::steady_timer _getListensTimer {_ioContext}; + boost::asio::steady_timer _syncTimer {_ioContext}; Http::IClient& _client; std::unordered_map _userContexts; diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp index e956370f..4e9acdf6 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.cpp @@ -19,14 +19,9 @@ #include "Utils.hpp" -#include - #include "services/database/Session.hpp" -#include "services/database/TrackList.hpp" #include "services/database/User.hpp" -static constexpr std::string_view historyTracklistName {"__scrobbler_listenbrainz_history__"}; - namespace Scrobbling::ListenBrainz::Utils { std::optional @@ -38,9 +33,6 @@ namespace Scrobbling::ListenBrainz::Utils if (!user) return std::nullopt; - if (user->getScrobbler() != Database::Scrobbler::ListenBrainz) - return std::nullopt; - return user->getListenBrainzToken(); } } diff --git a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp index 4a4897c3..47bd2a0e 100644 --- a/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp +++ b/src/libs/services/scrobbling/impl/listenbrainz/Utils.hpp @@ -32,5 +32,5 @@ namespace Database namespace Scrobbling::ListenBrainz::Utils { - std::optional getListenBrainzToken(Database::Session& session, Database::UserId userId); + std::optional getListenBrainzToken(Database::Session& session, Database::UserId userId); } diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 54e53642..d8abd57b 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -1394,7 +1394,7 @@ handleGetUsersRequest(RequestContext& context) Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node& usersNode {response.createNode("users")}; - const auto userIds {User::find(context.dbSession, Range {})}; + const auto userIds {User::find(context.dbSession, User::FindParameters {})}; for (const UserId userId : userIds.results) { const User::pointer user {User::find(context.dbSession, userId)}; diff --git a/src/libs/utils/impl/http/SendQueue.cpp b/src/libs/utils/impl/http/SendQueue.cpp index d689179e..8d99fc72 100644 --- a/src/libs/utils/impl/http/SendQueue.cpp +++ b/src/libs/utils/impl/http/SendQueue.cpp @@ -26,7 +26,7 @@ #include "utils/Logger.hpp" #include "utils/String.hpp" -#define LOG(sev) LMS_LOG(SCROBBLING, sev) << "[listenbrainz SendQueue] - " +#define LOG(sev) LMS_LOG(SCROBBLING, sev) << "[Http SendQueue] - " namespace StringUtils { @@ -143,7 +143,7 @@ namespace Http { if (ec == boost::asio::error::operation_aborted) { - LOG(DEBUG) << "SendQueue: client aborted"; + LOG(DEBUG) << "Client aborted"; return; } @@ -228,7 +228,7 @@ namespace Http { if (ec == boost::asio::error::operation_aborted) { - LOG(DEBUG) << "SendQueue: 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/Logger.hpp index 43b2f893..d7faf078 100644 --- a/src/libs/utils/include/utils/Logger.hpp +++ b/src/libs/utils/include/utils/Logger.hpp @@ -85,5 +85,6 @@ class Logger virtual void processLog(const Log& log) = 0; }; -#define LMS_LOG(module, severity) Log(Service::get(), Module::module, Severity::severity).getOstream() +#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() diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index a5daed2f..84f2c41b 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -71,7 +71,7 @@ UsersView::refreshView() auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - for (const UserId userId : User::find(LmsApp->getDbSession(), Range {}).results) + for (const UserId userId : User::find(LmsApp->getDbSession(), User::FindParameters {}).results) { const User::pointer user {User::find(LmsApp->getDbSession(), userId)}; From 3049babd41ee3ce584ce01d67b69f9f65d17bb6a Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 30 Jan 2022 14:57:04 +0100 Subject: [PATCH 41/49] CodeFactor review --- .../image/impl/graphicsmagick/RawImage.cpp | 3 ++ .../services/auth/impl/LoginThrottler.hpp | 31 +++++++++---------- src/libs/services/database/impl/AuthToken.cpp | 1 - src/libs/services/database/impl/SqlQuery.hpp | 12 +------ src/libs/services/database/impl/TrackList.cpp | 1 - .../include/services/database/Artist.hpp | 1 - .../include/services/database/AuthToken.hpp | 1 - .../include/services/database/Cluster.hpp | 1 - .../database/include/services/database/Db.hpp | 2 -- .../services/database/ScanSettings.hpp | 1 - .../services/database/TrackBookmark.hpp | 1 - .../include/services/database/TrackList.hpp | 2 -- src/libs/services/database/test/TrackList.cpp | 1 - src/libs/utils/impl/http/SendQueue.hpp | 1 - 14 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/libs/image/impl/graphicsmagick/RawImage.cpp b/src/libs/image/impl/graphicsmagick/RawImage.cpp index 8ec77b2e..ad4f2b70 100644 --- a/src/libs/image/impl/graphicsmagick/RawImage.cpp +++ b/src/libs/image/impl/graphicsmagick/RawImage.cpp @@ -35,6 +35,9 @@ namespace Image::GraphicsMagick std::unique_ptr decodeImage(const std::filesystem::path& path) { + return std::make_unique(path) + } +} void init(const std::filesystem::path& path) diff --git a/src/libs/services/auth/impl/LoginThrottler.hpp b/src/libs/services/auth/impl/LoginThrottler.hpp index b8390630..b60ca666 100644 --- a/src/libs/services/auth/impl/LoginThrottler.hpp +++ b/src/libs/services/auth/impl/LoginThrottler.hpp @@ -28,27 +28,24 @@ #include "utils/NetAddress.hpp" #include "utils/Exception.hpp" -namespace Auth { - -class LoginThrottler +namespace Auth { - public: - LoginThrottler(std::size_t maxEntries) : _maxEntries {maxEntries} {} + class LoginThrottler + { + public: + LoginThrottler(std::size_t maxEntries) : _maxEntries {maxEntries} {} - // user must lock these calls to avoid races - bool isClientThrottled(const boost::asio::ip::address& address) const; - void onBadClientAttempt(const boost::asio::ip::address& address); - void onGoodClientAttempt(const boost::asio::ip::address& address); + // user must lock these calls to avoid races + bool isClientThrottled(const boost::asio::ip::address& address) const; + void onBadClientAttempt(const boost::asio::ip::address& address); + void onGoodClientAttempt(const boost::asio::ip::address& address); - private: - - void removeOutdatedEntries(); - - const std::size_t _maxEntries; - - std::unordered_map _attemptsInfo; -}; + private: + void removeOutdatedEntries(); + const std::size_t _maxEntries; + std::unordered_map _attemptsInfo; + }; } // Auth diff --git a/src/libs/services/database/impl/AuthToken.cpp b/src/libs/services/database/impl/AuthToken.cpp index 52efdace..85058a01 100644 --- a/src/libs/services/database/impl/AuthToken.cpp +++ b/src/libs/services/database/impl/AuthToken.cpp @@ -33,7 +33,6 @@ namespace Database , _expiry {expiry} , _user {getDboPtr(user)} { - } AuthToken::pointer diff --git a/src/libs/services/database/impl/SqlQuery.hpp b/src/libs/services/database/impl/SqlQuery.hpp index 76e6487e..7c013aa7 100644 --- a/src/libs/services/database/impl/SqlQuery.hpp +++ b/src/libs/services/database/impl/SqlQuery.hpp @@ -26,7 +26,6 @@ class WhereClause { public: - WhereClause() {} WhereClause(const std::string& clause) { _clause = clause; } @@ -40,7 +39,6 @@ class WhereClause const std::list& getBindArgs(void) const {return _bindArgs;} private: - std::string _clause; // WHERE clause std::list _bindArgs; @@ -49,7 +47,6 @@ class WhereClause class InnerJoinClause { public: - InnerJoinClause() {} InnerJoinClause(const std::string& clause); @@ -57,7 +54,6 @@ class InnerJoinClause std::string get() const { return _clause;} private: - std::string _clause; }; @@ -87,14 +83,12 @@ class SelectStatement std::string get() const; private: - std::list _statement; }; class FromClause { public: - FromClause() {} FromClause(const std::string& clause); @@ -103,15 +97,12 @@ class FromClause std::string get() const; private: - std::list _clause; - }; class SqlQuery { public: - SelectStatement& select(void) { return _selectStatement;} SelectStatement& select(const std::string& statement) { _selectStatement = SelectStatement(statement); return _selectStatement; } FromClause& from(void) { return _fromClause; } @@ -122,10 +113,9 @@ class SqlQuery GroupByStatement& groupBy(void) { return _groupByStatement; } const GroupByStatement& groupBy(void) const { return _groupByStatement; } - std::string get(void) const; + std::string get() const; private: - SelectStatement _selectStatement; // SELECT statement InnerJoinClause _innerJoinClause; // INNER JOIN FromClause _fromClause; // FROM tables diff --git a/src/libs/services/database/impl/TrackList.cpp b/src/libs/services/database/impl/TrackList.cpp index c91826fd..58ea58b2 100644 --- a/src/libs/services/database/impl/TrackList.cpp +++ b/src/libs/services/database/impl/TrackList.cpp @@ -41,7 +41,6 @@ TrackList::TrackList(std::string_view name, Type type, bool isPublic, ObjectPtr< _isPublic {isPublic}, _user {getDboPtr(user)} { - } TrackList::pointer diff --git a/src/libs/services/database/include/services/database/Artist.hpp b/src/libs/services/database/include/services/database/Artist.hpp index 7f28e2fa..ac94ab3d 100644 --- a/src/libs/services/database/include/services/database/Artist.hpp +++ b/src/libs/services/database/include/services/database/Artist.hpp @@ -122,7 +122,6 @@ class Artist : public Object } private: - static const std::size_t _maxNameLength = 128; std::string _name; diff --git a/src/libs/services/database/include/services/database/AuthToken.hpp b/src/libs/services/database/include/services/database/AuthToken.hpp index 46467dc0..646a5d9f 100644 --- a/src/libs/services/database/include/services/database/AuthToken.hpp +++ b/src/libs/services/database/include/services/database/AuthToken.hpp @@ -57,7 +57,6 @@ namespace Database } private: - std::string _value; Wt::WDateTime _expiry; Wt::Dbo::ptr _user; diff --git a/src/libs/services/database/include/services/database/Cluster.hpp b/src/libs/services/database/include/services/database/Cluster.hpp index 59560f74..a1cac3f7 100644 --- a/src/libs/services/database/include/services/database/Cluster.hpp +++ b/src/libs/services/database/include/services/database/Cluster.hpp @@ -112,7 +112,6 @@ class ClusterType : public Object } private: - static const std::size_t _maxNameLength = 128; std::string _name; diff --git a/src/libs/services/database/include/services/database/Db.hpp b/src/libs/services/database/include/services/database/Db.hpp index 0fea5b24..b58077e8 100644 --- a/src/libs/services/database/include/services/database/Db.hpp +++ b/src/libs/services/database/include/services/database/Db.hpp @@ -31,7 +31,6 @@ class Session; class Db { public: - Db(const std::filesystem::path& dbPath, std::size_t connectionCount = 10); ~Db(); @@ -68,7 +67,6 @@ class Db std::unique_ptr _connection; }; - RecursiveSharedMutex _sharedMutex; std::unique_ptr _connectionPool; diff --git a/src/libs/services/database/include/services/database/ScanSettings.hpp b/src/libs/services/database/include/services/database/ScanSettings.hpp index 75fa4a58..1a81b757 100644 --- a/src/libs/services/database/include/services/database/ScanSettings.hpp +++ b/src/libs/services/database/include/services/database/ScanSettings.hpp @@ -89,7 +89,6 @@ class ScanSettings : public Object } private: - int _scanVersion {}; std::string _mediaDirectory; Wt::WTime _startTime = Wt::WTime {0,0,0}; diff --git a/src/libs/services/database/include/services/database/TrackBookmark.hpp b/src/libs/services/database/include/services/database/TrackBookmark.hpp index b5fb2985..d1d31768 100644 --- a/src/libs/services/database/include/services/database/TrackBookmark.hpp +++ b/src/libs/services/database/include/services/database/TrackBookmark.hpp @@ -71,7 +71,6 @@ class TrackBookmark : public Object Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); } private: - static const std::size_t _maxCommentLength = 128; std::chrono::duration _offset; diff --git a/src/libs/services/database/include/services/database/TrackList.hpp b/src/libs/services/database/include/services/database/TrackList.hpp index b168ad5d..ebb61772 100644 --- a/src/libs/services/database/include/services/database/TrackList.hpp +++ b/src/libs/services/database/include/services/database/TrackList.hpp @@ -121,7 +121,6 @@ class TrackList : public Object } private: - std::string _name; Type _type {Type::Playlist}; bool _isPublic {false}; @@ -158,7 +157,6 @@ class TrackListEntry : public Object } private: - Wt::WDateTime _dateTime; // optional date time Wt::Dbo::ptr _track; Wt::Dbo::ptr _tracklist; diff --git a/src/libs/services/database/test/TrackList.cpp b/src/libs/services/database/test/TrackList.cpp index 9bdb96ec..b771e472 100644 --- a/src/libs/services/database/test/TrackList.cpp +++ b/src/libs/services/database/test/TrackList.cpp @@ -130,7 +130,6 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed) TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); } { - auto transaction {session.createSharedTransaction()}; bool moreResults {}; diff --git a/src/libs/utils/impl/http/SendQueue.hpp b/src/libs/utils/impl/http/SendQueue.hpp index f309cb2c..e9eb4180 100644 --- a/src/libs/utils/impl/http/SendQueue.hpp +++ b/src/libs/utils/impl/http/SendQueue.hpp @@ -46,7 +46,6 @@ namespace Http void sendRequest(std::unique_ptr request); private: - void sendNextQueuedRequest(); bool sendRequest(const ClientRequest& request); void onClientDone(Wt::AsioWrapper::error_code ec, const Wt::Http::Message& msg); From 9cd17d32046398caf4cb3a822dc3245f224afd0b Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 30 Jan 2022 21:31:31 +0100 Subject: [PATCH 42/49] Mage GM work again --- .../image/impl/graphicsmagick/JPEGImage.cpp | 4 +- .../image/impl/graphicsmagick/JPEGImage.hpp | 4 +- .../image/impl/graphicsmagick/RawImage.cpp | 46 ++++++------ .../image/impl/graphicsmagick/RawImage.hpp | 10 ++- src/libs/image/impl/stb/RawImage.cpp | 6 ++ src/libs/image/include/image/IRawImage.hpp | 2 + src/libs/services/database/impl/SqlQuery.hpp | 1 - .../include/services/database/Track.hpp | 1 - .../scrobbling/impl/ScrobblerBase.cpp | 72 ------------------- src/lms/main.cpp | 2 + 10 files changed, 42 insertions(+), 106 deletions(-) delete mode 100644 src/libs/services/scrobbling/impl/ScrobblerBase.cpp diff --git a/src/libs/image/impl/graphicsmagick/JPEGImage.cpp b/src/libs/image/impl/graphicsmagick/JPEGImage.cpp index 8cf7dfdc..85b2fac3 100644 --- a/src/libs/image/impl/graphicsmagick/JPEGImage.cpp +++ b/src/libs/image/impl/graphicsmagick/JPEGImage.cpp @@ -19,11 +19,11 @@ #include "JPEGImage.hpp" -#include "Exception.hpp" #include "RawImage.hpp" +#include "image/Exception.hpp" #include "utils/Logger.hpp" -namespace CoverArt::GraphicsMagick +namespace Image::GraphicsMagick { JPEGImage::JPEGImage(const RawImage& rawImage, unsigned quality) { diff --git a/src/libs/image/impl/graphicsmagick/JPEGImage.hpp b/src/libs/image/impl/graphicsmagick/JPEGImage.hpp index 37a5e163..9ead9f3c 100644 --- a/src/libs/image/impl/graphicsmagick/JPEGImage.hpp +++ b/src/libs/image/impl/graphicsmagick/JPEGImage.hpp @@ -25,9 +25,9 @@ #include -#include "cover/IEncodedImage.hpp" +#include "image/IEncodedImage.hpp" -namespace CoverArt::GraphicsMagick +namespace Image::GraphicsMagick { class RawImage; class JPEGImage : public IEncodedImage diff --git a/src/libs/image/impl/graphicsmagick/RawImage.cpp b/src/libs/image/impl/graphicsmagick/RawImage.cpp index ad4f2b70..fc74ae37 100644 --- a/src/libs/image/impl/graphicsmagick/RawImage.cpp +++ b/src/libs/image/impl/graphicsmagick/RawImage.cpp @@ -21,41 +21,43 @@ #include -#include "utils/Logger.hpp" #include "JPEGImage.hpp" -#include "Exception.hpp" +#include "image/Exception.hpp" +#include "utils/Logger.hpp" -namespace Image::GraphicsMagick +namespace Image { - std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize) { - return std::make_unique(encodedData, encodedDataSize); + return std::make_unique(encodedData, encodedDataSize); } std::unique_ptr decodeImage(const std::filesystem::path& path) { - return std::make_unique(path) + return std::make_unique(path); + } + + void + init(const std::filesystem::path& path) + { + 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"; + + if (!MagickLib::SetMagickResourceLimit(MagickLib::ThreadsResource, 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, INFO) << "Magick threads resource limit = " << GetMagickResourceLimit(MagickLib::ThreadsResource); + LMS_LOG(COVER, INFO) << "Magick Disk resource limit = " << GetMagickResourceLimit(MagickLib::DiskResource); } } -void -init(const std::filesystem::path& path) +namespace Image::GraphicsMagick { - 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"; - - if (!MagickLib::SetMagickResourceLimit(MagickLib::ThreadsResource, 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, INFO) << "Magick threads resource limit = " << GetMagickResourceLimit(MagickLib::ThreadsResource); - LMS_LOG(COVER, INFO) << "Magick Disk resource limit = " << GetMagickResourceLimit(MagickLib::DiskResource); -} RawImage::RawImage(const std::byte* encodedData, std::size_t encodedDataSize) { diff --git a/src/libs/image/impl/graphicsmagick/RawImage.hpp b/src/libs/image/impl/graphicsmagick/RawImage.hpp index 354c07c0..d1c180ed 100644 --- a/src/libs/image/impl/graphicsmagick/RawImage.hpp +++ b/src/libs/image/impl/graphicsmagick/RawImage.hpp @@ -28,14 +28,12 @@ #include #include -#include "cover/IEncodedImage.hpp" -#include "IRawImage.hpp" +#include "image/IEncodedImage.hpp" +#include "image/IRawImage.hpp" -namespace CoverArt::GraphicsMagick +namespace Image::GraphicsMagick { - void init(const std::filesystem::path& path); - - class RawImage : IRawImage + class RawImage : public IRawImage { public: RawImage(const std::byte* encodedData, std::size_t encodedDataSize); diff --git a/src/libs/image/impl/stb/RawImage.cpp b/src/libs/image/impl/stb/RawImage.cpp index 8ad944bc..df49ec9c 100644 --- a/src/libs/image/impl/stb/RawImage.cpp +++ b/src/libs/image/impl/stb/RawImage.cpp @@ -43,6 +43,12 @@ namespace Image { return std::make_unique(path); } + + void + init(const std::filesystem::path& path) + { + } +{ } namespace Image::STB diff --git a/src/libs/image/include/image/IRawImage.hpp b/src/libs/image/include/image/IRawImage.hpp index 67968ed0..79d8587f 100644 --- a/src/libs/image/include/image/IRawImage.hpp +++ b/src/libs/image/include/image/IRawImage.hpp @@ -19,6 +19,7 @@ #pragma once +#include #include #include "image/IEncodedImage.hpp" @@ -33,6 +34,7 @@ namespace Image virtual std::unique_ptr encodeToJPEG(unsigned quality) const = 0; }; + void init(const std::filesystem::path& path); std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize); std::unique_ptr decodeImage(const std::filesystem::path& path); } diff --git a/src/libs/services/database/impl/SqlQuery.hpp b/src/libs/services/database/impl/SqlQuery.hpp index 7c013aa7..5a11fe99 100644 --- a/src/libs/services/database/impl/SqlQuery.hpp +++ b/src/libs/services/database/impl/SqlQuery.hpp @@ -41,7 +41,6 @@ class WhereClause private: std::string _clause; // WHERE clause std::list _bindArgs; - }; class InnerJoinClause diff --git a/src/libs/services/database/include/services/database/Track.hpp b/src/libs/services/database/include/services/database/Track.hpp index 38297cb3..3f0bf099 100644 --- a/src/libs/services/database/include/services/database/Track.hpp +++ b/src/libs/services/database/include/services/database/Track.hpp @@ -184,7 +184,6 @@ class Track : public Object } private: - static const std::size_t _maxNameLength = 128; static const std::size_t _maxCopyrightLength = 128; static const std::size_t _maxCopyrightURLLength = 128; diff --git a/src/libs/services/scrobbling/impl/ScrobblerBase.cpp b/src/libs/services/scrobbling/impl/ScrobblerBase.cpp deleted file mode 100644 index a21d312b..00000000 --- a/src/libs/services/scrobbling/impl/ScrobblerBase.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2021 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 "services/scrobbling/Listen.hpp" - -namespace Scrobbling -{ - ScrobblerBase::ScrobblerBase(Database::Db& db) - : _db {db} - { - } - - Database::TrackListId - ScrobberBase::getListensTrackList(Database::UserId userId, std::string_view trackListName) - { - std::string_view listensTrackListName {getListensTrackListName()}; - Database::Session& session {_db.getTLSSession()}; - - { - auto transaction {session.createSharedTransaction()}; - - if (auto trackList {Database::TrackList::get(session, listensTrackListName, Database::TrackList::Type::Internal, userId)}) - return trackList->getId(); - } - - { - auto transaction {session.createUniqueTransaction()}; - if (auto trackList {Database::TrackList::get(session, listensTrackListName, Database::TrackList::Type::Internal, userId)}) - return trackList->getId(); - - const Database::User::pointer user {Database::User::getById(session, userId)}; - if (!user) - return {}; - - Database::TrackList::pointer trackList {Database::TrackList::create(session, listensTrackListName, Database::TrackList::Type::Internal, false, user)}; - return trackList->getId(); - } - } - - bool - ScrobberBase::saveTimedListen(const TimedListen& listen, std::string_view trackListName) - { - const Database::TrackListId trackListId {getListensTrackList(listen.userId)}; - if (!trackListId) - return false; - - { - - return true; - } - } -} // ns Scrobbling - diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 34f82676..8ad33f19 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -25,6 +25,7 @@ #include #include +#include "image/IRawImage.hpp" #include "services/auth/IAuthTokenService.hpp" #include "services/auth/IPasswordService.hpp" #include "services/auth/IEnvService.hpp" @@ -254,6 +255,7 @@ int main(int argc, char* argv[]) else throw LmsException {"Bad value '" + authenticationBackend + "' for 'authentication-backend'"}; + Image::init(argv[0]); Service coverService {Cover::createCoverService(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg")}; Service recommendationService {Recommendation::createRecommendationService(database)}; Service scannerService {Scanner::createScannerService(database, *recommendationService)}; From 4330042bdbd47113a892959e5da1a272f86bf7fe Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 30 Jan 2022 21:36:00 +0100 Subject: [PATCH 43/49] Made STB work again --- src/libs/image/impl/stb/RawImage.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/image/impl/stb/RawImage.cpp b/src/libs/image/impl/stb/RawImage.cpp index df49ec9c..a68f36b9 100644 --- a/src/libs/image/impl/stb/RawImage.cpp +++ b/src/libs/image/impl/stb/RawImage.cpp @@ -45,10 +45,9 @@ namespace Image } void - init(const std::filesystem::path& path) + init(const std::filesystem::path&) { } -{ } namespace Image::STB From 644903bc02d95ca40170aefb7c0a2f5180bc57d1 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 31 Jan 2022 13:13:08 +0100 Subject: [PATCH 44/49] Fixed bad object id resolution in random view modes (IDs are cached and may be removed by the scanner) --- src/lms/ui/explore/ArtistsView.cpp | 4 ++-- src/lms/ui/explore/ReleasesView.cpp | 4 ++-- src/lms/ui/explore/TracksView.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index 34fc0966..d7ae20ed 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -157,8 +157,8 @@ Artists::addSome() const auto artistIds {_artistCollector.get(Range {static_cast(_container->getCount()), _batchSize})}; for (const ArtistId artistId : artistIds.results) { - const auto artist {Artist::find(LmsApp->getDbSession(), artistId)}; - _container->add(ArtistListHelpers::createEntry(artist)); + if (const auto artist {Artist::find(LmsApp->getDbSession(), artistId)}) + _container->add(ArtistListHelpers::createEntry(artist)); } _container->setHasMore(artistIds.moreResults); diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index 6fc358da..6ac2637b 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -121,8 +121,8 @@ Releases::addSome() const auto releaseIds {_releaseCollector.get(Range {static_cast(_container->getCount()), _batchSize})}; for (const ReleaseId releaseId : releaseIds.results) { - const Release::pointer release {Release::find(LmsApp->getDbSession(), releaseId)}; - _container->add(ReleaseListHelpers::createEntry(release)); + if (const Release::pointer release {Release::find(LmsApp->getDbSession(), releaseId)}) + _container->add(ReleaseListHelpers::createEntry(release)); } _container->setHasMore(releaseIds.moreResults); diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index c93e9a18..1b602ca8 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -125,8 +125,8 @@ Tracks::addSome() for (const TrackId trackId : trackIds.results) { - const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)}; - _container->add(TrackListHelpers::createEntry(track, tracksAction)); + if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)}) + _container->add(TrackListHelpers::createEntry(track, tracksAction)); } _container->setHasMore(trackIds.moreResults); From 24d239f1e79e0540574f75295c6b02862d478230 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 31 Jan 2022 20:18:41 +0100 Subject: [PATCH 45/49] Added WavPack support. fixes #195 --- src/libs/av/impl/Transcoder.cpp | 1 - .../av/include/av/TranscodeParameters.hpp | 19 +++++++++---------- src/libs/av/include/av/Types.hpp | 11 +++++------ src/libs/services/database/impl/Migration.cpp | 8 ++++++++ src/libs/services/database/impl/Migration.hpp | 2 +- .../services/database/ScanSettings.hpp | 2 +- 6 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/libs/av/impl/Transcoder.cpp b/src/libs/av/impl/Transcoder.cpp index 7efa0442..55b00d1b 100644 --- a/src/libs/av/impl/Transcoder.cpp +++ b/src/libs/av/impl/Transcoder.cpp @@ -119,7 +119,6 @@ Transcoder::start() args.emplace_back("-b:a"); args.emplace_back(std::to_string(_parameters.bitrate)); - // Codecs and formats switch (_parameters.format) { diff --git a/src/libs/av/include/av/TranscodeParameters.hpp b/src/libs/av/include/av/TranscodeParameters.hpp index 7449e2d9..c155f6e7 100644 --- a/src/libs/av/include/av/TranscodeParameters.hpp +++ b/src/libs/av/include/av/TranscodeParameters.hpp @@ -24,16 +24,15 @@ #include "Types.hpp" -namespace Av { - -struct TranscodeParameters +namespace Av { - Format format; - std::size_t bitrate {128000}; - std::optional stream; // Id of the stream to be transcoded (auto detect by default) - std::chrono::milliseconds offset {0}; - bool stripMetadata {true}; -}; - + struct TranscodeParameters + { + Format format; + std::size_t bitrate {128000}; + std::optional stream; // Id of the stream to be transcoded (auto detect by default) + std::chrono::milliseconds offset {0}; + bool stripMetadata {true}; + }; } // namespace Av diff --git a/src/libs/av/include/av/Types.hpp b/src/libs/av/include/av/Types.hpp index d1cc4cb5..166a19da 100644 --- a/src/libs/av/include/av/Types.hpp +++ b/src/libs/av/include/av/Types.hpp @@ -33,12 +33,11 @@ namespace Av { enum class Format { - // Values are important and must not be changed (stored in the UI's localstorage) - MP3 = 0, - OGG_OPUS = 1, - MATROSKA_OPUS = 2, - OGG_VORBIS = 3, - WEBM_VORBIS = 4, + MP3, + OGG_OPUS, + MATROSKA_OPUS, + OGG_VORBIS, + WEBM_VORBIS, }; std::string_view formatToMimetype(Format format); diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index 64198a88..b83ebee2 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -542,6 +542,13 @@ CREATE TABLE "listen" ( migrateListens("__scrobbler_listenbrainz_history__", Scrobbler::ListenBrainz); } + static + void + migrateFromV32(Session& session) + { + ScanSettings::get(session).modify()->addAudioFileExtension(".wv"); + } + void doDbMigration(Session& session) { @@ -580,6 +587,7 @@ CREATE TABLE "listen" ( {29, migrateFromV29}, {30, migrateFromV30}, {31, migrateFromV31}, + {32, migrateFromV32}, }; while (1) diff --git a/src/libs/services/database/impl/Migration.hpp b/src/libs/services/database/impl/Migration.hpp index ce5ae962..9b4f1eed 100644 --- a/src/libs/services/database/impl/Migration.hpp +++ b/src/libs/services/database/impl/Migration.hpp @@ -26,7 +26,7 @@ namespace Database class Session; using Version = std::size_t; - static constexpr Version LMS_DATABASE_VERSION {32}; + static constexpr Version LMS_DATABASE_VERSION {33}; class VersionInfo { public: diff --git a/src/libs/services/database/include/services/database/ScanSettings.hpp b/src/libs/services/database/include/services/database/ScanSettings.hpp index 1a81b757..19def107 100644 --- a/src/libs/services/database/include/services/database/ScanSettings.hpp +++ b/src/libs/services/database/include/services/database/ScanSettings.hpp @@ -94,7 +94,7 @@ class ScanSettings : public Object Wt::WTime _startTime = Wt::WTime {0,0,0}; UpdatePeriod _updatePeriod {UpdatePeriod::Never}; RecommendationEngineType _recommendationEngineType {RecommendationEngineType::Clusters}; - std::string _audioFileExtensions {".alac .mp3 .ogg .oga .aac .m4a .m4b .flac .wav .wma .aif .aiff .ape .mpc .shn .opus"}; + std::string _audioFileExtensions {".alac .mp3 .ogg .oga .aac .m4a .m4b .flac .wav .wma .aif .aiff .ape .mpc .shn .opus .wv"}; Wt::Dbo::collection> _clusterTypes; }; From 1e0bf580926ef8a07ef126905e0ef130e1d13ab9 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 31 Jan 2022 21:09:22 +0100 Subject: [PATCH 46/49] Added relevant indexes using artist sort name. fixes #184 --- src/libs/subsonic/impl/SubsonicResource.cpp | 223 ++++++++----------- src/libs/utils/impl/RecursiveSharedMutex.cpp | 2 +- 2 files changed, 92 insertions(+), 133 deletions(-) diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index d8abd57b..0f4f627c 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -953,50 +953,6 @@ handleGetArtistInfo2Request(RequestContext& context) return handleGetArtistInfoRequestCommon(context, true /* id3 */); } -static -Response -handleGetArtistsRequest(RequestContext& context) -{ - Response response {Response::createOkResponse(context.serverProtocolVersion)}; - - Response::Node& artistsNode {response.createNode("artists")}; - artistsNode.setAttribute("ignoredArticles", ""); - artistsNode.setAttribute("lastModified", reportedDummyDateULong); - - Response::Node& indexNode {artistsNode.createArrayChild("index")}; - indexNode.setAttribute("name", "?"); - - auto transaction {context.dbSession.createSharedTransaction()}; - - User::pointer user {User::find(context.dbSession, context.userId)}; - if (!user) - throw UserNotAuthorizedError {}; - - Artist::FindParameters findParameters; - - findParameters.setSortMethod(ArtistSortMethod::BySortName); - switch (user->getSubsonicArtistListMode()) - { - case SubsonicArtistListMode::AllArtists: - break; - case SubsonicArtistListMode::ReleaseArtists: - findParameters.setLinkType(TrackArtistLinkType::ReleaseArtist); - break; - case SubsonicArtistListMode::TrackArtists: - findParameters.setLinkType(TrackArtistLinkType::Artist); - break; - } - - const RangeResults artists {Artist::find(context.dbSession, findParameters)}; - for (const ArtistId artistId : artists.results) - { - const auto artist {Artist::find(context.dbSession, artistId)}; - indexNode.addArrayChild("artist", artistToResponseNode(user, artist, true /* id3 */)); - } - - return response; -} - static Response handleGetMusicDirectoryRequest(RequestContext& context) @@ -1079,6 +1035,88 @@ handleGetMusicFoldersRequest(RequestContext& context) return response; } +static +Response +handleGetArtistsRequestCommon(RequestContext& context, bool id3) +{ + Response response {Response::createOkResponse(context.serverProtocolVersion)}; + + Response::Node& artistsNode {response.createNode(id3 ? "artists" : "indexes")}; + artistsNode.setAttribute("ignoredArticles", ""); + artistsNode.setAttribute("lastModified", reportedDummyDateULong); + + auto transaction {context.dbSession.createSharedTransaction()}; + + User::pointer user {User::find(context.dbSession, context.userId)}; + if (!user) + throw UserNotAuthorizedError {}; + + Artist::FindParameters parameters; + parameters.setSortMethod(ArtistSortMethod::BySortName); + switch (user->getSubsonicArtistListMode()) + { + case SubsonicArtistListMode::AllArtists: + break; + case SubsonicArtistListMode::ReleaseArtists: + parameters.setLinkType(TrackArtistLinkType::ReleaseArtist); + break; + case SubsonicArtistListMode::TrackArtists: + parameters.setLinkType(TrackArtistLinkType::Artist); + break; + } + + Response::Node* currentIndexNode {}; + char currentIndex{}; + + Response::Node* unknownIndexNode {}; + auto getOrCreateUnknownIndexNode {[&] + { + if (!unknownIndexNode) + { + unknownIndexNode = &artistsNode.createArrayChild("index"); + unknownIndexNode->setAttribute("name", "?"); + } + + return unknownIndexNode; + }}; + + auto getOrCreateIndexNode {[&](char first) + { + if (!currentIndexNode || currentIndex != first) + { + currentIndexNode = &artistsNode.createArrayChild("index"); + currentIndexNode->setAttribute("name", std::string {first}); + currentIndex = first; + } + + return currentIndexNode; + }}; + + const RangeResults artists {Artist::find(context.dbSession, parameters)}; + for (const ArtistId artistId : artists.results) + { + const Artist::pointer artist {Artist::find(context.dbSession, artistId)}; + const std::string& sortName {artist->getSortName()}; + + Response::Node* indexNode{}; + if (sortName.empty() || !std::isalpha(sortName[0])) + indexNode = getOrCreateUnknownIndexNode(); + else + indexNode = getOrCreateIndexNode(std::toupper(sortName[0])); + + indexNode->addArrayChild("artist", artistToResponseNode(user, artist, id3)); + } + + return response; +} + +static +Response +handleGetIndexesRequest(RequestContext& context) +{ + return handleGetArtistsRequestCommon(context, false /* no id3 */); +} + static Response handleGetGenresRequest(RequestContext& context) @@ -1103,45 +1141,9 @@ handleGetGenresRequest(RequestContext& context) static Response -handleGetIndexesRequest(RequestContext& context) +handleGetArtistsRequest(RequestContext& context) { - Response response {Response::createOkResponse(context.serverProtocolVersion)}; - - Response::Node& artistsNode {response.createNode("indexes")}; - artistsNode.setAttribute("ignoredArticles", ""); - artistsNode.setAttribute("lastModified", reportedDummyDateULong); - - Response::Node& indexNode {artistsNode.createArrayChild("index")}; - indexNode.setAttribute("name", "?"); - - auto transaction {context.dbSession.createSharedTransaction()}; - - User::pointer user {User::find(context.dbSession, context.userId)}; - if (!user) - throw UserNotAuthorizedError {}; - - Artist::FindParameters parameters; - parameters.setSortMethod(ArtistSortMethod::BySortName); - switch (user->getSubsonicArtistListMode()) - { - case SubsonicArtistListMode::AllArtists: - break; - case SubsonicArtistListMode::ReleaseArtists: - parameters.setLinkType(TrackArtistLinkType::ReleaseArtist); - break; - case SubsonicArtistListMode::TrackArtists: - parameters.setLinkType(TrackArtistLinkType::Artist); - break; - } - - const RangeResults artists {Artist::find(context.dbSession, parameters)}; - for (const ArtistId artistId : artists.results) - { - const Artist::pointer artist {Artist::find(context.dbSession, artistId)}; - indexNode.addArrayChild("artist", artistToResponseNode(user, artist, false /* no id3 */)); - } - - return response; + return handleGetArtistsRequestCommon(context, true /* id3 */); } static @@ -1501,38 +1503,14 @@ handleStarRequest(RequestContext& context) { StarParameters params {getStarParameters(context.parameters)}; - auto transaction {context.dbSession.createSharedTransaction()}; - - User::pointer user {User::find(context.dbSession, context.userId)}; - if (!user) - throw UserNotAuthorizedError {}; - for (const ArtistId id : params.artistIds) - { - Artist::pointer artist {Artist::find(context.dbSession, id)}; - if (!artist) - continue; - - Service::get()->star(user->getId(), artist->getId()); - } + Service::get()->star(context.userId, id); for (const ReleaseId id : params.releaseIds) - { - Release::pointer release {Release::find(context.dbSession, id)}; - if (!release) - continue; - - Service::get()->star(user->getId(), release->getId()); - } + Service::get()->star(context.userId, id); for (const TrackId id : params.trackIds) - { - Track::pointer track {Track::find(context.dbSession, id)}; - if (!track) - continue; - - Service::get()->star(user->getId(), track->getId()); - } + Service::get()->star(context.userId, id); return Response::createOkResponse(context.serverProtocolVersion); } @@ -1557,33 +1535,14 @@ handleUnstarRequest(RequestContext& context) { StarParameters params {getStarParameters(context.parameters)}; - auto transaction {context.dbSession.createSharedTransaction()}; - - User::pointer user {User::find(context.dbSession, context.userId)}; - if (!user) - throw RequestedDataNotFoundError {}; - - for (const ArtistId artistId : params.artistIds) - Service::get()->unstar(context.userId, artistId); + for (const ArtistId id : params.artistIds) + Service::get()->unstar(context.userId, id); for (const ReleaseId id : params.releaseIds) - { - Release::pointer release {Release::find(context.dbSession, id)}; - if (!release) - continue; - - Service::get()->unstar(user->getId(), release->getId()); - } + Service::get()->unstar(context.userId, id); for (const TrackId id : params.trackIds) - { - Track::pointer track {Track::find(context.dbSession, id)}; - if (!track) - continue; - - Service::get()->unstar(user->getId(), track->getId()); - } - + Service::get()->unstar(context.userId, id); return Response::createOkResponse(context.serverProtocolVersion); } diff --git a/src/libs/utils/impl/RecursiveSharedMutex.cpp b/src/libs/utils/impl/RecursiveSharedMutex.cpp index 9dec11de..a763f4b3 100644 --- a/src/libs/utils/impl/RecursiveSharedMutex.cpp +++ b/src/libs/utils/impl/RecursiveSharedMutex.cpp @@ -128,7 +128,7 @@ bool RecursiveSharedMutex::isSharedLocked() { const auto thisThreadId {std::this_thread::get_id()}; - if (_uniqueOwner == thisThreadId ) + if (_uniqueOwner == thisThreadId ) return true; std::scoped_lock lock {_sharedCountMutex}; From 71d6d4825fbfc486747a7040d37365e38c208ca2 Mon Sep 17 00:00:00 2001 From: emeric Date: Tue, 1 Feb 2022 23:17:51 +0100 Subject: [PATCH 47/49] Added album play time. Fixes #186 --- approot/release.xml | 2 +- src/lms/ui/PlayQueue.cpp | 2 +- src/lms/ui/TrackStringUtils.cpp | 2 +- src/lms/ui/TrackStringUtils.hpp | 2 +- src/lms/ui/explore/ReleaseView.cpp | 4 +++- src/lms/ui/explore/TrackListHelpers.cpp | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/approot/release.xml b/approot/release.xml index b440c6c6..724dc1c1 100644 --- a/approot/release.xml +++ b/approot/release.xml @@ -16,7 +16,7 @@

${name}

${}${artists}${} ${}
${tr:Lms.Explore.various-artists}
${
} -
${}${year}${}${} (${orig-year})${}
+
${}${year}${} (${orig-year})${} · ${}${duration}
${clusters} ${play-btn class="Lms-explore-btn Lms-btn"}${more-btn class="Lms-explore-btn Lms-btn"} diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 0e88bd84..f7fb957b 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -455,7 +455,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); } - entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain); + entry->bindString("duration", durationToString(track->getDuration()), Wt::TextFormat::Plain); Wt::WText* playBtn {entry->bindNew("play-btn", Wt::WString::tr("Lms.PlayQueue.template.play-btn"), Wt::TextFormat::XHTML)}; playBtn->clicked().connect([=] diff --git a/src/lms/ui/TrackStringUtils.cpp b/src/lms/ui/TrackStringUtils.cpp index 8bd8cc39..79abede5 100644 --- a/src/lms/ui/TrackStringUtils.cpp +++ b/src/lms/ui/TrackStringUtils.cpp @@ -23,7 +23,7 @@ #include std::string -trackDurationToString(std::chrono::milliseconds msDuration) +durationToString(std::chrono::milliseconds msDuration) { const std::chrono::seconds duration {std::chrono::duration_cast(msDuration)}; diff --git a/src/lms/ui/TrackStringUtils.hpp b/src/lms/ui/TrackStringUtils.hpp index e07aec65..2af0b2fc 100644 --- a/src/lms/ui/TrackStringUtils.hpp +++ b/src/lms/ui/TrackStringUtils.hpp @@ -23,5 +23,5 @@ #include std::string -trackDurationToString(std::chrono::milliseconds msDuration); +durationToString(std::chrono::milliseconds msDuration); diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index 2e3f2deb..13ac9672 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -129,6 +129,8 @@ Release::refreshView() } } + bindString("duration", durationToString(release->getDuration()), Wt::TextFormat::Plain); + refreshReleaseArtists(release); { @@ -250,7 +252,7 @@ Release::refreshView() displayTrackPopupMenu(*moreBtn, trackId, tracksAction); }); - entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain); + entry->bindString("duration", durationToString(track->getDuration()), Wt::TextFormat::Plain); LmsApp->getMediaPlayer().trackLoaded.connect(entry, [=] (TrackId loadedTrackId) { diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index 69055806..49509385 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -87,7 +87,7 @@ namespace UserInterface::TrackListHelpers cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); } - entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain); + entry->bindString("duration", durationToString(track->getDuration()), Wt::TextFormat::Plain); Wt::WText* playBtn = entry->bindNew("play-btn", Wt::WString::tr("Lms.Explore.template.play-btn"), Wt::TextFormat::XHTML); playBtn->clicked().connect([trackId, &tracksAction] From 483cb1bdda6ef84f6b942335df82f7c8045ac99c Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 2 Feb 2022 13:31:23 +0100 Subject: [PATCH 48/49] Added an option to skip duplicated recording MBID. Fixes #180 --- approot/messages.xml | 2 +- approot/messages_fr.xml | 2 +- approot/messages_it.xml | 2 +- approot/messages_zh.xml | 2 +- conf/lms.conf | 3 ++ src/libs/services/database/impl/Track.cpp | 6 ++-- .../include/services/database/Track.hpp | 2 +- .../services/scanner/impl/ScannerService.cpp | 31 ++++++++++++++++--- .../services/scanner/impl/ScannerService.hpp | 1 + .../include/services/scanner/ScannerStats.hpp | 10 +++--- src/lms/ui/admin/ScannerController.cpp | 4 +-- 11 files changed, 46 insertions(+), 19 deletions(-) diff --git a/approot/messages.xml b/approot/messages.xml index 36d4bedd..751053d9 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -68,7 +68,7 @@ Scanned {1} files in {2} on {3} ({4} errors, {5} duplicates) No audio track Duplicated file hash -Duplicated MBID +Duplicated recording MBID Scan now Scanner Status diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml index bf3ba440..808cebb4 100644 --- a/approot/messages_fr.xml +++ b/approot/messages_fr.xml @@ -68,7 +68,7 @@ {1} fichiers scannés en {2} le {3} ({4} erreurs, {5} duplicatas) Pas de piste audio Hash dupliqué -MBID dupliqué +Recording MBID dupliqué Lancer un scan Scanner Statut diff --git a/approot/messages_it.xml b/approot/messages_it.xml index f285e9dc..c3a3b895 100644 --- a/approot/messages_it.xml +++ b/approot/messages_it.xml @@ -67,7 +67,7 @@ Scansionati {1} files in {2} su {3} ({4} errori, {5} duplicati) Nessuna traccia audio Hash doppio -MBID doppio +Recording MBID doppio Scansiona ora Scanner Stato diff --git a/approot/messages_zh.xml b/approot/messages_zh.xml index e7f5dab7..cd3c18be 100644 --- a/approot/messages_zh.xml +++ b/approot/messages_zh.xml @@ -68,7 +68,7 @@ 已扫描 {1} 文件 在 {2} 和 {3} ({4} 错误, {5} 副本) 没有音轨 复制文件哈希值 -复制 MBID +复制 Recording MBID 立即扫描 扫描器 状态 diff --git a/conf/lms.conf b/conf/lms.conf index ab0ba1c9..b44af624 100644 --- a/conf/lms.conf +++ b/conf/lms.conf @@ -70,3 +70,6 @@ cover-max-cache-size = 30; # JPEG quality for covers (range is 1-100) cover-jpeg-quality = 75; + +# Set to true if you want to hide duplicate tracks +scanner-skip-duplicate-recording-mbid = false; diff --git a/src/libs/services/database/impl/Track.cpp b/src/libs/services/database/impl/Track.cpp index a93f2339..6c1c2a94 100644 --- a/src/libs/services/database/impl/Track.cpp +++ b/src/libs/services/database/impl/Track.cpp @@ -186,12 +186,12 @@ Track::findPaths(Session& session, Range range) } RangeResults -Track::findMBIDDuplicates(Session& session, Range range) +Track::findRecordingMBIDDuplicates(Session& session, Range range) { session.checkSharedLocked(); - 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")}; + auto query {session.getDboSession().query( "SELECT track.id FROM track WHERE recording_mbid in (SELECT recording_mbid FROM track WHERE recording_mbid <> '' GROUP BY recording_mbid HAVING COUNT (*) > 1)") + .orderBy("track.release_id,track.disc_number,track.track_number,track.recording_mbid")}; return execQuery(query, range); } diff --git a/src/libs/services/database/include/services/database/Track.hpp b/src/libs/services/database/include/services/database/Track.hpp index 3f0bf099..cc530921 100644 --- a/src/libs/services/database/include/services/database/Track.hpp +++ b/src/libs/services/database/include/services/database/Track.hpp @@ -92,7 +92,7 @@ class Track : public Object static RangeResults find(Session& session, const FindParameters& parameters); static RangeResults findByNameAndReleaseName(Session& session, std::string_view trackName, std::string_view releaseName); static RangeResults findPaths(Session& session, Range range); - static RangeResults findMBIDDuplicates(Session& session, Range range); + static RangeResults findRecordingMBIDDuplicates(Session& session, Range range); static RangeResults findWithRecordingMBIDAndMissingFeatures(Session& session, Range range); // Create utility diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index abac1180..de5c94ae 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -34,6 +34,7 @@ #include "metadata/TagLibParser.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Exception.hpp" +#include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Path.hpp" #include "utils/UUID.hpp" @@ -256,8 +257,11 @@ createScannerService(Db& db, Recommendation::IRecommendationService& recommendat ScannerService::ScannerService(Db& db, Recommendation::IRecommendationService& recommendationService) : _recommendationService {recommendationService} +, _skipDuplicateRecordingMBID {Service::get()->getBool("scanner-skip-duplicate-recording-mbid", false)} , _dbSession {db} { + LMS_LOG(DBUPDATER, INFO) << "skipDuplicateRecordingMBID = " << _skipDuplicateRecordingMBID; + // For now, always use TagLib _metadataParser = std::make_unique(); @@ -719,6 +723,25 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan, Track::pointer track {Track::findByPath(_dbSession, file) }; + // Skip duplicate recording MBID + if (trackInfo->recordingMBID && _skipDuplicateRecordingMBID) + { + for (Track::pointer otherTrack : Track::findByRecordingMBID(_dbSession, *trackInfo->recordingMBID)) + { + if (track && track->getId() == otherTrack->getId()) + continue; + + LMS_LOG(DBUPDATER, DEBUG) << "Skipped '" << file.string() << "' (similar recording MBID in '" << otherTrack->getPath().string() << "')"; + // This recording MBID already exists, just remove what we just scanned + if (track) + { + track.remove(); + stats.deletions++; + } + return; + } + } + // We estimate this is an audio file if: // - we found a least one audio stream // - the duration is not null @@ -1026,14 +1049,14 @@ ScannerService::checkDuplicatedAudioFiles(ScanStats& stats) auto transaction {_dbSession.createSharedTransaction()}; - const RangeResults tracks = Track::findMBIDDuplicates(_dbSession, Range {}); + const RangeResults tracks = Track::findRecordingMBIDDuplicates(_dbSession, Range {}); for (const TrackId trackId : tracks.results) { const Track::pointer track {Track::find(_dbSession, trackId)}; - if (auto trackMBID {track->getTrackMBID()}) + if (auto recordingMBID {track->getRecordingMBID()}) { - LMS_LOG(DBUPDATER, INFO) << "Found duplicated Track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); - stats.duplicates.emplace_back(ScanDuplicate {track->getId(), DuplicateReason::SameMBID}); + LMS_LOG(DBUPDATER, INFO) << "Found duplicated recording MBID [" << recordingMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); + stats.duplicates.emplace_back(ScanDuplicate {track->getId(), DuplicateReason::SameRecordingMBID}); } } diff --git a/src/libs/services/scanner/impl/ScannerService.hpp b/src/libs/services/scanner/impl/ScannerService.hpp index 07adf5c6..0d8ecf91 100644 --- a/src/libs/services/scanner/impl/ScannerService.hpp +++ b/src/libs/services/scanner/impl/ScannerService.hpp @@ -98,6 +98,7 @@ namespace Scanner std::atomic _abortScan {}; Wt::WIOService _ioService; boost::asio::system_timer _scheduleTimer {_ioService}; + const bool _skipDuplicateRecordingMBID {}; Events _events; std::chrono::system_clock::time_point _lastScanInProgressEmit {}; Database::Session _dbSession; diff --git a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp index 1b5e3de4..53f79d52 100644 --- a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp +++ b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp @@ -30,16 +30,16 @@ namespace Scanner { enum class ScanErrorType { - CannotReadFile, // cannot read file - CannotParseFile, // cannot parse file - NoAudioTrack, // no audio track found - BadDuration, // bad duration + CannotReadFile, // cannot read file + CannotParseFile, // cannot parse file + NoAudioTrack, // no audio track found + BadDuration, // bad duration }; enum class DuplicateReason { SameHash, - SameMBID, + SameRecordingMBID, }; struct ScanError diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index 764d6bb8..6b5153db 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -96,7 +96,7 @@ class ReportResource : public Wt::WResource response.out() << track->getPath().string(); if (auto mbid {track->getTrackMBID()}) - response.out() << " (Track MBID " << mbid->getAsString() << ")"; + response.out() << " (Recording MBID " << mbid->getAsString() << ")"; response.out() << " - " << duplicateReasonToWString(duplicate.reason).toUTF8() << '\n'; } @@ -122,7 +122,7 @@ class ReportResource : public Wt::WResource switch (reason) { case Scanner::DuplicateReason::SameHash: return Wt::WString::tr("Lms.Admin.ScannerController.same-hash"); - case Scanner::DuplicateReason::SameMBID: return Wt::WString::tr("Lms.Admin.ScannerController.same-mbid"); + case Scanner::DuplicateReason::SameRecordingMBID: return Wt::WString::tr("Lms.Admin.ScannerController.same-mbid"); } return "?"; } From b93a78e1f4fb31d99bf8c7edf2b34de90130c859 Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 2 Feb 2022 13:58:04 +0100 Subject: [PATCH 49/49] CodeFactor review --- src/libs/services/database/impl/SqlQuery.hpp | 1 - .../services/database/include/services/database/TrackList.hpp | 1 - src/libs/som/test/SomTest.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/src/libs/services/database/impl/SqlQuery.hpp b/src/libs/services/database/impl/SqlQuery.hpp index 5a11fe99..bb15408a 100644 --- a/src/libs/services/database/impl/SqlQuery.hpp +++ b/src/libs/services/database/impl/SqlQuery.hpp @@ -68,7 +68,6 @@ class GroupByStatement private: std::string _statement; // SELECT statement - }; class SelectStatement diff --git a/src/libs/services/database/include/services/database/TrackList.hpp b/src/libs/services/database/include/services/database/TrackList.hpp index ebb61772..fe44db40 100644 --- a/src/libs/services/database/include/services/database/TrackList.hpp +++ b/src/libs/services/database/include/services/database/TrackList.hpp @@ -127,7 +127,6 @@ class TrackList : public Object Wt::Dbo::ptr _user; Wt::Dbo::collection> _entries; - }; class TrackListEntry : public Object diff --git a/src/libs/som/test/SomTest.cpp b/src/libs/som/test/SomTest.cpp index 74186d85..68a71285 100644 --- a/src/libs/som/test/SomTest.cpp +++ b/src/libs/som/test/SomTest.cpp @@ -51,7 +51,6 @@ TEST(som, Matrix) TEST(som, InputVector) { - { InputVector test1 {2}; test1[0] = 0;