Remove Grooveshark support.

Well that was nice while it lasted.
This commit is contained in:
John Maguire 2015-05-01 11:57:59 +01:00
parent c0cf5e1220
commit e1ea116bfb
20 changed files with 1 additions and 3002 deletions

View File

@ -9,7 +9,6 @@
<file>currenttrack_pause.png</file>
<file>currenttrack_play.png</file>
<file>globalsearch.css</file>
<file>grooveshark-valicert-ca.pem</file>
<file>hypnotoad.gif</file>
<file>icon_large_grey.png</file>
<file>icon_large.png</file>
@ -320,7 +319,6 @@
<file>providers/dropbox.png</file>
<file>providers/echonest.png</file>
<file>providers/googledrive.png</file>
<file>providers/grooveshark.png</file>
<file>providers/itunes.png</file>
<file>providers/jamendo.png</file>
<file>providers/jazzradio.png</file>

View File

@ -1,43 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -147,7 +147,6 @@ set(SOURCES
globalsearch/globalsearchsettingspage.cpp
globalsearch/globalsearchsortmodel.cpp
globalsearch/globalsearchview.cpp
globalsearch/groovesharksearchprovider.cpp
globalsearch/icecastsearchprovider.cpp
globalsearch/librarysearchprovider.cpp
globalsearch/savedradiosearchprovider.cpp
@ -167,10 +166,6 @@ set(SOURCES
internet/digitally/digitallyimportedsettingspage.cpp
internet/digitally/digitallyimportedurlhandler.cpp
internet/core/geolocator.cpp
internet/grooveshark/groovesharkradio.cpp
internet/grooveshark/groovesharkservice.cpp
internet/grooveshark/groovesharksettingspage.cpp
internet/grooveshark/groovesharkurlhandler.cpp
internet/icecast/icecastbackend.cpp
internet/icecast/icecastfilterwidget.cpp
internet/icecast/icecastmodel.cpp
@ -464,7 +459,6 @@ set(HEADERS
globalsearch/globalsearchmodel.h
globalsearch/globalsearchsettingspage.h
globalsearch/globalsearchview.h
globalsearch/groovesharksearchprovider.h
globalsearch/searchprovider.h
globalsearch/simplesearchprovider.h
globalsearch/soundcloudsearchprovider.h
@ -476,9 +470,6 @@ set(HEADERS
internet/digitally/digitallyimportedservicebase.h
internet/digitally/digitallyimportedsettingspage.h
internet/core/geolocator.h
internet/grooveshark/groovesharkservice.h
internet/grooveshark/groovesharksettingspage.h
internet/grooveshark/groovesharkurlhandler.h
internet/icecast/icecastbackend.h
internet/icecast/icecastfilterwidget.h
internet/icecast/icecastmodel.h
@ -694,7 +685,6 @@ set(UI
globalsearch/suggestionwidget.ui
internet/digitally/digitallyimportedsettingspage.ui
internet/grooveshark/groovesharksettingspage.ui
internet/icecast/icecastfilterwidget.ui
internet/core/internetshowsettingspage.ui
internet/core/internetviewcontainer.ui

View File

@ -890,19 +890,6 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin* bin,
g_object_set(element, "device",
instance->source_device().toLocal8Bit().constData(), nullptr);
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element),
"extra-headers") &&
instance->url().host().contains("grooveshark")) {
// Grooveshark streaming servers will answer with a 400 error 'Bad request'
// if we don't specify 'Range' field in HTTP header.
// Maybe it could be useful in some other cases, but for now, I prefer to
// keep this grooveshark specific.
GstStructure* headers;
headers = gst_structure_new("extra-headers", "Range", G_TYPE_STRING,
"bytes=0-", nullptr);
g_object_set(element, "extra-headers", headers, nullptr);
gst_structure_free(headers);
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element),
"extra-headers") &&
instance->url().host().contains("amazonaws.com")) {

View File

@ -1,132 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, 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 "groovesharksearchprovider.h"
#include <QIcon>
#include "core/application.h"
#include "core/logging.h"
#include "covers/albumcoverloader.h"
#include "internet/grooveshark/groovesharkservice.h"
GroovesharkSearchProvider::GroovesharkSearchProvider(Application* app,
QObject* parent)
: SearchProvider(app, parent), service_(nullptr) {}
void GroovesharkSearchProvider::Init(GroovesharkService* service) {
service_ = service;
SearchProvider::Init(
"Grooveshark", "grooveshark", QIcon(":providers/grooveshark.png"),
WantsDelayedQueries | ArtIsProbablyRemote | CanShowConfig);
connect(service_, SIGNAL(SimpleSearchResults(int, SongList)),
SLOT(SearchDone(int, SongList)));
connect(service_, SIGNAL(AlbumSearchResult(int, QList<quint64>)),
SLOT(AlbumSearchResult(int, QList<quint64>)));
connect(service_, SIGNAL(AlbumSongsLoaded(quint64, SongList)),
SLOT(AlbumSongsLoaded(quint64, SongList)));
cover_loader_options_.desired_height_ = kArtHeight;
cover_loader_options_.pad_output_image_ = true;
cover_loader_options_.scale_output_image_ = true;
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)),
SLOT(AlbumArtLoaded(quint64, QImage)));
}
void GroovesharkSearchProvider::SearchAsync(int id, const QString& query) {
const int service_id = service_->SimpleSearch(query);
pending_searches_[service_id] = PendingState(id, TokenizeQuery(query));
;
const int album_id = service_->SearchAlbums(query);
pending_searches_[album_id] = PendingState(id, TokenizeQuery(query));
}
void GroovesharkSearchProvider::SearchDone(int id, const SongList& songs) {
// Map back to the original id.
const PendingState state = pending_searches_.take(id);
const int global_search_id = state.orig_id_;
ResultList ret;
for (const Song& song : songs) {
Result result(this);
result.metadata_ = song;
ret << result;
}
emit ResultsAvailable(global_search_id, ret);
MaybeSearchFinished(global_search_id);
}
void GroovesharkSearchProvider::AlbumSearchResult(
int id, const QList<quint64>& albums_ids) {
// Map back to the original id.
const PendingState state = pending_searches_.take(id);
const int global_search_id = state.orig_id_;
if (albums_ids.isEmpty()) {
MaybeSearchFinished(global_search_id);
return;
}
for (const quint64 album_id : albums_ids) {
pending_searches_[album_id] = PendingState(global_search_id, QStringList());
}
}
void GroovesharkSearchProvider::MaybeSearchFinished(int id) {
if (pending_searches_.keys(PendingState(id, QStringList())).isEmpty()) {
emit SearchFinished(id);
}
}
void GroovesharkSearchProvider::LoadArtAsync(int id, const Result& result) {
quint64 loader_id = app_->album_cover_loader()->LoadImageAsync(
cover_loader_options_, result.metadata_);
cover_loader_tasks_[loader_id] = id;
}
void GroovesharkSearchProvider::AlbumArtLoaded(quint64 id,
const QImage& image) {
if (!cover_loader_tasks_.contains(id)) {
return;
}
int original_id = cover_loader_tasks_.take(id);
emit ArtLoaded(original_id, image);
}
bool GroovesharkSearchProvider::IsLoggedIn() {
return (service_ && service_->IsLoggedIn());
}
void GroovesharkSearchProvider::ShowConfig() { service_->ShowConfig(); }
void GroovesharkSearchProvider::AlbumSongsLoaded(quint64 id,
const SongList& songs) {
const PendingState state = pending_searches_.take(id);
const int global_search_id = state.orig_id_;
ResultList ret;
for (const Song& s : songs) {
Result result(this);
result.metadata_ = s;
ret << result;
}
emit ResultsAvailable(global_search_id, ret);
MaybeSearchFinished(global_search_id);
}

