Last.fm authentication

This commit is contained in:
David Sansome 2009-12-26 17:19:14 +00:00
parent bd30d815d1
commit 4cbad8929d
12 changed files with 389 additions and 5 deletions

View File

@ -38,5 +38,6 @@
<file>last.fm/neighbour_radio.png</file>
<file>last.fm/personal_radio.png</file>
<file>last.fm/recommended_radio.png</file>
<file>spinner.gif</file>
</qresource>
</RCC>

BIN
data/spinner.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

22
src/busyindicator.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "busyindicator.h"
#include <QMovie>
BusyIndicator::BusyIndicator(QWidget* parent)
: QLabel(parent),
movie_(new QMovie(":spinner.gif"))
{
setMovie(movie_);
}
BusyIndicator::~BusyIndicator() {
delete movie_;
}
void BusyIndicator::showEvent(QShowEvent*) {
movie_->start();
}
void BusyIndicator::hideEvent(QHideEvent*) {
movie_->stop();
}

23
src/busyindicator.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef BUSYINDICATOR_H
#define BUSYINDICATOR_H
#include <QLabel>
class QMovie;
class BusyIndicator : public QLabel {
Q_OBJECT
public:
BusyIndicator(QWidget* parent = 0);
~BusyIndicator();
protected:
void showEvent(QShowEvent* event);
void hideEvent(QHideEvent* event);
private:
QMovie* movie_;
};
#endif // BUSYINDICATOR_H

43
src/lastfmconfig.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "lastfmconfig.h"
#include "lastfmservice.h"
#include <lastfm/ws.h>
#include <QMessageBox>
LastFMConfig::LastFMConfig(LastFMService* service, QWidget *parent)
: QDialog(parent),
service_(service)
{
ui_.setupUi(this);
ui_.username->setText(lastfm::ws::Username);
ui_.busy->hide();
connect(service_, SIGNAL(AuthenticationComplete(bool)), SLOT(AuthenticationComplete(bool)));
}
void LastFMConfig::accept() {
if (ui_.username->text().isEmpty() || ui_.password->text().isEmpty()) {
QDialog::accept();
return;
}
ui_.busy->show();
ui_.button_box->setEnabled(false);
service_->Authenticate(ui_.username->text(), ui_.password->text());
}
void LastFMConfig::AuthenticationComplete(bool success) {
ui_.busy->hide();
ui_.button_box->setEnabled(true);
if (success) {
ui_.username->setText(lastfm::ws::Username);
ui_.password->clear();
QDialog::accept();
} else {
QMessageBox::warning(this, "Authentication failed", "Your Last.fm credentials were incorrect");
}
}

26
src/lastfmconfig.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef LASTFMCONFIG_H
#define LASTFMCONFIG_H
#include <QDialog>
#include "ui_lastfmconfig.h"
class LastFMService;
class LastFMConfig : public QDialog {
Q_OBJECT
public:
LastFMConfig(LastFMService* service, QWidget* parent = 0);
void accept();
private slots:
void AuthenticationComplete(bool success);
private:
Ui::LastFMConfig ui_;
LastFMService* service_;
};
#endif // LASTFMCONFIG_H

