mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-19 04:50:16 +01:00
Add Grooveshark radios
This commit is contained in:
parent
8d5ef62256
commit
1f5ac97934
@ -136,6 +136,7 @@ set(SOURCES
|
||||
internet/digitallyimportedservicebase.cpp
|
||||
internet/digitallyimportedsettingspage.cpp
|
||||
internet/digitallyimportedurlhandler.cpp
|
||||
internet/groovesharkradio.cpp
|
||||
internet/groovesharksearchplaylisttype.cpp
|
||||
internet/groovesharkservice.cpp
|
||||
internet/groovesharksettingspage.cpp
|
||||
@ -379,6 +380,7 @@ set(HEADERS
|
||||
internet/digitallyimportedclient.h
|
||||
internet/digitallyimportedservicebase.h
|
||||
internet/digitallyimportedsettingspage.h
|
||||
internet/groovesharkradio.h
|
||||
internet/groovesharkservice.h
|
||||
internet/groovesharksettingspage.h
|
||||
internet/groovesharkurlhandler.h
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "qtiocompressor.h"
|
||||
|
||||
#include "internetmodel.h"
|
||||
#include "groovesharkradio.h"
|
||||
#include "groovesharksearchplaylisttype.h"
|
||||
#include "groovesharkurlhandler.h"
|
||||
|
||||
@ -57,6 +58,9 @@
|
||||
#include "playlist/playlistmanager.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
using smart_playlists::Generator;
|
||||
using smart_playlists::GeneratorPtr;
|
||||
|
||||
// The Grooveshark terms of service require that application keys are not
|
||||
// accessible to third parties. Therefore this application key is obfuscated to
|
||||
// prevent third parties from viewing it.
|
||||
@ -84,6 +88,7 @@ GroovesharkService::GroovesharkService(InternetModel *parent)
|
||||
search_(NULL),
|
||||
popular_month_(NULL),
|
||||
popular_today_(NULL),
|
||||
stations_(NULL),
|
||||
favorites_(NULL),
|
||||
subscribed_playlists_divider_(NULL),
|
||||
network_(new NetworkAccessManager(this)),
|
||||
@ -306,21 +311,9 @@ void GroovesharkService::InitCountry() {
|
||||
return;
|
||||
// Get country info
|
||||
QNetworkReply *reply_country = CreateRequest("getCountry", QList<Param>());
|
||||
if (!WaitForReply(reply_country))
|
||||
return;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -332,20 +325,8 @@ QUrl GroovesharkService::GetStreamingUrlFromSongId(const QString& song_id,
|
||||
parameters << Param("songID", song_id)
|
||||
<< Param("country", country_);
|
||||
QNetworkReply* reply = CreateRequest("getSubscriberStreamKey", parameters);
|
||||
// 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();
|
||||
}
|
||||
if (!WaitForReply(reply))
|
||||
return QUrl();
|
||||
QVariantMap result = ExtractResult(reply);
|
||||
server_id->clear();
|
||||
server_id->append(result["StreamServerID"].toString());
|
||||
@ -544,6 +525,15 @@ void GroovesharkService::EnsureItemsCreated() {
|
||||
InternetModel::Role_PlayBehaviour);
|
||||
root_->appendRow(popular_today_);
|
||||
|
||||
QStandardItem* radios_divider = new QStandardItem(tr("Radios"));
|
||||
radios_divider->setData(true, InternetModel::Role_IsDivider);
|
||||
root_->appendRow(radios_divider);
|
||||
|
||||
stations_ = new QStandardItem(QIcon(":last.fm/icon_radio.png"), tr("Stations"));
|
||||
stations_->setData(InternetModel::Type_UserPlaylist, InternetModel::Role_Type);
|
||||
stations_->setData(true, InternetModel::Role_CanLazyLoad);
|
||||
root_->appendRow(stations_);
|
||||
|
||||
QStandardItem* playlists_divider = new QStandardItem(tr("Playlists"));
|
||||
playlists_divider->setData(true, InternetModel::Role_IsDivider);
|
||||
root_->appendRow(playlists_divider);
|
||||
@ -564,6 +554,7 @@ void GroovesharkService::EnsureItemsCreated() {
|
||||
RetrieveUserFavorites();
|
||||
RetrieveUserPlaylists();
|
||||
RetrieveSubscribedPlaylists();
|
||||
RetrieveAutoplayTags();
|
||||
RetrievePopularSongs();
|
||||
}
|
||||
}
|
||||
@ -802,6 +793,56 @@ void GroovesharkService::SubscribedPlaylistsRetrieved(QNetworkReply* reply) {
|
||||
}
|
||||
}
|
||||
|
||||
void GroovesharkService::RetrieveAutoplayTags() {
|
||||
QNetworkReply* reply = CreateRequest("getAutoplayTags", QList<Param>());
|
||||
NewClosure(reply, SIGNAL(finished()),
|
||||
this, SLOT(AutoplayTagsRetrieved(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void GroovesharkService::AutoplayTagsRetrieved(QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
QVariantMap result = ExtractResult(reply);
|
||||
QVariantMap::const_iterator it;
|
||||
for (it = result.constBegin(); it != result.constEnd(); ++it) {
|
||||
int id = it.key().toInt();
|
||||
QString name = it.value().toString().toLower();
|
||||
// Names received aren't very nice: make them more user friendly to display
|
||||
name.replace("_", " ");
|
||||
name[0] = name[0].toUpper();
|
||||
|
||||
QStandardItem* item = new QStandardItem(QIcon(":last.fm/icon_radio.png"), name);
|
||||
item->setData(InternetModel::Type_SmartPlaylist, InternetModel::Role_Type);
|
||||
item->setData(InternetModel::PlayBehaviour_SingleItem, InternetModel::Role_PlayBehaviour);
|
||||
item->setData(id, Role_UserPlaylistId);
|
||||
|
||||
stations_->appendRow(item);
|
||||
}
|
||||
}
|
||||
|
||||
Song GroovesharkService::StartAutoplayTag(int tag_id, QVariantMap& autoplay_state) {
|
||||
QList<Param> parameters;
|
||||
parameters << Param("tagID", tag_id);
|
||||
QNetworkReply* reply = CreateRequest("startAutoplayTag", parameters);
|
||||
if (!WaitForReply(reply))
|
||||
return Song();
|
||||
reply->deleteLater();
|
||||
QVariantMap result = ExtractResult(reply);
|
||||
autoplay_state = result["autoplayState"].toMap();
|
||||
return ExtractSong(result["nextSong"].toMap());
|
||||
}
|
||||
|
||||
Song GroovesharkService::GetAutoplaySong(QVariantMap& autoplay_state) {
|
||||
QList<Param> parameters;
|
||||
parameters << Param("autoplayState", autoplay_state);
|
||||
QNetworkReply* reply = CreateRequest("getAutoplaySong", parameters);
|
||||
if (!WaitForReply(reply))
|
||||
return Song();
|
||||
reply->deleteLater();
|
||||
QVariantMap result = ExtractResult(reply);
|
||||
autoplay_state = result["autoplayState"].toMap();
|
||||
return ExtractSong(result["nextSong"].toMap());
|
||||
}
|
||||
|
||||
void GroovesharkService::MarkStreamKeyOver30Secs(const QString& stream_key,
|
||||
const QString& server_id) {
|
||||
QList<Param> parameters;
|
||||
@ -862,6 +903,17 @@ void GroovesharkService::ItemDoubleClicked(QStandardItem* item) {
|
||||
}
|
||||
}
|
||||
|
||||
GeneratorPtr GroovesharkService::CreateGenerator(QStandardItem* item) {
|
||||
GeneratorPtr ret;
|
||||
if (!item ||
|
||||
item->data(InternetModel::Role_Type).toInt() != InternetModel::Type_SmartPlaylist) {
|
||||
return ret;
|
||||
}
|
||||
int tag_id = item->data(Role_UserPlaylistId).toInt();
|
||||
ret = GeneratorPtr(new GroovesharkRadio(this ,tag_id));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GroovesharkService::DropMimeData(const QMimeData* data, const QModelIndex& index) {
|
||||
if (!data) {
|
||||
return;
|
||||
@ -1279,6 +1331,21 @@ QNetworkReply* GroovesharkService::CreateRequest(const QString& method_name, QLi
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool GroovesharkService::WaitForReply(QNetworkReply* 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(10000);
|
||||
event_loop.exec();
|
||||
if (!timeout_timer.isActive()) {
|
||||
qLog(Error) << "Grooveshark request timeout";
|
||||
return false;
|
||||
}
|
||||
timeout_timer.stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap GroovesharkService::ExtractResult(QNetworkReply* reply) {
|
||||
QJson::Parser parser;
|
||||
bool ok;
|
||||
@ -1309,23 +1376,28 @@ SongList GroovesharkService::ExtractSongs(const QVariantMap& result) {
|
||||
SongList songs;
|
||||
for (int i=0; i<result_songs.size(); ++i) {
|
||||
QVariantMap result_song = result_songs[i].toMap();
|
||||
Song song;
|
||||
int song_id = result_song["SongID"].toInt();
|
||||
QString song_name = result_song["SongName"].toString();
|
||||
QString artist_name = result_song["ArtistName"].toString();
|
||||
QString album_name = result_song["AlbumName"].toString();
|
||||
QString cover = result_song["CoverArtFilename"].toString();
|
||||
song.Init(song_name, artist_name, album_name, 0);
|
||||
song.set_art_automatic(QString(kUrlCover) + cover);
|
||||
// Special kind of URL: because we need to request a stream key for each
|
||||
// play, we generate a fake URL for now, and we will create a real streaming
|
||||
// URL when user will actually play the song (through url handler)
|
||||
song.set_url(QString("grooveshark://%1").arg(song_id));
|
||||
songs << song;
|
||||
songs << ExtractSong(result_song);
|
||||
}
|
||||
return songs;
|
||||
}
|
||||
|
||||
Song GroovesharkService::ExtractSong(const QVariantMap& result_song) {
|
||||
Song song;
|
||||
int song_id = result_song["SongID"].toInt();
|
||||
QString song_name = result_song["SongName"].toString();
|
||||
QString artist_name = result_song["ArtistName"].toString();
|
||||
QString album_name = result_song["AlbumName"].toString();
|
||||
QString cover = result_song["CoverArtFilename"].toString();
|
||||
song.Init(song_name, artist_name, album_name, 0);
|
||||
song.set_art_automatic(QString(kUrlCover) + cover);
|
||||
// Special kind of URL: because we need to request a stream key for each
|
||||
// play, we generate a fake URL for now, and we will create a real streaming
|
||||
// URL when user will actually play the song (through url handler)
|
||||
song.set_url(QString("grooveshark://%1").arg(song_id));
|
||||
|
||||
return song;
|
||||
}
|
||||
|
||||
QList<int> GroovesharkService::ExtractSongsIds(const QVariantMap& result) {
|
||||
QVariantList result_songs = result["songs"].toList();
|
||||
QList<int> songs_ids;
|
||||
|
@ -65,6 +65,7 @@ class GroovesharkService : public InternetService {
|
||||
void LazyPopulate(QStandardItem *parent);
|
||||
|
||||
void ItemDoubleClicked(QStandardItem* item);
|
||||
smart_playlists::GeneratorPtr CreateGenerator(QStandardItem* item);
|
||||
void DropMimeData(const QMimeData* data, const QModelIndex& index);
|
||||
QList<QAction*> playlistitem_actions(const Song& song);
|
||||
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
|
||||
@ -83,6 +84,7 @@ class GroovesharkService : public InternetService {
|
||||
void RetrievePopularSongsMonth();
|
||||
void RetrievePopularSongsToday();
|
||||
void RetrieveSubscribedPlaylists();
|
||||
void RetrieveAutoplayTags();
|
||||
void SetPlaylistSongs(int playlist_id, const QList<int>& songs_ids);
|
||||
void RemoveFromPlaylist(int playlist_id, int song_id);
|
||||
// Refresh playlist_id playlist , or create it if it doesn't exist
|
||||
@ -92,6 +94,11 @@ class GroovesharkService : public InternetService {
|
||||
void AddUserFavoriteSong(int song_id);
|
||||
void RemoveFromFavorites(int song_id);
|
||||
void GetSongUrlToShare(int song_id);
|
||||
// Start autoplay for the given tag_id, fill the autoplay_state, returns a
|
||||
// first song to play
|
||||
Song StartAutoplayTag(int tag_id, QVariantMap& autoplay_state);
|
||||
// Get another autoplay song. autoplay_state is the autoplay_state received from StartAutoplayTag
|
||||
Song GetAutoplaySong(QVariantMap& autoplay_state);
|
||||
void MarkStreamKeyOver30Secs(const QString& stream_key, const QString& server_id);
|
||||
void MarkSongComplete(const QString& song_id, const QString& stream_key, const QString& server_id);
|
||||
|
||||
@ -146,6 +153,7 @@ class GroovesharkService : public InternetService {
|
||||
void PopularSongsMonthRetrieved(QNetworkReply* reply);
|
||||
void PopularSongsTodayRetrieved(QNetworkReply* reply);
|
||||
void SubscribedPlaylistsRetrieved(QNetworkReply* reply);
|
||||
void AutoplayTagsRetrieved(QNetworkReply* reply);
|
||||
void PlaylistSongsRetrieved();
|
||||
void PlaylistSongsSet(QNetworkReply* reply, int playlist_id, int task_id);
|
||||
void CreateNewPlaylist();
|
||||
@ -184,10 +192,18 @@ class GroovesharkService : public InternetService {
|
||||
// Returns the reply object created
|
||||
QNetworkReply* CreateRequest(const QString& method_name, const QList<QPair<QString, QVariant> > params,
|
||||
bool use_https = false);
|
||||
// Convenient function which block until 'reply' replies, or timeout after 10
|
||||
// seconds. Returns false if reply has timeouted
|
||||
bool WaitForReply(QNetworkReply* reply);
|
||||
// Convenient function for extracting result from reply
|
||||
QVariantMap ExtractResult(QNetworkReply* reply);
|
||||
// Convenient function for extracting songs from grooveshark result
|
||||
// Convenient function for extracting songs from grooveshark result. result
|
||||
// should be the "result" field of most Grooveshark replies
|
||||
SongList ExtractSongs(const QVariantMap& result);
|
||||
// Convenient function for extracting song from grooveshark result.
|
||||
// result_song should be the song field ('song', 'nextSong', ...) of the
|
||||
// Grooveshark reply
|
||||
Song ExtractSong(const QVariantMap& result_song);
|
||||
// Convenient functions for extracting Grooveshark songs ids
|
||||
QList<int> ExtractSongsIds(const QVariantMap& result);
|
||||
QList<int> ExtractSongsIds(const QList<QUrl>& urls);
|
||||
@ -213,6 +229,7 @@ class GroovesharkService : public InternetService {
|
||||
QStandardItem* search_;
|
||||
QStandardItem* popular_month_;
|
||||
QStandardItem* popular_today_;
|
||||
QStandardItem* stations_;
|
||||
QStandardItem* favorites_;
|
||||
QStandardItem* subscribed_playlists_divider_;
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "groovesharkservice.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/mergedproxymodel.h"
|
||||
#include "smartplaylists/generatormimedata.h"
|
||||
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
#include "lastfmservice.h"
|
||||
@ -38,6 +39,10 @@
|
||||
#include <QMimeData>
|
||||
#include <QtDebug>
|
||||
|
||||
using smart_playlists::Generator;
|
||||
using smart_playlists::GeneratorMimeData;
|
||||
using smart_playlists::GeneratorPtr;
|
||||
|
||||
QMap<QString, InternetService*>* InternetModel::sServices = NULL;
|
||||
|
||||
InternetModel::InternetModel(BackgroundThread<Database>* db_thread,
|
||||
@ -197,6 +202,18 @@ QMimeData* InternetModel::mimeData(const QModelIndexList& indexes) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (indexes.count() == 1 &&
|
||||
indexes[0].data(Role_Type).toInt() == Type_SmartPlaylist) {
|
||||
GeneratorPtr generator =
|
||||
InternetModel::ServiceForIndex(indexes[0])->CreateGenerator(itemFromIndex(indexes[0]));
|
||||
if (!generator)
|
||||
return NULL;
|
||||
GeneratorMimeData* data = new GeneratorMimeData(generator);
|
||||
data->setData(LibraryModel::kSmartPlaylistsMimeType, QByteArray());
|
||||
data->name_for_new_playlist_ = this->data(indexes.first()).toString();
|
||||
return data;
|
||||
}
|
||||
|
||||
QList<QUrl> urls;
|
||||
QModelIndexList new_indexes;
|
||||
|
||||
|
@ -88,6 +88,7 @@ public:
|
||||
enum Type {
|
||||
Type_Service = 1,
|
||||
Type_UserPlaylist,
|
||||
Type_SmartPlaylist,
|
||||
|
||||
TypeCount
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "core/song.h"
|
||||
#include "playlist/playlistitem.h"
|
||||
#include "smartplaylists/generator.h"
|
||||
#include "ui/settingsdialog.h"
|
||||
#include "widgets/multiloadingindicator.h"
|
||||
|
||||
@ -47,6 +48,8 @@ public:
|
||||
|
||||
virtual void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos) {}
|
||||
virtual void ItemDoubleClicked(QStandardItem* item) {}
|
||||
// Create a generator for smart playlists
|
||||
virtual smart_playlists::GeneratorPtr CreateGenerator(QStandardItem* item) { return smart_playlists::GeneratorPtr(); }
|
||||
// Give the service a chance to do a custom action when data is dropped on it
|
||||
virtual void DropMimeData(const QMimeData* data, const QModelIndex& index) {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user