Auto reformatted the base, ref #470
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "FeedbackService.hpp"
|
||||
#include "FeedbackService.impl.hpp"
|
||||
|
||||
#include "core/ILogger.hpp"
|
||||
#include "database/Artist.hpp"
|
||||
#include "database/Db.hpp"
|
||||
#include "database/Release.hpp"
|
||||
@@ -29,7 +30,6 @@
|
||||
#include "database/StarredTrack.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
|
||||
#include "internal/InternalBackend.hpp"
|
||||
#include "listenbrainz/ListenBrainzBackend.hpp"
|
||||
@@ -187,5 +187,4 @@ namespace lms::feedback
|
||||
|
||||
return Track::findIds(session, searchParams);
|
||||
}
|
||||
} // ns Feedback
|
||||
|
||||
} // namespace lms::feedback
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "services/feedback/IFeedbackService.hpp"
|
||||
|
||||
#include "IFeedbackBackend.hpp"
|
||||
|
||||
namespace lms::db
|
||||
@@ -46,7 +47,7 @@ namespace lms::feedback
|
||||
void unstar(db::UserId userId, db::ArtistId artistId) override;
|
||||
bool isStarred(db::UserId userId, db::ArtistId artistId) override;
|
||||
Wt::WDateTime getStarredDateTime(db::UserId userId, db::ArtistId artistId) override;
|
||||
ArtistContainer findStarredArtists(const ArtistFindParameters& params) override;
|
||||
ArtistContainer findStarredArtists(const ArtistFindParameters& params) override;
|
||||
|
||||
void star(db::UserId userId, db::ReleaseId releaseId) override;
|
||||
void unstar(db::UserId userId, db::ReleaseId releaseId) override;
|
||||
@@ -62,17 +63,17 @@ namespace lms::feedback
|
||||
|
||||
std::optional<db::FeedbackBackend> getUserFeedbackBackend(db::UserId userId);
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void star(db::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void unstar(db::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
bool isStarred(db::UserId userId, ObjIdType id);
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
Wt::WDateTime getStarredDateTime(db::UserId userId, ObjIdType id);
|
||||
|
||||
db::Db& _db;
|
||||
std::unordered_map<db::FeedbackBackend, std::unique_ptr<IFeedbackBackend>> _backends;
|
||||
};
|
||||
|
||||
} // ns Feedback
|
||||
} // namespace lms::feedback
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace lms::feedback
|
||||
{
|
||||
using namespace db;
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void FeedbackService::star(UserId userId, ObjIdType objId)
|
||||
{
|
||||
const auto backend{ getUserFeedbackBackend(userId) };
|
||||
@@ -58,7 +58,7 @@ namespace lms::feedback
|
||||
_backends[*backend]->onStarred(starredObjId);
|
||||
}
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
void FeedbackService::unstar(UserId userId, ObjIdType objId)
|
||||
{
|
||||
const auto backend{ getUserFeedbackBackend(userId) };
|
||||
@@ -79,7 +79,7 @@ namespace lms::feedback
|
||||
_backends[*backend]->onUnstarred(starredObjId);
|
||||
}
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
bool FeedbackService::isStarred(UserId userId, ObjIdType objId)
|
||||
{
|
||||
Session& session{ _db.getTLSSession() };
|
||||
@@ -89,7 +89,7 @@ namespace lms::feedback
|
||||
return starredObj && (starredObj->getSyncState() != SyncState::PendingRemove);
|
||||
}
|
||||
|
||||
template <typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
template<typename ObjType, typename ObjIdType, typename StarredObjType>
|
||||
Wt::WDateTime FeedbackService::getStarredDateTime(UserId userId, ObjIdType objId)
|
||||
{
|
||||
Session& session{ _db.getTLSSession() };
|
||||
@@ -102,4 +102,4 @@ namespace lms::feedback
|
||||
return {};
|
||||
}
|
||||
|
||||
} // ns Feedback
|
||||
} // namespace lms::feedback
|
||||
@@ -40,4 +40,4 @@ namespace lms::feedback
|
||||
|
||||
std::unique_ptr<IFeedbackBackend> createFeedbackBackend(std::string_view backendName);
|
||||
|
||||
} // ns Feedback
|
||||
} // namespace lms::feedback
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace lms::feedback
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename StarredObjType>
|
||||
template<typename StarredObjType>
|
||||
void onStarred(db::Session& session, typename StarredObjType::IdType id)
|
||||
{
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
@@ -38,7 +38,7 @@ namespace lms::feedback
|
||||
starredObj.modify()->setSyncState(db::SyncState::Synchronized);
|
||||
}
|
||||
|
||||
template <typename StarredObjType>
|
||||
template<typename StarredObjType>
|
||||
void onUnstarred(db::Session& session, typename StarredObjType::IdType id)
|
||||
{
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
@@ -46,11 +46,12 @@ namespace lms::feedback
|
||||
if (auto starredObj{ StarredObjType::find(session, id) })
|
||||
starredObj.remove();
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
|
||||
InternalBackend::InternalBackend(db::Db& db)
|
||||
: _db{ db }
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
void InternalBackend::onStarred(db::StarredArtistId starredArtistId)
|
||||
{
|
||||
@@ -81,4 +82,4 @@ namespace lms::feedback
|
||||
{
|
||||
details::onUnstarred<db::StarredTrack>(_db.getTLSSession(), starredTrackId);
|
||||
}
|
||||
} // Feedback
|
||||
} // namespace lms::feedback
|
||||
|
||||
@@ -43,5 +43,4 @@ namespace lms::feedback
|
||||
|
||||
db::Db& _db;
|
||||
};
|
||||
} // Feedback
|
||||
|
||||
} // namespace lms::feedback
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
namespace lms::feedback::listenBrainz
|
||||
{
|
||||
class Exception : public feedback::Exception
|
||||
{
|
||||
public:
|
||||
using feedback::Exception::Exception;
|
||||
};
|
||||
}
|
||||
class Exception : public feedback::Exception
|
||||
{
|
||||
public:
|
||||
using feedback::Exception::Exception;
|
||||
};
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
|
||||
namespace lms::feedback::listenBrainz
|
||||
{
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const Feedback& feedback)
|
||||
{
|
||||
os << "created = '" << feedback.created.toString() << "', recording MBID = '" << feedback.recordingMBID.getAsString() << "', score = " << static_cast<int>(feedback.score);
|
||||
return os;
|
||||
}
|
||||
} // feedback::ListenBrainz
|
||||
std::ostream& operator<<(std::ostream& os, const Feedback& feedback)
|
||||
{
|
||||
os << "created = '" << feedback.created.toString() << "', recording MBID = '" << feedback.recordingMBID.getAsString() << "', score = " << static_cast<int>(feedback.score);
|
||||
return os;
|
||||
}
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
|
||||
@@ -20,26 +20,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#include "core/UUID.hpp"
|
||||
|
||||
namespace lms::feedback::listenBrainz
|
||||
{
|
||||
// See https://listenbrainz.readthedocs.io/en/production/dev/feedback-json/#feedback-json-doc
|
||||
enum class FeedbackType
|
||||
{
|
||||
Love = 1,
|
||||
Hate = -1,
|
||||
Erase = 0,
|
||||
};
|
||||
// See https://listenbrainz.readthedocs.io/en/production/dev/feedback-json/#feedback-json-doc
|
||||
enum class FeedbackType
|
||||
{
|
||||
Love = 1,
|
||||
Hate = -1,
|
||||
Erase = 0,
|
||||
};
|
||||
|
||||
struct Feedback
|
||||
{
|
||||
Wt::WDateTime created;
|
||||
core::UUID recordingMBID;
|
||||
FeedbackType score;
|
||||
};
|
||||
struct Feedback
|
||||
{
|
||||
Wt::WDateTime created;
|
||||
core::UUID recordingMBID;
|
||||
FeedbackType score;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Feedback& feedback);
|
||||
std::ostream& operator<<(std::ostream& os, const Feedback& feedback);
|
||||
|
||||
} // feedback::ListenBrainz
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
#include <Wt/Json/Array.h>
|
||||
#include <Wt/Json/Object.h>
|
||||
#include <Wt/Json/Value.h>
|
||||
#include <Wt/Json/Parser.h>
|
||||
#include <Wt/Json/Value.h>
|
||||
|
||||
#include "Exception.hpp"
|
||||
#include "Utils.hpp"
|
||||
@@ -37,14 +37,13 @@ namespace lms::feedback::listenBrainz
|
||||
if (!recordingMBID)
|
||||
throw Exception{ "MBID not found!" };
|
||||
|
||||
return Feedback
|
||||
{
|
||||
return Feedback{
|
||||
Wt::WDateTime::fromTime_t(static_cast<int>(feedbackObj.get("created"))),
|
||||
*recordingMBID,
|
||||
static_cast<FeedbackType>(static_cast<int>(feedbackObj.get("score")))
|
||||
*recordingMBID,
|
||||
static_cast<FeedbackType>(static_cast<int>(feedbackObj.get("score")))
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FeedbacksParser::Result FeedbacksParser::parse(std::string_view msgBody)
|
||||
{
|
||||
@@ -87,4 +86,4 @@ namespace lms::feedback::listenBrainz
|
||||
|
||||
return res;
|
||||
}
|
||||
} // feedback::ListenBrainz
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
|
||||
@@ -25,16 +25,16 @@
|
||||
|
||||
namespace lms::feedback::listenBrainz
|
||||
{
|
||||
class FeedbacksParser
|
||||
{
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
std::size_t feedbackCount {}; // >= feedbacks.size()
|
||||
std::vector<Feedback> feedbacks;
|
||||
};
|
||||
class FeedbacksParser
|
||||
{
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
std::size_t feedbackCount{}; // >= feedbacks.size()
|
||||
std::vector<Feedback> feedbacks;
|
||||
};
|
||||
|
||||
static Result parse(std::string_view msgBody);
|
||||
};
|
||||
static Result parse(std::string_view msgBody);
|
||||
};
|
||||
|
||||
} // feedback::ListenBrainz
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
|
||||
@@ -19,20 +19,20 @@
|
||||
|
||||
#include "FeedbacksSynchronizer.hpp"
|
||||
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <Wt/Json/Array.h>
|
||||
#include <Wt/Json/Object.h>
|
||||
#include <Wt/Json/Value.h>
|
||||
#include <Wt/Json/Serializer.h>
|
||||
#include <Wt/Json/Value.h>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "core/http/IClient.hpp"
|
||||
#include "database/Db.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/StarredTrack.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "database/User.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/http/IClient.hpp"
|
||||
#include "core/Service.hpp"
|
||||
|
||||
#include "Exception.hpp"
|
||||
#include "FeedbacksParser.hpp"
|
||||
@@ -57,7 +57,7 @@ namespace lms::feedback::listenBrainz
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FeedbacksSynchronizer::FeedbacksSynchronizer(boost::asio::io_context& ioContext, db::Db& db, core::http::IClient& client)
|
||||
: _ioContext{ ioContext }
|
||||
@@ -125,19 +125,17 @@ namespace lms::feedback::listenBrainz
|
||||
request.message.addHeader("Authorization", "Token " + std::string{ listenBrainzToken->getAsString() });
|
||||
|
||||
Wt::Json::Object root;
|
||||
root["recording_mbid"] = Wt::Json::Value{ std::string {recordingMBID->getAsString()} };
|
||||
root["recording_mbid"] = Wt::Json::Value{ std::string{ recordingMBID->getAsString() } };
|
||||
root["score"] = Wt::Json::Value{ static_cast<int>(type) };
|
||||
|
||||
request.message.addBodyText(Wt::Json::serialize(root));
|
||||
request.message.addHeader("Content-Type", "application/json");
|
||||
|
||||
request.onSuccessFunc = [this, type, starredTrackId](std::string_view /*msgBody*/)
|
||||
{
|
||||
_strand.dispatch([this, type, starredTrackId]
|
||||
{
|
||||
onFeedbackSent(type, starredTrackId);
|
||||
});
|
||||
};
|
||||
request.onSuccessFunc = [this, type, starredTrackId](std::string_view /*msgBody*/) {
|
||||
_strand.dispatch([this, type, starredTrackId] {
|
||||
onFeedbackSent(type, starredTrackId);
|
||||
});
|
||||
};
|
||||
_client.sendPOSTRequest(std::move(request));
|
||||
}
|
||||
catch (Exception& e)
|
||||
@@ -193,20 +191,19 @@ namespace lms::feedback::listenBrainz
|
||||
|
||||
void FeedbacksSynchronizer::enquePendingFeedbacks()
|
||||
{
|
||||
using namespace db;
|
||||
using namespace db;
|
||||
|
||||
auto processPendingFeedbacks{ [this](SyncState scrobblingState, FeedbackType feedbackType)
|
||||
{
|
||||
auto processPendingFeedbacks{ [this](SyncState scrobblingState, FeedbackType feedbackType) {
|
||||
RangeResults<StarredTrackId> pendingFeedbacks;
|
||||
|
||||
{
|
||||
db::Session& session {_db.getTLSSession()};
|
||||
db::Session& session{ _db.getTLSSession() };
|
||||
|
||||
auto transaction {session.createReadTransaction()};
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
StarredTrack::FindParameters params;
|
||||
params.setFeedbackBackend(db::FeedbackBackend::ListenBrainz, scrobblingState)
|
||||
.setRange(db::Range {0, 100}); // don't flood too much?
|
||||
.setRange(db::Range{ 0, 100 }); // don't flood too much?
|
||||
|
||||
pendingFeedbacks = StarredTrack::find(session, params);
|
||||
}
|
||||
@@ -236,10 +233,9 @@ namespace lms::feedback::listenBrainz
|
||||
|
||||
bool FeedbacksSynchronizer::isSyncing() const
|
||||
{
|
||||
return std::any_of(std::cbegin(_userContexts), std::cend(_userContexts), [](const auto& contextEntry)
|
||||
{
|
||||
return contextEntry.second.syncing;
|
||||
});
|
||||
return std::any_of(std::cbegin(_userContexts), std::cend(_userContexts), [](const auto& contextEntry) {
|
||||
return contextEntry.second.syncing;
|
||||
});
|
||||
}
|
||||
|
||||
void FeedbacksSynchronizer::scheduleSync(std::chrono::seconds fromNow)
|
||||
@@ -249,20 +245,19 @@ namespace lms::feedback::listenBrainz
|
||||
|
||||
LOG(DEBUG, "Scheduled sync in " << fromNow.count() << " seconds...");
|
||||
_syncTimer.expires_after(fromNow);
|
||||
_syncTimer.async_wait(boost::asio::bind_executor(_strand, [this](const boost::system::error_code& ec)
|
||||
_syncTimer.async_wait(boost::asio::bind_executor(_strand, [this](const boost::system::error_code& ec) {
|
||||
if (ec == boost::asio::error::operation_aborted)
|
||||
{
|
||||
if (ec == boost::asio::error::operation_aborted)
|
||||
{
|
||||
LOG(DEBUG, "getFeedbacks aborted");
|
||||
return;
|
||||
}
|
||||
else if (ec)
|
||||
{
|
||||
throw Exception{ "GetFeedbacks timer failure: " + std::string {ec.message()} };
|
||||
}
|
||||
LOG(DEBUG, "getFeedbacks aborted");
|
||||
return;
|
||||
}
|
||||
else if (ec)
|
||||
{
|
||||
throw Exception{ "GetFeedbacks timer failure: " + std::string{ ec.message() } };
|
||||
}
|
||||
|
||||
startSync();
|
||||
}));
|
||||
startSync();
|
||||
}));
|
||||
}
|
||||
|
||||
void FeedbacksSynchronizer::startSync()
|
||||
@@ -301,14 +296,13 @@ namespace lms::feedback::listenBrainz
|
||||
|
||||
void FeedbacksSynchronizer::onSyncEnded(UserContext& context)
|
||||
{
|
||||
_strand.dispatch([this, &context]
|
||||
{
|
||||
LOG(INFO, "Feedback sync done for user '" << context.listenBrainzUserName << "', fetched: " << context.fetchedFeedbackCount << ", matched: " << context.matchedFeedbackCount << ", imported: " << context.importedFeedbackCount);
|
||||
context.syncing = false;
|
||||
_strand.dispatch([this, &context] {
|
||||
LOG(INFO, "Feedback sync done for user '" << context.listenBrainzUserName << "', fetched: " << context.fetchedFeedbackCount << ", matched: " << context.matchedFeedbackCount << ", imported: " << context.importedFeedbackCount);
|
||||
context.syncing = false;
|
||||
|
||||
if (!isSyncing())
|
||||
scheduleSync(_syncFeedbacksPeriod);
|
||||
});
|
||||
if (!isSyncing())
|
||||
scheduleSync(_syncFeedbacksPeriod);
|
||||
});
|
||||
}
|
||||
|
||||
void FeedbacksSynchronizer::enqueValidateToken(UserContext& context)
|
||||
@@ -325,21 +319,19 @@ namespace lms::feedback::listenBrainz
|
||||
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);
|
||||
if (context.listenBrainzUserName.empty())
|
||||
{
|
||||
onSyncEnded(context);
|
||||
return;
|
||||
}
|
||||
enqueGetFeedbackCount(context);
|
||||
};
|
||||
request.onFailureFunc = [this, &context]
|
||||
request.headers = { { "Authorization", "Token " + std::string{ listenBrainzToken->getAsString() } } };
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody) {
|
||||
context.listenBrainzUserName = utils::parseValidateToken(msgBody);
|
||||
if (context.listenBrainzUserName.empty())
|
||||
{
|
||||
onSyncEnded(context);
|
||||
};
|
||||
return;
|
||||
}
|
||||
enqueGetFeedbackCount(context);
|
||||
};
|
||||
request.onFailureFunc = [this, &context] {
|
||||
onSyncEnded(context);
|
||||
};
|
||||
|
||||
_client.sendGETRequest(std::move(request));
|
||||
}
|
||||
@@ -351,30 +343,27 @@ namespace lms::feedback::listenBrainz
|
||||
core::http::ClientGETRequestParameters request;
|
||||
request.relativeUrl = "/1/feedback/user/" + std::string{ context.listenBrainzUserName } + "/get-feedback?score=1&count=0";
|
||||
request.priority = core::http::ClientRequestParameters::Priority::Low;
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody)
|
||||
{
|
||||
std::string msgBodyCopy{ msgBody };
|
||||
_strand.dispatch([this, msgBodyCopy, &context]
|
||||
{
|
||||
LOG(DEBUG, "Current feedback count = " << (context.feedbackCount ? *context.feedbackCount : 0) << " for user '" << context.listenBrainzUserName << "'");
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody) {
|
||||
std::string msgBodyCopy{ msgBody };
|
||||
_strand.dispatch([this, msgBodyCopy, &context] {
|
||||
LOG(DEBUG, "Current feedback count = " << (context.feedbackCount ? *context.feedbackCount : 0) << " for user '" << context.listenBrainzUserName << "'");
|
||||
|
||||
const auto totalFeedbackCount = parseTotalFeedbackCount(msgBodyCopy);
|
||||
if (totalFeedbackCount)
|
||||
LOG(DEBUG, "Feedback count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *totalFeedbackCount);
|
||||
const auto totalFeedbackCount = parseTotalFeedbackCount(msgBodyCopy);
|
||||
if (totalFeedbackCount)
|
||||
LOG(DEBUG, "Feedback count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *totalFeedbackCount);
|
||||
|
||||
bool needSync{ totalFeedbackCount && (!context.feedbackCount || *context.feedbackCount != *totalFeedbackCount) };
|
||||
context.feedbackCount = totalFeedbackCount;
|
||||
bool needSync{ totalFeedbackCount && (!context.feedbackCount || *context.feedbackCount != *totalFeedbackCount) };
|
||||
context.feedbackCount = totalFeedbackCount;
|
||||
|
||||
if (needSync)
|
||||
enqueGetFeedbacks(context);
|
||||
else
|
||||
onSyncEnded(context);
|
||||
});
|
||||
};
|
||||
request.onFailureFunc = [this, &context]
|
||||
{
|
||||
onSyncEnded(context);
|
||||
};
|
||||
if (needSync)
|
||||
enqueGetFeedbacks(context);
|
||||
else
|
||||
onSyncEnded(context);
|
||||
});
|
||||
};
|
||||
request.onFailureFunc = [this, &context] {
|
||||
onSyncEnded(context);
|
||||
};
|
||||
|
||||
_client.sendGETRequest(std::move(request));
|
||||
}
|
||||
@@ -386,28 +375,25 @@ namespace lms::feedback::listenBrainz
|
||||
core::http::ClientGETRequestParameters request;
|
||||
request.relativeUrl = "/1/feedback/user/" + context.listenBrainzUserName + "/get-feedback?offset=" + std::to_string(context.fetchedFeedbackCount);
|
||||
request.priority = core::http::ClientRequestParameters::Priority::Low;
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody)
|
||||
{
|
||||
std::string msgBodyCopy{ msgBody };
|
||||
_strand.dispatch([this, msgBodyCopy, &context]
|
||||
{
|
||||
const std::size_t fetchedFeedbackCount{ processGetFeedbacks(msgBodyCopy, context) };
|
||||
if (fetchedFeedbackCount == 0 // no more thing available on server
|
||||
|| context.fetchedFeedbackCount >= context.feedbackCount // we may miss something, but we will get it next time
|
||||
|| context.fetchedFeedbackCount >= _maxSyncFeedbackCount)
|
||||
{
|
||||
onSyncEnded(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
enqueGetFeedbacks(context);
|
||||
}
|
||||
});
|
||||
};
|
||||
request.onFailureFunc = [this, &context]
|
||||
{
|
||||
onSyncEnded(context);
|
||||
};
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody) {
|
||||
std::string msgBodyCopy{ msgBody };
|
||||
_strand.dispatch([this, msgBodyCopy, &context] {
|
||||
const std::size_t fetchedFeedbackCount{ processGetFeedbacks(msgBodyCopy, context) };
|
||||
if (fetchedFeedbackCount == 0 // no more thing available on server
|
||||
|| context.fetchedFeedbackCount >= context.feedbackCount // we may miss something, but we will get it next time
|
||||
|| context.fetchedFeedbackCount >= _maxSyncFeedbackCount)
|
||||
{
|
||||
onSyncEnded(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
enqueGetFeedbacks(context);
|
||||
}
|
||||
});
|
||||
};
|
||||
request.onFailureFunc = [this, &context] {
|
||||
onSyncEnded(context);
|
||||
};
|
||||
|
||||
_client.sendGETRequest(std::move(request));
|
||||
}
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/io_context_strand.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include "database/Types.hpp"
|
||||
#include "database/StarredTrackId.hpp"
|
||||
#include "database/Types.hpp"
|
||||
#include "database/UserId.hpp"
|
||||
|
||||
#include "FeedbackTypes.hpp"
|
||||
@@ -35,13 +36,13 @@ namespace lms
|
||||
{
|
||||
namespace core::http
|
||||
{
|
||||
class IClient;
|
||||
class IClient;
|
||||
}
|
||||
namespace db
|
||||
{
|
||||
class Db;
|
||||
}
|
||||
}
|
||||
} // namespace lms
|
||||
|
||||
namespace lms::feedback::listenBrainz
|
||||
{
|
||||
@@ -59,22 +60,23 @@ namespace lms::feedback::listenBrainz
|
||||
|
||||
struct UserContext
|
||||
{
|
||||
UserContext(db::UserId id) : userId{ id } {}
|
||||
UserContext(db::UserId id)
|
||||
: userId{ id } {}
|
||||
|
||||
UserContext(const UserContext&) = delete;
|
||||
UserContext& operator=(const UserContext&) = delete;
|
||||
|
||||
const db::UserId userId;
|
||||
bool syncing{};
|
||||
std::optional<std::size_t> feedbackCount{};
|
||||
|
||||
const db::UserId userId;
|
||||
bool syncing{};
|
||||
std::optional<std::size_t> feedbackCount{};
|
||||
|
||||
// resetted at each sync
|
||||
std::string listenBrainzUserName; // need to be resolved first
|
||||
std::string listenBrainzUserName; // need to be resolved first
|
||||
|
||||
std::size_t currentOffset{};
|
||||
std::size_t fetchedFeedbackCount{};
|
||||
std::size_t matchedFeedbackCount{};
|
||||
std::size_t importedFeedbackCount{};
|
||||
std::size_t currentOffset{};
|
||||
std::size_t fetchedFeedbackCount{};
|
||||
std::size_t matchedFeedbackCount{};
|
||||
std::size_t importedFeedbackCount{};
|
||||
};
|
||||
|
||||
UserContext& getUserContext(db::UserId userId);
|
||||
@@ -90,15 +92,14 @@ namespace lms::feedback::listenBrainz
|
||||
void tryImportFeedback(const Feedback& feedback, UserContext& context);
|
||||
|
||||
boost::asio::io_context& _ioContext;
|
||||
boost::asio::io_context::strand _strand{ _ioContext };
|
||||
boost::asio::io_context::strand _strand{ _ioContext };
|
||||
db::Db& _db;
|
||||
boost::asio::steady_timer _syncTimer{ _ioContext };
|
||||
boost::asio::steady_timer _syncTimer{ _ioContext };
|
||||
core::http::IClient& _client;
|
||||
|
||||
std::unordered_map<db::UserId, UserContext> _userContexts;
|
||||
|
||||
const std::size_t _maxSyncFeedbackCount;
|
||||
const std::chrono::hours _syncFeedbacksPeriod;
|
||||
const std::size_t _maxSyncFeedbackCount;
|
||||
const std::chrono::hours _syncFeedbacksPeriod;
|
||||
};
|
||||
} // feedback::ListenBrainz
|
||||
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
|
||||
@@ -19,22 +19,23 @@
|
||||
|
||||
#include "ListenBrainzBackend.hpp"
|
||||
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "core/http/IClient.hpp"
|
||||
#include "database/Db.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/StarredArtist.hpp"
|
||||
#include "database/StarredRelease.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/http/IClient.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/Service.hpp"
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace lms::feedback::listenBrainz
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename StarredObjType>
|
||||
template<typename StarredObjType>
|
||||
void onStarred(db::Session& session, typename StarredObjType::IdType id)
|
||||
{
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
@@ -46,7 +47,7 @@ namespace lms::feedback::listenBrainz
|
||||
}
|
||||
}
|
||||
|
||||
template <typename StarredObjType>
|
||||
template<typename StarredObjType>
|
||||
void onUnstarred(db::Session& session, typename StarredObjType::IdType id)
|
||||
{
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
@@ -54,7 +55,7 @@ namespace lms::feedback::listenBrainz
|
||||
if (auto starredObj{ StarredObjType::find(session, id) })
|
||||
starredObj.remove();
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
|
||||
ListenBrainzBackend::ListenBrainzBackend(boost::asio::io_context& ioContext, db::Db& db)
|
||||
: _ioContext{ ioContext }
|
||||
@@ -100,4 +101,4 @@ namespace lms::feedback::listenBrainz
|
||||
{
|
||||
_feedbacksSynchronizer.enqueFeedback(FeedbackType::Erase, starredtrackId);
|
||||
}
|
||||
} // namespace lms::scrobbling::listenBrainz
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include "IFeedbackBackend.hpp"
|
||||
#include "FeedbacksSynchronizer.hpp"
|
||||
#include "IFeedbackBackend.hpp"
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
@@ -52,8 +52,8 @@ namespace lms::feedback::listenBrainz
|
||||
|
||||
boost::asio::io_context& _ioContext;
|
||||
db::Db& _db;
|
||||
std::string _baseAPIUrl;
|
||||
std::unique_ptr<core::http::IClient> _client;
|
||||
FeedbacksSynchronizer _feedbacksSynchronizer;
|
||||
std::string _baseAPIUrl;
|
||||
std::unique_ptr<core::http::IClient> _client;
|
||||
FeedbacksSynchronizer _feedbacksSynchronizer;
|
||||
};
|
||||
}
|
||||
} // namespace lms::feedback::listenBrainz
|
||||
@@ -59,4 +59,4 @@ namespace lms::feedback::listenBrainz::utils
|
||||
listenBrainzUserName = root.get("user_name").orIfNull("");
|
||||
return listenBrainzUserName;
|
||||
}
|
||||
}
|
||||
} // namespace lms::feedback::listenBrainz::utils
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "database/UserId.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/UUID.hpp"
|
||||
#include "database/UserId.hpp"
|
||||
|
||||
#define LOG(sev, message) LMS_LOG(FEEDBACK, sev, "[listenbrainz] " << message)
|
||||
#define LOG(sev, message) LMS_LOG(FEEDBACK, sev, "[listenbrainz] " << message)
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
@@ -32,6 +32,6 @@ namespace lms::db
|
||||
|
||||
namespace lms::feedback::listenBrainz::utils
|
||||
{
|
||||
std::optional<core::UUID> getListenBrainzToken(db::Session& session, db::UserId userId);
|
||||
std::optional<core::UUID> getListenBrainzToken(db::Session& session, db::UserId userId);
|
||||
std::string parseValidateToken(std::string_view msgBody);
|
||||
}
|
||||
} // namespace lms::feedback::listenBrainz::utils
|
||||
|
||||
@@ -28,4 +28,4 @@ namespace lms::feedback
|
||||
public:
|
||||
using LmsException::LmsException;
|
||||
};
|
||||
}
|
||||
} // namespace lms::feedback
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#include "database/Types.hpp"
|
||||
#include <Wt/WDateTime.h>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
|
||||
#include "database/ArtistId.hpp"
|
||||
#include "database/ClusterId.hpp"
|
||||
#include "database/MediaLibraryId.hpp"
|
||||
#include "database/ReleaseId.hpp"
|
||||
#include "database/TrackId.hpp"
|
||||
#include "database/UserId.hpp"
|
||||
#include "database/Types.hpp"
|
||||
#include "database/UserId.hpp"
|
||||
|
||||
namespace lms::db
|
||||
{
|
||||
@@ -52,51 +52,78 @@ namespace lms::feedback
|
||||
|
||||
struct FindParameters
|
||||
{
|
||||
db::UserId user;
|
||||
std::vector<db::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords
|
||||
std::optional<db::Range> range;
|
||||
db::MediaLibraryId library;
|
||||
db::UserId user;
|
||||
std::vector<db::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords
|
||||
std::optional<db::Range> range;
|
||||
db::MediaLibraryId library;
|
||||
|
||||
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
||||
FindParameters& setClusters(std::span<const db::ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||
FindParameters& setUser(const db::UserId _user)
|
||||
{
|
||||
user = _user;
|
||||
return *this;
|
||||
}
|
||||
FindParameters& setClusters(std::span<const db::ClusterId> _clusters)
|
||||
{
|
||||
clusters.assign(std::cbegin(_clusters), std::cend(_clusters));
|
||||
return *this;
|
||||
}
|
||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords)
|
||||
{
|
||||
keywords = _keywords;
|
||||
return *this;
|
||||
}
|
||||
FindParameters& setRange(std::optional<db::Range> _range)
|
||||
{
|
||||
range = _range;
|
||||
return *this;
|
||||
}
|
||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library)
|
||||
{
|
||||
library = _library;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Artists
|
||||
struct ArtistFindParameters : public FindParameters
|
||||
{
|
||||
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 };
|
||||
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<db::TrackArtistLinkType> _linkType) { linkType = _linkType; return *this; }
|
||||
ArtistFindParameters& setSortMethod(db::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;
|
||||
}
|
||||
};
|
||||
|
||||
virtual void star(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual void unstar(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual bool isStarred(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual Wt::WDateTime getStarredDateTime(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual ArtistContainer findStarredArtists(const ArtistFindParameters& params) = 0;
|
||||
virtual void star(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual void unstar(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual bool isStarred(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual Wt::WDateTime getStarredDateTime(db::UserId userId, db::ArtistId artistId) = 0;
|
||||
virtual ArtistContainer findStarredArtists(const ArtistFindParameters& params) = 0;
|
||||
|
||||
// Releases
|
||||
virtual void star(db::UserId userId, db::ReleaseId releaseId) = 0;
|
||||
virtual void unstar(db::UserId userId, db::ReleaseId releaseId) = 0;
|
||||
virtual bool isStarred(db::UserId userId, db::ReleaseId artistId) = 0;
|
||||
virtual Wt::WDateTime getStarredDateTime(db::UserId userId, db::ReleaseId artistId) = 0;
|
||||
virtual ReleaseContainer findStarredReleases(const FindParameters& params) = 0;
|
||||
virtual void star(db::UserId userId, db::ReleaseId releaseId) = 0;
|
||||
virtual void unstar(db::UserId userId, db::ReleaseId releaseId) = 0;
|
||||
virtual bool isStarred(db::UserId userId, db::ReleaseId artistId) = 0;
|
||||
virtual Wt::WDateTime getStarredDateTime(db::UserId userId, db::ReleaseId artistId) = 0;
|
||||
virtual ReleaseContainer findStarredReleases(const FindParameters& params) = 0;
|
||||
|
||||
// Tracks
|
||||
virtual void star(db::UserId userId, db::TrackId trackId) = 0;
|
||||
virtual void unstar(db::UserId userId, db::TrackId trackId) = 0;
|
||||
virtual bool isStarred(db::UserId userId, db::TrackId artistId) = 0;
|
||||
virtual Wt::WDateTime getStarredDateTime(db::UserId userId, db::TrackId artistId) = 0;
|
||||
virtual TrackContainer findStarredTracks(const FindParameters& params) = 0;
|
||||
virtual void star(db::UserId userId, db::TrackId trackId) = 0;
|
||||
virtual void unstar(db::UserId userId, db::TrackId trackId) = 0;
|
||||
virtual bool isStarred(db::UserId userId, db::TrackId artistId) = 0;
|
||||
virtual Wt::WDateTime getStarredDateTime(db::UserId userId, db::TrackId artistId) = 0;
|
||||
virtual TrackContainer findStarredTracks(const FindParameters& params) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IFeedbackService> createFeedbackService(boost::asio::io_service& ioService, db::Db& db);
|
||||
|
||||
} // ns Feedback
|
||||
|
||||
} // namespace lms::feedback
|
||||
|
||||
Reference in New Issue
Block a user