View File

@ -1,57 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, 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 GROOVESHARKSEARCHPROVIDER_H
#define GROOVESHARKSEARCHPROVIDER_H
#include "searchprovider.h"
#include "covers/albumcoverloaderoptions.h"
#include "internet/grooveshark/groovesharkservice.h"
class AlbumCoverLoader;
class GroovesharkSearchProvider : public SearchProvider {
Q_OBJECT
public:
explicit GroovesharkSearchProvider(Application* app, QObject* parent = nullptr);
void Init(GroovesharkService* service);
// SearchProvider
void SearchAsync(int id, const QString& query) override;
void LoadArtAsync(int id, const Result& result) override;
bool IsLoggedIn() override;
void ShowConfig() override;
InternetService* internet_service() override { return service_; }
private slots:
void SearchDone(int id, const SongList& songs);
void AlbumSearchResult(int id, const QList<quint64>& albums_ids);
void AlbumArtLoaded(quint64 id, const QImage& image);
void AlbumSongsLoaded(quint64 id, const SongList& songs);
private:
void MaybeSearchFinished(int id);
GroovesharkService* service_;
QMap<int, PendingState> pending_searches_;
AlbumCoverLoaderOptions cover_loader_options_;
QMap<quint64, int> cover_loader_tasks_;
};
#endif

