Subsonic API: reported correct starred date/time
This commit is contained in:
@@ -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<std::chrono::seconds> 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<std::chrono::seconds> 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<IScrobbler> createScrobbler(std::string_view backendName);
|
||||
std::unique_ptr<IScrobbler> createScrobbler(std::string_view backendName);
|
||||
|
||||
} // ns Scrobbling
|
||||
|
||||
|
||||
@@ -37,272 +37,264 @@
|
||||
|
||||
namespace Scrobbling
|
||||
{
|
||||
using namespace Database;
|
||||
using namespace Database;
|
||||
|
||||
std::unique_ptr<IScrobblingService>
|
||||
createScrobblingService(boost::asio::io_context& ioContext, Db& db)
|
||||
{
|
||||
return std::make_unique<ScrobblingService>(ioContext, db);
|
||||
}
|
||||
std::unique_ptr<IScrobblingService> createScrobblingService(boost::asio::io_context& ioContext, Db& db)
|
||||
{
|
||||
return std::make_unique<ScrobblingService>(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<InternalScrobbler>(_db));
|
||||
_scrobblers.emplace(Scrobbler::ListenBrainz, std::make_unique<ListenBrainz::Scrobbler>(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<InternalScrobbler>(_db));
|
||||
_scrobblers.emplace(Scrobbler::ListenBrainz, std::make_unique<ListenBrainz::Scrobbler>(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> scrobbler {getUserScrobbler(listen.userId)})
|
||||
_scrobblers[*scrobbler]->listenStarted(listen);
|
||||
}
|
||||
void ScrobblingService::listenStarted(const Listen& listen)
|
||||
{
|
||||
if (std::optional<Scrobbler> scrobbler{ getUserScrobbler(listen.userId) })
|
||||
_scrobblers[*scrobbler]->listenStarted(listen);
|
||||
}
|
||||
|
||||
void
|
||||
ScrobblingService::listenFinished(const Listen& listen, std::optional<std::chrono::seconds> duration)
|
||||
{
|
||||
if (std::optional<Scrobbler> scrobbler {getUserScrobbler(listen.userId)})
|
||||
_scrobblers[*scrobbler]->listenFinished(listen, duration);
|
||||
}
|
||||
void ScrobblingService::listenFinished(const Listen& listen, std::optional<std::chrono::seconds> duration)
|
||||
{
|
||||
if (std::optional<Scrobbler> scrobbler{ getUserScrobbler(listen.userId) })
|
||||
_scrobblers[*scrobbler]->listenFinished(listen, duration);
|
||||
}
|
||||
|
||||
void
|
||||
ScrobblingService::addTimedListen(const TimedListen& listen)
|
||||
{
|
||||
if (std::optional<Scrobbler> scrobbler {getUserScrobbler(listen.userId)})
|
||||
_scrobblers[*scrobbler]->addTimedListen(listen);
|
||||
}
|
||||
void ScrobblingService::addTimedListen(const TimedListen& listen)
|
||||
{
|
||||
if (std::optional<Scrobbler> scrobbler{ getUserScrobbler(listen.userId) })
|
||||
_scrobblers[*scrobbler]->addTimedListen(listen);
|
||||
}
|
||||
|
||||
std::optional<Scrobbler>
|
||||
ScrobblingService::getUserScrobbler(UserId userId)
|
||||
{
|
||||
std::optional<Scrobbler> scrobbler;
|
||||
std::optional<Scrobbler> ScrobblingService::getUserScrobbler(UserId userId)
|
||||
{
|
||||
std::optional<Scrobbler> 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<ClusterId>& clusterIds, std::optional<TrackArtistLinkType> linkType, Range range)
|
||||
{
|
||||
ArtistContainer res;
|
||||
ScrobblingService::ArtistContainer ScrobblingService::getRecentArtists(UserId userId, const std::vector<ClusterId>& clusterIds, std::optional<TrackArtistLinkType> 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<ClusterId>& clusterIds, Range range)
|
||||
{
|
||||
ReleaseContainer res;
|
||||
ScrobblingService::ReleaseContainer ScrobblingService::getRecentReleases(UserId userId, const std::vector<ClusterId>& 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<ClusterId>& clusterIds, Range range)
|
||||
{
|
||||
TrackContainer res;
|
||||
ScrobblingService::TrackContainer ScrobblingService::getRecentTracks(UserId userId, const std::vector<ClusterId>& 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<ClusterId>& clusterIds, std::optional<TrackArtistLinkType> linkType, Range range)
|
||||
{
|
||||
ArtistContainer res;
|
||||
// Top
|
||||
ScrobblingService::ArtistContainer ScrobblingService::getTopArtists(UserId userId, const std::vector<ClusterId>& clusterIds, std::optional<TrackArtistLinkType> 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<ClusterId>& clusterIds, Range range)
|
||||
{
|
||||
ReleaseContainer res;
|
||||
ScrobblingService::ReleaseContainer ScrobblingService::getTopReleases(UserId userId, const std::vector<ClusterId>& 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<ClusterId>& clusterIds, Range range)
|
||||
{
|
||||
TrackContainer res;
|
||||
ScrobblingService::TrackContainer ScrobblingService::getTopTracks(UserId userId, const std::vector<ClusterId>& 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<Artist, ArtistId, StarredArtist>(userId, artistId);
|
||||
}
|
||||
void ScrobblingService::star(UserId userId, ArtistId artistId)
|
||||
{
|
||||
star<Artist, ArtistId, StarredArtist>(userId, artistId);
|
||||
}
|
||||
|
||||
void
|
||||
ScrobblingService::unstar(UserId userId, ArtistId artistId)
|
||||
{
|
||||
unstar<Artist, ArtistId, StarredArtist>(userId, artistId);
|
||||
}
|
||||
void ScrobblingService::unstar(UserId userId, ArtistId artistId)
|
||||
{
|
||||
unstar<Artist, ArtistId, StarredArtist>(userId, artistId);
|
||||
}
|
||||
|
||||
bool
|
||||
ScrobblingService::isStarred(UserId userId, ArtistId artistId)
|
||||
{
|
||||
return isStarred<Artist, ArtistId, StarredArtist>(userId, artistId);
|
||||
}
|
||||
bool ScrobblingService::isStarred(UserId userId, ArtistId artistId)
|
||||
{
|
||||
return isStarred<Artist, ArtistId, StarredArtist>(userId, artistId);
|
||||
}
|
||||
|
||||
ScrobblingService::ArtistContainer
|
||||
ScrobblingService::getStarredArtists(UserId userId, const std::vector<ClusterId>& clusterIds,
|
||||
std::optional<TrackArtistLinkType> linkType,
|
||||
ArtistSortMethod sortMethod,
|
||||
Range range)
|
||||
{
|
||||
auto scrobbler {getUserScrobbler(userId)};
|
||||
if (!scrobbler)
|
||||
return {};
|
||||
Wt::WDateTime ScrobblingService::getStarredDateTime(UserId userId, ArtistId artistId)
|
||||
{
|
||||
return getStarredDateTime<Artist, ArtistId, StarredArtist>(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<ClusterId>& clusterIds,
|
||||
std::optional<TrackArtistLinkType> 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<Release, ReleaseId, StarredRelease>(userId, releaseId);
|
||||
}
|
||||
return Artist::find(session, params);
|
||||
}
|
||||
|
||||
void
|
||||
ScrobblingService::unstar(UserId userId, ReleaseId releaseId)
|
||||
{
|
||||
unstar<Release, ReleaseId, StarredRelease>(userId, releaseId);
|
||||
}
|
||||
void ScrobblingService::star(UserId userId, ReleaseId releaseId)
|
||||
{
|
||||
star<Release, ReleaseId, StarredRelease>(userId, releaseId);
|
||||
}
|
||||
|
||||
bool
|
||||
ScrobblingService::isStarred(UserId userId, ReleaseId releaseId)
|
||||
{
|
||||
return isStarred<Release, ReleaseId, StarredRelease>(userId, releaseId);
|
||||
}
|
||||
void ScrobblingService::unstar(UserId userId, ReleaseId releaseId)
|
||||
{
|
||||
unstar<Release, ReleaseId, StarredRelease>(userId, releaseId);
|
||||
}
|
||||
|
||||
ScrobblingService::ReleaseContainer
|
||||
ScrobblingService::getStarredReleases(UserId userId, const std::vector<ClusterId>& clusterIds, Range range)
|
||||
{
|
||||
auto scrobbler {getUserScrobbler(userId)};
|
||||
if (!scrobbler)
|
||||
return {};
|
||||
bool ScrobblingService::isStarred(UserId userId, ReleaseId releaseId)
|
||||
{
|
||||
return isStarred<Release, ReleaseId, StarredRelease>(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<Release, ReleaseId, StarredRelease>(userId, releaseId);
|
||||
}
|
||||
|
||||
Session& session {_db.getTLSSession()};
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
ScrobblingService::ReleaseContainer ScrobblingService::getStarredReleases(UserId userId, const std::vector<ClusterId>& 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<Track, TrackId, StarredTrack>(userId, trackId);
|
||||
}
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createSharedTransaction() };
|
||||
|
||||
void
|
||||
ScrobblingService::unstar(UserId userId, TrackId trackId)
|
||||
{
|
||||
unstar<Track, TrackId, StarredTrack>(userId, trackId);
|
||||
}
|
||||
return Release::find(session, params);
|
||||
}
|
||||
|
||||
bool
|
||||
ScrobblingService::isStarred(UserId userId, TrackId trackId)
|
||||
{
|
||||
return isStarred<Track, TrackId, StarredTrack>(userId, trackId);
|
||||
}
|
||||
void ScrobblingService::star(UserId userId, TrackId trackId)
|
||||
{
|
||||
star<Track, TrackId, StarredTrack>(userId, trackId);
|
||||
}
|
||||
|
||||
ScrobblingService::TrackContainer
|
||||
ScrobblingService::getStarredTracks(UserId userId, const std::vector<ClusterId>& clusterIds, Range range)
|
||||
{
|
||||
auto scrobbler {getUserScrobbler(userId)};
|
||||
if (!scrobbler)
|
||||
return {};
|
||||
void ScrobblingService::unstar(UserId userId, TrackId trackId)
|
||||
{
|
||||
unstar<Track, TrackId, StarredTrack>(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<Track, TrackId, StarredTrack>(userId, trackId);
|
||||
}
|
||||
|
||||
Session& session {_db.getTLSSession()};
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
Wt::WDateTime ScrobblingService::getStarredDateTime(UserId userId, TrackId trackId)
|
||||
{
|
||||
return getStarredDateTime<Track, TrackId, StarredTrack>(userId, trackId);
|
||||
}
|
||||
|
||||
return Track::find(session, params);
|
||||
}
|
||||
ScrobblingService::TrackContainer ScrobblingService::getStarredTracks(UserId userId, const std::vector<ClusterId>& 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
|
||||
|
||||
|
||||
@@ -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<std::chrono::seconds> duration) override;
|
||||
void addTimedListen(const TimedListen& listen) override;
|
||||
private:
|
||||
void listenStarted(const Listen& listen) override;
|
||||
void listenFinished(const Listen& listen, std::optional<std::chrono::seconds> duration) override;
|
||||
void addTimedListen(const TimedListen& listen) override;
|
||||
|
||||
ArtistContainer getRecentArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range range) override;
|
||||
ArtistContainer getRecentArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range range) override;
|
||||
|
||||
ReleaseContainer getRecentReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) override;
|
||||
ReleaseContainer getRecentReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) override;
|
||||
|
||||
TrackContainer getRecentTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) override;
|
||||
TrackContainer getRecentTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) override;
|
||||
|
||||
ArtistContainer getTopArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range range) override;
|
||||
ArtistContainer getTopArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range range) override;
|
||||
|
||||
ReleaseContainer getTopReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) override;
|
||||
ReleaseContainer getTopReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) override;
|
||||
|
||||
TrackContainer getTopTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) override;
|
||||
TrackContainer getTopTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& 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<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> 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<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> 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<Database::ClusterId>& 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<Database::ClusterId>& 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<Database::ClusterId>& 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<Database::ClusterId>& clusterIds, Database::Range range) override;
|
||||
|
||||
std::optional<Database::Scrobbler> getUserScrobbler(Database::UserId userId);
|
||||
std::optional<Database::Scrobbler> getUserScrobbler(Database::UserId userId);
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void star(Database::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void unstar(Database::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
bool isStarred(Database::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void star(Database::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void unstar(Database::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
bool isStarred(Database::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
Wt::WDateTime getStarredDateTime(Database::UserId userId, ObjIdType id);
|
||||
|
||||
Database::Db& _db;
|
||||
std::unordered_map<Database::Scrobbler, std::unique_ptr<IScrobbler>> _scrobblers;
|
||||
};
|
||||
Database::Db& _db;
|
||||
std::unordered_map<Database::Scrobbler, std::unique_ptr<IScrobbler>> _scrobblers;
|
||||
};
|
||||
|
||||
} // ns Scrobbling
|
||||
|
||||
|
||||
@@ -25,76 +25,90 @@
|
||||
|
||||
namespace Scrobbling
|
||||
{
|
||||
using namespace Database;
|
||||
using namespace Database;
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void
|
||||
ScrobblingService::star(UserId userId, ObjIdType objId)
|
||||
{
|
||||
auto scrobbler {getUserScrobbler(userId)};
|
||||
if (!scrobbler)
|
||||
return;
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
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<StarredObjType>(obj, user, *scrobbler);
|
||||
}
|
||||
starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime());
|
||||
starredObjId = starredObj->getId();
|
||||
}
|
||||
_scrobblers[*scrobbler]->onStarred(starredObjId);
|
||||
}
|
||||
starredObj = session.create<StarredObjType>(obj, user, *scrobbler);
|
||||
}
|
||||
starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime());
|
||||
starredObjId = starredObj->getId();
|
||||
}
|
||||
_scrobblers[*scrobbler]->onStarred(starredObjId);
|
||||
}
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void
|
||||
ScrobblingService::unstar(UserId userId, ObjIdType objId)
|
||||
{
|
||||
auto scrobbler {getUserScrobbler(userId)};
|
||||
if (!scrobbler)
|
||||
return;
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
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 <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
bool
|
||||
ScrobblingService::isStarred(UserId userId, ObjIdType objId)
|
||||
{
|
||||
auto scrobbler {getUserScrobbler(userId)};
|
||||
if (!scrobbler)
|
||||
return false;
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
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 <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
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
|
||||
|
||||
|
||||
@@ -30,101 +30,93 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename StarredObjType>
|
||||
void onStarred(Database::Session& session, typename StarredObjType::IdType id)
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
template <typename StarredObjType>
|
||||
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 <typename StarredObjType>
|
||||
void onUnstarred(Database::Session& session, typename StarredObjType::IdType id)
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
template <typename StarredObjType>
|
||||
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<std::chrono::seconds> 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<std::chrono::seconds> 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<Database::Listen>(user, track, Database::Scrobbler::Internal, listen.listenedAt)};
|
||||
dbListen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized);
|
||||
}
|
||||
auto dbListen{ session.create<Database::Listen>(user, track, Database::Scrobbler::Internal, listen.listenedAt) };
|
||||
dbListen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized);
|
||||
}
|
||||
|
||||
void
|
||||
InternalScrobbler::onStarred(Database::StarredArtistId starredArtistId)
|
||||
{
|
||||
::onStarred<Database::StarredArtist>(_db.getTLSSession(), starredArtistId);
|
||||
}
|
||||
void InternalScrobbler::onStarred(Database::StarredArtistId starredArtistId)
|
||||
{
|
||||
::onStarred<Database::StarredArtist>(_db.getTLSSession(), starredArtistId);
|
||||
}
|
||||
|
||||
void
|
||||
InternalScrobbler::onUnstarred(Database::StarredArtistId starredArtistId)
|
||||
{
|
||||
::onUnstarred<Database::StarredArtist>(_db.getTLSSession(), starredArtistId);
|
||||
}
|
||||
void InternalScrobbler::onUnstarred(Database::StarredArtistId starredArtistId)
|
||||
{
|
||||
::onUnstarred<Database::StarredArtist>(_db.getTLSSession(), starredArtistId);
|
||||
}
|
||||
|
||||
void
|
||||
InternalScrobbler::onStarred(Database::StarredReleaseId starredReleaseId)
|
||||
{
|
||||
::onStarred<Database::StarredRelease>(_db.getTLSSession(), starredReleaseId);
|
||||
}
|
||||
void InternalScrobbler::onStarred(Database::StarredReleaseId starredReleaseId)
|
||||
{
|
||||
::onStarred<Database::StarredRelease>(_db.getTLSSession(), starredReleaseId);
|
||||
}
|
||||
|
||||
void InternalScrobbler::onUnstarred(Database::StarredReleaseId starredReleaseId)
|
||||
{
|
||||
::onUnstarred<Database::StarredRelease>(_db.getTLSSession(), starredReleaseId);
|
||||
}
|
||||
void InternalScrobbler::onUnstarred(Database::StarredReleaseId starredReleaseId)
|
||||
{
|
||||
::onUnstarred<Database::StarredRelease>(_db.getTLSSession(), starredReleaseId);
|
||||
}
|
||||
|
||||
void
|
||||
InternalScrobbler::onStarred(Database::StarredTrackId starredTrackId)
|
||||
{
|
||||
::onStarred<Database::StarredTrack>(_db.getTLSSession(), starredTrackId);
|
||||
}
|
||||
void InternalScrobbler::onStarred(Database::StarredTrackId starredTrackId)
|
||||
{
|
||||
::onStarred<Database::StarredTrack>(_db.getTLSSession(), starredTrackId);
|
||||
}
|
||||
|
||||
void
|
||||
InternalScrobbler::onUnstarred(Database::StarredTrackId starredTrackId)
|
||||
{
|
||||
::onUnstarred<Database::StarredTrack>(_db.getTLSSession(), starredTrackId);
|
||||
}
|
||||
void InternalScrobbler::onUnstarred(Database::StarredTrackId starredTrackId)
|
||||
{
|
||||
::onUnstarred<Database::StarredTrack>(_db.getTLSSession(), starredTrackId);
|
||||
}
|
||||
} // Scrobbling
|
||||
|
||||
|
||||
@@ -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<std::chrono::seconds> duration) override;
|
||||
void addTimedListen(const TimedListen& listen) override;
|
||||
private:
|
||||
// IScrobbler
|
||||
void listenStarted(const Listen& listen) override;
|
||||
void listenFinished(const Listen& listen, std::optional<std::chrono::seconds> 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
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
namespace Scrobbling
|
||||
{
|
||||
class Exception : public LmsException
|
||||
{
|
||||
public:
|
||||
using LmsException::LmsException;
|
||||
};
|
||||
class Exception : public LmsException
|
||||
{
|
||||
public:
|
||||
using LmsException::LmsException;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/io_service.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#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<std::chrono::seconds> playedDuration = std::nullopt) = 0;
|
||||
// Scrobbling
|
||||
virtual void listenStarted(const Listen& listen) = 0;
|
||||
virtual void listenFinished(const Listen& listen, std::optional<std::chrono::seconds> playedDuration = std::nullopt) = 0;
|
||||
|
||||
virtual void addTimedListen(const TimedListen& listen) = 0;
|
||||
virtual void addTimedListen(const TimedListen& listen) = 0;
|
||||
|
||||
// Stats
|
||||
using ArtistContainer = Database::RangeResults<Database::ArtistId>;
|
||||
using ReleaseContainer = Database::RangeResults<Database::ReleaseId>;
|
||||
using TrackContainer = Database::RangeResults<Database::TrackId>;
|
||||
// From most recent to oldest
|
||||
virtual ArtistContainer getRecentArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range range) = 0;
|
||||
// Stats
|
||||
using ArtistContainer = Database::RangeResults<Database::ArtistId>;
|
||||
using ReleaseContainer = Database::RangeResults<Database::ReleaseId>;
|
||||
using TrackContainer = Database::RangeResults<Database::TrackId>;
|
||||
// From most recent to oldest
|
||||
virtual ArtistContainer getRecentArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range range) = 0;
|
||||
|
||||
virtual ReleaseContainer getRecentReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) = 0;
|
||||
virtual ReleaseContainer getRecentReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) = 0;
|
||||
|
||||
virtual TrackContainer getRecentTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) = 0;
|
||||
virtual TrackContainer getRecentTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) = 0;
|
||||
|
||||
// Top
|
||||
virtual ArtistContainer getTopArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range) = 0;
|
||||
// Top
|
||||
virtual ArtistContainer getTopArtists(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> linkType,
|
||||
Database::Range) = 0;
|
||||
|
||||
virtual ReleaseContainer getTopReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) = 0;
|
||||
virtual ReleaseContainer getTopReleases(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) = 0;
|
||||
|
||||
virtual TrackContainer getTopTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& clusterIds,
|
||||
Database::Range range) = 0;
|
||||
virtual TrackContainer getTopTracks(Database::UserId userId,
|
||||
const std::vector<Database::ClusterId>& 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<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> 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<Database::ClusterId>& clusterIds,
|
||||
std::optional<Database::TrackArtistLinkType> 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<Database::ClusterId>& 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<Database::ClusterId>& 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<Database::ClusterId>& 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<Database::ClusterId>& clusterIds, Database::Range range) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IScrobblingService> createScrobblingService(boost::asio::io_service& ioService, Database::Db& db);
|
||||
std::unique_ptr<IScrobblingService> createScrobblingService(boost::asio::io_service& ioService, Database::Db& db);
|
||||
|
||||
} // ns Scrobbling
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<Scrobbling::IScrobblingService>::get()->isStarred(user->getId(), release->getId()))
|
||||
albumNode.setAttribute("starred", reportedDummyStarredDate); // TODO report correct date/time
|
||||
if (const Wt::WDateTime dateTime{ Service<Scrobbling::IScrobblingService>::get()->getStarredDateTime(user->getId(), release->getId()) }; dateTime.isValid())
|
||||
albumNode.setAttribute("starred", StringUtils::toISO8601String(dateTime)); // TODO report correct date/time
|
||||
|
||||
return albumNode;
|
||||
}
|
||||
|
||||
@@ -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<Scrobbling::IScrobblingService>::get()->isStarred(user->getId(), artist->getId()))
|
||||
artistNode.setAttribute("starred", reportedDummyStarredDate); // TODO handle date/time
|
||||
if (const Wt::WDateTime dateTime{ Service<Scrobbling::IScrobblingService>::get()->getStarredDateTime(user->getId(), artist->getId()) }; dateTime.isValid())
|
||||
artistNode.setAttribute("starred", StringUtils::toISO8601String(dateTime));
|
||||
|
||||
return artistNode;
|
||||
}
|
||||
|
||||
@@ -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<Scrobbling::IScrobblingService>::get()->isStarred(user->getId(), track->getId()))
|
||||
trackResponse.setAttribute("starred", reportedDummyStarredDate); // TODO handle date/time
|
||||
if (const Wt::WDateTime dateTime{ Service<Scrobbling::IScrobblingService>::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") };
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user