Fetch artist images from spotify.
(cherry picked from commit 1304f8898e
)
This commit is contained in:
parent
4bba0fda09
commit
99b07fd707
|
@ -17,49 +17,126 @@
|
||||||
|
|
||||||
#include "echonestimages.h"
|
#include "echonestimages.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <echonest/Artist.h>
|
#include <echonest/Artist.h>
|
||||||
|
#include <qjson/parser.h>
|
||||||
|
|
||||||
|
#include "core/closure.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/network.h"
|
||||||
|
|
||||||
struct EchoNestImages::Request {
|
namespace {
|
||||||
Request(int id) : id_(id), artist_(new Echonest::Artist) {}
|
static const char* kSpotifyBucket = "spotify";
|
||||||
|
static const char* kSpotifyArtistUrl = "https://api.spotify.com/v1/artists/%1";
|
||||||
int id_;
|
|
||||||
std::unique_ptr<Echonest::Artist> artist_;
|
|
||||||
};
|
|
||||||
|
|
||||||
void EchoNestImages::FetchInfo(int id, const Song& metadata) {
|
|
||||||
std::shared_ptr<Request> request(new Request(id));
|
|
||||||
request->artist_->setName(metadata.artist());
|
|
||||||
|
|
||||||
QNetworkReply* reply = request->artist_->fetchImages();
|
|
||||||
connect(reply, SIGNAL(finished()), SLOT(RequestFinished()));
|
|
||||||
requests_[reply] = request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EchoNestImages::RequestFinished() {
|
EchoNestImages::EchoNestImages() : network_(new NetworkAccessManager) {}
|
||||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
|
||||||
if (!reply || !requests_.contains(reply)) return;
|
EchoNestImages::~EchoNestImages() {}
|
||||||
|
|
||||||
|
void EchoNestImages::FetchInfo(int id, const Song& metadata) {
|
||||||
|
Echonest::Artist artist;
|
||||||
|
artist.setName(metadata.artist());
|
||||||
|
|
||||||
|
// Search for images directly on echonest.
|
||||||
|
// This is currently a bit limited as most results are for last.fm urls that
|
||||||
|
// no longer work.
|
||||||
|
QNetworkReply* reply = artist.fetchImages();
|
||||||
|
RegisterReply(reply, id);
|
||||||
|
NewClosure(reply, SIGNAL(finished()), this,
|
||||||
|
SLOT(RequestFinished(QNetworkReply*, int, Echonest::Artist)),
|
||||||
|
reply, id, artist);
|
||||||
|
|
||||||
|
// Also look up the artist id for the spotify API so we can directly request
|
||||||
|
// images from there too.
|
||||||
|
Echonest::Artist::SearchParams params;
|
||||||
|
params.push_back(
|
||||||
|
qMakePair(Echonest::Artist::Name, QVariant(metadata.artist())));
|
||||||
|
QNetworkReply* rosetta_reply = Echonest::Artist::search(
|
||||||
|
params,
|
||||||
|
Echonest::ArtistInformation(Echonest::ArtistInformation::NoInformation,
|
||||||
|
QStringList() << kSpotifyBucket));
|
||||||
|
RegisterReply(rosetta_reply, id);
|
||||||
|
NewClosure(rosetta_reply, SIGNAL(finished()), this,
|
||||||
|
SLOT(IdsFound(QNetworkReply*, int)), rosetta_reply, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoNestImages::RequestFinished(QNetworkReply* reply, int id,
|
||||||
|
Echonest::Artist artist) {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
RequestPtr request = requests_.take(reply);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
request->artist_->parseProfile(reply);
|
artist.parseProfile(reply);
|
||||||
}
|
} catch (Echonest::ParseError e) {
|
||||||
catch (Echonest::ParseError e) {
|
|
||||||
qLog(Warning) << "Error parsing echonest reply:" << e.errorType()
|
qLog(Warning) << "Error parsing echonest reply:" << e.errorType()
|
||||||
<< e.what();
|
<< e.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const Echonest::ArtistImage& image : request->artist_->images()) {
|
for (const Echonest::ArtistImage& image : artist.images()) {
|
||||||
// Echonest still sends these broken URLs for last.fm.
|
// Echonest still sends these broken URLs for last.fm.
|
||||||
if (image.url().authority() != "userserve-ak.last.fm") {
|
if (image.url().authority() != "userserve-ak.last.fm") {
|
||||||
emit ImageReady(request->id_, image.url());
|
emit ImageReady(id, image.url());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
emit Finished(request->id_);
|
|
||||||
|
void EchoNestImages::IdsFound(QNetworkReply* reply, int request_id) {
|
||||||
|
reply->deleteLater();
|
||||||
|
try {
|
||||||
|
Echonest::Artists artists = Echonest::Artist::parseSearch(reply);
|
||||||
|
if (artists.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const Echonest::ForeignIds& foreign_ids = artists.first().foreignIds();
|
||||||
|
for (const Echonest::ForeignId& id : foreign_ids) {
|
||||||
|
if (id.catalog.contains("spotify")) {
|
||||||
|
DoSpotifyImageRequest(id.foreign_id, request_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Echonest::ParseError e) {
|
||||||
|
qLog(Warning) << "Error parsing echonest reply:" << e.errorType()
|
||||||
|
<< e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoNestImages::DoSpotifyImageRequest(const QString& id, int request_id) {
|
||||||
|
QString artist_id = id.split(":").last();
|
||||||
|
QUrl url(QString(kSpotifyArtistUrl).arg(artist_id));
|
||||||
|
QNetworkReply* reply = network_->get(QNetworkRequest(url));
|
||||||
|
RegisterReply(reply, request_id);
|
||||||
|
NewClosure(reply, SIGNAL(finished()), [this, reply, request_id]() {
|
||||||
|
reply->deleteLater();
|
||||||
|
QJson::Parser parser;
|
||||||
|
QVariantMap result = parser.parse(reply).toMap();
|
||||||
|
QVariantList images = result["images"].toList();
|
||||||
|
QList<QPair<QUrl, QSize>> image_urls;
|
||||||
|
for (const QVariant& image : images) {
|
||||||
|
QVariantMap image_result = image.toMap();
|
||||||
|
image_urls.append(qMakePair(image_result["url"].toUrl(),
|
||||||
|
QSize(image_result["width"].toInt(),
|
||||||
|
image_result["height"].toInt())));
|
||||||
|
}
|
||||||
|
// All the images are the same just different sizes; just pick the largest.
|
||||||
|
std::sort(image_urls.begin(), image_urls.end(),
|
||||||
|
[](decltype(image_urls)::const_reference a,
|
||||||
|
decltype(image_urls)::const_reference b) {
|
||||||
|
// Sorted by area ascending.
|
||||||
|
return (a.second.height() * a.second.width()) <
|
||||||
|
(b.second.height() * b.second.width());
|
||||||
|
});
|
||||||
|
emit ImageReady(request_id, image_urls.last().first);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeps track of replies and emits Finished() when all replies associated with
|
||||||
|
// a request are finished with.
|
||||||
|
void EchoNestImages::RegisterReply(QNetworkReply* reply, int id) {
|
||||||
|
replies_.insert(id, reply);
|
||||||
|
NewClosure(reply, SIGNAL(destroyed()), [this, reply, id]() {
|
||||||
|
replies_.remove(id, reply);
|
||||||
|
if (!replies_.contains(id)) {
|
||||||
|
emit Finished(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,24 +20,33 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "songinfoprovider.h"
|
#include <QMultiMap>
|
||||||
|
|
||||||
|
#include <echonest/Artist.h>
|
||||||
|
|
||||||
|
#include "songinfo/songinfoprovider.h"
|
||||||
|
|
||||||
|
class NetworkAccessManager;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
|
|
||||||
class EchoNestImages : public SongInfoProvider {
|
class EchoNestImages : public SongInfoProvider {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
EchoNestImages();
|
||||||
|
virtual ~EchoNestImages();
|
||||||
void FetchInfo(int id, const Song& metadata);
|
void FetchInfo(int id, const Song& metadata);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void RequestFinished();
|
void RequestFinished(QNetworkReply*, int id, Echonest::Artist artist);
|
||||||
|
void IdsFound(QNetworkReply* reply, int id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Request;
|
void DoSpotifyImageRequest(const QString& id, int request_id);
|
||||||
typedef std::shared_ptr<Request> RequestPtr;
|
|
||||||
|
|
||||||
QMap<QNetworkReply*, RequestPtr> requests_;
|
void RegisterReply(QNetworkReply* reply, int id);
|
||||||
|
QMultiMap<int, QNetworkReply*> replies_;
|
||||||
|
std::unique_ptr<NetworkAccessManager> network_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ECHONESTIMAGES_H
|
#endif // ECHONESTIMAGES_H
|
||||||
|
|
Loading…
Reference in New Issue