@@ -195,7 +195,7 @@ void AudioScrobbler::Submit() {
|
|||||||
|
|
||||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||||
if (!service->IsEnabled() || !service->IsAuthenticated() || service->IsSubmitted()) continue;
|
if (!service->IsEnabled() || !service->IsAuthenticated() || service->IsSubmitted()) continue;
|
||||||
service->DoSubmit();
|
service->StartSubmit();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
@@ -73,7 +75,8 @@ ListenBrainzScrobbler::ListenBrainzScrobbler(Application *app, QObject *parent)
|
|||||||
login_time_(0),
|
login_time_(0),
|
||||||
submitted_(false),
|
submitted_(false),
|
||||||
scrobbled_(false),
|
scrobbled_(false),
|
||||||
timestamp_(0) {
|
timestamp_(0),
|
||||||
|
submit_error_(false) {
|
||||||
|
|
||||||
refresh_login_timer_.setSingleShot(true);
|
refresh_login_timer_.setSingleShot(true);
|
||||||
QObject::connect(&refresh_login_timer_, &QTimer::timeout, this, &ListenBrainzScrobbler::RequestNewAccessToken);
|
QObject::connect(&refresh_login_timer_, &QTimer::timeout, this, &ListenBrainzScrobbler::RequestNewAccessToken);
|
||||||
@@ -339,7 +342,7 @@ void ListenBrainzScrobbler::AuthenticateReplyFinished(QNetworkReply *reply) {
|
|||||||
|
|
||||||
qLog(Debug) << "ListenBrainz: Authentication was successful, login expires in" << expires_in_;
|
qLog(Debug) << "ListenBrainz: Authentication was successful, login expires in" << expires_in_;
|
||||||
|
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,25 +512,22 @@ void ListenBrainzScrobbler::Scrobble(const Song &song) {
|
|||||||
|
|
||||||
if (app_->scrobbler()->IsOffline() || !IsAuthenticated()) return;
|
if (app_->scrobbler()->IsOffline() || !IsAuthenticated()) return;
|
||||||
|
|
||||||
if (!submitted_) {
|
StartSubmit();
|
||||||
submitted_ = true;
|
|
||||||
if (app_->scrobbler()->SubmitDelay() <= 0) {
|
|
||||||
Submit();
|
|
||||||
}
|
|
||||||
else if (!timer_submit_.isActive()) {
|
|
||||||
timer_submit_.setInterval(static_cast<int>(app_->scrobbler()->SubmitDelay() * kMsecPerSec));
|
|
||||||
timer_submit_.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListenBrainzScrobbler::DoSubmit() {
|
void ListenBrainzScrobbler::StartSubmit(const bool initial) {
|
||||||
|
|
||||||
if (!submitted_ && cache_->Count() > 0) {
|
if (!submitted_ && cache_->Count() > 0) {
|
||||||
submitted_ = true;
|
if (initial && app_->scrobbler()->SubmitDelay() <= 0 && !submit_error_) {
|
||||||
if (!timer_submit_.isActive()) {
|
if (timer_submit_.isActive()) {
|
||||||
timer_submit_.setInterval(static_cast<int>(app_->scrobbler()->SubmitDelay() * kMsecPerSec));
|
timer_submit_.stop();
|
||||||
|
}
|
||||||
|
Submit();
|
||||||
|
}
|
||||||
|
else if (!timer_submit_.isActive()) {
|
||||||
|
int submit_delay = static_cast<int>(std::max(app_->scrobbler()->SubmitDelay(), submit_error_ ? 30 : 5) * kMsecPerSec);
|
||||||
|
timer_submit_.setInterval(submit_delay);
|
||||||
timer_submit_.start();
|
timer_submit_.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,8 +538,6 @@ void ListenBrainzScrobbler::Submit() {
|
|||||||
|
|
||||||
qLog(Debug) << "ListenBrainz: Submitting scrobbles.";
|
qLog(Debug) << "ListenBrainz: Submitting scrobbles.";
|
||||||
|
|
||||||
submitted_ = false;
|
|
||||||
|
|
||||||
if (!IsEnabled() || !IsAuthenticated() || app_->scrobbler()->IsOffline()) return;
|
if (!IsEnabled() || !IsAuthenticated() || app_->scrobbler()->IsOffline()) return;
|
||||||
|
|
||||||
QJsonArray array;
|
QJsonArray array;
|
||||||
@@ -573,6 +571,8 @@ void ListenBrainzScrobbler::Submit() {
|
|||||||
|
|
||||||
if (i <= 0) return;
|
if (i <= 0) return;
|
||||||
|
|
||||||
|
submitted_ = true;
|
||||||
|
|
||||||
QJsonObject object;
|
QJsonObject object;
|
||||||
object.insert("listen_type", "import");
|
object.insert("listen_type", "import");
|
||||||
object.insert("payload", array);
|
object.insert("payload", array);
|
||||||
@@ -591,17 +591,21 @@ void ListenBrainzScrobbler::ScrobbleRequestFinished(QNetworkReply *reply, const
|
|||||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
|
submitted_ = false;
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply);
|
QByteArray data = GetReplyData(reply);
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
cache_->ClearSent(list);
|
cache_->ClearSent(list);
|
||||||
DoSubmit();
|
submit_error_ = true;
|
||||||
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject json_obj = ExtractJsonObj(data);
|
QJsonObject json_obj = ExtractJsonObj(data);
|
||||||
if (json_obj.isEmpty()) {
|
if (json_obj.isEmpty()) {
|
||||||
cache_->ClearSent(list);
|
cache_->ClearSent(list);
|
||||||
DoSubmit();
|
submit_error_ = true;
|
||||||
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,7 +613,8 @@ void ListenBrainzScrobbler::ScrobbleRequestFinished(QNetworkReply *reply, const
|
|||||||
QString error_desc = json_obj["error_description"].toString();
|
QString error_desc = json_obj["error_description"].toString();
|
||||||
Error(error_desc);
|
Error(error_desc);
|
||||||
cache_->ClearSent(list);
|
cache_->ClearSent(list);
|
||||||
DoSubmit();
|
submit_error_ = true;
|
||||||
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,7 +624,8 @@ void ListenBrainzScrobbler::ScrobbleRequestFinished(QNetworkReply *reply, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
cache_->Flush(list);
|
cache_->Flush(list);
|
||||||
DoSubmit();
|
submit_error_ = false;
|
||||||
|
StartSubmit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
|
|||||||
void AuthError(const QString &error);
|
void AuthError(const QString &error);
|
||||||
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||||
void RequestAccessToken(const QUrl &redirect_url = QUrl(), const QString &code = QString());
|
void RequestAccessToken(const QUrl &redirect_url = QUrl(), const QString &code = QString());
|
||||||
void DoSubmit() override;
|
void StartSubmit(const bool initial = false) override;
|
||||||
void CheckScrobblePrevSong();
|
void CheckScrobblePrevSong();
|
||||||
|
|
||||||
static const char *kOAuthAuthorizeUrl;
|
static const char *kOAuthAuthorizeUrl;
|
||||||
@@ -118,6 +118,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
|
|||||||
quint64 timestamp_;
|
quint64 timestamp_;
|
||||||
QTimer refresh_login_timer_;
|
QTimer refresh_login_timer_;
|
||||||
QTimer timer_submit_;
|
QTimer timer_submit_;
|
||||||
|
bool submit_error_;
|
||||||
|
|
||||||
QList<QNetworkReply*> replies_;
|
QList<QNetworkReply*> replies_;
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ScrobblerService : public QObject {
|
|||||||
virtual void Love() {}
|
virtual void Love() {}
|
||||||
virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0;
|
virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0;
|
||||||
|
|
||||||
virtual void DoSubmit() = 0;
|
virtual void StartSubmit(const bool initial = false) = 0;
|
||||||
virtual void Submitted() = 0;
|
virtual void Submitted() = 0;
|
||||||
virtual bool IsSubmitted() const { return false; }
|
virtual bool IsSubmitted() const { return false; }
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ ScrobblingAPI20::ScrobblingAPI20(const QString &name, const QString &settings_gr
|
|||||||
subscriber_(false),
|
subscriber_(false),
|
||||||
submitted_(false),
|
submitted_(false),
|
||||||
scrobbled_(false),
|
scrobbled_(false),
|
||||||
timestamp_(0) {
|
timestamp_(0),
|
||||||
|
submit_error_(false) {
|
||||||
|
|
||||||
timer_submit_.setSingleShot(true);
|
timer_submit_.setSingleShot(true);
|
||||||
QObject::connect(&timer_submit_, &QTimer::timeout, this, &ScrobblingAPI20::Submit);
|
QObject::connect(&timer_submit_, &QTimer::timeout, this, &ScrobblingAPI20::Submit);
|
||||||
@@ -345,7 +346,7 @@ void ScrobblingAPI20::AuthenticateReplyFinished(QNetworkReply *reply) {
|
|||||||
|
|
||||||
emit AuthenticationComplete(true);
|
emit AuthenticationComplete(true);
|
||||||
|
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,25 +537,22 @@ void ScrobblingAPI20::Scrobble(const Song &song) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!submitted_) {
|
StartSubmit(true);
|
||||||
submitted_ = true;
|
|
||||||
if (!batch_ || app_->scrobbler()->SubmitDelay() <= 0) {
|
|
||||||
Submit();
|
|
||||||
}
|
|
||||||
else if (!timer_submit_.isActive()) {
|
|
||||||
timer_submit_.setInterval(static_cast<int>(app_->scrobbler()->SubmitDelay() * kMsecPerSec));
|
|
||||||
timer_submit_.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrobblingAPI20::DoSubmit() {
|
void ScrobblingAPI20::StartSubmit(const bool initial) {
|
||||||
|
|
||||||
if (!submitted_ && cache()->Count() > 0) {
|
if (!submitted_ && cache()->Count() > 0) {
|
||||||
submitted_ = true;
|
if (initial && (!batch_ || app_->scrobbler()->SubmitDelay() <= 0) && !submit_error_) {
|
||||||
if (!timer_submit_.isActive()) {
|
if (timer_submit_.isActive()) {
|
||||||
timer_submit_.setInterval(static_cast<int>(app_->scrobbler()->SubmitDelay() * kMsecPerSec));
|
timer_submit_.stop();
|
||||||
|
}
|
||||||
|
Submit();
|
||||||
|
}
|
||||||
|
else if (!timer_submit_.isActive()) {
|
||||||
|
int submit_delay = static_cast<int>(std::max(app_->scrobbler()->SubmitDelay(), submit_error_ ? 30 : 5) * kMsecPerSec);
|
||||||
|
timer_submit_.setInterval(submit_delay);
|
||||||
timer_submit_.start();
|
timer_submit_.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -563,8 +561,6 @@ void ScrobblingAPI20::DoSubmit() {
|
|||||||
|
|
||||||
void ScrobblingAPI20::Submit() {
|
void ScrobblingAPI20::Submit() {
|
||||||
|
|
||||||
submitted_ = false;
|
|
||||||
|
|
||||||
if (!IsEnabled() || !IsAuthenticated() || app_->scrobbler()->IsOffline()) return;
|
if (!IsEnabled() || !IsAuthenticated() || app_->scrobbler()->IsOffline()) return;
|
||||||
|
|
||||||
qLog(Debug) << name_ << "Submitting scrobbles.";
|
qLog(Debug) << name_ << "Submitting scrobbles.";
|
||||||
@@ -601,6 +597,8 @@ void ScrobblingAPI20::Submit() {
|
|||||||
|
|
||||||
if (!batch_ || i <= 0) return;
|
if (!batch_ || i <= 0) return;
|
||||||
|
|
||||||
|
submitted_ = true;
|
||||||
|
|
||||||
QNetworkReply *reply = CreateRequest(params);
|
QNetworkReply *reply = CreateRequest(params);
|
||||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, list]() { ScrobbleRequestFinished(reply, list); });
|
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, list]() { ScrobbleRequestFinished(reply, list); });
|
||||||
|
|
||||||
@@ -613,17 +611,21 @@ void ScrobblingAPI20::ScrobbleRequestFinished(QNetworkReply *reply, const QList<
|
|||||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
|
submitted_ = false;
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply);
|
QByteArray data = GetReplyData(reply);
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
cache()->ClearSent(list);
|
cache()->ClearSent(list);
|
||||||
DoSubmit();
|
submit_error_ = true;
|
||||||
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject json_obj = ExtractJsonObj(data);
|
QJsonObject json_obj = ExtractJsonObj(data);
|
||||||
if (json_obj.isEmpty()) {
|
if (json_obj.isEmpty()) {
|
||||||
cache()->ClearSent(list);
|
cache()->ClearSent(list);
|
||||||
DoSubmit();
|
submit_error_ = true;
|
||||||
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,52 +635,53 @@ void ScrobblingAPI20::ScrobbleRequestFinished(QNetworkReply *reply, const QList<
|
|||||||
QString error_reason = QString("%1 (%2)").arg(error_message).arg(error_code);
|
QString error_reason = QString("%1 (%2)").arg(error_message).arg(error_code);
|
||||||
Error(error_reason);
|
Error(error_reason);
|
||||||
cache()->ClearSent(list);
|
cache()->ClearSent(list);
|
||||||
DoSubmit();
|
submit_error_ = true;
|
||||||
return;
|
StartSubmit();
|
||||||
}
|
|
||||||
|
|
||||||
if (!json_obj.contains("scrobbles")) {
|
|
||||||
Error("Json reply from server is missing scrobbles.", json_obj);
|
|
||||||
cache()->ClearSent(list);
|
|
||||||
DoSubmit();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache()->Flush(list);
|
cache()->Flush(list);
|
||||||
|
submit_error_ = false;
|
||||||
|
|
||||||
|
if (!json_obj.contains("scrobbles")) {
|
||||||
|
Error("Json reply from server is missing scrobbles.", json_obj);
|
||||||
|
StartSubmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QJsonValue value_scrobbles = json_obj["scrobbles"];
|
QJsonValue value_scrobbles = json_obj["scrobbles"];
|
||||||
if (!value_scrobbles.isObject()) {
|
if (!value_scrobbles.isObject()) {
|
||||||
Error("Json scrobbles is not an object.", json_obj);
|
Error("Json scrobbles is not an object.", json_obj);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
json_obj = value_scrobbles.toObject();
|
json_obj = value_scrobbles.toObject();
|
||||||
if (json_obj.isEmpty()) {
|
if (json_obj.isEmpty()) {
|
||||||
Error("Json scrobbles object is empty.", value_scrobbles);
|
Error("Json scrobbles object is empty.", value_scrobbles);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!json_obj.contains("@attr") || !json_obj.contains("scrobble")) {
|
if (!json_obj.contains("@attr") || !json_obj.contains("scrobble")) {
|
||||||
Error("Json scrobbles object is missing values.", json_obj);
|
Error("Json scrobbles object is missing values.", json_obj);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonValue value_attr = json_obj["@attr"];
|
QJsonValue value_attr = json_obj["@attr"];
|
||||||
if (!value_attr.isObject()) {
|
if (!value_attr.isObject()) {
|
||||||
Error("Json scrobbles attr is not an object.", value_attr);
|
Error("Json scrobbles attr is not an object.", value_attr);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QJsonObject obj_attr = value_attr.toObject();
|
QJsonObject obj_attr = value_attr.toObject();
|
||||||
if (obj_attr.isEmpty()) {
|
if (obj_attr.isEmpty()) {
|
||||||
Error("Json scrobbles attr is empty.", value_attr);
|
Error("Json scrobbles attr is empty.", value_attr);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!obj_attr.contains("accepted") || !obj_attr.contains("ignored")) {
|
if (!obj_attr.contains("accepted") || !obj_attr.contains("ignored")) {
|
||||||
Error("Json scrobbles attr is missing values.", obj_attr);
|
Error("Json scrobbles attr is missing values.", obj_attr);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int accepted = obj_attr["accepted"].toInt();
|
int accepted = obj_attr["accepted"].toInt();
|
||||||
@@ -693,7 +696,7 @@ void ScrobblingAPI20::ScrobbleRequestFinished(QNetworkReply *reply, const QList<
|
|||||||
QJsonObject obj_scrobble = value_scrobble.toObject();
|
QJsonObject obj_scrobble = value_scrobble.toObject();
|
||||||
if (obj_scrobble.isEmpty()) {
|
if (obj_scrobble.isEmpty()) {
|
||||||
Error("Json scrobbles scrobble object is empty.", obj_scrobble);
|
Error("Json scrobbles scrobble object is empty.", obj_scrobble);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
array_scrobble.append(obj_scrobble);
|
array_scrobble.append(obj_scrobble);
|
||||||
@@ -702,13 +705,13 @@ void ScrobblingAPI20::ScrobbleRequestFinished(QNetworkReply *reply, const QList<
|
|||||||
array_scrobble = value_scrobble.toArray();
|
array_scrobble = value_scrobble.toArray();
|
||||||
if (array_scrobble.isEmpty()) {
|
if (array_scrobble.isEmpty()) {
|
||||||
Error("Json scrobbles scrobble array is empty.", value_scrobble);
|
Error("Json scrobbles scrobble array is empty.", value_scrobble);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Error("Json scrobbles scrobble is not an object or array.", value_scrobble);
|
Error("Json scrobbles scrobble is not an object or array.", value_scrobble);
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,7 +777,7 @@ void ScrobblingAPI20::ScrobbleRequestFinished(QNetworkReply *reply, const QList<
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DoSubmit();
|
StartSubmit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class ScrobblingAPI20 : public ScrobblerService {
|
|||||||
void SendSingleScrobble(ScrobblerCacheItemPtr item);
|
void SendSingleScrobble(ScrobblerCacheItemPtr item);
|
||||||
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||||
static QString ErrorString(const ScrobbleErrorCode error);
|
static QString ErrorString(const ScrobbleErrorCode error);
|
||||||
void DoSubmit() override;
|
void StartSubmit(const bool initial = false) override;
|
||||||
void CheckScrobblePrevSong();
|
void CheckScrobblePrevSong();
|
||||||
|
|
||||||
QString name_;
|
QString name_;
|
||||||
@@ -154,6 +154,7 @@ class ScrobblingAPI20 : public ScrobblerService {
|
|||||||
Song song_playing_;
|
Song song_playing_;
|
||||||
bool scrobbled_;
|
bool scrobbled_;
|
||||||
quint64 timestamp_;
|
quint64 timestamp_;
|
||||||
|
bool submit_error_;
|
||||||
|
|
||||||
QTimer timer_submit_;
|
QTimer timer_submit_;
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class SubsonicScrobbler : public ScrobblerService {
|
|||||||
void Scrobble(const Song &song) override;
|
void Scrobble(const Song &song) override;
|
||||||
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||||
|
|
||||||
void DoSubmit() override {}
|
void StartSubmit(const bool initial = false) override { Q_UNUSED(initial) }
|
||||||
void Submitted() override { submitted_ = true; }
|
void Submitted() override { submitted_ = true; }
|
||||||
bool IsSubmitted() const override { return submitted_; }
|
bool IsSubmitted() const override { return submitted_; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user