From 441aed622c70acec55257a9301fa307d436948b6 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 20 Sep 2021 23:53:38 +0200 Subject: [PATCH] Made database ID manipulations safer --- CMakeLists.txt | 3 + src/libs/auth/impl/AuthServiceBase.cpp | 6 +- src/libs/auth/impl/AuthServiceBase.hpp | 4 +- src/libs/auth/impl/AuthTokenService.cpp | 6 +- src/libs/auth/impl/AuthTokenService.hpp | 4 +- src/libs/auth/impl/PasswordServiceBase.cpp | 2 +- .../http-headers/HttpHeadersEnvService.cpp | 4 +- .../impl/internal/InternalPasswordService.cpp | 4 +- .../impl/internal/InternalPasswordService.hpp | 2 +- src/libs/auth/impl/pam/PAMPasswordService.cpp | 2 +- src/libs/auth/impl/pam/PAMPasswordService.hpp | 2 +- .../auth/include/auth/IAuthTokenService.hpp | 6 +- src/libs/auth/include/auth/IEnvService.hpp | 2 +- .../auth/include/auth/IPasswordService.hpp | 4 +- src/libs/cover/impl/CoverArtGrabber.cpp | 26 +- src/libs/cover/impl/CoverArtGrabber.hpp | 27 +- .../cover/include/cover/ICoverArtGrabber.hpp | 4 +- src/libs/database/impl/Artist.cpp | 166 ++++----- src/libs/database/impl/Cluster.cpp | 82 ++--- src/libs/database/impl/Release.cpp | 222 ++++++----- src/libs/database/impl/ScanSettings.cpp | 14 +- src/libs/database/impl/Track.cpp | 214 +++++------ src/libs/database/impl/TrackArtistLink.cpp | 14 +- src/libs/database/impl/TrackBookmark.cpp | 35 +- src/libs/database/impl/TrackFeatures.cpp | 8 +- src/libs/database/impl/TrackList.cpp | 226 ++++++------ src/libs/database/impl/Traits.hpp | 60 +++ src/libs/database/impl/User.cpp | 81 ++-- src/libs/database/include/database/Artist.hpp | 53 ++- .../database/include/database/Cluster.hpp | 36 +- .../database/include/database/Release.hpp | 43 +-- .../include/database/ScanSettings.hpp | 13 +- src/libs/database/include/database/Track.hpp | 61 ++- .../include/database/TrackArtistLink.hpp | 14 +- .../include/database/TrackBookmark.hpp | 20 +- .../include/database/TrackFeatures.hpp | 11 +- .../database/include/database/TrackList.hpp | 65 ++-- src/libs/database/include/database/Types.hpp | 104 +++++- src/libs/database/include/database/User.hpp | 44 +-- src/libs/recommendation/CMakeLists.txt | 4 +- src/libs/recommendation/impl/Engine.cpp | 31 +- src/libs/recommendation/impl/Engine.hpp | 12 +- ...rCreator.hpp => FeaturesEngineCreator.hpp} | 2 +- src/libs/recommendation/impl/IClassifier.hpp | 18 +- .../impl/clusters/ClustersClassifier.cpp | 37 +- .../impl/clusters/ClustersClassifier.hpp | 10 +- .../impl/features/FeaturesClassifier.hpp | 117 ------ ...turesClassifier.cpp => FeaturesEngine.cpp} | 263 +++++-------- .../impl/features/FeaturesEngine.hpp | 216 +++++++++++ ...ifierCache.cpp => FeaturesEngineCache.cpp} | 34 +- ...ifierCache.hpp => FeaturesEngineCache.hpp} | 16 +- .../include/recommendation/IEngine.hpp | 25 +- .../recommendation/IRecommendation.hpp} | 37 +- src/libs/scanner/impl/Scanner.cpp | 20 +- src/libs/scanner/impl/Scanner.hpp | 9 +- .../scanner/include/scanner/ScannerStats.hpp | 2 +- src/libs/scrobbling/impl/IScrobbler.hpp | 3 +- src/libs/scrobbling/impl/Scrobbling.cpp | 66 ++-- src/libs/scrobbling/impl/Scrobbling.hpp | 40 +- .../impl/internal/InternalScrobbler.cpp | 6 +- .../impl/internal/InternalScrobbler.hpp | 7 +- .../listenbrainz/ListenBrainzScrobbler.cpp | 2 +- .../listenbrainz/ListenBrainzScrobbler.hpp | 2 +- .../impl/listenbrainz/ListensSynchronizer.cpp | 14 +- .../impl/listenbrainz/ListensSynchronizer.hpp | 10 +- .../scrobbling/impl/listenbrainz/Utils.cpp | 2 +- .../scrobbling/impl/listenbrainz/Utils.hpp | 7 +- .../include/scrobbling/IScrobbling.hpp | 38 +- .../scrobbling/include/scrobbling/Listen.hpp | 4 +- src/libs/som/impl/Network.cpp | 3 +- src/libs/som/include/som/Network.hpp | 3 +- src/libs/subsonic/CMakeLists.txt | 1 - src/libs/subsonic/impl/ParameterParsing.hpp | 15 +- src/libs/subsonic/impl/RequestContext.hpp | 2 +- src/libs/subsonic/impl/Stream.cpp | 10 +- src/libs/subsonic/impl/SubsonicId.cpp | 175 ++++++--- src/libs/subsonic/impl/SubsonicId.hpp | 56 ++- src/libs/subsonic/impl/SubsonicResource.cpp | 347 +++++++----------- src/libs/utils/include/utils/Utils.hpp | 23 +- src/lms/ui/Auth.cpp | 10 +- src/lms/ui/Auth.hpp | 4 +- src/lms/ui/LmsApplication.cpp | 28 +- src/lms/ui/LmsApplication.hpp | 18 +- src/lms/ui/LmsApplicationManager.hpp | 2 +- src/lms/ui/MediaPlayer.cpp | 8 +- src/lms/ui/MediaPlayer.hpp | 18 +- src/lms/ui/PlayQueue.cpp | 27 +- src/lms/ui/PlayQueue.hpp | 14 +- src/lms/ui/PlayQueueAction.hpp | 5 +- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/admin/InitWizardView.cpp | 2 +- src/lms/ui/admin/UserView.cpp | 10 +- src/lms/ui/admin/UsersView.cpp | 4 +- src/lms/ui/explore/ArtistCollector.cpp | 2 +- src/lms/ui/explore/ArtistCollector.hpp | 6 +- src/lms/ui/explore/ArtistListHelpers.cpp | 4 +- src/lms/ui/explore/ArtistListHelpers.hpp | 4 +- src/lms/ui/explore/ArtistView.cpp | 17 +- src/lms/ui/explore/ArtistView.hpp | 14 +- src/lms/ui/explore/Explore.cpp | 30 +- src/lms/ui/explore/Explore.hpp | 8 +- src/lms/ui/explore/Filters.cpp | 28 +- src/lms/ui/explore/Filters.hpp | 7 +- src/lms/ui/explore/ReleaseCollector.cpp | 6 +- src/lms/ui/explore/ReleaseCollector.hpp | 8 +- src/lms/ui/explore/ReleaseListHelpers.cpp | 4 +- src/lms/ui/explore/ReleaseListHelpers.hpp | 5 +- src/lms/ui/explore/ReleasePopup.cpp | 4 +- src/lms/ui/explore/ReleasePopup.hpp | 4 +- src/lms/ui/explore/ReleaseView.cpp | 23 +- src/lms/ui/explore/ReleaseView.hpp | 12 +- src/lms/ui/explore/ReleasesView.cpp | 2 +- src/lms/ui/explore/ReleasesView.hpp | 4 +- src/lms/ui/explore/SearchView.hpp | 2 +- src/lms/ui/explore/TrackCollector.cpp | 10 +- src/lms/ui/explore/TrackCollector.hpp | 8 +- src/lms/ui/explore/TrackListHelpers.cpp | 8 +- src/lms/ui/explore/TrackListHelpers.hpp | 3 +- src/lms/ui/explore/TrackPopup.cpp | 4 +- src/lms/ui/explore/TrackPopup.hpp | 4 +- src/lms/ui/explore/TracksView.cpp | 2 +- src/lms/ui/explore/TracksView.hpp | 4 +- src/lms/ui/resource/AudioFileResource.cpp | 8 +- src/lms/ui/resource/AudioFileResource.hpp | 2 +- .../ui/resource/AudioTranscodeResource.cpp | 10 +- .../ui/resource/AudioTranscodeResource.hpp | 6 +- src/lms/ui/resource/CoverResource.cpp | 12 +- src/lms/ui/resource/CoverResource.hpp | 4 +- src/lms/ui/resource/DownloadResource.cpp | 8 +- src/lms/ui/resource/DownloadResource.hpp | 12 +- src/test/database/Artist.cpp | 64 ++-- src/test/database/Cluster.cpp | 174 ++++----- src/test/database/Common.hpp | 8 +- src/test/database/DatabaseTest.cpp | 84 ++--- src/test/database/Track.cpp | 10 +- src/test/som/SomTest.cpp | 2 +- src/tools/cover/LmsCover.cpp | 6 +- .../recommendation/LmsRecommendation.cpp | 36 +- 138 files changed, 2164 insertions(+), 2054 deletions(-) create mode 100644 src/libs/database/impl/Traits.hpp rename src/libs/recommendation/impl/{FeaturesClassifierCreator.hpp => FeaturesEngineCreator.hpp} (93%) delete mode 100644 src/libs/recommendation/impl/features/FeaturesClassifier.hpp rename src/libs/recommendation/impl/features/{FeaturesClassifier.cpp => FeaturesEngine.cpp} (56%) create mode 100644 src/libs/recommendation/impl/features/FeaturesEngine.hpp rename src/libs/recommendation/impl/features/{FeaturesClassifierCache.cpp => FeaturesEngineCache.cpp} (85%) rename src/libs/recommendation/impl/features/{FeaturesClassifierCache.hpp => FeaturesEngineCache.hpp} (66%) rename src/libs/{subsonic/impl/ParameterParsing.cpp => recommendation/include/recommendation/IRecommendation.hpp} (64%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b34a72cc..149ebd7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules/) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) +if (UNIX) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") +endif () include(CTest) find_package(PkgConfig REQUIRED) diff --git a/src/libs/auth/impl/AuthServiceBase.cpp b/src/libs/auth/impl/AuthServiceBase.cpp index f31f737e..b4ab450a 100644 --- a/src/libs/auth/impl/AuthServiceBase.cpp +++ b/src/libs/auth/impl/AuthServiceBase.cpp @@ -25,7 +25,7 @@ namespace Auth { - Database::IdType + Database::UserId AuthServiceBase::getOrCreateUser(Database::Session& session, std::string_view loginName) { auto transaction {session.createUniqueTransaction()}; @@ -41,11 +41,11 @@ namespace Auth user.modify()->setType(type); } - return user.id(); + return user->getId(); } void - AuthServiceBase::onUserAuthenticated(Database::Session& session, Database::IdType userId) + AuthServiceBase::onUserAuthenticated(Database::Session& session, Database::UserId userId) { auto transaction {session.createUniqueTransaction()}; Database::User::pointer user {Database::User::getById(session, userId)}; diff --git a/src/libs/auth/impl/AuthServiceBase.hpp b/src/libs/auth/impl/AuthServiceBase.hpp index 8a7672e1..032d8f84 100644 --- a/src/libs/auth/impl/AuthServiceBase.hpp +++ b/src/libs/auth/impl/AuthServiceBase.hpp @@ -32,7 +32,7 @@ namespace Auth class AuthServiceBase { protected: - Database::IdType getOrCreateUser(Database::Session& session, std::string_view loginName); - void onUserAuthenticated(Database::Session& session, Database::IdType userId); + Database::UserId getOrCreateUser(Database::Session& session, std::string_view loginName); + void onUserAuthenticated(Database::Session& session, Database::UserId userId); }; } diff --git a/src/libs/auth/impl/AuthTokenService.cpp b/src/libs/auth/impl/AuthTokenService.cpp index b22a5ac1..89ad8870 100644 --- a/src/libs/auth/impl/AuthTokenService.cpp +++ b/src/libs/auth/impl/AuthTokenService.cpp @@ -45,7 +45,7 @@ namespace Auth } std::string - AuthTokenService::createAuthToken(Database::Session& session, Database::IdType userId, const Wt::WDateTime& expiry) + AuthTokenService::createAuthToken(Database::Session& session, Database::UserId userId, const Wt::WDateTime& expiry) { const std::string secret {Wt::WRandom::generateId(32)}; const std::string secretHash {sha1Function.compute(secret, {})}; @@ -86,7 +86,7 @@ namespace Auth LMS_LOG(UI, DEBUG) << "Found auth token for user '" << authToken->getUser()->getLoginName() << "'!"; - AuthTokenService::AuthTokenProcessResult::AuthTokenInfo res {authToken->getUser().id(), authToken->getExpiry()}; + AuthTokenService::AuthTokenProcessResult::AuthTokenInfo res {authToken->getUser()->getId(), authToken->getExpiry()}; authToken.remove(); return res; @@ -123,7 +123,7 @@ namespace Auth } void - AuthTokenService::clearAuthTokens(Database::Session& session, Database::IdType userId) + AuthTokenService::clearAuthTokens(Database::Session& session, Database::UserId userId) { auto transaction {session.createUniqueTransaction()}; diff --git a/src/libs/auth/impl/AuthTokenService.hpp b/src/libs/auth/impl/AuthTokenService.hpp index 92dff681..088f82ee 100644 --- a/src/libs/auth/impl/AuthTokenService.hpp +++ b/src/libs/auth/impl/AuthTokenService.hpp @@ -44,8 +44,8 @@ namespace Auth private: AuthTokenProcessResult processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) override; - std::string createAuthToken(Database::Session& session, Database::IdType userId, const Wt::WDateTime& expiry) override; - void clearAuthTokens(Database::Session& session, Database::IdType userId) override; + std::string createAuthToken(Database::Session& session, Database::UserId userId, const Wt::WDateTime& expiry) override; + void clearAuthTokens(Database::Session& session, Database::UserId userId) override; std::shared_mutex _mutex; LoginThrottler _loginThrottler; diff --git a/src/libs/auth/impl/PasswordServiceBase.cpp b/src/libs/auth/impl/PasswordServiceBase.cpp index f1d53f6d..02a9c63d 100644 --- a/src/libs/auth/impl/PasswordServiceBase.cpp +++ b/src/libs/auth/impl/PasswordServiceBase.cpp @@ -84,7 +84,7 @@ namespace Auth { _loginThrottler.onGoodClientAttempt(clientAddress); - const Database::IdType userId {getOrCreateUser(session, loginName)}; + const Database::UserId userId {getOrCreateUser(session, loginName)}; onUserAuthenticated(session, userId); return {CheckResult::State::Granted, userId}; } diff --git a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp index 93f0b584..3db1fb4a 100644 --- a/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp +++ b/src/libs/auth/impl/http-headers/HttpHeadersEnvService.cpp @@ -43,7 +43,7 @@ namespace Auth LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; - const Database::IdType userId {getOrCreateUser(session, loginName)}; + const Database::UserId userId {getOrCreateUser(session, loginName)}; onUserAuthenticated(session, userId); return {CheckResult::State::Granted, userId}; } @@ -57,7 +57,7 @@ namespace Auth LMS_LOG(AUTH, DEBUG) << "Extracted login name = '" << loginName << "' from HTTP header"; - const Database::IdType userId {getOrCreateUser(session, loginName)}; + const Database::UserId userId {getOrCreateUser(session, loginName)}; onUserAuthenticated(session, userId); return {CheckResult::State::Granted, userId}; } diff --git a/src/libs/auth/impl/internal/InternalPasswordService.cpp b/src/libs/auth/impl/internal/InternalPasswordService.cpp index 82acd56b..a021dd3c 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.cpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.cpp @@ -96,13 +96,13 @@ namespace Auth } void - InternalPasswordService::setPassword(Database::Session& session, Database::IdType userId, std::string_view newPassword) + InternalPasswordService::setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) { const Database::User::PasswordHash passwordHash {hashPassword(newPassword)}; auto transaction {session.createUniqueTransaction()}; - const Database::User::pointer user {Database::User::getById(session, userId)}; + Database::User::pointer user {Database::User::getById(session, userId)}; if (!user) throw Exception {"User not found!"}; diff --git a/src/libs/auth/impl/internal/InternalPasswordService.hpp b/src/libs/auth/impl/internal/InternalPasswordService.hpp index 6e599204..33cdb5cd 100644 --- a/src/libs/auth/impl/internal/InternalPasswordService.hpp +++ b/src/libs/auth/impl/internal/InternalPasswordService.hpp @@ -42,7 +42,7 @@ namespace Auth bool canSetPasswords() const override; PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; - void setPassword(Database::Session& session, Database::IdType userId, std::string_view newPassword) override; + void setPassword(Database::Session& session, 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 47b75932..1b25f537 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.cpp @@ -193,7 +193,7 @@ namespace Auth } void - PAMPasswordService::setPassword(Database::Session&, Database::IdType, std::string_view) + PAMPasswordService::setPassword(Database::Session&, 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 b6213e17..4b661b4b 100644 --- a/src/libs/auth/impl/pam/PAMPasswordService.hpp +++ b/src/libs/auth/impl/pam/PAMPasswordService.hpp @@ -38,7 +38,7 @@ namespace Auth bool canSetPasswords() const override; PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; void setPassword(Database::Session& session, - Database::IdType userId, + 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 44744198..e57ee6a9 100644 --- a/src/libs/auth/include/auth/IAuthTokenService.hpp +++ b/src/libs/auth/include/auth/IAuthTokenService.hpp @@ -54,7 +54,7 @@ namespace Auth struct AuthTokenInfo { - Database::IdType userId; + Database::UserId userId; Wt::WDateTime expiry; }; @@ -66,8 +66,8 @@ namespace Auth virtual AuthTokenProcessResult processAuthToken(Database::Session& session, const boost::asio::ip::address& clientAddress, std::string_view tokenValue) = 0; // Returns a one time token - virtual std::string createAuthToken(Database::Session& session, Database::IdType userid, const Wt::WDateTime& expiry) = 0; - virtual void clearAuthTokens(Database::Session& session, Database::IdType userid) = 0; + 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; }; std::unique_ptr createAuthTokenService(std::size_t maxThrottlerEntryCount); diff --git a/src/libs/auth/include/auth/IEnvService.hpp b/src/libs/auth/include/auth/IEnvService.hpp index 02a6d4e2..b001d602 100644 --- a/src/libs/auth/include/auth/IEnvService.hpp +++ b/src/libs/auth/include/auth/IEnvService.hpp @@ -57,7 +57,7 @@ namespace Auth }; State state {State::Denied}; - std::optional userId {}; + std::optional userId {}; }; virtual CheckResult processEnv(Database::Session& session, const Wt::WEnvironment& env) = 0; diff --git a/src/libs/auth/include/auth/IPasswordService.hpp b/src/libs/auth/include/auth/IPasswordService.hpp index a5b9a0bc..69278fc5 100644 --- a/src/libs/auth/include/auth/IPasswordService.hpp +++ b/src/libs/auth/include/auth/IPasswordService.hpp @@ -53,7 +53,7 @@ namespace Auth Throttled, }; State state {State::Denied}; - std::optional userId {}; + std::optional userId {}; std::optional expiry {}; }; virtual CheckResult checkUserPassword(Database::Session& session, @@ -70,7 +70,7 @@ namespace Auth MustMatchLoginName, }; virtual PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const = 0; - virtual void setPassword(Database::Session& session, Database::IdType userId, std::string_view newPassword) = 0; + virtual void setPassword(Database::Session& session, Database::UserId userId, std::string_view newPassword) = 0; }; std::unique_ptr createPasswordService(std::string_view authPasswordBackend, std::size_t maxThrottlerEntryCount, IAuthTokenService& authTokenService); diff --git a/src/libs/cover/impl/CoverArtGrabber.cpp b/src/libs/cover/impl/CoverArtGrabber.cpp index 1227fcd6..226d28a1 100644 --- a/src/libs/cover/impl/CoverArtGrabber.cpp +++ b/src/libs/cover/impl/CoverArtGrabber.cpp @@ -45,11 +45,11 @@ namespace bool hasCover {}; bool isMultiDisc {}; std::filesystem::path trackPath; - std::optional releaseId; + std::optional releaseId; }; std::optional - getTrackInfo(Database::Session& dbSession, Database::IdType trackId) + getTrackInfo(Database::Session& dbSession, Database::TrackId trackId) { std::optional res; @@ -66,7 +66,7 @@ namespace if (const Database::Release::pointer& release {track->getRelease()}) { - res->releaseId = release.id(); + res->releaseId = release->getId(); if (release->getTotalDisc() > 1) res->isMultiDisc = true; } @@ -75,7 +75,6 @@ namespace } } - namespace CoverArt { static @@ -101,7 +100,7 @@ Grabber::Grabber(const std::filesystem::path& execPath, : _defaultCoverPath {defaultCoverPath} , _maxCacheSize {maxCacheSize} , _maxFileSize {maxFileSize} - , _jpegQuality {clamp(jpegQuality, 1, 100)} + , _jpegQuality {Utils::clamp(jpegQuality, 1, 100)} { LMS_LOG(COVER, INFO) << "Default cover path = '" << _defaultCoverPath.string() << "'"; LMS_LOG(COVER, INFO) << "Max cache size = " << _maxCacheSize; @@ -314,20 +313,17 @@ Grabber::getFromTrack(const std::filesystem::path& p, ImageSize width) const } std::shared_ptr -Grabber::getFromTrack(Database::Session& dbSession, Database::IdType trackId, ImageSize width) +Grabber::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width) { return getFromTrack(dbSession, trackId, width, true /* allow release fallback*/); } - - - std::shared_ptr -Grabber::getFromTrack(Database::Session& dbSession, Database::IdType trackId, ImageSize width, bool allowReleaseFallback) +Grabber::getFromTrack(Database::Session& dbSession, Database::TrackId trackId, ImageSize width, bool allowReleaseFallback) { using namespace Database; - const CacheEntryDesc cacheEntryDesc {CacheEntryDesc::Type::Track, trackId, width}; + const CacheEntryDesc cacheEntryDesc {trackId, width}; std::shared_ptr cover {loadFromCache(cacheEntryDesc)}; if (cover) @@ -361,9 +357,9 @@ Grabber::getFromTrack(Database::Session& dbSession, Database::IdType trackId, Im } std::shared_ptr -Grabber::getFromRelease(Database::Session& session, Database::IdType releaseId, ImageSize width) +Grabber::getFromRelease(Database::Session& session, Database::ReleaseId releaseId, ImageSize width) { - const CacheEntryDesc cacheEntryDesc {CacheEntryDesc::Type::Release, releaseId, width}; + const CacheEntryDesc cacheEntryDesc {releaseId, width}; std::shared_ptr cover {loadFromCache(cacheEntryDesc)}; if (cover) @@ -371,7 +367,7 @@ Grabber::getFromRelease(Database::Session& session, Database::IdType releaseId, struct ReleaseInfo { - Database::IdType firstTrackId; + Database::TrackId firstTrackId; std::filesystem::path releaseDirectory; }; @@ -386,7 +382,7 @@ Grabber::getFromRelease(Database::Session& session, Database::IdType releaseId, if (const auto firstTrack {release->getFirstTrack()}) { res = ReleaseInfo {}; - res->firstTrackId = firstTrack.id(); + res->firstTrackId = firstTrack->getId(); res->releaseDirectory = firstTrack->getPath().parent_path(); } } diff --git a/src/libs/cover/impl/CoverArtGrabber.hpp b/src/libs/cover/impl/CoverArtGrabber.hpp index abe02fbc..e5c33fb8 100644 --- a/src/libs/cover/impl/CoverArtGrabber.hpp +++ b/src/libs/cover/impl/CoverArtGrabber.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "cover/ICoverArtGrabber.hpp" @@ -46,20 +47,12 @@ namespace CoverArt { struct CacheEntryDesc { - enum class Type - { - Track, - Release, - }; - - Type type; - Database::IdType id; + std::variant id; std::size_t size; bool operator==(const CacheEntryDesc& other) const { - return type == other.type - && id == other.id + return id == other.id && size == other.size; } }; @@ -75,8 +68,12 @@ namespace std public: size_t operator()(const CoverArt::CacheEntryDesc& e) const { - size_t h = std::hash()(static_cast(e.type)); - h ^= std::hash()(e.id) << 1; + size_t h {}; + std::visit([&](auto id) + { + using IdType = std::decay_t; + h ^= std::hash()(id); + }, e.id); h ^= std::hash()(e.size) << 1; return h; } @@ -101,11 +98,11 @@ namespace CoverArt Grabber& operator=(Grabber&&) = delete; private: - std::shared_ptr getFromTrack(Database::Session& dbSession, Database::IdType trackId, ImageSize width) override; - std::shared_ptr getFromRelease(Database::Session& dbSession, Database::IdType releaseId, ImageSize width) override; + 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; void flushCache() override; - std::shared_ptr getFromTrack(Database::Session& dbSession, Database::IdType trackId, ImageSize width, bool allowReleaseFallback); + 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; diff --git a/src/libs/cover/include/cover/ICoverArtGrabber.hpp b/src/libs/cover/include/cover/ICoverArtGrabber.hpp index 8f9671da..544cee7a 100644 --- a/src/libs/cover/include/cover/ICoverArtGrabber.hpp +++ b/src/libs/cover/include/cover/ICoverArtGrabber.hpp @@ -37,8 +37,8 @@ namespace CoverArt public: virtual ~IGrabber() = default; - virtual std::shared_ptr getFromTrack(Database::Session& dbSession, Database::IdType trackId, ImageSize width) = 0; - virtual std::shared_ptr getFromRelease(Database::Session& dbSession, Database::IdType releaseId, ImageSize width) = 0; + 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 void flushCache() = 0; }; diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/database/impl/Artist.cpp index 36c6dede..5c620561 100644 --- a/src/libs/database/impl/Artist.cpp +++ b/src/libs/database/impl/Artist.cpp @@ -28,6 +28,7 @@ #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "Utils.hpp" +#include "Traits.hpp" namespace Database { @@ -37,7 +38,6 @@ Artist::Artist(const std::string& name, const std::optional& MBID) _sortName {_name}, _MBID {MBID ? MBID->getAsString() : ""} { - } std::vector @@ -45,7 +45,7 @@ Artist::getByName(Session& session, const std::string& name) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find() + Wt::Dbo::collection> res = session.getDboSession().find() .where("name = ?").bind(std::string {name, 0, _maxNameLength}) .orderBy("LENGTH(mbid) DESC"); // put mbid entries first @@ -56,14 +56,14 @@ Artist::pointer Artist::getByMBID(Session& session, const UUID& mbid) { session.checkSharedLocked(); - return session.getDboSession().find().where("mbid = ?").bind(std::string {mbid.getAsString()}); + return session.getDboSession().find().where("mbid = ?").bind(std::string {mbid.getAsString()}).resultValue(); } Artist::pointer -Artist::getById(Session& session, IdType id) +Artist::getById(Session& session, ArtistId id) { session.checkSharedLocked(); - return session.getDboSession().find().where("id = ?").bind(id); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } Artist::pointer @@ -82,7 +82,7 @@ static Wt::Dbo::Query createQuery(Session& session, const std::string& queryStr, - const std::set& clusterIds, + const std::vector& clusterIds, const std::vector& keywords, std::optional linkType) { @@ -125,7 +125,7 @@ createQuery(Session& session, " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"; WhereClause clusterClause; - for (const IdType clusterId : clusterIds) + for (const ClusterId clusterId : clusterIds) { clusterClause.Or(WhereClause("c.id = ?")); query.bind(clusterId); @@ -145,7 +145,7 @@ Artist::getAll(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find(); + Wt::Dbo::collection> res = session.getDboSession().find(); return std::vector(res.begin(), res.end()); } @@ -167,7 +167,7 @@ Artist::getAll(Session& session, SortMethod sortMethod) break; } - Wt::Dbo::collection res = query; + Wt::Dbo::collection> res = query; return std::vector(res.begin(), res.end()); } @@ -177,7 +177,7 @@ Artist::getAll(Session& session, SortMethod sortMethod, std::optional ran { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT a FROM Artist a", {}, {}, std::nullopt)}; + auto query {createQuery>(session, "SELECT a FROM Artist a", {}, {}, std::nullopt)}; switch (sortMethod) { @@ -191,11 +191,11 @@ Artist::getAll(Session& session, SortMethod sortMethod, std::optional ran break; } - Wt::Dbo::collection collection = query + Wt::Dbo::collection> collection = query .limit(range ? static_cast(range->limit) + 1 : -1) .offset(range ? static_cast(range->offset) : -1); - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res (collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -207,27 +207,27 @@ Artist::getAll(Session& session, SortMethod sortMethod, std::optional ran return res; } -std::vector +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()); + 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::set& clusters, std::optional linkType, std::optional size) +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)}; + auto query {createQuery(session, "SELECT DISTINCT a.id from artist a", clusters, {}, linkType)}; - Wt::Dbo::collection res = query + Wt::Dbo::collection res = query .orderBy("RANDOM()") .limit(size ? static_cast(*size) : -1); - return std::vector(res.begin(), res.end()); + return std::vector(res.begin(), res.end()); } @@ -240,22 +240,22 @@ Artist::getAllOrphans(Session& session) return std::vector(res.begin(), res.end()); } -std::vector +std::vector Artist::getAllIdsWithClusters(Session& session, std::optional limit) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query + 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()); + return std::vector(res.begin(), res.end()); } std::vector -Artist::getByClusters(Session& session, const std::set& clusters, SortMethod sortMethod) +Artist::getByClusters(Session& session, const std::vector& clusters, SortMethod sortMethod) { assert(!clusters.empty()); @@ -266,7 +266,7 @@ Artist::getByClusters(Session& session, const std::set& clusters, SortMe std::vector Artist::getByFilter(Session& session, - const std::set& clusters, + const std::vector& clusters, const std::vector& keywords, std::optional linkType, SortMethod sortMethod, @@ -275,7 +275,7 @@ Artist::getByFilter(Session& session, { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT DISTINCT a from artist a", clusters, keywords, linkType)}; + auto query {createQuery>(session, "SELECT DISTINCT a from artist a", clusters, keywords, linkType)}; switch (sortMethod) { case Artist::SortMethod::None: @@ -288,11 +288,11 @@ Artist::getByFilter(Session& session, break; } - Wt::Dbo::collection collection = query + Wt::Dbo::collection> collection = query .limit(range ? static_cast(range->limit) + 1 : -1) .offset(range ? static_cast(range->offset) : -1); - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res (collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { @@ -308,23 +308,23 @@ Artist::getByFilter(Session& session, std::vector Artist::getLastWritten(Session& session, std::optional after, - const std::set& clusters, + 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)}; + 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 + 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); - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res (collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { @@ -340,14 +340,14 @@ Artist::getLastWritten(Session& session, std::vector Artist::getStarred(Session& session, User::pointer user, - const std::set& clusters, + 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)}; + auto query {createQuery>(session, "SELECT DISTINCT a from artist a", clusters, {}, linkType)}; { std::ostringstream oss; @@ -355,7 +355,7 @@ Artist::getStarred(Session& session, " 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.id()); + query.bind(user->getId()); query.where(oss.str()); } @@ -371,12 +371,12 @@ Artist::getStarred(Session& session, break; } - Wt::Dbo::collection collection = query + Wt::Dbo::collection> collection = query .groupBy("a.id") .limit(range ? static_cast(range->limit) + 1 : -1) .offset(range ? static_cast(range->offset) : -1); - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res (collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { @@ -389,11 +389,9 @@ Artist::getStarred(Session& session, return std::vector(res.begin(), res.end()); } -std::vector> -Artist::getReleases(const std::set& clusterIds) const +std::vector +Artist::getReleases(const std::vector& clusterIds) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); WhereClause where; @@ -409,12 +407,12 @@ Artist::getReleases(const std::set& clusterIds) const WhereClause clusterClause; for (auto id : clusterIds) - clusterClause.Or(WhereClause("c.id = ?")).bind(std::to_string(id)); + clusterClause.Or(WhereClause("c.id = ?")).bind(id.toString()); where.And(clusterClause); } - where.And(WhereClause("a.id = ?")).bind(std::to_string(id())); + where.And(WhereClause("a.id = ?")).bind(getId().toString()); oss << " " << where.get(); @@ -423,56 +421,48 @@ Artist::getReleases(const std::set& clusterIds) const oss << " ORDER BY t.year DESC, r.name COLLATE NOCASE"; - Wt::Dbo::Query query = session()->query( oss.str() ); + auto query {session()->query>(oss.str())}; for (const std::string& bindArg : where.getBindArgs()) query.bind(bindArg); - Wt::Dbo::collection> res = query; - - return std::vector>(res.begin(), res.end()); + auto res {query.resultList()}; + return std::vector(res.begin(), res.end()); } std::size_t Artist::getReleaseCount() const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); int res = session()->query("SELECT COUNT(DISTINCT r.id) FROM release r 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 INNER JOIN track t ON t.release_id = r.id") - .where("a.id = ?").bind(self()->id()); + .where("a.id = ?").bind(getId()); return res; } -std::vector> +std::vector Artist::getTracks(std::optional linkType) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); auto query {session()->query>("SELECT DISTINCT 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(self()->id()) + .where("a.id = ?").bind(getId()) .orderBy("t.year DESC,t.release_id,t.disc_number,t.track_number")}; if (linkType) query.where("t_a_l.type = ?").bind(*linkType); - Wt::Dbo::collection> tracks {query.resultList()}; - - return std::vector>(tracks.begin(), tracks.end()); + auto tracks {query.resultList()}; + return std::vector(tracks.begin(), tracks.end()); } -std::vector> +std::vector Artist::getNonReleaseTracks(std::optional linkType, std::optional range, bool& moreResults) const { - assert(self()); - assert(IdIsValid(self()->id())); 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(self()->id()) + .where("a.id = ?").bind(getId()) .where("t.release_id is NULL") .orderBy("t.name") .limit(range ? static_cast(range->limit) + 1 : -1) @@ -481,9 +471,8 @@ Artist::getNonReleaseTracks(std::optional linkType, std::op if (linkType) query.where("t_a_l.type = ?").bind(*linkType); - Wt::Dbo::collection tracks {query.resultList()}; - - auto res {std::vector(tracks.begin(), tracks.end())}; + Wt::Dbo::collection> tracks {query.resultList()}; + std::vector res(tracks.begin(), tracks.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -499,37 +488,32 @@ bool Artist::hasNonReleaseTracks(std::optional linkType) const { 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(self()->id()) + .where("a.id = ?").bind(getId()) .where("t.release_id is NULL") .orderBy("t.name")}; if (linkType) query.where("t_a_l.type = ?").bind(*linkType); - Wt::Dbo::collection tracks {query.resultList()}; - return !tracks.empty(); + return !query.resultList().empty(); } -std::vector> +std::vector Artist::getRandomTracks(std::optional count) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); Wt::Dbo::collection> tracks {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(self()->id()) + .where("a.id = ?").bind(getId()) .orderBy("RANDOM()") .limit(count ? static_cast(*count) : -1)}; - return std::vector>(tracks.begin(), tracks.end()); + return std::vector(tracks.begin(), tracks.end()); } -std::vector> +std::vector Artist::getSimilarArtists(EnumSet artistLinkTypes, std::optional range) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); std::ostringstream oss; @@ -563,9 +547,9 @@ Artist::getSimilarArtists(EnumSet artistLinkTypes, std::opt oss << ")"; } - Wt::Dbo::Query query {session()->query(oss.str()) - .bind(self()->id()) - .bind(self()->id()) + Wt::Dbo::Query> query {session()->query>(oss.str()) + .bind(getId()) + .bind(getId()) .groupBy("a.id") .orderBy("COUNT(*) DESC, RANDOM()") .limit(range ? static_cast(range->limit) : -1) @@ -574,15 +558,13 @@ Artist::getSimilarArtists(EnumSet artistLinkTypes, std::opt for (TrackArtistLinkType type : artistLinkTypes) query.bind(type); - Wt::Dbo::collection res = query; + Wt::Dbo::collection> res {query.resultList()}; return std::vector(res.begin(), res.end()); } -std::vector>> +std::vector> Artist::getClusterGroups(std::vector clusterTypes, std::size_t size) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); WhereClause where; @@ -590,34 +572,34 @@ Artist::getClusterGroups(std::vector clusterTypes, std::si std::ostringstream oss; oss << "SELECT c FROM cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id INNER JOIN artist a ON t_a_l.artist_id = a.id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id"; - where.And(WhereClause("a.id = ?")).bind(std::to_string(self()->id())); + where.And(WhereClause("a.id = ?")).bind(getId().toString()); { WhereClause clusterClause; for (auto clusterType : clusterTypes) - clusterClause.Or(WhereClause("c_type.id = ?")).bind(std::to_string(clusterType.id())); + clusterClause.Or(WhereClause("c_type.id = ?")).bind(clusterType->getId().toString()); where.And(clusterClause); } oss << " " << where.get(); oss << "GROUP BY c.id ORDER BY COUNT(DISTINCT c.id) DESC"; - Wt::Dbo::Query query = session()->query( oss.str() ); + Wt::Dbo::Query> query = session()->query>( oss.str() ); for (const std::string& bindArg : where.getBindArgs()) query.bind(bindArg); - Wt::Dbo::collection queryRes = query; + Wt::Dbo::collection> queryRes = query; - std::map> clusters; - for (auto cluster : queryRes) + std::map> clustersByType; + for (const Cluster::pointer& cluster : queryRes) { - if (clusters[cluster->getType().id()].size() < size) - clusters[cluster->getType().id()].push_back(cluster); + if (clustersByType[cluster->getType()->getId()].size() < size) + clustersByType[cluster->getType()->getId()].push_back(cluster); } std::vector> res; - for (auto cluster_list : clusters) - res.push_back(cluster_list.second); + for (const auto& [clusterTypeId, clusters] : clustersByType) + res.push_back(clusters); return res; } diff --git a/src/libs/database/impl/Cluster.cpp b/src/libs/database/impl/Cluster.cpp index 0a3143c8..184a03a1 100644 --- a/src/libs/database/impl/Cluster.cpp +++ b/src/libs/database/impl/Cluster.cpp @@ -25,21 +25,18 @@ #include "database/Session.hpp" #include "database/Track.hpp" #include "SqlQuery.hpp" +#include "Traits.hpp" namespace Database { -Cluster::Cluster() -{ -} - -Cluster::Cluster(Wt::Dbo::ptr type, std::string_view name) - : _name(std::string {name, 0, _maxNameLength}), - _clusterType {type} +Cluster::Cluster(ObjectPtr type, std::string_view name) + : _name {std::string {name, 0, _maxNameLength}}, + _clusterType {getDboPtr(type)} { } Cluster::pointer -Cluster::create(Session& session, Wt::Dbo::ptr type, std::string_view name) +Cluster::create(Session& session, ObjectPtr type, std::string_view name) { session.checkUniqueLocked(); @@ -54,8 +51,7 @@ Cluster::getAll(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res {session.getDboSession().find()}; - + Wt::Dbo::collection> res {session.getDboSession().find()}; return std::vector(res.begin(), res.end()); } @@ -63,67 +59,61 @@ std::vector Cluster::getAllOrphans(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection 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)")}; - + 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()); } Cluster::pointer -Cluster::getById(Session& session, IdType id) +Cluster::getById(Session& session, ClusterId id) { session.checkSharedLocked(); - return session.getDboSession().find().where("id = ?").bind(id); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } void -Cluster::addTrack(Wt::Dbo::ptr track) +Cluster::addTrack(ObjectPtr track) { - _tracks.insert(track); + _tracks.insert(getDboPtr(track)); } -std::vector> +std::vector Cluster::getTracks(std::optional offset, std::optional limit) const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::collection 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(self()->id()) + 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)}; + .limit(limit ? static_cast(*limit) : -1) + .resultList()}; - return std::vector>(res.begin(), res.end()); + return std::vector(res.begin(), res.end()); } -std::set +std::vector Cluster::getTrackIds() const { assert(session()); - assert(IdIsValid(self()->id())); - 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(self()->id()); - - return std::set(res.begin(), res.end()); + 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()); } std::size_t Cluster::getReleasesCount() const { assert(session()); - assert(IdIsValid(self()->id())); return session()->query("SELECT COUNT(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") - .where("c.id = ?").bind(self()->id()); - + .where("c.id = ?").bind(getId()); } -ClusterType::ClusterType(std::string name) - : _name(name) +ClusterType::ClusterType(std::string_view name) + : _name {name} { } @@ -132,7 +122,7 @@ ClusterType::getAllOrphans(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query>( + 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"); @@ -145,7 +135,7 @@ ClusterType::getAllUsed(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query>( + 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"); @@ -157,15 +147,15 @@ ClusterType::getByName(Session& session, const std::string& name) { session.checkSharedLocked(); - return session.getDboSession().find().where("name = ?").bind(name); + return session.getDboSession().find().where("name = ?").bind(name).resultValue(); } ClusterType::pointer -ClusterType::getById(Session& session, IdType id) +ClusterType::getById(Session& session, ClusterTypeId id) { session.checkSharedLocked(); - return session.getDboSession().find().where("id= ?").bind(id); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } std::vector @@ -173,8 +163,7 @@ ClusterType::getAll(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find(); - + auto res {session.getDboSession().find().resultList()}; return std::vector(res.begin(), res.end()); } @@ -193,24 +182,23 @@ Cluster::pointer ClusterType::getCluster(const std::string& name) const { assert(self()); - assert(IdIsValid(self()->id())); assert(session()); return session()->find() .where("name = ?").bind(name) - .where("cluster_type_id = ?").bind(self()->id()); + .where("cluster_type_id = ?").bind(getId()).resultValue(); } std::vector ClusterType::getClusters() const { assert(self()); - assert(IdIsValid(self()->id())); assert(session()); - Wt::Dbo::collection res = session()->find() - .where("cluster_type_id = ?").bind(self()->id()) - .orderBy("name"); + auto res = session()->find() + .where("cluster_type_id = ?").bind(getId()) + .orderBy("name") + .resultList(); return std::vector(res.begin(), res.end()); } diff --git a/src/libs/database/impl/Release.cpp b/src/libs/database/impl/Release.cpp index 5b0a2d1c..a782b06a 100644 --- a/src/libs/database/impl/Release.cpp +++ b/src/libs/database/impl/Release.cpp @@ -28,6 +28,7 @@ #include "database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" +#include "Traits.hpp" #include "Utils.hpp" namespace Database @@ -38,7 +39,7 @@ static Wt::Dbo::Query createQuery(Session& session, const std::string& queryStr, - const std::set& clusterIds, + const std::vector& clusterIds, const std::vector& keywords) { @@ -57,7 +58,7 @@ createQuery(Session& session, " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"; WhereClause clusterClause; - for (const IdType clusterId : clusterIds) + for (const ClusterId clusterId : clusterIds) { clusterClause.Or(WhereClause("c.id = ?")); query.bind(clusterId); @@ -76,7 +77,6 @@ Release::Release(const std::string& name, const std::optional& MBID) : _name {std::string(name, 0 , _maxNameLength)}, _MBID {MBID ? MBID->getAsString() : ""} { - } std::vector @@ -84,7 +84,11 @@ Release::getByName(Session& session, const std::string& name) { session.checkUniqueLocked(); - Wt::Dbo::collection res = session.getDboSession().find().where("name = ?").bind( std::string(name, 0, _maxNameLength) ); + auto res {session.getDboSession() + .find() + .where("name = ?").bind( std::string(name, 0, _maxNameLength) ) + .resultList()}; + return std::vector(res.begin(), res.end()); } @@ -93,15 +97,21 @@ Release::getByMBID(Session& session, const UUID& mbid) { session.checkSharedLocked(); - return session.getDboSession().find().where("mbid = ?").bind(std::string {mbid.getAsString()}); + return session.getDboSession() + .find() + .where("mbid = ?").bind(std::string {mbid.getAsString()}) + .resultValue();; } Release::pointer -Release::getById(Session& session, IdType id) +Release::getById(Session& session, ReleaseId id) { session.checkSharedLocked(); - return session.getDboSession().find().where("id = ?").bind(id); + return session.getDboSession() + .find() + .where("id = ?").bind(id) + .resultValue(); } Release::pointer @@ -120,8 +130,7 @@ Release::getCount(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection releases {session.getDboSession().find()}; - return releases.size(); + return session.getDboSession().find().resultList().size(); } std::vector @@ -129,21 +138,22 @@ Release::getAll(Session& session, std::optional range) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find() + auto res {session.getDboSession().find() .offset(range ? static_cast(range->offset) : -1) .limit(range ? static_cast(range->limit) : -1) - .orderBy("name COLLATE NOCASE"); + .orderBy("name COLLATE NOCASE") + .resultList()}; return std::vector(res.begin(), res.end()); } -std::vector +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()); + Wt::Dbo::collection res = session.getDboSession().query("SELECT id FROM release"); + return std::vector(res.begin(), res.end()); } std::vector @@ -151,44 +161,45 @@ Release::getAllOrderedByArtist(Session& session, std::optional offs { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query>( + auto res {session.getDboSession().query>( "SELECT DISTINCT r 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"); + .orderBy("a.name COLLATE NOCASE, r.name COLLATE NOCASE") + .resultList()}; return std::vector(res.begin(), res.end()); } std::vector -Release::getAllRandom(Session& session, const std::set& clusterIds, std::optional size) +Release::getAllRandom(Session& session, const std::vector& clusterIds, std::optional size) { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT DISTINCT r from release r", clusterIds,{})}; - - Wt::Dbo::collection res = query + auto query {createQuery>(session, "SELECT DISTINCT r from release r", clusterIds, {})}; + auto res {query .orderBy("RANDOM()") - .limit(size ? static_cast(*size) : -1); + .limit(size ? static_cast(*size) : -1) + .resultList()}; return std::vector(res.begin(), res.end()); } -std::vector -Release::getAllIdsRandom(Session& session, const std::set& clusterIds, std::optional size) +std::vector +Release::getAllIdsRandom(Session& session, const std::vector& clusterIds, std::optional size) { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT DISTINCT r.id from release r", clusterIds,{})}; + auto query {createQuery(session, "SELECT DISTINCT r.id from release r", clusterIds, {})}; - Wt::Dbo::collection res = query + Wt::Dbo::collection res = query .orderBy("RANDOM()") .limit(size ? static_cast(*size) : -1); - return std::vector(res.begin(), res.end()); + return std::vector(res.begin(), res.end()); } @@ -197,31 +208,31 @@ Release::getAllOrphans(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection 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"); - + 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::set& clusterIds, + const std::vector& clusterIds, std::optional range, bool& moreResults) { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT r from release r", clusterIds, {})}; + auto query {createQuery>(session, "SELECT r from release r", clusterIds, {})}; if (after) query.where("t.file_last_write > ?").bind(after); - Wt::Dbo::collection collection = query + 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); + .limit(range ? static_cast(range->limit) + 1: -1) + .resultList()}; - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res(collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -236,13 +247,14 @@ Release::getLastWritten(Session& session, std::vector Release::getByYear(Session& session, int yearFrom, int yearTo, std::optional range) { - Wt::Dbo::collection res = session.getDboSession().query + auto res {session.getDboSession().query> ("SELECT DISTINCT r from release r INNER JOIN track t ON r.id = t.release_id") .where("t.year >= ?").bind(yearFrom) .where("t.year <= ?").bind(yearTo) .orderBy("t.year, r.name COLLATE NOCASE") .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) : -1); + .limit(range ? static_cast(range->limit) : -1) + .resultList()}; return std::vector(res.begin(), res.end()); } @@ -250,30 +262,31 @@ Release::getByYear(Session& session, int yearFrom, int yearTo, std::optional Release::getStarred(Session& session, User::pointer user, - const std::set& clusterIds, + const std::vector& clusterIds, std::optional range, bool& moreResults) { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT r from release r", clusterIds, {})}; + 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.id()); + query.bind(user->getId()); query.where(oss.str()); } - Wt::Dbo::collection collection = query + 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); + .limit(range ? static_cast(range->limit) + 1: -1) + .resultList()}; - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res(collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -288,7 +301,7 @@ Release::getStarred(Session& session, } std::vector -Release::getByClusters(Session& session, const std::set& clusters) +Release::getByClusters(Session& session, const std::vector& clusters) { assert(!clusters.empty()); @@ -300,21 +313,21 @@ Release::getByClusters(Session& session, const std::set& clusters) std::vector Release::getByFilter(Session& session, - const std::set& clusterIds, + const std::vector& clusterIds, const std::vector& keywords, std::optional range, bool& moreResults) { session.checkSharedLocked(); - Wt::Dbo::collection collection = createQuery(session, "SELECT r from release r", clusterIds, keywords) + 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); - - auto res {std::vector(collection.begin(), collection.end())}; + .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; @@ -326,18 +339,18 @@ Release::getByFilter(Session& session, return res; } -std::vector +std::vector Release::getAllIdsWithClusters(Session& session, std::optional limit) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query + 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()); + return std::vector(res.begin(), res.end()); } @@ -345,11 +358,10 @@ std::optional Release::getTotalTrack(void) const { assert(session()); - assert(IdIsValid(self()->id())); int res = session()->query("SELECT COALESCE(MAX(total_track),0) FROM track t INNER JOIN release r ON r.id = t.release_id") .where("r.id = ?") - .bind(this->id()); + .bind(getId()); return (res > 0) ? std::make_optional(res) : std::nullopt; } @@ -358,11 +370,10 @@ std::optional Release::getTotalDisc(void) const { assert(session()); - assert(IdIsValid(self()->id())); int res = session()->query("SELECT COALESCE(MAX(total_disc),0) FROM track t INNER JOIN release r ON r.id = t.release_id") .where("r.id = ?") - .bind(this->id()); + .bind(getId()); return (res > 0) ? std::make_optional(res) : std::nullopt; } @@ -372,13 +383,13 @@ Release::getReleaseYear(bool original) const { assert(session()); - const std::string field {original ? "original_year" : "year"}; + const char* field {original ? "original_year" : "year"}; Wt::Dbo::collection dates = session()->query( std::string {"SELECT "} + "t." + field + " FROM track t INNER JOIN release r ON r.id = t.release_id") .where("r.id = ?") .groupBy(field) - .bind(this->id()); + .bind(getId()); // various dates => no date if (dates.empty() || dates.size() > 1) @@ -388,8 +399,8 @@ Release::getReleaseYear(bool original) const if (date > 0) return date; - else - return std::nullopt; + + return std::nullopt; } std::optional @@ -401,7 +412,7 @@ Release::getCopyright() const ("SELECT copyright FROM track t INNER JOIN release r ON r.id = t.release_id") .where("r.id = ?") .groupBy("copyright") - .bind(this->id()); + .bind(getId()); std::vector values(copyrights.begin(), copyrights.end()); @@ -421,7 +432,7 @@ Release::getCopyrightURL() const ("SELECT copyright_url FROM track t INNER JOIN release r ON r.id = t.release_id") .where("r.id = ?") .groupBy("copyright_url") - .bind(this->id()); + .bind(getId()); std::vector values(copyrights.begin(), copyrights.end()); @@ -432,32 +443,29 @@ Release::getCopyrightURL() const return values.front(); } -std::vector> +std::vector Release::getArtists(TrackArtistLinkType linkType) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); - Wt::Dbo::collection> res = session()->query>( + auto res {session()->query>( "SELECT DISTINCT a 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 release r ON r.id = t.release_id") - .where("r.id = ?").bind(self()->id()) - .where("t_a_l.type = ?").bind(linkType); + .where("r.id = ?").bind(getId()) + .where("t_a_l.type = ?").bind(linkType) + .resultList()}; - return std::vector>(res.begin(), res.end()); + return std::vector(res.begin(), res.end()); } std::vector Release::getSimilarReleases(std::optional offset, std::optional count) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); - Wt::Dbo::Query query {session()->query( + auto res {session()->query>( "SELECT r 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" @@ -465,14 +473,14 @@ Release::getSimilarReleases(std::optional offset, std::optional ?" ) - .bind(self()->id()) - .bind(self()->id()) + .bind(getId()) + .bind(getId()) .groupBy("r.id") .orderBy("COUNT(*) DESC, RANDOM()") .limit(count ? static_cast(*count) : -1) - .offset(offset ? static_cast(*offset) : -1)}; + .offset(offset ? static_cast(*offset) : -1) + .resultList()}; - Wt::Dbo::collection res = query; return std::vector(res.begin(), res.end()); } @@ -483,11 +491,9 @@ Release::hasVariousArtists() const return getArtists().size() > 1; } -std::vector> -Release::getTracks(const std::set& clusterIds) const +std::vector +Release::getTracks(const std::vector& clusterIds) const { - assert(self()); - assert(self()->id() != Wt::Dbo::dbo_traits::invalidId() ); assert(session()); WhereClause where; @@ -502,12 +508,12 @@ Release::getTracks(const std::set& clusterIds) const WhereClause clusterClause; for (auto id : clusterIds) - clusterClause.Or(WhereClause("c.id = ?")).bind(std::to_string(id)); + clusterClause.Or(WhereClause("c.id = ?")).bind(id.toString()); where.And(clusterClause); } - where.And(WhereClause("r.id = ?")).bind(std::to_string(id())); + where.And(WhereClause("r.id = ?")).bind(getId().toString()); oss << " " << where.get(); @@ -516,16 +522,12 @@ Release::getTracks(const std::set& clusterIds) const oss << " ORDER BY t.disc_number,t.track_number"; - Wt::Dbo::Query query = session()->query( oss.str() ); - + auto query {session()->query>(oss.str())}; for (const std::string& bindArg : where.getBindArgs()) - { query.bind(bindArg); - } - Wt::Dbo::collection< Wt::Dbo::ptr > res = query; - - return std::vector< Wt::Dbo::ptr > (res.begin(), res.end()); + auto res {query.resultList()}; + return std::vector (res.begin(), res.end()); } std::size_t @@ -534,31 +536,28 @@ Release::getTracksCount() const return _tracks.size(); } -Wt::Dbo::ptr +Track::pointer Release::getFirstTrack() const { - assert(self()); - assert(self()->id() != Wt::Dbo::dbo_traits::invalidId()); assert(session()); - return session()->query("SELECT t from track t") + return session()->query>("SELECT t from track t") .join("release r ON t.release_id = r.id") - .where("r.id = ?").bind(self()->id()) + .where("r.id = ?").bind(getId()) .orderBy("t.disc_number,t.track_number") - .limit(1); + .limit(1) + .resultValue(); } std::chrono::milliseconds Release::getDuration() const { - assert(self()); - assert(self()->id() != Wt::Dbo::dbo_traits::invalidId()); assert(session()); using milli = std::chrono::duration; Wt::Dbo::Query query {session()->query("SELECT COALESCE(SUM(duration), 0) FROM track t INNER JOIN release r ON t.release_id = r.id") - .where("r.id = ?").bind(self()->id())}; + .where("r.id = ?").bind(getId())}; return query.resultValue(); } @@ -566,21 +565,17 @@ Release::getDuration() const Wt::WDateTime Release::getLastWritten() const { - assert(self()); - assert(self()->id() != Wt::Dbo::dbo_traits::invalidId()); assert(session()); Wt::Dbo::Query query {session()->query("SELECT COALESCE(MAX(file_last_write), '1970-01-01T00:00:00') FROM track t INNER JOIN release r ON t.release_id = r.id") - .where("r.id = ?").bind(self()->id())}; + .where("r.id = ?").bind(getId())}; return query.resultValue(); } -std::vector>> -Release::getClusterGroups(std::vector clusterTypes, std::size_t size) const +std::vector> +Release::getClusterGroups(const std::vector& clusterTypes, std::size_t size) const { - assert(self()); - assert(self()->id() != Wt::Dbo::dbo_traits::invalidId() ); assert(session()); WhereClause where; @@ -589,33 +584,32 @@ Release::getClusterGroups(std::vector clusterTypes, std::s oss << "SELECT c from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id INNER JOIN release r ON t.release_id = r.id "; - where.And(WhereClause("r.id = ?")).bind(std::to_string(self()->id())); + where.And(WhereClause("r.id = ?")).bind(getId().toString()); { WhereClause clusterClause; for (auto clusterType : clusterTypes) - clusterClause.Or(WhereClause("c_type.id = ?")).bind(std::to_string(clusterType.id())); + clusterClause.Or(WhereClause("c_type.id = ?")).bind(clusterType->getId().toString()); where.And(clusterClause); } oss << " " << where.get(); oss << " GROUP BY c.id ORDER BY COUNT(c.id) DESC"; - Wt::Dbo::Query query = session()->query( oss.str() ); - + auto query {session()->query>(oss.str())}; for (const std::string& bindArg : where.getBindArgs()) query.bind(bindArg); - Wt::Dbo::collection queryRes = query; + auto queryRes {query.resultList()}; - std::map> clusters; - for (auto cluster : queryRes) + std::map> clustersByType; + for (const Wt::Dbo::ptr& cluster : queryRes) { - if (clusters[cluster->getType().id()].size() < size) - clusters[cluster->getType().id()].push_back(cluster); + if (clustersByType[cluster->getType()->getId()].size() < size) + clustersByType[cluster->getType()->getId()].push_back(cluster); } std::vector> res; - for (auto cluster_list : clusters) - res.push_back(cluster_list.second); + for (const auto& [clusterTypeId, clusters] : clustersByType) + res.push_back(clusters); return res; } diff --git a/src/libs/database/impl/ScanSettings.cpp b/src/libs/database/impl/ScanSettings.cpp index 3d9a1a86..9aafed53 100644 --- a/src/libs/database/impl/ScanSettings.cpp +++ b/src/libs/database/impl/ScanSettings.cpp @@ -60,14 +60,14 @@ ScanSettings::get(Session& session) { session.checkSharedLocked(); - return session.getDboSession().find(); + return session.getDboSession().find().resultValue(); } -std::unordered_set +std::vector ScanSettings::getAudioFileExtensions() const { - auto extensions = StringUtils::splitString(_audioFileExtensions, " "); - return std::unordered_set(std::cbegin(extensions), std::cend(extensions)); + const auto extensions {StringUtils::splitString(_audioFileExtensions, " ")}; + return std::vector(std::cbegin(extensions), std::cend(extensions)); } void @@ -111,19 +111,19 @@ ScanSettings::setClusterTypes(Session& session, const std::set& clu // Create any missing cluster type for (const std::string& clusterTypeName : clusterTypeNames) { - auto clusterType {ClusterType::getByName(session, clusterTypeName)}; + ClusterType::pointer clusterType {ClusterType::getByName(session, clusterTypeName)}; if (!clusterType) { LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName; clusterType = ClusterType::create(session, clusterTypeName); - _clusterTypes.insert(clusterType); + _clusterTypes.insert(getDboPtr(clusterType)); needRescan = true; } } // Delete no longer existing cluster types - for (ClusterType::pointer& clusterType : _clusterTypes) + for (Wt::Dbo::ptr clusterType : _clusterTypes) { if (std::none_of(clusterTypeNames.begin(), clusterTypeNames.end(), [clusterType](const std::string& name) { return name == clusterType->getName(); })) diff --git a/src/libs/database/impl/Track.cpp b/src/libs/database/impl/Track.cpp index 9e1ef2a0..91bdfd86 100644 --- a/src/libs/database/impl/Track.cpp +++ b/src/libs/database/impl/Track.cpp @@ -27,10 +27,12 @@ #include "database/TrackArtistLink.hpp" #include "database/TrackFeatures.hpp" #include "database/Session.hpp" +#include "database/User.hpp" #include "utils/Logger.hpp" #include "SqlQuery.hpp" #include "StringViewTraits.hpp" +#include "Traits.hpp" #include "Utils.hpp" namespace Database { @@ -40,7 +42,7 @@ static Wt::Dbo::Query createQuery(Session& session, const std::string& queryStr, - const std::set& clusterIds, + const std::vector& clusterIds, const std::vector& keywords) { session.checkSharedLocked(); @@ -58,7 +60,7 @@ createQuery(Session& session, " INNER JOIN cluster c ON c.id = t_c.cluster_id"; WhereClause clusterClause; - for (const IdType clusterId : clusterIds) + for (const ClusterId clusterId : clusterIds) { clusterClause.Or(WhereClause("c.id = ?")); query.bind(clusterId); @@ -91,48 +93,49 @@ Track::getAll(Session& session, std::optional limit) { session.checkSharedLocked(); - Wt::Dbo::collection res {session.getDboSession().find() - .limit(limit ? static_cast(*limit) : -1)}; + 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::set& clusterIds, std::optional limit) +Track::getAllRandom(Session& session, const std::vector& clusterIds, std::optional limit) { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT t from track t", clusterIds, {})}; - - Wt::Dbo::collection collection = query + auto query {createQuery>(session, "SELECT t from track t", clusterIds, {})}; + auto collection {query .orderBy("RANDOM()") - .limit(limit ? static_cast(*limit) + 1: -1); + .limit(limit ? static_cast(*limit) + 1: -1) + .resultList()}; return std::vector(collection.begin(), collection.end()); } -std::vector -Track::getAllIdsRandom(Session& session, const std::set& clusterIds, std::optional limit) +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, {})}; + auto query {createQuery(session, "SELECT t.id from track t", clusterIds, {})}; - Wt::Dbo::collection collection = query + Wt::Dbo::collection collection = query .orderBy("RANDOM()") .limit(limit ? static_cast(*limit) + 1: -1); - return std::vector(collection.begin(), collection.end()); + return std::vector(collection.begin(), collection.end()); } -std::vector +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()); + Wt::Dbo::collection res = session.getDboSession().query("SELECT id FROM track"); + return std::vector(res.begin(), res.end()); } Track::pointer @@ -140,16 +143,17 @@ Track::getByPath(Session& session, const std::filesystem::path& p) { session.checkSharedLocked(); - return session.getDboSession().find().where("file_path = ?").bind(p.string()); + return session.getDboSession().find().where("file_path = ?").bind(p.string()).resultValue(); } Track::pointer -Track::getById(Session& session, IdType id) +Track::getById(Session& session, TrackId id) { session.checkSharedLocked(); return session.getDboSession().find() - .where("id = ?").bind(id); + .where("id = ?").bind(id) + .resultValue(); } std::vector @@ -157,8 +161,9 @@ Track::getByRecordingMBID(Session& session, const UUID& mbid) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find() - .where("recording_mbid = ?").bind(std::string {mbid.getAsString()}); + auto res {session.getDboSession().find() + .where("recording_mbid = ?").bind(std::string {mbid.getAsString()}) + .resultList()}; return std::vector(res.begin(), res.end()); } @@ -174,17 +179,17 @@ Track::create(Session& session, const std::filesystem::path& p) return res; } -std::vector> +std::vector> Track::getAllPaths(Session& session, std::optional offset, std::optional size) { - using QueryResultType = std::tuple; + 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); - std::vector> result; + std::vector> result; result.reserve(queryRes.size()); std::transform(std::begin(queryRes), std::end(queryRes), std::back_inserter(result), @@ -201,26 +206,29 @@ Track::getMBIDDuplicates(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection 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"); + 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::set& clusterIds, std::optional range, bool& moreResults) +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, {})}; + auto query {createQuery>(session, "SELECT t from track t", clusterIds, {})}; if (after) query.where("t.file_last_write > ?").bind(after); - Wt::Dbo::collection collection = query + 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); + .limit(range ? static_cast(range->limit) + 1: -1) + .resultList()}; - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res(collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -237,63 +245,65 @@ Track::getAllWithRecordingMBIDAndMissingFeatures(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query + auto res {session.getDboSession().query> ("SELECT t 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)"); + .where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)") + .resultList()}; return std::vector(res.begin(), res.end()); } -std::vector +std::vector Track::getAllIdsWithFeatures(Session& session, std::optional limit) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query + 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()); + return std::vector(res.begin(), res.end()); } -std::vector +std::vector Track::getAllIdsWithClusters(Session& session, std::optional limit) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query + 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()); + return std::vector(res.begin(), res.end()); } std::vector Track::getStarred(Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) { session.checkSharedLocked(); - auto query {createQuery(session, "SELECT t from track t", clusterIds, {})}; + 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.id()); + query.bind(user->getId().toString()); query.where(oss.str()); } - Wt::Dbo::collection collection = query + auto collection {query .offset(range ? static_cast(range->offset) : -1) - .limit(range ? static_cast(range->limit) + 1: -1); + .limit(range ? static_cast(range->limit) + 1: -1) + .resultList()}; - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res(collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -308,43 +318,41 @@ Track::getStarred(Session& session, std::vector Track::getClusters() const { - std::vector< Cluster::pointer > clusters; - std::copy(_clusters.begin(), _clusters.end(), std::back_inserter(clusters)); - return clusters; + return std::vector(_clusters.begin(), _clusters.end()); } -std::vector +std::vector Track::getClusterIds() const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); - Wt::Dbo::collection res = session()->query + auto res {session()->query ("SELECT DISTINCT c.id FROM cluster c INNER JOIN track_cluster t_c ON t_c.cluster_id = c.id INNER JOIN track t ON t.id = t_c.track_id") - .where("t.id = ?").bind(self()->id()); + .where("t.id = ?").bind(getId()) + .resultList()}; - return std::vector(res.begin(), res.end()); + return std::vector(res.begin(), res.end()); } bool Track::hasTrackFeatures() const { - return (_trackFeatures.lock() != Database::TrackFeatures::pointer()); + return (_trackFeatures.lock() != Wt::Dbo::ptr {}); } std::vector Track::getByFilter(Session& session, - const std::set& clusterIds, + const std::vector& clusterIds, const std::vector& keywords, std::optional range, bool& moreResults) { session.checkSharedLocked(); - Wt::Dbo::collection collection = createQuery(session, "SELECT t from track t", clusterIds, keywords) + 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); + .offset(range ? static_cast(range->offset) : -1) + .resultList()}; std::vector res(collection.begin(), collection.end()); if (range && (res.size() == static_cast(range->limit) + 1)) @@ -362,17 +370,18 @@ std::vector Track::getByNameAndReleaseName(Session& session, std::string_view trackName, std::string_view releaseName) { session.checkSharedLocked(); - Wt::Dbo::collection collection = session.getDboSession().query("SELECT t from track t") + + auto res {session.getDboSession().query>("SELECT t from track t") .join("release r ON t.release_id = r.id") .where("t.name = ?").bind(trackName) - .where("r.name = ?").bind(releaseName); - - return std::vector(collection.begin(), collection.end()); + .where("r.name = ?").bind(releaseName) + .resultList()}; + return std::vector(res.begin(), res.end()); } std::vector Track::getSimilarTracks(Session& session, - const std::unordered_set& tracks, + const std::vector& tracks, std::optional offset, std::optional size) { @@ -387,7 +396,7 @@ Track::getSimilarTracks(Session& session, oss << "?"; } - Wt::Dbo::Query query {session.getDboSession().query( + auto query {session.getDboSession().query>( "SELECT t 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() + "))" @@ -397,19 +406,18 @@ Track::getSimilarTracks(Session& session, .limit(size ? static_cast(*size) : -1) .offset(offset ? static_cast(*offset) : -1)}; - for (IdType trackId : tracks) - query.bind(trackId ); + for (TrackId trackId : tracks) + query.bind(trackId); - for (IdType trackId : tracks) - query.bind(trackId ); + for (TrackId trackId : tracks) + query.bind(trackId); - Wt::Dbo::collection res = query; + auto res {query.resultList()}; return std::vector(res.begin(), res.end()); } std::vector -Track::getByClusters(Session& session, - const std::set& clusters) +Track::getByClusters(Session& session, const std::vector& clusters) { assert(!clusters.empty()); session.checkSharedLocked(); @@ -429,23 +437,23 @@ Track::clearArtistLinks() } void -Track::addArtistLink(const Wt::Dbo::ptr& artistLink) +Track::addArtistLink(const ObjectPtr& artistLink) { - _trackArtistLinks.insert(artistLink); + _trackArtistLinks.insert(getDboPtr(artistLink)); } void -Track::setClusters(const std::vector>& clusters) +Track::setClusters(const std::vector>& clusters) { _clusters.clear(); - for (const Wt::Dbo::ptr& cluster : clusters) - _clusters.insert(cluster); + for (const ObjectPtr& cluster : clusters) + _clusters.insert(getDboPtr(cluster)); } void -Track::setFeatures(const Wt::Dbo::ptr& features) +Track::setFeatures(const ObjectPtr& features) { - _trackFeatures = features; + _trackFeatures = getDboPtr(features); } std::optional @@ -496,11 +504,9 @@ Track::getCopyrightURL() const return _copyrightURL != "" ? std::make_optional(_copyrightURL) : std::nullopt; } -std::vector> +std::vector Track::getArtists(EnumSet linkTypes) const { - assert(self()); - assert(IdIsValid(self()->id())); assert(session()); std::ostringstream oss; @@ -525,22 +531,20 @@ Track::getArtists(EnumSet linkTypes) const oss << ")"; } - Wt::Dbo::Query query {session()->query(oss.str())}; - + auto query {session()->query>(oss.str())}; for (TrackArtistLinkType type : linkTypes) query.bind(type); - query.where("t.id = ?").bind(self()->id()); + query.where("t.id = ?").bind(getId()); - Wt::Dbo::collection res = query; + auto res {query.resultList()}; return std::vector(std::begin(res), std::end(res)); } -std::vector +std::vector Track::getArtistIds(EnumSet linkTypes) const { assert(self()); - assert(IdIsValid(self()->id())); assert(session()); std::ostringstream oss; @@ -565,33 +569,32 @@ Track::getArtistIds(EnumSet linkTypes) const oss << ")"; } - Wt::Dbo::Query query {session()->query(oss.str()) - .where("t.id = ?").bind(self()->id())}; + Wt::Dbo::Query query {session()->query(oss.str()) + .where("t.id = ?").bind(getId())}; for (TrackArtistLinkType type : linkTypes) query.bind(type); - Wt::Dbo::collection res = query; - return std::vector(std::begin(res), std::end(res)); + Wt::Dbo::collection res = query; + return std::vector(std::begin(res), std::end(res)); } -std::vector> +std::vector Track::getArtistLinks() const { - return std::vector>(_trackArtistLinks.begin(), _trackArtistLinks.end()); + return std::vector(_trackArtistLinks.begin(), _trackArtistLinks.end()); } -Wt::Dbo::ptr +ObjectPtr Track::getTrackFeatures() const { return _trackFeatures.lock(); } std::vector> -Track::getClusterGroups(std::vector clusterTypes, std::size_t size) const +Track::getClusterGroups(const std::vector& clusterTypes, std::size_t size) const { assert(self()); - assert(IdIsValid(self()->id())); assert(session()); WhereClause where; @@ -600,28 +603,27 @@ Track::getClusterGroups(std::vector clusterTypes, std::siz oss << "SELECT c from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id"; - where.And(WhereClause("t.id = ?")).bind(std::to_string(self()->id())); + where.And(WhereClause("t.id = ?")).bind(getId().toString()); { WhereClause clusterClause; for (auto clusterType : clusterTypes) - clusterClause.Or(WhereClause("c_type.id = ?")).bind(std::to_string(clusterType.id())); + clusterClause.Or(WhereClause("c_type.id = ?")).bind(clusterType->getId().toString()); where.And(clusterClause); } oss << " " << where.get(); oss << " GROUP BY c.id ORDER BY COUNT(c.id) DESC"; - Wt::Dbo::Query query = session()->query( oss.str() ); - + auto query {session()->query>(oss.str())}; for (const std::string& bindArg : where.getBindArgs()) query.bind(bindArg); - Wt::Dbo::collection queryRes = query; + auto queryRes {query.resultList()}; - std::map> clusters; - for (auto cluster : queryRes) + std::map> clusters; + for (const Wt::Dbo::ptr& cluster : queryRes) { - if (clusters[cluster->getType().id()].size() < size) - clusters[cluster->getType().id()].push_back(cluster); + if (clusters[cluster->getType()->getId()].size() < size) + clusters[cluster->getType()->getId()].push_back(cluster); } std::vector> res; diff --git a/src/libs/database/impl/TrackArtistLink.cpp b/src/libs/database/impl/TrackArtistLink.cpp index 45b62bbf..f8791dee 100644 --- a/src/libs/database/impl/TrackArtistLink.cpp +++ b/src/libs/database/impl/TrackArtistLink.cpp @@ -23,17 +23,19 @@ #include "database/Session.hpp" #include "database/Track.hpp" +#include "Traits.hpp" + namespace Database { -TrackArtistLink::TrackArtistLink(Wt::Dbo::ptr track, Wt::Dbo::ptr artist, TrackArtistLinkType type) +TrackArtistLink::TrackArtistLink(ObjectPtr track, ObjectPtr artist, TrackArtistLinkType type) : _type {type}, -_track {track}, -_artist {artist} +_track {getDboPtr(track)}, +_artist {getDboPtr(artist)} { } TrackArtistLink::pointer -TrackArtistLink::create(Session& session, Wt::Dbo::ptr track, Wt::Dbo::ptr artist, TrackArtistLinkType type) +TrackArtistLink::create(Session& session, ObjectPtr track, ObjectPtr artist, TrackArtistLinkType type) { session.checkUniqueLocked(); @@ -48,9 +50,9 @@ TrackArtistLink::getUsedTypes(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection collection = session.getDboSession().query("SELECT DISTINCT type from track_artist_link"); + auto res {session.getDboSession().query("SELECT DISTINCT type from track_artist_link").resultList()}; - return EnumSet(std::begin(collection), std::end(collection)); + return EnumSet(std::begin(res), std::end(res)); } } diff --git a/src/libs/database/impl/TrackBookmark.cpp b/src/libs/database/impl/TrackBookmark.cpp index 2a0c4bef..91f457bd 100644 --- a/src/libs/database/impl/TrackBookmark.cpp +++ b/src/libs/database/impl/TrackBookmark.cpp @@ -22,18 +22,18 @@ #include "database/Session.hpp" #include "database/Track.hpp" #include "database/User.hpp" +#include "Traits.hpp" namespace Database { -TrackBookmark::TrackBookmark(Wt::Dbo::ptr user, Wt::Dbo::ptr track) -: _user {user}, -_track {track} +TrackBookmark::TrackBookmark(ObjectPtr user, ObjectPtr track) +: _user {getDboPtr(user)}, +_track {getDboPtr(track)} { } - TrackBookmark::pointer -TrackBookmark::create(Session& session, Wt::Dbo::ptr user, Wt::Dbo::ptr track) +TrackBookmark::create(Session& session, ObjectPtr user, ObjectPtr track) { session.checkUniqueLocked(); @@ -48,42 +48,41 @@ TrackBookmark::getAll(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res {session.getDboSession().find()}; - + auto res {session.getDboSession().find().resultList()}; return std::vector(std::cbegin(res), std::cend(res)); } std::vector -TrackBookmark::getByUser(Session& session, Wt::Dbo::ptr user) +TrackBookmark::getByUser(Session& session, User::pointer user) { session.checkSharedLocked(); - Wt::Dbo::collection res - { - session.getDboSession().find() - .where("user_id = ?").bind(user.id()) - }; + auto res {session.getDboSession().find() + .where("user_id = ?").bind(user->getId()) + .resultList()}; return std::vector(std::cbegin(res), std::cend(res)); } TrackBookmark::pointer -TrackBookmark::getByUser(Session& session, Wt::Dbo::ptr user, Wt::Dbo::ptr track) +TrackBookmark::getByUser(Session& session, ObjectPtr user, ObjectPtr track) { session.checkSharedLocked(); return session.getDboSession().find() - .where("user_id = ?").bind(user.id()) - .where("track_id = ?").bind(track.id()); + .where("user_id = ?").bind(user->getId()) + .where("track_id = ?").bind(track->getId()) + .resultValue(); } TrackBookmark::pointer -TrackBookmark::getById(Session& session, IdType id) +TrackBookmark::getById(Session& session, TrackBookmarkId id) { session.checkSharedLocked(); return session.getDboSession().find() - .where("id = ?").bind(id); + .where("id = ?").bind(id) + .resultValue(); } diff --git a/src/libs/database/impl/TrackFeatures.cpp b/src/libs/database/impl/TrackFeatures.cpp index 6fb3c963..5770d0b1 100644 --- a/src/libs/database/impl/TrackFeatures.cpp +++ b/src/libs/database/impl/TrackFeatures.cpp @@ -28,14 +28,14 @@ namespace Database { -TrackFeatures::TrackFeatures(Wt::Dbo::ptr track, const std::string& jsonEncodedFeatures) -: _data(jsonEncodedFeatures), -_track(track) +TrackFeatures::TrackFeatures(ObjectPtr track, const std::string& jsonEncodedFeatures) +: _data {jsonEncodedFeatures}, +_track {getDboPtr(track)} { } TrackFeatures::pointer -TrackFeatures::create(Session& session, Wt::Dbo::ptr track, const std::string& jsonEncodedFeatures) +TrackFeatures::create(Session& session, ObjectPtr track, const std::string& jsonEncodedFeatures) { session.checkUniqueLocked(); return session.getDboSession().add(std::make_unique(track, jsonEncodedFeatures)); diff --git a/src/libs/database/impl/TrackList.cpp b/src/libs/database/impl/TrackList.cpp index 73c1067a..a9863d52 100644 --- a/src/libs/database/impl/TrackList.cpp +++ b/src/libs/database/impl/TrackList.cpp @@ -30,32 +30,33 @@ #include "database/Track.hpp" #include "SqlQuery.hpp" #include "StringViewTraits.hpp" +#include "Traits.hpp" namespace Database { -TrackList::TrackList(std::string_view name, Type type, bool isPublic, Wt::Dbo::ptr user) +TrackList::TrackList(std::string_view name, Type type, bool isPublic, ObjectPtr user) : _name {name}, _type {type}, _isPublic {isPublic}, - _user {user} + _user {getDboPtr(user)} { } TrackList::pointer -TrackList::create(Session& session, std::string_view name, Type type, bool isPublic, Wt::Dbo::ptr user) +TrackList::create(Session& session, std::string_view name, Type type, bool isPublic, ObjectPtr user) { session.checkUniqueLocked(); assert(user); - auto res = session.getDboSession().add( std::make_unique(name, type, isPublic, user) ); + TrackList::pointer res {session.getDboSession().add( std::make_unique(name, type, isPublic, user) )}; session.getDboSession().flush(); return res; } TrackList::pointer -TrackList::get(Session& session, std::string_view name, Type type, Wt::Dbo::ptr user) +TrackList::get(Session& session, std::string_view name, Type type, ObjectPtr user) { session.checkSharedLocked(); assert(user); @@ -63,49 +64,51 @@ TrackList::get(Session& session, std::string_view name, Type type, Wt::Dbo::ptr< return session.getDboSession().find() .where("name = ?").bind(name) .where("type = ?").bind(type) - .where("user_id = ?").bind(user.id()); + .where("user_id = ?").bind(user->getId()).resultValue(); } std::vector TrackList::getAll(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find(); + + auto res = session.getDboSession().find().resultList(); + return std::vector(res.begin(), res.end()); +} + +std::vector +TrackList::getAll(Session& session, ObjectPtr user) +{ + 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, Wt::Dbo::ptr user) +TrackList::getAll(Session& session, ObjectPtr user, Type type) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find() - .where("user_id = ?").bind(user.id()) - .orderBy("name COLLATE NOCASE"); - - return std::vector(res.begin(), res.end()); -} - -std::vector -TrackList::getAll(Session& session, Wt::Dbo::ptr user, Type type) -{ - session.checkSharedLocked(); - - Wt::Dbo::collection res = session.getDboSession().find() - .where("user_id = ?").bind(user.id()) + auto res {session.getDboSession().find() + .where("user_id = ?").bind(user->getId()) .where("type = ?").bind(type) - .orderBy("name COLLATE NOCASE"); + .orderBy("name COLLATE NOCASE") + .resultList()}; return std::vector(res.begin(), res.end()); } TrackList::pointer -TrackList::getById(Session& session, IdType id) +TrackList::getById(Session& session, TrackListId id) { session.checkSharedLocked(); - return session.getDboSession().find().where("id = ?").bind(id); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } bool @@ -120,10 +123,10 @@ TrackList::getCount() const return _entries.size(); } -Wt::Dbo::ptr +TrackListEntry::pointer TrackList::getEntry(std::size_t pos) const { - Wt::Dbo::ptr res; + TrackListEntry::pointer res; auto entries = getEntries(pos, 1); if (!entries.empty()) @@ -132,39 +135,39 @@ TrackList::getEntry(std::size_t pos) const return res; } -std::vector> +std::vector TrackList::getEntries(std::optional offset, std::optional size) const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::collection> entries = + auto entries { session()->find() - .where("tracklist_id = ?").bind(self().id()) + .where("tracklist_id = ?").bind(getId()) .orderBy("id") .limit(size ? static_cast(*size) : -1) - .offset(offset ? static_cast(*offset) : -1); + .offset(offset ? static_cast(*offset) : -1) + .resultList()}; - return std::vector>(entries.begin(), entries.end()); + return std::vector(entries.begin(), entries.end()); } -Wt::Dbo::ptr -TrackList::getEntryByTrackAndDateTime(Wt::Dbo::ptr track, const Wt::WDateTime& dateTime) const +TrackListEntry::pointer +TrackList::getEntryByTrackAndDateTime(ObjectPtr track, const Wt::WDateTime& dateTime) const { assert(session()); - assert(IdIsValid(self()->id())); return session()->find() - .where("tracklist_id = ?").bind(self().id()) - .where("track_id = ?").bind(track.id()) - .where("date_time = ?").bind(Wt::WDateTime::fromTime_t(dateTime.toTime_t())); + .where("tracklist_id = ?").bind(getId()) + .where("track_id = ?").bind(track->getId()) + .where("date_time = ?").bind(Wt::WDateTime::fromTime_t(dateTime.toTime_t())) + .resultValue(); } static -Wt::Dbo::Query -createArtistsQuery(Wt::Dbo::Session& session, const std::string& queryStr, IdType tracklistId, const std::set& clusterIds, std::optional linkType) +Wt::Dbo::Query> +createArtistsQuery(Wt::Dbo::Session& session, const std::string& queryStr, TrackListId tracklistId, const std::vector& clusterIds, std::optional linkType) { - auto query {session.query(queryStr)}; + auto query {session.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"); query.join("tracklist_entry p_e ON p_e.track_id = t.id"); @@ -201,10 +204,10 @@ createArtistsQuery(Wt::Dbo::Session& session, const std::string& queryStr, IdTyp } static -Wt::Dbo::Query -createReleasesQuery(Wt::Dbo::Session& session, const std::string& queryStr, IdType tracklistId, const std::set& clusterIds) +Wt::Dbo::Query> +createReleasesQuery(Wt::Dbo::Session& session, const std::string& queryStr, TrackListId tracklistId, const std::vector& clusterIds) { - auto query {session.query(queryStr)}; + auto query {session.query>(queryStr)}; query.join("track t ON t.release_id = r.id"); query.join("tracklist_entry p_e ON p_e.track_id = t.id"); query.join("tracklist p ON p.id = p_e.tracklist_id"); @@ -220,7 +223,7 @@ createReleasesQuery(Wt::Dbo::Session& session, const std::string& queryStr, IdTy " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"; WhereClause clusterClause; - for (auto id : clusterIds) + for (ClusterId id : clusterIds) { clusterClause.Or(WhereClause("c.id = ?")); query.bind(id); @@ -236,10 +239,10 @@ createReleasesQuery(Wt::Dbo::Session& session, const std::string& queryStr, IdTy } static -Wt::Dbo::Query -createTracksQuery(Wt::Dbo::Session& session, IdType tracklistId, const std::set& clusterIds) +Wt::Dbo::Query> +createTracksQuery(Wt::Dbo::Session& session, TrackListId tracklistId, const std::vector& clusterIds) { - auto query {session.query("SELECT t from track t INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id")}; + auto query {session.query>("SELECT t from track t INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id")}; query.where("p.id = ?").bind(tracklistId); @@ -253,7 +256,7 @@ createTracksQuery(Wt::Dbo::Session& session, IdType tracklistId, const std::set< WhereClause clusterClause; for (auto id : clusterIds) { - clusterClause.Or(WhereClause("c.id = ?")).bind(std::to_string(id)); + clusterClause.Or(WhereClause("c.id = ?")).bind(id.toString()); query.bind(id); } @@ -267,16 +270,16 @@ createTracksQuery(Wt::Dbo::Session& session, IdType tracklistId, const std::set< } std::vector -TrackList::getArtistsReverse(const std::set& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const +TrackList::getArtistsReverse(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::collection collection = createArtistsQuery(*session(), "SELECT a from artist a", self()->id(), clusterIds, linkType) + 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) - .offset(range ? static_cast(range->offset) : -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) @@ -291,18 +294,18 @@ TrackList::getArtistsReverse(const std::set& clusterIds, std::optional -TrackList::getReleasesReverse(const std::set& clusterIds, std::optional range, bool& moreResults) const +TrackList::getReleasesReverse(const std::vector& clusterIds, std::optional range, bool& moreResults) const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::collection collection = createReleasesQuery(*session(), "SELECT r from release r", self()->id(), clusterIds) + 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) - .offset(range ? static_cast(range->offset) : -1); + .offset(range ? static_cast(range->offset) : -1) + .resultList()}; - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res(collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -315,18 +318,18 @@ TrackList::getReleasesReverse(const std::set& clusterIds, std::optional< } std::vector -TrackList::getTracksReverse(const std::set& clusterIds, std::optional range, bool& moreResults) const +TrackList::getTracksReverse(const std::vector& clusterIds, std::optional range, bool& moreResults) const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::collection collection = createTracksQuery(*session(), self()->id(), clusterIds) + 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) - .offset(range ? static_cast(range->offset) : -1); + .offset(range ? static_cast(range->offset) : -1) + .resultList()}; - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res(collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { moreResults = true; @@ -338,29 +341,28 @@ TrackList::getTracksReverse(const std::set& clusterIds, std::optional> +std::vector TrackList::getClusters() const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::collection res = session()->query("SELECT c from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id") - .where("p.id = ?").bind(self()->id()) + auto res {session()->query>("SELECT c from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id") + .where("p.id = ?").bind(getId()) .groupBy("c.id") - .orderBy("COUNT(c.id) DESC"); + .orderBy("COUNT(c.id) DESC") + .resultList()}; - return std::vector>(res.begin(), res.end()); + return std::vector(res.begin(), res.end()); } bool -TrackList::hasTrack(IdType trackId) const +TrackList::hasTrack(TrackId trackId) const { assert(session()); - assert(IdIsValid(self()->id())); Wt::Dbo::collection res = session()->query("SELECT p_e from tracklist_entry p_e INNER JOIN tracklist p ON p_e.tracklist_id = p.id") .where("p_e.track_id = ?").bind(trackId) - .where("p.id = ?").bind(self()->id()); + .where("p.id = ?").bind(getId()); return res.size() > 0; } @@ -369,67 +371,64 @@ std::vector TrackList::getSimilarTracks(std::optional offset, std::optional size) const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::Query query {session()->query( + auto res {session()->query>( "SELECT t FROM track t" " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" " WHERE " " (t_c.cluster_id IN (SELECT c.id from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id WHERE p.id = ?)" " AND t.id NOT IN (SELECT tracklist_t.id FROM track tracklist_t INNER JOIN tracklist_entry t_e ON t_e.track_id = tracklist_t.id WHERE t_e.tracklist_id = ?))" ) - .bind(self()->id()) - .bind(self()->id()) + .bind(getId()) + .bind(getId()) .groupBy("t.id") .orderBy("COUNT(*) DESC, RANDOM()") .limit(size ? static_cast(*size) : -1) - .offset(offset ? static_cast(*offset) : -1)}; + .offset(offset ? static_cast(*offset) : -1) + .resultList()}; - Wt::Dbo::collection tracks = query; - return std::vector(tracks.begin(), tracks.end()); + return std::vector(res.begin(), res.end()); } -std::vector +std::vector TrackList::getTrackIds() const { assert(session()); - assert(IdIsValid(self()->id())); - Wt::Dbo::collection res = session()->query("SELECT p_e.track_id from tracklist_entry p_e INNER JOIN tracklist p ON p_e.tracklist_id = p.id") - .where("p.id = ?").bind(self()->id()); + Wt::Dbo::collection res = session()->query("SELECT p_e.track_id from tracklist_entry p_e INNER JOIN tracklist p ON p_e.tracklist_id = p.id") + .where("p.id = ?").bind(getId()); - return std::vector(res.begin(), res.end()); + return std::vector(res.begin(), res.end()); } std::chrono::milliseconds TrackList::getDuration() const { assert(session()); - assert(IdIsValid(self()->id())); using milli = std::chrono::duration; Wt::Dbo::Query query {session()->query("SELECT COALESCE(SUM(duration), 0) FROM track t INNER JOIN tracklist_entry p_e ON t.id = p_e.track_id") - .where("p_e.tracklist_id = ?").bind(self()->id())}; + .where("p_e.tracklist_id = ?").bind(getId())}; return query.resultValue(); } std::vector -TrackList::getTopArtists(const std::set& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const +TrackList::getTopArtists(const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const { assert(session()); - assert(IdIsValid(self()->id())); - auto query {createArtistsQuery(*session(), "SELECT a from artist a", self()->id(), clusterIds, linkType)}; + auto query {createArtistsQuery(*session(), "SELECT a from artist a", getId(), clusterIds, linkType)}; - Wt::Dbo::collection collection = query + auto collection {query .orderBy("COUNT(a.id) DESC") .groupBy("a.id") .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1); + .offset(range ? static_cast(range->offset) : -1) + .resultList()}; - auto res {std::vector(collection.begin(), collection.end())}; + std::vector res(collection.begin(), collection.end()); if (range && res.size() == static_cast(range->limit) + 1) { @@ -439,26 +438,23 @@ TrackList::getTopArtists(const std::set& clusterIds, std::optional -TrackList::getTopReleases(const std::set& clusterIds, std::optional range, bool& moreResults) const +TrackList::getTopReleases(const std::vector& clusterIds, std::optional range, bool& moreResults) const { assert(session()); - assert(IdIsValid(self()->id())); - auto query {createReleasesQuery(*session(), "SELECT r from release r", self()->id(), clusterIds)}; - - Wt::Dbo::collection collection = query + auto query {createReleasesQuery(*session(), "SELECT r from release r", getId(), clusterIds)}; + auto collection {query .orderBy("COUNT(r.id) DESC") .groupBy("r.id") .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1); - - auto res {std::vector(collection.begin(), collection.end())}; + .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; @@ -471,21 +467,19 @@ TrackList::getTopReleases(const std::set& clusterIds, std::optional -TrackList::getTopTracks(const std::set& clusterIds, std::optional range, bool& moreResults) const +TrackList::getTopTracks(const std::vector& clusterIds, std::optional range, bool& moreResults) const { assert(session()); - assert(IdIsValid(self()->id())); - auto query {createTracksQuery(*session(), self()->id(), clusterIds)}; - - Wt::Dbo::collection collection = query + auto query {createTracksQuery(*session(), getId(), clusterIds)}; + auto collection {query .orderBy("COUNT(t.id) DESC") .groupBy("t.id") .limit(range ? static_cast(range->limit) + 1 : -1) - .offset(range ? static_cast(range->offset) : -1); - - auto res {std::vector(collection.begin(), collection.end())}; + .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; @@ -497,16 +491,16 @@ TrackList::getTopTracks(const std::set& clusterIds, std::optional return res; } -TrackListEntry::TrackListEntry(Wt::Dbo::ptr track, Wt::Dbo::ptr tracklist, const Wt::WDateTime& dateTime) +TrackListEntry::TrackListEntry(ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime) : _dateTime {Wt::WDateTime::fromTime_t(dateTime.toTime_t())} // force second resolution -, _track {track} -, _tracklist {tracklist} +, _track {getDboPtr(track)} +, _tracklist {getDboPtr(tracklist)} { assert(_dateTime.isValid()); } TrackListEntry::pointer -TrackListEntry::create(Session& session, Wt::Dbo::ptr track, Wt::Dbo::ptr tracklist, const Wt::WDateTime& dateTime) +TrackListEntry::create(Session& session, ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime) { session.checkUniqueLocked(); assert(track); @@ -519,11 +513,11 @@ TrackListEntry::create(Session& session, Wt::Dbo::ptr track, Wt::Dbo::ptr } TrackListEntry::pointer -TrackListEntry::getById(Session& session, IdType id) +TrackListEntry::getById(Session& session, TrackListEntryId id) { session.checkSharedLocked(); - return session.getDboSession().find().where("id = ?").bind(id); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } } // namespace Database diff --git a/src/libs/database/impl/Traits.hpp b/src/libs/database/impl/Traits.hpp new file mode 100644 index 00000000..bdfa0045 --- /dev/null +++ b/src/libs/database/impl/Traits.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 "database/Types.hpp" + +namespace Wt::Dbo +{ + + template + struct sql_value_traits::value>::type> + { + static_assert(!std::is_same_v, "Cannot use IdType, use derived types"); + static const bool specialized = true; + + static std::string type(SqlConnection *conn, int size) + { + return sql_value_traits::type(conn, size); + } + + static void bind(const T& v, SqlStatement *statement, int column, int size) + { + sql_value_traits::bind(v.getValue(), statement, column, size); + } + + static bool read(T& v, SqlStatement *statement, int column, int size) + { + typename T::ValueType value; + if (sql_value_traits::read(value, statement, column, size)) + { + v = value; + return true; + } + + v = {}; + return false; + } + }; +} + diff --git a/src/libs/database/impl/User.cpp b/src/libs/database/impl/User.cpp index 9a6deb73..44f8c2b1 100644 --- a/src/libs/database/impl/User.cpp +++ b/src/libs/database/impl/User.cpp @@ -26,25 +26,25 @@ #include "database/TrackList.hpp" #include "utils/Logger.hpp" #include "StringViewTraits.hpp" +#include "Traits.hpp" namespace Database { -AuthToken::AuthToken(const std::string& value, const Wt::WDateTime& expiry, Wt::Dbo::ptr user) +AuthToken::AuthToken(const std::string& value, const Wt::WDateTime& expiry, ObjectPtr user) : _value {value} , _expiry {expiry} -, _user {user} +, _user {getDboPtr(user)} { } AuthToken::pointer -AuthToken::create(Session& session, const std::string& value, const Wt::WDateTime& expiry, Wt::Dbo::ptr user) +AuthToken::create(Session& session, const std::string& value, const Wt::WDateTime& expiry, ObjectPtr user) { session.checkUniqueLocked(); - auto res {session.getDboSession().add(std::make_unique(value, expiry, user))}; - + AuthToken::pointer res {session.getDboSession().add(std::make_unique(value, expiry, user))}; session.getDboSession().flush(); return res; @@ -65,7 +65,8 @@ AuthToken::getByValue(Session& session, const std::string& value) session.checkSharedLocked(); return session.getDboSession().find() - .where("value = ?").bind(value); + .where("value = ?").bind(value) + .resultValue(); } static const std::string queuedListName {"__queued_tracks__"}; @@ -80,17 +81,17 @@ User::getAll(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().find(); + auto res {session.getDboSession().find().resultList()}; return std::vector(res.begin(), res.end()); } -std::vector +std::vector User::getAllIds(Session& session) { session.checkSharedLocked(); - Wt::Dbo::collection res = session.getDboSession().query("SELECT id FROM user"); - return std::vector(res.begin(), res.end()); + auto res {session.getDboSession().query("SELECT id FROM user").resultList()}; + return std::vector(res.begin(), res.end()); } User::pointer @@ -98,8 +99,7 @@ User::getDemo(Session& session) { session.checkSharedLocked(); - pointer res = session.getDboSession().find().where("type = ?").bind(UserType::DEMO); - return res; + return session.getDboSession().find().where("type = ?").bind(UserType::DEMO).resultValue(); } std::size_t @@ -125,16 +125,17 @@ User::create(Session& session, std::string_view loginName) } User::pointer -User::getById(Session& session, IdType id) +User::getById(Session& session, UserId id) { - return session.getDboSession().find().where("id = ?").bind( id ); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); } User::pointer User::getByLoginName(Session& session, std::string_view name) { return session.getDboSession().find() - .where("login_name = ?").bind(name); + .where("login_name = ?").bind(name) + .resultValue(); } void @@ -150,7 +151,7 @@ User::clearAuthTokens() _authTokens.clear(); } -Wt::Dbo::ptr +TrackList::pointer User::getQueuedTrackList(Session& session) const { assert(self()); @@ -160,63 +161,63 @@ User::getQueuedTrackList(Session& session) const } void -User::starArtist(Wt::Dbo::ptr artist) +User::starArtist(ObjectPtr artist) { - if (_starredArtists.count(artist) == 0) - _starredArtists.insert(artist); + if (_starredArtists.count(getDboPtr(artist)) == 0) + _starredArtists.insert(getDboPtr(artist)); } void -User::unstarArtist(Wt::Dbo::ptr artist) +User::unstarArtist(ObjectPtr artist) { - if (_starredArtists.count(artist) != 0) - _starredArtists.erase(artist); + if (_starredArtists.count(getDboPtr(artist)) != 0) + _starredArtists.erase(getDboPtr(artist)); } bool -User::hasStarredArtist(Wt::Dbo::ptr artist) const +User::hasStarredArtist(ObjectPtr artist) const { - return _starredArtists.count(artist) != 0; + return _starredArtists.count(getDboPtr(artist)) != 0; } void -User::starRelease(Wt::Dbo::ptr release) +User::starRelease(ObjectPtr release) { - if (_starredReleases.count(release) == 0) - _starredReleases.insert(release); + if (_starredReleases.count(getDboPtr(release)) == 0) + _starredReleases.insert(getDboPtr(release)); } void -User::unstarRelease(Wt::Dbo::ptr release) +User::unstarRelease(ObjectPtr release) { - if (_starredReleases.count(release) != 0) - _starredReleases.erase(release); + if (_starredReleases.count(getDboPtr(release)) != 0) + _starredReleases.erase(getDboPtr(release)); } bool -User::hasStarredRelease(Wt::Dbo::ptr release) const +User::hasStarredRelease(ObjectPtr release) const { - return _starredReleases.count(release) != 0; + return _starredReleases.count(getDboPtr(release)) != 0; } void -User::starTrack(Wt::Dbo::ptr track) +User::starTrack(ObjectPtr track) { - if (_starredTracks.count(track) == 0) - _starredTracks.insert(track); + if (_starredTracks.count(getDboPtr(track)) == 0) + _starredTracks.insert(getDboPtr(track)); } void -User::unstarTrack(Wt::Dbo::ptr track) +User::unstarTrack(ObjectPtr track) { - if (_starredTracks.count(track) != 0) - _starredTracks.erase(track); + if (_starredTracks.count(getDboPtr(track)) != 0) + _starredTracks.erase(getDboPtr(track)); } bool -User::hasStarredTrack(Wt::Dbo::ptr track) const +User::hasStarredTrack(ObjectPtr track) const { - return _starredTracks.count(track) != 0; + return _starredTracks.count(getDboPtr(track)) != 0; } } // namespace Database diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/database/include/database/Artist.hpp index a9ef7c81..d5bab873 100644 --- a/src/libs/database/include/database/Artist.hpp +++ b/src/libs/database/include/database/Artist.hpp @@ -22,17 +22,15 @@ #include #include #include -#include #include #include #include +#include "database/Types.hpp" #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" -#include "Types.hpp" - namespace Database { @@ -44,10 +42,9 @@ class Track; class TrackArtistLink; class User; -class Artist : public Wt::Dbo::Dbo +class Artist : public Object { public: - enum class SortMethod { None, @@ -55,43 +52,41 @@ class Artist : public Wt::Dbo::Dbo BySortName, }; - using pointer = Wt::Dbo::ptr; - - Artist() {} + 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, IdType id); + static pointer getById(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::set& clusters, // at least one track that belongs to these clusters + const std::vector& clusters, // at least one track that belongs to these clusters SortMethod sortMethod ); static std::vector getByFilter(Session& session, - const std::set& clusters, // if non empty, at least one artist that belongs to these clusters + 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::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::set& clusters, std::optional linkType, std::optional size = {}); - static std::vector getAllOrphans(Session& session); // No track related - static std::vector getLastWritten(Session& session, + 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::set& clusters, + 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 getAllIdsWithClusters(Session& session, std::optional limit = {}); static std::vector getStarred(Session& session, - Wt::Dbo::ptr user, - const std::set& clusters, + 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); @@ -101,12 +96,12 @@ class Artist : public Wt::Dbo::Dbo const std::string& getSortName() const { return _sortName; } std::optional getMBID() const { return UUID::fromString(_MBID); } - std::vector> getReleases(const std::set& 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; + 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; - std::vector> getRandomTracks(std::optional count) const; + std::vector> getNonReleaseTracks(std::optional linkType, std::optional range, bool& moreResults) 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; @@ -114,14 +109,14 @@ class Artist : public Wt::Dbo::Dbo // Get the cluster of the tracks made by this artist // Each clusters are grouped by cluster type, sorted by the number of occurence // size is the max number of cluster per cluster type - std::vector>> getClusterGroups(std::vector> clusterTypes, std::size_t size) const; + std::vector>> getClusterGroups(std::vector> clusterTypes, std::size_t size) const; void setName(std::string_view name) { _name = name; } void setMBID(const std::optional& mbid) { _MBID = mbid ? mbid->getAsString() : ""; } void setSortName(const std::string& sortName); // Create - static pointer create(Session& session, const std::string& name, const std::optional& UUID = {}); + static pointer create(Session& session, const std::string& name, const std::optional& UUID = {}); template void persist(Action& a) diff --git a/src/libs/database/include/database/Cluster.hpp b/src/libs/database/include/database/Cluster.hpp index 71e0da7f..42a11e77 100644 --- a/src/libs/database/include/database/Cluster.hpp +++ b/src/libs/database/include/database/Cluster.hpp @@ -24,10 +24,9 @@ #include #include - #include -#include "Types.hpp" +#include "database/Types.hpp" namespace Database { @@ -36,31 +35,29 @@ class ClusterType; class ScanSettings; class Session; -class Cluster : public Wt::Dbo::Dbo +class Cluster : public Object { public: - using pointer = Wt::Dbo::ptr; - - Cluster(); - Cluster(Wt::Dbo::ptr type, std::string_view name); + Cluster() = default; + 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, IdType id); + static pointer getById(Session& session, ClusterId id); // Create utility - static pointer create(Session& session, Wt::Dbo::ptr type, std::string_view name); + static pointer create(Session& session, ObjectPtr type, std::string_view name); // Accessors const std::string& getName() const { return _name; } - Wt::Dbo::ptr getType() const { return _clusterType; } + ObjectPtr getType() const { return _clusterType; } std::size_t getTracksCount() const { return _tracks.size(); } - std::vector> getTracks(std::optional offset = {}, std::optional limit = {}) const; - std::set getTrackIds() const; + std::vector> getTracks(std::optional offset = {}, std::optional limit = {}) const; + std::vector getTrackIds() const; std::size_t getReleasesCount() const; - void addTrack(Wt::Dbo::ptr track); + void addTrack(ObjectPtr track); template void persist(Action& a) @@ -72,7 +69,6 @@ class Cluster : public Wt::Dbo::Dbo } private: - static const std::size_t _maxNameLength = 128; std::string _name; @@ -82,19 +78,17 @@ class Cluster : public Wt::Dbo::Dbo }; -class ClusterType : public Wt::Dbo::Dbo +class ClusterType : public Object { public: + ClusterType() = default; + ClusterType(std::string_view name); - using pointer = Wt::Dbo::ptr; - - ClusterType() {} - ClusterType(std::string 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, IdType id); + static pointer getById(Session& session, ClusterTypeId id); static std::vector getAll(Session& session); static pointer create(Session& session, const std::string& name); diff --git a/src/libs/database/include/database/Release.hpp b/src/libs/database/include/database/Release.hpp index f3be2da3..d39b1469 100644 --- a/src/libs/database/include/database/Release.hpp +++ b/src/libs/database/include/database/Release.hpp @@ -20,13 +20,13 @@ #pragma once #include -#include +#include #include #include +#include "database/Types.hpp" #include "utils/UUID.hpp" -#include "Types.hpp" namespace Database { @@ -39,49 +39,46 @@ class Session; class Track; class User; -class Release : public Wt::Dbo::Dbo +class Release : public Object { public: - - using pointer = Wt::Dbo::ptr; - - Release() {} + 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, IdType id); + static pointer getById(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 getAllIds(Session& session); static std::vector getAllOrderedByArtist(Session& session, std::optional offset = {}, std::optional size = {}); - static std::vector getAllRandom(Session& session, const std::set& clusters, std::optional size = {}); - static std::vector getAllIdsRandom(Session& session, const std::set& clusters, std::optional size = {}); - static std::vector getLastWritten(Session& session, std::optional after, const std::set& clusters, std::optional range, bool& moreResults); + 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, Wt::Dbo::ptr user, const std::set& clusters, std::optional range, bool& moreResults); + static std::vector getStarred(Session& session, ObjectPtr user, const std::vector& clusters, std::optional range, bool& moreResults); - static std::vector getByClusters(Session& session, const std::set& clusters); + static std::vector getByClusters(Session& session, const std::vector& clusters); static std::vector getByFilter(Session& session, - const std::set& clusters, // if non empty, at least one release that belongs to these clusters + 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 = {}); + static std::vector getAllIdsWithClusters(Session& session, std::optional limit = {}); - std::vector> getTracks(const std::set& clusters = std::set()) const; - std::size_t getTracksCount() const; - Wt::Dbo::ptr 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) // size is the max number of cluster per cluster type - std::vector>> getClusterGroups(std::vector> clusterTypes, std::size_t size) const; + std::vector>> getClusterGroups(const std::vector>& clusterTypes, std::size_t size) const; // Create - static pointer create(Session& session, const std::string& name, const std::optional& MBID = {}); + static pointer create(Session& session, const std::string& name, const std::optional& MBID = {}); // Utility functions std::optional getReleaseYear(bool originalDate = false) const; // 0 if unknown or various @@ -97,8 +94,8 @@ class Release : public Wt::Dbo::Dbo Wt::WDateTime getLastWritten() const; // Get the artists of this release - std::vector > getArtists(TrackArtistLinkType type = TrackArtistLinkType::Artist) const; - std::vector > getReleaseArtists() const { return getArtists(TrackArtistLinkType::ReleaseArtist); } + std::vector > getArtists(TrackArtistLinkType type = TrackArtistLinkType::Artist) const; + std::vector > getReleaseArtists() const { return getArtists(TrackArtistLinkType::ReleaseArtist); } bool hasVariousArtists() const; std::vector getSimilarReleases(std::optional offset = {}, std::optional count = {}) const; diff --git a/src/libs/database/include/database/ScanSettings.hpp b/src/libs/database/include/database/ScanSettings.hpp index 3451137a..9cb8cb5a 100644 --- a/src/libs/database/include/database/ScanSettings.hpp +++ b/src/libs/database/include/database/ScanSettings.hpp @@ -19,23 +19,22 @@ #pragma once -#include +#include +#include #include #include -#include "utils/Path.hpp" +#include "database/Types.hpp" namespace Database { class ClusterType; class Session; -class ScanSettings : public Wt::Dbo::Dbo +class ScanSettings : public Object { public: - using pointer = Wt::Dbo::ptr; - // Do not modify values (just add) enum class UpdatePeriod { Never = 0, @@ -61,8 +60,8 @@ class ScanSettings : public Wt::Dbo::Dbo std::filesystem::path getMediaDirectory() const { return _mediaDirectory; } Wt::WTime getUpdateStartTime() const { return _startTime; } UpdatePeriod getUpdatePeriod() const { return _updatePeriod; } - std::vector> getClusterTypes() const; - std::unordered_set getAudioFileExtensions() const; + std::vector> getClusterTypes() const; + std::vector getAudioFileExtensions() const; RecommendationEngineType getRecommendationEngineType() const { return _recommendationEngineType; } // Setters diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/database/include/database/Track.hpp index 7fee6355..edfdbecc 100644 --- a/src/libs/database/include/database/Track.hpp +++ b/src/libs/database/include/database/Track.hpp @@ -34,7 +34,7 @@ #include "utils/EnumSet.hpp" #include "utils/UUID.hpp" -#include "Types.hpp" +#include "database/Types.hpp" namespace Database { @@ -49,46 +49,43 @@ class TrackListEntry; class TrackStats; class User; -class Track : public Wt::Dbo::Dbo +class Track : public Object { public: - - using pointer = Wt::Dbo::ptr; - - Track() {} + 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, IdType id); + static pointer getById(Session& session, TrackId id); static std::vector getByRecordingMBID(Session& session, const UUID& MBID); static std::vector getSimilarTracks(Session& session, - const std::unordered_set& trackIds, + const std::vector& trackIds, std::optional offset = {}, std::optional size = {}); static std::vector getByClusters(Session& session, - const std::set& clusters); // tracks that belong to these clusters + const std::vector& clusters); // tracks that belong to these clusters static std::vector getByFilter(Session& session, - const std::set& clusters, // if non empty, tracks that belong to these clusters + 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::vector getAll(Session& session, std::optional limit = std::nullopt); - static std::vector getAllRandom(Session& session, const std::set& clusters, std::optional limit = std::nullopt); - static std::vector getAllIdsRandom(Session& session, const std::set& 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 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::set& clusters, std::optional range, bool& moreResults); + 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 getAllIdsWithFeatures(Session& session, std::optional limit = {}); + static std::vector getAllIdsWithClusters(Session& session, std::optional limit = {}); static std::vector getStarred(Session& session, - Wt::Dbo::ptr user, - const std::set& clusters, + ObjectPtr user, + const std::vector& clusters, std::optional range, bool& hasMore); // Create utility @@ -115,10 +112,10 @@ class Track : public Wt::Dbo::Dbo void setTrackReplayGain(float replayGain) { _trackReplayGain = replayGain; } void setReleaseReplayGain(float replayGain) { _releaseReplayGain = replayGain; } void clearArtistLinks(); - void addArtistLink(const Wt::Dbo::ptr& artistLink); - void setRelease(Wt::Dbo::ptr release) { _release = release; } - void setClusters(const std::vector>& clusters ); - void setFeatures(const Wt::Dbo::ptr& features); + 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; @@ -143,16 +140,16 @@ class Track : public Wt::Dbo::Dbo std::optional getReleaseReplayGain() const { return _releaseReplayGain; } // no artistLinkTypes means get all - std::vector> getArtists(EnumSet artistLinkTypes) const; - std::vector getArtistIds(EnumSet artistLinkTypes) const; - std::vector> getArtistLinks() const; - Wt::Dbo::ptr getRelease() const { return _release; } - std::vector> getClusters() const; - std::vector getClusterIds() const; - bool hasTrackFeatures() const; - Wt::Dbo::ptr getTrackFeatures() const; + std::vector> getArtists(EnumSet artistLinkTypes) const; + std::vector getArtistIds(EnumSet artistLinkTypes) const; + std::vector> getArtistLinks() const; + ObjectPtr getRelease() const { return _release; } + std::vector> getClusters() const; + std::vector getClusterIds() const; + bool hasTrackFeatures() const; + ObjectPtr getTrackFeatures() const; - std::vector>> getClusterGroups(std::vector> clusterTypes, std::size_t size) const; + std::vector>> getClusterGroups(const std::vector>& clusterTypes, std::size_t size) const; template void persist(Action& a) diff --git a/src/libs/database/include/database/TrackArtistLink.hpp b/src/libs/database/include/database/TrackArtistLink.hpp index 02a727e5..55d3b0c5 100644 --- a/src/libs/database/include/database/TrackArtistLink.hpp +++ b/src/libs/database/include/database/TrackArtistLink.hpp @@ -23,7 +23,7 @@ #include -#include "Types.hpp" +#include "database/Types.hpp" #include "utils/EnumSet.hpp" namespace Database @@ -33,20 +33,18 @@ namespace Database class Session; class Track; - class TrackArtistLink + class TrackArtistLink : public Object { public: - using pointer = Wt::Dbo::ptr; - TrackArtistLink() = default; - TrackArtistLink(Wt::Dbo::ptr track, Wt::Dbo::ptr artist, TrackArtistLinkType type); + TrackArtistLink(ObjectPtr track, ObjectPtr artist, TrackArtistLinkType type); - static pointer create(Session& session, Wt::Dbo::ptr track, Wt::Dbo::ptr artist, TrackArtistLinkType type); + static pointer create(Session& session, ObjectPtr track, ObjectPtr artist, TrackArtistLinkType type); static EnumSet getUsedTypes(Session& session); - Wt::Dbo::ptr getTrack() const { return _track; } - Wt::Dbo::ptr getArtist() const { return _artist; } + ObjectPtr getTrack() const { return _track; } + ObjectPtr getArtist() const { return _artist; } TrackArtistLinkType getType() const { return _type; } template diff --git a/src/libs/database/include/database/TrackBookmark.hpp b/src/libs/database/include/database/TrackBookmark.hpp index d5ff0c0c..2c4af0db 100644 --- a/src/libs/database/include/database/TrackBookmark.hpp +++ b/src/libs/database/include/database/TrackBookmark.hpp @@ -23,7 +23,7 @@ #include -#include "Types.hpp" +#include "database/Types.hpp" namespace Database { @@ -31,22 +31,20 @@ class Session; class Track; class User; -class TrackBookmark : public Wt::Dbo::Dbo +class TrackBookmark : public Object { public: - using pointer = Wt::Dbo::ptr; - TrackBookmark () = default; - TrackBookmark(Wt::Dbo::ptr user, Wt::Dbo::ptr track); + TrackBookmark(ObjectPtr user, ObjectPtr track); // utility - static pointer create(Session& session, Wt::Dbo::ptr user, Wt::Dbo::ptr track); + static pointer create(Session& session, ObjectPtr user, ObjectPtr track); // Find utility functions static std::vector getAll(Session& session); - static std::vector getByUser(Session& session, Wt::Dbo::ptr user); - static pointer getByUser(Session& session, Wt::Dbo::ptr user, Wt::Dbo::ptr track); - static pointer getById(Session& session, IdType id); + static std::vector getByUser(Session& session, ObjectPtr user); + static pointer getByUser(Session& session, ObjectPtr user, ObjectPtr track); + static pointer getById(Session& session, TrackBookmarkId id); // Setters void setOffset(std::chrono::milliseconds offset) { _offset = offset; } @@ -55,8 +53,8 @@ class TrackBookmark : public Wt::Dbo::Dbo // Getters std::chrono::milliseconds getOffset() const { return _offset; } std::string_view getComment() const { return _comment; } - Wt::Dbo::ptr getTrack() const { return _track; } - Wt::Dbo::ptr getUser() const { return _user; } + ObjectPtr getTrack() const { return _track; } + ObjectPtr getUser() const { return _user; } template void persist(Action& a) diff --git a/src/libs/database/include/database/TrackFeatures.hpp b/src/libs/database/include/database/TrackFeatures.hpp index 86b58679..99c36d6d 100644 --- a/src/libs/database/include/database/TrackFeatures.hpp +++ b/src/libs/database/include/database/TrackFeatures.hpp @@ -26,7 +26,7 @@ #include -#include "Types.hpp" +#include "database/Types.hpp" namespace Database { @@ -37,17 +37,14 @@ using FeatureName = std::string; using FeatureValues = std::vector; using FeatureValuesMap = std::unordered_map; -class TrackFeatures : public Wt::Dbo::Dbo +class TrackFeatures : public Object { public: - - using pointer = Wt::Dbo::ptr; - TrackFeatures() = default; - TrackFeatures(Wt::Dbo::ptr track, const std::string& jsonEncodedFeatures); + TrackFeatures(ObjectPtr track, const std::string& jsonEncodedFeatures); // Create utility - static pointer create(Session& session, Wt::Dbo::ptr 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; diff --git a/src/libs/database/include/database/TrackList.hpp b/src/libs/database/include/database/TrackList.hpp index d8505d23..03aaca0f 100644 --- a/src/libs/database/include/database/TrackList.hpp +++ b/src/libs/database/include/database/TrackList.hpp @@ -27,7 +27,7 @@ #include #include -#include "Types.hpp" +#include "database/Types.hpp" namespace Database { @@ -39,11 +39,9 @@ class Track; class TrackListEntry; class User; -class TrackList : public Wt::Dbo::Dbo +class TrackList : public Object { public: - using pointer = Wt::Dbo::ptr; - enum class Type { Playlist, // user controlled playlists @@ -51,28 +49,28 @@ class TrackList : public Wt::Dbo::Dbo }; TrackList() = default; - TrackList(std::string_view name, Type type, bool isPublic, Wt::Dbo::ptr user); + TrackList(std::string_view name, Type type, bool isPublic, ObjectPtr user); // Stats utility - std::vector> getTopArtists(const std::set& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const; - std::vector> getTopReleases(const std::set& clusterIds, std::optional range, bool& moreResults) const; - std::vector> getTopTracks(const std::set& 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, Wt::Dbo::ptr user); - static pointer getById(Session& session, IdType tracklistId); + 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, Wt::Dbo::ptr user); - static std::vector getAll(Session& session, Wt::Dbo::ptr user, Type type); + static std::vector getAll(Session& session, ObjectPtr user); + static std::vector getAll(Session& session, ObjectPtr user, Type type); // Create utility - static pointer create(Session& session, std::string_view name, Type type, bool isPublic, Wt::Dbo::ptr user); + 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; } - Wt::Dbo::ptr getUser() const { return _user; } + ObjectPtr getUser() const { return _user; } // Modifiers void setName(const std::string& name) { _name = name; } @@ -80,29 +78,29 @@ class TrackList : public Wt::Dbo::Dbo void clear() { _entries.clear(); } // Get tracks, ordered by position - bool isEmpty() const; - std::size_t getCount() const; - Wt::Dbo::ptr getEntry(std::size_t pos) const; - std::vector> getEntries(std::optional offset = {}, std::optional size = {}) const; - Wt::Dbo::ptr getEntryByTrackAndDateTime(Wt::Dbo::ptr track, const Wt::WDateTime& dateTime) const; + 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; + ObjectPtr getEntryByTrackAndDateTime(ObjectPtr track, const Wt::WDateTime& dateTime) const; // Get track bya - std::vector> getArtistsReverse(const std::set& clusterIds, std::optional linkType, std::optional range, bool& moreResults) const; - std::vector> getReleasesReverse(const std::set& clusterIds, std::optional range, bool& moreResults) const; - std::vector> getTracksReverse(const std::set& clusterIds, 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; - std::vector getTrackIds() const; + std::vector getTrackIds() const; std::chrono::milliseconds getDuration() const; // Get clusters, order by occurence - std::vector> getClusters() const; + std::vector> getClusters() const; - bool hasTrack(IdType trackId) const; + bool hasTrack(TrackId trackId) const; // Ordered from most clusters in common - std::vector> getSimilarTracks(std::optional offset = {}, std::optional size = {}) const; + std::vector> getSimilarTracks(std::optional offset = {}, std::optional size = {}) const; template void persist(Action& a) @@ -122,27 +120,24 @@ class TrackList : public Wt::Dbo::Dbo bool _isPublic {false}; Wt::Dbo::ptr _user; - Wt::Dbo::collection< Wt::Dbo::ptr > _entries; + Wt::Dbo::collection> _entries; }; -class TrackListEntry : public Wt::Dbo::Dbo +class TrackListEntry : public Object { public: - - using pointer = Wt::Dbo::ptr; - TrackListEntry() = default; - TrackListEntry(Wt::Dbo::ptr track, Wt::Dbo::ptr tracklist, const Wt::WDateTime& dateTime); + TrackListEntry(ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime); // find utility - static pointer getById(Session& session, IdType id); + static pointer getById(Session& session, TrackListEntryId id); // Create utility - static pointer create(Session& session, Wt::Dbo::ptr track, Wt::Dbo::ptr tracklist, const Wt::WDateTime& dateTime = Wt::WDateTime::currentDateTime()); + static pointer create(Session& session, ObjectPtr track, ObjectPtr tracklist, const Wt::WDateTime& dateTime = Wt::WDateTime::currentDateTime()); // Accessors - Wt::Dbo::ptr getTrack() const { return _track; } + ObjectPtr getTrack() const { return _track; } const Wt::WDateTime& getDateTime() const { return _dateTime; } template diff --git a/src/libs/database/include/database/Types.hpp b/src/libs/database/include/database/Types.hpp index 174bb65b..590518e3 100644 --- a/src/libs/database/include/database/Types.hpp +++ b/src/libs/database/include/database/Types.hpp @@ -20,16 +20,32 @@ #pragma once #include +#include +#include #include namespace Database { - using IdType = Wt::Dbo::dbo_default_traits::IdType; - - static inline bool IdIsValid(IdType id) + class IdType { - return id != Wt::Dbo::dbo_default_traits::invalidId(); - } + 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()}; + }; struct Range { @@ -78,5 +94,83 @@ namespace Database ADMIN = 1, DEMO = 2, }; + + 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::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; } + }; } +// 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/database/include/database/User.hpp b/src/libs/database/include/database/User.hpp index 2bd593fb..b94d41af 100644 --- a/src/libs/database/include/database/User.hpp +++ b/src/libs/database/include/database/User.hpp @@ -26,8 +26,8 @@ #include #include +#include "database/Types.hpp" #include "utils/UUID.hpp" -#include "Types.hpp" namespace Database { @@ -39,24 +39,21 @@ class TrackList; class Track; class User; -class AuthToken +class AuthToken : public Object { public: - - using pointer = Wt::Dbo::ptr; - AuthToken() = default; - AuthToken(const std::string& value, const Wt::WDateTime& expiry, Wt::Dbo::ptr user); + 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, Wt::Dbo::ptr user); + 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, IdType tokenId); + static pointer getById(Session& session, AuthTokenId tokenId); // Accessors const Wt::WDateTime& getExpiry() const { return _expiry; } - Wt::Dbo::ptr getUser() const { return _user; } + ObjectPtr getUser() const { return _user; } const std::string& getValue() const { return _value; } template @@ -75,11 +72,9 @@ class AuthToken Wt::Dbo::ptr _user; }; -class User : public Wt::Dbo::Dbo +class User : public Object { public: - using pointer = Wt::Dbo::ptr; - struct PasswordHash { std::string salt; @@ -120,17 +115,16 @@ class User : public Wt::Dbo::Dbo static inline const SubsonicArtistListMode defaultSubsonicArtistListMode {SubsonicArtistListMode::AllArtists}; static inline const Scrobbler defaultScrobbler {Scrobbler::Internal}; - User() = default; User(std::string_view loginName); // utility static pointer create(Session& session, std::string_view loginName); - static pointer getById(Session& session, IdType id); + 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 std::vector getAllIds(Session& session); static pointer getDemo(Session& session); static std::size_t getCount(Session& session); @@ -171,20 +165,20 @@ class User : public Wt::Dbo::Dbo Scrobbler getScrobbler() const { return _scrobbler; } std::optional getListenBrainzToken() const { return UUID::fromString(_listenbrainzToken); } - Wt::Dbo::ptr getQueuedTrackList(Session& session) const; + ObjectPtr getQueuedTrackList(Session& session) const; - void starArtist(Wt::Dbo::ptr artist); - void unstarArtist(Wt::Dbo::ptr artist); - bool hasStarredArtist(Wt::Dbo::ptr artist) const; + void starArtist(ObjectPtr artist); + void unstarArtist(ObjectPtr artist); + bool hasStarredArtist(ObjectPtr artist) const; - void starRelease(Wt::Dbo::ptr release); - void unstarRelease(Wt::Dbo::ptr release); - bool hasStarredRelease(Wt::Dbo::ptr release) const; + void starRelease(ObjectPtr release); + void unstarRelease(ObjectPtr release); + bool hasStarredRelease(ObjectPtr release) const; // Stars - void starTrack(Wt::Dbo::ptr track); - void unstarTrack(Wt::Dbo::ptr track); - bool hasStarredTrack(Wt::Dbo::ptr track) const; + void starTrack(ObjectPtr track); + void unstarTrack(ObjectPtr track); + bool hasStarredTrack(ObjectPtr track) const; template void persist(Action& a) diff --git a/src/libs/recommendation/CMakeLists.txt b/src/libs/recommendation/CMakeLists.txt index 7bb9696f..890cf3f9 100644 --- a/src/libs/recommendation/CMakeLists.txt +++ b/src/libs/recommendation/CMakeLists.txt @@ -1,8 +1,8 @@ add_library(lmsrecommendation SHARED impl/clusters/ClustersClassifier.cpp - impl/features/FeaturesClassifierCache.cpp - impl/features/FeaturesClassifier.cpp + impl/features/FeaturesEngineCache.cpp + impl/features/FeaturesEngine.cpp impl/features/FeaturesDefs.cpp impl/Engine.cpp ) diff --git a/src/libs/recommendation/impl/Engine.cpp b/src/libs/recommendation/impl/Engine.cpp index 717b087c..741a9dad 100644 --- a/src/libs/recommendation/impl/Engine.cpp +++ b/src/libs/recommendation/impl/Engine.cpp @@ -23,7 +23,7 @@ #include #include "ClustersClassifierCreator.hpp" -#include "FeaturesClassifierCreator.hpp" +#include "FeaturesEngineCreator.hpp" #include "database/Db.hpp" #include "database/Session.hpp" @@ -45,7 +45,7 @@ createClassifier(ClassifierType type) break; case ClassifierType::Features: - return createFeaturesClassifier(); + return createFeaturesEngine(); break; } @@ -63,10 +63,10 @@ Engine::Engine(Database::Db& db) { } -std::unordered_set -Engine::getSimilarTracksFromTrackList(Database::Session& session, Database::IdType trackListId, std::size_t maxCount) +Engine::TrackContainer +Engine::getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId trackListId, std::size_t maxCount) { - std::unordered_set res; + TrackContainer res; std::shared_lock lock {_classifiersMutex}; for (const auto& classifierName : _classifierPriorities) @@ -83,10 +83,10 @@ Engine::getSimilarTracksFromTrackList(Database::Session& session, Database::IdTy return res; } -std::unordered_set -Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set& trackIds, std::size_t maxCount) +Engine::TrackContainer +Engine::getSimilarTracks(Database::Session& dbSession, const std::vector& trackIds, std::size_t maxCount) { - std::unordered_set res; + TrackContainer res; std::shared_lock lock {_classifiersMutex}; for (ClassifierType classifierType : _classifierPriorities) @@ -107,10 +107,10 @@ Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set< return res; } -std::unordered_set -Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std::size_t maxCount) +Engine::ReleaseContainer +Engine::getSimilarReleases(Database::Session& dbSession, Database::ReleaseId releaseId, std::size_t maxCount) { - std::unordered_set res; + ReleaseContainer res; std::shared_lock lock {_classifiersMutex}; for (ClassifierType classifierType : _classifierPriorities) @@ -131,13 +131,10 @@ Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releas return res; } -std::unordered_set -Engine::getSimilarArtists(Database::Session& dbSession, - Database::IdType artistId, - EnumSet linkTypes, - std::size_t maxCount) +Engine::ArtistContainer +Engine::getSimilarArtists(Database::Session& dbSession, Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) { - std::unordered_set res; + ArtistContainer res; std::shared_lock lock {_classifiersMutex}; for (ClassifierType classifierType : _classifierPriorities) diff --git a/src/libs/recommendation/impl/Engine.hpp b/src/libs/recommendation/impl/Engine.hpp index 2e48e539..39338fbc 100644 --- a/src/libs/recommendation/impl/Engine.hpp +++ b/src/libs/recommendation/impl/Engine.hpp @@ -55,12 +55,13 @@ namespace Recommendation private: void load(bool forceReload, const ProgressCallback& progressCallback) override; void cancelLoad() override; + void requestCancelLoad() override {}; - ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) override; - ResultContainer getSimilarTracks(Database::Session& session, const std::unordered_set& tracksId, std::size_t maxCount) override; - ResultContainer getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) override; - ResultContainer getSimilarArtists(Database::Session& session, - Database::IdType artistId, + 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; @@ -79,7 +80,6 @@ namespace Recommendation using ClassifierContainer = std::unordered_map>; ClassifierContainer _classifiers; std::vector _classifierPriorities; // ordered by priority - }; } // ns Recommendation diff --git a/src/libs/recommendation/impl/FeaturesClassifierCreator.hpp b/src/libs/recommendation/impl/FeaturesEngineCreator.hpp similarity index 93% rename from src/libs/recommendation/impl/FeaturesClassifierCreator.hpp rename to src/libs/recommendation/impl/FeaturesEngineCreator.hpp index 7f754768..5b948e70 100644 --- a/src/libs/recommendation/impl/FeaturesClassifierCreator.hpp +++ b/src/libs/recommendation/impl/FeaturesEngineCreator.hpp @@ -24,6 +24,6 @@ namespace Recommendation { - std::unique_ptr createFeaturesClassifier(); + std::unique_ptr createFeaturesEngine(); } diff --git a/src/libs/recommendation/impl/IClassifier.hpp b/src/libs/recommendation/impl/IClassifier.hpp index 4670a424..62483d9e 100644 --- a/src/libs/recommendation/impl/IClassifier.hpp +++ b/src/libs/recommendation/impl/IClassifier.hpp @@ -21,9 +21,10 @@ #include #include -#include +#include #include "database/Types.hpp" +#include "recommendation/IRecommendation.hpp" #include "utils/EnumSet.hpp" namespace Database @@ -34,7 +35,7 @@ namespace Database namespace Recommendation { - class IClassifier + class IClassifier : public IRecommendation { public: virtual ~IClassifier() = default; @@ -50,13 +51,14 @@ namespace Recommendation virtual bool load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) = 0; virtual void requestCancelLoad() = 0; - using ResultContainer = std::unordered_set; + template + using ResultContainer = std::vector; - virtual ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) const = 0; - virtual ResultContainer getSimilarTracks(Database::Session& session, const std::unordered_set& tracksId, std::size_t maxCount) const = 0; - virtual ResultContainer getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) const = 0; - virtual ResultContainer getSimilarArtists(Database::Session& session, - Database::IdType artistId, + 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; }; diff --git a/src/libs/recommendation/impl/clusters/ClustersClassifier.cpp b/src/libs/recommendation/impl/clusters/ClustersClassifier.cpp index 7a9c9ea9..44f39a76 100644 --- a/src/libs/recommendation/impl/clusters/ClustersClassifier.cpp +++ b/src/libs/recommendation/impl/clusters/ClustersClassifier.cpp @@ -33,23 +33,23 @@ std::unique_ptr createClustersClassifier() return std::make_unique(); } -std::unordered_set -ClusterClassifier::getSimilarTracks(Database::Session& dbSession, const std::unordered_set& trackIds, std::size_t maxCount) const +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(); }); - std::unordered_set res; - std::transform(std::cbegin(tracks), std::cend(tracks), std::inserter(res, std::end(res)), - [](const auto& track) { return track.id(); }); return res; } -std::unordered_set -ClusterClassifier::getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) const +IClassifier::ResultContainer +ClusterClassifier::getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId tracklistId, std::size_t maxCount) const { - std::unordered_set res; + ResultContainer res; auto transaction {session.createSharedTransaction()}; @@ -58,16 +58,15 @@ ClusterClassifier::getSimilarTracksFromTrackList(Database::Session& session, Dat return res; const auto tracks {trackList->getSimilarTracks(0, maxCount)}; - std::transform(std::cbegin(tracks), std::cend(tracks), std::inserter(res, std::end(res)), - [](const Database::Track::pointer& track) { return track.id(); }); + std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const auto& track) { return track->getId(); }); return res; } -std::unordered_set -ClusterClassifier::getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std::size_t maxCount) const +IClassifier::ResultContainer +ClusterClassifier::getSimilarReleases(Database::Session& dbSession, Database::ReleaseId releaseId, std::size_t maxCount) const { - std::unordered_set res; + ResultContainer res; auto transaction {dbSession.createSharedTransaction()}; @@ -76,19 +75,18 @@ ClusterClassifier::getSimilarReleases(Database::Session& dbSession, Database::Id return res; const auto releases {release->getSimilarReleases(0, maxCount)}; - std::transform(std::cbegin(releases), std::cend(releases), std::inserter(res, std::end(res)), - [](const auto& release) { return release.id(); }); + std::transform(std::cbegin(releases), std::cend(releases), std::back_inserter(res), [](const auto& release) { return release->getId(); }); return res; } -std::unordered_set +IClassifier::ResultContainer ClusterClassifier::getSimilarArtists(Database::Session& dbSession, - Database::IdType artistId, + Database::ArtistId artistId, EnumSet artistLinkTypes, std::size_t maxCount) const { - std::unordered_set res; + ResultContainer res; auto transaction {dbSession.createSharedTransaction()}; @@ -97,8 +95,7 @@ ClusterClassifier::getSimilarArtists(Database::Session& dbSession, return res; const auto artists {artist->getSimilarArtists(artistLinkTypes, Database::Range {0, maxCount})}; - std::transform(std::cbegin(artists), std::cend(artists), std::inserter(res, std::end(res)), - [](const auto& artist) { return artist.id(); }); + std::transform(std::cbegin(artists), std::cend(artists), std::back_inserter(res), [](const auto& artist) { return artist->getId(); }); return res; } diff --git a/src/libs/recommendation/impl/clusters/ClustersClassifier.hpp b/src/libs/recommendation/impl/clusters/ClustersClassifier.hpp index dcb1a8dc..718d6872 100644 --- a/src/libs/recommendation/impl/clusters/ClustersClassifier.hpp +++ b/src/libs/recommendation/impl/clusters/ClustersClassifier.hpp @@ -40,11 +40,11 @@ namespace Recommendation bool load(Database::Session&, bool, const ProgressCallback&) override { return true; } void requestCancelLoad() override {} - ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) const override; - ResultContainer getSimilarTracks(Database::Session& session, const std::unordered_set& tracksId, std::size_t maxCount) const override; - ResultContainer getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) const override; - ResultContainer getSimilarArtists(Database::Session& session, - Database::IdType artistId, + 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; }; diff --git a/src/libs/recommendation/impl/features/FeaturesClassifier.hpp b/src/libs/recommendation/impl/features/FeaturesClassifier.hpp deleted file mode 100644 index 177962ac..00000000 --- a/src/libs/recommendation/impl/features/FeaturesClassifier.hpp +++ /dev/null @@ -1,117 +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 -#include -#include -#include - -#include "som/DataNormalizer.hpp" -#include "som/Network.hpp" -#include "FeaturesClassifierCache.hpp" -#include "FeaturesDefs.hpp" -#include "IClassifier.hpp" - -namespace Database -{ - class Session; -} - -namespace Recommendation { - -using FeatureWeight = double; - -class FeaturesClassifier : public IClassifier -{ - public: - FeaturesClassifier() = default; - FeaturesClassifier(const FeaturesClassifier&) = delete; - FeaturesClassifier(FeaturesClassifier&&) = delete; - FeaturesClassifier& operator=(const FeaturesClassifier&) = delete; - FeaturesClassifier& operator=(FeaturesClassifier&&) = delete; - - using FeaturesFetchFunc = std::function>>(Database::IdType /*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: - - std::string_view getName() const override { return "Features"; } - - bool load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) override; - void requestCancelLoad() override; - - std::unordered_set getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) const override; - std::unordered_set getSimilarTracks(Database::Session& session, const std::unordered_set& tracksId, std::size_t maxCount) const override; - std::unordered_set getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) const override; - std::unordered_set getSimilarArtists(Database::Session& session, - Database::IdType artistId, - EnumSet linkTypes, - std::size_t maxCount) const override; - - bool loadFromCache(Database::Session& session, const FeaturesClassifierCache& cache); - - // Use training (may be very slow) - struct TrainSettings - { - std::size_t iterationCount {10}; - float sampleCountPerNeuron {4}; - FeatureSettingsMap featureSettingsMap; - }; - bool loadFromTraining(Database::Session& session, const TrainSettings& trainSettings, const ProgressCallback& progressCallback); - - using ObjectPositions = std::unordered_map>; - using MatrixOfObjects = SOM::Matrix>; - - bool load(Database::Session& session, - SOM::Network network, - const ObjectPositions& tracksPosition); - - FeaturesClassifierCache toCache() const; - - static std::unordered_set getMatchingRefVectorsPosition(const std::unordered_set& ids, const ObjectPositions& objectPositions); - static std::unordered_set getObjectsIds(const std::unordered_set& positionSet, const MatrixOfObjects& objectsMap); - - std::unordered_set getSimilarObjects(const std::unordered_set& ids, - const SOM::Matrix>& objectsMap, - const ObjectPositions& objectPosition, - std::size_t maxCount) const; - - bool _loadCancelled {}; - std::unique_ptr _network; - double _networkRefVectorsDistanceMedian {}; - - ObjectPositions _artistPositions; - std::unordered_map _artistsMap; - - MatrixOfObjects _releasesMap; - ObjectPositions _releasePositions; - - MatrixOfObjects _tracksMap; - ObjectPositions _trackPositions; - - static inline FeaturesFetchFunc _featuresFetchFunc; -}; - -} // ns Recommendation diff --git a/src/libs/recommendation/impl/features/FeaturesClassifier.cpp b/src/libs/recommendation/impl/features/FeaturesEngine.cpp similarity index 56% rename from src/libs/recommendation/impl/features/FeaturesClassifier.cpp rename to src/libs/recommendation/impl/features/FeaturesEngine.cpp index f0ccf38d..03fb0db6 100644 --- a/src/libs/recommendation/impl/features/FeaturesClassifier.cpp +++ b/src/libs/recommendation/impl/features/FeaturesEngine.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "FeaturesClassifier.hpp" +#include "FeaturesEngine.hpp" #include @@ -35,13 +35,13 @@ namespace Recommendation { -std::unique_ptr createFeaturesClassifier() +std::unique_ptr createFeaturesEngine() { - return std::make_unique(); + return std::make_unique(); } const FeatureSettingsMap& -FeaturesClassifier::getDefaultTrainFeatureSettings() +FeaturesEngine::getDefaultTrainFeatureSettings() { static const FeatureSettingsMap defaultTrainFeatureSettings { @@ -57,16 +57,16 @@ FeaturesClassifier::getDefaultTrainFeatureSettings() static std::optional -getTrackFeatureValues(FeaturesClassifier::FeaturesFetchFunc func, Database::IdType trackId, const std::unordered_set& featureNames) +getTrackFeatureValues(FeaturesEngine::FeaturesFetchFunc func, Database::TrackId trackId, const std::unordered_set& featureNames) { return func(trackId, featureNames); } static std::optional -getTrackFeatureValuesFromDb(Database::Session& session, Database::IdType trackId, const std::unordered_set& featureNames) +getTrackFeatureValuesFromDb(Database::Session& session, Database::TrackId trackId, const std::unordered_set& featureNames) { - auto func = [&](Database::IdType trackId, const std::unordered_set& featureNames) + auto func = [&](Database::TrackId trackId, const std::unordered_set& featureNames) { std::optional res; @@ -128,7 +128,7 @@ getInputVectorWeights(const FeatureSettingsMap& featureSettingsMap, std::size_t } bool -FeaturesClassifier::loadFromTraining(Database::Session& session, const TrainSettings& trainSettings, const ProgressCallback& progressCallback) +FeaturesEngine::loadFromTraining(Database::Session& session, const TrainSettings& trainSettings, const ProgressCallback& progressCallback) { LMS_LOG(RECOMMENDATION, INFO) << "Constructing features classifier..."; @@ -141,7 +141,7 @@ FeaturesClassifier::loadFromTraining(Database::Session& session, const TrainSett LMS_LOG(RECOMMENDATION, DEBUG) << "Features dimension = " << nbDimensions; - std::vector trackIds; + std::vector trackIds; { auto transaction {session.createSharedTransaction()}; @@ -151,13 +151,13 @@ FeaturesClassifier::loadFromTraining(Database::Session& session, const TrainSett } std::vector samples; - std::vector samplesTrackIds; + std::vector samplesTrackIds; samples.reserve(trackIds.size()); samplesTrackIds.reserve(trackIds.size()); LMS_LOG(RECOMMENDATION, DEBUG) << "Extracting features..."; - for (Database::IdType trackId : trackIds) + for (Database::TrackId trackId : trackIds) { if (_loadCancelled) return false; @@ -223,7 +223,7 @@ FeaturesClassifier::loadFromTraining(Database::Session& session, const TrainSett return false; LMS_LOG(RECOMMENDATION, DEBUG) << "Classifying tracks..."; - ObjectPositions trackPositions; + TrackPositions trackPositions; for (std::size_t i {}; i < samples.size(); ++i) { if (_loadCancelled) @@ -231,7 +231,7 @@ FeaturesClassifier::loadFromTraining(Database::Session& session, const TrainSett const SOM::Position position {network.getClosestRefVectorPosition(samples[i])}; - trackPositions[samplesTrackIds[i]].insert(position); + trackPositions[samplesTrackIds[i]].push_back(position); } LMS_LOG(RECOMMENDATION, DEBUG) << "Classifying tracks DONE"; @@ -240,28 +240,25 @@ FeaturesClassifier::loadFromTraining(Database::Session& session, const TrainSett } bool -FeaturesClassifier::loadFromCache(Database::Session& session, const FeaturesClassifierCache& cache) +FeaturesEngine::loadFromCache(Database::Session& session, const FeaturesEngineCache& cache) { LMS_LOG(RECOMMENDATION, INFO) << "Constructing features classifier from cache..."; return load(session, std::move(cache._network), cache._trackPositions); } -std::unordered_set -FeaturesClassifier::getSimilarTracksFromTrackList(Database::Session& session, Database::IdType trackListId, std::size_t maxCount) const +IClassifier::ResultContainer +FeaturesEngine::getSimilarTracksFromTrackList(Database::Session& session, Database::TrackListId trackListId, std::size_t maxCount) const { - const std::unordered_set trackIds {[&] + const std::vector trackIds {[&] { - std::unordered_set res; + std::vector res; auto transaction {session.createSharedTransaction()}; const Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)}; if (trackList) - { - const std::vector orderedTrackIds {trackList->getTrackIds()}; - res = std::unordered_set(std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)); - } + res = trackList->getTrackIds(); return res; }()}; @@ -269,72 +266,64 @@ FeaturesClassifier::getSimilarTracksFromTrackList(Database::Session& session, Da return getSimilarTracks(session, trackIds, maxCount); } -std::unordered_set -FeaturesClassifier::getSimilarTracks(Database::Session& session, const std::unordered_set& tracksIds, std::size_t maxCount) const +std::vector +FeaturesEngine::getSimilarTracks(Database::Session& session, const std::vector& tracksIds, std::size_t maxCount) const { - auto similarTrackIds {getSimilarObjects(tracksIds, _tracksMap, _trackPositions, maxCount)}; - if (!similarTrackIds.empty()) + auto similarTrackIds {getSimilarObjects(tracksIds, _trackMatrix, _trackPositions, maxCount)}; + { - // Report only existing ids + // Report only existing ids, as tracks may have been removed a long time ago (refreshing the SOM takes some time) auto transaction {session.createSharedTransaction()}; - for (auto it {std::begin(similarTrackIds)}; it != std::end(similarTrackIds);) - { - const Database::IdType trackId {*it}; - if (!Database::Track::getById(session, trackId)) - it = similarTrackIds.erase(it); - else - it++; - } + similarTrackIds.erase(std::remove_if(std::begin(similarTrackIds), std::end(similarTrackIds), + [&](Database::TrackId trackId) + { + return Database::Track::getById(session, trackId); // TODO exists + }), std::end(similarTrackIds)); } return similarTrackIds; } -std::unordered_set -FeaturesClassifier::getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) const +std::vector +FeaturesEngine::getSimilarReleases(Database::Session& session, Database::ReleaseId releaseId, std::size_t maxCount) const { - auto similarReleaseIds {getSimilarObjects({releaseId}, _releasesMap, _releasePositions, maxCount)}; - if (!similarReleaseIds.empty()) + auto similarReleaseIds {getSimilarObjects({releaseId}, _releaseMatrix, _releasePositions, maxCount)}; + { // Report only existing ids auto transaction {session.createSharedTransaction()}; - for (auto it {std::begin(similarReleaseIds)}; it != std::end(similarReleaseIds);) - { - const Database::IdType similarReleaseId {*it}; - if (!Database::Release::getById(session, similarReleaseId)) - it = similarReleaseIds.erase(it); - else - it++; - } + similarReleaseIds.erase(std::remove_if(std::begin(similarReleaseIds), std::end(similarReleaseIds), + [&](Database::ReleaseId releaseId) + { + return Database::Release::getById(session, releaseId); // TODO exists + }), std::end(similarReleaseIds)); } return similarReleaseIds; } -std::unordered_set -FeaturesClassifier::getSimilarArtists(Database::Session& session, - Database::IdType artistId, +std::vector +FeaturesEngine::getSimilarArtists(Database::Session& session, + Database::ArtistId artistId, EnumSet linkTypes, std::size_t maxCount) const { auto getSimilarArtistIdsForLinkType {[&] (Database::TrackArtistLinkType linkType) { - std::unordered_set similarArtistIds; + std::vector similarArtistIds; - const auto itArtists {_artistsMap.find(linkType)}; - if (itArtists == std::cend(_artistsMap)) + const auto itArtists {_artistMatrix.find(linkType)}; + if (itArtists == std::cend(_artistMatrix)) { return similarArtistIds; } - similarArtistIds = getSimilarObjects({artistId}, itArtists->second, _artistPositions, maxCount); - - return similarArtistIds; + return getSimilarObjects({artistId}, itArtists->second, _artistPositions, maxCount); }}; - std::unordered_set similarArtistIds; + std::unordered_set similarArtistIds; for (Database::TrackArtistLinkType linkType : linkTypes) { @@ -342,44 +331,42 @@ FeaturesClassifier::getSimilarArtists(Database::Session& session, similarArtistIds.insert(std::begin(similarArtistIdsForLinkType), std::end(similarArtistIdsForLinkType)); } - if (!similarArtistIds.empty()) + std::vector res(std::cbegin(similarArtistIds), std::cend(similarArtistIds)); + { // Report only existing ids auto transaction {session.createSharedTransaction()}; - for (auto it {std::begin(similarArtistIds)}; it != std::end(similarArtistIds);) - { - const Database::IdType similarArtistId {*it}; - if (!Database::Artist::getById(session, similarArtistId)) - it = similarArtistIds.erase(it); - else - it++; - } + 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)); } - while (similarArtistIds.size() > maxCount) - similarArtistIds.erase(Random::pickRandom(similarArtistIds)); + while (res.size() > maxCount) + res.erase(Random::pickRandom(res)); - return similarArtistIds; + return res; } -FeaturesClassifierCache -FeaturesClassifier::toCache() const +FeaturesEngineCache +FeaturesEngine::toCache() const { - return FeaturesClassifierCache {*_network, _trackPositions}; + return FeaturesEngineCache {*_network, _trackPositions}; } bool -FeaturesClassifier::load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) +FeaturesEngine::load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) { if (forceReload) { - FeaturesClassifierCache::invalidate(); + FeaturesEngineCache::invalidate(); } else { - const std::optional cache {FeaturesClassifierCache::read()}; + const std::optional cache {FeaturesEngineCache::read()}; if (cache) return loadFromCache(session, *cache); } @@ -395,64 +382,65 @@ FeaturesClassifier::load(Database::Session& session, bool forceReload, const Pro } void -FeaturesClassifier::requestCancelLoad() +FeaturesEngine::requestCancelLoad() { LMS_LOG(RECOMMENDATION, DEBUG) << "Requesting init cancellation"; _loadCancelled = true; } bool -FeaturesClassifier::load(Database::Session& session, +FeaturesEngine::load(Database::Session& session, SOM::Network network, - const ObjectPositions& tracksPosition) + const TrackPositions& trackPositions) { + using namespace Database; + _networkRefVectorsDistanceMedian = network.computeRefVectorsDistanceMedian(); LMS_LOG(RECOMMENDATION, DEBUG) << "Median distance betweend ref vectors = " << _networkRefVectorsDistanceMedian; const SOM::Coordinate width {network.getWidth()}; const SOM::Coordinate height {network.getHeight()}; - _releasesMap = MatrixOfObjects {width, height}; - _tracksMap = MatrixOfObjects {width, height}; + _releaseMatrix = ReleaseMatrix {width, height}; + _trackMatrix = TrackMatrix {width, height}; LMS_LOG(RECOMMENDATION, DEBUG) << "Constructing maps..."; - for (auto itTrackCoord : tracksPosition) + for (const auto& [trackId, positions] : trackPositions) { if (_loadCancelled) return false; auto transaction {session.createSharedTransaction()}; - Database::IdType trackId {itTrackCoord.first}; - const std::unordered_set& positionSet {itTrackCoord.second}; - - const Database::Track::pointer track {Database::Track::getById(session, trackId)}; + const Track::pointer track {Database::Track::getById(session, trackId)}; if (!track) continue; - for (const SOM::Position& position : positionSet) + for (const SOM::Position& position : positions) { - _tracksMap[position].insert(trackId); - _trackPositions[trackId].insert(position); + Utils::push_back_if_not_present(_trackPositions[trackId], position); + Utils::push_back_if_not_present(_trackMatrix[position], trackId); - if (track->getRelease()) + if (Release::pointer release {track->getRelease()}) { - _releasePositions[track->getRelease().id()].insert(position); - _releasesMap[position].insert(track->getRelease().id()); + const ReleaseId releaseId {release->getId()}; + Utils::push_back_if_not_present(_releasePositions[releaseId], position); + Utils::push_back_if_not_present(_releaseMatrix[position], releaseId); } - for (const auto& artistLink : track->getArtistLinks()) + for (const TrackArtistLink::pointer& artistLink : track->getArtistLinks()) { - _artistPositions[artistLink->getArtist().id()].insert(position); - auto itArtists {_artistsMap.find(artistLink->getType())}; - if (itArtists == std::cend(_artistsMap)) + const ArtistId artistId {artistLink->getArtist()->getId()}; + + Utils::push_back_if_not_present(_artistPositions[artistId], position); + auto itArtists {_artistMatrix.find(artistLink->getType())}; + if (itArtists == std::cend(_artistMatrix)) { - auto [it, inserted] = _artistsMap.try_emplace(artistLink->getType(), MatrixOfObjects {}); + auto [it, inserted] = _artistMatrix.try_emplace(artistLink->getType(), ArtistMatrix {width, height}); assert(inserted); itArtists = it; - itArtists->second = MatrixOfObjects {width, height}; } - itArtists->second[position].insert(artistLink->getArtist().id()); + Utils::push_back_if_not_present(itArtists->second[position], artistId); } } } @@ -464,83 +452,4 @@ FeaturesClassifier::load(Database::Session& session, return true; } -std::unordered_set -FeaturesClassifier::getMatchingRefVectorsPosition(const std::unordered_set& ids, const ObjectPositions& objectPositions) -{ - std::unordered_set res; - - if (ids.empty()) - return res; - - for (auto id : ids) - { - auto it = objectPositions.find(id); - if (it == objectPositions.end()) - continue; - - for (const auto& position : it->second) - res.insert(position); - } - - return res; -} - -std::unordered_set -FeaturesClassifier::getObjectsIds(const std::unordered_set& positionSet, const MatrixOfObjects& objectsMap) -{ - std::unordered_set res; - - for (const auto& position : positionSet) - { - for (auto id : objectsMap.get(position)) - res.insert(id); - } - - return res; -} - -std::unordered_set -FeaturesClassifier::getSimilarObjects(const std::unordered_set& ids, - const MatrixOfObjects& objectsMap, - const ObjectPositions& objectPosition, - std::size_t maxCount) const -{ - std::unordered_set res; - - std::unordered_set searchedRefVectorsPosition {getMatchingRefVectorsPosition(ids, objectPosition)}; - if (searchedRefVectorsPosition.empty()) - return res; - - while (1) - { - std::unordered_set closestObjectIds {getObjectsIds(searchedRefVectorsPosition, objectsMap)}; - - // Remove objects that are already in input or already reported - for (auto id : ids) - closestObjectIds.erase(id); - - for (auto it {std::cbegin(closestObjectIds)}; it != std::cend(closestObjectIds); ++it) - { - if (res.size() == maxCount) - break; - - res.insert(*it); - } - - if (res.size() == maxCount) - break; - - // If there is not enough objects, try again with closest neighbour until there is too much distance - const std::optional closestRefVectorPosition {_network->getClosestRefVectorPosition(searchedRefVectorsPosition, _networkRefVectorsDistanceMedian * 0.75)}; - if (!closestRefVectorPosition) - break; - - searchedRefVectorsPosition.insert(closestRefVectorPosition.value()); - } - - return res; -} - - - } // ns Recommendation diff --git a/src/libs/recommendation/impl/features/FeaturesEngine.hpp b/src/libs/recommendation/impl/features/FeaturesEngine.hpp new file mode 100644 index 00000000..9ed313a4 --- /dev/null +++ b/src/libs/recommendation/impl/features/FeaturesEngine.hpp @@ -0,0 +1,216 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "som/DataNormalizer.hpp" +#include "som/Network.hpp" +#include "utils/Utils.hpp" +#include "FeaturesEngineCache.hpp" +#include "FeaturesDefs.hpp" +#include "IClassifier.hpp" + +namespace Database +{ + class Session; +} + +namespace Recommendation { + +using FeatureWeight = double; + +class FeaturesEngine : public IClassifier +{ + public: + FeaturesEngine() = default; + FeaturesEngine(const FeaturesEngine&) = delete; + FeaturesEngine(FeaturesEngine&&) = delete; + FeaturesEngine& operator=(const FeaturesEngine&) = delete; + FeaturesEngine& operator=(FeaturesEngine&&) = delete; + + using FeaturesFetchFunc = std::function>>(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: + + std::string_view getName() const override { return "Features"; } + + bool load(Database::Session& session, bool forceReload, const ProgressCallback& progressCallback) override; + 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; + + bool loadFromCache(Database::Session& session, const FeaturesEngineCache& cache); + + // Use training (may be very slow) + struct TrainSettings + { + std::size_t iterationCount {10}; + float sampleCountPerNeuron {4}; + FeatureSettingsMap featureSettingsMap; + }; + bool loadFromTraining(Database::Session& session, const TrainSettings& trainSettings, const ProgressCallback& progressCallback); + + template + using ObjectPositions = std::unordered_map>; + + using ArtistPositions = ObjectPositions; + using ReleasePositions = ObjectPositions; + using TrackPositions = ObjectPositions; + + template + using ObjectMatrix = SOM::Matrix>; + using ArtistMatrix = ObjectMatrix; + using ReleaseMatrix = ObjectMatrix; + using TrackMatrix = ObjectMatrix; + + bool load(Database::Session& session, SOM::Network network, const TrackPositions& tracksPosition); + + FeaturesEngineCache toCache() const; + + template + static std::vector getMatchingRefVectorsPosition(const std::vector& ids, const ObjectPositions& objectPositions); + + template + static std::vector getObjectsIds(const std::vector& positions, const ObjectMatrix& objectsMatrix); + + template + std::vector getSimilarObjects(const std::vector& ids, + const ObjectMatrix& objectMatrix, + const ObjectPositions& objectPositions, + std::size_t maxCount) const; + + bool _loadCancelled {}; + std::unique_ptr _network; + double _networkRefVectorsDistanceMedian {}; + + ArtistPositions _artistPositions; + std::unordered_map _artistMatrix; + + ReleasePositions _releasePositions; + ReleaseMatrix _releaseMatrix; + + TrackPositions _trackPositions; + TrackMatrix _trackMatrix; + + static inline FeaturesFetchFunc _featuresFetchFunc; +}; + +template +std::vector +FeaturesEngine::getMatchingRefVectorsPosition(const std::vector& ids, const ObjectPositions& objectPositions) +{ + std::vector res; + + if (ids.empty()) + return res; + + for (const IdType id : ids) + { + auto it = objectPositions.find(id); + if (it == objectPositions.end()) + continue; + + for (const SOM::Position& position : it->second) + Utils::push_back_if_not_present(res, position); + } + + return res; +} + +template +std::vector +FeaturesEngine::getObjectsIds(const std::vector& positions, const ObjectMatrix& objectMatrix) +{ + std::vector res; + + for (const SOM::Position& position : positions) + { + for (const IdType id : objectMatrix.get(position)) + Utils::push_back_if_not_present(res, id); + } + + return res; +} + +template +std::vector +FeaturesEngine::getSimilarObjects(const std::vector& ids, + const ObjectMatrix& objectMatrix, + const ObjectPositions& objectPositions, + std::size_t maxCount) const +{ + std::vector res; + + std::vector searchedRefVectorsPosition {getMatchingRefVectorsPosition(ids, objectPositions)}; + if (searchedRefVectorsPosition.empty()) + return res; + + while (1) + { + std::vector closestObjectIds {getObjectsIds(searchedRefVectorsPosition, objectMatrix)}; + + // Remove objects that are already in input or already reported + closestObjectIds.erase(std::remove_if(std::begin(closestObjectIds), std::end(closestObjectIds), + [&](IdType id) + { + return std::find(std::cbegin(ids), std::cend(ids), id) != std::cend(ids); + }) + , std::end(closestObjectIds)); + + for (IdType id : closestObjectIds) + { + if (res.size() == maxCount) + break; + + Utils::push_back_if_not_present(res, id); + } + + if (res.size() == maxCount) + break; + + // If there is not enough objects, try again with closest neighbour until there is too much distance + const std::optional closestRefVectorPosition {_network->getClosestRefVectorPosition(searchedRefVectorsPosition, _networkRefVectorsDistanceMedian * 0.75)}; + if (!closestRefVectorPosition) + break; + + Utils::push_back_if_not_present(searchedRefVectorsPosition, closestRefVectorPosition.value()); + } + + return res; +} + +} // ns Recommendation diff --git a/src/libs/recommendation/impl/features/FeaturesClassifierCache.cpp b/src/libs/recommendation/impl/features/FeaturesEngineCache.cpp similarity index 85% rename from src/libs/recommendation/impl/features/FeaturesClassifierCache.cpp rename to src/libs/recommendation/impl/features/FeaturesEngineCache.cpp index d184ac60..0f238627 100644 --- a/src/libs/recommendation/impl/features/FeaturesClassifierCache.cpp +++ b/src/libs/recommendation/impl/features/FeaturesEngineCache.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "FeaturesClassifierCache.hpp" +#include "FeaturesEngineCache.hpp" #include #include @@ -90,7 +90,7 @@ networkToCacheFile(const SOM::Network& network, std::filesystem::path path) } std::optional -FeaturesClassifierCache::createNetworkFromCacheFile(const std::filesystem::path& path) +FeaturesEngineCache::createNetworkFromCacheFile(const std::filesystem::path& path) { if (!std::filesystem::exists(path)) return std::nullopt; @@ -143,19 +143,19 @@ FeaturesClassifierCache::createNetworkFromCacheFile(const std::filesystem::path& } bool -FeaturesClassifierCache::objectPositionToCacheFile(const ObjectPositions& objectsPosition, const std::filesystem::path& path) +FeaturesEngineCache::objectPositionToCacheFile(const TrackPositions& trackPositions, const std::filesystem::path& path) { try { boost::property_tree::ptree root; - for (const auto& objectPosition : objectsPosition) + for (const auto& [id, positions] : trackPositions) { boost::property_tree::ptree node; - node.put("id", objectPosition.first); + node.put("id", id.getValue()); - for (const auto& position : objectPosition.second) + for (const SOM::Position& position : positions) { boost::property_tree::ptree positionNode; positionNode.put("x", position.x); @@ -177,8 +177,8 @@ FeaturesClassifierCache::objectPositionToCacheFile(const ObjectPositions& object } } -std::optional -FeaturesClassifierCache::createObjectPositionsFromCacheFile(const std::filesystem::path& path) +std::optional +FeaturesEngineCache::createObjectPositionsFromCacheFile(const std::filesystem::path& path) { try { @@ -188,17 +188,17 @@ FeaturesClassifierCache::createObjectPositionsFromCacheFile(const std::filesyste boost::property_tree::read_xml(path.string(), root); - ObjectPositions res; + TrackPositions res; for (const auto& object : root.get_child("objects")) { - auto id = object.second.get("id"); + const Database::TrackId id {object.second.get("id")}; for (const auto& position : object.second.get_child("position")) { auto x = position.second.get("x"); auto y = position.second.get("y"); - res[id].insert({x, y}); + res[id].push_back({x, y}); } } @@ -214,14 +214,14 @@ FeaturesClassifierCache::createObjectPositionsFromCacheFile(const std::filesyste } void -FeaturesClassifierCache::invalidate() +FeaturesEngineCache::invalidate() { std::filesystem::remove(getCacheNetworkFilePath()); std::filesystem::remove(getCacheTrackPositionsFilePath()); } -std::optional -FeaturesClassifierCache::read() +std::optional +FeaturesEngineCache::read() { auto network{createNetworkFromCacheFile(getCacheNetworkFilePath())}; if (!network) @@ -231,11 +231,11 @@ FeaturesClassifierCache::read() if (!trackPositions) return std::nullopt; - return FeaturesClassifierCache {std::move(*network), std::move(*trackPositions)}; + return FeaturesEngineCache {std::move(*network), std::move(*trackPositions)}; } void -FeaturesClassifierCache::write() const +FeaturesEngineCache::write() const { std::filesystem::create_directories(Service::get()->getPath("working-dir") / "cache" / "features"); @@ -246,7 +246,7 @@ FeaturesClassifierCache::write() const } } -FeaturesClassifierCache::FeaturesClassifierCache(SOM::Network network, ObjectPositions trackPositions) +FeaturesEngineCache::FeaturesEngineCache(SOM::Network network, TrackPositions trackPositions) : _network {std::move(network)}, _trackPositions {std::move(trackPositions)} { diff --git a/src/libs/recommendation/impl/features/FeaturesClassifierCache.hpp b/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp similarity index 66% rename from src/libs/recommendation/impl/features/FeaturesClassifierCache.hpp rename to src/libs/recommendation/impl/features/FeaturesEngineCache.hpp index 25ddaeff..08372345 100644 --- a/src/libs/recommendation/impl/features/FeaturesClassifierCache.hpp +++ b/src/libs/recommendation/impl/features/FeaturesEngineCache.hpp @@ -28,27 +28,27 @@ namespace Recommendation { -class FeaturesClassifierCache +class FeaturesEngineCache { public: static void invalidate(); - static std::optional read(); + static std::optional read(); void write() const; private: - using ObjectPositions = std::unordered_map>; + using TrackPositions = std::unordered_map>; - FeaturesClassifierCache(SOM::Network network, ObjectPositions trackPositions); + FeaturesEngineCache(SOM::Network network, TrackPositions trackPositions); static std::optional createNetworkFromCacheFile(const std::filesystem::path& path); - static std::optional createObjectPositionsFromCacheFile(const std::filesystem::path& path); - static bool objectPositionToCacheFile(const ObjectPositions& objectsPosition, const std::filesystem::path& path); + static std::optional createObjectPositionsFromCacheFile(const std::filesystem::path& path); + static bool objectPositionToCacheFile(const TrackPositions& trackPositions, const std::filesystem::path& path); - friend class FeaturesClassifier; + friend class FeaturesEngine; SOM::Network _network; - ObjectPositions _trackPositions; + TrackPositions _trackPositions; }; } // namespace Recommendation diff --git a/src/libs/recommendation/include/recommendation/IEngine.hpp b/src/libs/recommendation/include/recommendation/IEngine.hpp index bd7ce05d..6a15187b 100644 --- a/src/libs/recommendation/include/recommendation/IEngine.hpp +++ b/src/libs/recommendation/include/recommendation/IEngine.hpp @@ -20,9 +20,7 @@ #pragma once #include -#include -#include - +#include #include "database/Types.hpp" #include "utils/EnumSet.hpp" @@ -48,15 +46,20 @@ namespace Recommendation virtual void load(bool forceReload, const ProgressCallback& progressCallback = {}) = 0; virtual void cancelLoad() = 0; - using ResultContainer = std::unordered_set; + template + using ResultContainer = std::vector; - virtual ResultContainer getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) = 0; - virtual ResultContainer getSimilarTracks(Database::Session& session, const std::unordered_set& tracksId, std::size_t maxCount) = 0; - virtual ResultContainer getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) = 0; - virtual ResultContainer getSimilarArtists(Database::Session& session, - Database::IdType artistId, - EnumSet linkTypes, - std::size_t maxCount) = 0; + using ArtistContainer = ResultContainer; + 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; }; std::unique_ptr createEngine(Database::Db& db); diff --git a/src/libs/subsonic/impl/ParameterParsing.cpp b/src/libs/recommendation/include/recommendation/IRecommendation.hpp similarity index 64% rename from src/libs/subsonic/impl/ParameterParsing.cpp rename to src/libs/recommendation/include/recommendation/IRecommendation.hpp index bd8c8464..834a983e 100644 --- a/src/libs/subsonic/impl/ParameterParsing.cpp +++ b/src/libs/recommendation/include/recommendation/IRecommendation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Emeric Poupon + * Copyright (C) 2021 Emeric Poupon * * This file is part of LMS. * @@ -17,27 +17,22 @@ * along with LMS. If not, see . */ -#include "ParameterParsing.hpp" +#pragma once -namespace StringUtils +#include "database/Types.hpp" +#include "utils/EnumSet.hpp" + +namespace Database { - template<> - std::optional - readAs(std::string_view str) - { - return API::Subsonic::IdFromString(str); - } - - template<> - std::optional - readAs(std::string_view str) - { - if (str == "true") - return true; - else if (str == "false") - return false; - - return {}; - } + class Db; + class Session; } +namespace Recommendation +{ + class IRecommendation + { + }; + +} // ns Recommendation + diff --git a/src/libs/scanner/impl/Scanner.cpp b/src/libs/scanner/impl/Scanner.cpp index f1b1fe3f..6a8444be 100644 --- a/src/libs/scanner/impl/Scanner.cpp +++ b/src/libs/scanner/impl/Scanner.cpp @@ -111,7 +111,7 @@ createArtist(Session& session, const MetaData::Artist& artistInfo) static void -updateArtistIfNeeded(const Artist::pointer& artist, const MetaData::Artist& artistInfo) +updateArtistIfNeeded(Artist::pointer artist, const MetaData::Artist& artistInfo) { // Name may have been updated if (artist->getName() != artistInfo.name) @@ -543,7 +543,7 @@ Scanner::scan(bool forceScan) } bool -Scanner::fetchTrackFeatures(Database::IdType trackId, const UUID& recordingMBID) +Scanner::fetchTrackFeatures(Database::TrackId trackId, const UUID& recordingMBID) { std::map features; @@ -551,14 +551,14 @@ Scanner::fetchTrackFeatures(Database::IdType trackId, const UUID& recordingMBID) const std::string data {AcousticBrainz::extractLowLevelFeatures(recordingMBID)}; if (data.empty()) { - LMS_LOG(DBUPDATER, ERROR) << "Track " << trackId << ", recording MBID = '" << recordingMBID.getAsString() << "': cannot extract features using AcousticBrainz"; + LMS_LOG(DBUPDATER, ERROR) << "Track " << trackId.getValue() << ", recording MBID = '" << recordingMBID.getAsString() << "': cannot extract features using AcousticBrainz"; return false; } { auto uniqueTransaction {_dbSession.createUniqueTransaction()}; - Wt::Dbo::ptr track {Database::Track::getById(_dbSession, trackId)}; + Database::Track::pointer track {Database::Track::getById(_dbSession, trackId)}; if (!track) return false; @@ -580,7 +580,7 @@ Scanner::fetchTrackFeatures(ScanStats& stats) struct TrackInfo { - Database::IdType id; + Database::TrackId id; UUID recordingMBID; }; @@ -592,7 +592,7 @@ Scanner::fetchTrackFeatures(ScanStats& stats) auto tracks {Database::Track::getAllWithRecordingMBIDAndMissingFeatures(_dbSession)}; for (const auto& track : tracks) - res.emplace_back(TrackInfo {track.id(), *track->getRecordingMBID()}); + res.emplace_back(TrackInfo {track->getId(), *track->getRecordingMBID()}); return res; }()}; @@ -922,8 +922,8 @@ Scanner::removeMissingTracks(ScanStats& stats) stepStats.totalElems = trackCount; notifyInProgress(stepStats); - std::vector> trackPaths; - std::vector tracksToRemove; + std::vector> trackPaths; + std::vector tracksToRemove; for (std::size_t i {trackCount < batchSize ? 0 : trackCount - batchSize}; ; i -= (i > batchSize ? batchSize : i)) { @@ -950,7 +950,7 @@ Scanner::removeMissingTracks(ScanStats& stats) { auto transaction {_dbSession.createUniqueTransaction()}; - for (const IdType trackId : tracksToRemove) + for (const TrackId trackId : tracksToRemove) { Track::pointer track {Track::getById(_dbSession, trackId)}; if (track) @@ -1026,7 +1026,7 @@ Scanner::checkDuplicatedAudioFiles(ScanStats& stats) if (auto trackMBID {track->getTrackMBID()}) { LMS_LOG(DBUPDATER, INFO) << "Found duplicated Track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); - stats.duplicates.emplace_back(ScanDuplicate {track.id(), DuplicateReason::SameMBID}); + stats.duplicates.emplace_back(ScanDuplicate {track->getId(), DuplicateReason::SameMBID}); } } diff --git a/src/libs/scanner/impl/Scanner.hpp b/src/libs/scanner/impl/Scanner.hpp index e8564c66..76aeb819 100644 --- a/src/libs/scanner/impl/Scanner.hpp +++ b/src/libs/scanner/impl/Scanner.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,7 @@ #include "database/Session.hpp" #include "metadata/IParser.hpp" #include "scanner/IScanner.hpp" +#include "utils/Path.hpp" class UUID; @@ -75,7 +77,7 @@ class Scanner : public IScanner void scan(bool force); void scanMediaDirectory( const std::filesystem::path& mediaDirectory, bool forceScan, ScanStats& stats); - bool fetchTrackFeatures(Database::IdType trackId, const UUID& MBID); + bool fetchTrackFeatures(Database::TrackId trackId, const UUID& MBID); void fetchTrackFeatures(ScanStats& stats); // Helpers @@ -86,7 +88,6 @@ class Scanner : public IScanner void removeOrphanEntries(); void checkDuplicatedAudioFiles(ScanStats& stats); void scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanStats& stats); - Database::IdType doScanAudioFile(const std::filesystem::path& file, ScanStats& stats); void notifyInProgressIfNeeded(const ScanStepStats& stats); void notifyInProgress(const ScanStepStats& stats); void reloadSimilarityEngine(ScanStats& stats); @@ -112,8 +113,8 @@ class Scanner : public IScanner std::size_t _scanVersion {}; Wt::WTime _startTime; Database::ScanSettings::UpdatePeriod _updatePeriod {Database::ScanSettings::UpdatePeriod::Never}; - std::unordered_set _fileExtensions; - std::filesystem::path _mediaDirectory; + std::unordered_set _fileExtensions; + std::filesystem::path _mediaDirectory; Database::ScanSettings::RecommendationEngineType _recommendationEngineType; }; diff --git a/src/libs/scanner/include/scanner/ScannerStats.hpp b/src/libs/scanner/include/scanner/ScannerStats.hpp index b1996647..5528ce8b 100644 --- a/src/libs/scanner/include/scanner/ScannerStats.hpp +++ b/src/libs/scanner/include/scanner/ScannerStats.hpp @@ -53,7 +53,7 @@ namespace Scanner { struct ScanDuplicate { - Database::IdType trackId; + Database::TrackId trackId; DuplicateReason reason; }; diff --git a/src/libs/scrobbling/impl/IScrobbler.hpp b/src/libs/scrobbling/impl/IScrobbler.hpp index 01fae716..f442e773 100644 --- a/src/libs/scrobbling/impl/IScrobbler.hpp +++ b/src/libs/scrobbling/impl/IScrobbler.hpp @@ -29,7 +29,6 @@ namespace Database { - class Db; class Session; class TrackList; class User; @@ -48,7 +47,7 @@ namespace Scrobbling virtual void addTimedListen(const TimedListen& listen) = 0; - virtual Wt::Dbo::ptr getListensTrackList(Database::Session& session, Wt::Dbo::ptr user) = 0; + virtual Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user) = 0; }; std::unique_ptr createScrobbler(std::string_view backendName); diff --git a/src/libs/scrobbling/impl/Scrobbling.cpp b/src/libs/scrobbling/impl/Scrobbling.cpp index 65df227d..add4cf2e 100644 --- a/src/libs/scrobbling/impl/Scrobbling.cpp +++ b/src/libs/scrobbling/impl/Scrobbling.cpp @@ -64,7 +64,7 @@ namespace Scrobbling } std::optional - Scrobbling::getUserScrobbler(Database::IdType userId) + Scrobbling::getUserScrobbler(Database::UserId userId) { std::optional scrobbler; @@ -76,49 +76,49 @@ namespace Scrobbling return scrobbler; } - std::vector> + std::vector> Scrobbling::getRecentArtists(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) { - const Wt::Dbo::ptr history {getListensTrackList(session, user)}; + const Database::ObjectPtr history {getListensTrackList(session, user)}; - std::vector> res; + std::vector> res; if (history) res = history->getArtistsReverse(clusterIds, linkType, range, moreResults); return res; } - std::vector> + std::vector> Scrobbling::getRecentReleases(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) { - const Wt::Dbo::ptr history {getListensTrackList(session, user)}; + const Database::ObjectPtr history {getListensTrackList(session, user)}; - std::vector> res; + std::vector> res; if (history) res = history->getReleasesReverse(clusterIds, range, moreResults); return res; } - std::vector> + std::vector> Scrobbling::getRecentTracks(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) { - const Wt::Dbo::ptr history {getListensTrackList(session, user)}; + const Database::ObjectPtr history {getListensTrackList(session, user)}; - std::vector> res; + std::vector> res; if (history) res = history->getTracksReverse(clusterIds, range, moreResults); @@ -127,57 +127,57 @@ namespace Scrobbling // Top - std::vector> + std::vector> Scrobbling::getTopArtists(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) { - const Wt::Dbo::ptr history {getListensTrackList(session, user)}; + const Database::ObjectPtr history {getListensTrackList(session, user)}; - std::vector> res; + std::vector> res; if (history) res = history->getTopArtists(clusterIds, linkType, range, moreResults); return res; } - std::vector> + std::vector> Scrobbling::getTopReleases(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) { - const Wt::Dbo::ptr history {getListensTrackList(session, user)}; + const Database::ObjectPtr history {getListensTrackList(session, user)}; - std::vector> res; + std::vector> res; if (history) res = history->getTopReleases(clusterIds, range, moreResults); return res; } - std::vector> + std::vector> Scrobbling::getTopTracks(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) { - const Wt::Dbo::ptr history {getListensTrackList(session, user)}; + const Database::ObjectPtr history {getListensTrackList(session, user)}; - std::vector> res; + std::vector> res; if (history) res = history->getTopTracks(clusterIds, range, moreResults); return res; } - Wt::Dbo::ptr - Scrobbling::getListensTrackList(Database::Session& session, Wt::Dbo::ptr user) + Database::ObjectPtr + Scrobbling::getListensTrackList(Database::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 613335c8..6aa2ed2f 100644 --- a/src/libs/scrobbling/impl/Scrobbling.hpp +++ b/src/libs/scrobbling/impl/Scrobbling.hpp @@ -38,47 +38,47 @@ namespace Scrobbling void listenFinished(const Listen& listen, std::optional duration) override; void addTimedListen(const TimedListen& listen) override; - std::vector> getRecentArtists(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + std::vector> getRecentArtists(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) override; - std::vector> getRecentReleases(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + std::vector> getRecentReleases(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) override; - std::vector> getRecentTracks(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + std::vector> getRecentTracks(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) override; - std::vector> getTopArtists(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + std::vector> getTopArtists(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) override; - std::vector> getTopReleases(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + std::vector> getTopReleases(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) override; - std::vector> getTopTracks(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + std::vector> getTopTracks(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) override; - Wt::Dbo::ptr getListensTrackList(Database::Session& session, Wt::Dbo::ptr user); + Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user); - std::optional getUserScrobbler(Database::IdType userId); + std::optional getUserScrobbler(Database::UserId userId); Database::Db& _db; std::unordered_map> _scrobblers; diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp index 019759f2..e5b23d51 100644 --- a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp @@ -61,7 +61,7 @@ namespace Scrobbling if (!user) return; - Wt::Dbo::ptr tracklist {getListensTrackList(session, user)}; + Database::TrackList::pointer tracklist {getListensTrackList(session, user)}; if (!tracklist) tracklist = Database::TrackList::create(session, historyTracklistName, Database::TrackList::Type::Internal, false, user); @@ -72,8 +72,8 @@ namespace Scrobbling Database::TrackListEntry::create(session, track, getListensTrackList(session, user), listen.listenedAt); } - Wt::Dbo::ptr - InternalScrobbler::getListensTrackList(Database::Session& session, Wt::Dbo::ptr user) + Database::TrackList::pointer + InternalScrobbler::getListensTrackList(Database::Session& session, Database::ObjectPtr user) { return Database::TrackList::get(session, historyTracklistName, Database::TrackList::Type::Internal, user); } diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp b/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp index 4d3323ea..45c067da 100644 --- a/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp +++ b/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp @@ -21,6 +21,11 @@ #include "IScrobbler.hpp" +namespace Database +{ + class Db; +} + namespace Scrobbling { class InternalScrobbler final : public IScrobbler @@ -34,7 +39,7 @@ namespace Scrobbling void addTimedListen(const TimedListen& listen) override; - Wt::Dbo::ptr getListensTrackList(Database::Session& session, Wt::Dbo::ptr user) override; + Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user) override; Database::Db& _db; }; diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index 0cdb8443..c17254da 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -41,7 +41,7 @@ namespace { bool - canBeScrobbled(Database::Session& session, Database::IdType trackId, std::chrono::seconds duration) + canBeScrobbled(Database::Session& session, Database::TrackId trackId, std::chrono::seconds duration) { auto transaction {session.createSharedTransaction()}; diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp index ea6e5d1d..30e85bad 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp @@ -50,7 +50,7 @@ namespace Scrobbling::ListenBrainz void listenStarted(const Listen& listen) override; void listenFinished(const Listen& listen, std::optional duration) override; void addTimedListen(const TimedListen& listen) override; - Wt::Dbo::ptr getListensTrackList(Database::Session& session, Wt::Dbo::ptr user) override; + Database::ObjectPtr getListensTrackList(Database::Session& session, Database::ObjectPtr user) override; // Submit listens void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint); diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp index 1a59abcf..dd0b14a7 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.cpp @@ -195,7 +195,7 @@ namespace std::vector matchedListens; }; ParseGetListensResult - parseGetListens(Database::Session& session, std::string_view msgBody, Database::IdType userId) + parseGetListens(Database::Session& session, std::string_view msgBody, Database::UserId userId) { ParseGetListensResult result; @@ -233,7 +233,7 @@ namespace result.oldestEntry = listenedAt; if (const Database::Track::pointer track {tryMatchListen(session, metadata)}) - result.matchedListens.emplace_back(Scrobbling::TimedListen {userId, track.id(), listenedAt}); + result.matchedListens.emplace_back(Scrobbling::TimedListen {userId, track->getId(), listenedAt}); } } catch (const Wt::WException& error) @@ -285,7 +285,7 @@ namespace Scrobbling::ListenBrainz } ListensSynchronizer::UserContext& - ListensSynchronizer::getUserContext(Database::IdType userId) + ListensSynchronizer::getUserContext(Database::UserId userId) { auto itContext {_userContexts.find(userId)}; if (itContext == std::cend(_userContexts)) @@ -338,14 +338,14 @@ namespace Scrobbling::ListenBrainz assert(!isFetching()); - std::vector userIds; + std::vector userIds; { Database::Session& session {_db.getTLSSession()}; auto transaction {session.createSharedTransaction()}; userIds = Database::User::getAllIds(_db.getTLSSession()); } - for (const Database::IdType userId : userIds) + for (const Database::UserId userId : userIds) { if (Utils::getListenBrainzToken(_db.getTLSSession(), userId)) startGetListens(getUserContext(userId)); @@ -373,7 +373,7 @@ namespace Scrobbling::ListenBrainz { _strand.dispatch([this, &context] { - LOG(DEBUG) << "Fetch done for user " << context.userId << ", fetched: " << context.fetchedListenCount << ", matched: " << context.matchedListenCount << ", imported: " << context.importedListenCount; + LOG(DEBUG) << "Fetch done for user " << context.userId.getValue() << ", fetched: " << context.fetchedListenCount << ", matched: " << context.matchedListenCount << ", imported: " << context.importedListenCount; context.fetching = false; if (!isFetching()) @@ -473,7 +473,7 @@ namespace Scrobbling::ListenBrainz } std::optional - ListensSynchronizer::createValidateTokenRequestData(Database::IdType userId) + ListensSynchronizer::createValidateTokenRequestData(Database::UserId userId) { Database::Session& session {_db.getTLSSession()}; diff --git a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp index 511e07e8..e2914dee 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListensSynchronizer.hpp @@ -50,14 +50,14 @@ namespace Scrobbling::ListenBrainz private: struct UserContext { - UserContext(Database::IdType id) : userId {id} {} + UserContext(Database::UserId id) : userId {id} {} UserContext(const UserContext&) = delete; UserContext(UserContext&&) = delete; UserContext& operator=(const UserContext&) = delete; UserContext& operator=(UserContext&&) = delete; - const Database::IdType userId; + const Database::UserId userId; bool fetching {}; std::optional listenCount {}; @@ -69,7 +69,7 @@ namespace Scrobbling::ListenBrainz std::size_t importedListenCount{}; }; - UserContext& getUserContext(Database::IdType userId); + UserContext& getUserContext(Database::UserId userId); bool isFetching() const; void scheduleGetListens(std::chrono::seconds fromNow); void startGetListens(); @@ -78,7 +78,7 @@ namespace Scrobbling::ListenBrainz void enqueValidateToken(UserContext& context); void enqueGetListenCount(UserContext& context); void enqueGetListens(UserContext& context); - std::optional createValidateTokenRequestData(Database::IdType userId); + 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); @@ -88,7 +88,7 @@ namespace Scrobbling::ListenBrainz SendQueue& _sendQueue; boost::asio::steady_timer _getListensTimer {_ioContext}; - std::unordered_map _userContexts; + std::unordered_map _userContexts; const std::size_t _maxSyncListenCount; const std::chrono::hours _syncListensPeriod; diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.cpp b/src/libs/scrobbling/impl/listenbrainz/Utils.cpp index 47cf901c..ab293954 100644 --- a/src/libs/scrobbling/impl/listenbrainz/Utils.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/Utils.cpp @@ -30,7 +30,7 @@ static constexpr std::string_view historyTracklistName {"__scrobbler_listenbrain namespace Scrobbling::ListenBrainz::Utils { std::optional - getListenBrainzToken(Database::Session& session, Database::IdType userId) + getListenBrainzToken(Database::Session& session, Database::UserId userId) { auto transaction {session.createSharedTransaction()}; diff --git a/src/libs/scrobbling/impl/listenbrainz/Utils.hpp b/src/libs/scrobbling/impl/listenbrainz/Utils.hpp index 84074086..6b2d1271 100644 --- a/src/libs/scrobbling/impl/listenbrainz/Utils.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/Utils.hpp @@ -21,6 +21,7 @@ #include #include "utils/UUID.hpp" + #include "database/Types.hpp" namespace Database @@ -32,7 +33,7 @@ namespace Database namespace Scrobbling::ListenBrainz::Utils { - std::optional getListenBrainzToken(Database::Session& session, Database::IdType userId); - Wt::Dbo::ptr getOrCreateListensTrackList(Database::Session& session, Wt::Dbo::ptr user); - Wt::Dbo::ptr getListensTrackList(Database::Session& session, Wt::Dbo::ptr user); + 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/scrobbling/include/scrobbling/IScrobbling.hpp b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp index e1fc5ce9..6f4436c7 100644 --- a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp +++ b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp @@ -24,12 +24,12 @@ #include #include #include -#include #include #include #include "scrobbling/Listen.hpp" +#include "database/Types.hpp" namespace Database { @@ -57,42 +57,42 @@ namespace Scrobbling // Stats // From most recent to oldest - virtual std::vector> getRecentArtists(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + virtual std::vector> getRecentArtists(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) = 0; - virtual std::vector> getRecentReleases(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + virtual std::vector> getRecentReleases(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) = 0; - virtual std::vector> getRecentTracks(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + virtual std::vector> getRecentTracks(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) = 0; // Top - virtual std::vector> getTopArtists(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + virtual std::vector> getTopArtists(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional linkType, std::optional range, bool& moreResults) = 0; - virtual std::vector> getTopReleases(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + virtual std::vector> getTopReleases(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) = 0; - virtual std::vector> getTopTracks(Database::Session& session, - Wt::Dbo::ptr user, - const std::set& clusterIds, + virtual std::vector> getTopTracks(Database::Session& session, + Database::ObjectPtr user, + const std::vector& clusterIds, std::optional range, bool& moreResults) = 0; }; diff --git a/src/libs/scrobbling/include/scrobbling/Listen.hpp b/src/libs/scrobbling/include/scrobbling/Listen.hpp index b67a2ae9..ae80dbac 100644 --- a/src/libs/scrobbling/include/scrobbling/Listen.hpp +++ b/src/libs/scrobbling/include/scrobbling/Listen.hpp @@ -27,8 +27,8 @@ namespace Scrobbling { struct Listen { - Database::IdType userId {}; - Database::IdType trackId {}; + Database::UserId userId {}; + Database::TrackId trackId {}; }; struct TimedListen : public Listen diff --git a/src/libs/som/impl/Network.cpp b/src/libs/som/impl/Network.cpp index 535052a3..83f952d7 100644 --- a/src/libs/som/impl/Network.cpp +++ b/src/libs/som/impl/Network.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "utils/Logger.hpp" #include "utils/Random.hpp" @@ -197,7 +198,7 @@ Network::getClosestRefVectorPosition(const InputVector& data, InputVector::Dista } std::optional -Network::getClosestRefVectorPosition(const std::unordered_set& refVectorsPosition, InputVector::Distance maxDistance) const +Network::getClosestRefVectorPosition(const std::vector& refVectorsPosition, InputVector::Distance maxDistance) const { std::unordered_set neighboursPosition; for (const Position& refVectorPosition : refVectorsPosition) diff --git a/src/libs/som/include/som/Network.hpp b/src/libs/som/include/som/Network.hpp index f3ba2df1..be854237 100644 --- a/src/libs/som/include/som/Network.hpp +++ b/src/libs/som/include/som/Network.hpp @@ -20,7 +20,6 @@ #pragma once #include -#include #include #include #include @@ -70,7 +69,7 @@ class Network Position getClosestRefVectorPosition(const InputVector& data) const; std::optional getClosestRefVectorPosition(const InputVector& data, InputVector::Distance maxDistance) const; - std::optional getClosestRefVectorPosition(const std::unordered_set& refVectorsPosition, InputVector::Distance maxDistance) const; + std::optional getClosestRefVectorPosition(const std::vector& refVectorsPosition, InputVector::Distance maxDistance) const; InputVector::Distance getRefVectorsDistance(const Position& position1, const Position& position2) const; diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index e4aeeb0f..4c87d7cd 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -1,6 +1,5 @@ add_library(lmssubsonic SHARED - impl/ParameterParsing.cpp impl/Scan.cpp impl/Stream.cpp impl/SubsonicId.cpp diff --git a/src/libs/subsonic/impl/ParameterParsing.hpp b/src/libs/subsonic/impl/ParameterParsing.hpp index 0efae6c3..d805092f 100644 --- a/src/libs/subsonic/impl/ParameterParsing.hpp +++ b/src/libs/subsonic/impl/ParameterParsing.hpp @@ -20,8 +20,8 @@ #include +#include "database/Types.hpp" #include "utils/String.hpp" -#include "SubsonicId.hpp" #include "SubsonicResponse.hpp" namespace API::Subsonic @@ -82,18 +82,5 @@ namespace API::Subsonic return *res; } - - -} - -namespace StringUtils -{ - template<> - std::optional - readAs(std::string_view str); - - template<> - std::optional - readAs(std::string_view str); } diff --git a/src/libs/subsonic/impl/RequestContext.hpp b/src/libs/subsonic/impl/RequestContext.hpp index 6d599513..d2949a8b 100644 --- a/src/libs/subsonic/impl/RequestContext.hpp +++ b/src/libs/subsonic/impl/RequestContext.hpp @@ -36,7 +36,7 @@ namespace API::Subsonic { const Wt::Http::ParameterMap& parameters; Database::Session& dbSession; - Database::IdType userId; + Database::UserId userId; std::string clientName; }; } diff --git a/src/libs/subsonic/impl/Stream.cpp b/src/libs/subsonic/impl/Stream.cpp index 911b1514..d3d181ed 100644 --- a/src/libs/subsonic/impl/Stream.cpp +++ b/src/libs/subsonic/impl/Stream.cpp @@ -63,7 +63,7 @@ StreamParameters getStreamParameters(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; + const TrackId id {getMandatoryParameterAs(context.parameters, "id")}; // Optional params std::optional maxBitRate {getParameterAs(context.parameters, "maxBitRate")}; @@ -74,7 +74,7 @@ getStreamParameters(RequestContext& context) auto transaction {context.dbSession.createSharedTransaction()}; { - auto track {Track::getById(context.dbSession, id.value)}; + auto track {Track::getById(context.dbSession, id)}; if (!track) throw RequestedDataNotFoundError {}; @@ -94,7 +94,7 @@ getStreamParameters(RequestContext& context) // "If set to zero, no limit is imposed" if (maxBitRate && *maxBitRate != 0) - bitRate = clamp(*maxBitRate, std::size_t {48}, bitRate); + bitRate = Utils::clamp(*maxBitRate, std::size_t {48}, bitRate); Av::TranscodeParameters transcodeParameters; @@ -118,13 +118,13 @@ handleDownload(RequestContext& context, const Wt::Http::Request& request, Wt::Ht if (!continuation) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; + Database::TrackId id {getMandatoryParameterAs(context.parameters, "id")}; std::filesystem::path trackPath; { auto transaction {context.dbSession.createSharedTransaction()}; - auto track {Track::getById(context.dbSession, id.value)}; + auto track {Track::getById(context.dbSession, id)}; if (!track) throw RequestedDataNotFoundError {}; diff --git a/src/libs/subsonic/impl/SubsonicId.cpp b/src/libs/subsonic/impl/SubsonicId.cpp index 368b7d94..edd052f1 100644 --- a/src/libs/subsonic/impl/SubsonicId.cpp +++ b/src/libs/subsonic/impl/SubsonicId.cpp @@ -26,64 +26,127 @@ namespace API::Subsonic { - -std::optional -IdFromString(const std::string_view id) -{ - if (id == "root") - return Id {Id::Type::Root}; - - std::vector values {StringUtils::splitString(id, "-")}; - if (values.size() != 2) - return std::nullopt; - - Id res; - - const std::string type {std::move(values[0])}; - if (type == "ar") - res.type = Id::Type::Artist; - else if (type == "al") - res.type = Id::Type::Release; - else if (type == "tr") - res.type = Id::Type::Track; - else if (type == "pl") - res.type = Id::Type::Playlist; - else - return std::nullopt; - - auto optId {StringUtils::readAs(values[1])}; - if (!optId) - return std::nullopt; - - res.value = *optId; - - return res; -} - -std::string -IdToString(const Id& id) -{ - std::string res; - - switch (id.type) + std::string + idToString(Database::ArtistId id) { - case Id::Type::Root: - return "root"; - case Id::Type::Artist: - res = "ar-"; - break; - case Id::Type::Release: - res = "al-"; - break; - case Id::Type::Track: - res = "tr-"; - break; - case Id::Type::Playlist: - res = "pl-"; - break; + return "ar-" + id.toString(); } - return res + std::to_string(id.value); + std::string + idToString(Database::ReleaseId id) + { + return "al-" + id.toString(); + } + + std::string + idToString(RootId) + { + return "root"; + } + + std::string + idToString(Database::TrackId id) + { + return "tr-" + id.toString(); + } + + std::string + idToString(Database::TrackListId id) + { + return "pl-" + id.toString(); + } +} // namespace API::Subsonic + +namespace StringUtils +{ + template<> + std::optional + readAs(std::string_view str) + { + std::vector values {StringUtils::splitString(str, "-")}; + if (values.size() != 2) + return std::nullopt; + + if (values[0] != "ar") + return std::nullopt; + + if (const auto value {StringUtils::readAs(values[1])}) + return Database::ArtistId {*value}; + + return std::nullopt; + } + + template<> + std::optional + readAs(std::string_view str) + { + std::vector values {StringUtils::splitString(str, "-")}; + if (values.size() != 2) + return std::nullopt; + + if (values[0] != "al") + return std::nullopt; + + if (const auto value {StringUtils::readAs(values[1])}) + return Database::ReleaseId {*value}; + + return std::nullopt; + } + + template<> + std::optional + readAs(std::string_view str) + { + if (str == "root") + return API::Subsonic::RootId {}; + + return std::nullopt; + } + + template<> + std::optional + readAs(std::string_view str) + { + std::vector values {StringUtils::splitString(str, "-")}; + if (values.size() != 2) + return std::nullopt; + + if (values[0] != "tr") + return std::nullopt; + + if (const auto value {StringUtils::readAs(values[1])}) + return Database::TrackId {*value}; + + return std::nullopt; + } + + template<> + std::optional + readAs(std::string_view str) + { + std::vector values {StringUtils::splitString(str, "-")}; + if (values.size() != 2) + return std::nullopt; + + if (values[0] != "pl") + return std::nullopt; + + if (const auto value {StringUtils::readAs(values[1])}) + return Database::TrackListId {*value}; + + return std::nullopt; + } + + template<> + std::optional + readAs(std::string_view str) + { + if (str == "true") + return true; + else if (str == "false") + return false; + + return {}; + } } -} // namespace API::Subsonic diff --git a/src/libs/subsonic/impl/SubsonicId.hpp b/src/libs/subsonic/impl/SubsonicId.hpp index 52760798..0ac2a16a 100644 --- a/src/libs/subsonic/impl/SubsonicId.hpp +++ b/src/libs/subsonic/impl/SubsonicId.hpp @@ -19,29 +19,45 @@ #pragma once -#include - #include "database/Types.hpp" +#include "utils/String.hpp" namespace API::Subsonic { + struct RootId {}; -struct Id -{ - enum class Type - { - Root, // Where all artists artistless albums reside - Track, - Release, - Artist, - Playlist, - }; - - Type type; - Database::IdType value {}; -}; - -std::optional IdFromString(std::string_view id); -std::string IdToString(const Id& id); - + std::string idToString(Database::ArtistId id); + std::string idToString(Database::ReleaseId id); + std::string idToString(Database::TrackId id); + std::string idToString(Database::TrackListId id); + std::string idToString(RootId); } // namespace API::Subsonic + +// Used to parse parameters +namespace StringUtils +{ + template<> + std::optional + readAs(std::string_view str); + + template<> + std::optional + readAs(std::string_view str); + + template<> + std::optional + readAs(std::string_view str); + + template<> + std::optional + readAs(std::string_view str); + + template<> + std::optional + readAs(std::string_view str); + + template<> + std::optional + readAs(std::string_view str); +} + diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 2c0a7e1a..a3f8b3f4 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -48,6 +48,7 @@ #include "RequestContext.hpp" #include "Scan.hpp" #include "Stream.hpp" +#include "SubsonicId.hpp" #include "SubsonicResponse.hpp" using namespace Database; @@ -321,7 +322,7 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User: { Response::Node trackResponse; - trackResponse.setAttribute("id", IdToString({Id::Type::Track, track.id()})); + trackResponse.setAttribute("id", idToString(track->getId())); trackResponse.setAttribute("isDir", false); trackResponse.setAttribute("title", track->getName()); if (track->getTrackNumber()) @@ -348,7 +349,7 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User: if (user->getSubsonicTranscodeEnable()) trackResponse.setAttribute("transcodedSuffix", formatToSuffix(user->getSubsonicTranscodeFormat())); - trackResponse.setAttribute("coverArt", IdToString({Id::Type::Track, track.id()})); + trackResponse.setAttribute("coverArt", idToString(track->getId())); const std::vector& artists {track->getArtists({TrackArtistLinkType::Artist})}; if (!artists.empty()) @@ -356,14 +357,14 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User: trackResponse.setAttribute("artist", getArtistNames(artists)); if (artists.size() == 1) - trackResponse.setAttribute("artistId", IdToString({Id::Type::Artist, artists.front().id()})); + trackResponse.setAttribute("artistId", idToString(artists.front()->getId())); } if (track->getRelease()) { trackResponse.setAttribute("album", track->getRelease()->getName()); - trackResponse.setAttribute("albumId", IdToString({Id::Type::Release, track->getRelease().id()})); - trackResponse.setAttribute("parent", IdToString({Id::Type::Release, track->getRelease().id()})); + trackResponse.setAttribute("albumId", idToString(track->getRelease()->getId())); + trackResponse.setAttribute("parent", idToString(track->getRelease()->getId())); } trackResponse.setAttribute("duration", std::chrono::duration_cast(track->getDuration()).count()); @@ -420,8 +421,8 @@ releaseToResponseNode(const Release::pointer& release, Session& dbSession, const } albumNode.setAttribute("created", dateTimeToCreatedString(release->getLastWritten())); - albumNode.setAttribute("id", IdToString({Id::Type::Release, release.id()})); - albumNode.setAttribute("coverArt", IdToString({Id::Type::Release, release.id()})); + albumNode.setAttribute("id", idToString(release->getId())); + albumNode.setAttribute("coverArt", idToString(release->getId())); auto releaseYear {release->getReleaseYear()}; if (releaseYear) albumNode.setAttribute("year", *releaseYear); @@ -432,7 +433,7 @@ releaseToResponseNode(const Release::pointer& release, Session& dbSession, const if (artists.empty() && !id3) { - albumNode.setAttribute("parent", IdToString({Id::Type::Root})); + albumNode.setAttribute("parent", idToString(RootId {})); } else if (!artists.empty()) { @@ -440,15 +441,12 @@ releaseToResponseNode(const Release::pointer& release, Session& dbSession, const if (artists.size() == 1) { - if (id3) - albumNode.setAttribute("artistId", IdToString({Id::Type::Artist, artists.front().id()})); - else - albumNode.setAttribute("parent", IdToString({Id::Type::Artist, artists.front().id()})); + albumNode.setAttribute(id3 ? "artistId" : "parent", idToString(artists.front()->getId())); } else { if (!id3) - albumNode.setAttribute("parent", IdToString({Id::Type::Root})); + albumNode.setAttribute("parent", idToString(RootId {})); } } @@ -476,7 +474,7 @@ artistToResponseNode(const User::pointer& user, const Artist::pointer& artist, b { Response::Node artistNode; - artistNode.setAttribute("id", IdToString({Id::Type::Artist, artist.id()})); + artistNode.setAttribute("id", idToString(artist->getId())); artistNode.setAttribute("name", artist->getName()); if (id3) @@ -544,7 +542,7 @@ handleChangePassword(RequestContext& context) try { - Database::IdType userId; + Database::UserId userId; { auto transaction {context.dbSession.createSharedTransaction()}; @@ -554,7 +552,7 @@ handleChangePassword(RequestContext& context) if (!user) throw UserNotAuthorizedError {}; - userId = user.id(); + userId = user->getId(); } Service::get()->setPassword(context.dbSession, userId, password); @@ -580,15 +578,10 @@ Response handleCreatePlaylistRequest(RequestContext& context) { // Optional params - auto id {getParameterAs(context.parameters, "playlistId")}; - if (id && id->type != Id::Type::Playlist) - throw BadParameterGenericError {"playlistId"}; - + const auto id {getParameterAs(context.parameters, "playlistId")}; auto name {getParameterAs(context.parameters, "name")}; - std::vector trackIds {getMultiParametersAs(context.parameters, "songId")}; - if (!std::all_of(std::cbegin(trackIds), std::cend(trackIds ), [](const Id& id) { return id.type == Id::Type::Track; })) - throw BadParameterGenericError {"songId"}; + std::vector trackIds {getMultiParametersAs(context.parameters, "songId")}; if (!name && !id) throw RequiredParameterMissingError {"name or id"}; @@ -602,7 +595,7 @@ handleCreatePlaylistRequest(RequestContext& context) TrackList::pointer tracklist; if (id) { - tracklist = TrackList::getById(context.dbSession, id->value); + tracklist = TrackList::getById(context.dbSession, *id); if (!tracklist || tracklist->getUser() != user || tracklist->getType() != TrackList::Type::Playlist) @@ -618,9 +611,9 @@ handleCreatePlaylistRequest(RequestContext& context) tracklist = TrackList::create(context.dbSession, *name, TrackList::Type::Playlist, false, user); } - for (const Id& trackId : trackIds) + for (const TrackId trackId : trackIds) { - Track::pointer track {Track::getById(context.dbSession, trackId.value)}; + Track::pointer track {Track::getById(context.dbSession, trackId)}; if (!track) continue; @@ -638,7 +631,7 @@ handleCreateUserRequest(RequestContext& context) std::string password {decodePasswordIfNeeded(getMandatoryParameterAs(context.parameters, "password"))}; // Just ignore all the other fields as we don't handle them - Database::IdType userId; + Database::UserId userId; { auto transaction {context.dbSession.createUniqueTransaction()}; @@ -647,7 +640,7 @@ handleCreateUserRequest(RequestContext& context) throw UserAlreadyExistsGenericError {}; user = User::create(context.dbSession, username); - userId = user.id(); + userId = user->getId(); } auto removeCreatedUser {[&]() @@ -685,9 +678,7 @@ static Response handleDeletePlaylistRequest(RequestContext& context) { - Id id {getMandatoryParameterAs(context.parameters, "id")}; - if (id.type != Id::Type::Playlist) - throw BadParameterGenericError {"id"}; + TrackListId id {getMandatoryParameterAs(context.parameters, "id")}; auto transaction {context.dbSession.createUniqueTransaction()}; @@ -695,7 +686,7 @@ handleDeletePlaylistRequest(RequestContext& context) if (!user) throw UserNotAuthorizedError {}; - TrackList::pointer tracklist {TrackList::getById(context.dbSession, id.value)}; + TrackList::pointer tracklist {TrackList::getById(context.dbSession, id)}; if (!tracklist || tracklist->getUser() != user || tracklist->getType() != TrackList::Type::Playlist) @@ -721,7 +712,7 @@ handleDeleteUserRequest(RequestContext& context) throw RequestedDataNotFoundError {}; // cannot delete ourself - if (user.id() == context.userId) + if (user->getId() == context.userId) throw UserNotAuthorizedError {}; user.remove(); @@ -809,7 +800,7 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) if (cluster) { bool more; - releases = Release::getByFilter(context.dbSession, {cluster.id()}, {}, range, more); + releases = Release::getByFilter(context.dbSession, {cluster->getId()}, {}, range, more); } } } @@ -876,14 +867,11 @@ Response handleGetAlbumRequest(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; - - if (id.type != Id::Type::Release) - throw BadParameterGenericError {"id"}; + ReleaseId id {getMandatoryParameterAs(context.parameters, "id")}; auto transaction {context.dbSession.createSharedTransaction()}; - Release::pointer release {Release::getById(context.dbSession, id.value)}; + Release::pointer release {Release::getById(context.dbSession, id)}; if (!release) throw RequestedDataNotFoundError {}; @@ -908,14 +896,11 @@ Response handleGetArtistRequest(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; - - if (id.type != Id::Type::Artist) - throw BadParameterGenericError {"id"}; + ArtistId id {getMandatoryParameterAs(context.parameters, "id")}; auto transaction {context.dbSession.createSharedTransaction()}; - Artist::pointer artist {Artist::getById(context.dbSession, id.value)}; + Artist::pointer artist {Artist::getById(context.dbSession, id)}; if (!artist) throw RequestedDataNotFoundError {}; @@ -940,9 +925,7 @@ Response handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; - if (id.type != Id::Type::Artist) - throw BadParameterGenericError {"id"}; + ArtistId id {getMandatoryParameterAs(context.parameters, "id")}; // Optional params std::size_t count {getParameterAs(context.parameters, "count").value_or(20)}; @@ -953,7 +936,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) { auto transaction {context.dbSession.createSharedTransaction()}; - Artist::pointer artist {Artist::getById(context.dbSession, id.value)}; + Artist::pointer artist {Artist::getById(context.dbSession, id)}; if (!artist) throw RequestedDataNotFoundError {}; @@ -963,7 +946,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) } auto similarArtistsId {Service::get()->getSimilarArtists(context.dbSession, - id.value, + id, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, count)}; @@ -974,7 +957,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3) if (!user) throw UserNotAuthorizedError {}; - for ( const auto& similarArtistId : similarArtistsId ) + for ( const ArtistId similarArtistId : similarArtistsId ) { Artist::pointer similarArtist {Artist::getById(context.dbSession, similarArtistId)}; if (similarArtist) @@ -1049,66 +1032,63 @@ Response handleGetMusicDirectoryRequest(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; + const auto artistId {getParameterAs(context.parameters, "id")}; + const auto releaseId {getParameterAs(context.parameters, "id")}; + const auto trackId {getParameterAs(context.parameters, "id")}; + const auto root {getParameterAs(context.parameters, "id")}; + + if (!root && !artistId && !releaseId && !trackId) + throw BadParameterGenericError {"id"}; Response response {Response::createOkResponse(context)}; Response::Node& directoryNode {response.createNode("directory")}; - directoryNode.setAttribute("id", IdToString(id)); - auto transaction {context.dbSession.createSharedTransaction()}; User::pointer user {User::getById(context.dbSession, context.userId)}; if (!user) throw UserNotAuthorizedError {}; - switch (id.type) + if (root) { - case Id::Type::Root: - { - directoryNode.setAttribute("name", "Music"); + 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) - directoryNode.addArrayChild("child", artistToResponseNode(user, artist, false /* no id3 */)); - - break; - } - - case Id::Type::Artist: - { - auto artist {Artist::getById(context.dbSession, id.value)}; - if (!artist) - throw RequestedDataNotFoundError {}; - - directoryNode.setAttribute("name", makeNameFilesystemCompatible(artist->getName())); - - auto releases {artist->getReleases()}; - for (const Release::pointer& release : releases) - directoryNode.addArrayChild("child", releaseToResponseNode(release, context.dbSession, user, false /* no id3 */)); - - break; - } - - case Id::Type::Release: - { - auto release {Release::getById(context.dbSession, id.value)}; - if (!release) - throw RequestedDataNotFoundError {}; - - directoryNode.setAttribute("name", makeNameFilesystemCompatible(release->getName())); - - auto tracks {release->getTracks()}; - for (const Track::pointer& track : tracks) - directoryNode.addArrayChild("child", trackToResponseNode(track, context.dbSession, user)); - - break; - } - - default: - throw BadParameterGenericError {"id"}; + bool moreResults{}; + auto artists {Artist::getAll(context.dbSession, Artist::SortMethod::BySortName, std::nullopt, moreResults)}; + for (const Artist::pointer& artist : artists) + directoryNode.addArrayChild("child", artistToResponseNode(user, artist, false /* no id3 */)); } + else if (artistId) + { + directoryNode.setAttribute("id", idToString(*artistId)); + + auto artist {Artist::getById(context.dbSession, *artistId)}; + if (!artist) + throw RequestedDataNotFoundError {}; + + directoryNode.setAttribute("name", makeNameFilesystemCompatible(artist->getName())); + + auto releases {artist->getReleases()}; + for (const Release::pointer& release : releases) + directoryNode.addArrayChild("child", releaseToResponseNode(release, context.dbSession, user, false /* no id3 */)); + } + else if (releaseId) + { + directoryNode.setAttribute("id", idToString(*releaseId)); + + auto release {Release::getById(context.dbSession, *releaseId)}; + if (!release) + throw RequestedDataNotFoundError {}; + + directoryNode.setAttribute("name", makeNameFilesystemCompatible(release->getName())); + + auto tracks {release->getTracks()}; + for (const Track::pointer& track : tracks) + directoryNode.addArrayChild("child", trackToResponseNode(track, context.dbSession, user)); + } + else + throw BadParameterGenericError {"id"}; return response; } @@ -1199,21 +1179,19 @@ Response handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3) { // Mandatory params - const Id artistId {getMandatoryParameterAs(context.parameters, "id")}; - if (artistId.type != Id::Type::Artist) - throw BadParameterGenericError {"id"}; + const ArtistId artistId {getMandatoryParameterAs(context.parameters, "id")}; // Optional params std::size_t count {getParameterAs(context.parameters, "count").value_or(50)}; - auto similarArtistIds {Service::get()->getSimilarArtists(context.dbSession, - artistId.value, + const auto similarArtistIds {Service::get()->getSimilarArtists(context.dbSession, + artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; auto transaction {context.dbSession.createSharedTransaction()}; - const Artist::pointer artist {Artist::getById(context.dbSession, artistId.value)}; + const Artist::pointer artist {Artist::getById(context.dbSession, artistId)}; if (!artist) throw RequestedDataNotFoundError {}; @@ -1223,7 +1201,7 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3) // "Returns a random collection of songs from the given artist and similar artists" auto tracks {artist->getRandomTracks(count / 2)}; - for (const Database::IdType similarArtistId : similarArtistIds) + for (const ArtistId similarArtistId : similarArtistIds) { const Artist::pointer similarArtist {Artist::getById(context.dbSession, similarArtistId)}; if (!similarArtist) @@ -1318,7 +1296,7 @@ tracklistToResponseNode(const TrackList::pointer& tracklist, Session&) { Response::Node playlistNode; - playlistNode.setAttribute("id", IdToString({Id::Type::Playlist, tracklist.id()})); + playlistNode.setAttribute("id", idToString(tracklist->getId())); playlistNode.setAttribute("name", tracklist->getName()); playlistNode.setAttribute("songCount", tracklist->getCount()); playlistNode.setAttribute("duration", std::chrono::duration_cast(tracklist->getDuration()).count()); @@ -1334,9 +1312,7 @@ Response handleGetPlaylistRequest(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; - if (id.type != Id::Type::Playlist) - throw BadParameterGenericError {"id"}; + TrackListId trackListId {getMandatoryParameterAs(context.parameters, "id")}; auto transaction {context.dbSession.createSharedTransaction()}; @@ -1344,7 +1320,7 @@ handleGetPlaylistRequest(RequestContext& context) if (!user) throw UserNotAuthorizedError {}; - TrackList::pointer tracklist {TrackList::getById(context.dbSession, id.value)}; + TrackList::pointer tracklist {TrackList::getById(context.dbSession, trackListId)}; if (!tracklist) throw RequestedDataNotFoundError {}; @@ -1411,7 +1387,7 @@ handleGetSongsByGenreRequest(RequestContext& context) Response::Node& songsByGenreNode {response.createNode("songsByGenre")}; bool more; - auto tracks {Track::getByFilter(context.dbSession, {cluster.id()}, {}, Range {offset, size}, more)}; + auto tracks {Track::getByFilter(context.dbSession, {cluster->getId()}, {}, Range {offset, size}, more)}; for (const Track::pointer& track : tracks) songsByGenreNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user)); @@ -1504,9 +1480,9 @@ handleSearchRequestCommon(RequestContext& context, bool id3) struct StarParameters { - std::vector artistIds; - std::vector releaseIds; - std::vector trackIds; + std::vector artistIds; + std::vector releaseIds; + std::vector trackIds; }; static @@ -1515,34 +1491,10 @@ getStarParameters(const Wt::Http::ParameterMap& parameters) { StarParameters res; - std::vector ids {getMultiParametersAs(parameters, "id")}; - res.artistIds = getMultiParametersAs(parameters, "artistId"); - res.releaseIds = getMultiParametersAs(parameters, "albumId"); - - if (!std::all_of(std::cbegin(res.releaseIds ), std::cend(res.releaseIds ), [](const Id& id) { return id.type == Id::Type::Release; })) - throw BadParameterGenericError {"albumId"}; - - if (!std::all_of(std::cbegin(res.artistIds ), std::cend(res.artistIds ), [](const Id& id) { return id.type == Id::Type::Artist; })) - throw BadParameterGenericError {"artistId"}; - - // Redispatch the old "id" parameter in new lists - for (const Id& id : ids) - { - switch (id.type) - { - case Id::Type::Artist: - res.artistIds.emplace_back(id); - break; - case Id::Type::Release: - res.releaseIds.emplace_back(id); - break; - case Id::Type::Track: - res.trackIds.emplace_back(id); - break; - default: - throw BadParameterGenericError {"id"}; - } - } + // TODO handle parameters for legacy file browsing + res.trackIds = getMultiParametersAs(parameters, "id"); + res.artistIds = getMultiParametersAs(parameters, "artistId"); + res.releaseIds = getMultiParametersAs(parameters, "albumId"); return res; } @@ -1559,27 +1511,27 @@ handleStarRequest(RequestContext& context) if (!user) throw UserNotAuthorizedError {}; - for (const Id& id : params.artistIds) + for (const ArtistId id : params.artistIds) { - Artist::pointer artist {Artist::getById(context.dbSession, id.value)}; + Artist::pointer artist {Artist::getById(context.dbSession, id)}; if (!artist) continue; user.modify()->starArtist(artist); } - for (const Id& id : params.releaseIds) + for (const ReleaseId id : params.releaseIds) { - Release::pointer release {Release::getById(context.dbSession, id.value)}; + Release::pointer release {Release::getById(context.dbSession, id)}; if (!release) continue; user.modify()->starRelease(release); } - for (const Id& id : params.trackIds) + for (const TrackId id : params.trackIds) { - Track::pointer track {Track::getById(context.dbSession, id.value)}; + Track::pointer track {Track::getById(context.dbSession, id)}; if (!track) continue; @@ -1615,27 +1567,27 @@ handleUnstarRequest(RequestContext& context) if (!user) throw RequestedDataNotFoundError {}; - for (const Id& id : params.artistIds) + for (const ArtistId id : params.artistIds) { - Artist::pointer artist {Artist::getById(context.dbSession, id.value)}; + Artist::pointer artist {Artist::getById(context.dbSession, id)}; if (!artist) continue; user.modify()->unstarArtist(artist); } - for (const Id& id : params.releaseIds) + for (const ReleaseId id : params.releaseIds) { - Release::pointer release {Release::getById(context.dbSession, id.value)}; + Release::pointer release {Release::getById(context.dbSession, id)}; if (!release) continue; user.modify()->unstarRelease(release); } - for (const Id& id : params.trackIds) + for (const TrackId id : params.trackIds) { - Track::pointer track {Track::getById(context.dbSession, id.value)}; + Track::pointer track {Track::getById(context.dbSession, id)}; if (!track) continue; @@ -1650,14 +1602,10 @@ static Response handleScrobble(RequestContext& context) { - const std::vector ids {getMandatoryMultiParametersAs(context.parameters, "id")}; + const std::vector ids {getMandatoryMultiParametersAs(context.parameters, "id")}; const std::vector times {getMultiParametersAs(context.parameters, "time")}; const bool submission{getParameterAs(context.parameters, "submission").value_or(true)}; - // only for tracks - if (!std::all_of(std::cbegin(ids), std::cend(ids), [](const Id& id) { return id.type == Id::Type::Track; })) - throw BadParameterGenericError {"id"}; - // playing now => no time to be provided if (!submission && !times.empty()) throw BadParameterGenericError {"time"}; @@ -1672,19 +1620,19 @@ handleScrobble(RequestContext& context) if (!submission) { - Service::get()->listenStarted({context.userId, ids.front().value}); + Service::get()->listenStarted({context.userId, ids.front()}); } else { if (times.empty()) { - Service::get()->listenFinished({context.userId, ids.front().value}); + Service::get()->listenFinished({context.userId, ids.front()}); } else { for (std::size_t i {}; i < ids.size(); ++i) { - const Database::IdType trackId {ids[i].value}; + 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))}); } @@ -1701,7 +1649,7 @@ handleUpdateUserRequest(RequestContext& context) std::string username {getMandatoryParameterAs(context.parameters, "username")}; std::optional password {getParameterAs(context.parameters, "password")}; - Database::IdType userId; + UserId userId; { auto transaction {context.dbSession.createSharedTransaction()}; @@ -1709,7 +1657,7 @@ handleUpdateUserRequest(RequestContext& context) if (!user) throw RequestedDataNotFoundError {}; - userId = user.id(); + userId = user->getId(); } if (password) @@ -1742,18 +1690,13 @@ Response handleUpdatePlaylistRequest(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "playlistId")}; - if (id.type != Id::Type::Playlist) - throw BadParameterGenericError {"playlistId"}; + TrackListId id {getMandatoryParameterAs(context.parameters, "playlistId")}; // Optional parameters auto name {getParameterAs(context.parameters, "name")}; auto isPublic {getParameterAs(context.parameters, "public")}; - std::vector trackIdsToAdd {getMultiParametersAs(context.parameters, "songIdToAdd")}; - if (!std::all_of(std::cbegin(trackIdsToAdd), std::cend(trackIdsToAdd), [](const Id& id) { return id.type == Id::Type::Track; })) - throw BadParameterGenericError {"songIdToAdd"}; - + std::vector trackIdsToAdd {getMultiParametersAs(context.parameters, "songIdToAdd")}; std::vector trackPositionsToRemove {getMultiParametersAs(context.parameters, "songIndexToRemove")}; auto transaction {context.dbSession.createUniqueTransaction()}; @@ -1762,7 +1705,7 @@ handleUpdatePlaylistRequest(RequestContext& context) if (!user) throw UserNotAuthorizedError {}; - TrackList::pointer tracklist {TrackList::getById(context.dbSession, id.value)}; + TrackList::pointer tracklist {TrackList::getById(context.dbSession, id)}; if (!tracklist || tracklist->getUser() != user || tracklist->getType() != TrackList::Type::Playlist) @@ -1789,13 +1732,13 @@ handleUpdatePlaylistRequest(RequestContext& context) } // Add tracks - for (const Id& trackIdToAdd : trackIdsToAdd) + for (const TrackId trackIdToAdd : trackIdsToAdd) { - Track::pointer track {Track::getById(context.dbSession, trackIdToAdd.value)}; + Track::pointer track {Track::getById(context.dbSession, trackIdToAdd)}; if (!track) continue; - TrackListEntry::create(context.dbSession, track, tracklist ); + TrackListEntry::create(context.dbSession, track, tracklist); } return Response::createOkResponse(context); @@ -1832,10 +1775,7 @@ Response handleCreateBookmark(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; - if (id.type != Id::Type::Track) - throw BadParameterGenericError {"id"}; - + TrackId id {getMandatoryParameterAs(context.parameters, "id")}; unsigned long position {getMandatoryParameterAs(context.parameters, "position")}; const std::optional comment {getParameterAs(context.parameters, "comment")}; @@ -1845,7 +1785,7 @@ handleCreateBookmark(RequestContext& context) if (!user) throw UserNotAuthorizedError {}; - const Track::pointer track {Track::getById(context.dbSession, id.value)}; + const Track::pointer track {Track::getById(context.dbSession, id)}; if (!track) throw RequestedDataNotFoundError {}; @@ -1866,9 +1806,7 @@ Response handleDeleteBookmark(RequestContext& context) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; - if (id.type != Id::Type::Track) - throw BadParameterGenericError {"id"}; + TrackId id {getMandatoryParameterAs(context.parameters, "id")}; auto transaction {context.dbSession.createUniqueTransaction()}; @@ -1876,7 +1814,7 @@ handleDeleteBookmark(RequestContext& context) if (!user) throw UserNotAuthorizedError {}; - const Track::pointer track {Track::getById(context.dbSession, id.value)}; + const Track::pointer track {Track::getById(context.dbSession, id)}; if (!track) throw RequestedDataNotFoundError {}; @@ -1901,23 +1839,20 @@ void handleGetCoverArt(RequestContext& context, const Wt::Http::Request& /*request*/, Wt::Http::Response& response) { // Mandatory params - Id id {getMandatoryParameterAs(context.parameters, "id")}; + const auto trackId {getParameterAs(context.parameters, "id")}; + const auto releaseId {getParameterAs(context.parameters, "id")}; + + if (!trackId && !releaseId) + throw BadParameterGenericError {"id"}; std::size_t size {getParameterAs(context.parameters, "size").value_or(256)}; - size = clamp(size, std::size_t {32}, std::size_t {1024}); + size = Utils::clamp(size, std::size_t {32}, std::size_t {1024}); std::shared_ptr cover; - switch (id.type) - { - case Id::Type::Track: - cover = Service::get()->getFromTrack(context.dbSession, id.value, size); - break; - case Id::Type::Release: - cover = Service::get()->getFromRelease(context.dbSession, id.value, size); - break; - default: - throw BadParameterGenericError {"id"}; - } + if (trackId) + cover = Service::get()->getFromTrack(context.dbSession, *trackId, size); + else if (releaseId) + cover = Service::get()->getFromRelease(context.dbSession, *releaseId, size); response.out().write(reinterpret_cast(cover->getData()), cover->getDataSize()); response.setMimeType(std::string {cover->getMimeType()}); @@ -1928,7 +1863,7 @@ using CheckImplementedFunc = std::function; struct RequestEntryPointInfo { RequestHandlerFunc func; - EnumSet allowedUserTypes {Database::UserType::DEMO, Database::UserType::REGULAR, Database::UserType::ADMIN}; + EnumSet allowedUserTypes {UserType::DEMO, UserType::REGULAR, UserType::ADMIN}; CheckImplementedFunc checkFunc {}; }; @@ -2019,11 +1954,11 @@ static std::unordered_map requestEntryPoints // User management {"getUser", {handleGetUserRequest}}, - {"getUsers", {handleGetUsersRequest, {Database::UserType::ADMIN}}}, - {"createUser", {handleCreateUserRequest, {Database::UserType::ADMIN}, &checkSetPasswordImplemented}}, - {"updateUser", {handleUpdateUserRequest, {Database::UserType::ADMIN}}}, - {"deleteUser", {handleDeleteUserRequest, {Database::UserType::ADMIN}}}, - {"changePassword", {handleChangePassword, {Database::UserType::REGULAR, Database::UserType::ADMIN}, &checkSetPasswordImplemented}}, + {"getUsers", {handleGetUsersRequest, {UserType::ADMIN}}}, + {"createUser", {handleCreateUserRequest, {UserType::ADMIN}, &checkSetPasswordImplemented}}, + {"updateUser", {handleUpdateUserRequest, {UserType::ADMIN}}}, + {"deleteUser", {handleDeleteUserRequest, {UserType::ADMIN}}}, + {"changePassword", {handleChangePassword, {UserType::REGULAR, UserType::ADMIN}, &checkSetPasswordImplemented}}, // Bookmarks {"getBookmarks", {handleGetBookmarks}}, @@ -2033,8 +1968,8 @@ static std::unordered_map requestEntryPoints {"savePlayQueue", {handleNotImplemented}}, // Media library scanning - {"getScanStatus", {Scan::handleGetScanStatus, {Database::UserType::ADMIN}}}, - {"startScan", {Scan::handleStartScan, {Database::UserType::ADMIN}}}, + {"getScanStatus", {Scan::handleGetScanStatus, {UserType::ADMIN}}}, + {"startScan", {Scan::handleStartScan, {UserType::ADMIN}}}, }; using MediaRetrievalHandlerFunc = std::function; @@ -2047,7 +1982,7 @@ static std::unordered_map mediaRetrieval }; static -Database::IdType +Database::UserId authenticateUser(const Wt::Http::Request &request, const ClientInfo& clientInfo, Session& dbSession) { if (auto *authEnvService {Service<::Auth::IEnvService>::get()}) @@ -2108,7 +2043,7 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp Session& dbSession {_db.getTLSSession()}; - const Database::IdType userId {authenticateUser(request, clientInfo, dbSession)}; + const Database::UserId userId {authenticateUser(request, clientInfo, dbSession)}; RequestContext requestContext {parameters, dbSession, userId, clientInfo.name}; auto itEntryPoint {requestEntryPoints.find(requestPath)}; diff --git a/src/libs/utils/include/utils/Utils.hpp b/src/libs/utils/include/utils/Utils.hpp index 60cd80fc..ebf1df61 100644 --- a/src/libs/utils/include/utils/Utils.hpp +++ b/src/libs/utils/include/utils/Utils.hpp @@ -19,12 +19,25 @@ #pragma once +#include #include -template> -constexpr T clamp(T v, T lo, T hi, Compare comp = {}) +namespace Utils { - assert(!comp(hi, lo)); - return comp(v, lo) ? lo : comp(hi, v) ? hi : v; -} + template> + constexpr T clamp(T v, T lo, T hi, Compare comp = {}) + { + assert(!comp(hi, lo)); + return comp(v, lo) ? lo : comp(hi, v) ? hi : v; + } + + template + void + push_back_if_not_present(Container& container, const T& val) + { + if (std::find(std::cbegin(container), std::cend(container), val) == std::cend(container)) + container.push_back(val); + } + +} diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index 11a27a0b..5e1ec8ea 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -47,7 +47,7 @@ static const std::string authCookieName {"LmsAuth"}; static void -createAuthToken(Database::IdType userId, const Wt::WDateTime& expiry) +createAuthToken(Database::UserId userId, const Wt::WDateTime& expiry) { const std::string secret {Service<::Auth::IAuthTokenService>::get()->createAuthToken(LmsApp->getDbSession(), userId, expiry)}; @@ -60,7 +60,7 @@ createAuthToken(Database::IdType userId, const Wt::WDateTime& expiry) } -std::optional +std::optional processAuthToken(const Wt::WEnvironment& env) { const std::string* authCookie {env.getCookie(authCookieName)}; @@ -111,7 +111,7 @@ class AuthModel : public Wt::WFormModel Database::User::pointer user {Database::User::getByLoginName(LmsApp->getDbSession(), valueText(LoginNameField).toUTF8())}; user.modify()->setLastLogin(Wt::WDateTime::currentDateTime()); - _userId = user.id(); + _userId = user->getId(); isDemo = user->isDemo(); } @@ -158,11 +158,11 @@ class AuthModel : public Wt::WFormModel return (validation(field).state() == Wt::ValidationState::Valid); } - std::optional getUserId() const { return _userId; } + std::optional getUserId() const { return _userId; } private: - std::optional _userId; + std::optional _userId; }; const AuthModel::Field AuthModel::LoginNameField {"login-name"}; diff --git a/src/lms/ui/Auth.hpp b/src/lms/ui/Auth.hpp index 421271dc..5fda238b 100644 --- a/src/lms/ui/Auth.hpp +++ b/src/lms/ui/Auth.hpp @@ -25,7 +25,7 @@ namespace UserInterface { -std::optional +std::optional processAuthToken(const Wt::WEnvironment& env); class Auth : public Wt::WTemplateFormView @@ -33,7 +33,7 @@ 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 5a766550..a2d0b4e3 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -95,7 +95,7 @@ LmsApplication::getDbSession() return _db.getTLSSession(); } -Wt::Dbo::ptr +Database::User::pointer LmsApplication::getUser() { if (!_authenticatedUser) @@ -104,7 +104,7 @@ LmsApplication::getUser() return Database::User::getById(getDbSession(), _authenticatedUser->userId); } -Database::IdType +Database::UserId LmsApplication::getUserId() { return _authenticatedUser->userId; @@ -135,7 +135,7 @@ LmsApplication::getUserLoginName() LmsApplication::LmsApplication(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager, - std::optional userId) + std::optional userId) : Wt::WApplication {env} , _db {db} , _appManager {appManager} @@ -208,7 +208,7 @@ void LmsApplication::processPasswordAuth() { { - std::optional userId {processAuthToken(environment())}; + std::optional userId {processAuthToken(environment())}; if (userId) { LMS_LOG(UI, DEBUG) << "User authenticated using Auth token!"; @@ -236,7 +236,7 @@ LmsApplication::processPasswordAuth() else { Auth* auth {root()->addNew()}; - auth->userLoggedIn.connect(this, [this](Database::IdType userId) + auth->userLoggedIn.connect(this, [this](Database::UserId userId) { _authenticatedUser = {userId, true}; onUserLoggedIn(); @@ -272,7 +272,7 @@ LmsApplication::createArtistLink(Database::Artist::pointer artist) if (const auto mbid {artist->getMBID()}) return Wt::WLink {Wt::LinkType::InternalPath, "/artist/mbid/" + std::string {mbid->getAsString()}}; else - return Wt::WLink {Wt::LinkType::InternalPath, "/artist/" + std::to_string(artist.id())}; + return Wt::WLink {Wt::LinkType::InternalPath, "/artist/" + artist->getId().toString()}; } std::unique_ptr @@ -296,7 +296,7 @@ LmsApplication::createReleaseLink(Database::Release::pointer release) if (const auto mbid {release->getMBID()}) return Wt::WLink {Wt::LinkType::InternalPath, "/release/mbid/" + std::string {mbid->getAsString()}}; else - return Wt::WLink {Wt::LinkType::InternalPath, "/release/" + std::to_string(release.id())}; + return Wt::WLink {Wt::LinkType::InternalPath, "/release/" + release->getId().toString()}; } std::unique_ptr @@ -320,7 +320,7 @@ LmsApplication::createCluster(Database::Cluster::pointer cluster, bool canDelete { auto getStyleClass = [](const Database::Cluster::pointer cluster) { - switch (cluster->getType().id() % 6) + switch (cluster->getType()->getId().getValue() % 6) { case 0: return "label-info"; case 1: return "label-warning"; @@ -529,7 +529,7 @@ LmsApplication::createHome() mainStack->addNew(); } - explore->tracksAction.connect([this] (PlayQueueAction action, const std::vector& trackIds) + explore->tracksAction.connect([this] (PlayQueueAction action, const std::vector& trackIds) { _playQueue->processTracks(action, trackIds); }); @@ -544,15 +544,15 @@ LmsApplication::createHome() _playQueue->playPrevious(); }); - _mediaPlayer->scrobbleListenNow.connect([this](Database::IdType trackId) + _mediaPlayer->scrobbleListenNow.connect([this](Database::TrackId trackId) { - LMS_LOG(UI, DEBUG) << "Received ScrobbleListenNow from player for trackId = " << trackId; + LMS_LOG(UI, DEBUG) << "Received ScrobbleListenNow from player for trackId = " << trackId.toString(); const Scrobbling::Listen listen {getUserId(), trackId}; Service::get()->listenStarted(listen); }); - _mediaPlayer->scrobbleListenFinished.connect([this](Database::IdType trackId, unsigned durationMs) + _mediaPlayer->scrobbleListenFinished.connect([this](Database::TrackId trackId, unsigned durationMs) { - LMS_LOG(UI, DEBUG) << "Received ScrobbleListenFinished from player for trackId = " << trackId << ", duration = " << (durationMs / 1000) << "s"; + 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)); @@ -563,7 +563,7 @@ LmsApplication::createHome() _playQueue->playNext(); }); - _playQueue->trackSelected.connect([this] (Database::IdType trackId, bool play, float replayGain) + _playQueue->trackSelected.connect([this] (Database::TrackId trackId, bool play, float replayGain) { _mediaPlayer->loadTrack(trackId, play, replayGain); }); diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 5a00c168..7f8f9446 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -51,7 +51,7 @@ class LmsApplication : public Wt::WApplication { public: - LmsApplication(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager, std::optional userId = std::nullopt); + LmsApplication(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager, std::optional userId = std::nullopt); ~LmsApplication(); static std::unique_ptr create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager); @@ -62,8 +62,8 @@ class LmsApplication : public Wt::WApplication std::shared_ptr getCoverResource() { return _coverResource; } Database::Session& getDbSession(); // always thread safe - Wt::Dbo::ptr getUser(); - Database::IdType getUserId(); + Database::ObjectPtr getUser(); + Database::UserId getUserId(); bool isUserAuthStrong() const; // user must be logged in prior this call Database::UserType getUserType(); // user must be logged in prior this call std::string getUserLoginName(); // user must be logged in prior this call @@ -84,11 +84,11 @@ class LmsApplication : public Wt::WApplication }; void notifyMsg(MsgType type, const Wt::WString& message, std::chrono::milliseconds duration = std::chrono::milliseconds {4000}); - static Wt::WLink createArtistLink(Wt::Dbo::ptr artist); - static std::unique_ptr createArtistAnchor(Wt::Dbo::ptr artist, bool addText = true); - static Wt::WLink createReleaseLink(Wt::Dbo::ptr release); - static std::unique_ptr createReleaseAnchor(Wt::Dbo::ptr release, bool addText = true); - static std::unique_ptr createCluster(Wt::Dbo::ptr cluster, bool canDelete = false); + static Wt::WLink createArtistLink(Database::ObjectPtr artist); + static std::unique_ptr createArtistAnchor(Database::ObjectPtr artist, bool addText = true); + static Wt::WLink createReleaseLink(Database::ObjectPtr release); + static std::unique_ptr createReleaseAnchor(Database::ObjectPtr release, bool addText = true); + static std::unique_ptr createCluster(Database::ObjectPtr cluster, bool canDelete = false); Wt::WPopupMenu* createPopupMenu(); MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; } @@ -119,7 +119,7 @@ class LmsApplication : public Wt::WApplication Scanner::Events _scannerEvents; struct UserAuthInfo { - Database::IdType userId; + Database::UserId userId; bool strongAuth {}; }; std::optional _authenticatedUser; diff --git a/src/lms/ui/LmsApplicationManager.hpp b/src/lms/ui/LmsApplicationManager.hpp index 2e3459cd..1d7761f7 100644 --- a/src/lms/ui/LmsApplicationManager.hpp +++ b/src/lms/ui/LmsApplicationManager.hpp @@ -43,6 +43,6 @@ namespace UserInterface void unregisterApplication(LmsApplication& application); std::mutex _mutex; - std::unordered_map> m_applications; + std::unordered_map> m_applications; }; } // UserInterface diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index 5095c5a7..4ee65e06 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -154,7 +154,7 @@ replayGainPreAmpGainFromString(const std::string& str) if (!value) return std::nullopt; - return clamp(*value, (double)MediaPlayer::Settings::ReplayGain::minPreAmpGain, (double)MediaPlayer::Settings::ReplayGain::maxPreAmpGain); + return Utils::clamp(*value, (double)MediaPlayer::Settings::ReplayGain::minPreAmpGain, (double)MediaPlayer::Settings::ReplayGain::maxPreAmpGain); } static MediaPlayer::Settings settingsfromJSString(const std::string& strSettings) @@ -233,9 +233,9 @@ MediaPlayer::MediaPlayer() } void -MediaPlayer::loadTrack(Database::IdType trackId, bool play, float replayGain) +MediaPlayer::loadTrack(Database::TrackId trackId, bool play, float replayGain) { - LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId; + LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId.toString(); std::ostringstream oss; { @@ -252,7 +252,7 @@ MediaPlayer::loadTrack(Database::IdType trackId, bool play, float replayGain) oss << "var params = {" - << " trackId :\"" << trackId << "\"," + << " trackId :\"" << trackId.toString() << "\"," << " nativeResource: \"" << nativeResource << "\"," << " transcodeResource: \"" << transcodeResource << "\"," << " duration: " << std::chrono::duration_cast(track->getDuration()).count() << "," diff --git a/src/lms/ui/MediaPlayer.hpp b/src/lms/ui/MediaPlayer.hpp index a5be6bd5..d4f1700b 100644 --- a/src/lms/ui/MediaPlayer.hpp +++ b/src/lms/ui/MediaPlayer.hpp @@ -94,22 +94,22 @@ class MediaPlayer : public Wt::WTemplate MediaPlayer& operator=(const MediaPlayer&) = delete; MediaPlayer& operator=(MediaPlayer&&) = delete; - std::optional getTrackLoaded() const { return _trackIdLoaded; } + std::optional getTrackLoaded() const { return _trackIdLoaded; } - void loadTrack(Database::IdType trackId, bool play, float replayGain); + void loadTrack(Database::TrackId trackId, bool play, float replayGain); void stop(); std::optional getSettings() const { return _settings; } void setSettings(const Settings& settings); // Signals - Wt::JSignal<> playPrevious; - Wt::JSignal<> playNext; - Wt::Signal trackLoaded; - Wt::Signal<> settingsLoaded; + Wt::JSignal<> playPrevious; + Wt::JSignal<> playNext; + Wt::Signal trackLoaded; + Wt::Signal<> settingsLoaded; - Wt::JSignal scrobbleListenNow; - Wt::JSignal scrobbleListenFinished; + Wt::JSignal scrobbleListenNow; + Wt::JSignal scrobbleListenFinished; Wt::JSignal<> playbackEnded; @@ -117,7 +117,7 @@ class MediaPlayer : public Wt::WTemplate std::unique_ptr _audioFileResource; std::unique_ptr _audioTranscodeResource; - std::optional _trackIdLoaded; + std::optional _trackIdLoaded; std::optional _settings; Wt::JSignal _settingsLoaded; diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index be8c0c82..846069b0 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -23,6 +23,7 @@ #include #include "database/Cluster.hpp" +#include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" #include "database/TrackList.hpp" @@ -129,7 +130,7 @@ PlayQueue::PlayQueue() if (LmsApp->getUser()->isDemo()) { - LMS_LOG(UI, DEBUG) << "Removing tracklist id " << _tracklistId; + LMS_LOG(UI, DEBUG) << "Removing tracklist id " << _tracklistId.toString(); auto tracklist = Database::TrackList::getById(LmsApp->getDbSession(), _tracklistId); if (tracklist) tracklist.remove(); @@ -167,7 +168,7 @@ PlayQueue::PlayQueue() trackList = Database::TrackList::create(LmsApp->getDbSession(), currentPlayQueueName, Database::TrackList::Type::Internal, false, LmsApp->getUser()); } - _tracklistId = trackList.id(); + _tracklistId = trackList->getId(); } updateInfo(); @@ -226,7 +227,7 @@ PlayQueue::loadTrack(std::size_t pos, bool play) { updateCurrentTrack(false); - Database::IdType trackId {}; + Database::TrackId trackId {}; bool addRadioTrack {}; std::optional replayGain {}; { @@ -253,7 +254,7 @@ PlayQueue::loadTrack(std::size_t pos, bool play) _trackPos = pos; auto track = tracklist->getEntry(*_trackPos)->getTrack(); - trackId = track.id(); + trackId = track->getId(); replayGain = getReplayGain(pos, track); @@ -313,7 +314,7 @@ PlayQueue::updateCurrentTrack(bool selected) } std::size_t -PlayQueue::enqueueTracks(const std::vector& trackIds) +PlayQueue::enqueueTracks(const std::vector& trackIds) { std::size_t nbTracksQueued {}; @@ -323,7 +324,7 @@ PlayQueue::enqueueTracks(const std::vector& trackIds) auto tracklist {getTrackList()}; std::size_t nbTracksToEnqueue {tracklist->getCount() + trackIds.size() > _nbMaxEntries ? _nbMaxEntries - tracklist->getCount() : trackIds.size()}; - for (Database::IdType trackId : trackIds) + for (const Database::TrackId trackId : trackIds) { Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), trackId)}; if (!track) @@ -344,7 +345,7 @@ PlayQueue::enqueueTracks(const std::vector& trackIds) } void -PlayQueue::processTracks(PlayQueueAction action, const std::vector& trackIds) +PlayQueue::processTracks(PlayQueueAction action, const std::vector& trackIds) { std::size_t nbAddedTracks {}; @@ -367,7 +368,7 @@ PlayQueue::processTracks(PlayQueueAction action, const std::vector shuffledTrackIds {trackIds}; + std::vector shuffledTrackIds {trackIds}; Random::shuffleContainer(shuffledTrackIds); nbAddedTracks = enqueueTracks(shuffledTrackIds); } @@ -402,9 +403,9 @@ PlayQueue::addSome() void PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) { - const auto tracklistEntryId {tracklistEntry.id()}; + const Database::TrackListEntryId tracklistEntryId {tracklistEntry->getId()}; const auto track {tracklistEntry->getTrack()}; - const Database::IdType trackId {track->id()}; + const Database::TrackId trackId {track->getId()}; Wt::WTemplate* entry = _entriesContainer->addNew(Wt::WString::tr("Lms.PlayQueue.template.entry")); @@ -434,7 +435,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) { Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false)); auto cover = std::make_unique(); - cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large)); + cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release->getId(), CoverResource::Size::Large)); cover->setStyleClass("Lms-cover"); cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); anchor->setImage(std::move(cover)); @@ -443,7 +444,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) else { auto cover = entry->bindNew("cover"); - cover->setImageLink(LmsApp->getCoverResource()->getTrackUrl(track.id(), CoverResource::Size::Large)); + cover->setImageLink(LmsApp->getCoverResource()->getTrackUrl(track->getId(), CoverResource::Size::Large)); cover->setStyleClass("Lms-cover"); cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); } @@ -520,7 +521,7 @@ PlayQueue::enqueueRadioTracks() { const auto similarTrackIds {Service::get()->getSimilarTracksFromTrackList(LmsApp->getDbSession(), _tracklistId, 3)}; - std::vector trackToAddIds(std::cbegin(similarTrackIds), std::cend(similarTrackIds)); + std::vector trackToAddIds(std::cbegin(similarTrackIds), std::cend(similarTrackIds)); Random::shuffleContainer(trackToAddIds); enqueueTracks(trackToAddIds); } diff --git a/src/lms/ui/PlayQueue.hpp b/src/lms/ui/PlayQueue.hpp index 77b10afa..1bbb996a 100644 --- a/src/lms/ui/PlayQueue.hpp +++ b/src/lms/ui/PlayQueue.hpp @@ -49,7 +49,7 @@ class PlayQueue : public Wt::WTemplate public: PlayQueue(); - void processTracks(PlayQueueAction action, const std::vector& trackIds); + void processTracks(PlayQueueAction action, const std::vector& trackIds); // play the next track in the queue void playNext(); @@ -58,19 +58,19 @@ class PlayQueue : public Wt::WTemplate void playPrevious(); // Signal emitted when a track is to be load(and optionally played) - Wt::Signal trackSelected; + Wt::Signal trackSelected; // Signal emitted when track is unselected (has to be stopped) Wt::Signal<> trackUnselected; private: - Wt::Dbo::ptr getTrackList() const; + Database::ObjectPtr getTrackList() const; bool isFull() const; void clearTracks(); - std::size_t enqueueTracks(const std::vector& trackIds); + std::size_t enqueueTracks(const std::vector& trackIds); void addSome(); - void addEntry(const Wt::Dbo::ptr& entry); + void addEntry(const Database::ObjectPtr& entry); void enqueueRadioTracks(); void updateInfo(); void updateCurrentTrack(bool selected); @@ -82,7 +82,7 @@ class PlayQueue : public Wt::WTemplate void addRadioTrackFromSimilarity(std::shared_ptr similarityFinder); void addRadioTrackFromClusters(); - std::optional getReplayGain(std::size_t pos, const Wt::Dbo::ptr& track) const; + std::optional getReplayGain(std::size_t pos, const Database::ObjectPtr& track) const; static inline constexpr std::size_t _nbMaxEntries {1000}; static inline constexpr std::size_t _batchSize {12}; @@ -90,7 +90,7 @@ class PlayQueue : public Wt::WTemplate bool _repeatAll {}; bool _radioMode {}; bool _mediaPlayerSettingsLoaded {}; - Database::IdType _tracklistId {}; + Database::TrackListId _tracklistId {}; InfiniteScrollingContainer* _entriesContainer {}; Wt::WText* _nbTracks {}; Wt::WText* _repeatBtn {}; diff --git a/src/lms/ui/PlayQueueAction.hpp b/src/lms/ui/PlayQueueAction.hpp index 29a91351..fffd8133 100644 --- a/src/lms/ui/PlayQueueAction.hpp +++ b/src/lms/ui/PlayQueueAction.hpp @@ -34,7 +34,8 @@ namespace UserInterface PlayShuffled, }; - using PlayQueueActionSignal = Wt::Signal&>; - + using PlayQueueActionArtistSignal = Wt::Signal&>; + using PlayQueueActionReleaseSignal = Wt::Signal&>; + using PlayQueueActionTrackSignal = Wt::Signal&>; } diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index fc446feb..1f25251e 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.id(), valueText(PasswordField).toUTF8()); + _authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); } } diff --git a/src/lms/ui/admin/InitWizardView.cpp b/src/lms/ui/admin/InitWizardView.cpp index c353dbf0..b187f472 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.id(), valueText(PasswordField).toUTF8()); + Service<::Auth::IPasswordService>::get()->setPassword(LmsApp->getDbSession(), 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 e0467939..f27901b9 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} { @@ -87,7 +87,7 @@ class UserModel : public Wt::WFormModel throw UserNotFoundException {}; if (_authPasswordService && !valueText(PasswordField).empty()) - _authPasswordService->setPassword(LmsApp->getDbSession(), user.id(), valueText(PasswordField).toUTF8()); + _authPasswordService->setPassword(LmsApp->getDbSession(), 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.id(), valueText(PasswordField).toUTF8()); + _authPasswordService->setPassword(LmsApp->getDbSession(), user->getId(), valueText(PasswordField).toUTF8()); } } @@ -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; - auto userId = StringUtils::readAs(wApp->internalPathNextPart("/admin/user/")); + const std::optional userId {StringUtils::readAs(wApp->internalPathNextPart("/admin/user/"))}; clear(); diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp index a4fc63ca..50bc58e7 100644 --- a/src/lms/ui/admin/UsersView.cpp +++ b/src/lms/ui/admin/UsersView.cpp @@ -72,7 +72,7 @@ UsersView::refreshView() auto users = Database::User::getAll(LmsApp->getDbSession()); for (const auto& user : users) { - const Database::IdType userId {user.id()}; + const Database::UserId userId {user->getId()}; Wt::WTemplate* entry {_container->addNew(Wt::WString::tr("Lms.Admin.Users.template.entry"))}; @@ -93,7 +93,7 @@ UsersView::refreshView() Wt::WPushButton* editBtn = entry->bindNew("edit-btn", Wt::WString::tr("Lms.Admin.Users.edit")); editBtn->clicked().connect([=]() { - LmsApp->setInternalPath("/admin/user/" + std::to_string(userId), true); + LmsApp->setInternalPath("/admin/user/" + userId.toString(), true); }); Wt::WPushButton* delBtn = entry->bindNew("del-btn", Wt::WString::tr("Lms.Admin.Users.del")); diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index 45ebf810..49e19731 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -31,7 +31,7 @@ namespace UserInterface { using namespace Database; - std::vector> + std::vector> ArtistCollector::get(std::optional range, bool& moreResults) { range = getActualRange(range); diff --git a/src/lms/ui/explore/ArtistCollector.hpp b/src/lms/ui/explore/ArtistCollector.hpp index b7bb04d7..05734af0 100644 --- a/src/lms/ui/explore/ArtistCollector.hpp +++ b/src/lms/ui/explore/ArtistCollector.hpp @@ -38,13 +38,13 @@ namespace UserInterface public: using DatabaseCollectorBase::DatabaseCollectorBase; - std::vector> get(std::optional range, bool& moreResults); + std::vector> get(std::optional range, bool& moreResults); void reset() { _randomArtists.clear(); } void setArtistLinkType(std::optional linkType) { _linkType = linkType; } private: - std::vector> getRandomArtists(std::optional range, bool& moreResults); - std::vector _randomArtists; + std::vector> getRandomArtists(std::optional range, bool& moreResults); + std::vector _randomArtists; std::optional _linkType; }; } // ns UserInterface diff --git a/src/lms/ui/explore/ArtistListHelpers.cpp b/src/lms/ui/explore/ArtistListHelpers.cpp index a19906ae..85adb362 100644 --- a/src/lms/ui/explore/ArtistListHelpers.cpp +++ b/src/lms/ui/explore/ArtistListHelpers.cpp @@ -26,7 +26,7 @@ namespace UserInterface::ArtistListHelpers { std::unique_ptr - createEntry(const Wt::Dbo::ptr& artist) + createEntry(const Database::ObjectPtr& artist) { auto res {std::make_unique(Wt::WString::tr("Lms.Explore.Artists.template.entry"))}; res->bindWidget("name", LmsApplication::createArtistAnchor(artist)); @@ -35,7 +35,7 @@ namespace UserInterface::ArtistListHelpers } std::unique_ptr - createEntrySmall(const Wt::Dbo::ptr& artist) + createEntrySmall(const Database::ObjectPtr& artist) { auto res {std::make_unique(Wt::WString::tr("Lms.Explore.Artists.template.entry-small"))}; res->bindWidget("name", LmsApplication::createArtistAnchor(artist)); diff --git a/src/lms/ui/explore/ArtistListHelpers.hpp b/src/lms/ui/explore/ArtistListHelpers.hpp index 22dc67d8..02659bf7 100644 --- a/src/lms/ui/explore/ArtistListHelpers.hpp +++ b/src/lms/ui/explore/ArtistListHelpers.hpp @@ -32,7 +32,7 @@ namespace Database namespace UserInterface::ArtistListHelpers { - std::unique_ptr createEntry(const Wt::Dbo::ptr& artist); - std::unique_ptr createEntrySmall(const Wt::Dbo::ptr& artist); + std::unique_ptr createEntry(const Database::ObjectPtr& artist); + std::unique_ptr createEntrySmall(const Database::ObjectPtr& artist); } diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 79941fc1..0e803db5 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -26,6 +26,7 @@ #include #include "database/Artist.hpp" +#include "database/Cluster.hpp" #include "database/Release.hpp" #include "database/ScanSettings.hpp" #include "database/Session.hpp" @@ -68,7 +69,7 @@ Artist::Artist(Filters* filters) } static -std::optional +std::optional extractArtistIdFromInternalPath() { if (wApp->internalPathMatches("/artist/mbid/")) @@ -78,13 +79,13 @@ extractArtistIdFromInternalPath() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; if (const Database::Artist::pointer artist {Database::Artist::getByMBID(LmsApp->getDbSession(), *mbid)}) - return artist.id(); + return artist->getId(); } return std::nullopt; } - return StringUtils::readAs(wApp->internalPathNextPart("/artist/")); + return StringUtils::readAs(wApp->internalPathNextPart("/artist/")); } void @@ -129,7 +130,7 @@ Artist::refreshView() { for (auto cluster : clusters) { - auto clusterId = cluster.id(); + auto clusterId = cluster->getId(); auto entry = clusterContainers->addWidget(LmsApp->createCluster(cluster)); entry->clicked().connect([=] { @@ -197,7 +198,7 @@ Artist::refreshView() } void -Artist::refreshReleases(const Wt::Dbo::ptr& artist) +Artist::refreshReleases(const Database::ObjectPtr& artist) { const auto releases {artist->getReleases(_filters->getClusterIds())}; if (releases.empty()) @@ -213,7 +214,7 @@ Artist::refreshReleases(const Wt::Dbo::ptr& artist) } void -Artist::refreshNonReleaseTracks(const Wt::Dbo::ptr& artist) +Artist::refreshNonReleaseTracks(const Database::ObjectPtr& artist) { if (!artist->hasNonReleaseTracks()) return; @@ -229,7 +230,7 @@ Artist::refreshNonReleaseTracks(const Wt::Dbo::ptr& artist) } void -Artist::refreshSimilarArtists(const std::unordered_set& similarArtistsId) +Artist::refreshSimilarArtists(const std::vector& similarArtistsId) { if (similarArtistsId.empty()) return; @@ -237,7 +238,7 @@ Artist::refreshSimilarArtists(const std::unordered_set& simila setCondition("if-has-similar-artists", true); Wt::WContainerWidget* similarArtistsContainer {bindNew("similar-artists")}; - for (Database::IdType artistId : similarArtistsId) + for (const Database::ArtistId artistId : similarArtistsId) { const Database::Artist::pointer similarArtist{Database::Artist::getById(LmsApp->getDbSession(), artistId)}; if (!similarArtist) diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index 2d5ce695..18b3894c 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -45,15 +45,15 @@ namespace UserInterface public: Artist(Filters* filters); - PlayQueueActionSignal artistsAction; - PlayQueueActionSignal tracksAction; + PlayQueueActionArtistSignal artistsAction; + PlayQueueActionTrackSignal tracksAction; private: void refreshView(); - void refreshReleases(const Wt::Dbo::ptr& artist); - void refreshNonReleaseTracks(const Wt::Dbo::ptr& artist); - void refreshSimilarArtists(const std::unordered_set& similarArtistsId); - void refreshLinks(const Wt::Dbo::ptr& artist); + void refreshReleases(const Database::ObjectPtr& artist); + void refreshNonReleaseTracks(const Database::ObjectPtr& artist); + void refreshSimilarArtists(const std::vector& similarArtistsId); + void refreshLinks(const Database::ObjectPtr& artist); void addSomeNonReleaseTracks(); static constexpr std::size_t _tracksBatchSize {6}; @@ -61,7 +61,7 @@ namespace UserInterface Filters* _filters {}; InfiniteScrollingContainer* _trackContainer {}; - Database::IdType _artistId {}; + Database::ArtistId _artistId {}; }; } // namespace UserInterface diff --git a/src/lms/ui/explore/Explore.cpp b/src/lms/ui/explore/Explore.cpp index de7a1f6f..8c821fdb 100644 --- a/src/lms/ui/explore/Explore.cpp +++ b/src/lms/ui/explore/Explore.cpp @@ -129,16 +129,16 @@ Explore::search(const Wt::WString& searchText) } static -std::vector -getArtistsTracks(Database::Session& session, const std::vector& artistsId, const std::set&) +std::vector +getArtistsTracks(Database::Session& session, const std::vector& artistsId, const std::vector&) { - std::vector res; + std::vector res; auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - for (Database::IdType artistId : artistsId) + for (const Database::ArtistId artistId : artistsId) { - Database::Artist::pointer artist {Database::Artist::getById(session, artistId)}; + const Database::Artist::pointer artist {Database::Artist::getById(session, artistId)}; if (!artist) continue; @@ -146,49 +146,49 @@ getArtistsTracks(Database::Session& session, const std::vector const std::vector tracks {artist->getTracks()}; res.reserve(res.size() + tracks.size()); - std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const Database::Track::pointer& track) { return track.id(); }); + std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const Database::Track::pointer& track) { return track->getId(); }); } return res; } static -std::vector -getReleasesTracks(Database::Session& session, const std::vector& releasesId, const std::set& clusters) +std::vector +getReleasesTracks(Database::Session& session, const std::vector& releasesId, const std::vector& clusters) { - std::vector res; + std::vector res; auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - for (Database::IdType releaseId : releasesId) + for (const Database::ReleaseId releaseId : releasesId) { - Database::Release::pointer release {Database::Release::getById(session, releaseId)}; + const Database::Release::pointer release {Database::Release::getById(session, releaseId)}; if (!release) continue; const std::vector tracks {release->getTracks(clusters)}; res.reserve(res.size() + tracks.size()); - std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const Database::Track::pointer& track) { return track.id(); }); + std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const Database::Track::pointer& track) { return track->getId(); }); } return res; } void -Explore::handleArtistsAction(PlayQueueAction action, const std::vector& artistsId) +Explore::handleArtistsAction(PlayQueueAction action, const std::vector& artistsId) { tracksAction.emit(action, getArtistsTracks(LmsApp->getDbSession(), artistsId, _filters->getClusterIds())); } void -Explore::handleReleasesAction(PlayQueueAction action, const std::vector& releasesId) +Explore::handleReleasesAction(PlayQueueAction action, const std::vector& releasesId) { tracksAction.emit(action, getReleasesTracks(LmsApp->getDbSession(), releasesId, _filters->getClusterIds())); } void -Explore::handleTracksAction(PlayQueueAction action, const std::vector& tracksId) +Explore::handleTracksAction(PlayQueueAction action, const std::vector& tracksId) { tracksAction.emit(action, tracksId); } diff --git a/src/lms/ui/explore/Explore.hpp b/src/lms/ui/explore/Explore.hpp index 2db65540..66815002 100644 --- a/src/lms/ui/explore/Explore.hpp +++ b/src/lms/ui/explore/Explore.hpp @@ -36,13 +36,13 @@ class Explore : public Wt::WTemplate void search(const Wt::WString& searchText); - PlayQueueActionSignal tracksAction; + PlayQueueActionTrackSignal tracksAction; 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); + 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 {}; diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp index 0796ab01..f8ef1c74 100644 --- a/src/lms/ui/explore/Filters.cpp +++ b/src/lms/ui/explore/Filters.cpp @@ -58,12 +58,10 @@ Filters::showDialog() if (!types.empty()) { - const auto values {types.front()->getClusters()}; - - for (const Database::Cluster::pointer& value : values) + for (const Database::Cluster::pointer& cluster : types.front()->getClusters()) { - if (_filterIds.find(value.id()) == _filterIds.end()) - valueCombo->addItem(Wt::WString::fromUTF8(value->getName())); + if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), cluster->getId()) == _clusterIds.end()) + valueCombo->addItem(Wt::WString::fromUTF8(cluster->getName())); } } } @@ -78,11 +76,10 @@ Filters::showDialog() auto clusterType = Database::ClusterType::getByName(LmsApp->getDbSession(), name); - const auto values = clusterType->getClusters(); - for (const Database::Cluster::pointer& value : values) + for (const Database::Cluster::pointer& cluster : clusterType->getClusters()) { - if (_filterIds.find(value.id()) == _filterIds.end()) - valueCombo->addItem(Wt::WString::fromUTF8(value->getName())); + if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), cluster->getId()) == _clusterIds.end()) + valueCombo->addItem(Wt::WString::fromUTF8(cluster->getName())); } }); @@ -101,7 +98,7 @@ Filters::showDialog() const std::string value {valueCombo->valueText().toUTF8()}; // TODO use a model to store the cluster.id() values - Database::IdType clusterId {}; + Database::ClusterId clusterId {}; { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; @@ -114,7 +111,7 @@ Filters::showDialog() if (!cluster) return; - clusterId = cluster.id(); + clusterId = cluster->getId(); } add(clusterId); @@ -124,7 +121,7 @@ Filters::showDialog() } void -Filters::add(Database::IdType clusterId) +Filters::add(Database::ClusterId clusterId) { Wt::WInteractWidget* filter {}; @@ -136,17 +133,18 @@ Filters::add(Database::IdType clusterId) if (!cluster) return; - auto res {_filterIds.insert(clusterId)}; - if (!res.second) + if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), clusterId) != std::cend(_clusterIds)) return; + _clusterIds.push_back(clusterId); + filter = _filters->addWidget(LmsApp->createCluster(cluster, true)); } filter->clicked().connect([=] { _filters->removeWidget(filter); - _filterIds.erase(clusterId); + _clusterIds.erase(std::remove_if(std::begin(_clusterIds), std::end(_clusterIds), [clusterId](Database::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 845f0305..079d52c2 100644 --- a/src/lms/ui/explore/Filters.hpp +++ b/src/lms/ui/explore/Filters.hpp @@ -19,6 +19,7 @@ #pragma once +#include #include #include #include @@ -34,9 +35,9 @@ class Filters : public Wt::WTemplate public: Filters(); - void add(Database::IdType clusterId); + void add(Database::ClusterId clusterId); - std::set getClusterIds() const { return _filterIds; } + const std::vector& getClusterIds() const { return _clusterIds; } Wt::Signal<>& updated() { return _sigUpdated; } @@ -46,7 +47,7 @@ class Filters : public Wt::WTemplate Wt::WContainerWidget *_filters; Wt::Signal<> _sigUpdated; - std::set _filterIds; + std::vector _clusterIds; }; } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index 552f2e66..473278df 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -77,7 +77,7 @@ namespace UserInterface return releases; } - std::vector + std::vector ReleaseCollector::getAll() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; @@ -85,9 +85,9 @@ namespace UserInterface bool moreResults; const auto releases {get(std::nullopt, moreResults)}; - std::vector res; + 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.id(); }); + std::transform(std::cbegin(releases), std::cend(releases), std::back_inserter(res), [](const Release::pointer& release) { return release->getId(); }); return res; } diff --git a/src/lms/ui/explore/ReleaseCollector.hpp b/src/lms/ui/explore/ReleaseCollector.hpp index 84b57fcb..3882cc80 100644 --- a/src/lms/ui/explore/ReleaseCollector.hpp +++ b/src/lms/ui/explore/ReleaseCollector.hpp @@ -35,13 +35,13 @@ namespace UserInterface public: using DatabaseCollectorBase::DatabaseCollectorBase; - std::vector> get(std::optional range, bool& moreResults); - std::vector getAll(); + std::vector> get(std::optional range, bool& moreResults); + std::vector getAll(); void reset() { _randomReleases.clear(); } private: - std::vector> getRandomReleases(std::optional range, bool& moreResults); - std::vector _randomReleases; + std::vector> getRandomReleases(std::optional range, bool& moreResults); + std::vector _randomReleases; }; } // ns UserInterface diff --git a/src/lms/ui/explore/ReleaseListHelpers.cpp b/src/lms/ui/explore/ReleaseListHelpers.cpp index eb1984cc..71c5daa5 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.cpp +++ b/src/lms/ui/explore/ReleaseListHelpers.cpp @@ -45,7 +45,7 @@ namespace UserInterface::ReleaseListHelpers Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false)); auto cover = std::make_unique(); - cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large)); + cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release->getId(), CoverResource::Size::Large)); cover->setStyleClass("Lms-cover"); cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); anchor->setImage(std::move(cover)); @@ -100,7 +100,7 @@ namespace UserInterface::ReleaseListHelpers } std::unique_ptr - createEntryForArtist(const Wt::Dbo::ptr& release, const Wt::Dbo::ptr& artist) + createEntryForArtist(const Database::Release::pointer& release, const Database::Artist::pointer& artist) { return createEntry(release, artist, true); } diff --git a/src/lms/ui/explore/ReleaseListHelpers.hpp b/src/lms/ui/explore/ReleaseListHelpers.hpp index 31a936e6..3aad6698 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.hpp +++ b/src/lms/ui/explore/ReleaseListHelpers.hpp @@ -22,6 +22,7 @@ #include #include +#include "database/Types.hpp" namespace Database { @@ -31,7 +32,7 @@ namespace Database namespace UserInterface::ReleaseListHelpers { - std::unique_ptr createEntry(const Wt::Dbo::ptr& release); - std::unique_ptr createEntryForArtist(const Wt::Dbo::ptr& release, const Wt::Dbo::ptr& artist); + std::unique_ptr createEntry(const Database::ObjectPtr& release); + std::unique_ptr createEntryForArtist(const Database::ObjectPtr& release, const Database::ObjectPtr& artist); } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleasePopup.cpp b/src/lms/ui/explore/ReleasePopup.cpp index 79617838..7224683f 100644 --- a/src/lms/ui/explore/ReleasePopup.cpp +++ b/src/lms/ui/explore/ReleasePopup.cpp @@ -32,8 +32,8 @@ namespace UserInterface void displayReleasePopupMenu(Wt::WInteractWidget& target, - Database::IdType releaseId, - PlayQueueActionSignal& releasesAction) + Database::ReleaseId releaseId, + PlayQueueActionReleaseSignal& releasesAction) { Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; diff --git a/src/lms/ui/explore/ReleasePopup.hpp b/src/lms/ui/explore/ReleasePopup.hpp index 23e576b2..5ffb4f48 100644 --- a/src/lms/ui/explore/ReleasePopup.hpp +++ b/src/lms/ui/explore/ReleasePopup.hpp @@ -28,7 +28,7 @@ namespace UserInterface { void displayReleasePopupMenu(Wt::WInteractWidget& target, - Database::IdType releaseId, - PlayQueueActionSignal& releasesAction); + Database::ReleaseId releaseId, + PlayQueueActionReleaseSignal& releasesAction); } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index cc6d0f23..ceb82d90 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -25,6 +25,7 @@ #include #include +#include "database/Cluster.hpp" #include "database/Release.hpp" #include "database/ScanSettings.hpp" #include "database/Session.hpp" @@ -68,7 +69,7 @@ Release::Release(Filters* filters) } static -std::optional +std::optional extractReleaseIdFromInternalPath() { if (wApp->internalPathMatches("/release/mbid/")) @@ -78,13 +79,13 @@ extractReleaseIdFromInternalPath() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; if (const Database::Release::pointer release {Database::Release::getByMBID(LmsApp->getDbSession(), *mbid)}) - return release.id(); + return release->getId(); } return std::nullopt; } - return StringUtils::readAs(wApp->internalPathNextPart("/release/")); + return StringUtils::readAs(wApp->internalPathNextPart("/release/")); } @@ -131,7 +132,7 @@ Release::refreshView() refreshReleaseArtists(release); { - Wt::WImage* cover {bindNew("cover", Wt::WLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large)))}; + Wt::WImage* cover {bindNew("cover", Wt::WLink(LmsApp->getCoverResource()->getReleaseUrl(release->getId(), CoverResource::Size::Large)))}; cover->setStyleClass("Lms-cover-large"); cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); } @@ -145,7 +146,7 @@ Release::refreshView() { for (const auto& cluster : clusters) { - auto clusterId {cluster.id()}; + const ClusterId clusterId {cluster->getId()}; auto entry {clusterContainers->addWidget(LmsApp->createCluster(cluster))}; entry->clicked().connect([=] { @@ -205,7 +206,7 @@ Release::refreshView() for (const auto& track : tracks) { - auto trackId {track.id()}; + auto trackId {track->getId()}; const auto discNumber {track->getDiscNumber()}; @@ -251,7 +252,7 @@ Release::refreshView() entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain); - LmsApp->getMediaPlayer().trackLoaded.connect(entry, [=] (Database::IdType loadedTrackId) + LmsApp->getMediaPlayer().trackLoaded.connect(entry, [=] (Database::TrackId loadedTrackId) { entry->bindString("is-playing", loadedTrackId == trackId ? "Lms-entry-playing" : ""); }); @@ -269,7 +270,7 @@ Release::refreshView() void Release::refreshReleaseArtists(const Database::Release::pointer& release) { - std::vector> artists; + std::vector> artists; artists = release->getReleaseArtists(); if (artists.empty()) @@ -339,7 +340,7 @@ Release::refreshLinks(const Database::Release::pointer& release) } void -Release::refreshSimilarReleases(const std::unordered_set& similarReleasesId) +Release::refreshSimilarReleases(const std::vector& similarReleasesId) { if (similarReleasesId.empty()) return; @@ -347,9 +348,9 @@ Release::refreshSimilarReleases(const std::unordered_set& simi setCondition("if-has-similar-releases", true); auto* similarReleasesContainer {bindNew("similar-releases")}; - for (Database::IdType id : similarReleasesId) + for (const Database::ReleaseId id : similarReleasesId) { - Database::Release::pointer similarRelease{Database::Release::getById(LmsApp->getDbSession(), id)}; + const Database::Release::pointer similarRelease{Database::Release::getById(LmsApp->getDbSession(), id)}; if (!similarRelease) continue; diff --git a/src/lms/ui/explore/ReleaseView.hpp b/src/lms/ui/explore/ReleaseView.hpp index 037d6d5c..d3700eaa 100644 --- a/src/lms/ui/explore/ReleaseView.hpp +++ b/src/lms/ui/explore/ReleaseView.hpp @@ -39,15 +39,15 @@ namespace UserInterface public: Release(Filters* filters); - PlayQueueActionSignal releasesAction; - PlayQueueActionSignal tracksAction; + PlayQueueActionReleaseSignal releasesAction; + PlayQueueActionTrackSignal tracksAction; private: void refreshView(); - void refreshReleaseArtists(const Wt::Dbo::ptr& release); - void refreshCopyright(const Wt::Dbo::ptr& release); - void refreshLinks(const Wt::Dbo::ptr& release); - void refreshSimilarReleases(const std::unordered_set& similarReleasesId); + void refreshReleaseArtists(const Database::ObjectPtr& release); + void refreshCopyright(const Database::ObjectPtr& release); + void refreshLinks(const Database::ObjectPtr& release); + void refreshSimilarReleases(const std::vector& similarReleasesId); Filters* _filters {}; }; diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index d2040366..93c56428 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -128,7 +128,7 @@ Releases::addSome() _container->setHasMore(moreResults); } -std::vector +std::vector Releases::getAllReleases() { return _releaseCollector.getAll(); diff --git a/src/lms/ui/explore/ReleasesView.hpp b/src/lms/ui/explore/ReleasesView.hpp index 31f1d17e..f2a9b91b 100644 --- a/src/lms/ui/explore/ReleasesView.hpp +++ b/src/lms/ui/explore/ReleasesView.hpp @@ -38,7 +38,7 @@ namespace UserInterface public: Releases(Filters& filters); - PlayQueueActionSignal releasesAction; + PlayQueueActionReleaseSignal releasesAction; private: @@ -46,7 +46,7 @@ namespace UserInterface void refreshView(ReleaseCollector::Mode mode); void addSome(); - std::vector getAllReleases(); + std::vector getAllReleases(); static constexpr std::size_t _maxItemsPerLine {6}; static constexpr std::size_t _batchSize {_maxItemsPerLine}; diff --git a/src/lms/ui/explore/SearchView.hpp b/src/lms/ui/explore/SearchView.hpp index e7a802b4..f75bfd37 100644 --- a/src/lms/ui/explore/SearchView.hpp +++ b/src/lms/ui/explore/SearchView.hpp @@ -44,7 +44,7 @@ namespace UserInterface public: SearchView(Filters* filters); - PlayQueueActionSignal tracksAction; + PlayQueueActionTrackSignal tracksAction; void refreshView(const Wt::WString& searchText); diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index ffcebf4c..26495626 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -77,17 +77,17 @@ namespace UserInterface return releases; } - std::vector + std::vector TrackCollector::getAll() { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; bool moreResults; - const auto releases {get(std::nullopt, moreResults)}; + const auto tracks {get(std::nullopt, moreResults)}; - std::vector res; - res.reserve(releases.size()); - std::transform(std::cbegin(releases), std::cend(releases), std::back_inserter(res), [](const Track::pointer& release) { return release.id(); }); + 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; } diff --git a/src/lms/ui/explore/TrackCollector.hpp b/src/lms/ui/explore/TrackCollector.hpp index 11a7c7f7..79011adf 100644 --- a/src/lms/ui/explore/TrackCollector.hpp +++ b/src/lms/ui/explore/TrackCollector.hpp @@ -35,13 +35,13 @@ namespace UserInterface public: using DatabaseCollectorBase::DatabaseCollectorBase; - std::vector> get(std::optional range, bool& moreResults); - std::vector getAll(); + std::vector> get(std::optional range, bool& moreResults); + std::vector getAll(); void reset() { _randomTracks.clear(); } private: - std::vector> getRandomTracks(std::optional range, bool& moreResults); - std::vector _randomTracks; + std::vector> getRandomTracks(std::optional range, bool& moreResults); + std::vector _randomTracks; }; } // ns UserInterface diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index 0fc280fb..ba36cb9d 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -39,7 +39,7 @@ using namespace Database; namespace UserInterface::TrackListHelpers { std::unique_ptr - createEntry(const Wt::Dbo::ptr& track, PlayQueueActionSignal& tracksAction) + createEntry(const Database::ObjectPtr& track, PlayQueueActionTrackSignal& tracksAction) { auto entry {std::make_unique(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))}; auto* entryPtr {entry.get()}; @@ -49,7 +49,7 @@ namespace UserInterface::TrackListHelpers const auto artists {track->getArtists({TrackArtistLinkType::Artist})}; const Release::pointer release {track->getRelease()}; - const IdType trackId {track.id()}; + const TrackId trackId {track->getId()}; if (!artists.empty() || release) entry->setCondition("if-has-artists-or-release", true); @@ -73,7 +73,7 @@ namespace UserInterface::TrackListHelpers { Wt::WAnchor* anchor {entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false))}; auto cover {std::make_unique()}; - cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release.id(), CoverResource::Size::Large)); + cover->setImageLink(LmsApp->getCoverResource()->getReleaseUrl(release->getId(), CoverResource::Size::Large)); cover->setStyleClass("Lms-cover"); cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); anchor->setImage(std::move(cover)); @@ -101,7 +101,7 @@ namespace UserInterface::TrackListHelpers displayTrackPopupMenu(*moreBtn, trackId, tracksAction); }); - LmsApp->getMediaPlayer().trackLoaded.connect(entryPtr, [=] (Database::IdType loadedTrackId) + LmsApp->getMediaPlayer().trackLoaded.connect(entryPtr, [=] (Database::TrackId loadedTrackId) { entryPtr->bindString("is-playing", loadedTrackId == trackId ? "Lms-entry-playing" : ""); }); diff --git a/src/lms/ui/explore/TrackListHelpers.hpp b/src/lms/ui/explore/TrackListHelpers.hpp index 32ac5b12..2e3ea306 100644 --- a/src/lms/ui/explore/TrackListHelpers.hpp +++ b/src/lms/ui/explore/TrackListHelpers.hpp @@ -22,6 +22,7 @@ #include #include +#include "database/Types.hpp" #include "PlayQueueAction.hpp" namespace Database @@ -31,6 +32,6 @@ namespace Database namespace UserInterface::TrackListHelpers { - std::unique_ptr createEntry(const Wt::Dbo::ptr& track, PlayQueueActionSignal& tracksAction); + std::unique_ptr createEntry(const Database::ObjectPtr& track, PlayQueueActionTrackSignal& tracksAction); } // namespace UserInterface diff --git a/src/lms/ui/explore/TrackPopup.cpp b/src/lms/ui/explore/TrackPopup.cpp index ff1bbd5f..8884347c 100644 --- a/src/lms/ui/explore/TrackPopup.cpp +++ b/src/lms/ui/explore/TrackPopup.cpp @@ -32,8 +32,8 @@ namespace UserInterface void displayTrackPopupMenu(Wt::WInteractWidget& target, - Database::IdType trackId, - PlayQueueActionSignal& tracksAction) + Database::TrackId trackId, + PlayQueueActionTrackSignal& tracksAction) { Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; diff --git a/src/lms/ui/explore/TrackPopup.hpp b/src/lms/ui/explore/TrackPopup.hpp index 66419e6b..1597d994 100644 --- a/src/lms/ui/explore/TrackPopup.hpp +++ b/src/lms/ui/explore/TrackPopup.hpp @@ -28,7 +28,7 @@ namespace UserInterface { void displayTrackPopupMenu(Wt::WInteractWidget& target, - Database::IdType releaseId, - PlayQueueActionSignal& releasesAction); + Database::TrackId trackId, + PlayQueueActionTrackSignal& tracksAction); } // namespace UserInterface diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index ba1b30d2..daec0fcd 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -132,7 +132,7 @@ Tracks::addSome() _container->setHasMore(moreResults); } -std::vector +std::vector Tracks::getAllTracks() { return _trackCollector.getAll(); diff --git a/src/lms/ui/explore/TracksView.hpp b/src/lms/ui/explore/TracksView.hpp index f81355ad..ac011e01 100644 --- a/src/lms/ui/explore/TracksView.hpp +++ b/src/lms/ui/explore/TracksView.hpp @@ -39,14 +39,14 @@ namespace UserInterface public: Tracks(Filters& filters); - PlayQueueActionSignal tracksAction; + PlayQueueActionTrackSignal tracksAction; private: void refreshView(); void refreshView(TrackCollector::Mode mode); void addSome(); - std::vector getAllTracks(); + std::vector getAllTracks(); static constexpr TrackCollector::Mode _defaultMode {TrackCollector::Mode::Random}; static constexpr std::size_t _batchSize {6}; diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index 2a2b5ed4..def318df 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -40,14 +40,14 @@ AudioFileResource:: ~AudioFileResource() } std::string -AudioFileResource::getUrl(Database::IdType trackId) const +AudioFileResource::getUrl(Database::TrackId trackId) const { - return url()+ "&trackid=" + std::to_string(trackId); + return url()+ "&trackid=" + trackId.toString(); } static std::optional -getTrackPathFromTrackId(Database::IdType trackId) +getTrackPathFromTrackId(Database::TrackId trackId) { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; @@ -72,7 +72,7 @@ getTrackPathFromURLArgs(const Wt::Http::Request& request) return std::nullopt; } - const auto trackId {StringUtils::readAs(*trackIdParameter)}; + const std::optional trackId {StringUtils::readAs(*trackIdParameter)}; if (!trackId) { LOG(ERROR) << "Bad trackid URL parameter!"; diff --git a/src/lms/ui/resource/AudioFileResource.hpp b/src/lms/ui/resource/AudioFileResource.hpp index 0865376b..fda116eb 100644 --- a/src/lms/ui/resource/AudioFileResource.hpp +++ b/src/lms/ui/resource/AudioFileResource.hpp @@ -31,7 +31,7 @@ class AudioFileResource : public Wt::WResource public: ~AudioFileResource(); - std::string getUrl(Database::IdType trackId) const; + std::string getUrl(Database::TrackId trackId) const; private: diff --git a/src/lms/ui/resource/AudioTranscodeResource.cpp b/src/lms/ui/resource/AudioTranscodeResource.cpp index ea374c0c..66ecac06 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.cpp +++ b/src/lms/ui/resource/AudioTranscodeResource.cpp @@ -96,9 +96,9 @@ AudioTranscodeResource:: ~AudioTranscodeResource() } std::string -AudioTranscodeResource::getUrl(Database::IdType trackId) const +AudioTranscodeResource::getUrl(Database::TrackId trackId) const { - return url() + "&trackid=" + std::to_string(trackId); + return url() + "&trackid=" + trackId.toString(); } template @@ -132,9 +132,9 @@ readTranscodeParameters(const Wt::Http::Request& request) TranscodeParameters parameters; // mandatory parameters - auto trackId {readParameterAs(request, "trackid")}; - auto format {readParameterAs(request, "format")}; - auto bitrate {readParameterAs(request, "bitrate")}; + const std::optional trackId {readParameterAs(request, "trackid")}; + const auto format {readParameterAs(request, "format")}; + const auto bitrate {readParameterAs(request, "bitrate")}; if (!trackId || !format || !bitrate) return std::nullopt; diff --git a/src/lms/ui/resource/AudioTranscodeResource.hpp b/src/lms/ui/resource/AudioTranscodeResource.hpp index a23e6602..ef516e91 100644 --- a/src/lms/ui/resource/AudioTranscodeResource.hpp +++ b/src/lms/ui/resource/AudioTranscodeResource.hpp @@ -37,13 +37,11 @@ class AudioTranscodeResource : public Wt::WResource ~AudioTranscodeResource(); // Url depends on the user since settings are used in parameters - std::string getUrl(Database::IdType trackId) const; + std::string getUrl(Database::TrackId trackId) const; - void handleRequest(const Wt::Http::Request& request, - Wt::Http::Response& response); + void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response); private: - static constexpr std::size_t _chunkSize {262144}; }; diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index 8adf85d8..a2978c6b 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -50,15 +50,15 @@ CoverResource::~CoverResource() } std::string -CoverResource::getReleaseUrl(Database::IdType releaseId, Size size) const +CoverResource::getReleaseUrl(Database::ReleaseId releaseId, Size size) const { - return url() + "&releaseid=" + std::to_string(releaseId) + "&size=" + std::to_string(static_cast(size)); + return url() + "&releaseid=" + releaseId.toString() + "&size=" + std::to_string(static_cast(size)); } std::string -CoverResource::getTrackUrl(Database::IdType trackId, Size size) const +CoverResource::getTrackUrl(Database::TrackId trackId, Size size) const { - return url() + "&trackid=" + std::to_string(trackId) + "&size=" + std::to_string(static_cast(size)); + return url() + "&trackid=" + trackId.toString() + "&size=" + std::to_string(static_cast(size)); } void @@ -89,7 +89,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons { LOG(DEBUG) << "Requested cover for track " << *trackIdStr << ", size = " << *size; - const auto trackId {StringUtils::readAs(*trackIdStr)}; + const std::optional trackId {StringUtils::readAs(*trackIdStr)}; if (!trackId) { LOG(DEBUG) << "track not found"; @@ -102,7 +102,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons { LOG(DEBUG) << "Requested cover for release " << *releaseIdStr << ", size = " << *size; - const auto releaseId {StringUtils::readAs(*releaseIdStr)}; + const std::optional releaseId {StringUtils::readAs(*releaseIdStr)}; if (!releaseId) return; diff --git a/src/lms/ui/resource/CoverResource.hpp b/src/lms/ui/resource/CoverResource.hpp index ff58976c..1e8cb8ad 100644 --- a/src/lms/ui/resource/CoverResource.hpp +++ b/src/lms/ui/resource/CoverResource.hpp @@ -39,8 +39,8 @@ namespace UserInterface Large = 512, }; - std::string getReleaseUrl(Database::IdType releaseId, Size size) const; - std::string getTrackUrl(Database::IdType trackId, Size size) const; + std::string getReleaseUrl(Database::ReleaseId releaseId, Size size) const; + std::string getTrackUrl(Database::TrackId trackId, Size size) const; private: void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; diff --git a/src/lms/ui/resource/DownloadResource.cpp b/src/lms/ui/resource/DownloadResource.cpp index ce3e7b2e..998096a5 100644 --- a/src/lms/ui/resource/DownloadResource.cpp +++ b/src/lms/ui/resource/DownloadResource.cpp @@ -97,7 +97,7 @@ getReleaseArtistPathName(Database::Release::pointer release) { std::string releaseArtistName; - std::vector> artists; + std::vector> artists; artists = release->getReleaseArtists(); if (artists.empty()) @@ -171,7 +171,7 @@ createZipper(const std::vector& tracks) return std::make_unique(files, Wt::WLocalDateTime::currentDateTime().toUTC()); } -DownloadArtistResource::DownloadArtistResource(Database::IdType artistId) +DownloadArtistResource::DownloadArtistResource(Database::ArtistId artistId) : _artistId {artistId} { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; @@ -196,7 +196,7 @@ DownloadArtistResource::createZipper() return UserInterface::createZipper(artist->getTracks()); } -DownloadReleaseResource::DownloadReleaseResource(Database::IdType releaseId) +DownloadReleaseResource::DownloadReleaseResource(Database::ReleaseId releaseId) : _releaseId {releaseId} { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; @@ -222,7 +222,7 @@ DownloadReleaseResource::createZipper() return UserInterface::createZipper(release->getTracks()); } -DownloadTrackResource::DownloadTrackResource(Database::IdType trackId) +DownloadTrackResource::DownloadTrackResource(Database::TrackId trackId) : _trackId {trackId} { auto transaction {LmsApp->getDbSession().createSharedTransaction()}; diff --git a/src/lms/ui/resource/DownloadResource.hpp b/src/lms/ui/resource/DownloadResource.hpp index adc6ce76..581a55ec 100644 --- a/src/lms/ui/resource/DownloadResource.hpp +++ b/src/lms/ui/resource/DownloadResource.hpp @@ -43,31 +43,31 @@ class DownloadResource : public Wt::WResource class DownloadArtistResource : public DownloadResource { public: - DownloadArtistResource(Database::IdType artistId); + DownloadArtistResource(Database::ArtistId artistId); private: std::unique_ptr createZipper() override; - Database::IdType _artistId; + Database::ArtistId _artistId; }; class DownloadReleaseResource : public DownloadResource { public: - DownloadReleaseResource(Database::IdType releaseId); + DownloadReleaseResource(Database::ReleaseId releaseId); private: std::unique_ptr createZipper() override; - Database::IdType _releaseId; + Database::ReleaseId _releaseId; }; class DownloadTrackResource : public DownloadResource { public: - DownloadTrackResource(Database::IdType trackId); + DownloadTrackResource(Database::TrackId trackId); private: std::unique_ptr createZipper() override; - Database::IdType _trackId; + Database::TrackId _trackId; }; } // namespace UserInterface diff --git a/src/test/database/Artist.cpp b/src/test/database/Artist.cpp index efb43d68..7193b164 100644 --- a/src/test/database/Artist.cpp +++ b/src/test/database/Artist.cpp @@ -25,16 +25,24 @@ TEST_F(DatabaseFixture, SingleArtist) { ScopedArtist artist {session, "MyArtist"}; + { + auto transaction {session.createSharedTransaction()}; + + EXPECT_TRUE(artist.get()); + EXPECT_FALSE(!artist.get()); + EXPECT_EQ(artist.get()->getId(), artist.getId()); + } + { auto transaction {session.createSharedTransaction()}; auto artists {Artist::getAll(session, Artist::SortMethod::ByName)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); artists = Artist::getAllOrphans(session); ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); } } @@ -59,14 +67,14 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtist) auto artists {track->getArtists({TrackArtistLinkType::Artist})}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); EXPECT_EQ(artist->getReleaseCount(), 0); ASSERT_EQ(track->getArtistLinks().size(), 1); auto artistLink {track->getArtistLinks().front()}; - EXPECT_EQ(artistLink->getTrack().id(), track.getId()); - EXPECT_EQ(artistLink->getArtist().id(), artist.getId()); + EXPECT_EQ(artistLink->getTrack()->getId(), track.getId()); + EXPECT_EQ(artistLink->getArtist()->getId(), artist.getId()); ASSERT_EQ(track->getArtists({TrackArtistLinkType::Artist}).size(), 1); EXPECT_TRUE(track->getArtists({TrackArtistLinkType::ReleaseArtist}).empty()); @@ -78,7 +86,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtist) auto tracks {artist->getTracks()}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track.getId()); + EXPECT_EQ(tracks.front()->getId(), track.getId()); EXPECT_TRUE(artist->getTracks(TrackArtistLinkType::ReleaseArtist).empty()); EXPECT_EQ(artist->getTracks(TrackArtistLinkType::Artist).size(), 1); @@ -117,11 +125,11 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRoles) auto artists {track->getArtists({TrackArtistLinkType::Artist})}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); artists = track->getArtists({TrackArtistLinkType::ReleaseArtist}); ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); EXPECT_EQ(track->getArtistLinks().size(), 3); @@ -156,8 +164,8 @@ TEST_F(DatabaseFixture,SingleTrackMultiArtists) auto artists {track->getArtists({TrackArtistLinkType::Artist})}; ASSERT_EQ(artists.size(), 2); - EXPECT_TRUE((artists[0].id() == artist1.getId() && artists[1].id() == artist2.getId()) - || (artists[0].id() == artist2.getId() && artists[1].id() == artist1.getId())); + EXPECT_TRUE((artists[0]->getId() == artist1.getId() && artists[1]->getId() == artist2.getId()) + || (artists[0]->getId() == artist2.getId() && artists[1]->getId() == artist1.getId())); EXPECT_EQ(track->getArtists({}).size(), 2); EXPECT_EQ(track->getArtists({TrackArtistLinkType::Artist}).size(), 2); @@ -198,11 +206,11 @@ TEST_F(DatabaseFixture, SingleArtistSearchByName) 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().id(), artist.getId()); + EXPECT_EQ(artistsByAAA.front()->getId(), 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().id(), artist.getId()); + EXPECT_EQ(artistsByZZZ.front()->getId(), artist.getId()); EXPECT_TRUE(Artist::getByName(session, "NNN").empty()); } @@ -223,19 +231,19 @@ TEST_F(DatabaseFixture, MultipleArtistsSearchByNameEscaped) { const auto artists {Artist::getByName(session, "MyArtist%")}; ASSERT_TRUE(artists.size() == 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); EXPECT_TRUE(Artist::getByName(session, "MyArtistFoo").empty()); } { const auto artists {Artist::getByName(session, "%MyArtist")}; ASSERT_TRUE(artists.size() == 1); - EXPECT_EQ(artists.front().id(), artist2.getId()); + EXPECT_EQ(artists.front()->getId(), artist2.getId()); EXPECT_TRUE(Artist::getByName(session, "FooMyArtist").empty()); } { const auto artists {Artist::getByName(session, "%_MyArtist")}; ASSERT_TRUE(artists.size() == 1); - ASSERT_EQ(artists.front().id(), artist3.getId()); + ASSERT_EQ(artists.front()->getId(), artist3.getId()); EXPECT_TRUE(Artist::getByName(session, "%CMyArtist").empty()); } } @@ -265,21 +273,21 @@ TEST_F(DatabaseFixture, MultipleArtistsSearchByNameEscaped) { const auto artists {Artist::getByFilter(session, {}, {"MyArtist%"}, std::nullopt, Artist::SortMethod::ByName, std::nullopt, more)}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist1.getId()); - EXPECT_EQ(artists[1].id(), artist4.getId()); + EXPECT_EQ(artists[0]->getId(), artist1.getId()); + EXPECT_EQ(artists[1]->getId(), 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].id(), artist2.getId()); - EXPECT_EQ(artists[1].id(), artist5.getId()); + EXPECT_EQ(artists[0]->getId(), artist2.getId()); + EXPECT_EQ(artists[1]->getId(), 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].id(), artist3.getId()); + EXPECT_EQ(artists[0]->getId(), artist3.getId()); } } } @@ -303,12 +311,12 @@ TEST_F(DatabaseFixture, MultiArtistsSortMethod) auto allArtistsBySortName {Artist::getAll(session, Artist::SortMethod::BySortName)}; ASSERT_EQ(allArtistsByName.size(), 2); - EXPECT_EQ(allArtistsByName.front().id(), artistA.getId()); - EXPECT_EQ(allArtistsByName.back().id(), artistB.getId()); + EXPECT_EQ(allArtistsByName.front()->getId(), artistA.getId()); + EXPECT_EQ(allArtistsByName.back()->getId(), artistB.getId()); ASSERT_EQ(allArtistsBySortName.size(), 2); - EXPECT_EQ(allArtistsBySortName.front().id(), artistB.getId()); - EXPECT_EQ(allArtistsBySortName.back().id(), artistA.getId()); + EXPECT_EQ(allArtistsBySortName.front()->getId(), artistB.getId()); + EXPECT_EQ(allArtistsBySortName.back()->getId(), artistA.getId()); } } @@ -321,7 +329,7 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) { auto transaction {session.createSharedTransaction()}; - EXPECT_EQ(artist->hasNonReleaseTracks(std::nullopt), false); + EXPECT_FALSE(artist->hasNonReleaseTracks(std::nullopt)); bool moreResults; const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )}; @@ -343,9 +351,9 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) bool moreResults; const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )}; - EXPECT_EQ(artist->hasNonReleaseTracks(std::nullopt), true); - EXPECT_EQ(moreResults, false); + EXPECT_TRUE(artist->hasNonReleaseTracks(std::nullopt)); + EXPECT_FALSE(moreResults); ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track2.getId()); + EXPECT_EQ(tracks.front()->getId(), track2.getId()); } } diff --git a/src/test/database/Cluster.cpp b/src/test/database/Cluster.cpp index 9135b48c..aba28a41 100644 --- a/src/test/database/Cluster.cpp +++ b/src/test/database/Cluster.cpp @@ -36,20 +36,20 @@ TEST_F(DatabaseFixture, SingleCluster) auto clusters {Cluster::getAll(session)}; ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front().id(), cluster.getId()); - EXPECT_EQ(clusters.front()->getType().id(), clusterType.getId()); + EXPECT_EQ(clusters.front()->getId(), cluster.getId()); + EXPECT_EQ(clusters.front()->getType()->getId(), clusterType.getId()); clusters = Cluster::getAllOrphans(session); ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front().id(), cluster.getId()); + EXPECT_EQ(clusters.front()->getId(), cluster.getId()); auto clusterTypes {ClusterType::getAll(session)}; ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front().id(), clusterType.getId()); + EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); clusterTypes = ClusterType::getAllUsed(session); ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front().id(), clusterType.getId()); + EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); clusterTypes = ClusterType::getAllOrphans(session); EXPECT_TRUE(clusterTypes.empty()); @@ -61,7 +61,7 @@ TEST_F(DatabaseFixture, SingleCluster) auto clusterTypes {ClusterType::getAllOrphans(session)}; ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front().id(), clusterType.getId()); + EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); ASSERT_TRUE(ClusterType::getAllUsed(session).empty()); } @@ -77,7 +77,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleCluster) EXPECT_TRUE(Cluster::getAllOrphans(session).empty()); auto clusterTypes {ClusterType::getAllOrphans(session)}; ASSERT_EQ(clusterTypes.size(), 1); - EXPECT_EQ(clusterTypes.front().id(), clusterType.getId()); + EXPECT_EQ(clusterTypes.front()->getId(), clusterType.getId()); } ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; @@ -113,7 +113,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleCluster) auto transaction {session.createSharedTransaction()}; auto clusters {Cluster::getAllOrphans(session)}; ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front().id(), cluster2.getId()); + EXPECT_EQ(clusters.front()->getId(), cluster2.getId()); EXPECT_TRUE(ClusterType::getAllOrphans(session).empty()); } @@ -123,7 +123,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleCluster) auto tracks {Track::getByClusters(session, {cluster1.getId()})}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track.getId()); + EXPECT_EQ(tracks.front()->getId(), track.getId()); tracks = Track::getByClusters(session, {cluster2.getId()}); EXPECT_TRUE(tracks.empty()); @@ -134,7 +134,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleCluster) auto clusters {track->getClusters()}; ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front().id(), cluster1.getId()); + EXPECT_EQ(clusters.front()->getId(), cluster1.getId()); auto clusterIds {track->getClusterIds()}; ASSERT_EQ(clusterIds.size(), 1); @@ -166,7 +166,7 @@ TEST_F(DatabaseFixture, MultipleTracksSingleCluster) for (auto trackCluster : cluster->getTracks()) { - auto it {std::find_if(std::cbegin(tracks), std::cend(tracks), [&](const ScopedTrack& track) { return trackCluster.id() == track.getId(); })}; + auto it {std::find_if(std::cbegin(tracks), std::cend(tracks), [&](const ScopedTrack& track) { return trackCluster->getId() == track.getId(); })}; EXPECT_TRUE(it != std::cend(tracks)); } } @@ -221,8 +221,8 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersTopRelease) bool hasMore; const auto releases{trackList->getTopReleases({}, std::nullopt, hasMore)}; ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0].id(), releaseB.getId()); - EXPECT_EQ(releases[1].id(), releaseA.getId()); + EXPECT_EQ(releases[0]->getId(), releaseB.getId()); + EXPECT_EQ(releases[1]->getId(), releaseA.getId()); } { @@ -231,11 +231,11 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersTopRelease) bool hasMore; auto releases{trackList->getTopReleases({cluster1.getId()}, std::nullopt, hasMore)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0].id(), releaseA.getId()); + EXPECT_EQ(releases[0]->getId(), releaseA.getId()); releases = trackList->getTopReleases({cluster2.getId()}, std::nullopt, hasMore); ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0].id(), releaseB.getId()); + EXPECT_EQ(releases[0]->getId(), releaseB.getId()); releases = trackList->getTopReleases({cluster2.getId(), cluster1.getId()}, std::nullopt, hasMore); EXPECT_TRUE(releases.empty()); @@ -259,12 +259,12 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersTopRelease) bool hasMore; auto releases {trackList->getTopReleases({cluster2.getId(), cluster3.getId()}, std::nullopt, hasMore)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0].id(), releaseC.getId()); + EXPECT_EQ(releases[0]->getId(), releaseC.getId()); releases = trackList->getTopReleases({cluster2.getId()}, std::nullopt, hasMore); ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0].id(), releaseC.getId()); - EXPECT_EQ(releases[1].id(), releaseB.getId()); + EXPECT_EQ(releases[0]->getId(), releaseC.getId()); + EXPECT_EQ(releases[1]->getId(), releaseB.getId()); } } @@ -306,7 +306,7 @@ TEST_F(DatabaseFixture,SingleTrackSingleReleaseSingleCluster) auto releases {Release::getByClusters(session, {cluster.getId()})}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); } { @@ -351,7 +351,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) auto artists {Artist::getByClusters(session, {cluster1.getId()}, Artist::SortMethod::ByName)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); EXPECT_TRUE(Artist::getByClusters(session, {cluster2.getId()}, Artist::SortMethod::ByName).empty()); EXPECT_TRUE(Artist::getByClusters(session, {cluster3.getId()}, Artist::SortMethod::ByName).empty()); @@ -364,15 +364,15 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) auto artists {Artist::getByClusters(session, {cluster1.getId()}, Artist::SortMethod::ByName)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); artists = Artist::getByClusters(session, {cluster2.getId()}, Artist::SortMethod::ByName); ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); artists = Artist::getByClusters(session, {cluster1.getId(), cluster2.getId()}, Artist::SortMethod::ByName); ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); EXPECT_TRUE(Artist::getByClusters(session, {cluster3.getId()}, Artist::SortMethod::ByName).empty()); } @@ -405,7 +405,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) auto artists {Artist::getByClusters(session, {cluster.getId()}, Artist::SortMethod::ByName)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); } } @@ -442,12 +442,12 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) { auto transaction {session.createSharedTransaction()}; - std::set clusterIds; - std::transform(std::cbegin(clusters), std::cend(clusters), std::inserter(clusterIds, std::begin(clusterIds)), [](const ScopedCluster& cluster) { return cluster.getId(); }); + 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().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); } } @@ -478,7 +478,7 @@ TEST_F(DatabaseFixture, MultipleTracksSingleClusterSimilarity) EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const ScopedTrack& track) { - return similarTrack.id() == track.getId(); + return similarTrack->getId() == track.getId(); }) != std::cend(tracks)); } } @@ -519,14 +519,14 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleClustersSimilarity) 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.id() == track.getId(); }) != std::cend(tracks)); + 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::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.id() == track.getId(); }) != std::cend(tracks)); + 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)); } } } @@ -573,15 +573,15 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) auto artists {Artist::getByClusters(session, {cluster.getId()}, Artist::SortMethod::ByName)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); auto releases {artist->getReleases()}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); releases = artist->getReleases({cluster.getId()}); ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); } } @@ -608,11 +608,11 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters) auto releases {artist->getReleases()}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); releases = artist->getReleases({cluster1.getId(), cluster2.getId()}); ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); } } @@ -644,7 +644,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackSingleCluster) EXPECT_EQ(similarTracks.size(), 5); for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::cend(tracks), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack.id(); })); + EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::cend(tracks), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); } } @@ -685,7 +685,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters) ASSERT_EQ(similarTracks.size(), 5); for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::next(std::cbegin(tracks), 10), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack.id(); })); + EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::next(std::cbegin(tracks), 10), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); } { @@ -693,7 +693,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters) ASSERT_EQ(similarTracks.size(), 5); for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 10), std::next(std::cbegin(tracks), 15), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack.id(); })); + EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 10), std::next(std::cbegin(tracks), 15), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); } EXPECT_TRUE(trackList->getSimilarTracks(10, 10).empty()); @@ -752,15 +752,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size() , 1); - EXPECT_EQ(releases.front().id(), release1.getId()); + EXPECT_EQ(releases.front()->getId(), release1.getId()); const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track1.getId()); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); } { @@ -769,15 +769,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); const auto releases {trackList->getReleasesReverse({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release1.getId()); + EXPECT_EQ(releases.front()->getId(), release1.getId()); const auto tracks {trackList->getTracksReverse({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track1.getId()); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); } { @@ -786,15 +786,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); const auto releases {trackList->getReleasesReverse({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release1.getId()); + EXPECT_EQ(releases.front()->getId(), release1.getId()); const auto tracks {trackList->getTracksReverse({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track1.getId()); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); } { @@ -803,7 +803,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({cluster1.getId()}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); } { @@ -812,7 +812,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); } { @@ -838,18 +838,18 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist2.getId()); - EXPECT_EQ(artists[1].id(), artist1.getId()); + 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].id(), release2.getId()); - EXPECT_EQ(releases[1].id(), release1.getId()); + 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].id(), track2.getId()); - EXPECT_EQ(tracks[1].id(),track1.getId()); + EXPECT_EQ(tracks[0]->getId(), track2.getId()); + EXPECT_EQ(tracks[1]->getId(),track1.getId()); } { @@ -858,18 +858,18 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist2.getId()); - EXPECT_EQ(artists[1].id(), artist1.getId()); + EXPECT_EQ(artists[0]->getId(), artist2.getId()); + EXPECT_EQ(artists[1]->getId(), artist1.getId()); const auto releases {trackList->getReleasesReverse({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0].id(), release2.getId()); - EXPECT_EQ(releases[1].id(), release1.getId()); + EXPECT_EQ(releases[0]->getId(), release2.getId()); + EXPECT_EQ(releases[1]->getId(), release1.getId()); const auto tracks {trackList->getTracksReverse({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0].id(), track2.getId()); - EXPECT_EQ(tracks[1].id(), track1.getId()); + EXPECT_EQ(tracks[0]->getId(), track2.getId()); + EXPECT_EQ(tracks[1]->getId(), track1.getId()); } { @@ -878,15 +878,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); const auto releases {trackList->getReleasesReverse({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release1.getId()); + EXPECT_EQ(releases.front()->getId(), release1.getId()); const auto tracks {trackList->getTracksReverse({cluster1.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track1.getId()); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); } { @@ -895,15 +895,15 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({cluster2.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist2.getId()); + EXPECT_EQ(artists.front()->getId(), artist2.getId()); const auto releases {trackList->getReleasesReverse({cluster2.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release2.getId()); + EXPECT_EQ(releases.front()->getId(), release2.getId()); const auto tracks {trackList->getTracksReverse({cluster2.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track2.getId()); + EXPECT_EQ(tracks.front()->getId(), track2.getId()); } { @@ -918,18 +918,18 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist1.getId()); - EXPECT_EQ(artists[1].id(), artist2.getId()); + 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].id(), release1.getId()); - EXPECT_EQ(releases[1].id(), release2.getId()); + 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].id(), track1.getId()); - EXPECT_EQ(tracks[1].id(), track2.getId()); + EXPECT_EQ(tracks[0]->getId(), track1.getId()); + EXPECT_EQ(tracks[1]->getId(), track2.getId()); } { @@ -938,18 +938,18 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist1.getId()); - EXPECT_EQ(artists[1].id(), artist2.getId()); + EXPECT_EQ(artists[0]->getId(), artist1.getId()); + EXPECT_EQ(artists[1]->getId(), artist2.getId()); const auto releases {trackList->getReleasesReverse({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0].id(), release1.getId()); - EXPECT_EQ(releases[1].id(), release2.getId()); + EXPECT_EQ(releases[0]->getId(), release1.getId()); + EXPECT_EQ(releases[1]->getId(), release2.getId()); const auto tracks {trackList->getTracksReverse({cluster3.getId()}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0].id(), track1.getId()); - EXPECT_EQ(tracks[1].id(), track2.getId()); + EXPECT_EQ(tracks[0]->getId(), track1.getId()); + EXPECT_EQ(tracks[1]->getId(), track2.getId()); } } @@ -1000,13 +1000,13 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { auto artists {artist1->getSimilarArtists()}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist2.getId()); + EXPECT_EQ(artists.front()->getId(), artist2.getId()); } { auto artists {artist1->getSimilarArtists({TrackArtistLinkType::Artist})}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist2.getId()); + EXPECT_EQ(artists.front()->getId(), artist2.getId()); } { @@ -1017,7 +1017,7 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { auto artists {artist1->getSimilarArtists({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist})}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist2.getId()); + EXPECT_EQ(artists.front()->getId(), artist2.getId()); } { @@ -1028,8 +1028,8 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { auto artists {artist2->getSimilarArtists()}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist1.getId()); - EXPECT_EQ(artists[1].id(), artist3.getId()); + EXPECT_EQ(artists[0]->getId(), artist1.getId()); + EXPECT_EQ(artists[1]->getId(), artist3.getId()); } } } @@ -1081,14 +1081,14 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleReleasesMultiClusters) { auto releases {release1->getSimilarReleases()}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release2.getId()); + EXPECT_EQ(releases.front()->getId(), release2.getId()); } { auto releases {release2->getSimilarReleases()}; ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0].id(), release1.getId()); - EXPECT_EQ(releases[1].id(), release3.getId()); + EXPECT_EQ(releases[0]->getId(), release1.getId()); + EXPECT_EQ(releases[1]->getId(), release3.getId()); } } } diff --git a/src/test/database/Common.hpp b/src/test/database/Common.hpp index 056ca415..8864e681 100644 --- a/src/test/database/Common.hpp +++ b/src/test/database/Common.hpp @@ -40,6 +40,8 @@ template class ScopedEntity { public: + using IdType = typename T::IdType; + template ScopedEntity(Database::Session& session, Args&& ...args) : _session {session} @@ -48,7 +50,7 @@ class ScopedEntity auto entity {T::create(_session, std::forward(args)...)}; EXPECT_TRUE(entity); - _id = entity.id(); + _id = entity->getId(); } ~ScopedEntity() @@ -84,11 +86,11 @@ class ScopedEntity return get(); } - Database::IdType getId() const { return _id; } + IdType getId() const { return _id; } private: Database::Session& _session; - Database::IdType _id {}; + IdType _id {}; }; using ScopedArtist = ScopedEntity; diff --git a/src/test/database/DatabaseTest.cpp b/src/test/database/DatabaseTest.cpp index c5d2ad4b..c62df827 100644 --- a/src/test/database/DatabaseTest.cpp +++ b/src/test/database/DatabaseTest.cpp @@ -32,11 +32,11 @@ TEST_F(DatabaseFixture, SingleRelease) auto releases {Release::getAllOrphans(session)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); releases = Release::getAll(session); ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); EXPECT_EQ(release->getDuration(), std::chrono::seconds {0}); } } @@ -61,21 +61,21 @@ TEST_F(DatabaseFixture, SingleTrackSingleRelease) EXPECT_EQ(release->getTracksCount(), 1); ASSERT_EQ(release->getTracks().size(), 1); - EXPECT_EQ(release->getTracks().front().id(), track.getId()); + EXPECT_EQ(release->getTracks().front()->getId(), track.getId()); } { auto transaction {session.createUniqueTransaction()}; ASSERT_TRUE(track->getRelease()); - EXPECT_EQ(track->getRelease().id(), release.getId()); + EXPECT_EQ(track->getRelease()->getId(), release.getId()); } { auto transaction {session.createUniqueTransaction()}; auto tracks {Track::getByNameAndReleaseName(session, "MyTrackName", "MyReleaseName")}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track.getId()); + EXPECT_EQ(tracks.front()->getId(), track.getId()); } { auto transaction {session.createUniqueTransaction()}; @@ -96,7 +96,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleRelease) auto releases {Release::getAllOrphans(session)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); } } @@ -140,29 +140,29 @@ TEST_F(DatabaseFixture, MulitpleReleaseSearchByName) { 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.id() == release6.getId(); })); + EXPECT_TRUE(std::none_of(std::cbegin(releases), std::cend(releases), [&](const Release::pointer& release) { return release->getId() == release6.getId(); })); } { const auto releases {Release::getByFilter(session, {}, {"MyRelease%"}, std::nullopt, more)}; - EXPECT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0].id(), release2.getId()); - EXPECT_EQ(releases[1].id(), release4.getId()); + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release2.getId()); + EXPECT_EQ(releases[1]->getId(), release4.getId()); } { const auto releases {Release::getByFilter(session, {}, {"%MyRelease"}, std::nullopt, more)}; - EXPECT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0].id(), release3.getId()); - EXPECT_EQ(releases[1].id(), release5.getId()); + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release3.getId()); + EXPECT_EQ(releases[1]->getId(), release5.getId()); } { const auto releases {Release::getByFilter(session, {}, {"Foo%MyRelease"}, std::nullopt, more)}; - EXPECT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0].id(), release5.getId()); + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases[0]->getId(), release5.getId()); } { const auto releases {Release::getByFilter(session, {}, {"MyRelease%Foo"}, std::nullopt, more)}; - EXPECT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0].id(), release4.getId()); + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases[0]->getId(), release4.getId()); } } } @@ -296,8 +296,8 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseFirstTrack) EXPECT_TRUE(release1->getFirstTrack()); EXPECT_TRUE(release2->getFirstTrack()); - EXPECT_EQ(release1->getFirstTrack().id(), track1A.getId()); - EXPECT_EQ(release2->getFirstTrack().id(), track2B.getId()); + EXPECT_EQ(release1->getFirstTrack()->getId(), track1A.getId()); + EXPECT_EQ(release2->getFirstTrack()->getId(), track2B.getId()); } } @@ -329,7 +329,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease) EXPECT_EQ(artist->getReleaseCount(), 1); ASSERT_EQ(artist->getReleases().size(), 1); - EXPECT_EQ(artist->getReleases().front().id(), release.getId()); + EXPECT_EQ(artist->getReleases().front()->getId(), release.getId()); EXPECT_EQ(release->getTracks().size(), nbTracks); } @@ -353,13 +353,13 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist) auto releases {artist->getReleases()}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); EXPECT_EQ(artist->getReleaseCount(), 1); auto artists {release->getArtists()}; ASSERT_EQ(artists.size(), 1); - ASSERT_EQ(artists.front().id(), artist.getId()); + ASSERT_EQ(artists.front()->getId(), artist.getId()); } } @@ -409,7 +409,7 @@ TEST_F(DatabaseFixture, SingleStarredArtist) 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().id(), artist.getId()); + EXPECT_EQ(artists.front()->getId(), artist.getId()); EXPECT_FALSE(hasMore); } } @@ -441,7 +441,7 @@ TEST_F(DatabaseFixture, SingleStarredRelease) bool hasMore {}; auto releases {Release::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release.getId()); + EXPECT_EQ(releases.front()->getId(), release.getId()); EXPECT_FALSE(hasMore); } } @@ -471,7 +471,7 @@ TEST_F(DatabaseFixture, SingleStarredTrack) bool hasMore {}; auto tracks {Track::getStarred(session, user.get(), {}, std::nullopt, hasMore)}; ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front().id(), track.getId()); + EXPECT_EQ(tracks.front()->getId(), track.getId()); EXPECT_FALSE(hasMore); } } @@ -486,7 +486,7 @@ TEST_F(DatabaseFixture, SingleTrackList) auto trackLists {TrackList::getAll(session, user.get(), TrackList::Type::Playlist)}; ASSERT_EQ(trackLists.size(), 1); - EXPECT_EQ(trackLists.front().id(), trackList.getId()); + EXPECT_EQ(trackLists.front()->getId(), trackList.getId()); } } @@ -536,8 +536,8 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackDateTime) bool moreResults; const auto tracks {trackList.get()->getTracksReverse({}, std::nullopt, moreResults)}; ASSERT_EQ(tracks.size(), 3); - EXPECT_EQ(tracks.front().id(), track3.getId()); - EXPECT_EQ(tracks.back().id(), track2.getId()); + EXPECT_EQ(tracks.front()->getId(), track3.getId()); + EXPECT_EQ(tracks.back()->getId(), track2.getId()); } } @@ -584,11 +584,11 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front().id(), artist1.getId()); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); const auto releases {trackList->getReleasesReverse({}, std::nullopt, moreResults)}; ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front().id(), release1.getId()); + EXPECT_EQ(releases.front()->getId(), release1.getId()); const auto tracks {trackList->getTracksReverse({}, std::nullopt, moreResults)}; EXPECT_EQ(tracks.size(), 1); @@ -606,18 +606,18 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist2.getId()); - EXPECT_EQ(artists[1].id(), artist1.getId()); + 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].id(), release2.getId()); - EXPECT_EQ(releases[1].id(), release1.getId()); + 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].id(), track2.getId()); - EXPECT_EQ(tracks[1].id(), track1.getId()); + EXPECT_EQ(tracks[0]->getId(), track2.getId()); + EXPECT_EQ(tracks[1]->getId(), track1.getId()); } { @@ -632,18 +632,18 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed) bool moreResults {}; const auto artists {trackList->getArtistsReverse({}, std::nullopt, std::nullopt, moreResults)}; ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0].id(), artist1.getId()); - EXPECT_EQ(artists[1].id(), artist2.getId()); + 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].id(), release1.getId()); - EXPECT_EQ(releases[1].id(), release2.getId()); + 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].id(), track1.getId()); - EXPECT_EQ(tracks[1].id(), track2.getId()); + EXPECT_EQ(tracks[0]->getId(), track1.getId()); + EXPECT_EQ(tracks[1]->getId(), track2.getId()); } } diff --git a/src/test/database/Track.cpp b/src/test/database/Track.cpp index b5de2e56..cb1759a9 100644 --- a/src/test/database/Track.cpp +++ b/src/test/database/Track.cpp @@ -70,19 +70,19 @@ TEST_F(DatabaseFixture, MultipleTracksSearchByFilter) { 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.id() == track6.getId(); })); + EXPECT_TRUE(std::none_of(std::cbegin(tracks), std::cend(tracks), [&](const Track::pointer& track) { return track->getId() == track6.getId(); })); } { const auto tracks {Track::getByFilter(session, {}, {"MyTrack%"}, std::nullopt, more)}; ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0].id(), track2.getId()); - EXPECT_EQ(tracks[1].id(), track3.getId()); + EXPECT_EQ(tracks[0]->getId(), track2.getId()); + EXPECT_EQ(tracks[1]->getId(), track3.getId()); } { const auto tracks {Track::getByFilter(session, {}, {"%MyTrack"}, std::nullopt, more)}; ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0].id(), track4.getId()); - EXPECT_EQ(tracks[1].id(), track5.getId()); + EXPECT_EQ(tracks[0]->getId(), track4.getId()); + EXPECT_EQ(tracks[1]->getId(), track5.getId()); } } } diff --git a/src/test/som/SomTest.cpp b/src/test/som/SomTest.cpp index c2ef65db..74186d85 100644 --- a/src/test/som/SomTest.cpp +++ b/src/test/som/SomTest.cpp @@ -17,8 +17,8 @@ * along with LMS. If not, see . */ +#include #include - #include "som/DataNormalizer.hpp" #include "som/Network.hpp" diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index 3a1eb30a..6a9e2767 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -38,15 +38,15 @@ static void dumpTrackCovers(Database::Session& session, CoverArt::ImageSize width) { - std::vector trackIds; + std::vector trackIds; { auto transaction {session.createSharedTransaction()}; trackIds = Database::Track::getAllIds(session); } - for (Database::IdType trackId : trackIds) + for (const Database::TrackId trackId : trackIds) { - std::cout << "Getting cover for track id " << trackId << std::endl; + std::cout << "Getting cover for track id " << trackId.toString() << std::endl; Service::get()->getFromTrack(session, trackId, width); } } diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index bea308e8..0a1f4a3c 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -39,16 +39,16 @@ static void dumpTracksRecommendation(Database::Session session, Recommendation::IEngine& engine, unsigned maxSimilarityCount) { - const std::vector trackIds {[&]() + const std::vector trackIds {[&]() { auto transaction {session.createSharedTransaction()}; return Database::Track::getAllIds(session); }()}; std::cout << "*** Tracks (" << trackIds.size() << ") ***" << std::endl; - for (Database::IdType trackId : trackIds) + for (Database::TrackId trackId : trackIds) { - auto trackToString = [&](Database::IdType trackId) + auto trackToString = [&](Database::TrackId trackId) { std::string res; auto transaction {session.createSharedTransaction()}; @@ -66,7 +66,7 @@ dumpTracksRecommendation(Database::Session session, Recommendation::IEngine& eng }; std::cout << "Processing track '" << trackToString(trackId) << std::endl; - for (Database::IdType similarTrackId : engine.getSimilarTracks(session, {trackId}, maxSimilarityCount)) + for (Database::TrackId similarTrackId : engine.getSimilarTracks(session, {trackId}, maxSimilarityCount)) std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl; } } @@ -75,16 +75,16 @@ static void dumpReleasesRecommendation(Database::Session session, Recommendation::IEngine& engine, unsigned maxSimilarityCount) { - const std::vector releaseIds = std::invoke([&]() - { + const std::vector releaseIds = std::invoke([&]() + { auto transaction {session.createSharedTransaction()}; return Database::Release::getAllIds(session); - }); + }); std::cout << "*** Releases ***" << std::endl; - for (Database::IdType releaseId : releaseIds) + for (Database::ReleaseId releaseId : releaseIds) { - auto releaseToString = [&](Database::IdType releaseId) + auto releaseToString = [&](Database::ReleaseId releaseId) { 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::IdType similarReleaseId : engine.getSimilarReleases(session, releaseId, maxSimilarityCount)) + for (Database::ReleaseId similarReleaseId : engine.getSimilarReleases(session, releaseId, maxSimilarityCount)) std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl; } } @@ -102,16 +102,16 @@ static void dumpArtistsRecommendation(Database::Session session, Recommendation::IEngine& engine, unsigned maxSimilarityCount) { - const std::vector artistIds = std::invoke([&]() - { - auto transaction {session.createSharedTransaction()}; - return Database::Artist::getAllIds(session); - }); + const std::vector artistIds = std::invoke([&]() + { + auto transaction {session.createSharedTransaction()}; + return Database::Artist::getAllIds(session); + }); std::cout << "*** Artists ***" << std::endl; - for (Database::IdType artistId : artistIds) + for (Database::ArtistId artistId : artistIds) { - auto artistToString = [&](Database::IdType artistId) + auto artistToString = [&](Database::ArtistId artistId) { auto transaction {session.createSharedTransaction()}; @@ -120,7 +120,7 @@ dumpArtistsRecommendation(Database::Session session, Recommendation::IEngine& en }; std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl; - for (Database::IdType similarArtistId : engine.getSimilarArtists(session, artistId, {Database::TrackArtistLinkType::Artist, Database::TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) + for (Database::ArtistId similarArtistId : engine.getSimilarArtists(session, artistId, {Database::TrackArtistLinkType::Artist, Database::TrackArtistLinkType::ReleaseArtist}, maxSimilarityCount)) { std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl; }