Reworked internal namespaces
This commit is contained in:
@@ -19,7 +19,7 @@ target_include_directories(lmsscrobbling PRIVATE
|
||||
)
|
||||
|
||||
target_link_libraries(lmsscrobbling PRIVATE
|
||||
lmsutils
|
||||
lmscore
|
||||
)
|
||||
|
||||
target_link_libraries(lmsscrobbling PUBLIC
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
|
||||
#include "services/scrobbling/Listen.hpp"
|
||||
|
||||
namespace Database
|
||||
namespace lms::db
|
||||
{
|
||||
class Session;
|
||||
class TrackList;
|
||||
class User;
|
||||
}
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
class IScrobblingBackend
|
||||
{
|
||||
|
||||
@@ -26,20 +26,20 @@
|
||||
#include "database/Session.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "utils/ILogger.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
|
||||
#include "internal/InternalBackend.hpp"
|
||||
#include "listenbrainz/ListenBrainzBackend.hpp"
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
using namespace Database;
|
||||
using namespace db;
|
||||
|
||||
namespace
|
||||
{
|
||||
Database::Listen::StatsFindParameters convertToListenFindParameters(const ScrobblingService::FindParameters& params)
|
||||
db::Listen::StatsFindParameters convertToListenFindParameters(const ScrobblingService::FindParameters& params)
|
||||
{
|
||||
Database::Listen::StatsFindParameters listenFindParams;
|
||||
db::Listen::StatsFindParameters listenFindParams;
|
||||
listenFindParams.setUser(params.user);
|
||||
listenFindParams.setClusters(params.clusters);
|
||||
listenFindParams.setRange(params.range);
|
||||
@@ -49,9 +49,9 @@ namespace Scrobbling
|
||||
return listenFindParams;
|
||||
}
|
||||
|
||||
Database::Listen::ArtistStatsFindParameters convertToListenFindParameters(const ScrobblingService::ArtistFindParameters& params)
|
||||
db::Listen::ArtistStatsFindParameters convertToListenFindParameters(const ScrobblingService::ArtistFindParameters& params)
|
||||
{
|
||||
return Database::Listen::ArtistStatsFindParameters{ convertToListenFindParameters(static_cast<const ScrobblingService::FindParameters&>(params)), params.linkType };
|
||||
return db::Listen::ArtistStatsFindParameters{ convertToListenFindParameters(static_cast<const ScrobblingService::FindParameters&>(params)), params.linkType };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Scrobbling
|
||||
{
|
||||
LMS_LOG(SCROBBLING, INFO, "Starting service...");
|
||||
_scrobblingBackends.emplace(ScrobblingBackend::Internal, std::make_unique<InternalBackend>(_db));
|
||||
_scrobblingBackends.emplace(ScrobblingBackend::ListenBrainz, std::make_unique<ListenBrainz::ListenBrainzBackend>(ioContext, _db));
|
||||
_scrobblingBackends.emplace(ScrobblingBackend::ListenBrainz, std::make_unique<listenBrainz::ListenBrainzBackend>(ioContext, _db));
|
||||
LMS_LOG(SCROBBLING, INFO, "Service started!");
|
||||
}
|
||||
|
||||
@@ -112,13 +112,13 @@ namespace Scrobbling
|
||||
if (!backend)
|
||||
return res;
|
||||
|
||||
Database::Listen::ArtistStatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
db::Listen::ArtistStatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
listenFindParams.setScrobblingBackend(backend);
|
||||
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
res = Database::Listen::getRecentArtists(session, listenFindParams);
|
||||
res = db::Listen::getRecentArtists(session, listenFindParams);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -130,13 +130,13 @@ namespace Scrobbling
|
||||
if (!backend)
|
||||
return res;
|
||||
|
||||
Database::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
db::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
listenFindParams.setScrobblingBackend(backend);
|
||||
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
res = Database::Listen::getRecentReleases(session, listenFindParams);
|
||||
res = db::Listen::getRecentReleases(session, listenFindParams);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -148,31 +148,31 @@ namespace Scrobbling
|
||||
if (!backend)
|
||||
return res;
|
||||
|
||||
Database::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
db::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
listenFindParams.setScrobblingBackend(backend);
|
||||
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
res = Database::Listen::getRecentTracks(session, listenFindParams);
|
||||
res = db::Listen::getRecentTracks(session, listenFindParams);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t ScrobblingService::getCount(Database::UserId userId, Database::ReleaseId releaseId)
|
||||
std::size_t ScrobblingService::getCount(db::UserId userId, db::ReleaseId releaseId)
|
||||
{
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
return Database::Listen::getCount(session, userId, releaseId);
|
||||
return db::Listen::getCount(session, userId, releaseId);
|
||||
}
|
||||
|
||||
std::size_t ScrobblingService::getCount(Database::UserId userId, Database::TrackId trackId)
|
||||
std::size_t ScrobblingService::getCount(db::UserId userId, db::TrackId trackId)
|
||||
{
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
return Database::Listen::getCount(session, userId, trackId);
|
||||
return db::Listen::getCount(session, userId, trackId);
|
||||
}
|
||||
|
||||
Wt::WDateTime ScrobblingService::getLastListenDateTime(Database::UserId userId, Database::ReleaseId releaseId)
|
||||
Wt::WDateTime ScrobblingService::getLastListenDateTime(db::UserId userId, db::ReleaseId releaseId)
|
||||
{
|
||||
const auto backend{ getUserBackend(userId) };
|
||||
if (!backend)
|
||||
@@ -181,11 +181,11 @@ namespace Scrobbling
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
const Database::Listen::pointer listen{ Database::Listen::getMostRecentListen(session, userId, *backend, releaseId) };
|
||||
const db::Listen::pointer listen{ db::Listen::getMostRecentListen(session, userId, *backend, releaseId) };
|
||||
return listen ? listen->getDateTime() : Wt::WDateTime{};
|
||||
}
|
||||
|
||||
Wt::WDateTime ScrobblingService::getLastListenDateTime(Database::UserId userId, Database::TrackId trackId)
|
||||
Wt::WDateTime ScrobblingService::getLastListenDateTime(db::UserId userId, db::TrackId trackId)
|
||||
{
|
||||
const auto backend{ getUserBackend(userId) };
|
||||
if (!backend)
|
||||
@@ -194,7 +194,7 @@ namespace Scrobbling
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
const Database::Listen::pointer listen{ Database::Listen::getMostRecentListen(session, userId, *backend, trackId) };
|
||||
const db::Listen::pointer listen{ db::Listen::getMostRecentListen(session, userId, *backend, trackId) };
|
||||
return listen ? listen->getDateTime() : Wt::WDateTime{};
|
||||
}
|
||||
|
||||
@@ -207,13 +207,13 @@ namespace Scrobbling
|
||||
if (!backend)
|
||||
return res;
|
||||
|
||||
Database::Listen::ArtistStatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
db::Listen::ArtistStatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
listenFindParams.setScrobblingBackend(backend);
|
||||
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
res = Database::Listen::getTopArtists(session, listenFindParams);
|
||||
res = db::Listen::getTopArtists(session, listenFindParams);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -225,13 +225,13 @@ namespace Scrobbling
|
||||
if (!backend)
|
||||
return res;
|
||||
|
||||
Database::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
db::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
listenFindParams.setScrobblingBackend(backend);
|
||||
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
res = Database::Listen::getTopReleases(session, listenFindParams);
|
||||
res = db::Listen::getTopReleases(session, listenFindParams);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -243,13 +243,13 @@ namespace Scrobbling
|
||||
if (!backend)
|
||||
return res;
|
||||
|
||||
Database::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
db::Listen::StatsFindParameters listenFindParams{ convertToListenFindParameters(params) };
|
||||
listenFindParams.setScrobblingBackend(backend);
|
||||
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
res = Database::Listen::getTopTracks(session, listenFindParams);
|
||||
res = db::Listen::getTopTracks(session, listenFindParams);
|
||||
return res;
|
||||
}
|
||||
} // ns Scrobbling
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
#include "services/scrobbling/IScrobblingService.hpp"
|
||||
#include "IScrobblingBackend.hpp"
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
class ScrobblingService : public IScrobblingService
|
||||
{
|
||||
public:
|
||||
ScrobblingService(boost::asio::io_context& ioContext, Database::Db& db);
|
||||
ScrobblingService(boost::asio::io_context& ioContext, db::Db& db);
|
||||
~ScrobblingService();
|
||||
|
||||
private:
|
||||
@@ -43,20 +43,20 @@ namespace Scrobbling
|
||||
ReleaseContainer getRecentReleases(const FindParameters& params) override;
|
||||
TrackContainer getRecentTracks(const FindParameters& params) override;
|
||||
|
||||
std::size_t getCount(Database::UserId userId, Database::ReleaseId releaseId) override;
|
||||
std::size_t getCount(Database::UserId userId, Database::TrackId trackId) override;
|
||||
std::size_t getCount(db::UserId userId, db::ReleaseId releaseId) override;
|
||||
std::size_t getCount(db::UserId userId, db::TrackId trackId) override;
|
||||
|
||||
Wt::WDateTime getLastListenDateTime(Database::UserId userId, Database::ReleaseId releaseId) override;
|
||||
Wt::WDateTime getLastListenDateTime(Database::UserId userId, Database::TrackId trackId) override;
|
||||
Wt::WDateTime getLastListenDateTime(db::UserId userId, db::ReleaseId releaseId) override;
|
||||
Wt::WDateTime getLastListenDateTime(db::UserId userId, db::TrackId trackId) override;
|
||||
|
||||
ArtistContainer getTopArtists(const ArtistFindParameters& params) override;
|
||||
ReleaseContainer getTopReleases(const FindParameters& params) override;
|
||||
TrackContainer getTopTracks(const FindParameters& params) override;
|
||||
|
||||
std::optional<Database::ScrobblingBackend> getUserBackend(Database::UserId userId);
|
||||
std::optional<db::ScrobblingBackend> getUserBackend(db::UserId userId);
|
||||
|
||||
Database::Db& _db;
|
||||
std::unordered_map<Database::ScrobblingBackend, std::unique_ptr<IScrobblingBackend>> _scrobblingBackends;
|
||||
db::Db& _db;
|
||||
std::unordered_map<db::ScrobblingBackend, std::unique_ptr<IScrobblingBackend>> _scrobblingBackends;
|
||||
};
|
||||
|
||||
} // ns Scrobbling
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
#include "database/Track.hpp"
|
||||
#include "database/User.hpp"
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
InternalBackend::InternalBackend(Database::Db& db)
|
||||
InternalBackend::InternalBackend(db::Db& db)
|
||||
: _db{ db }
|
||||
{}
|
||||
|
||||
@@ -47,22 +47,22 @@ namespace Scrobbling
|
||||
|
||||
void InternalBackend::addTimedListen(const TimedListen& listen)
|
||||
{
|
||||
Database::Session& session{ _db.getTLSSession() };
|
||||
db::Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
|
||||
if (Database::Listen::find(session, listen.userId, listen.trackId, Database::ScrobblingBackend::Internal, listen.listenedAt))
|
||||
if (db::Listen::find(session, listen.userId, listen.trackId, db::ScrobblingBackend::Internal, listen.listenedAt))
|
||||
return;
|
||||
|
||||
const Database::User::pointer user{ Database::User::find(session, listen.userId) };
|
||||
const db::User::pointer user{ db::User::find(session, listen.userId) };
|
||||
if (!user)
|
||||
return;
|
||||
|
||||
const Database::Track::pointer track{ Database::Track::find(session, listen.trackId) };
|
||||
const db::Track::pointer track{ db::Track::find(session, listen.trackId) };
|
||||
if (!track)
|
||||
return;
|
||||
|
||||
auto dbListen{ session.create<Database::Listen>(user, track, Database::ScrobblingBackend::Internal, listen.listenedAt) };
|
||||
dbListen.modify()->setSyncState(Database::SyncState::Synchronized);
|
||||
auto dbListen{ session.create<db::Listen>(user, track, db::ScrobblingBackend::Internal, listen.listenedAt) };
|
||||
dbListen.modify()->setSyncState(db::SyncState::Synchronized);
|
||||
}
|
||||
} // Scrobbling
|
||||
|
||||
|
||||
@@ -21,24 +21,24 @@
|
||||
|
||||
#include "IScrobblingBackend.hpp"
|
||||
|
||||
namespace Database
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
}
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
class InternalBackend final : public IScrobblingBackend
|
||||
{
|
||||
public:
|
||||
InternalBackend(Database::Db& db);
|
||||
InternalBackend(db::Db& db);
|
||||
|
||||
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;
|
||||
|
||||
Database::Db& _db;
|
||||
db::Db& _db;
|
||||
};
|
||||
} // Scrobbling
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
#include "services/scrobbling/Exception.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
class Exception : public Scrobbling::Exception
|
||||
class Exception : public scrobbling::Exception
|
||||
{
|
||||
public:
|
||||
using Scrobbling::Exception::Exception;
|
||||
using scrobbling::Exception::Exception;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
#include "database/Db.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "utils/IConfig.hpp"
|
||||
#include "utils/http/IClient.hpp"
|
||||
#include "utils/ILogger.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/http/IClient.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
using namespace Database;
|
||||
using namespace db;
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -53,8 +53,8 @@ namespace Scrobbling::ListenBrainz
|
||||
ListenBrainzBackend::ListenBrainzBackend(boost::asio::io_context& ioContext, Db& db)
|
||||
: _ioContext{ ioContext }
|
||||
, _db{ db }
|
||||
, _baseAPIUrl{ Service<IConfig>::get()->getString("listenbrainz-api-base-url", "https://api.listenbrainz.org") }
|
||||
, _client{ Http::createClient(_ioContext, _baseAPIUrl) }
|
||||
, _baseAPIUrl{ core::Service<core::IConfig>::get()->getString("listenbrainz-api-base-url", "https://api.listenbrainz.org") }
|
||||
, _client{ core::http::createClient(_ioContext, _baseAPIUrl) }
|
||||
, _listensSynchronizer{ _ioContext, db, *_client }
|
||||
{
|
||||
LOG(INFO, "Starting ListenBrainz backend... API endpoint = '" << _baseAPIUrl << "'");
|
||||
@@ -83,5 +83,5 @@ namespace Scrobbling::ListenBrainz
|
||||
{
|
||||
_listensSynchronizer.enqueListen(timedListen);
|
||||
}
|
||||
} // namespace Scrobbling::ListenBrainz
|
||||
} // namespace lms::scrobbling::listenBrainz
|
||||
|
||||
|
||||
@@ -26,17 +26,17 @@
|
||||
#include "IScrobblingBackend.hpp"
|
||||
#include "ListensSynchronizer.hpp"
|
||||
|
||||
namespace Database
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
}
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
class ListenBrainzBackend final : public IScrobblingBackend
|
||||
{
|
||||
public:
|
||||
ListenBrainzBackend(boost::asio::io_context& ioContext, Database::Db& db);
|
||||
ListenBrainzBackend(boost::asio::io_context& ioContext, db::Db& db);
|
||||
~ListenBrainzBackend() override;
|
||||
|
||||
private:
|
||||
@@ -51,10 +51,10 @@ namespace Scrobbling::ListenBrainz
|
||||
void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint);
|
||||
|
||||
boost::asio::io_context& _ioContext;
|
||||
Database::Db& _db;
|
||||
db::Db& _db;
|
||||
std::string _baseAPIUrl;
|
||||
std::unique_ptr<Http::IClient> _client;
|
||||
std::unique_ptr<core::http::IClient> _client;
|
||||
ListensSynchronizer _listensSynchronizer;
|
||||
};
|
||||
} // Scrobbling::ListenBrainz
|
||||
} // scrobbling::ListenBrainz
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "ListenTypes.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const Listen& listen)
|
||||
@@ -38,4 +38,4 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
return os;
|
||||
}
|
||||
} // Scrobbling::ListenBrainz
|
||||
} // scrobbling::ListenBrainz
|
||||
|
||||
@@ -23,21 +23,21 @@
|
||||
#include <ostream>
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#include "utils/UUID.hpp"
|
||||
#include "core/UUID.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
struct Listen
|
||||
{
|
||||
std::string trackName;
|
||||
std::string releaseName;
|
||||
std::string artistName;
|
||||
std::optional<UUID> recordingMBID;
|
||||
std::optional<UUID> trackMBID;
|
||||
std::optional<UUID> releaseMBID;
|
||||
std::optional<core::UUID> recordingMBID;
|
||||
std::optional<core::UUID> trackMBID;
|
||||
std::optional<core::UUID> releaseMBID;
|
||||
std::optional<unsigned> trackNumber;
|
||||
Wt::WDateTime listenedAt;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Listen& listen);
|
||||
} // Scrobbling::ListenBrainz
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
#include <Wt/Json/Value.h>
|
||||
#include <Wt/Json/Parser.h>
|
||||
|
||||
#include "utils/ILogger.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
namespace
|
||||
{
|
||||
@@ -50,9 +50,9 @@ namespace Scrobbling::ListenBrainz
|
||||
if (metadata.type("additional_info") == Wt::Json::Type::Object)
|
||||
{
|
||||
const Wt::Json::Object& additionalInfo = metadata.get("additional_info");
|
||||
listen.trackMBID = UUID::fromString(additionalInfo.get("track_mbid").orIfNull(""));
|
||||
listen.recordingMBID = UUID::fromString(additionalInfo.get("recording_mbid").orIfNull(""));
|
||||
listen.releaseMBID = UUID::fromString(additionalInfo.get("release_mbid").orIfNull(""));
|
||||
listen.trackMBID = core::UUID::fromString(additionalInfo.get("track_mbid").orIfNull(""));
|
||||
listen.recordingMBID = core::UUID::fromString(additionalInfo.get("recording_mbid").orIfNull(""));
|
||||
listen.releaseMBID = core::UUID::fromString(additionalInfo.get("release_mbid").orIfNull(""));
|
||||
|
||||
// tracknumber should be an integer but some players encode as strings
|
||||
int trackNumber{ additionalInfo.get("tracknumber").toNumber().orIfNull(-1) };
|
||||
@@ -102,4 +102,4 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
return result;
|
||||
}
|
||||
} // Scrobbling::ListenBrainz
|
||||
} // scrobbling::ListenBrainz
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "ListenTypes.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
class ListensParser
|
||||
{
|
||||
@@ -36,4 +36,4 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
static Result parse(std::string_view msgBody);
|
||||
};
|
||||
} // Scrobbling::ListenBrainz
|
||||
} // scrobbling::ListenBrainz
|
||||
|
||||
@@ -33,28 +33,28 @@
|
||||
#include "database/Track.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "services/scrobbling/Exception.hpp"
|
||||
#include "utils/IConfig.hpp"
|
||||
#include "utils/http/IClient.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/http/IClient.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "ListensParser.hpp"
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::optional<Wt::Json::Object> listenToJsonPayload(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint)
|
||||
std::optional<Wt::Json::Object> listenToJsonPayload(db::Session& session, const scrobbling::Listen& listen, const Wt::WDateTime& timePoint)
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
const Database::Track::pointer track{ Database::Track::find(session, listen.trackId) };
|
||||
const db::Track::pointer track{ db::Track::find(session, listen.trackId) };
|
||||
if (!track)
|
||||
return std::nullopt;
|
||||
|
||||
auto artists{ track->getArtists({Database::TrackArtistLinkType::Artist}) };
|
||||
auto artists{ track->getArtists({db::TrackArtistLinkType::Artist}) };
|
||||
if (artists.empty())
|
||||
artists = track->getArtists({ Database::TrackArtistLinkType::ReleaseArtist });
|
||||
artists = track->getArtists({ db::TrackArtistLinkType::ReleaseArtist });
|
||||
|
||||
if (artists.empty())
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
{
|
||||
Wt::Json::Array artistMBIDs;
|
||||
for (const Database::Artist::pointer& artist : artists)
|
||||
for (const db::Artist::pointer& artist : artists)
|
||||
{
|
||||
if (auto MBID{ artist->getMBID() })
|
||||
artistMBIDs.push_back(Wt::Json::Value{ std::string {MBID->getAsString()} });
|
||||
@@ -109,7 +109,7 @@ namespace Scrobbling::ListenBrainz
|
||||
return payload;
|
||||
}
|
||||
|
||||
std::string listenToJsonString(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint, std::string_view listenType)
|
||||
std::string listenToJsonString(db::Session& session, const scrobbling::Listen& listen, const Wt::WDateTime& timePoint, std::string_view listenType)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
@@ -142,9 +142,9 @@ namespace Scrobbling::ListenBrainz
|
||||
}
|
||||
}
|
||||
|
||||
Database::TrackId tryGetMatchingTrack(Database::Session& session, const Listen& listen)
|
||||
db::TrackId tryGetMatchingTrack(db::Session& session, const Listen& listen)
|
||||
{
|
||||
using namespace Database;
|
||||
using namespace db;
|
||||
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
@@ -209,12 +209,12 @@ namespace Scrobbling::ListenBrainz
|
||||
}
|
||||
}
|
||||
|
||||
ListensSynchronizer::ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, Http::IClient& client)
|
||||
ListensSynchronizer::ListensSynchronizer(boost::asio::io_context& ioContext, db::Db& db, core::http::IClient& client)
|
||||
: _ioContext{ ioContext }
|
||||
, _db{ db }
|
||||
, _client{ client }
|
||||
, _maxSyncListenCount{ Service<IConfig>::get()->getULong("listenbrainz-max-sync-listen-count", 1000) }
|
||||
, _syncListensPeriod{ Service<IConfig>::get()->getULong("listenbrainz-sync-listens-period-hours", 1) }
|
||||
, _maxSyncListenCount{ core::Service<core::IConfig>::get()->getULong("listenbrainz-max-sync-listen-count", 1000) }
|
||||
, _syncListensPeriod{ core::Service<core::IConfig>::get()->getULong("listenbrainz-sync-listens-period-hours", 1) }
|
||||
{
|
||||
LOG(INFO, "Starting Listens synchronizer, maxSyncListenCount = " << _maxSyncListenCount << ", _syncListensPeriod = " << _syncListensPeriod.count() << " hours");
|
||||
|
||||
@@ -227,28 +227,28 @@ namespace Scrobbling::ListenBrainz
|
||||
enqueListen(listen, listen.listenedAt);
|
||||
}
|
||||
|
||||
void ListensSynchronizer::enqueListenNow(const Scrobbling::Listen& listen)
|
||||
void ListensSynchronizer::enqueListenNow(const scrobbling::Listen& listen)
|
||||
{
|
||||
enqueListen(listen, {});
|
||||
}
|
||||
|
||||
void ListensSynchronizer::enqueListen(const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint)
|
||||
void ListensSynchronizer::enqueListen(const scrobbling::Listen& listen, const Wt::WDateTime& timePoint)
|
||||
{
|
||||
Http::ClientPOSTRequestParameters request;
|
||||
core::http::ClientPOSTRequestParameters request;
|
||||
request.relativeUrl = "/1/submit-listens";
|
||||
|
||||
if (timePoint.isValid())
|
||||
{
|
||||
const TimedListen timedListen{ listen, timePoint };
|
||||
// We want the listen to be sent again later in case of failure, so we just save it as pending send
|
||||
saveListen(timedListen, Database::SyncState::PendingAdd);
|
||||
saveListen(timedListen, db::SyncState::PendingAdd);
|
||||
|
||||
request.priority = Http::ClientRequestParameters::Priority::Normal;
|
||||
request.priority = core::http::ClientRequestParameters::Priority::Normal;
|
||||
request.onSuccessFunc = [this, timedListen](std::string_view)
|
||||
{
|
||||
_strand.dispatch([this, timedListen]
|
||||
{
|
||||
if (saveListen(timedListen, Database::SyncState::Synchronized))
|
||||
if (saveListen(timedListen, db::SyncState::Synchronized))
|
||||
{
|
||||
UserContext& context{ getUserContext(timedListen.userId) };
|
||||
if (context.listenCount)
|
||||
@@ -261,7 +261,7 @@ namespace Scrobbling::ListenBrainz
|
||||
else
|
||||
{
|
||||
// We want "listen now" to appear as soon as possible
|
||||
request.priority = Http::ClientRequestParameters::Priority::High;
|
||||
request.priority = core::http::ClientRequestParameters::Priority::High;
|
||||
// don't retry on failure
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ namespace Scrobbling::ListenBrainz
|
||||
return;
|
||||
}
|
||||
|
||||
const std::optional<UUID> listenBrainzToken{ Utils::getListenBrainzToken(_db.getTLSSession(), listen.userId) };
|
||||
const std::optional<core::UUID> listenBrainzToken{ utils::getListenBrainzToken(_db.getTLSSession(), listen.userId) };
|
||||
if (!listenBrainzToken)
|
||||
{
|
||||
LOG(DEBUG, "No listenbrainz token found: skipping");
|
||||
@@ -285,14 +285,14 @@ namespace Scrobbling::ListenBrainz
|
||||
_client.sendPOSTRequest(std::move(request));
|
||||
}
|
||||
|
||||
bool ListensSynchronizer::saveListen(const TimedListen& listen, Database::SyncState scrobblingState)
|
||||
bool ListensSynchronizer::saveListen(const TimedListen& listen, db::SyncState scrobblingState)
|
||||
{
|
||||
using namespace Database;
|
||||
using namespace db;
|
||||
|
||||
Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createWriteTransaction() }; // TODO: unique only if needed
|
||||
|
||||
Database::Listen::pointer dbListen{ Database::Listen::find(session, listen.userId, listen.trackId, Database::ScrobblingBackend::ListenBrainz, listen.listenedAt) };
|
||||
db::Listen::pointer dbListen{ db::Listen::find(session, listen.userId, listen.trackId, db::ScrobblingBackend::ListenBrainz, listen.listenedAt) };
|
||||
if (!dbListen)
|
||||
{
|
||||
const User::pointer user{ User::find(session, listen.userId) };
|
||||
@@ -303,7 +303,7 @@ namespace Scrobbling::ListenBrainz
|
||||
if (!track)
|
||||
return false;
|
||||
|
||||
dbListen = session.create<Database::Listen>(user, track, Database::ScrobblingBackend::ListenBrainz, listen.listenedAt);
|
||||
dbListen = session.create<db::Listen>(user, track, db::ScrobblingBackend::ListenBrainz, listen.listenedAt);
|
||||
dbListen.modify()->setSyncState(scrobblingState);
|
||||
|
||||
LOG(DEBUG, "LISTEN CREATED for user " << user->getLoginName() << ", track '" << track->getName() << "' AT " << listen.listenedAt.toString());
|
||||
@@ -323,21 +323,21 @@ namespace Scrobbling::ListenBrainz
|
||||
std::vector<TimedListen> pendingListens;
|
||||
|
||||
{
|
||||
Database::Session& session{ _db.getTLSSession() };
|
||||
db::Session& session{ _db.getTLSSession() };
|
||||
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
|
||||
Database::Listen::FindParameters params;
|
||||
params.setScrobblingBackend(Database::ScrobblingBackend::ListenBrainz)
|
||||
.setSyncState(Database::SyncState::PendingAdd)
|
||||
.setRange(Database::Range{ 0, 100 }); // don't flood too much?
|
||||
db::Listen::FindParameters params;
|
||||
params.setScrobblingBackend(db::ScrobblingBackend::ListenBrainz)
|
||||
.setSyncState(db::SyncState::PendingAdd)
|
||||
.setRange(db::Range{ 0, 100 }); // don't flood too much?
|
||||
|
||||
const Database::RangeResults results{ Database::Listen::find(session, params) };
|
||||
const db::RangeResults results{ db::Listen::find(session, params) };
|
||||
pendingListens.reserve(results.results.size());
|
||||
|
||||
for (Database::ListenId listenId : results.results)
|
||||
for (db::ListenId listenId : results.results)
|
||||
{
|
||||
const Database::Listen::pointer listen{ Database::Listen::find(session, listenId) };
|
||||
const db::Listen::pointer listen{ db::Listen::find(session, listenId) };
|
||||
|
||||
TimedListen timedListen;
|
||||
timedListen.listenedAt = listen->getDateTime();
|
||||
@@ -354,7 +354,7 @@ namespace Scrobbling::ListenBrainz
|
||||
enqueListen(pendingListen);
|
||||
}
|
||||
|
||||
ListensSynchronizer::UserContext& ListensSynchronizer::getUserContext(Database::UserId userId)
|
||||
ListensSynchronizer::UserContext& ListensSynchronizer::getUserContext(db::UserId userId)
|
||||
{
|
||||
assert(_strand.running_in_this_thread());
|
||||
|
||||
@@ -406,14 +406,14 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
enquePendingListens();
|
||||
|
||||
Database::RangeResults<Database::UserId> userIds;
|
||||
db::RangeResults<db::UserId> userIds;
|
||||
{
|
||||
Database::Session& session{ _db.getTLSSession() };
|
||||
db::Session& session{ _db.getTLSSession() };
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
userIds = Database::User::find(_db.getTLSSession(), Database::User::FindParameters{}.setScrobblingBackend(Database::ScrobblingBackend::ListenBrainz));
|
||||
userIds = db::User::find(_db.getTLSSession(), db::User::FindParameters{}.setScrobblingBackend(db::ScrobblingBackend::ListenBrainz));
|
||||
}
|
||||
|
||||
for (const Database::UserId userId : userIds.results)
|
||||
for (const db::UserId userId : userIds.results)
|
||||
startSync(getUserContext(userId));
|
||||
|
||||
if (!isSyncing())
|
||||
@@ -448,20 +448,20 @@ namespace Scrobbling::ListenBrainz
|
||||
{
|
||||
assert(context.listenBrainzUserName.empty());
|
||||
|
||||
const std::optional<UUID> listenBrainzToken{ Utils::getListenBrainzToken(_db.getTLSSession(), context.userId) };
|
||||
const std::optional<core::UUID> listenBrainzToken{ utils::getListenBrainzToken(_db.getTLSSession(), context.userId) };
|
||||
if (!listenBrainzToken)
|
||||
{
|
||||
onSyncEnded(context);
|
||||
return;
|
||||
}
|
||||
|
||||
Http::ClientGETRequestParameters request;
|
||||
request.priority = Http::ClientRequestParameters::Priority::Low;
|
||||
core::http::ClientGETRequestParameters request;
|
||||
request.priority = core::http::ClientRequestParameters::Priority::Low;
|
||||
request.relativeUrl = "/1/validate-token";
|
||||
request.headers = { {"Authorization", "Token " + std::string {listenBrainzToken->getAsString()}} };
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody)
|
||||
{
|
||||
context.listenBrainzUserName = Utils::parseValidateToken(msgBody);
|
||||
context.listenBrainzUserName = utils::parseValidateToken(msgBody);
|
||||
if (context.listenBrainzUserName.empty())
|
||||
{
|
||||
onSyncEnded(context);
|
||||
@@ -481,9 +481,9 @@ namespace Scrobbling::ListenBrainz
|
||||
{
|
||||
assert(!context.listenBrainzUserName.empty());
|
||||
|
||||
Http::ClientGETRequestParameters request;
|
||||
core::http::ClientGETRequestParameters request;
|
||||
request.relativeUrl = "/1/user/" + std::string{ context.listenBrainzUserName } + "/listen-count";
|
||||
request.priority = Http::ClientRequestParameters::Priority::Low;
|
||||
request.priority = core::http::ClientRequestParameters::Priority::Low;
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody)
|
||||
{
|
||||
const auto listenCount{ parseListenCount(msgBody) };
|
||||
@@ -517,9 +517,9 @@ namespace Scrobbling::ListenBrainz
|
||||
{
|
||||
assert(!context.listenBrainzUserName.empty());
|
||||
|
||||
Http::ClientGETRequestParameters request;
|
||||
core::http::ClientGETRequestParameters request;
|
||||
request.relativeUrl = "/1/user/" + context.listenBrainzUserName + "/listens?max_ts=" + std::to_string(context.maxDateTime.toTime_t());
|
||||
request.priority = Http::ClientRequestParameters::Priority::Low;
|
||||
request.priority = core::http::ClientRequestParameters::Priority::Low;
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody)
|
||||
{
|
||||
processGetListensResponse(msgBody, context);
|
||||
@@ -541,7 +541,7 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
void ListensSynchronizer::processGetListensResponse(std::string_view msgBody, UserContext& context)
|
||||
{
|
||||
Database::Session& session{ _db.getTLSSession() };
|
||||
db::Session& session{ _db.getTLSSession() };
|
||||
|
||||
context.maxDateTime = {}; // invalidate to break in case no more listens are fetched
|
||||
ListensParser::Result result{ ListensParser::parse(msgBody) };
|
||||
@@ -559,14 +559,14 @@ namespace Scrobbling::ListenBrainz
|
||||
if (!context.maxDateTime.isValid() || context.maxDateTime > parsedListen.listenedAt)
|
||||
context.maxDateTime = parsedListen.listenedAt;
|
||||
|
||||
if (const Database::TrackId trackId{ tryGetMatchingTrack(session, parsedListen) }; trackId.isValid())
|
||||
if (const db::TrackId trackId{ tryGetMatchingTrack(session, parsedListen) }; trackId.isValid())
|
||||
{
|
||||
context.matchedListenCount++;
|
||||
|
||||
const Scrobbling::TimedListen listen{ {context.userId, trackId}, parsedListen.listenedAt };
|
||||
if (saveListen(listen, Database::SyncState::Synchronized))
|
||||
const scrobbling::TimedListen listen{ {context.userId, trackId}, parsedListen.listenedAt };
|
||||
if (saveListen(listen, db::SyncState::Synchronized))
|
||||
context.importedListenCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Scrobbling::ListenBrainz
|
||||
} // namespace lms::scrobbling::listenBrainz
|
||||
|
||||
@@ -30,42 +30,44 @@
|
||||
|
||||
#include "services/scrobbling/Listen.hpp"
|
||||
|
||||
namespace Database
|
||||
namespace lms
|
||||
{
|
||||
class Db;
|
||||
namespace core::http
|
||||
{
|
||||
class IClient;
|
||||
}
|
||||
namespace db
|
||||
{
|
||||
class Db;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Http
|
||||
{
|
||||
class IClient;
|
||||
}
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
namespace lms::scrobbling::listenBrainz
|
||||
{
|
||||
class ListensSynchronizer
|
||||
{
|
||||
public:
|
||||
ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, Http::IClient& client);
|
||||
ListensSynchronizer(boost::asio::io_context& ioContext, db::Db& db, core::http::IClient& client);
|
||||
|
||||
void enqueListen(const TimedListen& listen);
|
||||
void enqueListenNow(const Listen& listen);
|
||||
|
||||
private:
|
||||
void enqueListen(const Listen& listen, const Wt::WDateTime& timePoint);
|
||||
bool saveListen(const TimedListen& listen, Database::SyncState scrobblinState);
|
||||
bool saveListen(const TimedListen& listen, db::SyncState scrobblinState);
|
||||
|
||||
void enquePendingListens();
|
||||
|
||||
struct UserContext
|
||||
{
|
||||
UserContext(Database::UserId id) : userId {id} {}
|
||||
UserContext(db::UserId id) : userId {id} {}
|
||||
|
||||
UserContext(const UserContext&) = delete;
|
||||
UserContext(UserContext&&) = delete;
|
||||
UserContext& operator=(const UserContext&) = delete;
|
||||
UserContext& operator=(UserContext&&) = delete;
|
||||
|
||||
const Database::UserId userId;
|
||||
const db::UserId userId;
|
||||
bool syncing {};
|
||||
std::optional<std::size_t> listenCount {};
|
||||
|
||||
@@ -77,7 +79,7 @@ namespace Scrobbling::ListenBrainz
|
||||
std::size_t importedListenCount{};
|
||||
};
|
||||
|
||||
UserContext& getUserContext(Database::UserId userId);
|
||||
UserContext& getUserContext(db::UserId userId);
|
||||
bool isSyncing() const;
|
||||
void scheduleSync(std::chrono::seconds fromNow);
|
||||
void startSync();
|
||||
@@ -90,14 +92,14 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
boost::asio::io_context& _ioContext;
|
||||
boost::asio::io_context::strand _strand {_ioContext};
|
||||
Database::Db& _db;
|
||||
db::Db& _db;
|
||||
boost::asio::steady_timer _syncTimer {_ioContext};
|
||||
Http::IClient& _client;
|
||||
core::http::IClient& _client;
|
||||
|
||||
std::unordered_map<Database::UserId, UserContext> _userContexts;
|
||||
std::unordered_map<db::UserId, UserContext> _userContexts;
|
||||
|
||||
const std::size_t _maxSyncListenCount;
|
||||
const std::chrono::hours _syncListensPeriod;
|
||||
};
|
||||
} // Scrobbling::ListenBrainz
|
||||
} // scrobbling::ListenBrainz
|
||||
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
#include "database/Session.hpp"
|
||||
#include "database/User.hpp"
|
||||
|
||||
namespace Scrobbling::ListenBrainz::Utils
|
||||
namespace lms::scrobbling::listenBrainz::utils
|
||||
{
|
||||
std::optional<UUID> getListenBrainzToken(Database::Session& session, Database::UserId userId)
|
||||
std::optional<core::UUID> getListenBrainzToken(db::Session& session, db::UserId userId)
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
const Database::User::pointer user{ Database::User::find(session, userId) };
|
||||
const db::User::pointer user{ db::User::find(session, userId) };
|
||||
if (!user)
|
||||
return std::nullopt;
|
||||
|
||||
|
||||
@@ -20,18 +20,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "database/UserId.hpp"
|
||||
#include "utils/ILogger.hpp"
|
||||
#include "utils/UUID.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/UUID.hpp"
|
||||
|
||||
#define LOG(sev, message) LMS_LOG(SCROBBLING, sev, "[listenbrainz] " << message)
|
||||
|
||||
namespace Database
|
||||
namespace lms::db
|
||||
{
|
||||
class Session;
|
||||
}
|
||||
|
||||
namespace Scrobbling::ListenBrainz::Utils
|
||||
namespace lms::scrobbling::listenBrainz::utils
|
||||
{
|
||||
std::optional<UUID> getListenBrainzToken(Database::Session& session, Database::UserId userId);
|
||||
std::optional<core::UUID> getListenBrainzToken(db::Session& session, db::UserId userId);
|
||||
std::string parseValidateToken(std::string_view msgBody);
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils/Exception.hpp"
|
||||
#include "core/Exception.hpp"
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
class Exception : public LmsException
|
||||
class Exception : public core::LmsException
|
||||
{
|
||||
public:
|
||||
using LmsException::LmsException;
|
||||
|
||||
@@ -33,12 +33,12 @@
|
||||
#include "database/TrackId.hpp"
|
||||
#include "database/Types.hpp"
|
||||
|
||||
namespace Database
|
||||
namespace lms::db
|
||||
{
|
||||
class Db;
|
||||
}
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
class IScrobblingService
|
||||
{
|
||||
@@ -52,33 +52,33 @@ namespace Scrobbling
|
||||
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>;
|
||||
using ArtistContainer = db::RangeResults<db::ArtistId>;
|
||||
using ReleaseContainer = db::RangeResults<db::ReleaseId>;
|
||||
using TrackContainer = db::RangeResults<db::TrackId>;
|
||||
|
||||
struct FindParameters
|
||||
{
|
||||
Database::UserId user;
|
||||
std::vector<Database::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||
std::optional<Database::Range> range;
|
||||
Database::MediaLibraryId library; // if set, match this library
|
||||
Database::ArtistId artist; // if set, match this artist
|
||||
db::UserId user;
|
||||
std::vector<db::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||
std::optional<db::Range> range;
|
||||
db::MediaLibraryId library; // if set, match this library
|
||||
db::ArtistId artist; // if set, match this artist
|
||||
|
||||
FindParameters& setUser(const Database::UserId _user) { user = _user; return *this; }
|
||||
FindParameters& setClusters(const std::vector<Database::ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||
FindParameters& setRange(std::optional<Database::Range> _range) { range = _range; return *this; }
|
||||
FindParameters& setMediaLibrary(Database::MediaLibraryId _library) { library = _library; return *this; }
|
||||
FindParameters& setArtist(Database::ArtistId _artist) { artist = _artist; return *this; }
|
||||
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
||||
FindParameters& setClusters(const std::vector<db::ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||
FindParameters& setArtist(db::ArtistId _artist) { artist = _artist; return *this; }
|
||||
};
|
||||
|
||||
// Artists
|
||||
struct ArtistFindParameters : public FindParameters
|
||||
{
|
||||
std::optional<Database::TrackArtistLinkType> linkType; // if set, only artists that have produced at least one track with this link type
|
||||
Database::ArtistSortMethod sortMethod{ Database::ArtistSortMethod::None };
|
||||
std::optional<db::TrackArtistLinkType> linkType; // if set, only artists that have produced at least one track with this link type
|
||||
db::ArtistSortMethod sortMethod{ db::ArtistSortMethod::None };
|
||||
|
||||
ArtistFindParameters& setLinkType(std::optional<Database::TrackArtistLinkType> _linkType) { linkType = _linkType; return *this; }
|
||||
ArtistFindParameters& setSortMethod(Database::ArtistSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
|
||||
ArtistFindParameters& setLinkType(std::optional<db::TrackArtistLinkType> _linkType) { linkType = _linkType; return *this; }
|
||||
ArtistFindParameters& setSortMethod(db::ArtistSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
|
||||
};
|
||||
|
||||
// From most recent to oldest
|
||||
@@ -86,11 +86,11 @@ namespace Scrobbling
|
||||
virtual ReleaseContainer getRecentReleases(const FindParameters& params) = 0;
|
||||
virtual TrackContainer getRecentTracks(const FindParameters& params) = 0;
|
||||
|
||||
virtual std::size_t getCount(Database::UserId userId, Database::ReleaseId releaseId) = 0;
|
||||
virtual std::size_t getCount(Database::UserId userId, Database::TrackId trackId) = 0;
|
||||
virtual std::size_t getCount(db::UserId userId, db::ReleaseId releaseId) = 0;
|
||||
virtual std::size_t getCount(db::UserId userId, db::TrackId trackId) = 0;
|
||||
|
||||
virtual Wt::WDateTime getLastListenDateTime(Database::UserId userId, Database::ReleaseId releaseId) = 0;
|
||||
virtual Wt::WDateTime getLastListenDateTime(Database::UserId userId, Database::TrackId trackId) = 0;
|
||||
virtual Wt::WDateTime getLastListenDateTime(db::UserId userId, db::ReleaseId releaseId) = 0;
|
||||
virtual Wt::WDateTime getLastListenDateTime(db::UserId userId, db::TrackId trackId) = 0;
|
||||
|
||||
// Top
|
||||
virtual ArtistContainer getTopArtists(const ArtistFindParameters& params) = 0;
|
||||
@@ -98,5 +98,5 @@ namespace Scrobbling
|
||||
virtual TrackContainer getTopTracks(const FindParameters& params) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IScrobblingService> createScrobblingService(boost::asio::io_service& ioService, Database::Db& db);
|
||||
std::unique_ptr<IScrobblingService> createScrobblingService(boost::asio::io_service& ioService, db::Db& db);
|
||||
} // ns Scrobbling
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#include "database/TrackId.hpp"
|
||||
#include "database/UserId.hpp"
|
||||
|
||||
namespace Scrobbling
|
||||
namespace lms::scrobbling
|
||||
{
|
||||
struct Listen
|
||||
{
|
||||
Database::UserId userId{};
|
||||
Database::TrackId trackId{};
|
||||
db::UserId userId{};
|
||||
db::TrackId trackId{};
|
||||
};
|
||||
|
||||
struct TimedListen : public Listen
|
||||
|
||||
@@ -5,7 +5,7 @@ add_executable(test-scrobbling
|
||||
)
|
||||
|
||||
target_link_libraries(test-scrobbling PRIVATE
|
||||
lmsutils
|
||||
lmscore
|
||||
lmsscrobbling
|
||||
GTest::GTest
|
||||
)
|
||||
|
||||
@@ -21,115 +21,115 @@
|
||||
|
||||
#include "listenbrainz/ListensParser.hpp"
|
||||
|
||||
using namespace Scrobbling::ListenBrainz;
|
||||
|
||||
TEST(Listenbrainz, parseListens_empty)
|
||||
namespace lms::scrobbling::listenBrainz::tests
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse("")};
|
||||
EXPECT_EQ(result.listenCount, 0);
|
||||
EXPECT_EQ(result.listens.size(), 0);
|
||||
TEST(Listenbrainz, parseListens_empty)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse("")};
|
||||
EXPECT_EQ(result.listenCount, 0);
|
||||
EXPECT_EQ(result.listens.size(), 0);
|
||||
|
||||
result = ListensParser::parse(R"({"payload":{"count":0,"latest_listen_ts":1664105200,"listens":[],"user_id":"epoupon"}})");
|
||||
EXPECT_EQ(result.listenCount, 0);
|
||||
EXPECT_EQ(result.listens.size(), 0);
|
||||
}
|
||||
result = ListensParser::parse(R"({"payload":{"count":0,"latest_listen_ts":1664105200,"listens":[],"user_id":"epoupon"}})");
|
||||
EXPECT_EQ(result.listenCount, 0);
|
||||
EXPECT_EQ(result.listens.size(), 0);
|
||||
}
|
||||
|
||||
TEST(Listenbrainz, parseListens_single_missingMBID)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1663159479,"listens":[{"inserted_at":1650541124,"listened_at":1650541124,"recording_msid":"0e1418e3-b485-413a-84af-6316312cb116","track_metadata":{"additional_info":{"artist_msid":"ab5b27ad-e579-441c-ac60-d5dd9975c044","listening_from":"LMS","recording_msid":"0e1418e3-b485-413a-84af-6316312cb116","release_msid":"3f22f274-a9ee-4cb2-8dd1-f3bd18407099","tracknumber":8},"artist_name":"Broke For Free","release_name":"YEKOMS","track_name":"U2B"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackName, "U2B");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "YEKOMS");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Broke For Free");
|
||||
EXPECT_EQ(result.listens[0].recordingMBID, std::nullopt);
|
||||
EXPECT_EQ(result.listens[0].releaseMBID, std::nullopt);
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 8);
|
||||
TEST(Listenbrainz, parseListens_single_missingMBID)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1663159479,"listens":[{"inserted_at":1650541124,"listened_at":1650541124,"recording_msid":"0e1418e3-b485-413a-84af-6316312cb116","track_metadata":{"additional_info":{"artist_msid":"ab5b27ad-e579-441c-ac60-d5dd9975c044","listening_from":"LMS","recording_msid":"0e1418e3-b485-413a-84af-6316312cb116","release_msid":"3f22f274-a9ee-4cb2-8dd1-f3bd18407099","tracknumber":8},"artist_name":"Broke For Free","release_name":"YEKOMS","track_name":"U2B"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackName, "U2B");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "YEKOMS");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Broke For Free");
|
||||
EXPECT_EQ(result.listens[0].recordingMBID, std::nullopt);
|
||||
EXPECT_EQ(result.listens[0].releaseMBID, std::nullopt);
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 8);
|
||||
|
||||
Wt::WDateTime listenedAt;
|
||||
listenedAt.setTime_t(1650541124);
|
||||
EXPECT_EQ(result.listens[0].listenedAt, listenedAt);
|
||||
}
|
||||
Wt::WDateTime listenedAt;
|
||||
listenedAt.setTime_t(1650541124);
|
||||
EXPECT_EQ(result.listens[0].listenedAt, listenedAt);
|
||||
}
|
||||
|
||||
TEST(Listenbrainz, parseListens_tworesults)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":2,"latest_listen_ts":1664028167,"listens":[{"inserted_at":1664028167,"listened_at":1664028167,"recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","track_metadata":{"additional_info":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc","release_msid":null,"track_mbid":"5427a943-a096-4d0b-8b9a-53aca9ed61ac","tracknumber":5},"artist_name":"Broke For Free","mbid_mapping":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc"},"release_name":"Petal","track_name":"Juparo"},"user_name":"epoupon"},{"inserted_at":1664027919,"listened_at":1664027918,"recording_msid":"fe5abc47-89cd-4235-80b5-00f47cecbe01","track_metadata":{"additional_info":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"d89d042c-8cc1-4526-9080-5bab728ee15f","recording_msid":"fe5abc47-89cd-4235-80b5-00f47cecbe01","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc","release_msid":null,"track_mbid":"9f33a17f-e33e-492f-85a4-7b2e9e09613e","tracknumber":4},"artist_name":"Broke For Free","mbid_mapping":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"recording_mbid":"d89d042c-8cc1-4526-9080-5bab728ee15f","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc"},"release_name":"Petal","track_name":"Melt"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 2);
|
||||
ASSERT_EQ(result.listens.size(), 2);
|
||||
EXPECT_EQ(result.listens[0].trackName, "Juparo");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "Petal");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Broke For Free");
|
||||
ASSERT_TRUE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].recordingMBID->getAsString(), "46ae879f-2dbe-46d3-99ad-05c116f97a30");
|
||||
ASSERT_TRUE(result.listens[0].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].releaseMBID->getAsString(), "44915500-fbb9-4060-98ce-59a57a429edc");
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 5);
|
||||
TEST(Listenbrainz, parseListens_tworesults)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":2,"latest_listen_ts":1664028167,"listens":[{"inserted_at":1664028167,"listened_at":1664028167,"recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","track_metadata":{"additional_info":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc","release_msid":null,"track_mbid":"5427a943-a096-4d0b-8b9a-53aca9ed61ac","tracknumber":5},"artist_name":"Broke For Free","mbid_mapping":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc"},"release_name":"Petal","track_name":"Juparo"},"user_name":"epoupon"},{"inserted_at":1664027919,"listened_at":1664027918,"recording_msid":"fe5abc47-89cd-4235-80b5-00f47cecbe01","track_metadata":{"additional_info":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"d89d042c-8cc1-4526-9080-5bab728ee15f","recording_msid":"fe5abc47-89cd-4235-80b5-00f47cecbe01","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc","release_msid":null,"track_mbid":"9f33a17f-e33e-492f-85a4-7b2e9e09613e","tracknumber":4},"artist_name":"Broke For Free","mbid_mapping":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"recording_mbid":"d89d042c-8cc1-4526-9080-5bab728ee15f","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc"},"release_name":"Petal","track_name":"Melt"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 2);
|
||||
ASSERT_EQ(result.listens.size(), 2);
|
||||
EXPECT_EQ(result.listens[0].trackName, "Juparo");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "Petal");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Broke For Free");
|
||||
ASSERT_TRUE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].recordingMBID->getAsString(), "46ae879f-2dbe-46d3-99ad-05c116f97a30");
|
||||
ASSERT_TRUE(result.listens[0].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].releaseMBID->getAsString(), "44915500-fbb9-4060-98ce-59a57a429edc");
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 5);
|
||||
|
||||
EXPECT_EQ(result.listens[1].trackName, "Melt");
|
||||
EXPECT_EQ(result.listens[1].releaseName, "Petal");
|
||||
EXPECT_EQ(result.listens[1].artistName, "Broke For Free");
|
||||
ASSERT_TRUE(result.listens[1].recordingMBID.has_value());
|
||||
EXPECT_EQ(result.listens[1].recordingMBID->getAsString(), "d89d042c-8cc1-4526-9080-5bab728ee15f");
|
||||
ASSERT_TRUE(result.listens[1].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[1].releaseMBID->getAsString(), "44915500-fbb9-4060-98ce-59a57a429edc");
|
||||
EXPECT_EQ(result.listens[1].trackNumber, 4);
|
||||
}
|
||||
EXPECT_EQ(result.listens[1].trackName, "Melt");
|
||||
EXPECT_EQ(result.listens[1].releaseName, "Petal");
|
||||
EXPECT_EQ(result.listens[1].artistName, "Broke For Free");
|
||||
ASSERT_TRUE(result.listens[1].recordingMBID.has_value());
|
||||
EXPECT_EQ(result.listens[1].recordingMBID->getAsString(), "d89d042c-8cc1-4526-9080-5bab728ee15f");
|
||||
ASSERT_TRUE(result.listens[1].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[1].releaseMBID->getAsString(), "44915500-fbb9-4060-98ce-59a57a429edc");
|
||||
EXPECT_EQ(result.listens[1].trackNumber, 4);
|
||||
}
|
||||
|
||||
TEST(Listenbrainz, parseListens_tworesults_butinvalid)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":2,"latest_listen_ts":1664028167,"listens":[{"inserted_at":1664028167,"listened_at":1664028167,"recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","track_metadata":{"additional_info":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc","release_msid":null,"track_mbid":"5427a943-a096-4d0b-8b9a-53aca9ed61ac","tracknumber":5},"artist_name":"Broke For Free","mbid_mapping":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc"},"release_name":"Petal","track_name":"Juparo"},"user_name":"epoupon"},{"inserted_at":1664027919}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 2);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackName, "Juparo");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "Petal");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Broke For Free");
|
||||
ASSERT_TRUE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].recordingMBID->getAsString(), "46ae879f-2dbe-46d3-99ad-05c116f97a30");
|
||||
ASSERT_TRUE(result.listens[0].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].releaseMBID->getAsString(), "44915500-fbb9-4060-98ce-59a57a429edc");
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 5);
|
||||
}
|
||||
TEST(Listenbrainz, parseListens_tworesults_butinvalid)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":2,"latest_listen_ts":1664028167,"listens":[{"inserted_at":1664028167,"listened_at":1664028167,"recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","track_metadata":{"additional_info":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","recording_msid":"29c11137-e40b-4875-9ec0-9a20a4bdc2d3","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc","release_msid":null,"track_mbid":"5427a943-a096-4d0b-8b9a-53aca9ed61ac","tracknumber":5},"artist_name":"Broke For Free","mbid_mapping":{"artist_mbids":["069a1c1f-14eb-4d36-b0a0-77dffbd67713"],"recording_mbid":"46ae879f-2dbe-46d3-99ad-05c116f97a30","release_mbid":"44915500-fbb9-4060-98ce-59a57a429edc"},"release_name":"Petal","track_name":"Juparo"},"user_name":"epoupon"},{"inserted_at":1664027919}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 2);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackName, "Juparo");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "Petal");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Broke For Free");
|
||||
ASSERT_TRUE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].recordingMBID->getAsString(), "46ae879f-2dbe-46d3-99ad-05c116f97a30");
|
||||
ASSERT_TRUE(result.listens[0].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].releaseMBID->getAsString(), "44915500-fbb9-4060-98ce-59a57a429edc");
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 5);
|
||||
}
|
||||
|
||||
TEST(Listenbrainz, parseListens_entryNotFromLms)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1664105730,"listens":[{"inserted_at":1664105730,"listened_at":1664105730,"recording_msid":"6a11ff4d-0623-4b2e-98e0-0e172f1f28d7","track_metadata":{"additional_info":{"artist_msid":null,"media_player":"BrainzPlayer","music_service":"youtube.com","music_service_name":"youtube","origin_url":"https://www.youtube.com/watch?v=EBP5vL3YWTI","recording_msid":"6a11ff4d-0623-4b2e-98e0-0e172f1f28d7","release_msid":null,"submission_client":"BrainzPlayer"},"artist_name":"Dio","brainzplayer_metadata":{"track_name":"Dio - Breathless"},"mbid_mapping":{"artist_mbids":["c55193fb-f5d2-4839-a263-4c044fca1456"],"recording_mbid":"92929526-21d7-4e75-b759-1072951664c4","release_mbid":"16cbf9ba-2e38-3893-9f23-f8567e26c18b"},"release_name":"The Last in Line","track_name":"Breathless"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackName, "Breathless");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "The Last in Line");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Dio");
|
||||
EXPECT_FALSE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_FALSE(result.listens[0].releaseMBID.has_value());
|
||||
}
|
||||
TEST(Listenbrainz, parseListens_entryNotFromLms)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1664105730,"listens":[{"inserted_at":1664105730,"listened_at":1664105730,"recording_msid":"6a11ff4d-0623-4b2e-98e0-0e172f1f28d7","track_metadata":{"additional_info":{"artist_msid":null,"media_player":"BrainzPlayer","music_service":"youtube.com","music_service_name":"youtube","origin_url":"https://www.youtube.com/watch?v=EBP5vL3YWTI","recording_msid":"6a11ff4d-0623-4b2e-98e0-0e172f1f28d7","release_msid":null,"submission_client":"BrainzPlayer"},"artist_name":"Dio","brainzplayer_metadata":{"track_name":"Dio - Breathless"},"mbid_mapping":{"artist_mbids":["c55193fb-f5d2-4839-a263-4c044fca1456"],"recording_mbid":"92929526-21d7-4e75-b759-1072951664c4","release_mbid":"16cbf9ba-2e38-3893-9f23-f8567e26c18b"},"release_name":"The Last in Line","track_name":"Breathless"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackName, "Breathless");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "The Last in Line");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Dio");
|
||||
EXPECT_FALSE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_FALSE(result.listens[0].releaseMBID.has_value());
|
||||
}
|
||||
|
||||
TEST(Listenbrainz, parseListens_multiArtists)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1664106427,"listens":[{"inserted_at":1664106427,"listened_at":1664106427,"recording_msid":"b1dad0df-329b-443d-bacf-cdbebdddbfd0","track_metadata":{"additional_info":{"artist_mbids":["04ce0202-043d-4cbe-8f09-8abaf3b80c71","79311c51-9748-49df-baa1-d925fd29f4e8"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"a5f380bc-0a85-4a9f-88db-d41bb9aa2a4b","recording_msid":"b1dad0df-329b-443d-bacf-cdbebdddbfd0","release_mbid":"147b4669-3d20-43f8-89c0-ba1da8b87dd3","release_msid":null,"track_mbid":"a20dd067-29b6-3d38-a0be-eeb86b4671c1","tracknumber":1},"artist_name":"Gloom","release_name":"Demovibes 9: Party, people going","track_name":"Stargazer of Disgrace"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 1);
|
||||
}
|
||||
TEST(Listenbrainz, parseListens_multiArtists)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1664106427,"listens":[{"inserted_at":1664106427,"listened_at":1664106427,"recording_msid":"b1dad0df-329b-443d-bacf-cdbebdddbfd0","track_metadata":{"additional_info":{"artist_mbids":["04ce0202-043d-4cbe-8f09-8abaf3b80c71","79311c51-9748-49df-baa1-d925fd29f4e8"],"artist_msid":null,"listening_from":"LMS","recording_mbid":"a5f380bc-0a85-4a9f-88db-d41bb9aa2a4b","recording_msid":"b1dad0df-329b-443d-bacf-cdbebdddbfd0","release_mbid":"147b4669-3d20-43f8-89c0-ba1da8b87dd3","release_msid":null,"track_mbid":"a20dd067-29b6-3d38-a0be-eeb86b4671c1","tracknumber":1},"artist_name":"Gloom","release_name":"Demovibes 9: Party, people going","track_name":"Stargazer of Disgrace"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 1);
|
||||
}
|
||||
|
||||
TEST(Listenbrainz, parseListens_minPayload)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1664106427,"listens":[{"track_metadata":{"artist_name":"Gloom","track_name":"Stargazer of Disgrace"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_FALSE(result.listens[0].listenedAt.isValid());
|
||||
EXPECT_EQ(result.listens[0].trackName, "Stargazer of Disgrace");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Gloom");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "");
|
||||
EXPECT_FALSE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_FALSE(result.listens[0].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].trackNumber, std::nullopt);
|
||||
}
|
||||
TEST(Listenbrainz, parseListens_minPayload)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1664106427,"listens":[{"track_metadata":{"artist_name":"Gloom","track_name":"Stargazer of Disgrace"},"user_name":"epoupon"}],"user_id":"epoupon"}})")};
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_FALSE(result.listens[0].listenedAt.isValid());
|
||||
EXPECT_EQ(result.listens[0].trackName, "Stargazer of Disgrace");
|
||||
EXPECT_EQ(result.listens[0].artistName, "Gloom");
|
||||
EXPECT_EQ(result.listens[0].releaseName, "");
|
||||
EXPECT_FALSE(result.listens[0].recordingMBID.has_value());
|
||||
EXPECT_FALSE(result.listens[0].releaseMBID.has_value());
|
||||
EXPECT_EQ(result.listens[0].trackNumber, std::nullopt);
|
||||
}
|
||||
|
||||
TEST(Listenbrainz, parseListens_stringInsteadOfInt)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1683298766,"listens":[{"inserted_at":1683299849,"listened_at":1683298766,"recording_msid":"3841ad1c-f674-491c-b855-f10a82ea38fe","track_metadata":{"additional_info":{"albumartist":"Various Artists","artist_mbids":["80ccfede-c258-4575-a7ad-c982e9932e0f"],"date":"2002-06-10","discnumber":"1","duration":190,"isrc":"USAM10200204","media_player":"foobar2000","media_player_version":"1.6.16","recording_msid":"3841ad1c-f674-491c-b855-f10a82ea38fe","release_group_mbid":"25f44677-2ecd-33fb-8454-9a9776496e3c","release_mbid":"bddad4c6-e9e1-4218-b9d2-b02a5a778a7d","submission_client":"foobar2000","submission_client_version":"1.3.2","totaldiscs":"1","totaltracks":"18","track_mbid":"08b1a5c1-c845-3d3b-9aa4-560a1af7f28c","tracknumber":"4"},"artist_name":"Sheryl Crow","mbid_mapping":{"artist_mbids":["80ccfede-c258-4575-a7ad-c982e9932e0f"],"artists":[{"artist_credit_name":"Sheryl Crow","artist_mbid":"80ccfede-c258-4575-a7ad-c982e9932e0f","join_phrase":""}],"caa_id":22039936498,"caa_release_mbid":"f10b4b40-4db1-4b9a-a223-06915fa06b3c","recording_mbid":"7ca21c82-1345-44df-b7ac-035a6e05d4ed","recording_name":"Strong Enough","release_mbid":"f10b4b40-4db1-4b9a-a223-06915fa06b3c"},"release_name":"The Very Best of MTV Unplugged","track_name":"Strong Enough"},"user_name":"awesomeuser"}],"user_id":"awesomeuser"}})")};
|
||||
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 4);
|
||||
}
|
||||
TEST(Listenbrainz, parseListens_stringInsteadOfInt)
|
||||
{
|
||||
ListensParser::Result result {ListensParser::parse(R"({"payload":{"count":1,"latest_listen_ts":1683298766,"listens":[{"inserted_at":1683299849,"listened_at":1683298766,"recording_msid":"3841ad1c-f674-491c-b855-f10a82ea38fe","track_metadata":{"additional_info":{"albumartist":"Various Artists","artist_mbids":["80ccfede-c258-4575-a7ad-c982e9932e0f"],"date":"2002-06-10","discnumber":"1","duration":190,"isrc":"USAM10200204","media_player":"foobar2000","media_player_version":"1.6.16","recording_msid":"3841ad1c-f674-491c-b855-f10a82ea38fe","release_group_mbid":"25f44677-2ecd-33fb-8454-9a9776496e3c","release_mbid":"bddad4c6-e9e1-4218-b9d2-b02a5a778a7d","submission_client":"foobar2000","submission_client_version":"1.3.2","totaldiscs":"1","totaltracks":"18","track_mbid":"08b1a5c1-c845-3d3b-9aa4-560a1af7f28c","tracknumber":"4"},"artist_name":"Sheryl Crow","mbid_mapping":{"artist_mbids":["80ccfede-c258-4575-a7ad-c982e9932e0f"],"artists":[{"artist_credit_name":"Sheryl Crow","artist_mbid":"80ccfede-c258-4575-a7ad-c982e9932e0f","join_phrase":""}],"caa_id":22039936498,"caa_release_mbid":"f10b4b40-4db1-4b9a-a223-06915fa06b3c","recording_mbid":"7ca21c82-1345-44df-b7ac-035a6e05d4ed","recording_name":"Strong Enough","release_mbid":"f10b4b40-4db1-4b9a-a223-06915fa06b3c"},"release_name":"The Very Best of MTV Unplugged","track_name":"Strong Enough"},"user_name":"awesomeuser"}],"user_id":"awesomeuser"}})")};
|
||||
|
||||
EXPECT_EQ(result.listenCount, 1);
|
||||
ASSERT_EQ(result.listens.size(), 1);
|
||||
EXPECT_EQ(result.listens[0].trackNumber, 4);
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,15 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "utils/ILogger.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "utils/StreamLogger.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "core/StreamLogger.hpp"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace lms;
|
||||
// log to stdout
|
||||
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout, EnumSet<Severity> {Severity::FATAL, Severity::ERROR}) };
|
||||
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout, core::EnumSet<core::logging::Severity> {core::logging::Severity::FATAL, core::logging::Severity::ERROR}) };
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
Reference in New Issue
Block a user