Add settings page + login method for GrooveShark
This commit is contained in:
parent
b8d5068624
commit
bc3f56ae0e
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
Page_Notifications,
|
||||
Page_Library,
|
||||
Page_Lastfm,
|
||||
Page_GrooveShark,
|
||||
Page_Spotify,
|
||||
Page_Magnatune,
|
||||
Page_DigitallyImported,
|
||||
|
Loading…
x
Reference in New Issue
Block a user