Add settings page + login method for GrooveShark

This commit is contained in:
Arnaud Bienner 2011-09-13 22:32:10 +02:00
parent b8d5068624
commit bc3f56ae0e
5 changed files with 182 additions and 28 deletions

View File

@ -129,6 +129,7 @@ set(SOURCES
internet/digitallyimportedurlhandler.cpp
internet/groovesharksearchplaylisttype.cpp
internet/groovesharkservice.cpp
internet/groovesharksettingspage.cpp
internet/icecastbackend.cpp
internet/icecastfilterwidget.cpp
internet/icecastmodel.cpp
@ -367,6 +368,7 @@ set(HEADERS
internet/digitallyimportedsettingspage.h
internet/groovesharksearchplaylisttype.h
internet/groovesharkservice.h
internet/groovesharksettingspage.h
internet/icecastbackend.h
internet/icecastfilterwidget.h
internet/icecastmodel.h
@ -530,6 +532,7 @@ set(UI
globalsearch/globalsearchwidget.ui
internet/digitallyimportedsettingspage.ui
internet/groovesharksettingspage.ui
internet/icecastfilterwidget.ui
internet/internetviewcontainer.ui
internet/magnatunedownloaddialog.ui

View File

@ -17,6 +17,7 @@
#include <QMenu>
#include <QMessageBox>
#include <QNetworkReply>
#include <QNetworkRequest>
@ -51,6 +52,7 @@ const char* GrooveSharkService::kApiKey = "clementineplayer";
const char* GrooveSharkService::kApiSecret = "MWVlNmU1N2IzNGY3MjA1ZTg1OWJkMTllNjk4YzEzZjY";
const char* GrooveSharkService::kServiceName = "GrooveShark";
const char* GrooveSharkService::kSettingsGroup = "GrooveShark";
const char* GrooveSharkService::kUrl = "http://api.grooveshark.com/ws/3.0/";
const int GrooveSharkService::kSongSearchLimit = 50;
@ -64,9 +66,17 @@ GrooveSharkService::GrooveSharkService(InternetModel *parent)
search_(NULL),
network_(new NetworkAccessManager(this)),
context_menu_(NULL),
api_key_(QByteArray::fromBase64(kApiSecret)) {
api_key_(QByteArray::fromBase64(kApiSecret)),
login_state_(LoginState_OtherError) {
model()->player()->playlists()->RegisterSpecialPlaylistType(new GrooveSharkSearchPlaylistType(this));
// Get already existing (authenticated) session id, if any
QSettings s;
s.beginGroup(GrooveSharkService::kSettingsGroup);
session_id_ = s.value("sessionid").toString();
username_ = s.value("username").toString();
}
@ -89,6 +99,9 @@ QStandardItem* GrooveSharkService::CreateRootItem() {
}
void GrooveSharkService::LazyPopulate(QStandardItem* item) {
if (session_id_.isEmpty()) {
emit OpenSettingsAtPage(SettingsDialog::Page_GrooveShark);
}
switch (item->data(InternetModel::Role_Type).toInt()) {
case InternetModel::Type_Service: {
EnsureConnected();
@ -113,18 +126,6 @@ void GrooveSharkService::Search(const QString& text, Playlist* playlist, bool no
connect(reply, SIGNAL(finished()), SLOT(SearchSongsFinished()));
}
void GrooveSharkService::ShowContextMenu(const QModelIndex& index, const QPoint& global_pos) {
EnsureMenuCreated();
context_menu_->popup(global_pos);
}
QModelIndex GrooveSharkService::GetCurrentIndex() {
return context_item_;
}
void GrooveSharkService::UpdateTotalSongCount(int count) {
}
void GrooveSharkService::SearchSongsFinished() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply)
@ -132,14 +133,8 @@ void GrooveSharkService::SearchSongsFinished() {
reply->deleteLater();
QJson::Parser parser;
bool ok;
QVariantMap result = parser.parse(reply, &ok).toMap();
if (!ok) {
qLog(Error) << "Error while parsing GrooveShark result";
}
qLog(Debug) << result;
QVariantList result_songs = result["result"].toMap()["songs"].toList();
QVariantMap result = ExtractResult(reply);
QVariantList result_songs = result["songs"].toList();
SongList songs;
for (int i=0; i<result_songs.size(); ++i) {
QVariantMap result_song = result_songs[i].toMap();
@ -161,6 +156,102 @@ void GrooveSharkService::SearchSongsFinished() {
pending_search_playlist_->InsertSongs(songs);
}
void GrooveSharkService::Login(const QString& username, const QString& password) {
// To login, we first need to create a session. Next, we will authenticate
// this session using the user's username and password (for now, we just keep
// them in mind)
username_ = username;
password_ = QCryptographicHash::hash(password.toLocal8Bit(), QCryptographicHash::Md5).toHex();
QList<Param> parameters;
QNetworkReply *reply;
reply = CreateRequest("startSession", parameters, false, true);
connect(reply, SIGNAL(finished()), SLOT(SessionCreated()));
}
void GrooveSharkService::SessionCreated() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply)
return;
reply->deleteLater();
QVariantMap result = ExtractResult(reply);
if (!result["success"].toBool()) {
qLog(Error) << "GrooveShark returned an error during session creation";
}
session_id_ = result["sessionID"].toString();
qLog(Debug) << "Session ID returned: " << session_id_;
AuthenticateSession();
}
void GrooveSharkService::AuthenticateSession() {
QList<Param> parameters;
QNetworkReply *reply;
parameters << Param("login", username_)
<< Param("password", password_);
reply = CreateRequest("authenticate", parameters, true, true);
connect(reply, SIGNAL(finished()), SLOT(Authenticated()));
}
void GrooveSharkService::Authenticated() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply)
return;
reply->deleteLater();
QVariantMap result = ExtractResult(reply);
// Check if the user has been authenticated correctly
if (!result["success"].toBool()) {
QString error;
if (result["UserID"].toInt() == 0) {
error = tr("Invalid username and/or password");
login_state_ = LoginState_AuthFailed;
} else if(!result["IsAnywhere"].toBool() || !result["IsPremium"].toBool()) {
error = tr("It seems user %1 doesn't have a GrooveShark Anywhere account").arg(username_);
login_state_ = LoginState_NoPremium;
}
QMessageBox::warning(NULL, tr("GrooveShark login error"), error, QMessageBox::Close);
ResetSessionId();
emit LoginFinished(false);
return;
}
login_state_ = LoginState_LoggedIn;
user_id_ = result["UserID"].toString();
emit LoginFinished(true);
}
void GrooveSharkService::Logout() {
ResetSessionId();
}
void GrooveSharkService::ResetSessionId() {
QSettings s;
s.beginGroup(GrooveSharkService::kSettingsGroup);
session_id_.clear();
s.setValue("sessionid", session_id_);
}
void GrooveSharkService::ShowContextMenu(const QModelIndex& index, const QPoint& global_pos) {
EnsureMenuCreated();
context_menu_->popup(global_pos);
}
QModelIndex GrooveSharkService::GetCurrentIndex() {
return context_item_;
}
void GrooveSharkService::UpdateTotalSongCount(int count) {
}
void GrooveSharkService::EnsureMenuCreated() {
if(!context_menu_) {
context_menu_ = new QMenu;
@ -186,15 +277,23 @@ void GrooveSharkService::ItemDoubleClicked(QStandardItem* item) {
}
}
QNetworkReply *GrooveSharkService::CreateRequest(const QString& method_name, QList<Param> params,
bool need_authentication) {
QNetworkReply* GrooveSharkService::CreateRequest(const QString& method_name, QList<Param> params,
bool need_authentication,
bool use_https) {
QVariantMap request_params;
request_params.insert("method", method_name);
QVariantMap wsKey;
wsKey.insert("wsKey", kApiKey);
request_params.insert("header", wsKey);
QVariantMap header;
header.insert("wsKey", kApiKey);
if (need_authentication) {
if (session_id_.isEmpty()) {
qLog(Warning) << "Session ID is empty: will not be added to query";
} else {
header.insert("sessionID", session_id_);
}
}
request_params.insert("header", header);
QVariantMap parameters;
foreach(const Param& param, params) {
@ -208,9 +307,23 @@ QNetworkReply *GrooveSharkService::CreateRequest(const QString& method_name, QLi
qLog(Debug) << post_params;
QUrl url(kUrl);
if (use_https) {
url.setScheme("https");
}
url.setQueryItems( QList<Param>() << Param("sig", Utilities::HmacMd5(api_key_, post_params).toHex()));
QNetworkRequest req(url);
QNetworkReply *reply = network_->post(req, post_params);
return reply;
}
QVariantMap GrooveSharkService::ExtractResult(QNetworkReply* reply) {
QJson::Parser parser;
bool ok;
QVariantMap result = parser.parse(reply, &ok).toMap();
if (!ok) {
qLog(Error) << "Error while parsing GrooveShark result";
}
qLog(Debug) << result;
return result["result"].toMap();
}

View File

@ -36,6 +36,15 @@ class GrooveSharkService : public InternetService {
enum Type {
Type_SearchResults = InternetModel::TypeCount
};
// Values are persisted - don't change.
enum LoginState {
LoginState_LoggedIn = 1,
LoginState_AuthFailed = 2,
LoginState_NoPremium = 3,
LoginState_OtherError = 4
};
// Internet Service methods
QStandardItem* CreateRootItem();
void LazyPopulate(QStandardItem *parent);
@ -44,8 +53,17 @@ class GrooveSharkService : public InternetService {
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
void Search(const QString& text, Playlist* playlist, bool now = false);
void Login(const QString& username, const QString& password);
void Logout();
bool IsLoggedIn() { return !session_id_.isEmpty(); }
// Persisted in the settings and updated on each Login().
LoginState login_state() const { return login_state_; }
const QString& session_id() { return session_id_; }
static const char* kServiceName;
static const char* kSettingsGroup;
static const char* kUrl;
static const int kSongSearchLimit;
@ -53,25 +71,37 @@ class GrooveSharkService : public InternetService {
static const char* kApiKey;
static const char* kApiSecret;
signals:
void LoginFinished(bool success);
protected:
QModelIndex GetCurrentIndex();
private slots:
void UpdateTotalSongCount(int count);
void SessionCreated();
void SearchSongsFinished();
void Authenticated();
private:
void EnsureMenuCreated();
void EnsureConnected();
void OpenSearchTab();
void AuthenticateSession();
// Create a request for the given method, with the given params.
// If need_authentication is true, add session_id to params.
// Returns the reply object created
QNetworkReply *CreateRequest(const QString& method_name, const QList<QPair<QString, QString> > params,
bool need_authentication = false);
QNetworkReply* CreateRequest(const QString& method_name, const QList<QPair<QString, QString> > params,
bool need_authentication = false,
bool use_https = false);
// Convenient function for extracting result from reply, checking resulst's
// validity, and deleting reply object
QVariantMap ExtractResult(QNetworkReply* reply);
void ResetSessionId();
Playlist* pending_search_playlist_;
@ -83,9 +113,13 @@ class GrooveSharkService : public InternetService {
QMenu* context_menu_;
QModelIndex context_item_;
QString username_;
QString password_; // In fact, password's md5 hash
QString user_id_;
QString session_id_;
QByteArray api_key_;
LoginState login_state_;
};

View File

@ -31,6 +31,7 @@
#include "engines/enginebase.h"
#include "engines/gstengine.h"
#include "internet/digitallyimportedsettingspage.h"
#include "internet/groovesharksettingspage.h"
#include "internet/magnatunesettingspage.h"
#include "library/librarysettingspage.h"
#include "playlist/playlistview.h"
@ -84,6 +85,8 @@ SettingsDialog::SettingsDialog(BackgroundStreams* streams, QWidget* parent)
AddPage(Page_Lastfm, new LastFMSettingsPage(this));
#endif
AddPage(Page_GrooveShark, new GrooveSharkSettingsPage(this));
#ifdef HAVE_SPOTIFY
AddPage(Page_Spotify, new SpotifySettingsPage(this));
#endif

View File

@ -49,6 +49,7 @@ public:
Page_Notifications,
Page_Library,
Page_Lastfm,
Page_GrooveShark,
Page_Spotify,
Page_Magnatune,
Page_DigitallyImported,