Add Grooveshark autoplay radio

This commit is contained in:
Arnaud Bienner 2011-11-29 23:52:19 +01:00
parent 91b4cfcb68
commit 3f2faf818a
7 changed files with 714 additions and 674 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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_;

View File

@ -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);
}

View File

@ -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