From 646588da2c84b63680354e8257996e373dad06ef Mon Sep 17 00:00:00 2001 From: Arnaud Bienner Date: Fri, 2 Sep 2011 00:28:11 +0200 Subject: [PATCH] Start to work on Grooveshark: currently, we can only search for songs (and this has to be improve); lot of work remains to be done... --- .../groovesharksearchplaylisttype.cpp | 46 ++++ src/internet/groovesharksearchplaylisttype.h | 43 ++++ src/internet/groovesharkservice.cpp | 217 ++++++++++++++++++ src/internet/groovesharkservice.h | 92 ++++++++ src/internet/internetmodel.cpp | 2 + 5 files changed, 400 insertions(+) create mode 100644 src/internet/groovesharksearchplaylisttype.cpp create mode 100644 src/internet/groovesharksearchplaylisttype.h create mode 100644 src/internet/groovesharkservice.cpp create mode 100644 src/internet/groovesharkservice.h diff --git a/src/internet/groovesharksearchplaylisttype.cpp b/src/internet/groovesharksearchplaylisttype.cpp new file mode 100644 index 000000000..07b42f2e4 --- /dev/null +++ b/src/internet/groovesharksearchplaylisttype.cpp @@ -0,0 +1,46 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "groovesharksearchplaylisttype.h" +#include "groovesharkservice.h" + +const char* GrooveSharkSearchPlaylistType::kName = "grooveshark-search"; + +GrooveSharkSearchPlaylistType::GrooveSharkSearchPlaylistType(GrooveSharkService* service) + : service_(service) { +} + +QIcon GrooveSharkSearchPlaylistType::icon(Playlist* playlist) const { + return QIcon(":providers/grooveshark.png"); +} + +QString GrooveSharkSearchPlaylistType::search_hint_text(Playlist* playlist) const { + return QObject::tr("Search GrooveShark"); +} + +QString GrooveSharkSearchPlaylistType::empty_playlist_text(Playlist* playlist) const { + return QObject::tr("Start typing in the search box above to find music on GrooveShark."); +} + +bool GrooveSharkSearchPlaylistType::has_special_search_behaviour(Playlist* playlist) const { + return true; +} + +void GrooveSharkSearchPlaylistType::Search(const QString& text, Playlist* playlist) { + service_->Search(text, playlist); +} + diff --git a/src/internet/groovesharksearchplaylisttype.h b/src/internet/groovesharksearchplaylisttype.h new file mode 100644 index 000000000..e96996eea --- /dev/null +++ b/src/internet/groovesharksearchplaylisttype.h @@ -0,0 +1,43 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef GROOVESHARKSEARCHPLAYLISTTYPE_H +#define GROOVESHARKSEARCHPLAYLISTTYPE_H + +#include "playlist/specialplaylisttype.h" + +class GrooveSharkService; + +class GrooveSharkSearchPlaylistType : public SpecialPlaylistType { +public: + GrooveSharkSearchPlaylistType(GrooveSharkService* service); + + static const char* kName; + virtual QString name() const { return kName; } + + virtual QIcon icon(Playlist* playlist) const; + virtual QString search_hint_text(Playlist* playlist) const; + virtual QString empty_playlist_text(Playlist* playlist) const; + + virtual bool has_special_search_behaviour(Playlist* playlist) const; + virtual void Search(const QString& text, Playlist* playlist); + +private: + GrooveSharkService* service_; +}; + +#endif // GROOVESHARKSEARCHPLAYLISTTYPE_H diff --git a/src/internet/groovesharkservice.cpp b/src/internet/groovesharkservice.cpp new file mode 100644 index 000000000..6c15592fc --- /dev/null +++ b/src/internet/groovesharkservice.cpp @@ -0,0 +1,217 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . + */ + + +#include +#include +#include + +#include +#include + +#include "qtiocompressor.h" + +#include "internetmodel.h" +#include "groovesharksearchplaylisttype.h" + +#include "core/database.h" +#include "core/logging.h" +#include "core/mergedproxymodel.h" +#include "core/network.h" +#include "core/player.h" +#include "core/scopedtransaction.h" +#include "core/song.h" +#include "core/taskmanager.h" +#include "core/utilities.h" +#include "playlist/playlist.h" +#include "playlist/playlistcontainer.h" +#include "playlist/playlistmanager.h" +#include "ui/iconloader.h" + +#include "groovesharkservice.h" + +// The GrooveShark terms of service require that application keys are not +// accessible to third parties. Therefore this application key is ofuscated to +// prevent third parties from viewing it. +const char* GrooveSharkService::kApiKey = "clementineplayer"; +const char* GrooveSharkService::kApiSecret = "MWVlNmU1N2IzNGY3MjA1ZTg1OWJkMTllNjk4YzEzZjY"; + +const char* GrooveSharkService::kServiceName = "GrooveShark"; +const char* GrooveSharkService::kUrl = "http://api.grooveshark.com/ws/3.0/"; + +const int GrooveSharkService::kSongSearchLimit = 50; + +typedef QPair Param; + +GrooveSharkService::GrooveSharkService(InternetModel *parent) + : InternetService(kServiceName, parent, parent), + pending_search_playlist_(NULL), + root_(NULL), + search_(NULL), + network_(new NetworkAccessManager(this)), + context_menu_(NULL), + session_id_(NULL), + api_key_(QByteArray::fromBase64(kApiSecret)) { + + model()->player()->playlists()->RegisterSpecialPlaylistType(new GrooveSharkSearchPlaylistType(this)); +} + + +GrooveSharkService::~GrooveSharkService() { +} + +QStandardItem* GrooveSharkService::CreateRootItem() { + root_ = new QStandardItem(QIcon(":providers/grooveshark.png"), kServiceName); + root_->setData(true, InternetModel::Role_CanLazyLoad); + if (!search_) { + search_ = new QStandardItem(IconLoader::Load("edit-find"), + tr("Search GrooveShark (opens a new tab)")); + search_->setData(Type_SearchResults, InternetModel::Role_Type); + search_->setData(InternetModel::PlayBehaviour_DoubleClickAction, + InternetModel::Role_PlayBehaviour); + root_->appendRow(search_); + } + + return root_; +} + +void GrooveSharkService::LazyPopulate(QStandardItem* item) { + switch (item->data(InternetModel::Role_Type).toInt()) { + case InternetModel::Type_Service: { + EnsureConnected(); + break; + } + default: + break; + } +} + +void GrooveSharkService::Search(const QString& text, Playlist* playlist, bool now) { + QList parameters; + QNetworkReply *reply; + + pending_search_playlist_ = playlist; + + parameters << Param("query", text) + << Param("country", "") + << Param("limit", QString("%1").arg(kSongSearchLimit)) + << Param("offset", ""); + reply = CreateRequest("getSongSearchResults", parameters, false); + 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(sender()); + if (!reply) + return; + + 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(); + SongList songs; + for (int i=0; iClear(); + pending_search_playlist_->InsertSongs(songs); +} + +void GrooveSharkService::EnsureMenuCreated() { + if(!context_menu_) { + context_menu_ = new QMenu; + context_menu_->addActions(GetPlaylistActions()); + } +} + +void GrooveSharkService::EnsureConnected() { + if (!session_id_.isEmpty()) { + return; + } + // TODO +} + +void GrooveSharkService::OpenSearchTab() { + model()->player()->playlists()->New(tr("Search GrooveShark"), SongList(), + GrooveSharkSearchPlaylistType::kName); +} + +void GrooveSharkService::ItemDoubleClicked(QStandardItem* item) { + if (item == search_) { + OpenSearchTab(); + } +} + +QNetworkReply *GrooveSharkService::CreateRequest(const QString& method_name, QList params, + bool need_authentication) { + QVariantMap request_params; + + request_params.insert("method", method_name); + + QVariantMap wsKey; + wsKey.insert("wsKey", kApiKey); + request_params.insert("header", wsKey); + + QVariantMap parameters; + foreach(const Param& param, params) { + parameters.insert(param.first, param.second); + } + request_params.insert("parameters", parameters); + + QJson::Serializer serializer; + QByteArray post_params = serializer.serialize(request_params); + + qLog(Debug) << post_params; + + QUrl url(kUrl); + url.setQueryItems( QList() << Param("sig", Utilities::HmacMd5(api_key_, post_params).toHex())); + QNetworkRequest req(url); + QNetworkReply *reply = network_->post(req, post_params); + + return reply; +} diff --git a/src/internet/groovesharkservice.h b/src/internet/groovesharkservice.h new file mode 100644 index 000000000..36026965d --- /dev/null +++ b/src/internet/groovesharkservice.h @@ -0,0 +1,92 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . + */ + +#ifndef GROOVESHARKSERVICE_H +#define GROOVESHARKSERVICE_H + +#include "internetmodel.h" +#include "internetservice.h" + +class NetworkAccessManager; +class Playlist; +class QMenu; +class QSortFilterProxyModel; +class QNetworkRequest; + +class GrooveSharkService : public InternetService { + Q_OBJECT + public: + GrooveSharkService(InternetModel *parent); + ~GrooveSharkService(); + + enum Type { + Type_SearchResults = InternetModel::TypeCount + }; + // Internet Service methods + QStandardItem* CreateRootItem(); + void LazyPopulate(QStandardItem *parent); + + void ItemDoubleClicked(QStandardItem* item); + void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos); + + void Search(const QString& text, Playlist* playlist, bool now = false); + + static const char* kServiceName; + static const char* kUrl; + + static const int kSongSearchLimit; + + static const char* kApiKey; + static const char* kApiSecret; + + protected: + QModelIndex GetCurrentIndex(); + + private slots: + void UpdateTotalSongCount(int count); + + void SearchSongsFinished(); + + private: + void EnsureMenuCreated(); + void EnsureConnected(); + + void OpenSearchTab(); + + // 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 > params, + bool need_authentication = false); + + Playlist* pending_search_playlist_; + + QStandardItem* root_; + QStandardItem* search_; + + NetworkAccessManager* network_; + + QMenu* context_menu_; + QModelIndex context_item_; + + QString session_id_; + QByteArray api_key_; + +}; + + +#endif // GROOVESHARKSERVICE_H diff --git a/src/internet/internetmodel.cpp b/src/internet/internetmodel.cpp index fc58912db..14556b515 100644 --- a/src/internet/internetmodel.cpp +++ b/src/internet/internetmodel.cpp @@ -25,6 +25,7 @@ #include "savedradio.h" #include "skyfmservice.h" #include "somafmservice.h" +#include "groovesharkservice.h" #include "core/logging.h" #include "core/mergedproxymodel.h" @@ -70,6 +71,7 @@ InternetModel::InternetModel(BackgroundThread* db_thread, #ifdef HAVE_SPOTIFY AddService(new SpotifyService(this)); #endif + AddService(new GrooveSharkService(this)); } void InternetModel::AddService(InternetService *service) {