Authenticate Last.fm with "oauth".
This commit is contained in:
parent
a056a73165
commit
a8cb9bbd2a
@ -39,7 +39,10 @@
|
|||||||
|
|
||||||
#include "lastfmservice.h"
|
#include "lastfmservice.h"
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QDesktopServices>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#ifdef HAVE_LIBLASTFM1
|
#ifdef HAVE_LIBLASTFM1
|
||||||
@ -49,14 +52,16 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "lastfmcompat.h"
|
#include "lastfmcompat.h"
|
||||||
#include "internet/core/internetmodel.h"
|
|
||||||
#include "internet/core/internetplaylistitem.h"
|
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/closure.h"
|
#include "core/closure.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/network.h"
|
||||||
#include "core/player.h"
|
#include "core/player.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/taskmanager.h"
|
#include "core/taskmanager.h"
|
||||||
|
#include "internet/core/internetmodel.h"
|
||||||
|
#include "internet/core/internetplaylistitem.h"
|
||||||
|
#include "internet/core/localredirectserver.h"
|
||||||
#include "covers/coverproviders.h"
|
#include "covers/coverproviders.h"
|
||||||
#include "covers/lastfmcoverprovider.h"
|
#include "covers/lastfmcoverprovider.h"
|
||||||
#include "ui/iconloader.h"
|
#include "ui/iconloader.h"
|
||||||
@ -82,7 +87,8 @@ LastFMService::LastFMService(Application* app, QObject* parent)
|
|||||||
already_scrobbled_(false),
|
already_scrobbled_(false),
|
||||||
scrobbling_enabled_(false),
|
scrobbling_enabled_(false),
|
||||||
connection_problems_(false),
|
connection_problems_(false),
|
||||||
app_(app) {
|
app_(app),
|
||||||
|
network_(new NetworkAccessManager) {
|
||||||
#ifdef HAVE_LIBLASTFM1
|
#ifdef HAVE_LIBLASTFM1
|
||||||
lastfm::ws::setScheme(lastfm::ws::Https);
|
lastfm::ws::setScheme(lastfm::ws::Https);
|
||||||
#endif
|
#endif
|
||||||
@ -130,37 +136,50 @@ bool LastFMService::IsSubscriber() const {
|
|||||||
return settings.value("Subscriber", false).toBool();
|
return settings.value("Subscriber", false).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LastFMService::GetToken() {
|
namespace {
|
||||||
QMap<QString, QString> params;
|
QByteArray SignApiRequest(QList<QPair<QString, QString>> params) {
|
||||||
params["method"] = "auth.getToken";
|
qSort(params);
|
||||||
QNetworkReply* reply = lastfm::ws::post(params);
|
QString to_sign;
|
||||||
NewClosure(reply, SIGNAL(finished()), this,
|
for (const auto& p : params) {
|
||||||
SLOT(GetTokenReplyFinished(QNetworkReply*)), reply);
|
to_sign += p.first;
|
||||||
}
|
to_sign += p.second;
|
||||||
|
|
||||||
void LastFMService::GetTokenReplyFinished(QNetworkReply* reply) {
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
// Parse the reply
|
|
||||||
lastfm::XmlQuery lfm(lastfm::compat::EmptyXmlQuery());
|
|
||||||
if (lastfm::compat::ParseQuery(reply->readAll(), &lfm)) {
|
|
||||||
QString token = lfm["token"].text();
|
|
||||||
|
|
||||||
emit TokenReceived(true, token);
|
|
||||||
} else {
|
|
||||||
emit TokenReceived(false, lfm["error"].text().trimmed());
|
|
||||||
}
|
}
|
||||||
|
to_sign += LastFMService::kSecret;
|
||||||
|
return QCryptographicHash::hash(to_sign.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void LastFMService::Authenticate(const QString& token) {
|
void LastFMService::Authenticate() {
|
||||||
QMap<QString, QString> params;
|
QUrl url("https://www.last.fm/api/auth/");
|
||||||
params["method"] = "auth.getSession";
|
url.addQueryItem("api_key", kApiKey);
|
||||||
params["token"] = token;
|
|
||||||
|
|
||||||
QNetworkReply* reply = lastfm::ws::post(params);
|
LocalRedirectServer* server = new LocalRedirectServer(this);
|
||||||
NewClosure(reply, SIGNAL(finished()), this,
|
server->Listen();
|
||||||
SLOT(AuthenticateReplyFinished(QNetworkReply*)), reply);
|
|
||||||
// If we need more detailed error reporting, handle error(NetworkError) signal
|
url.addQueryItem("cb", server->url().toString());
|
||||||
|
|
||||||
|
NewClosure(server, SIGNAL(Finished()), [this, server]() {
|
||||||
|
server->deleteLater();
|
||||||
|
|
||||||
|
const QUrl& url = server->request_url();
|
||||||
|
QString token = url.queryItemValue("token");
|
||||||
|
|
||||||
|
QUrl session_url("https://ws.audioscrobbler.com/2.0/");
|
||||||
|
session_url.addQueryItem("api_key", kApiKey);
|
||||||
|
session_url.addQueryItem("method", "auth.getSession");
|
||||||
|
session_url.addQueryItem("token", token);
|
||||||
|
session_url.addQueryItem("api_sig", SignApiRequest(session_url.queryItems()));
|
||||||
|
|
||||||
|
QNetworkReply* reply = network_->get(QNetworkRequest(session_url));
|
||||||
|
NewClosure(reply, SIGNAL(finished()), this, SLOT(AuthenticateReplyFinished(QNetworkReply*)), reply);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!QDesktopServices::openUrl(url)) {
|
||||||
|
QMessageBox box(QMessageBox::NoIcon, tr("Last.fm Authentication"), tr("Please open this url in your browser: <a href=\"%1\">%1</a>").arg(url.toString()), QMessageBox::Ok);
|
||||||
|
box.setTextFormat(Qt::RichText);
|
||||||
|
qLog(Debug) << "Last.fm authentication URL: " << url.toString();
|
||||||
|
box.exec();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LastFMService::AuthenticateReplyFinished(QNetworkReply* reply) {
|
void LastFMService::AuthenticateReplyFinished(QNetworkReply* reply) {
|
||||||
@ -181,14 +200,14 @@ void LastFMService::AuthenticateReplyFinished(QNetworkReply* reply) {
|
|||||||
settings.setValue("Session", lastfm::ws::SessionKey);
|
settings.setValue("Session", lastfm::ws::SessionKey);
|
||||||
settings.setValue("Subscriber", is_subscriber);
|
settings.setValue("Subscriber", is_subscriber);
|
||||||
} else {
|
} else {
|
||||||
emit AuthenticationComplete(false, lfm["error"].text().trimmed());
|
emit AuthenticationComplete(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate the scrobbler - it will get recreated later
|
// Invalidate the scrobbler - it will get recreated later
|
||||||
scrobbler_.reset(nullptr);
|
scrobbler_.reset(nullptr);
|
||||||
|
|
||||||
emit AuthenticationComplete(true, QString());
|
emit AuthenticationComplete(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LastFMService::SignOut() {
|
void LastFMService::SignOut() {
|
||||||
|
@ -38,8 +38,8 @@ uint qHash(const lastfm::Track& track);
|
|||||||
|
|
||||||
class Application;
|
class Application;
|
||||||
class LastFMUrlHandler;
|
class LastFMUrlHandler;
|
||||||
|
class NetworkAccessManager;
|
||||||
class QAction;
|
class QAction;
|
||||||
class QNetworkAccessManager;
|
|
||||||
class Song;
|
class Song;
|
||||||
|
|
||||||
class LastFMService : public Scrobbler {
|
class LastFMService : public Scrobbler {
|
||||||
@ -69,8 +69,7 @@ class LastFMService : public Scrobbler {
|
|||||||
bool PreferAlbumArtist() const { return prefer_albumartist_; }
|
bool PreferAlbumArtist() const { return prefer_albumartist_; }
|
||||||
bool HasConnectionProblems() const { return connection_problems_; }
|
bool HasConnectionProblems() const { return connection_problems_; }
|
||||||
|
|
||||||
void GetToken();
|
void Authenticate();
|
||||||
void Authenticate(const QString& token);
|
|
||||||
void SignOut();
|
void SignOut();
|
||||||
void UpdateSubscriberStatus();
|
void UpdateSubscriberStatus();
|
||||||
|
|
||||||
@ -83,8 +82,7 @@ class LastFMService : public Scrobbler {
|
|||||||
void ToggleScrobbling();
|
void ToggleScrobbling();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void TokenReceived(bool success, const QString& token);
|
void AuthenticationComplete(bool success);
|
||||||
void AuthenticationComplete(bool success, const QString& error_message);
|
|
||||||
void ScrobblingEnabledChanged(bool value);
|
void ScrobblingEnabledChanged(bool value);
|
||||||
void ButtonVisibilityChanged(bool value);
|
void ButtonVisibilityChanged(bool value);
|
||||||
void ScrobbleButtonVisibilityChanged(bool value);
|
void ScrobbleButtonVisibilityChanged(bool value);
|
||||||
@ -97,7 +95,6 @@ signals:
|
|||||||
void SavedItemsChanged();
|
void SavedItemsChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void GetTokenReplyFinished(QNetworkReply* reply);
|
|
||||||
void AuthenticateReplyFinished(QNetworkReply* reply);
|
void AuthenticateReplyFinished(QNetworkReply* reply);
|
||||||
void UpdateSubscriberStatusFinished(QNetworkReply* reply);
|
void UpdateSubscriberStatusFinished(QNetworkReply* reply);
|
||||||
|
|
||||||
@ -129,6 +126,7 @@ signals:
|
|||||||
bool connection_problems_;
|
bool connection_problems_;
|
||||||
|
|
||||||
Application* app_;
|
Application* app_;
|
||||||
|
std::unique_ptr<NetworkAccessManager> network_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INTERNET_LASTFM_LASTFMSERVICE_H_
|
#endif // INTERNET_LASTFM_LASTFMSERVICE_H_
|
||||||
|
@ -43,10 +43,8 @@ LastFMSettingsPage::LastFMSettingsPage(SettingsDialog* dialog)
|
|||||||
// Icons
|
// Icons
|
||||||
setWindowIcon(IconLoader::Load("lastfm", IconLoader::Provider));
|
setWindowIcon(IconLoader::Load("lastfm", IconLoader::Provider));
|
||||||
|
|
||||||
connect(service_, SIGNAL(TokenReceived(bool,QString)),
|
connect(service_, SIGNAL(AuthenticationComplete(bool)),
|
||||||
SLOT(TokenReceived(bool,QString)));
|
SLOT(AuthenticationComplete(bool)));
|
||||||
connect(service_, SIGNAL(AuthenticationComplete(bool, QString)),
|
|
||||||
SLOT(AuthenticationComplete(bool, QString)));
|
|
||||||
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
|
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
|
||||||
connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login()));
|
connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login()));
|
||||||
connect(ui_->login, SIGNAL(clicked()), SLOT(Login()));
|
connect(ui_->login, SIGNAL(clicked()), SLOT(Login()));
|
||||||
@ -62,26 +60,10 @@ void LastFMSettingsPage::Login() {
|
|||||||
waiting_for_auth_ = true;
|
waiting_for_auth_ = true;
|
||||||
|
|
||||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
|
ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
|
||||||
service_->GetToken();
|
service_->Authenticate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LastFMSettingsPage::TokenReceived(bool success, const QString &token) {
|
void LastFMSettingsPage::AuthenticationComplete(bool success) {
|
||||||
if (!success) {
|
|
||||||
QMessageBox::warning(this, tr("Last.fm authentication failed"), token);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString url = QString(LastFMService::kAuthLoginUrl).arg(LastFMService::kApiKey, token);
|
|
||||||
QDesktopServices::openUrl(QUrl(url));
|
|
||||||
|
|
||||||
QMessageBox::information(this, tr("Last.fm authentication"),
|
|
||||||
tr("Click Ok once you authenticated Clementine in your last.fm account."));
|
|
||||||
|
|
||||||
service_->Authenticate(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LastFMSettingsPage::AuthenticationComplete(bool success,
|
|
||||||
const QString& message) {
|
|
||||||
if (!waiting_for_auth_) return; // Wasn't us that was waiting for auth
|
if (!waiting_for_auth_) return; // Wasn't us that was waiting for auth
|
||||||
|
|
||||||
waiting_for_auth_ = false;
|
waiting_for_auth_ = false;
|
||||||
@ -90,10 +72,7 @@ void LastFMSettingsPage::AuthenticationComplete(bool success,
|
|||||||
// Save settings
|
// Save settings
|
||||||
Save();
|
Save();
|
||||||
} else {
|
} else {
|
||||||
QString dialog_text = tr("Your Last.fm credentials were incorrect");
|
QString dialog_text = tr("Failed to login to last.fm. Please try again.");
|
||||||
if (!message.isEmpty()) {
|
|
||||||
dialog_text = message;
|
|
||||||
}
|
|
||||||
QMessageBox::warning(this, tr("Authentication failed"), dialog_text);
|
QMessageBox::warning(this, tr("Authentication failed"), dialog_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,7 @@ class LastFMSettingsPage : public SettingsPage {
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void Login();
|
void Login();
|
||||||
void TokenReceived(bool success, const QString& token);
|
void AuthenticationComplete(bool success);
|
||||||
void AuthenticationComplete(bool success, const QString& error_message);
|
|
||||||
void Logout();
|
void Logout();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user