View File

@ -29,7 +29,6 @@
#include <QtDebug>
#include "internet/digitally/digitallyimportedservicebase.h"
#include "internet/grooveshark/groovesharkservice.h"
#include "internet/icecast/icecastservice.h"
#include "internet/core/internetmimedata.h"
#include "internet/core/internetservice.h"
@ -90,7 +89,6 @@ InternetModel::InternetModel(Application* app, QObject* parent)
AddService(new DigitallyImportedService(app, this));
AddService(new IcecastService(app, this));
AddService(new JamendoService(app, this));
AddService(new GroovesharkService(app, this));
AddService(new JazzRadioService(app, this));
AddService(new MagnatuneService(app, this));
AddService(new PodcastService(app, this));

View File

@ -1,62 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.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 "groovesharkradio.h"
#include "groovesharkservice.h"
#include "core/logging.h"
#include "internet/core/internetplaylistitem.h"
GroovesharkRadio::GroovesharkRadio(GroovesharkService* service)
: service_(service), tag_id_(0), use_tag_(false), first_time_(true) {}
GroovesharkRadio::GroovesharkRadio(GroovesharkService* service, int tag_id)
: service_(service), tag_id_(tag_id), use_tag_(true), first_time_(true) {}
void GroovesharkRadio::Load(const QByteArray& data) {}
QByteArray GroovesharkRadio::Save() const { return QByteArray(); }
PlaylistItemList GroovesharkRadio::Generate() {
PlaylistItemList items;
if (first_time_) {
Song song;
if (use_tag_) {
song = service_->StartAutoplayTag(tag_id_, autoplay_state_);
} else {
song = service_->StartAutoplay(autoplay_state_);
}
// If the song url isn't valid, stop here
if (!song.is_valid()) {
return items;
}
PlaylistItemPtr playlist_item =
PlaylistItemPtr(new InternetPlaylistItem(service_, song));
items << playlist_item;
first_time_ = false;
}
Song song = service_->GetAutoplaySong(autoplay_state_);
if (!song.is_valid()) {
return items;
}
PlaylistItemPtr playlist_item =
PlaylistItemPtr(new InternetPlaylistItem(service_, song));
items << playlist_item;
return items;
}

View File

