Switched to C++20

This commit is contained in:
emeric
2024-02-01 22:34:36 +01:00
parent 82be2e1a41
commit 6cfa4e9de6
26 changed files with 365 additions and 378 deletions
@@ -68,7 +68,7 @@ namespace Av::Transcoding
{
Wt::Http::ResponseContinuation* continuation{ response.createContinuation() };
continuation->waitForMoreData();
_transcoder.asyncRead(_buffer.data(), _buffer.size(), [=](std::size_t nbBytesRead)
_transcoder.asyncRead(_buffer.data(), _buffer.size(), [this, continuation](std::size_t nbBytesRead)
{
LMS_LOG(TRANSCODING, DEBUG, "Have " << nbBytesRead << " more bytes to send back");
@@ -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);
};
+1 -1
View File
@@ -66,7 +66,7 @@ namespace Http
{
_client.done().connect([this](Wt::AsioWrapper::error_code ec, const Wt::Http::Message& msg)
{
_strand.dispatch([=, msg = std::move(msg)]
_strand.dispatch([this, ec, msg = std::move(msg)]
{
onClientDone(ec, msg);
});