Tidal: Remove deprecated username/password login
This commit is contained in:
@@ -99,8 +99,7 @@ bool TidalCoverProvider::StartSearch(const QString &artist, const QString &album
|
|||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||||
if (service_->oauth() && !service_->access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + service_->access_token().toUtf8());
|
if (!service_->access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + service_->access_token().toUtf8());
|
||||||
else if (!service_->session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", service_->session_id().toUtf8());
|
|
||||||
|
|
||||||
QNetworkReply *reply = network_->get(req);
|
QNetworkReply *reply = network_->get(req);
|
||||||
replies_ << reply;
|
replies_ << reply;
|
||||||
|
|||||||
@@ -53,10 +53,8 @@ TidalSettingsPage::TidalSettingsPage(SettingsDialog *dialog, SharedPtr<TidalServ
|
|||||||
|
|
||||||
QObject::connect(ui_->button_login, &QPushButton::clicked, this, &TidalSettingsPage::LoginClicked);
|
QObject::connect(ui_->button_login, &QPushButton::clicked, this, &TidalSettingsPage::LoginClicked);
|
||||||
QObject::connect(ui_->login_state, &LoginStateWidget::LogoutClicked, this, &TidalSettingsPage::LogoutClicked);
|
QObject::connect(ui_->login_state, &LoginStateWidget::LogoutClicked, this, &TidalSettingsPage::LogoutClicked);
|
||||||
QObject::connect(ui_->oauth, &QCheckBox::toggled, this, &TidalSettingsPage::OAuthClicked);
|
|
||||||
|
|
||||||
QObject::connect(this, &TidalSettingsPage::Authorize, &*service_, &TidalService::StartAuthorization);
|
QObject::connect(this, &TidalSettingsPage::Authorize, &*service_, &TidalService::StartAuthorization);
|
||||||
QObject::connect(this, &TidalSettingsPage::Login, &*service_, &TidalService::SendLoginWithCredentials);
|
|
||||||
|
|
||||||
QObject::connect(&*service_, &StreamingService::LoginFailure, this, &TidalSettingsPage::LoginFailure);
|
QObject::connect(&*service_, &StreamingService::LoginFailure, this, &TidalSettingsPage::LoginFailure);
|
||||||
QObject::connect(&*service_, &StreamingService::LoginSuccess, this, &TidalSettingsPage::LoginSuccess);
|
QObject::connect(&*service_, &StreamingService::LoginSuccess, this, &TidalSettingsPage::LoginSuccess);
|
||||||
@@ -88,16 +86,7 @@ void TidalSettingsPage::Load() {
|
|||||||
Settings s;
|
Settings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
ui_->enable->setChecked(s.value(kEnabled, false).toBool());
|
ui_->enable->setChecked(s.value(kEnabled, false).toBool());
|
||||||
ui_->oauth->setChecked(s.value(kOAuth, true).toBool());
|
|
||||||
|
|
||||||
ui_->client_id->setText(s.value(kClientId).toString());
|
ui_->client_id->setText(s.value(kClientId).toString());
|
||||||
ui_->api_token->setText(s.value(kApiToken).toString());
|
|
||||||
|
|
||||||
ui_->username->setText(s.value(kUsername).toString());
|
|
||||||
QByteArray password = s.value(kPassword).toByteArray();
|
|
||||||
if (password.isEmpty()) ui_->password->clear();
|
|
||||||
else ui_->password->setText(QString::fromUtf8(QByteArray::fromBase64(password)));
|
|
||||||
|
|
||||||
ComboBoxLoadFromSettings(s, ui_->quality, QLatin1String(kQuality), u"LOSSLESS"_s);
|
ComboBoxLoadFromSettings(s, ui_->quality, QLatin1String(kQuality), u"LOSSLESS"_s);
|
||||||
ui_->searchdelay->setValue(s.value(kSearchDelay, 1500).toInt());
|
ui_->searchdelay->setValue(s.value(kSearchDelay, 1500).toInt());
|
||||||
ui_->artistssearchlimit->setValue(s.value("kArtistsSearchLimit", 4).toInt());
|
ui_->artistssearchlimit->setValue(s.value("kArtistsSearchLimit", 4).toInt());
|
||||||
@@ -108,11 +97,11 @@ void TidalSettingsPage::Load() {
|
|||||||
ComboBoxLoadFromSettings(s, ui_->coversize, QLatin1String(kCoverSize), u"640x640"_s);
|
ComboBoxLoadFromSettings(s, ui_->coversize, QLatin1String(kCoverSize), u"640x640"_s);
|
||||||
ui_->streamurl->setCurrentIndex(ui_->streamurl->findData(s.value(kStreamUrl, static_cast<int>(StreamUrlMethod::StreamUrl)).toInt()));
|
ui_->streamurl->setCurrentIndex(ui_->streamurl->findData(s.value(kStreamUrl, static_cast<int>(StreamUrlMethod::StreamUrl)).toInt()));
|
||||||
ui_->checkbox_album_explicit->setChecked(s.value(kAlbumExplicit, false).toBool());
|
ui_->checkbox_album_explicit->setChecked(s.value(kAlbumExplicit, false).toBool());
|
||||||
|
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
OAuthClicked(ui_->oauth->isChecked());
|
if (service_->authenticated()) {
|
||||||
if (service_->authenticated()) ui_->login_state->SetLoggedIn(LoginStateWidget::State::LoggedIn);
|
ui_->login_state->SetLoggedIn(LoginStateWidget::State::LoggedIn);
|
||||||
|
}
|
||||||
|
|
||||||
Init(ui_->layout_tidalsettingspage->parentWidget());
|
Init(ui_->layout_tidalsettingspage->parentWidget());
|
||||||
|
|
||||||
@@ -125,13 +114,19 @@ void TidalSettingsPage::Save() {
|
|||||||
Settings s;
|
Settings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
s.setValue(kEnabled, ui_->enable->isChecked());
|
s.setValue(kEnabled, ui_->enable->isChecked());
|
||||||
s.setValue(kOAuth, ui_->oauth->isChecked());
|
if (s.contains(kOAuth)) {
|
||||||
|
s.remove(kOAuth);
|
||||||
|
}
|
||||||
|
if (s.contains(kApiToken)) {
|
||||||
|
s.remove(kApiToken);
|
||||||
|
}
|
||||||
|
if (s.contains(kUsername)) {
|
||||||
|
s.remove(kUsername);
|
||||||
|
}
|
||||||
|
if (s.contains(kPassword)) {
|
||||||
|
s.remove(kPassword);
|
||||||
|
}
|
||||||
s.setValue(kClientId, ui_->client_id->text());
|
s.setValue(kClientId, ui_->client_id->text());
|
||||||
s.setValue(kApiToken, ui_->api_token->text());
|
|
||||||
|
|
||||||
s.setValue(kUsername, ui_->username->text());
|
|
||||||
s.setValue(kPassword, QString::fromUtf8(ui_->password->text().toUtf8().toBase64()));
|
|
||||||
|
|
||||||
s.setValue(kQuality, ui_->quality->currentData().toString());
|
s.setValue(kQuality, ui_->quality->currentData().toString());
|
||||||
s.setValue(kSearchDelay, ui_->searchdelay->value());
|
s.setValue(kSearchDelay, ui_->searchdelay->value());
|
||||||
s.setValue(kArtistsSearchLimit, ui_->artistssearchlimit->value());
|
s.setValue(kArtistsSearchLimit, ui_->artistssearchlimit->value());
|
||||||
@@ -148,28 +143,11 @@ void TidalSettingsPage::Save() {
|
|||||||
|
|
||||||
void TidalSettingsPage::LoginClicked() {
|
void TidalSettingsPage::LoginClicked() {
|
||||||
|
|
||||||
if (ui_->oauth->isChecked()) {
|
if (ui_->client_id->text().isEmpty()) {
|
||||||
if (ui_->client_id->text().isEmpty()) {
|
QMessageBox::critical(this, tr("Configuration incomplete"), tr("Missing Tidal client ID."));
|
||||||
QMessageBox::critical(this, tr("Configuration incomplete"), tr("Missing Tidal client ID."));
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
Q_EMIT Authorize(ui_->client_id->text());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (ui_->api_token->text().isEmpty()) {
|
|
||||||
QMessageBox::critical(this, tr("Configuration incomplete"), tr("Missing API token."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ui_->username->text().isEmpty()) {
|
|
||||||
QMessageBox::critical(this, tr("Configuration incomplete"), tr("Missing username."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ui_->password->text().isEmpty()) {
|
|
||||||
QMessageBox::critical(this, tr("Configuration incomplete"), tr("Missing password."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Q_EMIT Login(ui_->api_token->text(), ui_->username->text(), ui_->password->text());
|
|
||||||
}
|
}
|
||||||
|
Q_EMIT Authorize(ui_->client_id->text());
|
||||||
ui_->button_login->setEnabled(false);
|
ui_->button_login->setEnabled(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -184,15 +162,6 @@ bool TidalSettingsPage::eventFilter(QObject *object, QEvent *event) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalSettingsPage::OAuthClicked(const bool enabled) {
|
|
||||||
|
|
||||||
ui_->client_id->setEnabled(enabled);
|
|
||||||
ui_->api_token->setEnabled(!enabled);
|
|
||||||
ui_->username->setEnabled(!enabled);
|
|
||||||
ui_->password->setEnabled(!enabled);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalSettingsPage::LogoutClicked() {
|
void TidalSettingsPage::LogoutClicked() {
|
||||||
|
|
||||||
service_->Logout();
|
service_->Logout();
|
||||||
|
|||||||
@@ -47,10 +47,8 @@ class TidalSettingsPage : public SettingsPage {
|
|||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void Authorize(const QString &client_id);
|
void Authorize(const QString &client_id);
|
||||||
void Login(const QString &api_token, const QString &username, const QString &password);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void OAuthClicked(const bool enabled);
|
|
||||||
void LoginClicked();
|
void LoginClicked();
|
||||||
void LogoutClicked();
|
void LogoutClicked();
|
||||||
void LoginSuccess();
|
void LoginSuccess();
|
||||||
|
|||||||
@@ -47,13 +47,6 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="layout_credential_group">
|
<layout class="QFormLayout" name="layout_credential_group">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="oauth">
|
|
||||||
<property name="text">
|
|
||||||
<string>Use OAuth</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_client_id">
|
<widget class="QLabel" name="label_client_id">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
@@ -66,74 +59,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="client_id">
|
<widget class="QLineEdit" name="client_id">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string notr="true"/>
|
<string notr="true"/>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_api_token">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>150</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>API Token</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="api_token">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>200</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_username">
|
|
||||||
<property name="text">
|
|
||||||
<string>Username</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="username">
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label_password">
|
|
||||||
<property name="text">
|
|
||||||
<string>Password</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QLineEdit" name="password">
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true"/>
|
|
||||||
</property>
|
|
||||||
<property name="echoMode">
|
|
||||||
<enum>QLineEdit::EchoMode::Password</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -308,7 +240,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<spacer name="spacer_middle">
|
<spacer name="spacer_middle">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
@@ -323,7 +255,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<spacer name="spacer_bottom">
|
<spacer name="spacer_bottom">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
@@ -366,11 +298,7 @@
|
|||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>enable</tabstop>
|
<tabstop>enable</tabstop>
|
||||||
<tabstop>oauth</tabstop>
|
|
||||||
<tabstop>client_id</tabstop>
|
<tabstop>client_id</tabstop>
|
||||||
<tabstop>api_token</tabstop>
|
|
||||||
<tabstop>username</tabstop>
|
|
||||||
<tabstop>password</tabstop>
|
|
||||||
<tabstop>button_login</tabstop>
|
<tabstop>button_login</tabstop>
|
||||||
<tabstop>quality</tabstop>
|
<tabstop>quality</tabstop>
|
||||||
<tabstop>searchdelay</tabstop>
|
<tabstop>searchdelay</tabstop>
|
||||||
@@ -384,6 +312,8 @@
|
|||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../data/icons.qrc"/>
|
<include location="../../data/icons.qrc"/>
|
||||||
|
<include location="../../data/icons.qrc"/>
|
||||||
|
<include location="../../data/icons.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ QNetworkReply *TidalBaseRequest::CreateRequest(const QString &ressource_name, co
|
|||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||||
if (oauth() && !access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
if (!access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||||
else if (!session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", session_id().toUtf8());
|
|
||||||
|
|
||||||
QNetworkReply *reply = network_->get(req);
|
QNetworkReply *reply = network_->get(req);
|
||||||
QObject::connect(reply, &QNetworkReply::sslErrors, this, &TidalBaseRequest::HandleSSLErrors);
|
QObject::connect(reply, &QNetworkReply::sslErrors, this, &TidalBaseRequest::HandleSSLErrors);
|
||||||
@@ -82,7 +81,7 @@ void TidalBaseRequest::HandleSSLErrors(const QList<QSslError> &ssl_errors) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply, const bool send_login) {
|
QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply) {
|
||||||
|
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
|
|
||||||
@@ -121,24 +120,8 @@ QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply, const bool send_
|
|||||||
}
|
}
|
||||||
if (status == 401 && sub_status == 6001) { // User does not have a valid session
|
if (status == 401 && sub_status == 6001) { // User does not have a valid session
|
||||||
service_->Logout();
|
service_->Logout();
|
||||||
if (!oauth() && send_login && login_attempts() < max_login_attempts() && !api_token().isEmpty() && !username().isEmpty() && !password().isEmpty()) {
|
|
||||||
qLog(Error) << "Tidal:" << error;
|
|
||||||
set_need_login();
|
|
||||||
if (login_sent()) {
|
|
||||||
qLog(Info) << "Tidal:" << "Waiting for login.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qLog(Info) << "Tidal:" << "Attempting to login.";
|
|
||||||
Q_EMIT RequestLogin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Error(error);
|
|
||||||
}
|
}
|
||||||
|
Error(error);
|
||||||
}
|
}
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class TidalBaseRequest : public QObject {
|
|||||||
using ParamList = QList<Param>;
|
using ParamList = QList<Param>;
|
||||||
|
|
||||||
QNetworkReply *CreateRequest(const QString &ressource_name, const ParamList ¶ms_provided);
|
QNetworkReply *CreateRequest(const QString &ressource_name, const ParamList ¶ms_provided);
|
||||||
QByteArray GetReplyData(QNetworkReply *reply, const bool send_login);
|
QByteArray GetReplyData(QNetworkReply *reply);
|
||||||
QJsonObject ExtractJsonObj(const QByteArray &data);
|
QJsonObject ExtractJsonObj(const QByteArray &data);
|
||||||
QJsonValue ExtractItems(const QByteArray &data);
|
QJsonValue ExtractItems(const QByteArray &data);
|
||||||
QJsonValue ExtractItems(const QJsonObject &json_obj);
|
QJsonValue ExtractItems(const QJsonObject &json_obj);
|
||||||
@@ -72,30 +72,15 @@ class TidalBaseRequest : public QObject {
|
|||||||
virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0;
|
virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0;
|
||||||
static QString ErrorsToHTML(const QStringList &errors);
|
static QString ErrorsToHTML(const QStringList &errors);
|
||||||
|
|
||||||
bool oauth() const { return service_->oauth(); }
|
|
||||||
QString client_id() const { return service_->client_id(); }
|
QString client_id() const { return service_->client_id(); }
|
||||||
QString api_token() const { return service_->api_token(); }
|
|
||||||
quint64 user_id() const { return service_->user_id(); }
|
quint64 user_id() const { return service_->user_id(); }
|
||||||
QString country_code() const { return service_->country_code(); }
|
QString country_code() const { return service_->country_code(); }
|
||||||
QString username() const { return service_->username(); }
|
|
||||||
QString password() const { return service_->password(); }
|
|
||||||
QString quality() const { return service_->quality(); }
|
QString quality() const { return service_->quality(); }
|
||||||
int artistssearchlimit() const { return service_->artistssearchlimit(); }
|
int artistssearchlimit() const { return service_->artistssearchlimit(); }
|
||||||
int albumssearchlimit() const { return service_->albumssearchlimit(); }
|
int albumssearchlimit() const { return service_->albumssearchlimit(); }
|
||||||
int songssearchlimit() const { return service_->songssearchlimit(); }
|
int songssearchlimit() const { return service_->songssearchlimit(); }
|
||||||
|
|
||||||
QString access_token() const { return service_->access_token(); }
|
QString access_token() const { return service_->access_token(); }
|
||||||
QString session_id() const { return service_->session_id(); }
|
|
||||||
|
|
||||||
bool authenticated() const { return service_->authenticated(); }
|
bool authenticated() const { return service_->authenticated(); }
|
||||||
bool login_sent() const { return service_->login_sent(); }
|
|
||||||
int max_login_attempts() const { return service_->max_login_attempts(); }
|
|
||||||
int login_attempts() const { return service_->login_attempts(); }
|
|
||||||
|
|
||||||
virtual void set_need_login() = 0;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void RequestLogin();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void HandleSSLErrors(const QList<QSslError> &ssl_errors);
|
void HandleSSLErrors(const QList<QSslError> &ssl_errors);
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ using namespace Qt::Literals::StringLiterals;
|
|||||||
TidalFavoriteRequest::TidalFavoriteRequest(TidalService *service, const SharedPtr<NetworkAccessManager> network, QObject *parent)
|
TidalFavoriteRequest::TidalFavoriteRequest(TidalService *service, const SharedPtr<NetworkAccessManager> network, QObject *parent)
|
||||||
: TidalBaseRequest(service, network, parent),
|
: TidalBaseRequest(service, network, parent),
|
||||||
service_(service),
|
service_(service),
|
||||||
network_(network),
|
network_(network) {}
|
||||||
need_login_(false) {}
|
|
||||||
|
|
||||||
TidalFavoriteRequest::~TidalFavoriteRequest() {
|
TidalFavoriteRequest::~TidalFavoriteRequest() {
|
||||||
|
|
||||||
@@ -148,8 +147,7 @@ void TidalFavoriteRequest::AddFavoritesRequest(const FavoriteType type, const QS
|
|||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||||
if (oauth() && !access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
if (!access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||||
else if (!session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", session_id().toUtf8());
|
|
||||||
QByteArray query = url_query.toString(QUrl::FullyEncoded).toUtf8();
|
QByteArray query = url_query.toString(QUrl::FullyEncoded).toUtf8();
|
||||||
QNetworkReply *reply = network_->post(req, query);
|
QNetworkReply *reply = network_->post(req, query);
|
||||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, type, songs]() { AddFavoritesReply(reply, type, songs); });
|
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, type, songs]() { AddFavoritesReply(reply, type, songs); });
|
||||||
@@ -170,7 +168,7 @@ void TidalFavoriteRequest::AddFavoritesReply(QNetworkReply *reply, const Favorit
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetReplyData(reply, false);
|
GetReplyData(reply);
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
return;
|
return;
|
||||||
@@ -258,8 +256,7 @@ void TidalFavoriteRequest::RemoveFavoritesRequest(const FavoriteType type, const
|
|||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||||
if (oauth() && !access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
if (!access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||||
else if (!session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", session_id().toUtf8());
|
|
||||||
QNetworkReply *reply = network_->deleteResource(req);
|
QNetworkReply *reply = network_->deleteResource(req);
|
||||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, type, songs]() { RemoveFavoritesReply(reply, type, songs); });
|
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, type, songs]() { RemoveFavoritesReply(reply, type, songs); });
|
||||||
replies_ << reply;
|
replies_ << reply;
|
||||||
@@ -279,7 +276,7 @@ void TidalFavoriteRequest::RemoveFavoritesReply(QNetworkReply *reply, const Favo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetReplyData(reply, false);
|
GetReplyData(reply);
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,6 @@ class TidalFavoriteRequest : public TidalBaseRequest {
|
|||||||
explicit TidalFavoriteRequest(TidalService *service, const SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
|
explicit TidalFavoriteRequest(TidalService *service, const SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
|
||||||
~TidalFavoriteRequest() override;
|
~TidalFavoriteRequest() override;
|
||||||
|
|
||||||
bool need_login() const { return need_login_; }
|
|
||||||
void set_need_login() override { need_login_ = true; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class FavoriteType {
|
enum class FavoriteType {
|
||||||
Artists,
|
Artists,
|
||||||
@@ -89,7 +86,6 @@ class TidalFavoriteRequest : public TidalBaseRequest {
|
|||||||
TidalService *service_;
|
TidalService *service_;
|
||||||
const SharedPtr<NetworkAccessManager> network_;
|
const SharedPtr<NetworkAccessManager> network_;
|
||||||
QList <QNetworkReply*> replies_;
|
QList <QNetworkReply*> replies_;
|
||||||
bool need_login_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TIDALFAVORITEREQUEST_H
|
#endif // TIDALFAVORITEREQUEST_H
|
||||||
|
|||||||
@@ -99,8 +99,7 @@ TidalRequest::TidalRequest(TidalService *service, TidalUrlHandler *url_handler,
|
|||||||
album_songs_received_(0),
|
album_songs_received_(0),
|
||||||
album_covers_requests_total_(0),
|
album_covers_requests_total_(0),
|
||||||
album_covers_requests_active_(0),
|
album_covers_requests_active_(0),
|
||||||
album_covers_requests_received_(0),
|
album_covers_requests_received_(0) {
|
||||||
need_login_(false) {
|
|
||||||
|
|
||||||
timer_flush_requests_->setInterval(kFlushRequestsDelay);
|
timer_flush_requests_->setInterval(kFlushRequestsDelay);
|
||||||
timer_flush_requests_->setSingleShot(false);
|
timer_flush_requests_->setSingleShot(false);
|
||||||
@@ -126,29 +125,8 @@ TidalRequest::~TidalRequest() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalRequest::LoginComplete(const bool success, const QString &error) {
|
|
||||||
|
|
||||||
if (!need_login_) return;
|
|
||||||
need_login_ = false;
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
Error(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalRequest::Process() {
|
void TidalRequest::Process() {
|
||||||
|
|
||||||
if (!service_->authenticated()) {
|
|
||||||
Q_EMIT UpdateStatus(query_id_, tr("Authenticating..."));
|
|
||||||
need_login_ = true;
|
|
||||||
service_->TryLogin();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (query_type_) {
|
switch (query_type_) {
|
||||||
case Type::FavouriteArtists:
|
case Type::FavouriteArtists:
|
||||||
GetArtists();
|
GetArtists();
|
||||||
@@ -417,7 +395,7 @@ void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
|
|||||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply, (offset_requested == 0));
|
QByteArray data = GetReplyData(reply);
|
||||||
|
|
||||||
--artists_requests_active_;
|
--artists_requests_active_;
|
||||||
++artists_requests_received_;
|
++artists_requests_received_;
|
||||||
@@ -564,7 +542,7 @@ void TidalRequest::AlbumsReplyReceived(QNetworkReply *reply, const int limit_req
|
|||||||
|
|
||||||
--albums_requests_active_;
|
--albums_requests_active_;
|
||||||
++albums_requests_received_;
|
++albums_requests_received_;
|
||||||
AlbumsReceived(reply, Artist(), limit_requested, offset_requested, offset_requested == 0);
|
AlbumsReceived(reply, Artist(), limit_requested, offset_requested);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,18 +582,18 @@ void TidalRequest::ArtistAlbumsReplyReceived(QNetworkReply *reply, const Artist
|
|||||||
--artist_albums_requests_active_;
|
--artist_albums_requests_active_;
|
||||||
++artist_albums_requests_received_;
|
++artist_albums_requests_received_;
|
||||||
Q_EMIT UpdateProgress(query_id_, GetProgress(artist_albums_requests_received_, artist_albums_requests_total_));
|
Q_EMIT UpdateProgress(query_id_, GetProgress(artist_albums_requests_received_, artist_albums_requests_total_));
|
||||||
AlbumsReceived(reply, artist, 0, offset_requested, false);
|
AlbumsReceived(reply, artist, 0, offset_requested);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalRequest::AlbumsReceived(QNetworkReply *reply, const Artist &artist_requested, const int limit_requested, const int offset_requested, const bool auto_login) {
|
void TidalRequest::AlbumsReceived(QNetworkReply *reply, const Artist &artist_requested, const int limit_requested, const int offset_requested) {
|
||||||
|
|
||||||
if (!replies_.contains(reply)) return;
|
if (!replies_.contains(reply)) return;
|
||||||
replies_.removeAll(reply);
|
replies_.removeAll(reply);
|
||||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply, auto_login);
|
QByteArray data = GetReplyData(reply);
|
||||||
|
|
||||||
if (finished_) return;
|
if (finished_) return;
|
||||||
|
|
||||||
@@ -835,10 +813,10 @@ void TidalRequest::SongsReplyReceived(QNetworkReply *reply, const int limit_requ
|
|||||||
--songs_requests_active_;
|
--songs_requests_active_;
|
||||||
++songs_requests_received_;
|
++songs_requests_received_;
|
||||||
if (query_type_ == Type::SearchSongs && fetchalbums_) {
|
if (query_type_ == Type::SearchSongs && fetchalbums_) {
|
||||||
AlbumsReceived(reply, Artist(), limit_requested, offset_requested, offset_requested == 0);
|
AlbumsReceived(reply, Artist(), limit_requested, offset_requested);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SongsReceived(reply, Artist(), Album(), limit_requested, offset_requested, offset_requested == 0);
|
SongsReceived(reply, Artist(), Album(), limit_requested, offset_requested);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -881,18 +859,18 @@ void TidalRequest::AlbumSongsReplyReceived(QNetworkReply *reply, const Artist &a
|
|||||||
if (offset_requested == 0) {
|
if (offset_requested == 0) {
|
||||||
Q_EMIT UpdateProgress(query_id_, GetProgress(album_songs_requests_received_, album_songs_requests_total_));
|
Q_EMIT UpdateProgress(query_id_, GetProgress(album_songs_requests_received_, album_songs_requests_total_));
|
||||||
}
|
}
|
||||||
SongsReceived(reply, artist, album, 0, offset_requested, false);
|
SongsReceived(reply, artist, album, 0, offset_requested);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalRequest::SongsReceived(QNetworkReply *reply, const Artist &artist, const Album &album, const int limit_requested, const int offset_requested, const bool auto_login) {
|
void TidalRequest::SongsReceived(QNetworkReply *reply, const Artist &artist, const Album &album, const int limit_requested, const int offset_requested) {
|
||||||
|
|
||||||
if (!replies_.contains(reply)) return;
|
if (!replies_.contains(reply)) return;
|
||||||
replies_.removeAll(reply);
|
replies_.removeAll(reply);
|
||||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply, auto_login);
|
QByteArray data = GetReplyData(reply);
|
||||||
|
|
||||||
if (finished_) return;
|
if (finished_) return;
|
||||||
|
|
||||||
@@ -1339,7 +1317,6 @@ void TidalRequest::FinishCheck() {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!finished_ &&
|
!finished_ &&
|
||||||
!need_login_ &&
|
|
||||||
artists_requests_queue_.isEmpty() &&
|
artists_requests_queue_.isEmpty() &&
|
||||||
albums_requests_queue_.isEmpty() &&
|
albums_requests_queue_.isEmpty() &&
|
||||||
songs_requests_queue_.isEmpty() &&
|
songs_requests_queue_.isEmpty() &&
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ class TidalRequest : public TidalBaseRequest {
|
|||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
|
||||||
void Process();
|
void Process();
|
||||||
void set_need_login() override { need_login_ = true; }
|
|
||||||
void Search(const int query_id, const QString &search_text);
|
void Search(const int query_id, const QString &search_text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -110,18 +109,15 @@ class TidalRequest : public TidalBaseRequest {
|
|||||||
void ArtistsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
|
void ArtistsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
|
||||||
|
|
||||||
void AlbumsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
|
void AlbumsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
|
||||||
void AlbumsReceived(QNetworkReply *reply, const TidalRequest::Artist &artist_requested, const int limit_requested, const int offset_requested, const bool auto_login);
|
void AlbumsReceived(QNetworkReply *reply, const TidalRequest::Artist &artist_requested, const int limit_requested, const int offset_requested);
|
||||||
|
|
||||||
void SongsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
|
void SongsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
|
||||||
void SongsReceived(QNetworkReply *reply, const TidalRequest::Artist &artist, const TidalRequest::Album &album, const int limit_requested, const int offset_requested, const bool auto_login = false);
|
void SongsReceived(QNetworkReply *reply, const TidalRequest::Artist &artist, const TidalRequest::Album &album, const int limit_requested, const int offset_requested);
|
||||||
|
|
||||||
void ArtistAlbumsReplyReceived(QNetworkReply *reply, const TidalRequest::Artist &artist, const int offset_requested);
|
void ArtistAlbumsReplyReceived(QNetworkReply *reply, const TidalRequest::Artist &artist, const int offset_requested);
|
||||||
void AlbumSongsReplyReceived(QNetworkReply *reply, const TidalRequest::Artist &artist, const TidalRequest::Album &album, const int offset_requested);
|
void AlbumSongsReplyReceived(QNetworkReply *reply, const TidalRequest::Artist &artist, const TidalRequest::Album &album, const int offset_requested);
|
||||||
void AlbumCoverReceived(QNetworkReply *reply, const QString &album_id, const QUrl &url, const QString &filename);
|
void AlbumCoverReceived(QNetworkReply *reply, const QString &album_id, const QUrl &url, const QString &filename);
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void LoginComplete(const bool success, const QString &error = QString());
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsQuery() const { return (query_type_ == Type::FavouriteArtists || query_type_ == Type::FavouriteAlbums || query_type_ == Type::FavouriteSongs); }
|
bool IsQuery() const { return (query_type_ == Type::FavouriteArtists || query_type_ == Type::FavouriteAlbums || query_type_ == Type::FavouriteSongs); }
|
||||||
bool IsSearch() const { return (query_type_ == Type::SearchArtists || query_type_ == Type::SearchAlbums || query_type_ == Type::SearchSongs); }
|
bool IsSearch() const { return (query_type_ == Type::SearchArtists || query_type_ == Type::SearchAlbums || query_type_ == Type::SearchSongs); }
|
||||||
@@ -233,7 +229,6 @@ class TidalRequest : public TidalBaseRequest {
|
|||||||
|
|
||||||
SongMap songs_;
|
SongMap songs_;
|
||||||
QStringList errors_;
|
QStringList errors_;
|
||||||
bool need_login_;
|
|
||||||
QList<QNetworkReply*> replies_;
|
QList<QNetworkReply*> replies_;
|
||||||
QList<QNetworkReply*> album_cover_replies_;
|
QList<QNetworkReply*> album_cover_replies_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,16 +75,12 @@ const Song::Source TidalService::kSource = Song::Source::Tidal;
|
|||||||
|
|
||||||
const char TidalService::kApiUrl[] = "https://api.tidalhifi.com/v1";
|
const char TidalService::kApiUrl[] = "https://api.tidalhifi.com/v1";
|
||||||
const char TidalService::kResourcesUrl[] = "https://resources.tidal.com";
|
const char TidalService::kResourcesUrl[] = "https://resources.tidal.com";
|
||||||
const int TidalService::kLoginAttempts = 2;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char kOAuthUrl[] = "https://login.tidal.com/authorize";
|
constexpr char kOAuthUrl[] = "https://login.tidal.com/authorize";
|
||||||
constexpr char kOAuthAccessTokenUrl[] = "https://login.tidal.com/oauth2/token";
|
constexpr char kOAuthAccessTokenUrl[] = "https://login.tidal.com/oauth2/token";
|
||||||
constexpr char kOAuthRedirectUrl[] = "tidal://login/auth";
|
constexpr char kOAuthRedirectUrl[] = "tidal://login/auth";
|
||||||
constexpr char kAuthUrl[] = "https://api.tidalhifi.com/v1/login/username";
|
|
||||||
|
|
||||||
constexpr int kTimeResetLoginAttempts = 60000;
|
|
||||||
|
|
||||||
constexpr char kArtistsSongsTable[] = "tidal_artists_songs";
|
constexpr char kArtistsSongsTable[] = "tidal_artists_songs";
|
||||||
constexpr char kAlbumsSongsTable[] = "tidal_albums_songs";
|
constexpr char kAlbumsSongsTable[] = "tidal_albums_songs";
|
||||||
@@ -116,11 +112,9 @@ TidalService::TidalService(const SharedPtr<TaskManager> task_manager,
|
|||||||
albums_collection_model_(nullptr),
|
albums_collection_model_(nullptr),
|
||||||
songs_collection_model_(nullptr),
|
songs_collection_model_(nullptr),
|
||||||
timer_search_delay_(new QTimer(this)),
|
timer_search_delay_(new QTimer(this)),
|
||||||
timer_login_attempt_(new QTimer(this)),
|
|
||||||
timer_refresh_login_(new QTimer(this)),
|
timer_refresh_login_(new QTimer(this)),
|
||||||
favorite_request_(new TidalFavoriteRequest(this, network_, this)),
|
favorite_request_(new TidalFavoriteRequest(this, network_, this)),
|
||||||
enabled_(false),
|
enabled_(false),
|
||||||
oauth_(false),
|
|
||||||
user_id_(0),
|
user_id_(0),
|
||||||
artistssearchlimit_(1),
|
artistssearchlimit_(1),
|
||||||
albumssearchlimit_(1),
|
albumssearchlimit_(1),
|
||||||
@@ -135,8 +129,6 @@ TidalService::TidalService(const SharedPtr<TaskManager> task_manager,
|
|||||||
next_pending_search_id_(1),
|
next_pending_search_id_(1),
|
||||||
pending_search_type_(StreamingSearchView::SearchType::Artists),
|
pending_search_type_(StreamingSearchView::SearchType::Artists),
|
||||||
search_id_(0),
|
search_id_(0),
|
||||||
login_sent_(false),
|
|
||||||
login_attempts_(0),
|
|
||||||
next_stream_url_request_id_(0) {
|
next_stream_url_request_id_(0) {
|
||||||
|
|
||||||
url_handlers->Register(url_handler_);
|
url_handlers->Register(url_handler_);
|
||||||
@@ -165,16 +157,9 @@ TidalService::TidalService(const SharedPtr<TaskManager> task_manager,
|
|||||||
timer_search_delay_->setSingleShot(true);
|
timer_search_delay_->setSingleShot(true);
|
||||||
QObject::connect(timer_search_delay_, &QTimer::timeout, this, &TidalService::StartSearch);
|
QObject::connect(timer_search_delay_, &QTimer::timeout, this, &TidalService::StartSearch);
|
||||||
|
|
||||||
timer_login_attempt_->setSingleShot(true);
|
|
||||||
timer_login_attempt_->setInterval(kTimeResetLoginAttempts);
|
|
||||||
QObject::connect(timer_login_attempt_, &QTimer::timeout, this, &TidalService::ResetLoginAttempts);
|
|
||||||
|
|
||||||
timer_refresh_login_->setSingleShot(true);
|
timer_refresh_login_->setSingleShot(true);
|
||||||
QObject::connect(timer_refresh_login_, &QTimer::timeout, this, &TidalService::RequestNewAccessToken);
|
QObject::connect(timer_refresh_login_, &QTimer::timeout, this, &TidalService::RequestNewAccessToken);
|
||||||
|
|
||||||
QObject::connect(this, &TidalService::RequestLogin, this, &TidalService::SendLogin);
|
|
||||||
QObject::connect(this, &TidalService::LoginWithCredentials, this, &TidalService::SendLoginWithCredentials);
|
|
||||||
|
|
||||||
QObject::connect(this, &TidalService::AddArtists, favorite_request_, &TidalFavoriteRequest::AddArtists);
|
QObject::connect(this, &TidalService::AddArtists, favorite_request_, &TidalFavoriteRequest::AddArtists);
|
||||||
QObject::connect(this, &TidalService::AddAlbums, favorite_request_, &TidalFavoriteRequest::AddAlbums);
|
QObject::connect(this, &TidalService::AddAlbums, favorite_request_, &TidalFavoriteRequest::AddAlbums);
|
||||||
QObject::connect(this, &TidalService::AddSongs, favorite_request_, QOverload<const SongList&>::of(&TidalFavoriteRequest::AddSongs));
|
QObject::connect(this, &TidalService::AddSongs, favorite_request_, QOverload<const SongList&>::of(&TidalFavoriteRequest::AddSongs));
|
||||||
@@ -184,8 +169,6 @@ TidalService::TidalService(const SharedPtr<TaskManager> task_manager,
|
|||||||
QObject::connect(this, &TidalService::RemoveSongsByList, favorite_request_, QOverload<const SongList&>::of(&TidalFavoriteRequest::RemoveSongs));
|
QObject::connect(this, &TidalService::RemoveSongsByList, favorite_request_, QOverload<const SongList&>::of(&TidalFavoriteRequest::RemoveSongs));
|
||||||
QObject::connect(this, &TidalService::RemoveSongsByMap, favorite_request_, QOverload<const SongMap&>::of(&TidalFavoriteRequest::RemoveSongs));
|
QObject::connect(this, &TidalService::RemoveSongsByMap, favorite_request_, QOverload<const SongMap&>::of(&TidalFavoriteRequest::RemoveSongs));
|
||||||
|
|
||||||
QObject::connect(favorite_request_, &TidalFavoriteRequest::RequestLogin, this, &TidalService::SendLogin);
|
|
||||||
|
|
||||||
QObject::connect(favorite_request_, &TidalFavoriteRequest::ArtistsAdded, &*artists_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
QObject::connect(favorite_request_, &TidalFavoriteRequest::ArtistsAdded, &*artists_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
||||||
QObject::connect(favorite_request_, &TidalFavoriteRequest::AlbumsAdded, &*albums_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
QObject::connect(favorite_request_, &TidalFavoriteRequest::AlbumsAdded, &*albums_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
||||||
QObject::connect(favorite_request_, &TidalFavoriteRequest::SongsAdded, &*songs_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
QObject::connect(favorite_request_, &TidalFavoriteRequest::SongsAdded, &*songs_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
|
||||||
@@ -247,7 +230,6 @@ void TidalService::LoadSession() {
|
|||||||
country_code_ = s.value(kCountryCode, u"US"_s).toString();
|
country_code_ = s.value(kCountryCode, u"US"_s).toString();
|
||||||
access_token_ = s.value(kAccessToken).toString();
|
access_token_ = s.value(kAccessToken).toString();
|
||||||
refresh_token_ = s.value(kRefreshToken).toString();
|
refresh_token_ = s.value(kRefreshToken).toString();
|
||||||
session_id_ = s.value(kSessionId).toString();
|
|
||||||
expires_in_ = s.value(kExpiresIn).toLongLong();
|
expires_in_ = s.value(kExpiresIn).toLongLong();
|
||||||
login_time_ = s.value(kLoginTime).toLongLong();
|
login_time_ = s.value(kLoginTime).toLongLong();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
@@ -271,15 +253,7 @@ void TidalService::ReloadSettings() {
|
|||||||
s.beginGroup(TidalSettings::kSettingsGroup);
|
s.beginGroup(TidalSettings::kSettingsGroup);
|
||||||
|
|
||||||
enabled_ = s.value(TidalSettings::kEnabled, false).toBool();
|
enabled_ = s.value(TidalSettings::kEnabled, false).toBool();
|
||||||
oauth_ = s.value(TidalSettings::kOAuth, true).toBool();
|
|
||||||
client_id_ = s.value(TidalSettings::kClientId).toString();
|
client_id_ = s.value(TidalSettings::kClientId).toString();
|
||||||
api_token_ = s.value(TidalSettings::kApiToken).toString();
|
|
||||||
|
|
||||||
username_ = s.value(TidalSettings::kUsername).toString();
|
|
||||||
QByteArray password = s.value(TidalSettings::kPassword).toByteArray();
|
|
||||||
if (password.isEmpty()) password_.clear();
|
|
||||||
else password_ = QString::fromUtf8(QByteArray::fromBase64(password));
|
|
||||||
|
|
||||||
quality_ = s.value(TidalSettings::kQuality, u"LOSSLESS"_s).toString();
|
quality_ = s.value(TidalSettings::kQuality, u"LOSSLESS"_s).toString();
|
||||||
quint64 search_delay = s.value(TidalSettings::kSearchDelay, 1500).toInt();
|
quint64 search_delay = s.value(TidalSettings::kSearchDelay, 1500).toInt();
|
||||||
artistssearchlimit_ = s.value(TidalSettings::kArtistsSearchLimit, 4).toInt();
|
artistssearchlimit_ = s.value(TidalSettings::kArtistsSearchLimit, 4).toInt();
|
||||||
@@ -339,7 +313,6 @@ void TidalService::AuthorizationUrlReceived(const QUrl &url) {
|
|||||||
}
|
}
|
||||||
expires_in_ = url_query.queryItemValue(u"expires_in"_s).toInt();
|
expires_in_ = url_query.queryItemValue(u"expires_in"_s).toInt();
|
||||||
login_time_ = QDateTime::currentSecsSinceEpoch();
|
login_time_ = QDateTime::currentSecsSinceEpoch();
|
||||||
session_id_.clear();
|
|
||||||
|
|
||||||
Settings s;
|
Settings s;
|
||||||
s.beginGroup(TidalSettings::kSettingsGroup);
|
s.beginGroup(TidalSettings::kSettingsGroup);
|
||||||
@@ -382,7 +355,7 @@ void TidalService::RequestAccessToken(const QString &code) {
|
|||||||
params << Param(u"redirect_uri"_s, QLatin1String(kOAuthRedirectUrl));
|
params << Param(u"redirect_uri"_s, QLatin1String(kOAuthRedirectUrl));
|
||||||
params << Param(u"scope"_s, u"r_usr w_usr"_s);
|
params << Param(u"scope"_s, u"r_usr w_usr"_s);
|
||||||
}
|
}
|
||||||
else if (!refresh_token_.isEmpty() && enabled_ && oauth_) {
|
else if (!refresh_token_.isEmpty() && enabled_) {
|
||||||
params << Param(u"grant_type"_s, u"refresh_token"_s);
|
params << Param(u"grant_type"_s, u"refresh_token"_s);
|
||||||
params << Param(u"refresh_token"_s, refresh_token_);
|
params << Param(u"refresh_token"_s, refresh_token_);
|
||||||
}
|
}
|
||||||
@@ -501,8 +474,6 @@ void TidalService::AccessTokenRequestFinished(QNetworkReply *reply) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session_id_.clear();
|
|
||||||
|
|
||||||
Settings s;
|
Settings s;
|
||||||
s.beginGroup(TidalSettings::kSettingsGroup);
|
s.beginGroup(TidalSettings::kSettingsGroup);
|
||||||
s.setValue(kAccessToken, access_token_);
|
s.setValue(kAccessToken, access_token_);
|
||||||
@@ -512,6 +483,8 @@ void TidalService::AccessTokenRequestFinished(QNetworkReply *reply) {
|
|||||||
s.setValue(kCountryCode, country_code_);
|
s.setValue(kCountryCode, country_code_);
|
||||||
s.setValue(kUserId, user_id_);
|
s.setValue(kUserId, user_id_);
|
||||||
s.remove(kSessionId);
|
s.remove(kSessionId);
|
||||||
|
s.remove(kUserId);
|
||||||
|
s.remove(kCountryCode);
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
if (expires_in_ > 0) {
|
if (expires_in_ > 0) {
|
||||||
@@ -526,153 +499,12 @@ void TidalService::AccessTokenRequestFinished(QNetworkReply *reply) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalService::SendLogin() {
|
|
||||||
SendLoginWithCredentials(api_token_, username_, password_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalService::SendLoginWithCredentials(const QString &api_token, const QString &username, const QString &password) {
|
|
||||||
|
|
||||||
login_sent_ = true;
|
|
||||||
++login_attempts_;
|
|
||||||
timer_login_attempt_->start();
|
|
||||||
timer_refresh_login_->stop();
|
|
||||||
|
|
||||||
const ParamList params = ParamList() << Param(u"token"_s, (api_token.isEmpty() ? api_token_ : api_token))
|
|
||||||
<< Param(u"username"_s, username)
|
|
||||||
<< Param(u"password"_s, password)
|
|
||||||
<< Param(u"clientVersion"_s, u"2.2.1--7"_s);
|
|
||||||
|
|
||||||
QUrlQuery url_query;
|
|
||||||
for (const Param ¶m : params) {
|
|
||||||
url_query.addQueryItem(QString::fromLatin1(QUrl::toPercentEncoding(param.first)), QString::fromLatin1(QUrl::toPercentEncoding(param.second)));
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl url(QString::fromLatin1(kAuthUrl));
|
|
||||||
QNetworkRequest req(url);
|
|
||||||
|
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
|
||||||
req.setRawHeader("X-Tidal-Token", (api_token.isEmpty() ? api_token_.toUtf8() : api_token.toUtf8()));
|
|
||||||
|
|
||||||
QByteArray query = url_query.toString(QUrl::FullyEncoded).toUtf8();
|
|
||||||
QNetworkReply *reply = network_->post(req, query);
|
|
||||||
QObject::connect(reply, &QNetworkReply::sslErrors, this, &TidalService::HandleLoginSSLErrors);
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply]() { HandleAuthReply(reply); });
|
|
||||||
replies_ << reply;
|
|
||||||
|
|
||||||
//qLog(Debug) << "Tidal: Sending request" << url << query;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalService::HandleAuthReply(QNetworkReply *reply) {
|
|
||||||
|
|
||||||
if (!replies_.contains(reply)) return;
|
|
||||||
replies_.removeAll(reply);
|
|
||||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
login_sent_ = false;
|
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError || reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
|
||||||
if (reply->error() != QNetworkReply::NoError && reply->error() < 200) {
|
|
||||||
// This is a network error, there is nothing more to do.
|
|
||||||
LoginError(QStringLiteral("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
|
|
||||||
login_errors_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// See if there is Json data containing "status" and "userMessage" - then use that instead.
|
|
||||||
QByteArray data(reply->readAll());
|
|
||||||
QJsonParseError json_error;
|
|
||||||
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
|
||||||
if (json_error.error == QJsonParseError::NoError && !json_doc.isEmpty() && json_doc.isObject()) {
|
|
||||||
QJsonObject json_obj = json_doc.object();
|
|
||||||
if (!json_obj.isEmpty() && json_obj.contains("status"_L1) && json_obj.contains("userMessage"_L1)) {
|
|
||||||
int status = json_obj["status"_L1].toInt();
|
|
||||||
int sub_status = json_obj["subStatus"_L1].toInt();
|
|
||||||
QString user_message = json_obj["userMessage"_L1].toString();
|
|
||||||
login_errors_ << QStringLiteral("Authentication failure: %1 (%2) (%3)").arg(user_message).arg(status).arg(sub_status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (login_errors_.isEmpty()) {
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
login_errors_ << QStringLiteral("%1 (%2)").arg(reply->errorString()).arg(reply->error());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
login_errors_ << QStringLiteral("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoginError();
|
|
||||||
login_errors_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
login_errors_.clear();
|
|
||||||
|
|
||||||
const QByteArray data = reply->readAll();
|
|
||||||
QJsonParseError json_error;
|
|
||||||
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
|
||||||
|
|
||||||
if (json_error.error != QJsonParseError::NoError) {
|
|
||||||
LoginError(u"Authentication reply from server missing Json data."_s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (json_doc.isEmpty()) {
|
|
||||||
LoginError(u"Authentication reply from server has empty Json document."_s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!json_doc.isObject()) {
|
|
||||||
LoginError(u"Authentication reply from server has Json document that is not an object."_s, json_doc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject json_obj = json_doc.object();
|
|
||||||
if (json_obj.isEmpty()) {
|
|
||||||
LoginError(u"Authentication reply from server has empty Json object."_s, json_doc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!json_obj.contains("userId"_L1) || !json_obj.contains("sessionId"_L1) || !json_obj.contains("countryCode"_L1)) {
|
|
||||||
LoginError(u"Authentication reply from server is missing userId, sessionId or countryCode"_s, json_obj);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
country_code_ = json_obj["countryCode"_L1].toString();
|
|
||||||
session_id_ = json_obj["sessionId"_L1].toString();
|
|
||||||
user_id_ = json_obj["userId"_L1].toInt();
|
|
||||||
access_token_.clear();
|
|
||||||
refresh_token_.clear();
|
|
||||||
|
|
||||||
Settings s;
|
|
||||||
s.beginGroup(TidalSettings::kSettingsGroup);
|
|
||||||
s.remove(kAccessToken);
|
|
||||||
s.remove(kRefreshToken);
|
|
||||||
s.remove(kExpiresIn);
|
|
||||||
s.remove(kLoginTime);
|
|
||||||
s.setValue(kUserId, user_id_);
|
|
||||||
s.setValue(kSessionId, session_id_);
|
|
||||||
s.setValue(kCountryCode, country_code_);
|
|
||||||
s.endGroup();
|
|
||||||
|
|
||||||
qLog(Debug) << "Tidal: Login successful" << "user id" << user_id_ << "session id" << session_id_ << "country code" << country_code_;
|
|
||||||
|
|
||||||
login_attempts_ = 0;
|
|
||||||
timer_login_attempt_->stop();
|
|
||||||
|
|
||||||
Q_EMIT LoginComplete(true);
|
|
||||||
Q_EMIT LoginSuccess();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalService::Logout() {
|
void TidalService::Logout() {
|
||||||
|
|
||||||
user_id_ = 0;
|
user_id_ = 0;
|
||||||
country_code_.clear();
|
country_code_.clear();
|
||||||
access_token_.clear();
|
access_token_.clear();
|
||||||
refresh_token_.clear();
|
refresh_token_.clear();
|
||||||
session_id_.clear();
|
|
||||||
expires_in_ = 0;
|
expires_in_ = 0;
|
||||||
login_time_ = 0;
|
login_time_ = 0;
|
||||||
|
|
||||||
@@ -691,35 +523,6 @@ void TidalService::Logout() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalService::ResetLoginAttempts() {
|
|
||||||
login_attempts_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalService::TryLogin() {
|
|
||||||
|
|
||||||
if (authenticated() || login_sent_) return;
|
|
||||||
|
|
||||||
if (api_token_.isEmpty()) {
|
|
||||||
Q_EMIT LoginComplete(false, tr("Missing Tidal API token."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (username_.isEmpty()) {
|
|
||||||
Q_EMIT LoginComplete(false, tr("Missing Tidal username."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (password_.isEmpty()) {
|
|
||||||
Q_EMIT LoginComplete(false, tr("Missing Tidal password."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (login_attempts_ >= kLoginAttempts) {
|
|
||||||
Q_EMIT LoginComplete(false, tr("Not authenticated with Tidal and reached maximum number of login attempts."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT RequestLogin();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalService::ResetArtistsRequest() {
|
void TidalService::ResetArtistsRequest() {
|
||||||
|
|
||||||
if (artists_request_) {
|
if (artists_request_) {
|
||||||
@@ -733,25 +536,16 @@ void TidalService::ResetArtistsRequest() {
|
|||||||
void TidalService::GetArtists() {
|
void TidalService::GetArtists() {
|
||||||
|
|
||||||
if (!authenticated()) {
|
if (!authenticated()) {
|
||||||
if (oauth_) {
|
Q_EMIT ArtistsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||||
Q_EMIT ArtistsResults(SongMap(), tr("Not authenticated with Tidal."));
|
Q_EMIT OpenSettingsDialog(kSource);
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
|
||||||
Q_EMIT ArtistsResults(SongMap(), tr("Missing Tidal API token, username or password."));
|
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetArtistsRequest();
|
ResetArtistsRequest();
|
||||||
artists_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::Type::FavouriteArtists, this), [](TidalRequest *request) { request->deleteLater(); });
|
artists_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::Type::FavouriteArtists, this), [](TidalRequest *request) { request->deleteLater(); });
|
||||||
QObject::connect(&*artists_request_, &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
|
|
||||||
QObject::connect(&*artists_request_, &TidalRequest::Results, this, &TidalService::ArtistsResultsReceived);
|
QObject::connect(&*artists_request_, &TidalRequest::Results, this, &TidalService::ArtistsResultsReceived);
|
||||||
QObject::connect(&*artists_request_, &TidalRequest::UpdateStatus, this, &TidalService::ArtistsUpdateStatusReceived);
|
QObject::connect(&*artists_request_, &TidalRequest::UpdateStatus, this, &TidalService::ArtistsUpdateStatusReceived);
|
||||||
QObject::connect(&*artists_request_, &TidalRequest::UpdateProgress, this, &TidalService::ArtistsUpdateProgressReceived);
|
QObject::connect(&*artists_request_, &TidalRequest::UpdateProgress, this, &TidalService::ArtistsUpdateProgressReceived);
|
||||||
QObject::connect(this, &TidalService::LoginComplete, &*artists_request_, &TidalRequest::LoginComplete);
|
|
||||||
|
|
||||||
artists_request_->Process();
|
artists_request_->Process();
|
||||||
|
|
||||||
@@ -788,25 +582,16 @@ void TidalService::ResetAlbumsRequest() {
|
|||||||
void TidalService::GetAlbums() {
|
void TidalService::GetAlbums() {
|
||||||
|
|
||||||
if (!authenticated()) {
|
if (!authenticated()) {
|
||||||
if (oauth_) {
|
Q_EMIT AlbumsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||||
Q_EMIT AlbumsResults(SongMap(), tr("Not authenticated with Tidal."));
|
Q_EMIT OpenSettingsDialog(kSource);
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
|
||||||
Q_EMIT AlbumsResults(SongMap(), tr("Missing Tidal API token, username or password."));
|
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetAlbumsRequest();
|
ResetAlbumsRequest();
|
||||||
albums_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::Type::FavouriteAlbums, this), [](TidalRequest *request) { request->deleteLater(); });
|
albums_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::Type::FavouriteAlbums, this), [](TidalRequest *request) { request->deleteLater(); });
|
||||||
QObject::connect(&*albums_request_, &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
|
|
||||||
QObject::connect(&*albums_request_, &TidalRequest::Results, this, &TidalService::AlbumsResultsReceived);
|
QObject::connect(&*albums_request_, &TidalRequest::Results, this, &TidalService::AlbumsResultsReceived);
|
||||||
QObject::connect(&*albums_request_, &TidalRequest::UpdateStatus, this, &TidalService::AlbumsUpdateStatusReceived);
|
QObject::connect(&*albums_request_, &TidalRequest::UpdateStatus, this, &TidalService::AlbumsUpdateStatusReceived);
|
||||||
QObject::connect(&*albums_request_, &TidalRequest::UpdateProgress, this, &TidalService::AlbumsUpdateProgressReceived);
|
QObject::connect(&*albums_request_, &TidalRequest::UpdateProgress, this, &TidalService::AlbumsUpdateProgressReceived);
|
||||||
QObject::connect(this, &TidalService::LoginComplete, &*albums_request_, &TidalRequest::LoginComplete);
|
|
||||||
|
|
||||||
albums_request_->Process();
|
albums_request_->Process();
|
||||||
|
|
||||||
@@ -843,25 +628,16 @@ void TidalService::ResetSongsRequest() {
|
|||||||
void TidalService::GetSongs() {
|
void TidalService::GetSongs() {
|
||||||
|
|
||||||
if (!authenticated()) {
|
if (!authenticated()) {
|
||||||
if (oauth_) {
|
Q_EMIT SongsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||||
Q_EMIT SongsResults(SongMap(), tr("Not authenticated with Tidal."));
|
Q_EMIT OpenSettingsDialog(kSource);
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
|
||||||
Q_EMIT SongsResults(SongMap(), tr("Missing Tidal API token, username or password."));
|
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetSongsRequest();
|
ResetSongsRequest();
|
||||||
songs_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::Type::FavouriteSongs, this), [](TidalRequest *request) { request->deleteLater(); });
|
songs_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::Type::FavouriteSongs, this), [](TidalRequest *request) { request->deleteLater(); });
|
||||||
QObject::connect(&*songs_request_, &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
|
|
||||||
QObject::connect(&*songs_request_, &TidalRequest::Results, this, &TidalService::SongsResultsReceived);
|
QObject::connect(&*songs_request_, &TidalRequest::Results, this, &TidalService::SongsResultsReceived);
|
||||||
QObject::connect(&*songs_request_, &TidalRequest::UpdateStatus, this, &TidalService::SongsUpdateStatusReceived);
|
QObject::connect(&*songs_request_, &TidalRequest::UpdateStatus, this, &TidalService::SongsUpdateStatusReceived);
|
||||||
QObject::connect(&*songs_request_, &TidalRequest::UpdateProgress, this, &TidalService::SongsUpdateProgressReceived);
|
QObject::connect(&*songs_request_, &TidalRequest::UpdateProgress, this, &TidalService::SongsUpdateProgressReceived);
|
||||||
QObject::connect(this, &TidalService::LoginComplete, &*songs_request_, &TidalRequest::LoginComplete);
|
|
||||||
|
|
||||||
songs_request_->Process();
|
songs_request_->Process();
|
||||||
|
|
||||||
@@ -906,16 +682,9 @@ int TidalService::Search(const QString &text, StreamingSearchView::SearchType ty
|
|||||||
void TidalService::StartSearch() {
|
void TidalService::StartSearch() {
|
||||||
|
|
||||||
if (!authenticated()) {
|
if (!authenticated()) {
|
||||||
if (oauth_) {
|
Q_EMIT SearchResults(pending_search_id_, SongMap(), tr("Not authenticated with Tidal."));
|
||||||
Q_EMIT SearchResults(pending_search_id_, SongMap(), tr("Not authenticated with Tidal."));
|
Q_EMIT OpenSettingsDialog(kSource);
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
|
||||||
Q_EMIT SearchResults(pending_search_id_, SongMap(), tr("Missing Tidal API token, username or password."));
|
|
||||||
Q_EMIT OpenSettingsDialog(kSource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search_id_ = pending_search_id_;
|
search_id_ = pending_search_id_;
|
||||||
@@ -925,8 +694,7 @@ void TidalService::StartSearch() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalService::CancelSearch() {
|
void TidalService::CancelSearch() {}
|
||||||
}
|
|
||||||
|
|
||||||
void TidalService::SendSearch() {
|
void TidalService::SendSearch() {
|
||||||
|
|
||||||
@@ -949,11 +717,9 @@ void TidalService::SendSearch() {
|
|||||||
|
|
||||||
search_request_.reset(new TidalRequest(this, url_handler_, network_, query_type, this), [](TidalRequest *request) { request->deleteLater(); });
|
search_request_.reset(new TidalRequest(this, url_handler_, network_, query_type, this), [](TidalRequest *request) { request->deleteLater(); });
|
||||||
|
|
||||||
QObject::connect(&*search_request_, &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
|
|
||||||
QObject::connect(&*search_request_, &TidalRequest::Results, this, &TidalService::SearchResultsReceived);
|
QObject::connect(&*search_request_, &TidalRequest::Results, this, &TidalService::SearchResultsReceived);
|
||||||
QObject::connect(&*search_request_, &TidalRequest::UpdateStatus, this, &TidalService::SearchUpdateStatus);
|
QObject::connect(&*search_request_, &TidalRequest::UpdateStatus, this, &TidalService::SearchUpdateStatus);
|
||||||
QObject::connect(&*search_request_, &TidalRequest::UpdateProgress, this, &TidalService::SearchUpdateProgress);
|
QObject::connect(&*search_request_, &TidalRequest::UpdateProgress, this, &TidalService::SearchUpdateProgress);
|
||||||
QObject::connect(this, &TidalService::LoginComplete, &*search_request_, &TidalRequest::LoginComplete);
|
|
||||||
|
|
||||||
search_request_->Search(search_id_, search_text_);
|
search_request_->Search(search_id_, search_text_);
|
||||||
search_request_->Process();
|
search_request_->Process();
|
||||||
@@ -970,26 +736,18 @@ void TidalService::SearchResultsReceived(const int id, const SongMap &songs, con
|
|||||||
uint TidalService::GetStreamURL(const QUrl &url, QString &error) {
|
uint TidalService::GetStreamURL(const QUrl &url, QString &error) {
|
||||||
|
|
||||||
if (!authenticated()) {
|
if (!authenticated()) {
|
||||||
if (oauth_) {
|
error = tr("Not authenticated with Tidal.");
|
||||||
error = tr("Not authenticated with Tidal.");
|
return 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
|
||||||
error = tr("Missing Tidal API token, username or password.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint id = 0;
|
uint id = 0;
|
||||||
while (id == 0) id = ++next_stream_url_request_id_;
|
while (id == 0) id = ++next_stream_url_request_id_;
|
||||||
SharedPtr<TidalStreamURLRequest> stream_url_req;
|
SharedPtr<TidalStreamURLRequest> stream_url_req;
|
||||||
stream_url_req.reset(new TidalStreamURLRequest(this, network_, url, id), [](TidalStreamURLRequest *request) { request->deleteLater(); });
|
stream_url_req.reset(new TidalStreamURLRequest(this, network_, url, id), [](TidalStreamURLRequest *request) { request->deleteLater(); });
|
||||||
stream_url_requests_.insert(id, stream_url_req);
|
stream_url_requests_.insert(id, stream_url_req);
|
||||||
|
|
||||||
QObject::connect(&*stream_url_req, &TidalStreamURLRequest::TryLogin, this, &TidalService::TryLogin);
|
|
||||||
QObject::connect(&*stream_url_req, &TidalStreamURLRequest::StreamURLFailure, this, &TidalService::HandleStreamURLFailure);
|
QObject::connect(&*stream_url_req, &TidalStreamURLRequest::StreamURLFailure, this, &TidalService::HandleStreamURLFailure);
|
||||||
QObject::connect(&*stream_url_req, &TidalStreamURLRequest::StreamURLSuccess, this, &TidalService::HandleStreamURLSuccess);
|
QObject::connect(&*stream_url_req, &TidalStreamURLRequest::StreamURLSuccess, this, &TidalService::HandleStreamURLSuccess);
|
||||||
QObject::connect(this, &TidalService::LoginComplete, &*stream_url_req, &TidalStreamURLRequest::LoginComplete);
|
|
||||||
|
|
||||||
stream_url_req->Process();
|
stream_url_req->Process();
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ class TidalService : public StreamingService {
|
|||||||
static const Song::Source kSource;
|
static const Song::Source kSource;
|
||||||
static const char kApiUrl[];
|
static const char kApiUrl[];
|
||||||
static const char kResourcesUrl[];
|
static const char kResourcesUrl[];
|
||||||
static const int kLoginAttempts;
|
|
||||||
|
|
||||||
void Exit() override;
|
void Exit() override;
|
||||||
void ReloadSettings() override;
|
void ReloadSettings() override;
|
||||||
@@ -83,15 +82,9 @@ class TidalService : public StreamingService {
|
|||||||
int Search(const QString &text, StreamingSearchView::SearchType type) override;
|
int Search(const QString &text, StreamingSearchView::SearchType type) override;
|
||||||
void CancelSearch() override;
|
void CancelSearch() override;
|
||||||
|
|
||||||
int max_login_attempts() const { return kLoginAttempts; }
|
|
||||||
|
|
||||||
bool oauth() const override { return oauth_; }
|
|
||||||
QString client_id() const { return client_id_; }
|
QString client_id() const { return client_id_; }
|
||||||
QString api_token() const { return api_token_; }
|
|
||||||
quint64 user_id() const { return user_id_; }
|
quint64 user_id() const { return user_id_; }
|
||||||
QString country_code() const { return country_code_; }
|
QString country_code() const { return country_code_; }
|
||||||
QString username() const { return username_; }
|
|
||||||
QString password() const { return password_; }
|
|
||||||
QString quality() const { return quality_; }
|
QString quality() const { return quality_; }
|
||||||
int artistssearchlimit() const { return artistssearchlimit_; }
|
int artistssearchlimit() const { return artistssearchlimit_; }
|
||||||
int albumssearchlimit() const { return albumssearchlimit_; }
|
int albumssearchlimit() const { return albumssearchlimit_; }
|
||||||
@@ -103,11 +96,8 @@ class TidalService : public StreamingService {
|
|||||||
bool album_explicit() const { return album_explicit_; }
|
bool album_explicit() const { return album_explicit_; }
|
||||||
|
|
||||||
QString access_token() const { return access_token_; }
|
QString access_token() const { return access_token_; }
|
||||||
QString session_id() const { return session_id_; }
|
|
||||||
|
|
||||||
bool authenticated() const override { return (!access_token_.isEmpty() || !session_id_.isEmpty()); }
|
bool authenticated() const override { return !access_token_.isEmpty(); }
|
||||||
bool login_sent() const { return login_sent_; }
|
|
||||||
bool login_attempts() const { return login_attempts_; }
|
|
||||||
|
|
||||||
uint GetStreamURL(const QUrl &url, QString &error);
|
uint GetStreamURL(const QUrl &url, QString &error);
|
||||||
|
|
||||||
@@ -125,9 +115,6 @@ class TidalService : public StreamingService {
|
|||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void StartAuthorization(const QString &client_id);
|
void StartAuthorization(const QString &client_id);
|
||||||
void TryLogin();
|
|
||||||
void SendLogin();
|
|
||||||
void SendLoginWithCredentials(const QString &api_token, const QString &username, const QString &password);
|
|
||||||
void GetArtists() override;
|
void GetArtists() override;
|
||||||
void GetAlbums() override;
|
void GetAlbums() override;
|
||||||
void GetSongs() override;
|
void GetSongs() override;
|
||||||
@@ -141,8 +128,6 @@ class TidalService : public StreamingService {
|
|||||||
void RequestNewAccessToken() { RequestAccessToken(); }
|
void RequestNewAccessToken() { RequestAccessToken(); }
|
||||||
void HandleLoginSSLErrors(const QList<QSslError> &ssl_errors);
|
void HandleLoginSSLErrors(const QList<QSslError> &ssl_errors);
|
||||||
void AccessTokenRequestFinished(QNetworkReply *reply);
|
void AccessTokenRequestFinished(QNetworkReply *reply);
|
||||||
void HandleAuthReply(QNetworkReply *reply);
|
|
||||||
void ResetLoginAttempts();
|
|
||||||
void StartSearch();
|
void StartSearch();
|
||||||
void ArtistsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
void ArtistsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||||
void AlbumsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
void AlbumsResultsReceived(const int id, const SongMap &songs, const QString &error);
|
||||||
@@ -178,7 +163,6 @@ class TidalService : public StreamingService {
|
|||||||
CollectionModel *songs_collection_model_;
|
CollectionModel *songs_collection_model_;
|
||||||
|
|
||||||
QTimer *timer_search_delay_;
|
QTimer *timer_search_delay_;
|
||||||
QTimer *timer_login_attempt_;
|
|
||||||
QTimer *timer_refresh_login_;
|
QTimer *timer_refresh_login_;
|
||||||
|
|
||||||
SharedPtr<TidalRequest> artists_request_;
|
SharedPtr<TidalRequest> artists_request_;
|
||||||
@@ -188,13 +172,9 @@ class TidalService : public StreamingService {
|
|||||||
TidalFavoriteRequest *favorite_request_;
|
TidalFavoriteRequest *favorite_request_;
|
||||||
|
|
||||||
bool enabled_;
|
bool enabled_;
|
||||||
bool oauth_;
|
|
||||||
QString client_id_;
|
QString client_id_;
|
||||||
QString api_token_;
|
|
||||||
quint64 user_id_;
|
quint64 user_id_;
|
||||||
QString country_code_;
|
QString country_code_;
|
||||||
QString username_;
|
|
||||||
QString password_;
|
|
||||||
QString quality_;
|
QString quality_;
|
||||||
int artistssearchlimit_;
|
int artistssearchlimit_;
|
||||||
int albumssearchlimit_;
|
int albumssearchlimit_;
|
||||||
@@ -207,7 +187,6 @@ class TidalService : public StreamingService {
|
|||||||
|
|
||||||
QString access_token_;
|
QString access_token_;
|
||||||
QString refresh_token_;
|
QString refresh_token_;
|
||||||
QString session_id_;
|
|
||||||
quint64 expires_in_;
|
quint64 expires_in_;
|
||||||
quint64 login_time_;
|
quint64 login_time_;
|
||||||
|
|
||||||
@@ -218,8 +197,6 @@ class TidalService : public StreamingService {
|
|||||||
|
|
||||||
int search_id_;
|
int search_id_;
|
||||||
QString search_text_;
|
QString search_text_;
|
||||||
bool login_sent_;
|
|
||||||
int login_attempts_;
|
|
||||||
|
|
||||||
QString code_verifier_;
|
QString code_verifier_;
|
||||||
QString code_challenge_;
|
QString code_challenge_;
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ TidalStreamURLRequest::TidalStreamURLRequest(TidalService *service, const Shared
|
|||||||
reply_(nullptr),
|
reply_(nullptr),
|
||||||
media_url_(media_url),
|
media_url_(media_url),
|
||||||
id_(id),
|
id_(id),
|
||||||
song_id_(media_url.path().toInt()),
|
song_id_(media_url.path().toInt()) {}
|
||||||
tries_(0),
|
|
||||||
need_login_(false) {}
|
|
||||||
|
|
||||||
TidalStreamURLRequest::~TidalStreamURLRequest() {
|
TidalStreamURLRequest::~TidalStreamURLRequest() {
|
||||||
|
|
||||||
@@ -64,33 +62,10 @@ TidalStreamURLRequest::~TidalStreamURLRequest() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TidalStreamURLRequest::LoginComplete(const bool success, const QString &error) {
|
|
||||||
|
|
||||||
if (!need_login_) return;
|
|
||||||
need_login_ = false;
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
Q_EMIT StreamURLFailure(id_, media_url_, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TidalStreamURLRequest::Process() {
|
void TidalStreamURLRequest::Process() {
|
||||||
|
|
||||||
if (!authenticated()) {
|
if (!authenticated()) {
|
||||||
if (oauth()) {
|
Q_EMIT StreamURLFailure(id_, media_url_, tr("Not authenticated with Tidal."));
|
||||||
Q_EMIT StreamURLFailure(id_, media_url_, tr("Not authenticated with Tidal."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (api_token().isEmpty() || username().isEmpty() || password().isEmpty()) {
|
|
||||||
Q_EMIT StreamURLFailure(id_, media_url_, tr("Missing Tidal API token, username or password."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
need_login_ = true;
|
|
||||||
Q_EMIT TryLogin();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +86,6 @@ void TidalStreamURLRequest::Cancel() {
|
|||||||
|
|
||||||
void TidalStreamURLRequest::GetStreamURL() {
|
void TidalStreamURLRequest::GetStreamURL() {
|
||||||
|
|
||||||
++tries_;
|
|
||||||
|
|
||||||
if (reply_) {
|
if (reply_) {
|
||||||
QObject::disconnect(reply_, nullptr, this, nullptr);
|
QObject::disconnect(reply_, nullptr, this, nullptr);
|
||||||
if (reply_->isRunning()) reply_->abort();
|
if (reply_->isRunning()) reply_->abort();
|
||||||
@@ -150,17 +123,13 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
|||||||
|
|
||||||
if (!reply_) return;
|
if (!reply_) return;
|
||||||
|
|
||||||
QByteArray data = GetReplyData(reply_, true);
|
QByteArray data = GetReplyData(reply_);
|
||||||
|
|
||||||
QObject::disconnect(reply_, nullptr, this, nullptr);
|
QObject::disconnect(reply_, nullptr, this, nullptr);
|
||||||
reply_->deleteLater();
|
reply_->deleteLater();
|
||||||
reply_ = nullptr;
|
reply_ = nullptr;
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
if (!authenticated() && login_sent() && tries_ <= 1) {
|
|
||||||
need_login_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Q_EMIT StreamURLFailure(id_, media_url_, errors_.constFirst());
|
Q_EMIT StreamURLFailure(id_, media_url_, errors_.constFirst());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,20 +54,13 @@ class TidalStreamURLRequest : public TidalBaseRequest {
|
|||||||
QUrl media_url() const { return media_url_; }
|
QUrl media_url() const { return media_url_; }
|
||||||
int song_id() const { return song_id_; }
|
int song_id() const { return song_id_; }
|
||||||
|
|
||||||
void set_need_login() override { need_login_ = true; }
|
|
||||||
bool need_login() const { return need_login_; }
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void TryLogin();
|
|
||||||
void StreamURLFailure(const uint id, const QUrl &media_url, const QString &error);
|
void StreamURLFailure(const uint id, const QUrl &media_url, const QString &error);
|
||||||
void StreamURLSuccess(const uint id, const QUrl &media_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate = -1, const int bit_depth = -1, const qint64 duration = -1);
|
void StreamURLSuccess(const uint id, const QUrl &media_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate = -1, const int bit_depth = -1, const qint64 duration = -1);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void StreamURLReceived();
|
void StreamURLReceived();
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void LoginComplete(const bool success, const QString &error = QString());
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||||
|
|
||||||
@@ -76,7 +69,6 @@ class TidalStreamURLRequest : public TidalBaseRequest {
|
|||||||
QUrl media_url_;
|
QUrl media_url_;
|
||||||
uint id_;
|
uint id_;
|
||||||
int song_id_;
|
int song_id_;
|
||||||
int tries_;
|
|
||||||
bool need_login_;
|
bool need_login_;
|
||||||
QStringList errors_;
|
QStringList errors_;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user