@ -1,52 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.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 INTERNET_GROOVESHARK_GROOVESHARKRADIO_H_
#define INTERNET_GROOVESHARK_GROOVESHARKRADIO_H_
#include "smartplaylists/generator.h"
class GroovesharkService;
class GroovesharkRadio : public smart_playlists::Generator {
public:
// Start Grooveshark radio for a particular type of music
GroovesharkRadio(GroovesharkService* service, int tag_id);
// Start Grooveshark radio based on last artists and songs you listen to
explicit GroovesharkRadio(GroovesharkService* service);
QString type() const { return "Grooveshark"; }
void Load(const QByteArray& data);
QByteArray Save() const;
PlaylistItemList Generate();
PlaylistItemList GenerateMore(int count) { return Generate(); }
bool is_dynamic() const { return true; }
private:
GroovesharkService* service_;
int tag_id_;
// Boolean to specify if we should use tag. If not, we will used autoplay
// without tag
bool use_tag_;
// For Generate: indicates if it's the first time we generate songs
bool first_time_;
QVariantMap autoplay_state_;
};
#endif // INTERNET_GROOVESHARK_GROOVESHARKRADIO_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,338 +0,0 @@
/* This file is part of Clementine.
Copyright 2011-2014, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2011-2012, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2012, David Sansome <me@davidsansome.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.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 INTERNET_GROOVESHARK_GROOVESHARKSERVICE_H_
#define INTERNET_GROOVESHARK_GROOVESHARKSERVICE_H_
#include "internet/core/internetmodel.h"
#include "internet/core/internetservice.h"
#include <QSslError>
class GroovesharkUrlHandler;
class NetworkAccessManager;
class Playlist;
class SearchBoxWidget;
class QMenu;
class QNetworkReply;
class QNetworkRequest;
class QSortFilterProxyModel;
class GroovesharkService : public InternetService {
Q_OBJECT
public:
GroovesharkService(Application* app, InternetModel* parent);
~GroovesharkService();
enum Role {
Role_UserPlaylistId = InternetModel::RoleCount,
Role_PlaylistType
};
enum PlaylistType {
UserPlaylist = Qt::UserRole,
// Favorites and Library list are like playlists, but we want to do special
// treatments in some cases
UserFavorites,
UserLibrary,
SubscribedPlaylist
};
// 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);
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 QPoint& global_pos);
QWidget* HeaderWidget() const;
// User should be logged in to be able to generate streaming urls
QUrl GetStreamingUrlFromSongId(const QString& song_id,
const QString& artist_id, QString* server_id,
QString* stream_key, qint64* length_nanosec);
void Login(const QString& username, const QString& password);
void Logout();
bool IsLoggedIn() const { return !session_id_.isEmpty(); }
void RetrieveUserPlaylists();
void RetrieveUserFavorites();
void RetrieveUserLibrarySongs();
void RetrievePopularSongs();
void RetrievePopularSongsMonth();
void RetrievePopularSongsToday();
void RetrieveSubscribedPlaylists();
void RetrieveAutoplayTags();
void SetPlaylistSongs(int playlist_id, const QList<int>& songs_ids);
void RemoveFromPlaylist(int playlist_id,
const QList<int>& songs_ids_to_remove);
// Refresh playlist_id playlist , or create it if it doesn't exist
void RefreshPlaylist(int playlist_id);
void DeletePlaylist(int playlist_id);
void RenamePlaylist(int playlist_id);
void AddUserFavoriteSong(int song_id);
void RemoveFromFavorites(const QList<int>& songs_ids_to_remove);
void AddUserLibrarySongs(const QList<int>& songs_ids);
void RemoveFromLibrary(const QList<int>& songs_ids_to_remove);
void GetSongUrlToShare(int song_id);
void GetPlaylistUrlToShare(int playlist_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);
Song StartAutoplay(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);
// Persisted in the settings and updated on each Login().
LoginState login_state() const { return login_state_; }
const QString& session_id() { return session_id_; }
int SimpleSearch(const QString& query);
int SearchAlbums(const QString& query);
void GetAlbumSongs(quint64 album_id);
static const char* kServiceName;
static const char* kSettingsGroup;
signals:
void LoginFinished(bool success);
void SimpleSearchResults(int id, SongList songs);
// AlbumSearchResult emits the search id and the Grooveshark ids of the
// albums found. Albums' songs will be loaded asynchronously and
// AlbumSongsLoaded will be emitted, containing the actual Albums' songs.
void AlbumSearchResult(int id, QList<quint64> albums_ids);
void AlbumSongsLoaded(quint64 id, SongList songs);
public slots:
void Search(const QString& text, bool now = false);
void ShowConfig();
// Refresh all Grooveshark's items, and re-fill them
void RefreshItems();
protected:
struct PlaylistInfo {
PlaylistInfo() {}
PlaylistInfo(int id, QString name, QStandardItem* item = nullptr)
: id_(id), name_(name), item_(item) {}
bool operator<(const PlaylistInfo other) const {
return name_.localeAwareCompare(other.name_) < 0;
}
int id_;
QString name_;
QStandardItem* item_;
QList<int> songs_ids_;
};
private slots:
void SessionCreated(QNetworkReply* reply);
void DoSearch();
void SearchSongsFinished(QNetworkReply* reply);
void SimpleSearchFinished(QNetworkReply* reply, int id);
void SearchAlbumsFinished(QNetworkReply* reply, int id);
void GetAlbumSongsFinished(QNetworkReply* reply, quint64 album_id);
void Authenticated(QNetworkReply* reply);
void UserPlaylistsRetrieved(QNetworkReply* reply);
void UserFavoritesRetrieved(QNetworkReply* reply, int task_id);
void UserLibrarySongsRetrieved(QNetworkReply* reply, int task_id);
void PopularSongsMonthRetrieved(QNetworkReply* reply);
void PopularSongsTodayRetrieved(QNetworkReply* reply);
void SubscribedPlaylistsRetrieved(QNetworkReply* reply);
void AutoplayTagsRetrieved(QNetworkReply* reply);
void PlaylistSongsRetrieved(QNetworkReply* reply, int playlist_id, int request_id);
void PlaylistSongsSet(QNetworkReply* reply, int playlist_id, int task_id);
void CreateNewPlaylist();
void NewPlaylistCreated(QNetworkReply* reply, const QString& name);
void DeleteCurrentPlaylist();
void RenameCurrentPlaylist();
void PlaylistDeleted(QNetworkReply* reply, int playlist_id);
void PlaylistRenamed(QNetworkReply* reply, int playlist_id,
const QString& new_name);
void AddCurrentSongToUserFavorites() {
AddUserFavoriteSong(current_song_id_);
}
void AddCurrentSongToUserLibrary() {
AddUserLibrarySongs(QList<int>() << current_song_id_);
}
void AddCurrentSongToPlaylist(QAction* action);
void UserFavoriteSongAdded(QNetworkReply* reply, int task_id);
void UserLibrarySongAdded(QNetworkReply* reply, int task_id);
void GetCurrentSongUrlToShare();
void SongUrlToShareReceived(QNetworkReply* reply);
void GetCurrentPlaylistUrlToShare();
void PlaylistUrlToShareReceived(QNetworkReply* reply);
void RemoveCurrentFromPlaylist();
void RemoveCurrentFromFavorites();
void RemoveCurrentFromLibrary();
void SongsRemovedFromFavorites(QNetworkReply* reply, int task_id);
void SongsRemovedFromLibrary(QNetworkReply* reply, int task_id);
void StreamMarked(QNetworkReply* reply);
void SongMarkedAsComplete(QNetworkReply* reply);
void RequestSslErrors(const QList<QSslError>& errors);
void Homepage();
private:
void EnsureMenuCreated();
void EnsureItemsCreated();
void RemoveItems();
void EnsureConnected();
void ClearSearchResults();
// Create a playlist item, with data set as excepted. Doesn't fill the item
// with songs rows.
QStandardItem* CreatePlaylistItem(const QString& playlist_name,
int playlist_id);
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, 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. 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);
int ExtractSongId(
const QUrl& url); // Returns 0 if url is not a Grooveshark url
// Convenient function for extracting basic playlist info (only 'id' and
// 'name': QStandardItem still need to be created), and sort them by name
QList<PlaylistInfo> ExtractPlaylistInfo(const QVariantMap& result);
void ResetSessionId();
// Sort songs alphabetically only if the "sort_alphabetically" option has been
// checked in the preferences settings.
void SortSongsAlphabeticallyIfNeeded(SongList* songs) const;
GroovesharkUrlHandler* url_handler_;
QString pending_search_;
int next_pending_search_id_;
int next_pending_playlist_retrieve_id_;
QSet<int> pending_retrieve_playlists_;
QMap<int, PlaylistInfo> playlists_;
QMap<int, PlaylistInfo> subscribed_playlists_;
QStandardItem* root_;
QStandardItem* search_;
QStandardItem* popular_month_;
QStandardItem* popular_today_;
QStandardItem* stations_;
QStandardItem* grooveshark_radio_;
QStandardItem* favorites_;
// Grooveshark Library (corresponds to Grooveshark 'MyMusic' actually, but
// called 'Library' in the API).
// Nothing to do with Clementine's local library
QStandardItem* library_;
QStandardItem* playlists_parent_;
QStandardItem* subscribed_playlists_parent_;
NetworkAccessManager* network_;
QMenu* context_menu_;
// IDs kept when showing menu, to know what the user has clicked on, to be
// able to perform actions on corresponding items
int current_song_id_;
int current_playlist_id_;
QAction* create_playlist_;
QAction* delete_playlist_;
QAction* rename_playlist_;
QAction* remove_from_playlist_;
QAction* remove_from_favorites_;
QAction* remove_from_library_;
QAction* get_url_to_share_song_;
QAction* get_url_to_share_playlist_;
QList<QAction*> playlistitem_actions_;
SearchBoxWidget* search_box_;
QTimer* search_delay_;
QNetworkReply* last_search_reply_;
QString username_;
QString password_; // In fact, password's md5 hash
QString user_id_;
QString session_id_;
QMap<QString, QVariant> country_;
// The last artists and songs ids th users has listened to. Used for autoplay
QList<int> last_artists_ids_;
QList<int> last_songs_ids_;
QByteArray api_key_;
LoginState login_state_;
// Tasks' ids: we need to keep them in mind to be able to update task status
// on each step
int task_popular_id_;
int task_playlists_id_;
int task_search_id_;
static const char* kUrl;
static const char* kUrlCover;
static const char* kHomepage;
static const int kSongSearchLimit;
static const int kSongSimpleSearchLimit;
static const int kAlbumSearchLimit;
static const int kSearchDelayMsec;
static const char* kApiKey;
static const char* kApiSecret;
};
#endif // INTERNET_GROOVESHARK_GROOVESHARKSERVICE_H_

View File

@ -1,131 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, 2014, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.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 "groovesharksettingspage.h"
#include "ui_groovesharksettingspage.h"
#include <QMessageBox>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QSettings>
#include <QtDebug>
#include "groovesharkservice.h"
#include "internet/core/internetmodel.h"
#include "core/logging.h"
#include "core/network.h"
#include "ui/iconloader.h"
GroovesharkSettingsPage::GroovesharkSettingsPage(SettingsDialog* dialog)
: SettingsPage(dialog),
ui_(new Ui_GroovesharkSettingsPage),
service_(InternetModel::Service<GroovesharkService>()),
validated_(false) {
ui_->setupUi(this);
setWindowIcon(QIcon(":/providers/grooveshark.png"));
connect(ui_->login, SIGNAL(clicked()), SLOT(Login()));
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login()));
connect(service_, SIGNAL(LoginFinished(bool)), SLOT(LoginFinished(bool)));
ui_->login_state->AddCredentialField(ui_->username);
ui_->login_state->AddCredentialField(ui_->password);
ui_->login_state->AddCredentialGroup(ui_->account_group);
}
GroovesharkSettingsPage::~GroovesharkSettingsPage() { delete ui_; }
void GroovesharkSettingsPage::Login() {
if (service_->IsLoggedIn()) {
return;
}
ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
service_->Login(ui_->username->text(), ui_->password->text());
}
void GroovesharkSettingsPage::Load() {
QSettings s;
s.beginGroup(GroovesharkService::kSettingsGroup);
original_username_ = s.value("username").toString();
ui_->username->setText(original_username_);
validated_ = false;
UpdateLoginState();
ui_->sort_alphabetically->setChecked(s.value("sort_alphabetically").toBool());
}
void GroovesharkSettingsPage::Save() {
QSettings s;
s.beginGroup(GroovesharkService::kSettingsGroup);
s.setValue("username", ui_->username->text());
s.setValue("sessionid", service_->session_id());
const bool old_sort_value = s.value("sort_alphabetically").toBool();
const bool new_sort_value = ui_->sort_alphabetically->isChecked();
if (old_sort_value != new_sort_value) {
s.setValue("sort_alphabetically", new_sort_value);
service_->RefreshItems();
}
}
void GroovesharkSettingsPage::LoginFinished(bool success) {
validated_ = success;
Save();
UpdateLoginState();
}
void GroovesharkSettingsPage::UpdateLoginState() {
const bool logged_in = service_->IsLoggedIn();
ui_->login_state->SetLoggedIn(
logged_in ? LoginStateWidget::LoggedIn : LoginStateWidget::LoggedOut,
ui_->username->text());
ui_->login_state->SetAccountTypeVisible(!logged_in);
switch (service_->login_state()) {
case GroovesharkService::LoginState_NoPremium:
ui_->login_state->SetAccountTypeText(
tr("You do not have a Grooveshark Anywhere account."));
break;
case GroovesharkService::LoginState_AuthFailed:
ui_->login_state->SetAccountTypeText(
tr("Your username or password was incorrect."));
break;
default:
ui_->login_state->SetAccountTypeText(
tr("A Grooveshark Anywhere account is required."));
break;
}
}
void GroovesharkSettingsPage::Logout() {
service_->Logout();
UpdateLoginState();
}

View File

@ -1,56 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.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 INTERNET_GROOVESHARK_GROOVESHARKSETTINGSPAGE_H_
#define INTERNET_GROOVESHARK_GROOVESHARKSETTINGSPAGE_H_
#include "ui/settingspage.h"
class NetworkAccessManager;
class Ui_GroovesharkSettingsPage;
class GroovesharkService;
class GroovesharkSettingsPage : public SettingsPage {
Q_OBJECT
public:
explicit GroovesharkSettingsPage(SettingsDialog* dialog);
~GroovesharkSettingsPage();
void Load();
void Save();
private slots:
void Login();
void LoginFinished(bool success);
void Logout();
private:
void UpdateLoginState();
private:
Ui_GroovesharkSettingsPage* ui_;
GroovesharkService* service_;
bool validated_;
QString original_username_;
QString original_password_;
};
#endif // INTERNET_GROOVESHARK_GROOVESHARKSETTINGSPAGE_H_

View File

@ -1,128 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GroovesharkSettingsPage</class>
<widget class="QWidget" name="GroovesharkSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>184</height>
</rect>
</property>
<property name="windowTitle">
<string>Grooveshark</string>
</property>
<property name="toolTip">
<string>By default, Grooveshark sorts songs on date added</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="LoginStateWidget" name="login_state" native="true"/>
</item>
<item>
<widget class="QGroupBox" name="account_group">
<property name="title">
<string>Account details</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="login_container" native="true">
<property name="enabled">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="username_label">
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="username"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="password_label">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="login">
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="preferences_group">
<property name="title">
<string>Preferences</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QCheckBox" name="sort_alphabetically">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Sort playlists songs alphabetically</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LoginStateWidget</class>
<extends>QWidget</extends>
<header>widgets/loginstatewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../data/data.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,82 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.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 "groovesharkurlhandler.h"
#include <QTimer>
#include "groovesharkservice.h"
#include "core/logging.h"
GroovesharkUrlHandler::GroovesharkUrlHandler(GroovesharkService* service,
QObject* parent)
: UrlHandler(parent),
service_(service),
timer_mark_stream_key_(new QTimer(this)) {
// We have to warn Grooveshark when user has listened for more than 30
// seconds of a song, and when it ends. I guess this is used by Grooveshark
// for statistics and user history.
// To do this, we have TrackAboutToEnd method, and timer_mark_stream_key_
// timer.
// It is not perfect, as we may call Grooveshark MarkStreamKeyOver30Secs even
// if user hasn't actually listen to 30 seconds (e.g. stream set to pause
// state) but this is not a big deal and it should be accurate enough anyway.
timer_mark_stream_key_->setInterval(30000);
timer_mark_stream_key_->setSingleShot(true);
connect(timer_mark_stream_key_, SIGNAL(timeout()),
SLOT(MarkStreamKeyOver30Secs()));
}
UrlHandler::LoadResult GroovesharkUrlHandler::StartLoading(const QUrl& url) {
qint64 length_nanosec = 0;
QUrl streaming_url;
QStringList ids = url.toString().remove("grooveshark://").split("/");
if (ids.size() < 3) {
qLog(Error) << "Invalid grooveshark URL: " << url.toString();
qLog(Error) << "Should be grooveshark://artist_id/album_id/song_id";
} else {
last_artist_id_ = ids[0];
last_album_id_ = ids[1];
last_song_id_ = ids[2];
streaming_url = service_->GetStreamingUrlFromSongId(
last_song_id_, last_artist_id_, &last_server_id_, &last_stream_key_,
&length_nanosec);
qLog(Debug) << "Grooveshark Streaming URL: " << streaming_url;
timer_mark_stream_key_->start();
}
return LoadResult(url, LoadResult::TrackAvailable, streaming_url,
length_nanosec);
}
void GroovesharkUrlHandler::TrackAboutToEnd() {
if (timer_mark_stream_key_->isActive()) {
timer_mark_stream_key_->stop();
return;
}
service_->MarkSongComplete(last_song_id_, last_stream_key_, last_server_id_);
}
void GroovesharkUrlHandler::TrackSkipped() { timer_mark_stream_key_->stop(); }
void GroovesharkUrlHandler::MarkStreamKeyOver30Secs() {
service_->MarkStreamKeyOver30Secs(last_stream_key_, last_server_id_);
}

View File

@ -1,52 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.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 INTERNET_GROOVESHARK_GROOVESHARKURLHANDLER_H_
#define INTERNET_GROOVESHARK_GROOVESHARKURLHANDLER_H_
#include "core/urlhandler.h"
class GroovesharkService;
class QTimer;
class GroovesharkUrlHandler : public UrlHandler {
Q_OBJECT
public:
GroovesharkUrlHandler(GroovesharkService* service, QObject* parent);
QString scheme() const { return "grooveshark"; }
QIcon icon() const { return QIcon(":providers/grooveshark.png"); }
LoadResult StartLoading(const QUrl& url);
void TrackAboutToEnd();
void TrackSkipped();
private slots:
void MarkStreamKeyOver30Secs();
private:
GroovesharkService* service_;
QTimer* timer_mark_stream_key_;
QString last_artist_id_;
QString last_album_id_;
QString last_song_id_;
QString last_server_id_;
QString last_stream_key_;
};
#endif // INTERNET_GROOVESHARK_GROOVESHARKURLHANDLER_H_

View File

@ -398,15 +398,7 @@ int main(int argc, char* argv[]) {
Q_INIT_RESOURCE(data);
Q_INIT_RESOURCE(translations);
// Grooveshark uses GoDaddy to sign its SSL certificates, which are in turn
// signed by a ValiCert CA. This CA certificate isn't installed by default
// in Windows, it's only added by windows update, or manually browsing to a
// website with a certificate signed by ValiCert. Here we explicitly add
// that CA to the default list used by QSslSocket, so it always works in
// Clementine.
QSslSocket::addDefaultCaCertificates(
QSslCertificate::fromPath(":/grooveshark-valicert-ca.pem", QSsl::Pem));
// Do the same for SoundCloud, whose certificate is missing on OS X.
// Add root CA cert for SoundCloud, whose certificate is missing on OS X.
QSslSocket::addDefaultCaCertificates(
QSslCertificate::fromPath(":/soundcloud-ca.pem", QSsl::Pem));

View File

@ -36,7 +36,6 @@
#include "engines/gstengine.h"
#include "globalsearch/globalsearchsettingspage.h"
#include "internet/digitally/digitallyimportedsettingspage.h"
#include "internet/grooveshark/groovesharksettingspage.h"
#include "internet/core/internetshowsettingspage.h"
#include "internet/magnatune/magnatunesettingspage.h"
#include "internet/soundcloud/soundcloudsettingspage.h"
@ -168,8 +167,6 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams,
AddPage(Page_Lastfm, new LastFMSettingsPage(this), providers);
#endif
AddPage(Page_Grooveshark, new GroovesharkSettingsPage(this), providers);
#ifdef HAVE_GOOGLE_DRIVE
AddPage(Page_GoogleDrive, new GoogleDriveSettingsPage(this), providers);
#endif

View File

@ -68,7 +68,6 @@ class SettingsDialog : public QDialog {
Page_Notifications,
Page_Library,
Page_Lastfm,
Page_Grooveshark,
Page_SoundCloud,
Page_Spotify,
Page_Magnatune,