From 4cbad8929dcb204f5c43809119bcb728bfc63b74 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sat, 26 Dec 2009 17:19:14 +0000 Subject: [PATCH] Last.fm authentication --- data/data.qrc | 1 + data/spinner.gif | Bin 0 -> 673 bytes src/busyindicator.cpp | 22 ++++++ src/busyindicator.h | 23 ++++++ src/lastfmconfig.cpp | 43 +++++++++++ src/lastfmconfig.h | 26 +++++++ src/lastfmconfig.ui | 173 ++++++++++++++++++++++++++++++++++++++++++ src/lastfmservice.cpp | 70 +++++++++++++++++ src/lastfmservice.h | 18 +++++ src/radioservice.h | 1 + src/simpletreemodel.h | 3 +- src/src.pro | 14 +++- 12 files changed, 389 insertions(+), 5 deletions(-) create mode 100644 data/spinner.gif create mode 100644 src/busyindicator.cpp create mode 100644 src/busyindicator.h create mode 100644 src/lastfmconfig.cpp create mode 100644 src/lastfmconfig.h create mode 100644 src/lastfmconfig.ui diff --git a/data/data.qrc b/data/data.qrc index 6ca9a0efe..15217e8a9 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -38,5 +38,6 @@ last.fm/neighbour_radio.png last.fm/personal_radio.png last.fm/recommended_radio.png + spinner.gif diff --git a/data/spinner.gif b/data/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..d0bce1542342e912da81a2c260562df172f30d73 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nnmm28Kh24mmkF0U1e2Nli^nlO|14{Lk&@8WQa67~pE8 zXTZz|lvDgC+Z`3#dv5h=E26FfcG1 zbL_hF&)}42ws10s6^G;;cE1^EoUR)U5A70}d2pLv!jVIT7j&Z~EblI3x0K*v_sV|m z0kj3v921Z^em#l`(k(o@H$3ZdDRc@9NidXDNbqrumReCGv$gd8+e8WW28HVqkJ_9i zH>s*<31KtHjANIPvi2#*6BEu%3Dak5O_t&NBI)H?V$TxT}#l{vOTn5naXTfF^&~Hhq+NX@#Ccc>y7T?;vjI&jdhsDsPJyAw*m0Qz>i}K7# zL9w50Ng{fT}A5JUe8lRK1h7_Y2;BWJDd=c6f&i?Wv5(5q?6|P zQw{>maxZP<537OA37Uk}7@%_$4o$EWe_Zl>&#id|lE-BpDC#+Fn|msJ%_2h{Hg1vP z#N8WAzfWasG}yq|xqE)DrWaOofX=z|?*pgc%{ig5vl!pqDlC|q&~Z0$&Rvsft&VO- z4MZj+%-+Vx%W}v;V76hyp=;+R;x+~t^Q%*xuFTQAF2})fSfTHDAs>sO!OBw`)&)o$ c0!CNZt))x~rAZP^^P&YOFfdqy5)K#u0POD40{{R3 literal 0 HcmV?d00001 diff --git a/src/busyindicator.cpp b/src/busyindicator.cpp new file mode 100644 index 000000000..e4f7a5d6e --- /dev/null +++ b/src/busyindicator.cpp @@ -0,0 +1,22 @@ +#include "busyindicator.h" + +#include + +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(); +} diff --git a/src/busyindicator.h b/src/busyindicator.h new file mode 100644 index 000000000..a65ebb87c --- /dev/null +++ b/src/busyindicator.h @@ -0,0 +1,23 @@ +#ifndef BUSYINDICATOR_H +#define BUSYINDICATOR_H + +#include + +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 diff --git a/src/lastfmconfig.cpp b/src/lastfmconfig.cpp new file mode 100644 index 000000000..a05f080a2 --- /dev/null +++ b/src/lastfmconfig.cpp @@ -0,0 +1,43 @@ +#include "lastfmconfig.h" +#include "lastfmservice.h" + +#include + +#include + +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"); + } +} diff --git a/src/lastfmconfig.h b/src/lastfmconfig.h new file mode 100644 index 000000000..e0659bdff --- /dev/null +++ b/src/lastfmconfig.h @@ -0,0 +1,26 @@ +#ifndef LASTFMCONFIG_H +#define LASTFMCONFIG_H + +#include + +#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 diff --git a/src/lastfmconfig.ui b/src/lastfmconfig.ui new file mode 100644 index 000000000..2dfe53ff7 --- /dev/null +++ b/src/lastfmconfig.ui @@ -0,0 +1,173 @@ + + + LastFMConfig + + + + 0 + 0 + 385 + 255 + + + + Last.fm + + + + + + Enter your Last.fm details below: + + + + + + + + + Last.fm username + + + + + + + + + + Last.fm password + + + + + + + QLineEdit::Password + + + + + + + Scrobble tracks that I listen to + + + + + + + + + Qt::Horizontal + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Note that you must be a <span style=" font-weight:600;">paid subscriber</span> to listen to Last.fm radio from within Tangerine.</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Authenticating... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + BusyIndicator + QLabel +
busyindicator.h
+
+
+ + + + button_box + accepted() + LastFMConfig + accept() + + + 248 + 254 + + + 157 + 274 + + + + + button_box + rejected() + LastFMConfig + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/lastfmservice.cpp b/src/lastfmservice.cpp index 7c686eb62..98c2e0f0e 100644 --- a/src/lastfmservice.cpp +++ b/src/lastfmservice.cpp @@ -1,9 +1,31 @@ #include "lastfmservice.h" +#include "lastfmconfig.h" #include "radioitem.h" +#include +#include +#include + +#include + +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 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(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); +} diff --git a/src/lastfmservice.h b/src/lastfmservice.h index c1e73a2b0..91535df7b 100644 --- a/src/lastfmservice.h +++ b/src/lastfmservice.h @@ -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 diff --git a/src/radioservice.h b/src/radioservice.h index 108864d8c..ff36ec1ce 100644 --- a/src/radioservice.h +++ b/src/radioservice.h @@ -10,6 +10,7 @@ class RadioService : public QObject { public: RadioService(const QString& name, QObject* parent = 0); + virtual ~RadioService() {} QString name() const { return name_; } diff --git a/src/simpletreemodel.h b/src/simpletreemodel.h index 22cd601e7..54abe8850 100644 --- a/src/simpletreemodel.h +++ b/src/simpletreemodel.h @@ -72,7 +72,8 @@ QModelIndex SimpleTreeModel::parent(const QModelIndex& index) const { template int SimpleTreeModel::rowCount(const QModelIndex & parent) const { T* item = IndexToItem(parent); - const_cast*>(this)->LazyPopulate(item); // Ahem + if (!item->lazy_loaded) + const_cast*>(this)->LazyPopulate(item); // Ahem return item->children.count(); } diff --git a/src/src.pro b/src/src.pro index 42f00900d..1c9895cee 100644 --- a/src/src.pro +++ b/src/src.pro @@ -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)