diff --git a/src/libs/scrobbling/impl/IScrobbler.hpp b/src/libs/scrobbling/impl/IScrobbler.hpp index 9359a071..7587ce0e 100644 --- a/src/libs/scrobbling/impl/IScrobbler.hpp +++ b/src/libs/scrobbling/impl/IScrobbler.hpp @@ -43,7 +43,7 @@ namespace Scrobbling virtual ~IScrobbler() = default; virtual void listenStarted(const Listen& listen) = 0; - virtual void listenFinished(const Listen& listen, std::chrono::seconds duration) = 0; + virtual void listenFinished(const Listen& listen, std::optional duration) = 0; virtual void addListen(const Listen& listen, const Wt::WDateTime& timePoint) = 0; diff --git a/src/libs/scrobbling/impl/Scrobbling.cpp b/src/libs/scrobbling/impl/Scrobbling.cpp index 6b755f8e..14f84c7e 100644 --- a/src/libs/scrobbling/impl/Scrobbling.cpp +++ b/src/libs/scrobbling/impl/Scrobbling.cpp @@ -50,7 +50,7 @@ namespace Scrobbling } void - Scrobbling::listenFinished(const Listen& listen, std::chrono::seconds duration) + Scrobbling::listenFinished(const Listen& listen, std::optional duration) { if (auto scrobbler {getUserScrobbler(listen.userId)}) _scrobblers[*scrobbler]->listenFinished(listen, duration); diff --git a/src/libs/scrobbling/impl/Scrobbling.hpp b/src/libs/scrobbling/impl/Scrobbling.hpp index 6babe3f7..26f3cbb1 100644 --- a/src/libs/scrobbling/impl/Scrobbling.hpp +++ b/src/libs/scrobbling/impl/Scrobbling.hpp @@ -35,7 +35,7 @@ namespace Scrobbling private: void listenStarted(const Listen& listen) override; - void listenFinished(const Listen& listen, std::chrono::seconds duration) override; + void listenFinished(const Listen& listen, std::optional duration) override; void addListen(const Listen& listen, Wt::WDateTime timePoint) override; std::vector> getRecentArtists(Database::Session& session, diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp index cd0ff9d2..14a86adf 100644 --- a/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp +++ b/src/libs/scrobbling/impl/internal/InternalScrobbler.cpp @@ -35,15 +35,19 @@ namespace Scrobbling {} void - InternalScrobbler::listenStarted(const Listen& listen) + InternalScrobbler::listenStarted(const Listen& /*listen*/) { - addListen(listen, Wt::WDateTime::currentDateTime()); + // nothing to do } void - InternalScrobbler::listenFinished(const Listen& /*event*/, std::chrono::seconds /* duration */) + InternalScrobbler::listenFinished(const Listen& listen, std::optional duration) { - // nothing to do + // record tracks that have been played for at least of few seconds... + if (duration && *duration < std::chrono::seconds {5}) + return; + + addListen(listen, Wt::WDateTime::currentDateTime()); } void diff --git a/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp b/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp index 64cfb87b..e11ff43e 100644 --- a/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp +++ b/src/libs/scrobbling/impl/internal/InternalScrobbler.hpp @@ -30,7 +30,7 @@ namespace Scrobbling private: void listenStarted(const Listen& listen) override; - void listenFinished(const Listen& listen, std::chrono::seconds duration) override; + void listenFinished(const Listen& listen, std::optional duration) override; void addListen(const Listen& listen, const Wt::WDateTime& timePoint) override; diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp index 50cb4fb3..c7f1bd1a 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.cpp @@ -214,13 +214,13 @@ namespace Scrobbling } void - ListenBrainzScrobbler::listenFinished(const Listen& listen, std::chrono::seconds duration) + ListenBrainzScrobbler::listenFinished(const Listen& listen, std::optional duration) { - if (!canBeScrobbled(_db.getTLSSession(), listen.trackId, duration)) + if (duration && !canBeScrobbled(_db.getTLSSession(), listen.trackId, *duration)) return; Listen timedListen {listen}; - const Wt::WDateTime now {Wt::WDateTime::currentDateTime().addSecs(-duration.count())}; + const Wt::WDateTime now {Wt::WDateTime::currentDateTime()}; _ioService.post([=] { diff --git a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp index d053a81d..80c50fb2 100644 --- a/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp +++ b/src/libs/scrobbling/impl/listenbrainz/ListenBrainzScrobbler.hpp @@ -50,7 +50,7 @@ namespace Scrobbling private: void listenStarted(const Listen& listen) override; - void listenFinished(const Listen& listen, std::chrono::seconds duration) override; + void listenFinished(const Listen& listen, std::optional duration) override; void addListen(const Listen& listen, const Wt::WDateTime& timePoint) override; Wt::Dbo::ptr getListensTrackList(Database::Session& session, Wt::Dbo::ptr user) override; diff --git a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp index 3a5a3e0f..7b46436b 100644 --- a/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp +++ b/src/libs/scrobbling/include/scrobbling/IScrobbling.hpp @@ -49,7 +49,7 @@ namespace Scrobbling // Scrobbling virtual void listenStarted(const Listen& listen) = 0; - virtual void listenFinished(const Listen& listen, std::chrono::seconds duration) = 0; + virtual void listenFinished(const Listen& listen, std::optional playedDuration = std::nullopt) = 0; virtual void addListen(const Listen& listen, Wt::WDateTime timePoint) = 0; diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 854e8cf8..a9caad89 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -1644,52 +1644,46 @@ Response handleScrobble(RequestContext& context) { const std::vector ids {getMandatoryMultiParametersAs(context.parameters, "id")}; - const std::vector times {getMandatoryMultiParametersAs(context.parameters, "time")}; + const std::vector times {getMultiParametersAs(context.parameters, "time")}; + const bool submission{getParameterAs(context.parameters, "submission").value_or(true)}; + // only for tracks if (!std::all_of(std::cbegin(ids), std::cend(ids), [](const Id& id) { return id.type == Id::Type::Track; })) throw BadParameterGenericError {"id"}; - if (ids.size() != times.size()) + // playing now => no time to be provided + if (!submission && !times.empty()) throw BadParameterGenericError {"time"}; - struct Scrobble + // playing now => only one at a time + if (!submission && ids.size() > 1) + throw BadParameterGenericError {"id"}; + + // if multiple submissions, must have times + if (ids.size() > 1 && ids.size() != times.size()) + throw BadParameterGenericError {"time"}; + + if (!submission) { - Scrobbling::Listen listen; - Wt::WDateTime timePoint; - }; - - std::vector scrobbles; - scrobbles.reserve(ids.size()); - + Service::get()->listenStarted({context.userId, ids.front().value}); + } + else { - auto transaction {context.dbSession.createSharedTransaction()}; - - User::pointer user {User::getById(context.dbSession, context.userId)}; - if (!user) - throw RequestedDataNotFoundError {}; - - Scrobble scrobble; - scrobble.listen.userId = context.userId; - - for (std::size_t i {}; i < ids.size(); ++i) + if (times.empty()) { - const Id id {ids[i]}; - const unsigned long time {times[i]}; - - const Track::pointer track {Track::getById(context.dbSession, id.value)}; - if (!track) - continue; - - scrobble.listen.trackId = id.value; - scrobble.timePoint.setTime_t(static_cast(time / 1000)); - - scrobbles.emplace_back(scrobble); + Service::get()->listenFinished({context.userId, ids.front().value}); + } + else + { + for (std::size_t i {}; i < ids.size(); ++i) + { + const Database::IdType trackId {ids[i].value}; + const unsigned long time {times[i]}; + Service::get()->addListen({context.userId, trackId}, Wt::WDateTime::fromTime_t(static_cast(time / 1000))); + } } } - for (const Scrobble& scrobble : scrobbles) - Service::get()->addListen(scrobble.listen, scrobble.timePoint); - return Response::createOkResponse(context); }