Add the ability to play streaming songs from GrooveShark

This commit is contained in:
Arnaud Bienner 2011-09-20 00:26:24 +02:00
parent d380c2e49d
commit 5dfa4f4838
5 changed files with 141 additions and 6 deletions

View File

@ -130,6 +130,7 @@ set(SOURCES
internet/groovesharksearchplaylisttype.cpp
internet/groovesharkservice.cpp
internet/groovesharksettingspage.cpp
internet/groovesharkurlhandler.cpp
internet/icecastbackend.cpp
internet/icecastfilterwidget.cpp
internet/icecastmodel.cpp
@ -369,6 +370,7 @@ set(HEADERS
internet/groovesharksearchplaylisttype.h
internet/groovesharkservice.h
internet/groovesharksettingspage.h
internet/groovesharkurlhandler.h
internet/icecastbackend.h
internet/icecastfilterwidget.h
internet/icecastmodel.h

View File

@ -20,6 +20,7 @@
#include <QMessageBox>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QTimer>
#include <qjson/parser.h>
#include <qjson/serializer.h>
@ -28,6 +29,7 @@
#include "internetmodel.h"
#include "groovesharksearchplaylisttype.h"
#include "groovesharkurlhandler.h"
#include "core/database.h"
#include "core/logging.h"
@ -57,10 +59,11 @@ const char* GrooveSharkService::kUrl = "http://api.grooveshark.com/ws/3.0/";
const int GrooveSharkService::kSongSearchLimit = 50;
typedef QPair<QString, QString> Param;
typedef QPair<QString, QVariant> Param;
GrooveSharkService::GrooveSharkService(InternetModel *parent)
: InternetService(kServiceName, parent, parent),
url_handler_(new GrooveSharkUrlHandler(this, this)),
pending_search_playlist_(NULL),
root_(NULL),
search_(NULL),
@ -69,6 +72,7 @@ GrooveSharkService::GrooveSharkService(InternetModel *parent)
api_key_(QByteArray::fromBase64(kApiSecret)),
login_state_(LoginState_OtherError) {
model()->player()->RegisterUrlHandler(url_handler_);
model()->player()->playlists()->RegisterSpecialPlaylistType(new GrooveSharkSearchPlaylistType(this));
// Get already existing (authenticated) session id, if any
@ -156,6 +160,57 @@ void GrooveSharkService::SearchSongsFinished() {
pending_search_playlist_->InsertSongs(songs);
}
void GrooveSharkService::InitCountry() {
if (!country_.isEmpty())
return;
// Get country info
QNetworkReply *reply_country;
reply_country = CreateRequest("getCountry", QList<Param>(), true);
// Wait for the reply
{
QEventLoop event_loop;
QTimer timeout_timer;
connect(&timeout_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
connect(reply_country, SIGNAL(finished()), &event_loop, SLOT(quit()));
timeout_timer.start(3000);
event_loop.exec();
if (!timeout_timer.isActive()) {
qLog(Error) << "GrooveShark request timeout";
return;
}
timeout_timer.stop();
}
country_ = ExtractResult(reply_country);
}
QUrl GrooveSharkService::GetStreamingUrlFromSongId(const QString& song_id) {
QList<Param> parameters;
QNetworkReply *reply;
InitCountry();
parameters << Param("songID", song_id)
<< Param("country", country_);
reply = CreateRequest("getSubscriberStreamKey", parameters, true);
// Wait for the reply
{
QEventLoop event_loop;
QTimer timeout_timer;
connect(&timeout_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
connect(reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
timeout_timer.start(3000);
event_loop.exec();
if (!timeout_timer.isActive()) {
qLog(Error) << "GrooveShark request timeout";
return QUrl();
}
timeout_timer.stop();
}
QVariantMap result = ExtractResult(reply);
return QUrl(result["url"].toString());
}
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
@ -310,7 +365,7 @@ QNetworkReply* GrooveSharkService::CreateRequest(const QString& method_name, QLi
if (use_https) {
url.setScheme("https");
}
url.setQueryItems( QList<Param>() << Param("sig", Utilities::HmacMd5(api_key_, post_params).toHex()));
url.setQueryItems( QList<QPair<QString, QString> >() << QPair<QString, QString>("sig", Utilities::HmacMd5(api_key_, post_params).toHex()));
QNetworkRequest req(url);
QNetworkReply *reply = network_->post(req, post_params);

View File

@ -21,6 +21,7 @@
#include "internetmodel.h"
#include "internetservice.h"
class GrooveSharkUrlHandler;
class NetworkAccessManager;
class Playlist;
class QMenu;
@ -53,12 +54,13 @@ class GrooveSharkService : public InternetService {
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
void Search(const QString& text, Playlist* playlist, bool now = false);
// User should be logged in to be able to generate streaming urls
QUrl GetStreamingUrlFromSongId(const QString& song_id);
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_; }
@ -90,19 +92,20 @@ class GrooveSharkService : public InternetService {
void OpenSearchTab();
void AuthenticateSession();
void InitCountry();
// 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,
QNetworkReply* CreateRequest(const QString& method_name, const QList<QPair<QString, QVariant> > params,
bool need_authentication = false,
bool use_https = false);
// Convenient function for extracting result from reply, checking resulst's
// validity, and deleting reply object
// Convenient function for extracting result from reply
QVariantMap ExtractResult(QNetworkReply* reply);
void ResetSessionId();
GrooveSharkUrlHandler* url_handler_;
Playlist* pending_search_playlist_;
QStandardItem* root_;
@ -117,6 +120,7 @@ class GrooveSharkService : public InternetService {
QString password_; // In fact, password's md5 hash
QString user_id_;
QString session_id_;
QMap<QString, QVariant> country_;
QByteArray api_key_;
LoginState login_state_;

View File

@ -0,0 +1,35 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "groovesharkservice.h"
#include "groovesharkurlhandler.h"
#include "core/logging.h"
GrooveSharkUrlHandler::GrooveSharkUrlHandler(GrooveSharkService* service, QObject* parent)
: UrlHandler(parent),
service_(service) {
}
UrlHandler::LoadResult GrooveSharkUrlHandler::StartLoading(const QUrl& url) {
QString song_id = url.toString().remove("grooveshark://");
QUrl streaming_url = service_->GetStreamingUrlFromSongId(song_id);
qLog(Debug) << "GrooveShark Streaming URL: " << streaming_url;
return LoadResult(url, LoadResult::TrackAvailable, streaming_url);
}

View File

@ -0,0 +1,39 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef GROOVESHARKURLHANDLER_H
#define GROOVESHARKURLHANDLER_H
#include "core/urlhandler.h"
class GrooveSharkService;
class GrooveSharkUrlHandler : public UrlHandler {
public:
GrooveSharkUrlHandler(GrooveSharkService* service, QObject* parent);
QString scheme() const { return "grooveshark"; }
LoadResult StartLoading(const QUrl& url);
private:
GrooveSharkService* service_;
QString last_song_id_;
QString last_server_id_;
QString last_stream_key;
};
#endif // GROOVESHARKURLHANDLER_H