173
src/lastfmconfig.ui Normal file
View File

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LastFMConfig</class>
<widget class="QDialog" name="LastFMConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>385</width>
<height>255</height>
</rect>
</property>
<property name="windowTitle">
<string>Last.fm</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Enter your Last.fm details below:</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Last.fm username</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="username"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Last.fm password</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="scrobble">
<property name="text">
<string>Scrobble tracks that I listen to</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Note that you must be a &lt;span style=&quot; font-weight:600;&quot;&gt;paid subscriber&lt;/span&gt; to listen to Last.fm radio from within Tangerine.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="busy" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Authenticating...</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="BusyIndicator" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="button_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>BusyIndicator</class>
<extends>QLabel</extends>
<header>busyindicator.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>button_box</sender>
<signal>accepted()</signal>
<receiver>LastFMConfig</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>button_box</sender>
<signal>rejected()</signal>
<receiver>LastFMConfig</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1,9 +1,31 @@
#include "lastfmservice.h"
#include "lastfmconfig.h"
#include "radioitem.h"
#include <lastfm/ws.h>
#include <lastfm/misc.h>
#include <lastfm/XmlQuery>
#include <QSettings>
const char* LastFMService::kSettingsGroup = "Last.fm";
LastFMService::LastFMService(QObject* parent)
: RadioService("Last.fm", parent)
{
lastfm::ws::ApiKey = "75d20fb472be99275392aefa2760ea09";
lastfm::ws::SharedSecret = "d3072b60ae626be12be69448f5c46e70";
QSettings settings;
settings.beginGroup(kSettingsGroup);
lastfm::ws::Username = settings.value("user").toString();
lastfm::ws::SessionKey = settings.value("session").toString();
config_ = new LastFMConfig(this);
}
LastFMService::~LastFMService() {
delete config_;
}
RadioItem* LastFMService::CreateRootItem(RadioItem* parent) {
@ -15,6 +37,7 @@ RadioItem* LastFMService::CreateRootItem(RadioItem* parent) {
void LastFMService::LazyPopulate(RadioItem *item) {
switch (item->type) {
case RadioItem::Type_Service:
// Create child items
CreateStationItem(Type_MyRecommendations, "My Recommendations",
":last.fm/recommended_radio.png", item);
CreateStationItem(Type_MyRadio, "My Radio Station",
@ -23,7 +46,16 @@ void LastFMService::LazyPopulate(RadioItem *item) {
":last.fm/loved_radio.png", item);
CreateStationItem(Type_MyNeighbourhood, "My Neighbourhood",
":last.fm/neighbour_radio.png", item);
if (lastfm::ws::SessionKey.isEmpty())
config_->show();
break;
default:
break;
}
item->lazy_loaded = true;
}
RadioItem* LastFMService::CreateStationItem(ItemType type, const QString& name,
@ -34,3 +66,41 @@ RadioItem* LastFMService::CreateStationItem(ItemType type, const QString& name,
return ret;
}
void LastFMService::Authenticate(const QString& username, const QString& password) {
QMap<QString, QString> params;
params["method"] = "auth.getMobileSession";
params["username"] = username;
params["authToken"] = lastfm::md5((username + lastfm::md5(password.toUtf8())).toUtf8());
QNetworkReply* reply = lastfm::ws::post(params);
connect(reply, SIGNAL(finished()), SLOT(AuthenticateReplyFinished()));
}
void LastFMService::AuthenticateReplyFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) {
emit AuthenticationComplete(false);
return;
}
// Parse the reply
try {
lastfm::XmlQuery const lfm = lastfm::ws::parse(reply);
lastfm::ws::Username = lfm["session"]["name"].text();
lastfm::ws::SessionKey = lfm["session"]["key"].text();
} catch (std::runtime_error& e) {
qDebug() << e.what();
emit AuthenticationComplete(false);
return;
}
// Save the session key
QSettings settings;
settings.beginGroup(kSettingsGroup);
settings.setValue("username", lastfm::ws::Username);
settings.setValue("session", lastfm::ws::SessionKey);
emit AuthenticationComplete(true);
}

View File

@ -3,9 +3,16 @@
#include "radioservice.h"
class LastFMConfig;
class LastFMService : public RadioService {
Q_OBJECT
public:
LastFMService(QObject* parent = 0);
~LastFMService();
static const char* kSettingsGroup;
enum ItemType {
Type_MyRecommendations = 1000,
@ -17,9 +24,20 @@ class LastFMService : public RadioService {
RadioItem* CreateRootItem(RadioItem* parent);
void LazyPopulate(RadioItem *item);
void Authenticate(const QString& username, const QString& password);
signals:
void AuthenticationComplete(bool success);
private slots:
void AuthenticateReplyFinished();
private:
RadioItem* CreateStationItem(ItemType type, const QString& name,
const QString& icon, RadioItem* parent);
private:
LastFMConfig* config_;
};
#endif // LASTFMSERVICE_H

View File

@ -10,6 +10,7 @@ class RadioService : public QObject {
public:
RadioService(const QString& name, QObject* parent = 0);
virtual ~RadioService() {}
QString name() const { return name_; }

View File

@ -72,7 +72,8 @@ QModelIndex SimpleTreeModel<T>::parent(const QModelIndex& index) const {
template <typename T>
int SimpleTreeModel<T>::rowCount(const QModelIndex & parent) const {
T* item = IndexToItem(parent);
const_cast<SimpleTreeModel<T>*>(this)->LazyPopulate(item); // Ahem
if (!item->lazy_loaded)
const_cast<SimpleTreeModel<T>*>(this)->LazyPopulate(item); // Ahem
return item->children.count();
}

View File

@ -2,7 +2,7 @@
# Project created by QtCreator 2009-12-15T18:38:35
# -------------------------------------------------
QT += sql \
opengl
network opengl xml
TARGET = tangerine
TEMPLATE = app
SOURCES += main.cpp \
@ -36,7 +36,9 @@ SOURCES += main.cpp \
radioitem.cpp \
radioservice.cpp \
lastfmservice.cpp \
radiomodel.cpp
radiomodel.cpp \
lastfmconfig.cpp \
busyindicator.cpp
HEADERS += mainwindow.h \
player.h \
library.h \
@ -72,13 +74,17 @@ HEADERS += mainwindow.h \
radioservice.h \
lastfmservice.h \
simpletreemodel.h \
radiomodel.h
radiomodel.h \
lastfmconfig.h \
busyindicator.h
FORMS += mainwindow.ui \
libraryconfig.ui \
fileview.ui
fileview.ui \
lastfmconfig.ui
RESOURCES += ../data/data.qrc
OTHER_FILES += ../data/schema.sql \
../data/mainwindow.css
LIBS += -llastfm
!win32 {
QMAKE_CXXFLAGS += $$system(taglib-config --cflags)
LIBS += $$system(taglib-config --libs)