diff --git a/src/libs/services/scrobbling/impl/IScrobbler.hpp b/src/libs/services/scrobbling/impl/IScrobbler.hpp index 7fd1f71f..e98ea1b7 100644 --- a/src/libs/services/scrobbling/impl/IScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/IScrobbler.hpp @@ -30,33 +30,33 @@ namespace Database { - class Session; - class TrackList; - class User; + class Session; + class TrackList; + class User; } namespace Scrobbling { - class IScrobbler - { - public: - virtual ~IScrobbler() = default; + class IScrobbler + { + public: + virtual ~IScrobbler() = default; - // Listens - virtual void listenStarted(const Listen& listen) = 0; - virtual void listenFinished(const Listen& listen, std::optional duration) = 0; - virtual void addTimedListen(const TimedListen& listen) = 0; + // Listens + virtual void listenStarted(const Listen& listen) = 0; + virtual void listenFinished(const Listen& listen, std::optional duration) = 0; + virtual void addTimedListen(const TimedListen& listen) = 0; - // Feedbacks - virtual void onStarred(Database::StarredArtistId) = 0; - virtual void onUnstarred(Database::StarredArtistId) = 0; - virtual void onStarred(Database::StarredReleaseId) = 0; - virtual void onUnstarred(Database::StarredReleaseId) = 0; - virtual void onStarred(Database::StarredTrackId) = 0; - virtual void onUnstarred(Database::StarredTrackId) = 0; - }; + // Feedbacks + virtual void onStarred(Database::StarredArtistId) = 0; + virtual void onUnstarred(Database::StarredArtistId) = 0; + virtual void onStarred(Database::StarredReleaseId) = 0; + virtual void onUnstarred(Database::StarredReleaseId) = 0; + virtual void onStarred(Database::StarredTrackId) = 0; + virtual void onUnstarred(Database::StarredTrackId) = 0; + }; - std::unique_ptr createScrobbler(std::string_view backendName); + std::unique_ptr createScrobbler(std::string_view backendName); } // ns Scrobbling diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.cpp b/src/libs/services/scrobbling/impl/ScrobblingService.cpp index ac24670d..cecb7c00 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.cpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.cpp @@ -37,272 +37,264 @@ namespace Scrobbling { - using namespace Database; + using namespace Database; - std::unique_ptr - createScrobblingService(boost::asio::io_context& ioContext, Db& db) - { - return std::make_unique(ioContext, db); - } + std::unique_ptr createScrobblingService(boost::asio::io_context& ioContext, Db& db) + { + return std::make_unique(ioContext, db); + } - ScrobblingService::ScrobblingService(boost::asio::io_context& ioContext, Db& db) - : _db {db} - { - LMS_LOG(SCROBBLING, INFO) << "Starting service..."; - _scrobblers.emplace(Scrobbler::Internal, std::make_unique(_db)); - _scrobblers.emplace(Scrobbler::ListenBrainz, std::make_unique(ioContext, _db)); - LMS_LOG(SCROBBLING, INFO) << "Service started!"; - } + ScrobblingService::ScrobblingService(boost::asio::io_context& ioContext, Db& db) + : _db{ db } + { + LMS_LOG(SCROBBLING, INFO) << "Starting service..."; + _scrobblers.emplace(Scrobbler::Internal, std::make_unique(_db)); + _scrobblers.emplace(Scrobbler::ListenBrainz, std::make_unique(ioContext, _db)); + LMS_LOG(SCROBBLING, INFO) << "Service started!"; + } - ScrobblingService::~ScrobblingService() - { - LMS_LOG(SCROBBLING, INFO) << "Service stopped!"; - } + ScrobblingService::~ScrobblingService() + { + LMS_LOG(SCROBBLING, INFO) << "Service stopped!"; + } - void - ScrobblingService::listenStarted(const Listen& listen) - { - if (std::optional scrobbler {getUserScrobbler(listen.userId)}) - _scrobblers[*scrobbler]->listenStarted(listen); - } + void ScrobblingService::listenStarted(const Listen& listen) + { + if (std::optional scrobbler{ getUserScrobbler(listen.userId) }) + _scrobblers[*scrobbler]->listenStarted(listen); + } - void - ScrobblingService::listenFinished(const Listen& listen, std::optional duration) - { - if (std::optional scrobbler {getUserScrobbler(listen.userId)}) - _scrobblers[*scrobbler]->listenFinished(listen, duration); - } + void ScrobblingService::listenFinished(const Listen& listen, std::optional duration) + { + if (std::optional scrobbler{ getUserScrobbler(listen.userId) }) + _scrobblers[*scrobbler]->listenFinished(listen, duration); + } - void - ScrobblingService::addTimedListen(const TimedListen& listen) - { - if (std::optional scrobbler {getUserScrobbler(listen.userId)}) - _scrobblers[*scrobbler]->addTimedListen(listen); - } + void ScrobblingService::addTimedListen(const TimedListen& listen) + { + if (std::optional scrobbler{ getUserScrobbler(listen.userId) }) + _scrobblers[*scrobbler]->addTimedListen(listen); + } - std::optional - ScrobblingService::getUserScrobbler(UserId userId) - { - std::optional scrobbler; + std::optional ScrobblingService::getUserScrobbler(UserId userId) + { + std::optional scrobbler; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; - if (const User::pointer user {User::find(session, userId)}) - scrobbler = user->getScrobbler(); + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; + if (const User::pointer user{ User::find(session, userId) }) + scrobbler = user->getScrobbler(); - return scrobbler; - } + return scrobbler; + } - ScrobblingService::ArtistContainer - ScrobblingService::getRecentArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, Range range) - { - ArtistContainer res; + ScrobblingService::ArtistContainer ScrobblingService::getRecentArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, Range range) + { + ArtistContainer res; - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return res; + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return res; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - res = Database::Listen::getRecentArtists(session, userId, *scrobbler, clusterIds, linkType, range); - return res; - } + res = Database::Listen::getRecentArtists(session, userId, *scrobbler, clusterIds, linkType, range); + return res; + } - ScrobblingService::ReleaseContainer - ScrobblingService::getRecentReleases(UserId userId, const std::vector& clusterIds, Range range) - { - ReleaseContainer res; + ScrobblingService::ReleaseContainer ScrobblingService::getRecentReleases(UserId userId, const std::vector& clusterIds, Range range) + { + ReleaseContainer res; - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return res; + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return res; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - res = Database::Listen::getRecentReleases(session, userId, *scrobbler, clusterIds, range); - return res; - } + res = Database::Listen::getRecentReleases(session, userId, *scrobbler, clusterIds, range); + return res; + } - ScrobblingService::TrackContainer - ScrobblingService::getRecentTracks(UserId userId, const std::vector& clusterIds, Range range) - { - TrackContainer res; + ScrobblingService::TrackContainer ScrobblingService::getRecentTracks(UserId userId, const std::vector& clusterIds, Range range) + { + TrackContainer res; - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return res; + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return res; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - res = Database::Listen::getRecentTracks(session, userId, *scrobbler, clusterIds, range); - return res; - } + res = Database::Listen::getRecentTracks(session, userId, *scrobbler, clusterIds, range); + return res; + } - // Top - ScrobblingService::ArtistContainer - ScrobblingService::getTopArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, Range range) - { - ArtistContainer res; + // Top + ScrobblingService::ArtistContainer ScrobblingService::getTopArtists(UserId userId, const std::vector& clusterIds, std::optional linkType, Range range) + { + ArtistContainer res; - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return res; + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return res; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - res = Database::Listen::getTopArtists(session, userId, *scrobbler, clusterIds, linkType, range); - return res; - } + res = Database::Listen::getTopArtists(session, userId, *scrobbler, clusterIds, linkType, range); + return res; + } - ScrobblingService::ReleaseContainer - ScrobblingService::getTopReleases(UserId userId, const std::vector& clusterIds, Range range) - { - ReleaseContainer res; + ScrobblingService::ReleaseContainer ScrobblingService::getTopReleases(UserId userId, const std::vector& clusterIds, Range range) + { + ReleaseContainer res; - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return res; + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return res; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - res = Database::Listen::getTopReleases(session, userId, *scrobbler, clusterIds, range); - return res; - } + res = Database::Listen::getTopReleases(session, userId, *scrobbler, clusterIds, range); + return res; + } - ScrobblingService::TrackContainer - ScrobblingService::getTopTracks(UserId userId, const std::vector& clusterIds, Range range) - { - TrackContainer res; + ScrobblingService::TrackContainer ScrobblingService::getTopTracks(UserId userId, const std::vector& clusterIds, Range range) + { + TrackContainer res; - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return res; + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return res; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - res = Database::Listen::getTopTracks(session, userId, *scrobbler, clusterIds, range); - return res; - } + res = Database::Listen::getTopTracks(session, userId, *scrobbler, clusterIds, range); + return res; + } - void - ScrobblingService::star(UserId userId, ArtistId artistId) - { - star(userId, artistId); - } + void ScrobblingService::star(UserId userId, ArtistId artistId) + { + star(userId, artistId); + } - void - ScrobblingService::unstar(UserId userId, ArtistId artistId) - { - unstar(userId, artistId); - } + void ScrobblingService::unstar(UserId userId, ArtistId artistId) + { + unstar(userId, artistId); + } - bool - ScrobblingService::isStarred(UserId userId, ArtistId artistId) - { - return isStarred(userId, artistId); - } + bool ScrobblingService::isStarred(UserId userId, ArtistId artistId) + { + return isStarred(userId, artistId); + } - ScrobblingService::ArtistContainer - ScrobblingService::getStarredArtists(UserId userId, const std::vector& clusterIds, - std::optional linkType, - ArtistSortMethod sortMethod, - Range range) - { - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return {}; + Wt::WDateTime ScrobblingService::getStarredDateTime(UserId userId, ArtistId artistId) + { + return getStarredDateTime(userId, artistId); + } - Artist::FindParameters params; - params.setStarringUser(userId, *scrobbler); - params.setClusters(clusterIds); - params.setLinkType(linkType); - params.setSortMethod(sortMethod); - params.setRange(range); + ScrobblingService::ArtistContainer ScrobblingService::getStarredArtists(UserId userId, const std::vector& clusterIds, + std::optional linkType, + ArtistSortMethod sortMethod, + Range range) + { + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return {}; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Artist::FindParameters params; + params.setStarringUser(userId, *scrobbler); + params.setClusters(clusterIds); + params.setLinkType(linkType); + params.setSortMethod(sortMethod); + params.setRange(range); - return Artist::find(session, params); - } + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - void - ScrobblingService::star(UserId userId, ReleaseId releaseId) - { - star(userId, releaseId); - } + return Artist::find(session, params); + } - void - ScrobblingService::unstar(UserId userId, ReleaseId releaseId) - { - unstar(userId, releaseId); - } + void ScrobblingService::star(UserId userId, ReleaseId releaseId) + { + star(userId, releaseId); + } - bool - ScrobblingService::isStarred(UserId userId, ReleaseId releaseId) - { - return isStarred(userId, releaseId); - } + void ScrobblingService::unstar(UserId userId, ReleaseId releaseId) + { + unstar(userId, releaseId); + } - ScrobblingService::ReleaseContainer - ScrobblingService::getStarredReleases(UserId userId, const std::vector& clusterIds, Range range) - { - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return {}; + bool ScrobblingService::isStarred(UserId userId, ReleaseId releaseId) + { + return isStarred(userId, releaseId); + } - Release::FindParameters params; - params.setStarringUser(userId, *scrobbler); - params.setClusters(clusterIds); - params.setSortMethod(ReleaseSortMethod::StarredDateDesc); - params.setRange(range); + Wt::WDateTime ScrobblingService::getStarredDateTime(UserId userId, ReleaseId releaseId) + { + return getStarredDateTime(userId, releaseId); + } - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + ScrobblingService::ReleaseContainer ScrobblingService::getStarredReleases(UserId userId, const std::vector& clusterIds, Range range) + { + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return {}; - return Release::find(session, params); - } + Release::FindParameters params; + params.setStarringUser(userId, *scrobbler); + params.setClusters(clusterIds); + params.setSortMethod(ReleaseSortMethod::StarredDateDesc); + params.setRange(range); - void - ScrobblingService::star(UserId userId, TrackId trackId) - { - star(userId, trackId); - } + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; - void - ScrobblingService::unstar(UserId userId, TrackId trackId) - { - unstar(userId, trackId); - } + return Release::find(session, params); + } - bool - ScrobblingService::isStarred(UserId userId, TrackId trackId) - { - return isStarred(userId, trackId); - } + void ScrobblingService::star(UserId userId, TrackId trackId) + { + star(userId, trackId); + } - ScrobblingService::TrackContainer - ScrobblingService::getStarredTracks(UserId userId, const std::vector& clusterIds, Range range) - { - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return {}; + void ScrobblingService::unstar(UserId userId, TrackId trackId) + { + unstar(userId, trackId); + } - Track::FindParameters params; - params.setStarringUser(userId, *scrobbler); - params.setClusters(clusterIds); - params.setSortMethod(TrackSortMethod::StarredDateDesc); - params.setRange(range); + bool ScrobblingService::isStarred(UserId userId, TrackId trackId) + { + return isStarred(userId, trackId); + } - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Wt::WDateTime ScrobblingService::getStarredDateTime(UserId userId, TrackId trackId) + { + return getStarredDateTime(userId, trackId); + } - return Track::find(session, params); - } + ScrobblingService::TrackContainer ScrobblingService::getStarredTracks(UserId userId, const std::vector& clusterIds, Range range) + { + auto scrobbler{ getUserScrobbler(userId) }; + if (!scrobbler) + return {}; + + Track::FindParameters params; + params.setStarringUser(userId, *scrobbler); + params.setClusters(clusterIds); + params.setSortMethod(TrackSortMethod::StarredDateDesc); + params.setRange(range); + + Session& session{ _db.getTLSSession() }; + auto transaction{ session.createSharedTransaction() }; + + return Track::find(session, params); + } } // ns Scrobbling diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.hpp b/src/libs/services/scrobbling/impl/ScrobblingService.hpp index ce9efaa5..225338a1 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.hpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.hpp @@ -28,74 +28,79 @@ namespace Scrobbling { - class ScrobblingService : public IScrobblingService - { - public: - ScrobblingService(boost::asio::io_context& ioContext, Database::Db& db); - ~ScrobblingService(); + class ScrobblingService : public IScrobblingService + { + public: + ScrobblingService(boost::asio::io_context& ioContext, Database::Db& db); + ~ScrobblingService(); - private: - void listenStarted(const Listen& listen) override; - void listenFinished(const Listen& listen, std::optional duration) override; - void addTimedListen(const TimedListen& listen) override; + private: + void listenStarted(const Listen& listen) override; + void listenFinished(const Listen& listen, std::optional duration) override; + void addTimedListen(const TimedListen& listen) override; - ArtistContainer getRecentArtists(Database::UserId userId, - const std::vector& clusterIds, - std::optional linkType, - Database::Range range) override; + ArtistContainer getRecentArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::Range range) override; - ReleaseContainer getRecentReleases(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) override; + ReleaseContainer getRecentReleases(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) override; - TrackContainer getRecentTracks(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) override; + TrackContainer getRecentTracks(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) override; - ArtistContainer getTopArtists(Database::UserId userId, - const std::vector& clusterIds, - std::optional linkType, - Database::Range range) override; + ArtistContainer getTopArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::Range range) override; - ReleaseContainer getTopReleases(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) override; + ReleaseContainer getTopReleases(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) override; - TrackContainer getTopTracks(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) override; + TrackContainer getTopTracks(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) override; - void star(Database::UserId userId, Database::ArtistId artistId) override; - void unstar(Database::UserId userId, Database::ArtistId artistId) override; - bool isStarred(Database::UserId userId, Database::ArtistId artistId) override; - ArtistContainer getStarredArtists(Database::UserId userId, - const std::vector& clusterIds, - std::optional linkType, - Database::ArtistSortMethod sortMethod, - Database::Range range) override; + void star(Database::UserId userId, Database::ArtistId artistId) override; + void unstar(Database::UserId userId, Database::ArtistId artistId) override; + bool isStarred(Database::UserId userId, Database::ArtistId artistId) override; + Wt::WDateTime getStarredDateTime(Database::UserId userId, Database::ArtistId artistId) override; + ArtistContainer getStarredArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::ArtistSortMethod sortMethod, + Database::Range range) override; - void star(Database::UserId userId, Database::ReleaseId releaseId) override; - void unstar(Database::UserId userId, Database::ReleaseId releaseId) override; - bool isStarred(Database::UserId userId, Database::ReleaseId artistId) override; - ReleaseContainer getStarredReleases(Database::UserId userId, const std::vector& clusterIds, Database::Range range) override; + void star(Database::UserId userId, Database::ReleaseId releaseId) override; + void unstar(Database::UserId userId, Database::ReleaseId releaseId) override; + bool isStarred(Database::UserId userId, Database::ReleaseId releasedId) override; + Wt::WDateTime getStarredDateTime(Database::UserId userId, Database::ReleaseId releasedId) override; + ReleaseContainer getStarredReleases(Database::UserId userId, const std::vector& clusterIds, Database::Range range) override; - void star(Database::UserId userId, Database::TrackId trackId) override; - void unstar(Database::UserId userId, Database::TrackId trackId) override; - bool isStarred(Database::UserId userId, Database::TrackId trackId) override; - TrackContainer getStarredTracks(Database::UserId userId, const std::vector& clusterIds, Database::Range range) override; + void star(Database::UserId userId, Database::TrackId trackId) override; + void unstar(Database::UserId userId, Database::TrackId trackId) override; + bool isStarred(Database::UserId userId, Database::TrackId trackId) override; + Wt::WDateTime getStarredDateTime(Database::UserId userId, Database::TrackId trackId) override; + TrackContainer getStarredTracks(Database::UserId userId, const std::vector& clusterIds, Database::Range range) override; - std::optional getUserScrobbler(Database::UserId userId); + std::optional getUserScrobbler(Database::UserId userId); - template - void star(Database::UserId userId, ObjIdType id); - template - void unstar(Database::UserId userId, ObjIdType id); - template - bool isStarred(Database::UserId userId, ObjIdType id); + template + void star(Database::UserId userId, ObjIdType id); + template + void unstar(Database::UserId userId, ObjIdType id); + template + bool isStarred(Database::UserId userId, ObjIdType id); + template + Wt::WDateTime getStarredDateTime(Database::UserId userId, ObjIdType id); - Database::Db& _db; - std::unordered_map> _scrobblers; - }; + Database::Db& _db; + std::unordered_map> _scrobblers; + }; } // ns Scrobbling diff --git a/src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp b/src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp index 9514c1f1..bf26979b 100644 --- a/src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp +++ b/src/libs/services/scrobbling/impl/ScrobblingService.impl.hpp @@ -25,76 +25,90 @@ namespace Scrobbling { - using namespace Database; + using namespace Database; - template - void - ScrobblingService::star(UserId userId, ObjIdType objId) - { - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return; + template + void ScrobblingService::star(UserId userId, ObjIdType objId) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return; - typename StarredObjType::IdType starredObjId; - { - Session& session {_db.getTLSSession()}; - auto transaction {session.createUniqueTransaction()}; + typename StarredObjType::IdType starredObjId; + { + Session& session {_db.getTLSSession()}; + auto transaction {session.createUniqueTransaction()}; - typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; - if (!starredObj) - { - const typename ObjType::pointer obj {ObjType::find(session, objId)}; - if (!obj) - return; + typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; + if (!starredObj) + { + const typename ObjType::pointer obj {ObjType::find(session, objId)}; + if (!obj) + return; - const User::pointer user {User::find(session, userId)}; - if (!user) - return; + const User::pointer user {User::find(session, userId)}; + if (!user) + return; - starredObj = session.create(obj, user, *scrobbler); - } - starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime()); - starredObjId = starredObj->getId(); - } - _scrobblers[*scrobbler]->onStarred(starredObjId); - } + starredObj = session.create(obj, user, *scrobbler); + } + starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime()); + starredObjId = starredObj->getId(); + } + _scrobblers[*scrobbler]->onStarred(starredObjId); + } - template - void - ScrobblingService::unstar(UserId userId, ObjIdType objId) - { - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return; + template + void ScrobblingService::unstar(UserId userId, ObjIdType objId) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return; - typename StarredObjType::IdType starredObjId; - { - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + typename StarredObjType::IdType starredObjId; + { + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; - typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; - if (!starredObj) - return; + typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; + if (!starredObj) + return; - starredObjId = starredObj->getId(); - } - _scrobblers[*scrobbler]->onUnstarred(starredObjId); - } + starredObjId = starredObj->getId(); + } + _scrobblers[*scrobbler]->onUnstarred(starredObjId); + } - template - bool - ScrobblingService::isStarred(UserId userId, ObjIdType objId) - { - auto scrobbler {getUserScrobbler(userId)}; - if (!scrobbler) - return false; + template + bool ScrobblingService::isStarred(UserId userId, ObjIdType objId) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return false; - Session& session {_db.getTLSSession()}; - auto transaction {session.createSharedTransaction()}; + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; - typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; - return starredObj && (starredObj->getScrobblingState() != ScrobblingState::PendingRemove); - } + typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; + return starredObj && (starredObj->getScrobblingState() != ScrobblingState::PendingRemove); + } + + template + Wt::WDateTime ScrobblingService::getStarredDateTime(UserId userId, ObjIdType objId) + { + auto scrobbler {getUserScrobbler(userId)}; + if (!scrobbler) + return {}; + + Session& session {_db.getTLSSession()}; + auto transaction {session.createSharedTransaction()}; + + typename StarredObjType::pointer starredObj {StarredObjType::find(session, objId, userId, *scrobbler)}; + if (starredObj && (starredObj->getScrobblingState() != ScrobblingState::PendingRemove)) + return starredObj->getDateTime(); + + return {}; + } } // ns Scrobbling diff --git a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp index 775ef943..02c9ed1c 100644 --- a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.cpp @@ -30,101 +30,93 @@ namespace { - template - void onStarred(Database::Session& session, typename StarredObjType::IdType id) - { - auto transaction {session.createUniqueTransaction()}; + template + void onStarred(Database::Session& session, typename StarredObjType::IdType id) + { + auto transaction{ session.createUniqueTransaction() }; - if (auto starredObj {StarredObjType::find(session, id)}) - starredObj.modify()->setScrobblingState(Database::ScrobblingState::Synchronized); - } + if (auto starredObj{ StarredObjType::find(session, id) }) + starredObj.modify()->setScrobblingState(Database::ScrobblingState::Synchronized); + } - template - void onUnstarred(Database::Session& session, typename StarredObjType::IdType id) - { - auto transaction {session.createUniqueTransaction()}; + template + void onUnstarred(Database::Session& session, typename StarredObjType::IdType id) + { + auto transaction{ session.createUniqueTransaction() }; - if (auto starredObj {StarredObjType::find(session, id)}) - starredObj.remove(); - } + if (auto starredObj{ StarredObjType::find(session, id) }) + starredObj.remove(); + } } namespace Scrobbling { - InternalScrobbler::InternalScrobbler(Database::Db& db) - : _db {db} - {} + InternalScrobbler::InternalScrobbler(Database::Db& db) + : _db{ db } + {} - void - InternalScrobbler::listenStarted(const Listen&) - { - // nothing to do - } + void InternalScrobbler::listenStarted(const Listen&) + { + // nothing to do + } - void - InternalScrobbler::listenFinished(const Listen& listen, std::optional duration) - { - // only record tracks that have been played for at least of few seconds... - if (duration && *duration < std::chrono::seconds {5}) - return; + void InternalScrobbler::listenFinished(const Listen& listen, std::optional duration) + { + // only record tracks that have been played for at least of few seconds... + if (duration && *duration < std::chrono::seconds{ 5 }) + return; - addTimedListen({listen, Wt::WDateTime::currentDateTime()}); - } + addTimedListen({ listen, Wt::WDateTime::currentDateTime() }); + } - void - InternalScrobbler::addTimedListen(const TimedListen& listen) - { - Database::Session& session {_db.getTLSSession()}; - auto transaction {session.createUniqueTransaction()}; + void InternalScrobbler::addTimedListen(const TimedListen& listen) + { + Database::Session& session{ _db.getTLSSession() }; + auto transaction{ session.createUniqueTransaction() }; - if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::Internal, listen.listenedAt)) - return; + if (Database::Listen::find(session, listen.userId, listen.trackId, Database::Scrobbler::Internal, listen.listenedAt)) + return; - const Database::User::pointer user {Database::User::find(session, listen.userId)}; - if (!user) - return; + const Database::User::pointer user{ Database::User::find(session, listen.userId) }; + if (!user) + return; - const Database::Track::pointer track {Database::Track::find(session, listen.trackId)}; - if (!track) - return; + const Database::Track::pointer track{ Database::Track::find(session, listen.trackId) }; + if (!track) + return; - auto dbListen {session.create(user, track, Database::Scrobbler::Internal, listen.listenedAt)}; - dbListen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized); - } + auto dbListen{ session.create(user, track, Database::Scrobbler::Internal, listen.listenedAt) }; + dbListen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized); + } - void - InternalScrobbler::onStarred(Database::StarredArtistId starredArtistId) - { - ::onStarred(_db.getTLSSession(), starredArtistId); - } + void InternalScrobbler::onStarred(Database::StarredArtistId starredArtistId) + { + ::onStarred(_db.getTLSSession(), starredArtistId); + } - void - InternalScrobbler::onUnstarred(Database::StarredArtistId starredArtistId) - { - ::onUnstarred(_db.getTLSSession(), starredArtistId); - } + void InternalScrobbler::onUnstarred(Database::StarredArtistId starredArtistId) + { + ::onUnstarred(_db.getTLSSession(), starredArtistId); + } - void - InternalScrobbler::onStarred(Database::StarredReleaseId starredReleaseId) - { - ::onStarred(_db.getTLSSession(), starredReleaseId); - } + void InternalScrobbler::onStarred(Database::StarredReleaseId starredReleaseId) + { + ::onStarred(_db.getTLSSession(), starredReleaseId); + } - void InternalScrobbler::onUnstarred(Database::StarredReleaseId starredReleaseId) - { - ::onUnstarred(_db.getTLSSession(), starredReleaseId); - } + void InternalScrobbler::onUnstarred(Database::StarredReleaseId starredReleaseId) + { + ::onUnstarred(_db.getTLSSession(), starredReleaseId); + } - void - InternalScrobbler::onStarred(Database::StarredTrackId starredTrackId) - { - ::onStarred(_db.getTLSSession(), starredTrackId); - } + void InternalScrobbler::onStarred(Database::StarredTrackId starredTrackId) + { + ::onStarred(_db.getTLSSession(), starredTrackId); + } - void - InternalScrobbler::onUnstarred(Database::StarredTrackId starredTrackId) - { - ::onUnstarred(_db.getTLSSession(), starredTrackId); - } + void InternalScrobbler::onUnstarred(Database::StarredTrackId starredTrackId) + { + ::onUnstarred(_db.getTLSSession(), starredTrackId); + } } // Scrobbling diff --git a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp index 4b6dd39c..ce3fb608 100644 --- a/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp +++ b/src/libs/services/scrobbling/impl/internal/InternalScrobbler.hpp @@ -23,30 +23,30 @@ namespace Database { - class Db; + class Db; } namespace Scrobbling { - class InternalScrobbler final : public IScrobbler - { - public: - InternalScrobbler(Database::Db& db); + class InternalScrobbler final : public IScrobbler + { + public: + InternalScrobbler(Database::Db& db); - private: - // IScrobbler - void listenStarted(const Listen& listen) override; - void listenFinished(const Listen& listen, std::optional duration) override; - void addTimedListen(const TimedListen& listen) override; + private: + // IScrobbler + void listenStarted(const Listen& listen) override; + void listenFinished(const Listen& listen, std::optional duration) override; + void addTimedListen(const TimedListen& listen) override; - void onStarred(Database::StarredArtistId) override; - void onUnstarred(Database::StarredArtistId) override; - void onStarred(Database::StarredReleaseId) override; - void onUnstarred(Database::StarredReleaseId) override; - void onStarred(Database::StarredTrackId) override; - void onUnstarred(Database::StarredTrackId) override; + void onStarred(Database::StarredArtistId) override; + void onUnstarred(Database::StarredArtistId) override; + void onStarred(Database::StarredReleaseId) override; + void onUnstarred(Database::StarredReleaseId) override; + void onStarred(Database::StarredTrackId) override; + void onUnstarred(Database::StarredTrackId) override; - Database::Db& _db; - }; + Database::Db& _db; + }; } // Scrobbling diff --git a/src/libs/services/scrobbling/include/services/scrobbling/Exception.hpp b/src/libs/services/scrobbling/include/services/scrobbling/Exception.hpp index e6009e68..699bfa19 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/Exception.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/Exception.hpp @@ -23,9 +23,9 @@ namespace Scrobbling { - class Exception : public LmsException - { - public: - using LmsException::LmsException; - }; + class Exception : public LmsException + { + public: + using LmsException::LmsException; + }; } diff --git a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp index 18b00129..fd27df5d 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp @@ -19,11 +19,11 @@ #pragma once -#include - #include #include #include +#include +#include #include "services/scrobbling/Listen.hpp" #include "services/database/ArtistId.hpp" @@ -34,77 +34,80 @@ namespace Database { - class Db; + class Db; } namespace Scrobbling { - class IScrobblingService - { - public: - virtual ~IScrobblingService() = default; + class IScrobblingService + { + public: + virtual ~IScrobblingService() = default; - // Scrobbling - virtual void listenStarted(const Listen& listen) = 0; - virtual void listenFinished(const Listen& listen, std::optional playedDuration = std::nullopt) = 0; + // Scrobbling + virtual void listenStarted(const Listen& listen) = 0; + virtual void listenFinished(const Listen& listen, std::optional playedDuration = std::nullopt) = 0; - virtual void addTimedListen(const TimedListen& listen) = 0; + virtual void addTimedListen(const TimedListen& listen) = 0; - // Stats - using ArtistContainer = Database::RangeResults; - using ReleaseContainer = Database::RangeResults; - using TrackContainer = Database::RangeResults; - // From most recent to oldest - virtual ArtistContainer getRecentArtists(Database::UserId userId, - const std::vector& clusterIds, - std::optional linkType, - Database::Range range) = 0; + // Stats + using ArtistContainer = Database::RangeResults; + using ReleaseContainer = Database::RangeResults; + using TrackContainer = Database::RangeResults; + // From most recent to oldest + virtual ArtistContainer getRecentArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::Range range) = 0; - virtual ReleaseContainer getRecentReleases(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) = 0; + virtual ReleaseContainer getRecentReleases(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) = 0; - virtual TrackContainer getRecentTracks(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) = 0; + virtual TrackContainer getRecentTracks(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) = 0; - // Top - virtual ArtistContainer getTopArtists(Database::UserId userId, - const std::vector& clusterIds, - std::optional linkType, - Database::Range) = 0; + // Top + virtual ArtistContainer getTopArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::Range) = 0; - virtual ReleaseContainer getTopReleases(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) = 0; + virtual ReleaseContainer getTopReleases(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) = 0; - virtual TrackContainer getTopTracks(Database::UserId userId, - const std::vector& clusterIds, - Database::Range range) = 0; + virtual TrackContainer getTopTracks(Database::UserId userId, + const std::vector& clusterIds, + Database::Range range) = 0; - // Star - virtual void star(Database::UserId userId, Database::ArtistId artistId) = 0; - virtual void unstar(Database::UserId userId, Database::ArtistId artistId) = 0; - virtual bool isStarred(Database::UserId userId, Database::ArtistId artistId) = 0; - virtual ArtistContainer getStarredArtists(Database::UserId userId, - const std::vector& clusterIds, - std::optional linkType, - Database::ArtistSortMethod sortMethod, - Database::Range range) = 0; + // Star + virtual void star(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual void unstar(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual bool isStarred(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual Wt::WDateTime getStarredDateTime(Database::UserId userId, Database::ArtistId artistId) = 0; + virtual ArtistContainer getStarredArtists(Database::UserId userId, + const std::vector& clusterIds, + std::optional linkType, + Database::ArtistSortMethod sortMethod, + Database::Range range) = 0; - virtual void star(Database::UserId userId, Database::ReleaseId releaseId) = 0; - virtual void unstar(Database::UserId userId, Database::ReleaseId releaseId) = 0; - virtual bool isStarred(Database::UserId userId, Database::ReleaseId artistId) = 0; - virtual ReleaseContainer getStarredReleases(Database::UserId userId, const std::vector& clusterIds, Database::Range range) = 0; + virtual void star(Database::UserId userId, Database::ReleaseId releaseId) = 0; + virtual void unstar(Database::UserId userId, Database::ReleaseId releaseId) = 0; + virtual bool isStarred(Database::UserId userId, Database::ReleaseId artistId) = 0; + virtual Wt::WDateTime getStarredDateTime(Database::UserId userId, Database::ReleaseId artistId) = 0; + virtual ReleaseContainer getStarredReleases(Database::UserId userId, const std::vector& clusterIds, Database::Range range) = 0; - virtual void star(Database::UserId userId, Database::TrackId trackId) = 0; - virtual void unstar(Database::UserId userId, Database::TrackId trackId) = 0; - virtual bool isStarred(Database::UserId userId, Database::TrackId artistId) = 0; - virtual TrackContainer getStarredTracks(Database::UserId userId, const std::vector& clusterIds, Database::Range range) = 0; - }; + virtual void star(Database::UserId userId, Database::TrackId trackId) = 0; + virtual void unstar(Database::UserId userId, Database::TrackId trackId) = 0; + virtual bool isStarred(Database::UserId userId, Database::TrackId artistId) = 0; + virtual Wt::WDateTime getStarredDateTime(Database::UserId userId, Database::TrackId artistId) = 0; + virtual TrackContainer getStarredTracks(Database::UserId userId, const std::vector& clusterIds, Database::Range range) = 0; + }; - std::unique_ptr createScrobblingService(boost::asio::io_service& ioService, Database::Db& db); + std::unique_ptr createScrobblingService(boost::asio::io_service& ioService, Database::Db& db); } // ns Scrobbling diff --git a/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp b/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp index a42ab649..3838f1bc 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/Listen.hpp @@ -26,15 +26,15 @@ namespace Scrobbling { - struct Listen - { - Database::UserId userId {}; - Database::TrackId trackId {}; - }; + struct Listen + { + Database::UserId userId{}; + Database::TrackId trackId{}; + }; - struct TimedListen : public Listen - { - Wt::WDateTime listenedAt; - }; + struct TimedListen : public Listen + { + Wt::WDateTime listenedAt; + }; } // ns Scrobbling diff --git a/src/libs/subsonic/impl/responses/Album.cpp b/src/libs/subsonic/impl/responses/Album.cpp index 5855d755..1248c4b5 100644 --- a/src/libs/subsonic/impl/responses/Album.cpp +++ b/src/libs/subsonic/impl/responses/Album.cpp @@ -32,8 +32,6 @@ namespace API::Subsonic { - static const std::string_view reportedDummyStarredDate{ "2000-01-01T00:00:00" }; - using namespace Database; Response::Node createAlbumNode(const Release::pointer& release, Session& dbSession, const User::pointer& user, bool id3) @@ -95,8 +93,8 @@ namespace API::Subsonic } } - if (Service::get()->isStarred(user->getId(), release->getId())) - albumNode.setAttribute("starred", reportedDummyStarredDate); // TODO report correct date/time + if (const Wt::WDateTime dateTime{ Service::get()->getStarredDateTime(user->getId(), release->getId()) }; dateTime.isValid()) + albumNode.setAttribute("starred", StringUtils::toISO8601String(dateTime)); // TODO report correct date/time return albumNode; } diff --git a/src/libs/subsonic/impl/responses/Artist.cpp b/src/libs/subsonic/impl/responses/Artist.cpp index 32f05851..85ec6d40 100644 --- a/src/libs/subsonic/impl/responses/Artist.cpp +++ b/src/libs/subsonic/impl/responses/Artist.cpp @@ -24,13 +24,12 @@ #include "services/database/User.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" +#include "utils/String.hpp" #include "SubsonicId.hpp" namespace API::Subsonic { - static const std::string_view reportedDummyStarredDate{ "2000-01-01T00:00:00" }; - using namespace Database; namespace Utils @@ -66,8 +65,8 @@ namespace API::Subsonic artistNode.setAttribute("albumCount", releases.results.size()); } - if (Service::get()->isStarred(user->getId(), artist->getId())) - artistNode.setAttribute("starred", reportedDummyStarredDate); // TODO handle date/time + if (const Wt::WDateTime dateTime{ Service::get()->getStarredDateTime(user->getId(), artist->getId()) }; dateTime.isValid()) + artistNode.setAttribute("starred", StringUtils::toISO8601String(dateTime)); return artistNode; } diff --git a/src/libs/subsonic/impl/responses/Song.cpp b/src/libs/subsonic/impl/responses/Song.cpp index 63b0aa58..af319ff2 100644 --- a/src/libs/subsonic/impl/responses/Song.cpp +++ b/src/libs/subsonic/impl/responses/Song.cpp @@ -37,8 +37,6 @@ namespace API::Subsonic { using namespace Database; - static const std::string_view reportedDummyStarredDate{ "2000-01-01T00:00:00" }; - namespace { std::string_view formatToSuffix(AudioFormat format) @@ -143,8 +141,8 @@ namespace API::Subsonic trackResponse.setAttribute("type", "music"); trackResponse.setAttribute("created", StringUtils::toISO8601String(track->getLastWritten())); - if (Service::get()->isStarred(user->getId(), track->getId())) - trackResponse.setAttribute("starred", reportedDummyStarredDate); // TODO handle date/time + if (const Wt::WDateTime dateTime{ Service::get()->getStarredDateTime(user->getId(), track->getId()) }; dateTime.isValid()) + trackResponse.setAttribute("starred", StringUtils::toISO8601String(dateTime)); // Report the first GENRE for this track ClusterType::pointer clusterType{ ClusterType::find(dbSession, "GENRE") }; diff --git a/src/libs/utils/impl/String.cpp b/src/libs/utils/impl/String.cpp index 96443f07..ff7f64ff 100644 --- a/src/libs/utils/impl/String.cpp +++ b/src/libs/utils/impl/String.cpp @@ -288,11 +288,13 @@ namespace StringUtils std::string toISO8601String(const Wt::WDateTime& dateTime) { + // assume UTC return dateTime.toString("yyyy-MM-ddThh:mm:ss.zzz", false).toUTF8(); } std::string toISO8601String(const Wt::WDate& date) { + // assume UTC return date.toString("yyyy-MM-dd").toUTF8(); } } // StringUtils