Permalink
Browse files

Use desktopauth instead of mobileauth for last.fm

It now uses an oauth like authentication process so the user isn't required to enter the last.fm password in Clementine but rather authenticate through the last.fm website.

Updates #5028
  • Loading branch information...
amuttsch committed Feb 3, 2016
1 parent ac0aff5 commit 6c726e3e382bdd28fd00a5e68725980ef0da91e1
@@ -74,6 +74,7 @@ const char* LastFMService::kSettingsGroup = "Last.fm";
const char* LastFMService::kAudioscrobblerClientId = "tng";
const char* LastFMService::kApiKey = "75d20fb472be99275392aefa2760ea09";
const char* LastFMService::kSecret = "d3072b60ae626be12be69448f5c46e70";
const char* LastFMService::kAuthLoginUrl = "http://www.last.fm/api/auth/?api_key=%1&token=%2";

LastFMService::LastFMService(Application* app, QObject* parent)
: Scrobbler(parent),
@@ -82,6 +83,8 @@ LastFMService::LastFMService(Application* app, QObject* parent)
scrobbling_enabled_(false),
connection_problems_(false),
app_(app) {
lastfm::ws::setScheme(lastfm::ws::Https);

ReloadSettings();

// we emit the signal the first time to be sure the buttons are in the right
@@ -127,29 +130,37 @@ bool LastFMService::IsSubscriber() const {
return settings.value("Subscriber", false).toBool();
}

void LastFMService::Authenticate(const QString& username,
const QString& password) {
void LastFMService::GetToken() {
QMap<QString, QString> params;
params["method"] = "auth.getMobileSession";
params["username"] = username;
params["authToken"] =
lastfm::md5((username + lastfm::md5(password.toUtf8())).toUtf8());

params["method"] = "auth.getToken";
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
SLOT(GetTokenReplyFinished(QNetworkReply*)), reply);
}

void LastFMService::SignOut() {
lastfm::ws::Username.clear();
lastfm::ws::SessionKey.clear();
void LastFMService::GetTokenReplyFinished(QNetworkReply* reply) {
reply->deleteLater();

QSettings settings;
settings.beginGroup(kSettingsGroup);
// Parse the reply
lastfm::XmlQuery lfm(lastfm::compat::EmptyXmlQuery());
if (lastfm::compat::ParseQuery(reply->readAll(), &lfm)) {
QString token = lfm["token"].text();

settings.setValue("Username", QString());
settings.setValue("Session", QString());
emit TokenReceived(true, token);
} else {
emit TokenReceived(false, lfm["error"].text().trimmed());
}
}

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,6 +192,17 @@ void LastFMService::AuthenticateReplyFinished(QNetworkReply* reply) {
emit AuthenticationComplete(true, QString());
}

void LastFMService::SignOut() {
lastfm::ws::Username.clear();
lastfm::ws::SessionKey.clear();

QSettings settings;
settings.beginGroup(kSettingsGroup);

settings.setValue("Username", QString());
settings.setValue("Session", QString());
}

void LastFMService::UpdateSubscriberStatus() {
QMap<QString, QString> params;
params["method"] = "user.getInfo";
@@ -54,6 +54,7 @@ class LastFMService : public Scrobbler {
static const char* kAudioscrobblerClientId;
static const char* kApiKey;
static const char* kSecret;
static const char* kAuthLoginUrl;

void ReloadSettings();

@@ -68,7 +69,8 @@ class LastFMService : public Scrobbler {
bool PreferAlbumArtist() const { return prefer_albumartist_; }
bool HasConnectionProblems() const { return connection_problems_; }

void Authenticate(const QString& username, const QString& password);
void GetToken();
void Authenticate(const QString& token);
void SignOut();
void UpdateSubscriberStatus();

@@ -81,6 +83,7 @@ class LastFMService : public Scrobbler {
void ToggleScrobbling();

signals:
void TokenReceived(bool success, const QString& token);
void AuthenticationComplete(bool success, const QString& error_message);
void ScrobblingEnabledChanged(bool value);
void ButtonVisibilityChanged(bool value);
@@ -94,6 +97,7 @@ class LastFMService : public Scrobbler {
void SavedItemsChanged();

private slots:
void GetTokenReplyFinished(QNetworkReply* reply);
void AuthenticateReplyFinished(QNetworkReply* reply);
void UpdateSubscriberStatusFinished(QNetworkReply* reply);

@@ -24,6 +24,7 @@

#include <lastfm/ws.h>

#include <QDesktopServices>
#include <QMessageBox>
#include <QSettings>

@@ -42,17 +43,16 @@ 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(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login()));
connect(ui_->login, SIGNAL(clicked()), SLOT(Login()));

ui_->login_state->AddCredentialField(ui_->username);
ui_->login_state->AddCredentialField(ui_->password);
ui_->login_state->AddCredentialGroup(ui_->groupBox);
ui_->login_state->AddCredentialGroup(ui_->login_container);

ui_->username->setMinimumWidth(QFontMetrics(QFont()).width("WWWWWWWWWWWW"));
resize(sizeHint());
}

@@ -62,7 +62,22 @@ void LastFMSettingsPage::Login() {
waiting_for_auth_ = true;

ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
service_->Authenticate(ui_->username->text(), ui_->password->text());
service_->GetToken();
}

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,
@@ -72,8 +87,6 @@ void LastFMSettingsPage::AuthenticationComplete(bool success,
waiting_for_auth_ = false;

if (success) {
// Clear password just to be sure
ui_->password->clear();
// Save settings
Save();
} else {
@@ -109,8 +122,6 @@ void LastFMSettingsPage::Save() {
}

void LastFMSettingsPage::Logout() {
ui_->username->clear();
ui_->password->clear();
RefreshControls(false);

service_->SignOut();
@@ -38,6 +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 Logout();

@@ -18,46 +18,48 @@
<widget class="LoginStateWidget" name="login_state" native="true"/>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Account details</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
<widget class="QWidget" name="login_container" native="true">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>28</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Last.fm username</string>
</property>
</widget>
</item>
<item row="1" column="1">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="username"/>
</item>
<item>
<widget class="QPushButton" name="login">
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Last.fm password</string>
<string>Clicking the Login button will open a web browser. You should return to Clementine after you have logged in.</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
@@ -112,6 +114,9 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
@@ -131,9 +136,6 @@
</customwidget>
</customwidgets>
<tabstops>
<tabstop>username</tabstop>
<tabstop>password</tabstop>
<tabstop>login</tabstop>
<tabstop>scrobble</tabstop>
<tabstop>love_ban_</tabstop>
<tabstop>scrobble_button</tabstop>

1 comment on commit 6c726e3

@amuttsch

This comment has been minimized.

Copy link
Member

amuttsch commented on 6c726e3 Feb 3, 2016

@davidsansome @hatstand : Should this be cherry picked to the release-1.3 branch?

Please sign in to comment.