diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6eac41b84..ea273870e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -277,6 +277,7 @@ set(SOURCES songinfo/songinfotextview.cpp songinfo/songinfoview.cpp songinfo/songplaystats.cpp + songinfo/twitterartistinfo.cpp songinfo/ultimatelyricsprovider.cpp songinfo/ultimatelyricsreader.cpp @@ -529,6 +530,7 @@ set(HEADERS songinfo/songinfotextview.h songinfo/songinfoview.h songinfo/songplaystats.h + songinfo/twitterartistinfo.h songinfo/ultimatelyricsprovider.h songinfo/ultimatelyricsreader.h diff --git a/src/songinfo/artistinfoview.cpp b/src/songinfo/artistinfoview.cpp index 6c13bea14..c09c7b5cd 100644 --- a/src/songinfo/artistinfoview.cpp +++ b/src/songinfo/artistinfoview.cpp @@ -19,6 +19,7 @@ #include "echonestbiographies.h" #include "echonestimages.h" #include "songinfofetcher.h" +#include "twitterartistinfo.h" #include "widgets/prettyimageview.h" #ifdef HAVE_LIBLASTFM @@ -31,6 +32,7 @@ ArtistInfoView::ArtistInfoView(QWidget *parent) { fetcher_->AddProvider(new EchoNestBiographies); fetcher_->AddProvider(new EchoNestImages); + fetcher_->AddProvider(new TwitterArtistInfo); #ifdef HAVE_LIBLASTFM fetcher_->AddProvider(new EchoNestSimilarArtists); fetcher_->AddProvider(new EchoNestTags); diff --git a/src/songinfo/twitterartistinfo.cpp b/src/songinfo/twitterartistinfo.cpp new file mode 100644 index 000000000..14975a134 --- /dev/null +++ b/src/songinfo/twitterartistinfo.cpp @@ -0,0 +1,97 @@ +#include "twitterartistinfo.h" + +#include + +#include + +#include "core/closure.h" +#include "songinfotextview.h" + +const char* TwitterArtistInfo::kTwitterBucket = "id:twitter"; +const char* TwitterArtistInfo::kTwitterTimelineUrl = + "https://api.twitter.com/1/statuses/user_timeline.json?include_rts=1&count=10&screen_name=%1"; + +TwitterArtistInfo::TwitterArtistInfo() + : network_(this) { +} + +void TwitterArtistInfo::FetchInfo(int id, const Song& metadata) { + Echonest::Artist::SearchParams params; + params.push_back(qMakePair(Echonest::Artist::Name, QVariant(metadata.artist()))); + params.push_back(qMakePair(Echonest::Artist::IdSpace, QVariant(kTwitterBucket))); + + QNetworkReply* reply = Echonest::Artist::search(params); + NewClosure(reply, SIGNAL(finished()), this, SLOT(ArtistSearchFinished(QNetworkReply*, int)), reply, id); +} + +void TwitterArtistInfo::ArtistSearchFinished(QNetworkReply* reply, int id) { + reply->deleteLater(); + try { + Echonest::Artists artists = Echonest::Artist::parseSearch(reply); + if (artists.isEmpty()) { + qLog(Debug) << "Failed to find artist in echonest"; + return; + } + + const Echonest::Artist& artist = artists[0]; + QString twitter_id; + foreach (const Echonest::ForeignId& id, artist.foreignIds()) { + if (id.catalog == "twitter") { + twitter_id = id.foreign_id; + break; + } + } + + if (twitter_id.isEmpty()) { + qLog(Debug) << "Failed to fetch Twitter foreign id for artist"; + return; + } + + QStringList split = twitter_id.split(':'); + if (split.count() != 3) { + qLog(Debug) << "Funky twitter id:" << twitter_id; + return; + } + FetchUserTimeline(split[2], id); + } catch (Echonest::ParseError& e) { + qLog(Error) << "Error parsing echonest reply:" << e.errorType() << e.what(); + } +} + +void TwitterArtistInfo::FetchUserTimeline(const QString& twitter_id, int id) { + QUrl url(QString(kTwitterTimelineUrl).arg(twitter_id)); + QNetworkReply* reply = network_.get(QNetworkRequest(url)); + NewClosure(reply, SIGNAL(finished()), this, + SLOT(UserTimelineRequestFinished(QNetworkReply*, QString, int)), reply, twitter_id, id); +} + +void TwitterArtistInfo::UserTimelineRequestFinished(QNetworkReply* reply, const QString& twitter_id, int id) { + QJson::Parser parser; + bool ok = false; + QVariant result = parser.parse(reply, &ok); + if (!ok) { + qLog(Error) << "Error parsing Twitter reply"; + return; + } + CollapsibleInfoPane::Data data; + data.type_ = CollapsibleInfoPane::Data::Type_Biography; + data.id_ = "twitter/" + twitter_id; + data.title_ = QString("Twitter (%1)").arg(twitter_id); + + QString html; + QVariantList tweets = result.toList(); + foreach (const QVariant& v, tweets) { + QVariantMap tweet = v.toMap(); + QString text = tweet["text"].toString(); + html += "
"; + html += Qt::escape(text); + html += "
"; + } + + SongInfoTextView* text_view = new SongInfoTextView; + text_view->SetHtml(html); + data.contents_ = text_view; + + emit InfoReady(id, data); + emit Finished(id); +} diff --git a/src/songinfo/twitterartistinfo.h b/src/songinfo/twitterartistinfo.h new file mode 100644 index 000000000..dc518faac --- /dev/null +++ b/src/songinfo/twitterartistinfo.h @@ -0,0 +1,29 @@ +#ifndef TWITTERARTISTINFO_H +#define TWITTERARTISTINFO_H + +#include "songinfoprovider.h" + +#include "core/network.h" + +class TwitterArtistInfo : public SongInfoProvider { + Q_OBJECT + public: + TwitterArtistInfo(); + + void FetchInfo(int id, const Song& metadata); + + private slots: + void ArtistSearchFinished(QNetworkReply* reply, int id); + void UserTimelineRequestFinished( + QNetworkReply* reply, const QString& twitter_id, int id); + + private: + void FetchUserTimeline(const QString& twitter_id, int id); + + NetworkAccessManager network_; + + static const char* kTwitterBucket; + static const char* kTwitterTimelineUrl; +}; + +#endif