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);
|
||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
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());
|
||||
else if (!service_->session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", service_->session_id().toUtf8());
|
||||
if (!service_->access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + service_->access_token().toUtf8());
|
||||
|
||||
QNetworkReply *reply = network_->get(req);
|
||||
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_->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::Login, &*service_, &TidalService::SendLoginWithCredentials);
|
||||
|
||||
QObject::connect(&*service_, &StreamingService::LoginFailure, this, &TidalSettingsPage::LoginFailure);
|
||||
QObject::connect(&*service_, &StreamingService::LoginSuccess, this, &TidalSettingsPage::LoginSuccess);
|
||||
@@ -88,16 +86,7 @@ void TidalSettingsPage::Load() {
|
||||
Settings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
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_->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);
|
||||
ui_->searchdelay->setValue(s.value(kSearchDelay, 1500).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);
|
||||
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());
|
||||
|
||||
s.endGroup();
|
||||
|
||||
OAuthClicked(ui_->oauth->isChecked());
|
||||
if (service_->authenticated()) ui_->login_state->SetLoggedIn(LoginStateWidget::State::LoggedIn);
|
||||
if (service_->authenticated()) {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::State::LoggedIn);
|
||||
}
|
||||
|
||||
Init(ui_->layout_tidalsettingspage->parentWidget());
|
||||
|
||||
@@ -125,13 +114,19 @@ void TidalSettingsPage::Save() {
|
||||
Settings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
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(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(kSearchDelay, ui_->searchdelay->value());
|
||||
s.setValue(kArtistsSearchLimit, ui_->artistssearchlimit->value());
|
||||
@@ -148,28 +143,11 @@ void TidalSettingsPage::Save() {
|
||||
|
||||
void TidalSettingsPage::LoginClicked() {
|
||||
|
||||
if (ui_->oauth->isChecked()) {
|
||||
if (ui_->client_id->text().isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Configuration incomplete"), tr("Missing Tidal client ID."));
|
||||
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());
|
||||
if (ui_->client_id->text().isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Configuration incomplete"), tr("Missing Tidal client ID."));
|
||||
return;
|
||||
}
|
||||
Q_EMIT Authorize(ui_->client_id->text());
|
||||
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() {
|
||||
|
||||
service_->Logout();
|
||||
|
||||
@@ -47,10 +47,8 @@ class TidalSettingsPage : public SettingsPage {
|
||||
|
||||
Q_SIGNALS:
|
||||
void Authorize(const QString &client_id);
|
||||
void Login(const QString &api_token, const QString &username, const QString &password);
|
||||
|
||||
private Q_SLOTS:
|
||||
void OAuthClicked(const bool enabled);
|
||||
void LoginClicked();
|
||||
void LogoutClicked();
|
||||
void LoginSuccess();
|
||||
|
||||
@@ -47,13 +47,6 @@
|
||||
</property>
|
||||
<layout class="QFormLayout" name="layout_credential_group">
|
||||
<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">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
@@ -66,74 +59,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="client_id">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -308,7 +240,7 @@
|
||||
<item>
|
||||
<spacer name="spacer_middle">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@@ -323,7 +255,7 @@
|
||||
<item>
|
||||
<spacer name="spacer_bottom">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@@ -366,11 +298,7 @@
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>enable</tabstop>
|
||||
<tabstop>oauth</tabstop>
|
||||
<tabstop>client_id</tabstop>
|
||||
<tabstop>api_token</tabstop>
|
||||
<tabstop>username</tabstop>
|
||||
<tabstop>password</tabstop>
|
||||
<tabstop>button_login</tabstop>
|
||||
<tabstop>quality</tabstop>
|
||||
<tabstop>searchdelay</tabstop>
|
||||
@@ -384,6 +312,8 @@
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../data/icons.qrc"/>
|
||||
<include location="../../data/icons.qrc"/>
|
||||
<include location="../../data/icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -62,8 +62,7 @@ QNetworkReply *TidalBaseRequest::CreateRequest(const QString &ressource_name, co
|
||||
QNetworkRequest req(url);
|
||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||
if (oauth() && !access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||
else if (!session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", session_id().toUtf8());
|
||||
if (!access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||
|
||||
QNetworkReply *reply = network_->get(req);
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class TidalBaseRequest : public QObject {
|
||||
using ParamList = QList<Param>;
|
||||
|
||||
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);
|
||||
QJsonValue ExtractItems(const QByteArray &data);
|
||||
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;
|
||||
static QString ErrorsToHTML(const QStringList &errors);
|
||||
|
||||
bool oauth() const { return service_->oauth(); }
|
||||
QString client_id() const { return service_->client_id(); }
|
||||
QString api_token() const { return service_->api_token(); }
|
||||
quint64 user_id() const { return service_->user_id(); }
|
||||
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(); }
|
||||
int artistssearchlimit() const { return service_->artistssearchlimit(); }
|
||||
int albumssearchlimit() const { return service_->albumssearchlimit(); }
|
||||
int songssearchlimit() const { return service_->songssearchlimit(); }
|
||||
|
||||
QString access_token() const { return service_->access_token(); }
|
||||
QString session_id() const { return service_->session_id(); }
|
||||
|
||||
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:
|
||||
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)
|
||||
: TidalBaseRequest(service, network, parent),
|
||||
service_(service),
|
||||
network_(network),
|
||||
need_login_(false) {}
|
||||
network_(network) {}
|
||||
|
||||
TidalFavoriteRequest::~TidalFavoriteRequest() {
|
||||
|
||||
@@ -148,8 +147,7 @@ void TidalFavoriteRequest::AddFavoritesRequest(const FavoriteType type, const QS
|
||||
QNetworkRequest req(url);
|
||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||
if (oauth() && !access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||
else if (!session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", session_id().toUtf8());
|
||||
if (!access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||
QByteArray query = url_query.toString(QUrl::FullyEncoded).toUtf8();
|
||||
QNetworkReply *reply = network_->post(req, query);
|
||||
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;
|
||||
}
|
||||
|
||||
GetReplyData(reply, false);
|
||||
GetReplyData(reply);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
return;
|
||||
@@ -258,8 +256,7 @@ void TidalFavoriteRequest::RemoveFavoritesRequest(const FavoriteType type, const
|
||||
QNetworkRequest req(url);
|
||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, u"application/x-www-form-urlencoded"_s);
|
||||
if (oauth() && !access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||
else if (!session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", session_id().toUtf8());
|
||||
if (!access_token().isEmpty()) req.setRawHeader("authorization", "Bearer " + access_token().toUtf8());
|
||||
QNetworkReply *reply = network_->deleteResource(req);
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, type, songs]() { RemoveFavoritesReply(reply, type, songs); });
|
||||
replies_ << reply;
|
||||
@@ -279,7 +276,7 @@ void TidalFavoriteRequest::RemoveFavoritesReply(QNetworkReply *reply, const Favo
|
||||
return;
|
||||
}
|
||||
|
||||
GetReplyData(reply, false);
|
||||
GetReplyData(reply);
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,9 +43,6 @@ class TidalFavoriteRequest : public TidalBaseRequest {
|
||||
explicit TidalFavoriteRequest(TidalService *service, const SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
|
||||
~TidalFavoriteRequest() override;
|
||||
|
||||
bool need_login() const { return need_login_; }
|
||||
void set_need_login() override { need_login_ = true; }
|
||||
|
||||
private:
|
||||
enum class FavoriteType {
|
||||
Artists,
|
||||
@@ -89,7 +86,6 @@ class TidalFavoriteRequest : public TidalBaseRequest {
|
||||
TidalService *service_;
|
||||
const SharedPtr<NetworkAccessManager> network_;
|
||||
QList <QNetworkReply*> replies_;
|
||||
bool need_login_;
|
||||
};
|
||||
|
||||
#endif // TIDALFAVORITEREQUEST_H
|
||||
|
||||
@@ -99,8 +99,7 @@ TidalRequest::TidalRequest(TidalService *service, TidalUrlHandler *url_handler,
|
||||
album_songs_received_(0),
|
||||
album_covers_requests_total_(0),
|
||||
album_covers_requests_active_(0),
|
||||
album_covers_requests_received_(0),
|
||||
need_login_(false) {
|
||||
album_covers_requests_received_(0) {
|
||||
|
||||
timer_flush_requests_->setInterval(kFlushRequestsDelay);
|
||||
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() {
|
||||
|
||||
if (!service_->authenticated()) {
|
||||
Q_EMIT UpdateStatus(query_id_, tr("Authenticating..."));
|
||||
need_login_ = true;
|
||||
service_->TryLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (query_type_) {
|
||||
case Type::FavouriteArtists:
|
||||
GetArtists();
|
||||
@@ -417,7 +395,7 @@ void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = GetReplyData(reply, (offset_requested == 0));
|
||||
QByteArray data = GetReplyData(reply);
|
||||
|
||||
--artists_requests_active_;
|
||||
++artists_requests_received_;
|
||||
@@ -564,7 +542,7 @@ void TidalRequest::AlbumsReplyReceived(QNetworkReply *reply, const int limit_req
|
||||
|
||||
--albums_requests_active_;
|
||||
++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_received_;
|
||||
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;
|
||||
replies_.removeAll(reply);
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = GetReplyData(reply, auto_login);
|
||||
QByteArray data = GetReplyData(reply);
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
@@ -835,10 +813,10 @@ void TidalRequest::SongsReplyReceived(QNetworkReply *reply, const int limit_requ
|
||||
--songs_requests_active_;
|
||||
++songs_requests_received_;
|
||||
if (query_type_ == Type::SearchSongs && fetchalbums_) {
|
||||
AlbumsReceived(reply, Artist(), limit_requested, offset_requested, offset_requested == 0);
|
||||
AlbumsReceived(reply, Artist(), limit_requested, offset_requested);
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
replies_.removeAll(reply);
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = GetReplyData(reply, auto_login);
|
||||
QByteArray data = GetReplyData(reply);
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
@@ -1339,7 +1317,6 @@ void TidalRequest::FinishCheck() {
|
||||
|
||||
if (
|
||||
!finished_ &&
|
||||
!need_login_ &&
|
||||
artists_requests_queue_.isEmpty() &&
|
||||
albums_requests_queue_.isEmpty() &&
|
||||
songs_requests_queue_.isEmpty() &&
|
||||
|
||||
@@ -58,7 +58,6 @@ class TidalRequest : public TidalBaseRequest {
|
||||
void ReloadSettings();
|
||||
|
||||
void Process();
|
||||
void set_need_login() override { need_login_ = true; }
|
||||
void Search(const int query_id, const QString &search_text);
|
||||
|
||||
private:
|
||||
@@ -110,18 +109,15 @@ class TidalRequest : public TidalBaseRequest {
|
||||
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 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 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 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);
|
||||
|
||||
public Q_SLOTS:
|
||||
void LoginComplete(const bool success, const QString &error = QString());
|
||||
|
||||
private:
|
||||
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); }
|
||||
@@ -233,7 +229,6 @@ class TidalRequest : public TidalBaseRequest {
|
||||
|
||||
SongMap songs_;
|
||||
QStringList errors_;
|
||||
bool need_login_;
|
||||
QList<QNetworkReply*> 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::kResourcesUrl[] = "https://resources.tidal.com";
|
||||
const int TidalService::kLoginAttempts = 2;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kOAuthUrl[] = "https://login.tidal.com/authorize";
|
||||
constexpr char kOAuthAccessTokenUrl[] = "https://login.tidal.com/oauth2/token";
|
||||
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 kAlbumsSongsTable[] = "tidal_albums_songs";
|
||||
@@ -116,11 +112,9 @@ TidalService::TidalService(const SharedPtr<TaskManager> task_manager,
|
||||
albums_collection_model_(nullptr),
|
||||
songs_collection_model_(nullptr),
|
||||
timer_search_delay_(new QTimer(this)),
|
||||
timer_login_attempt_(new QTimer(this)),
|
||||
timer_refresh_login_(new QTimer(this)),
|
||||
favorite_request_(new TidalFavoriteRequest(this, network_, this)),
|
||||
enabled_(false),
|
||||
oauth_(false),
|
||||
user_id_(0),
|
||||
artistssearchlimit_(1),
|
||||
albumssearchlimit_(1),
|
||||
@@ -135,8 +129,6 @@ TidalService::TidalService(const SharedPtr<TaskManager> task_manager,
|
||||
next_pending_search_id_(1),
|
||||
pending_search_type_(StreamingSearchView::SearchType::Artists),
|
||||
search_id_(0),
|
||||
login_sent_(false),
|
||||
login_attempts_(0),
|
||||
next_stream_url_request_id_(0) {
|
||||
|
||||
url_handlers->Register(url_handler_);
|
||||
@@ -165,16 +157,9 @@ TidalService::TidalService(const SharedPtr<TaskManager> task_manager,
|
||||
timer_search_delay_->setSingleShot(true);
|
||||
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);
|
||||
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::AddAlbums, favorite_request_, &TidalFavoriteRequest::AddAlbums);
|
||||
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::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::AlbumsAdded, &*albums_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();
|
||||
access_token_ = s.value(kAccessToken).toString();
|
||||
refresh_token_ = s.value(kRefreshToken).toString();
|
||||
session_id_ = s.value(kSessionId).toString();
|
||||
expires_in_ = s.value(kExpiresIn).toLongLong();
|
||||
login_time_ = s.value(kLoginTime).toLongLong();
|
||||
s.endGroup();
|
||||
@@ -271,15 +253,7 @@ void TidalService::ReloadSettings() {
|
||||
s.beginGroup(TidalSettings::kSettingsGroup);
|
||||
|
||||
enabled_ = s.value(TidalSettings::kEnabled, false).toBool();
|
||||
oauth_ = s.value(TidalSettings::kOAuth, true).toBool();
|
||||
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();
|
||||
quint64 search_delay = s.value(TidalSettings::kSearchDelay, 1500).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();
|
||||
login_time_ = QDateTime::currentSecsSinceEpoch();
|
||||
session_id_.clear();
|
||||
|
||||
Settings s;
|
||||
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"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"refresh_token"_s, refresh_token_);
|
||||
}
|
||||
@@ -501,8 +474,6 @@ void TidalService::AccessTokenRequestFinished(QNetworkReply *reply) {
|
||||
}
|
||||
}
|
||||
|
||||
session_id_.clear();
|
||||
|
||||
Settings s;
|
||||
s.beginGroup(TidalSettings::kSettingsGroup);
|
||||
s.setValue(kAccessToken, access_token_);
|
||||
@@ -512,6 +483,8 @@ void TidalService::AccessTokenRequestFinished(QNetworkReply *reply) {
|
||||
s.setValue(kCountryCode, country_code_);
|
||||
s.setValue(kUserId, user_id_);
|
||||
s.remove(kSessionId);
|
||||
s.remove(kUserId);
|
||||
s.remove(kCountryCode);
|
||||
s.endGroup();
|
||||
|
||||
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() {
|
||||
|
||||
user_id_ = 0;
|
||||
country_code_.clear();
|
||||
access_token_.clear();
|
||||
refresh_token_.clear();
|
||||
session_id_.clear();
|
||||
expires_in_ = 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() {
|
||||
|
||||
if (artists_request_) {
|
||||
@@ -733,25 +536,16 @@ void TidalService::ResetArtistsRequest() {
|
||||
void TidalService::GetArtists() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
Q_EMIT ArtistsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
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;
|
||||
}
|
||||
Q_EMIT ArtistsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
return;
|
||||
}
|
||||
|
||||
ResetArtistsRequest();
|
||||
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::UpdateStatus, this, &TidalService::ArtistsUpdateStatusReceived);
|
||||
QObject::connect(&*artists_request_, &TidalRequest::UpdateProgress, this, &TidalService::ArtistsUpdateProgressReceived);
|
||||
QObject::connect(this, &TidalService::LoginComplete, &*artists_request_, &TidalRequest::LoginComplete);
|
||||
|
||||
artists_request_->Process();
|
||||
|
||||
@@ -788,25 +582,16 @@ void TidalService::ResetAlbumsRequest() {
|
||||
void TidalService::GetAlbums() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
Q_EMIT AlbumsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
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;
|
||||
}
|
||||
Q_EMIT AlbumsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
return;
|
||||
}
|
||||
|
||||
ResetAlbumsRequest();
|
||||
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::UpdateStatus, this, &TidalService::AlbumsUpdateStatusReceived);
|
||||
QObject::connect(&*albums_request_, &TidalRequest::UpdateProgress, this, &TidalService::AlbumsUpdateProgressReceived);
|
||||
QObject::connect(this, &TidalService::LoginComplete, &*albums_request_, &TidalRequest::LoginComplete);
|
||||
|
||||
albums_request_->Process();
|
||||
|
||||
@@ -843,25 +628,16 @@ void TidalService::ResetSongsRequest() {
|
||||
void TidalService::GetSongs() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
Q_EMIT SongsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
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;
|
||||
}
|
||||
Q_EMIT SongsResults(SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
return;
|
||||
}
|
||||
|
||||
ResetSongsRequest();
|
||||
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::UpdateStatus, this, &TidalService::SongsUpdateStatusReceived);
|
||||
QObject::connect(&*songs_request_, &TidalRequest::UpdateProgress, this, &TidalService::SongsUpdateProgressReceived);
|
||||
QObject::connect(this, &TidalService::LoginComplete, &*songs_request_, &TidalRequest::LoginComplete);
|
||||
|
||||
songs_request_->Process();
|
||||
|
||||
@@ -906,16 +682,9 @@ int TidalService::Search(const QString &text, StreamingSearchView::SearchType ty
|
||||
void TidalService::StartSearch() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
Q_EMIT SearchResults(pending_search_id_, SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
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;
|
||||
}
|
||||
Q_EMIT SearchResults(pending_search_id_, SongMap(), tr("Not authenticated with Tidal."));
|
||||
Q_EMIT OpenSettingsDialog(kSource);
|
||||
return;
|
||||
}
|
||||
|
||||
search_id_ = pending_search_id_;
|
||||
@@ -925,8 +694,7 @@ void TidalService::StartSearch() {
|
||||
|
||||
}
|
||||
|
||||
void TidalService::CancelSearch() {
|
||||
}
|
||||
void TidalService::CancelSearch() {}
|
||||
|
||||
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(); });
|
||||
|
||||
QObject::connect(&*search_request_, &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
|
||||
QObject::connect(&*search_request_, &TidalRequest::Results, this, &TidalService::SearchResultsReceived);
|
||||
QObject::connect(&*search_request_, &TidalRequest::UpdateStatus, this, &TidalService::SearchUpdateStatus);
|
||||
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_->Process();
|
||||
@@ -970,26 +736,18 @@ void TidalService::SearchResultsReceived(const int id, const SongMap &songs, con
|
||||
uint TidalService::GetStreamURL(const QUrl &url, QString &error) {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth_) {
|
||||
error = tr("Not authenticated with Tidal.");
|
||||
return 0;
|
||||
}
|
||||
else if (api_token_.isEmpty() || username_.isEmpty() || password_.isEmpty()) {
|
||||
error = tr("Missing Tidal API token, username or password.");
|
||||
return 0;
|
||||
}
|
||||
error = tr("Not authenticated with Tidal.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint id = 0;
|
||||
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_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::StreamURLSuccess, this, &TidalService::HandleStreamURLSuccess);
|
||||
QObject::connect(this, &TidalService::LoginComplete, &*stream_url_req, &TidalStreamURLRequest::LoginComplete);
|
||||
|
||||
stream_url_req->Process();
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ class TidalService : public StreamingService {
|
||||
static const Song::Source kSource;
|
||||
static const char kApiUrl[];
|
||||
static const char kResourcesUrl[];
|
||||
static const int kLoginAttempts;
|
||||
|
||||
void Exit() override;
|
||||
void ReloadSettings() override;
|
||||
@@ -83,15 +82,9 @@ class TidalService : public StreamingService {
|
||||
int Search(const QString &text, StreamingSearchView::SearchType type) 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 api_token() const { return api_token_; }
|
||||
quint64 user_id() const { return user_id_; }
|
||||
QString country_code() const { return country_code_; }
|
||||
QString username() const { return username_; }
|
||||
QString password() const { return password_; }
|
||||
QString quality() const { return quality_; }
|
||||
int artistssearchlimit() const { return artistssearchlimit_; }
|
||||
int albumssearchlimit() const { return albumssearchlimit_; }
|
||||
@@ -103,11 +96,8 @@ class TidalService : public StreamingService {
|
||||
bool album_explicit() const { return album_explicit_; }
|
||||
|
||||
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 login_sent() const { return login_sent_; }
|
||||
bool login_attempts() const { return login_attempts_; }
|
||||
bool authenticated() const override { return !access_token_.isEmpty(); }
|
||||
|
||||
uint GetStreamURL(const QUrl &url, QString &error);
|
||||
|
||||
@@ -125,9 +115,6 @@ class TidalService : public StreamingService {
|
||||
|
||||
public Q_SLOTS:
|
||||
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 GetAlbums() override;
|
||||
void GetSongs() override;
|
||||
@@ -141,8 +128,6 @@ class TidalService : public StreamingService {
|
||||
void RequestNewAccessToken() { RequestAccessToken(); }
|
||||
void HandleLoginSSLErrors(const QList<QSslError> &ssl_errors);
|
||||
void AccessTokenRequestFinished(QNetworkReply *reply);
|
||||
void HandleAuthReply(QNetworkReply *reply);
|
||||
void ResetLoginAttempts();
|
||||
void StartSearch();
|
||||
void ArtistsResultsReceived(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_;
|
||||
|
||||
QTimer *timer_search_delay_;
|
||||
QTimer *timer_login_attempt_;
|
||||
QTimer *timer_refresh_login_;
|
||||
|
||||
SharedPtr<TidalRequest> artists_request_;
|
||||
@@ -188,13 +172,9 @@ class TidalService : public StreamingService {
|
||||
TidalFavoriteRequest *favorite_request_;
|
||||
|
||||
bool enabled_;
|
||||
bool oauth_;
|
||||
QString client_id_;
|
||||
QString api_token_;
|
||||
quint64 user_id_;
|
||||
QString country_code_;
|
||||
QString username_;
|
||||
QString password_;
|
||||
QString quality_;
|
||||
int artistssearchlimit_;
|
||||
int albumssearchlimit_;
|
||||
@@ -207,7 +187,6 @@ class TidalService : public StreamingService {
|
||||
|
||||
QString access_token_;
|
||||
QString refresh_token_;
|
||||
QString session_id_;
|
||||
quint64 expires_in_;
|
||||
quint64 login_time_;
|
||||
|
||||
@@ -218,8 +197,6 @@ class TidalService : public StreamingService {
|
||||
|
||||
int search_id_;
|
||||
QString search_text_;
|
||||
bool login_sent_;
|
||||
int login_attempts_;
|
||||
|
||||
QString code_verifier_;
|
||||
QString code_challenge_;
|
||||
|
||||
@@ -50,9 +50,7 @@ TidalStreamURLRequest::TidalStreamURLRequest(TidalService *service, const Shared
|
||||
reply_(nullptr),
|
||||
media_url_(media_url),
|
||||
id_(id),
|
||||
song_id_(media_url.path().toInt()),
|
||||
tries_(0),
|
||||
need_login_(false) {}
|
||||
song_id_(media_url.path().toInt()) {}
|
||||
|
||||
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() {
|
||||
|
||||
if (!authenticated()) {
|
||||
if (oauth()) {
|
||||
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();
|
||||
Q_EMIT StreamURLFailure(id_, media_url_, tr("Not authenticated with Tidal."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,8 +86,6 @@ void TidalStreamURLRequest::Cancel() {
|
||||
|
||||
void TidalStreamURLRequest::GetStreamURL() {
|
||||
|
||||
++tries_;
|
||||
|
||||
if (reply_) {
|
||||
QObject::disconnect(reply_, nullptr, this, nullptr);
|
||||
if (reply_->isRunning()) reply_->abort();
|
||||
@@ -150,17 +123,13 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
||||
|
||||
if (!reply_) return;
|
||||
|
||||
QByteArray data = GetReplyData(reply_, true);
|
||||
QByteArray data = GetReplyData(reply_);
|
||||
|
||||
QObject::disconnect(reply_, nullptr, this, nullptr);
|
||||
reply_->deleteLater();
|
||||
reply_ = nullptr;
|
||||
|
||||
if (data.isEmpty()) {
|
||||
if (!authenticated() && login_sent() && tries_ <= 1) {
|
||||
need_login_ = true;
|
||||
return;
|
||||
}
|
||||
Q_EMIT StreamURLFailure(id_, media_url_, errors_.constFirst());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,20 +54,13 @@ class TidalStreamURLRequest : public TidalBaseRequest {
|
||||
QUrl media_url() const { return media_url_; }
|
||||
int song_id() const { return song_id_; }
|
||||
|
||||
void set_need_login() override { need_login_ = true; }
|
||||
bool need_login() const { return need_login_; }
|
||||
|
||||
Q_SIGNALS:
|
||||
void TryLogin();
|
||||
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);
|
||||
|
||||
private Q_SLOTS:
|
||||
void StreamURLReceived();
|
||||
|
||||
public Q_SLOTS:
|
||||
void LoginComplete(const bool success, const QString &error = QString());
|
||||
|
||||
private:
|
||||
void Error(const QString &error, const QVariant &debug = QVariant()) override;
|
||||
|
||||
@@ -76,7 +69,6 @@ class TidalStreamURLRequest : public TidalBaseRequest {
|
||||
QUrl media_url_;
|
||||
uint id_;
|
||||
int song_id_;
|
||||
int tries_;
|
||||
bool need_login_;
|
||||
QStringList errors_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user