mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-19 04:50:16 +01:00
Add Grooveshark autoplay radio
This commit is contained in:
parent
91b4cfcb68
commit
3f2faf818a
@ -21,9 +21,17 @@
|
||||
#include "core/logging.h"
|
||||
#include "internet/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) {
|
||||
}
|
||||
|
||||
@ -37,7 +45,12 @@ QByteArray GroovesharkRadio::Save() const {
|
||||
PlaylistItemList GroovesharkRadio::Generate() {
|
||||
PlaylistItemList items;
|
||||
if (first_time_) {
|
||||
Song song = service_->StartAutoplayTag(tag_id_, autoplay_state_);
|
||||
Song song;
|
||||
if (use_tag_) {
|
||||
song = service_->StartAutoplayTag(tag_id_, autoplay_state_);
|
||||
} else {
|
||||
song = service_->StartAutoplay(autoplay_state_);
|
||||
}
|
||||
PlaylistItemPtr playlist_item = PlaylistItemPtr(new InternetPlaylistItem(service_, song));
|
||||
items << playlist_item;
|
||||
first_time_ = false;
|
||||
|
@ -15,8 +15,8 @@
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GROOVESHARKDYNAMICPLAYLIST_H
|
||||
#define GROOVESHARKDYNAMICPLAYLIST_H
|
||||
#ifndef GROOVESHARKRADIO_H
|
||||
#define GROOVESHARKRADIO_H
|
||||
|
||||
#include "smartplaylists/generator.h"
|
||||
|
||||
@ -25,7 +25,10 @@ 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
|
||||
GroovesharkRadio(GroovesharkService* service);
|
||||
|
||||
QString type() const { return "Grooveshark"; }
|
||||
void Load(const QByteArray& data);
|
||||
@ -37,9 +40,12 @@ public:
|
||||
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 // GROOVESHARKDYNAMICPLAYLIST_H
|
||||
#endif // GROOVESHARKRADIO_H
|
||||
|
@ -89,6 +89,7 @@ GroovesharkService::GroovesharkService(InternetModel *parent)
|
||||
popular_month_(NULL),
|
||||
popular_today_(NULL),
|
||||
stations_(NULL),
|
||||
grooveshark_radio_(NULL),
|
||||
favorites_(NULL),
|
||||
subscribed_playlists_divider_(NULL),
|
||||
network_(new NetworkAccessManager(this)),
|
||||
@ -317,7 +318,7 @@ void GroovesharkService::InitCountry() {
|
||||
country_ = ExtractResult(reply_country);
|
||||
}
|
||||
|
||||
QUrl GroovesharkService::GetStreamingUrlFromSongId(const QString& song_id,
|
||||
QUrl GroovesharkService::GetStreamingUrlFromSongId(const QString& song_id, const QString& artist_id,
|
||||
QString* server_id, QString* stream_key, qint64* length_nanosec) {
|
||||
QList<Param> parameters;
|
||||
|
||||
@ -333,6 +334,14 @@ QUrl GroovesharkService::GetStreamingUrlFromSongId(const QString& song_id,
|
||||
stream_key->clear();
|
||||
stream_key->append(result["StreamKey"].toString());
|
||||
*length_nanosec = result["uSecs"].toLongLong() * 1000;
|
||||
// Keep in mind that user has request to listen to this song
|
||||
last_songs_ids_.append(song_id.toInt());
|
||||
last_artists_ids_.append(artist_id.toInt());
|
||||
// If we have enough ids, remove the old ones
|
||||
if (last_songs_ids_.size() > 100)
|
||||
last_songs_ids_.removeFirst();
|
||||
if (last_artists_ids_.size() > 100)
|
||||
last_artists_ids_.removeFirst();
|
||||
|
||||
return QUrl(result["url"].toString());
|
||||
}
|
||||
@ -408,13 +417,15 @@ void GroovesharkService::Authenticated() {
|
||||
void GroovesharkService::Logout() {
|
||||
ResetSessionId();
|
||||
root_->removeRows(0, root_->rowCount());
|
||||
// 'search', 'favorites' and 'popular' items were root's children, and have
|
||||
// 'search', 'favorites', 'popular', ... items were root's children, and have
|
||||
// been deleted: we should update these now invalid pointers
|
||||
search_ = NULL;
|
||||
popular_month_ = NULL;
|
||||
popular_today_ = NULL;
|
||||
favorites_ = NULL;
|
||||
subscribed_playlists_divider_ = NULL;
|
||||
stations_ = NULL;
|
||||
grooveshark_radio_ = NULL;
|
||||
playlists_.clear();
|
||||
subscribed_playlists_.clear();
|
||||
}
|
||||
@ -534,6 +545,11 @@ void GroovesharkService::EnsureItemsCreated() {
|
||||
stations_->setData(true, InternetModel::Role_CanLazyLoad);
|
||||
root_->appendRow(stations_);
|
||||
|
||||
grooveshark_radio_ = new QStandardItem(QIcon(":last.fm/icon_radio.png"), tr("Grooveshark radio"));
|
||||
grooveshark_radio_->setToolTip(tr("Listen to Grooveshark songs based on what you've listened to previously"));
|
||||
grooveshark_radio_->setData(InternetModel::Type_SmartPlaylist, InternetModel::Role_Type);
|
||||
root_->appendRow(grooveshark_radio_);
|
||||
|
||||
QStandardItem* playlists_divider = new QStandardItem(tr("Playlists"));
|
||||
playlists_divider->setData(true, InternetModel::Role_IsDivider);
|
||||
root_->appendRow(playlists_divider);
|
||||
@ -831,6 +847,27 @@ Song GroovesharkService::StartAutoplayTag(int tag_id, QVariantMap& autoplay_stat
|
||||
return ExtractSong(result["nextSong"].toMap());
|
||||
}
|
||||
|
||||
Song GroovesharkService::StartAutoplay(QVariantMap& autoplay_state) {
|
||||
QList<Param> parameters;
|
||||
QVariantList artists_ids_qvariant;
|
||||
foreach (int artist_id, last_artists_ids_) {
|
||||
artists_ids_qvariant << QVariant(artist_id);
|
||||
}
|
||||
QVariantList songs_ids_qvariant;
|
||||
foreach (int song_id, last_songs_ids_) {
|
||||
songs_ids_qvariant << QVariant(song_id);
|
||||
}
|
||||
parameters << Param("artistIDs", artists_ids_qvariant)
|
||||
<< Param("songIDs", songs_ids_qvariant);
|
||||
QNetworkReply* reply = CreateRequest("startAutoplay", 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);
|
||||
@ -909,8 +946,18 @@ GeneratorPtr GroovesharkService::CreateGenerator(QStandardItem* 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));
|
||||
|
||||
if (item == grooveshark_radio_) {
|
||||
if (last_artists_ids_.isEmpty()) {
|
||||
QMessageBox::warning(NULL, tr("Error"),
|
||||
tr("To start Grooveshark radio, you should first listen to few other Grooveshark songs"));
|
||||
return ret;
|
||||
}
|
||||
ret = GeneratorPtr(new GroovesharkRadio(this));
|
||||
} else {
|
||||
int tag_id = item->data(Role_UserPlaylistId).toInt();
|
||||
ret = GeneratorPtr(new GroovesharkRadio(this ,tag_id));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1385,7 +1432,9 @@ Song GroovesharkService::ExtractSong(const QVariantMap& result_song) {
|
||||
Song song;
|
||||
int song_id = result_song["SongID"].toInt();
|
||||
QString song_name = result_song["SongName"].toString();
|
||||
int artist_id = result_song["ArtistID"].toInt();
|
||||
QString artist_name = result_song["ArtistName"].toString();
|
||||
int album_id = result_song["AlbumID"].toInt();
|
||||
QString album_name = result_song["AlbumName"].toString();
|
||||
QString cover = result_song["CoverArtFilename"].toString();
|
||||
song.Init(song_name, artist_name, album_name, 0);
|
||||
@ -1393,7 +1442,8 @@ Song GroovesharkService::ExtractSong(const QVariantMap& result_song) {
|
||||
// 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));
|
||||
// URL is grooveshark://artist_id/album_id/song_id
|
||||
song.set_url(QString("grooveshark://%1/%2/%3").arg(artist_id).arg(album_id).arg(song_id));
|
||||
|
||||
return song;
|
||||
}
|
||||
@ -1422,7 +1472,10 @@ QList<int> GroovesharkService::ExtractSongsIds(const QList<QUrl>& urls) {
|
||||
|
||||
int GroovesharkService::ExtractSongId(const QUrl& url) {
|
||||
if (url.scheme() == "grooveshark") {
|
||||
return url.authority().toInt();
|
||||
QStringList ids = url.toString().remove("grooveshark://").split("/");
|
||||
if (ids.size() == 3)
|
||||
// Returns the third id: song id
|
||||
return ids[2].toInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class GroovesharkService : public InternetService {
|
||||
|
||||
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,
|
||||
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);
|
||||
@ -97,6 +97,7 @@ class GroovesharkService : public InternetService {
|
||||
// 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);
|
||||
@ -230,6 +231,7 @@ class GroovesharkService : public InternetService {
|
||||
QStandardItem* popular_month_;
|
||||
QStandardItem* popular_today_;
|
||||
QStandardItem* stations_;
|
||||
QStandardItem* grooveshark_radio_;
|
||||
QStandardItem* favorites_;
|
||||
QStandardItem* subscribed_playlists_divider_;
|
||||
|
||||
@ -254,6 +256,9 @@ class GroovesharkService : public InternetService {
|
||||
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_;
|
||||
|
@ -40,14 +40,23 @@ GroovesharkUrlHandler::GroovesharkUrlHandler(GroovesharkService* service, QObjec
|
||||
}
|
||||
|
||||
UrlHandler::LoadResult GroovesharkUrlHandler::StartLoading(const QUrl& url) {
|
||||
qint64 length_nanosec;
|
||||
last_song_id_ = url.toString().remove("grooveshark://");
|
||||
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];
|
||||
|
||||
QUrl streaming_url = service_->GetStreamingUrlFromSongId(last_song_id_,
|
||||
&last_server_id_, &last_stream_key_, &length_nanosec);
|
||||
qLog(Debug) << "Grooveshark Streaming URL: " << streaming_url;
|
||||
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();
|
||||
timer_mark_stream_key_->start();
|
||||
}
|
||||
|
||||
return LoadResult(url, LoadResult::TrackAvailable, streaming_url, length_nanosec);
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ private slots:
|
||||
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_;
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user