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