Switched to C++20
This commit is contained in:
@@ -131,9 +131,9 @@ namespace Feedback::ListenBrainz
|
||||
request.message.addBodyText(Wt::Json::serialize(root));
|
||||
request.message.addHeader("Content-Type", "application/json");
|
||||
|
||||
request.onSuccessFunc = [=](std::string_view /*msgBody*/)
|
||||
request.onSuccessFunc = [this, type, starredTrackId](std::string_view /*msgBody*/)
|
||||
{
|
||||
_strand.dispatch([=]
|
||||
_strand.dispatch([this, type, starredTrackId]
|
||||
{
|
||||
onFeedbackSent(type, starredTrackId);
|
||||
});
|
||||
@@ -404,7 +404,7 @@ namespace Feedback::ListenBrainz
|
||||
}
|
||||
});
|
||||
};
|
||||
request.onFailureFunc = [=, &context]
|
||||
request.onFailureFunc = [this, &context]
|
||||
{
|
||||
onSyncEnded(context);
|
||||
};
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Scanner
|
||||
void ScannerService::requestImmediateScan(bool force)
|
||||
{
|
||||
abortScan();
|
||||
_ioService.post([=]()
|
||||
_ioService.post([this, force]
|
||||
{
|
||||
if (_abortScan)
|
||||
return;
|
||||
@@ -156,7 +156,7 @@ namespace Scanner
|
||||
void ScannerService::requestReload()
|
||||
{
|
||||
abortScan();
|
||||
_ioService.post([=]()
|
||||
_ioService.post([this]()
|
||||
{
|
||||
if (_abortScan)
|
||||
return;
|
||||
@@ -234,7 +234,7 @@ namespace Scanner
|
||||
|
||||
void ScannerService::scheduleScan(bool force, const Wt::WDateTime& dateTime)
|
||||
{
|
||||
auto cb{ [=](boost::system::error_code ec)
|
||||
auto cb{ [this, force](boost::system::error_code ec)
|
||||
{
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
@@ -40,175 +40,173 @@
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace Scrobbling::ListenBrainz;
|
||||
|
||||
std::optional<Wt::Json::Object> listenToJsonPayload(Database::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) };
|
||||
if (!track)
|
||||
return std::nullopt;
|
||||
|
||||
auto artists{ track->getArtists({Database::TrackArtistLinkType::Artist}) };
|
||||
if (artists.empty())
|
||||
artists = track->getArtists({ Database::TrackArtistLinkType::ReleaseArtist });
|
||||
|
||||
if (artists.empty())
|
||||
{
|
||||
LOG(DEBUG, "Track cannot be scrobbled since it does not have any artist");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Wt::Json::Object additionalInfo;
|
||||
additionalInfo["listening_from"] = "LMS";
|
||||
additionalInfo["duration_ms"] = std::chrono::duration_cast<std::chrono::milliseconds>(track->getDuration()).count();
|
||||
if (track->getRelease())
|
||||
{
|
||||
if (auto MBID{ track->getRelease()->getMBID() })
|
||||
additionalInfo["release_mbid"] = Wt::Json::Value{ std::string {MBID->getAsString()} };
|
||||
}
|
||||
|
||||
{
|
||||
Wt::Json::Array artistMBIDs;
|
||||
for (const Database::Artist::pointer& artist : artists)
|
||||
{
|
||||
if (auto MBID{ artist->getMBID() })
|
||||
artistMBIDs.push_back(Wt::Json::Value{ std::string {MBID->getAsString()} });
|
||||
}
|
||||
|
||||
if (!artistMBIDs.empty())
|
||||
additionalInfo["artist_mbids"] = std::move(artistMBIDs);
|
||||
}
|
||||
|
||||
if (auto MBID{ track->getTrackMBID() })
|
||||
additionalInfo["track_mbid"] = Wt::Json::Value{ std::string {MBID->getAsString()} };
|
||||
|
||||
if (auto MBID{ track->getRecordingMBID() })
|
||||
additionalInfo["recording_mbid"] = Wt::Json::Value{ std::string {MBID->getAsString()} };
|
||||
|
||||
if (const std::optional<std::size_t> trackNumber{ track->getTrackNumber() })
|
||||
additionalInfo["tracknumber"] = Wt::Json::Value{ static_cast<long long int>(*trackNumber) };
|
||||
|
||||
Wt::Json::Object trackMetadata;
|
||||
trackMetadata["additional_info"] = std::move(additionalInfo);
|
||||
trackMetadata["artist_name"] = Wt::Json::Value{ artists.front()->getName() };
|
||||
trackMetadata["track_name"] = Wt::Json::Value{ track->getName() };
|
||||
if (track->getRelease())
|
||||
trackMetadata["release_name"] = Wt::Json::Value{ track->getRelease()->getName() };
|
||||
|
||||
Wt::Json::Object payload;
|
||||
payload["track_metadata"] = std::move(trackMetadata);
|
||||
if (timePoint.isValid())
|
||||
payload["listened_at"] = Wt::Json::Value{ static_cast<long long int>(timePoint.toTime_t()) };
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
std::string listenToJsonString(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint, std::string_view listenType)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
std::optional<Wt::Json::Object> payload{ listenToJsonPayload(session, listen, timePoint) };
|
||||
if (!payload)
|
||||
return res;
|
||||
|
||||
Wt::Json::Object root;
|
||||
root["listen_type"] = Wt::Json::Value{ std::string {listenType} };
|
||||
root["payload"] = Wt::Json::Array{ std::move(*payload) };
|
||||
|
||||
res = Wt::Json::serialize(root);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> parseListenCount(std::string_view msgBody)
|
||||
{
|
||||
try
|
||||
{
|
||||
Wt::Json::Object root;
|
||||
Wt::Json::parse(std::string{ msgBody }, root);
|
||||
|
||||
const Wt::Json::Object& payload{ static_cast<const Wt::Json::Object&>(root.get("payload")) };
|
||||
return static_cast<int>(payload.get("count"));
|
||||
}
|
||||
catch (const Wt::WException& e)
|
||||
{
|
||||
LOG(ERROR, "Cannot parse listen count response: " << e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Database::TrackId tryGetMatchingTrack(Database::Session& session, const Listen& listen)
|
||||
{
|
||||
using namespace Database;
|
||||
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
// first try to match using track MBID, and then fallback on possibly ambiguous info
|
||||
if (listen.trackMBID)
|
||||
{
|
||||
const auto tracks{ Track::findByMBID(session, *listen.trackMBID) };
|
||||
// if duplicated files, do not record it (let the user correct its database)
|
||||
if (tracks.size() == 1)
|
||||
{
|
||||
LOG(DEBUG, "Matched listen '" << listen << "' using track MBID");
|
||||
return tracks.front()->getId();
|
||||
}
|
||||
else if (tracks.size() > 1)
|
||||
{
|
||||
LOG(DEBUG, "Too many matches for listen '" << listen << "' using track MBID!");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (listen.recordingMBID)
|
||||
{
|
||||
const auto tracks{ Track::findByRecordingMBID(session, *listen.recordingMBID) };
|
||||
// if duplicated files, do not record it (let the user correct its database)
|
||||
if (tracks.size() == 1)
|
||||
{
|
||||
LOG(DEBUG, "Matched listen '" << listen << "' using recording MBID");
|
||||
return tracks.front()->getId();
|
||||
}
|
||||
else if (tracks.size() > 1)
|
||||
{
|
||||
LOG(DEBUG, "Too many matches for listen '" << listen << "' using recording MBID!");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
assert(!listen.trackName.empty() && !listen.artistName.empty());
|
||||
|
||||
// TODO check release MBID?
|
||||
Track::FindParameters params;
|
||||
params.setName(listen.trackName);
|
||||
params.setReleaseName(listen.releaseName);
|
||||
params.setArtistName(listen.artistName);
|
||||
if (listen.trackNumber)
|
||||
params.setTrackNumber(*listen.trackNumber);
|
||||
|
||||
const auto tracks{ Track::findIds(session, params) };
|
||||
// conservative behavior: in case of multiple matches: reject
|
||||
if (tracks.results.size() == 1)
|
||||
{
|
||||
LOG(DEBUG, "Matched listen '" << listen << "' using metadata");
|
||||
return tracks.results.front();
|
||||
}
|
||||
else if (tracks.results.size() > 1)
|
||||
{
|
||||
LOG(DEBUG, "Too many matches for listen '" << listen << "' using metadata");
|
||||
return {};
|
||||
}
|
||||
|
||||
LOG(DEBUG, "No match for listen '" << listen << "'");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
namespace Scrobbling::ListenBrainz
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::optional<Wt::Json::Object> listenToJsonPayload(Database::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) };
|
||||
if (!track)
|
||||
return std::nullopt;
|
||||
|
||||
auto artists{ track->getArtists({Database::TrackArtistLinkType::Artist}) };
|
||||
if (artists.empty())
|
||||
artists = track->getArtists({ Database::TrackArtistLinkType::ReleaseArtist });
|
||||
|
||||
if (artists.empty())
|
||||
{
|
||||
LOG(DEBUG, "Track cannot be scrobbled since it does not have any artist");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Wt::Json::Object additionalInfo;
|
||||
additionalInfo["listening_from"] = "LMS";
|
||||
additionalInfo["duration_ms"] = std::chrono::duration_cast<std::chrono::milliseconds>(track->getDuration()).count();
|
||||
if (track->getRelease())
|
||||
{
|
||||
if (auto MBID{ track->getRelease()->getMBID() })
|
||||
additionalInfo["release_mbid"] = Wt::Json::Value{ std::string {MBID->getAsString()} };
|
||||
}
|
||||
|
||||
{
|
||||
Wt::Json::Array artistMBIDs;
|
||||
for (const Database::Artist::pointer& artist : artists)
|
||||
{
|
||||
if (auto MBID{ artist->getMBID() })
|
||||
artistMBIDs.push_back(Wt::Json::Value{ std::string {MBID->getAsString()} });
|
||||
}
|
||||
|
||||
if (!artistMBIDs.empty())
|
||||
additionalInfo["artist_mbids"] = std::move(artistMBIDs);
|
||||
}
|
||||
|
||||
if (auto MBID{ track->getTrackMBID() })
|
||||
additionalInfo["track_mbid"] = Wt::Json::Value{ std::string {MBID->getAsString()} };
|
||||
|
||||
if (auto MBID{ track->getRecordingMBID() })
|
||||
additionalInfo["recording_mbid"] = Wt::Json::Value{ std::string {MBID->getAsString()} };
|
||||
|
||||
if (const std::optional<std::size_t> trackNumber{ track->getTrackNumber() })
|
||||
additionalInfo["tracknumber"] = Wt::Json::Value{ static_cast<long long int>(*trackNumber) };
|
||||
|
||||
Wt::Json::Object trackMetadata;
|
||||
trackMetadata["additional_info"] = std::move(additionalInfo);
|
||||
trackMetadata["artist_name"] = Wt::Json::Value{ artists.front()->getName() };
|
||||
trackMetadata["track_name"] = Wt::Json::Value{ track->getName() };
|
||||
if (track->getRelease())
|
||||
trackMetadata["release_name"] = Wt::Json::Value{ track->getRelease()->getName() };
|
||||
|
||||
Wt::Json::Object payload;
|
||||
payload["track_metadata"] = std::move(trackMetadata);
|
||||
if (timePoint.isValid())
|
||||
payload["listened_at"] = Wt::Json::Value{ static_cast<long long int>(timePoint.toTime_t()) };
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
std::string listenToJsonString(Database::Session& session, const Scrobbling::Listen& listen, const Wt::WDateTime& timePoint, std::string_view listenType)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
std::optional<Wt::Json::Object> payload{ listenToJsonPayload(session, listen, timePoint) };
|
||||
if (!payload)
|
||||
return res;
|
||||
|
||||
Wt::Json::Object root;
|
||||
root["listen_type"] = Wt::Json::Value{ std::string {listenType} };
|
||||
root["payload"] = Wt::Json::Array{ std::move(*payload) };
|
||||
|
||||
res = Wt::Json::serialize(root);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> parseListenCount(std::string_view msgBody)
|
||||
{
|
||||
try
|
||||
{
|
||||
Wt::Json::Object root;
|
||||
Wt::Json::parse(std::string{ msgBody }, root);
|
||||
|
||||
const Wt::Json::Object& payload{ static_cast<const Wt::Json::Object&>(root.get("payload")) };
|
||||
return static_cast<int>(payload.get("count"));
|
||||
}
|
||||
catch (const Wt::WException& e)
|
||||
{
|
||||
LOG(ERROR, "Cannot parse listen count response: " << e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Database::TrackId tryGetMatchingTrack(Database::Session& session, const Listen& listen)
|
||||
{
|
||||
using namespace Database;
|
||||
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
// first try to match using track MBID, and then fallback on possibly ambiguous info
|
||||
if (listen.trackMBID)
|
||||
{
|
||||
const auto tracks{ Track::findByMBID(session, *listen.trackMBID) };
|
||||
// if duplicated files, do not record it (let the user correct its database)
|
||||
if (tracks.size() == 1)
|
||||
{
|
||||
LOG(DEBUG, "Matched listen '" << listen << "' using track MBID");
|
||||
return tracks.front()->getId();
|
||||
}
|
||||
else if (tracks.size() > 1)
|
||||
{
|
||||
LOG(DEBUG, "Too many matches for listen '" << listen << "' using track MBID!");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (listen.recordingMBID)
|
||||
{
|
||||
const auto tracks{ Track::findByRecordingMBID(session, *listen.recordingMBID) };
|
||||
// if duplicated files, do not record it (let the user correct its database)
|
||||
if (tracks.size() == 1)
|
||||
{
|
||||
LOG(DEBUG, "Matched listen '" << listen << "' using recording MBID");
|
||||
return tracks.front()->getId();
|
||||
}
|
||||
else if (tracks.size() > 1)
|
||||
{
|
||||
LOG(DEBUG, "Too many matches for listen '" << listen << "' using recording MBID!");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
assert(!listen.trackName.empty() && !listen.artistName.empty());
|
||||
|
||||
// TODO check release MBID?
|
||||
Track::FindParameters params;
|
||||
params.setName(listen.trackName);
|
||||
params.setReleaseName(listen.releaseName);
|
||||
params.setArtistName(listen.artistName);
|
||||
if (listen.trackNumber)
|
||||
params.setTrackNumber(*listen.trackNumber);
|
||||
|
||||
const auto tracks{ Track::findIds(session, params) };
|
||||
// conservative behavior: in case of multiple matches: reject
|
||||
if (tracks.results.size() == 1)
|
||||
{
|
||||
LOG(DEBUG, "Matched listen '" << listen << "' using metadata");
|
||||
return tracks.results.front();
|
||||
}
|
||||
else if (tracks.results.size() > 1)
|
||||
{
|
||||
LOG(DEBUG, "Too many matches for listen '" << listen << "' using metadata");
|
||||
return {};
|
||||
}
|
||||
|
||||
LOG(DEBUG, "No match for listen '" << listen << "'");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
ListensSynchronizer::ListensSynchronizer(boost::asio::io_context& ioContext, Database::Db& db, Http::IClient& client)
|
||||
: _ioContext{ ioContext }
|
||||
, _db{ db }
|
||||
@@ -244,13 +242,13 @@ namespace Scrobbling::ListenBrainz
|
||||
saveListen(timedListen, Database::SyncState::PendingAdd);
|
||||
|
||||
request.priority = Http::ClientRequestParameters::Priority::Normal;
|
||||
request.onSuccessFunc = [=](std::string_view)
|
||||
request.onSuccessFunc = [this, timedListen](std::string_view)
|
||||
{
|
||||
_strand.dispatch([=]
|
||||
_strand.dispatch([this, timedListen]
|
||||
{
|
||||
if (saveListen(timedListen, Database::SyncState::Synchronized))
|
||||
{
|
||||
UserContext& context{ getUserContext(listen.userId) };
|
||||
UserContext& context{ getUserContext(timedListen.userId) };
|
||||
if (context.listenCount)
|
||||
(*context.listenCount)++;
|
||||
}
|
||||
@@ -484,11 +482,11 @@ namespace Scrobbling::ListenBrainz
|
||||
Http::ClientGETRequestParameters request;
|
||||
request.relativeUrl = "/1/user/" + std::string{ context.listenBrainzUserName } + "/listen-count";
|
||||
request.priority = Http::ClientRequestParameters::Priority::Low;
|
||||
request.onSuccessFunc = [=, &context](std::string_view msgBody)
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody)
|
||||
{
|
||||
_strand.dispatch([=, &context]
|
||||
const auto listenCount{ parseListenCount(msgBody) };
|
||||
_strand.dispatch([this, listenCount, &context]
|
||||
{
|
||||
const auto listenCount = parseListenCount(msgBody);
|
||||
if (listenCount)
|
||||
LOG(DEBUG, "Listen count for listenbrainz user '" << context.listenBrainzUserName << "' = " << *listenCount);
|
||||
|
||||
@@ -520,7 +518,7 @@ namespace Scrobbling::ListenBrainz
|
||||
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.onSuccessFunc = [=, &context](std::string_view msgBody)
|
||||
request.onSuccessFunc = [this, &context](std::string_view msgBody)
|
||||
{
|
||||
processGetListensResponse(msgBody, context);
|
||||
if (context.fetchedListenCount >= _maxSyncListenCount || !context.maxDateTime.isValid())
|
||||
@@ -531,7 +529,7 @@ namespace Scrobbling::ListenBrainz
|
||||
|
||||
enqueGetListens(context);
|
||||
};
|
||||
request.onFailureFunc = [=, &context]
|
||||
request.onFailureFunc = [this, &context]
|
||||
{
|
||||
onSyncEnded(context);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user