Add the ability to play streaming songs from GrooveShark
This commit is contained in:
parent
d380c2e49d
commit
5dfa4f4838
@ -130,6 +130,7 @@ set(SOURCES
|
|||||||
internet/groovesharksearchplaylisttype.cpp
|
internet/groovesharksearchplaylisttype.cpp
|
||||||
internet/groovesharkservice.cpp
|
internet/groovesharkservice.cpp
|
||||||
internet/groovesharksettingspage.cpp
|
internet/groovesharksettingspage.cpp
|
||||||
|
internet/groovesharkurlhandler.cpp
|
||||||
internet/icecastbackend.cpp
|
internet/icecastbackend.cpp
|
||||||
internet/icecastfilterwidget.cpp
|
internet/icecastfilterwidget.cpp
|
||||||
internet/icecastmodel.cpp
|
internet/icecastmodel.cpp
|
||||||
@ -369,6 +370,7 @@ set(HEADERS
|
|||||||
internet/groovesharksearchplaylisttype.h
|
internet/groovesharksearchplaylisttype.h
|
||||||
internet/groovesharkservice.h
|
internet/groovesharkservice.h
|
||||||
internet/groovesharksettingspage.h
|
internet/groovesharksettingspage.h
|
||||||
|
internet/groovesharkurlhandler.h
|
||||||
internet/icecastbackend.h
|
internet/icecastbackend.h
|
||||||
internet/icecastfilterwidget.h
|
internet/icecastfilterwidget.h
|
||||||
internet/icecastmodel.h
|
internet/icecastmodel.h
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <qjson/parser.h>
|
#include <qjson/parser.h>
|
||||||
#include <qjson/serializer.h>
|
#include <qjson/serializer.h>
|
||||||
@ -28,6 +29,7 @@
|
|||||||
|
|
||||||
#include "internetmodel.h"
|
#include "internetmodel.h"
|
||||||
#include "groovesharksearchplaylisttype.h"
|
#include "groovesharksearchplaylisttype.h"
|
||||||
|
#include "groovesharkurlhandler.h"
|
||||||
|
|
||||||
#include "core/database.h"
|
#include "core/database.h"
|
||||||
#include "core/logging.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;
|
const int GrooveSharkService::kSongSearchLimit = 50;
|
||||||
|
|
||||||
typedef QPair<QString, QString> Param;
|
typedef QPair<QString, QVariant> Param;
|
||||||
|
|
||||||
GrooveSharkService::GrooveSharkService(InternetModel *parent)
|
GrooveSharkService::GrooveSharkService(InternetModel *parent)
|
||||||
: InternetService(kServiceName, parent, parent),
|
: InternetService(kServiceName, parent, parent),
|
||||||
|
url_handler_(new GrooveSharkUrlHandler(this, this)),
|
||||||
pending_search_playlist_(NULL),
|
pending_search_playlist_(NULL),
|
||||||
root_(NULL),
|
root_(NULL),
|
||||||
search_(NULL),
|
search_(NULL),
|
||||||
@ -69,6 +72,7 @@ GrooveSharkService::GrooveSharkService(InternetModel *parent)
|
|||||||
api_key_(QByteArray::fromBase64(kApiSecret)),
|
api_key_(QByteArray::fromBase64(kApiSecret)),
|
||||||
login_state_(LoginState_OtherError) {
|
login_state_(LoginState_OtherError) {
|
||||||
|
|
||||||
|
model()->player()->RegisterUrlHandler(url_handler_);
|
||||||
model()->player()->playlists()->RegisterSpecialPlaylistType(new GrooveSharkSearchPlaylistType(this));
|
model()->player()->playlists()->RegisterSpecialPlaylistType(new GrooveSharkSearchPlaylistType(this));
|
||||||
|
|
||||||
// Get already existing (authenticated) session id, if any
|
// Get already existing (authenticated) session id, if any
|
||||||
@ -156,6 +160,57 @@ void GrooveSharkService::SearchSongsFinished() {
|
|||||||
pending_search_playlist_->InsertSongs(songs);
|
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) {
|
void GrooveSharkService::Login(const QString& username, const QString& password) {
|
||||||
// To login, we first need to create a session. Next, we will authenticate
|
// 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
|
// 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) {
|
if (use_https) {
|
||||||
url.setScheme("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);
|
QNetworkRequest req(url);
|
||||||
QNetworkReply *reply = network_->post(req, post_params);
|
QNetworkReply *reply = network_->post(req, post_params);
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "internetmodel.h"
|
#include "internetmodel.h"
|
||||||
#include "internetservice.h"
|
#include "internetservice.h"
|
||||||
|
|
||||||
|
class GrooveSharkUrlHandler;
|
||||||
class NetworkAccessManager;
|
class NetworkAccessManager;
|
||||||
class Playlist;
|
class Playlist;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
@ -53,12 +54,13 @@ class GrooveSharkService : public InternetService {
|
|||||||
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
|
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
|
||||||
|
|
||||||
void Search(const QString& text, Playlist* playlist, bool now = false);
|
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 Login(const QString& username, const QString& password);
|
||||||
void Logout();
|
void Logout();
|
||||||
bool IsLoggedIn() { return !session_id_.isEmpty(); }
|
bool IsLoggedIn() { return !session_id_.isEmpty(); }
|
||||||
// Persisted in the settings and updated on each Login().
|
// Persisted in the settings and updated on each Login().
|
||||||
LoginState login_state() const { return login_state_; }
|
LoginState login_state() const { return login_state_; }
|
||||||
|
|
||||||
const QString& session_id() { return session_id_; }
|
const QString& session_id() { return session_id_; }
|
||||||
|
|
||||||
|
|
||||||
@ -90,19 +92,20 @@ class GrooveSharkService : public InternetService {
|
|||||||
|
|
||||||
void OpenSearchTab();
|
void OpenSearchTab();
|
||||||
void AuthenticateSession();
|
void AuthenticateSession();
|
||||||
|
void InitCountry();
|
||||||
|
|
||||||
// Create a request for the given method, with the given params.
|
// Create a request for the given method, with the given params.
|
||||||
// If need_authentication is true, add session_id to params.
|
// If need_authentication is true, add session_id to params.
|
||||||
// Returns the reply object created
|
// 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 need_authentication = false,
|
||||||
bool use_https = false);
|
bool use_https = false);
|
||||||
// Convenient function for extracting result from reply, checking resulst's
|
// Convenient function for extracting result from reply
|
||||||
// validity, and deleting reply object
|
|
||||||
QVariantMap ExtractResult(QNetworkReply* reply);
|
QVariantMap ExtractResult(QNetworkReply* reply);
|
||||||
void ResetSessionId();
|
void ResetSessionId();
|
||||||
|
|
||||||
|
|
||||||
|
GrooveSharkUrlHandler* url_handler_;
|
||||||
Playlist* pending_search_playlist_;
|
Playlist* pending_search_playlist_;
|
||||||
|
|
||||||
QStandardItem* root_;
|
QStandardItem* root_;
|
||||||
@ -117,6 +120,7 @@ class GrooveSharkService : public InternetService {
|
|||||||
QString password_; // In fact, password's md5 hash
|
QString password_; // In fact, password's md5 hash
|
||||||
QString user_id_;
|
QString user_id_;
|
||||||
QString session_id_;
|
QString session_id_;
|
||||||
|
QMap<QString, QVariant> country_;
|
||||||
QByteArray api_key_;
|
QByteArray api_key_;
|
||||||
|
|
||||||
LoginState login_state_;
|
LoginState login_state_;
|
||||||
|
35
src/internet/groovesharkurlhandler.cpp
Normal file
35
src/internet/groovesharkurlhandler.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
39
src/internet/groovesharkurlhandler.h
Normal file
39
src/internet/groovesharkurlhandler.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user