parent
f13288876f
commit
a8a0f2e4fd
@ -364,9 +364,6 @@ else (USE_SYSTEM_QXT)
|
|||||||
endif (NOT APPLE)
|
endif (NOT APPLE)
|
||||||
endif (USE_SYSTEM_QXT)
|
endif (USE_SYSTEM_QXT)
|
||||||
|
|
||||||
find_path(ECHONEST_INCLUDE_DIRS echonest/echonest_export.h)
|
|
||||||
find_library(ECHONEST_LIBRARIES echonest)
|
|
||||||
|
|
||||||
# Use system gmock if it's available
|
# Use system gmock if it's available
|
||||||
# We need to look for both gmock and gtest
|
# We need to look for both gmock and gtest
|
||||||
find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h)
|
find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h)
|
||||||
|
@ -35,7 +35,6 @@ include_directories(${LIBPROJECTM_INCLUDE_DIRS})
|
|||||||
include_directories(${QTSINGLEAPPLICATION_INCLUDE_DIRS})
|
include_directories(${QTSINGLEAPPLICATION_INCLUDE_DIRS})
|
||||||
include_directories(${QTIOCOMPRESSOR_INCLUDE_DIRS})
|
include_directories(${QTIOCOMPRESSOR_INCLUDE_DIRS})
|
||||||
include_directories(${QXT_INCLUDE_DIRS})
|
include_directories(${QXT_INCLUDE_DIRS})
|
||||||
include_directories(${ECHONEST_INCLUDE_DIRS})
|
|
||||||
include_directories(${SHA2_INCLUDE_DIRS})
|
include_directories(${SHA2_INCLUDE_DIRS})
|
||||||
include_directories(${CHROMAPRINT_INCLUDE_DIRS})
|
include_directories(${CHROMAPRINT_INCLUDE_DIRS})
|
||||||
include_directories(${MYGPOQT_INCLUDE_DIRS})
|
include_directories(${MYGPOQT_INCLUDE_DIRS})
|
||||||
@ -301,7 +300,6 @@ set(SOURCES
|
|||||||
songinfo/artistinfoview.cpp
|
songinfo/artistinfoview.cpp
|
||||||
songinfo/collapsibleinfoheader.cpp
|
songinfo/collapsibleinfoheader.cpp
|
||||||
songinfo/collapsibleinfopane.cpp
|
songinfo/collapsibleinfopane.cpp
|
||||||
songinfo/echonestbiographies.cpp
|
|
||||||
songinfo/songinfobase.cpp
|
songinfo/songinfobase.cpp
|
||||||
songinfo/songinfofetcher.cpp
|
songinfo/songinfofetcher.cpp
|
||||||
songinfo/songinfoprovider.cpp
|
songinfo/songinfoprovider.cpp
|
||||||
@ -593,7 +591,6 @@ set(HEADERS
|
|||||||
songinfo/artistinfoview.h
|
songinfo/artistinfoview.h
|
||||||
songinfo/collapsibleinfoheader.h
|
songinfo/collapsibleinfoheader.h
|
||||||
songinfo/collapsibleinfopane.h
|
songinfo/collapsibleinfopane.h
|
||||||
songinfo/echonestbiographies.h
|
|
||||||
songinfo/songinfobase.h
|
songinfo/songinfobase.h
|
||||||
songinfo/songinfofetcher.h
|
songinfo/songinfofetcher.h
|
||||||
songinfo/songinfoprovider.h
|
songinfo/songinfoprovider.h
|
||||||
@ -830,16 +827,12 @@ optional_source(HAVE_LIBLASTFM
|
|||||||
internet/lastfm/lastfmcompat.cpp
|
internet/lastfm/lastfmcompat.cpp
|
||||||
internet/lastfm/lastfmservice.cpp
|
internet/lastfm/lastfmservice.cpp
|
||||||
internet/lastfm/lastfmsettingspage.cpp
|
internet/lastfm/lastfmsettingspage.cpp
|
||||||
songinfo/echonestsimilarartists.cpp
|
|
||||||
songinfo/echonesttags.cpp
|
|
||||||
songinfo/lastfmtrackinfoprovider.cpp
|
songinfo/lastfmtrackinfoprovider.cpp
|
||||||
songinfo/tagwidget.cpp
|
songinfo/tagwidget.cpp
|
||||||
HEADERS
|
HEADERS
|
||||||
covers/lastfmcoverprovider.h
|
covers/lastfmcoverprovider.h
|
||||||
internet/lastfm/lastfmservice.h
|
internet/lastfm/lastfmservice.h
|
||||||
internet/lastfm/lastfmsettingspage.h
|
internet/lastfm/lastfmsettingspage.h
|
||||||
songinfo/echonestsimilarartists.h
|
|
||||||
songinfo/echonesttags.h
|
|
||||||
songinfo/lastfmtrackinfoprovider.h
|
songinfo/lastfmtrackinfoprovider.h
|
||||||
songinfo/tagwidget.h
|
songinfo/tagwidget.h
|
||||||
UI
|
UI
|
||||||
@ -1254,7 +1247,6 @@ target_link_libraries(clementine_lib
|
|||||||
${TAGLIB_LIBRARIES}
|
${TAGLIB_LIBRARIES}
|
||||||
${MYGPOQT_LIBRARIES}
|
${MYGPOQT_LIBRARIES}
|
||||||
${CHROMAPRINT_LIBRARIES}
|
${CHROMAPRINT_LIBRARIES}
|
||||||
${ECHONEST_LIBRARIES}
|
|
||||||
${GOBJECT_LIBRARIES}
|
${GOBJECT_LIBRARIES}
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${GIO_LIBRARIES}
|
${GIO_LIBRARIES}
|
||||||
|
10
src/main.cpp
10
src/main.cpp
@ -73,8 +73,6 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
#include <echonest/Config.h>
|
|
||||||
|
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
@ -398,8 +396,8 @@ int main(int argc, char* argv[]) {
|
|||||||
// Add root CA cert for SoundCloud, whose certificate is missing on OS X.
|
// Add root CA cert for SoundCloud, whose certificate is missing on OS X.
|
||||||
QSslSocket::addDefaultCaCertificates(
|
QSslSocket::addDefaultCaCertificates(
|
||||||
QSslCertificate::fromPath(":/soundcloud-ca.pem", QSsl::Pem));
|
QSslCertificate::fromPath(":/soundcloud-ca.pem", QSsl::Pem));
|
||||||
QSslSocket::addDefaultCaCertificates(
|
QSslSocket::addDefaultCaCertificates(QSslCertificate::fromPath(
|
||||||
QSslCertificate::fromPath(":/Equifax_Secure_Certificate_Authority.pem", QSsl::Pem));
|
":/Equifax_Secure_Certificate_Authority.pem", QSsl::Pem));
|
||||||
|
|
||||||
// Has the user forced a different language?
|
// Has the user forced a different language?
|
||||||
QString override_language = options.language();
|
QString override_language = options.language();
|
||||||
@ -437,10 +435,6 @@ int main(int argc, char* argv[]) {
|
|||||||
Application app;
|
Application app;
|
||||||
app.set_language_name(language);
|
app.set_language_name(language);
|
||||||
|
|
||||||
Echonest::Config::instance()->setAPIKey("DFLFLJBUF4EGTXHIG");
|
|
||||||
Echonest::Config::instance()->setNetworkAccessManager(
|
|
||||||
new NetworkAccessManager);
|
|
||||||
|
|
||||||
// Network proxy
|
// Network proxy
|
||||||
QNetworkProxyFactory::setApplicationProxyFactory(
|
QNetworkProxyFactory::setApplicationProxyFactory(
|
||||||
NetworkProxyFactory::Instance());
|
NetworkProxyFactory::Instance());
|
||||||
|
@ -17,25 +17,14 @@
|
|||||||
|
|
||||||
#include "artistinfoview.h"
|
#include "artistinfoview.h"
|
||||||
|
|
||||||
#include "songinfo/echonestbiographies.h"
|
|
||||||
#include "songinfo/songinfofetcher.h"
|
#include "songinfo/songinfofetcher.h"
|
||||||
#include "songinfo/songkickconcerts.h"
|
#include "songinfo/songkickconcerts.h"
|
||||||
#include "songinfo/spotifyimages.h"
|
#include "songinfo/spotifyimages.h"
|
||||||
#include "widgets/prettyimageview.h"
|
#include "widgets/prettyimageview.h"
|
||||||
|
|
||||||
#ifdef HAVE_LIBLASTFM
|
|
||||||
#include "echonestsimilarartists.h"
|
|
||||||
#include "echonesttags.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ArtistInfoView::ArtistInfoView(QWidget* parent) : SongInfoBase(parent) {
|
ArtistInfoView::ArtistInfoView(QWidget* parent) : SongInfoBase(parent) {
|
||||||
fetcher_->AddProvider(new EchoNestBiographies);
|
|
||||||
fetcher_->AddProvider(new SongkickConcerts);
|
fetcher_->AddProvider(new SongkickConcerts);
|
||||||
fetcher_->AddProvider(new SpotifyImages);
|
fetcher_->AddProvider(new SpotifyImages);
|
||||||
#ifdef HAVE_LIBLASTFM
|
|
||||||
fetcher_->AddProvider(new EchoNestSimilarArtists);
|
|
||||||
fetcher_->AddProvider(new EchoNestTags);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtistInfoView::~ArtistInfoView() {}
|
ArtistInfoView::~ArtistInfoView() {}
|
||||||
|
@ -1,123 +0,0 @@
|
|||||||
/* This file is part of Clementine.
|
|
||||||
Copyright 2010, 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 "echonestbiographies.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <echonest/Artist.h>
|
|
||||||
|
|
||||||
#include "songinfotextview.h"
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "ui/iconloader.h"
|
|
||||||
|
|
||||||
struct EchoNestBiographies::Request {
|
|
||||||
Request(int id) : id_(id), artist_(new Echonest::Artist) {}
|
|
||||||
|
|
||||||
int id_;
|
|
||||||
std::unique_ptr<Echonest::Artist> artist_;
|
|
||||||
};
|
|
||||||
|
|
||||||
EchoNestBiographies::EchoNestBiographies() {
|
|
||||||
site_relevance_["wikipedia"] = 100;
|
|
||||||
site_relevance_["lastfm"] = 60;
|
|
||||||
site_relevance_["amazon"] = 30;
|
|
||||||
|
|
||||||
site_icons_["amazon"] = IconLoader::Load("amazon", IconLoader::Provider);
|
|
||||||
site_icons_["aol"] = IconLoader::Load("aol", IconLoader::Provider);
|
|
||||||
site_icons_["cdbaby"] = IconLoader::Load("cdbaby", IconLoader::Provider);
|
|
||||||
site_icons_["lastfm"] = IconLoader::Load("as", IconLoader::Lastfm);
|
|
||||||
site_icons_["mog"] = IconLoader::Load("mog", IconLoader::Provider);
|
|
||||||
site_icons_["mtvmusic"] = IconLoader::Load("mtvmusic", IconLoader::Provider);
|
|
||||||
site_icons_["myspace"] = IconLoader::Load("myspace", IconLoader::Provider);
|
|
||||||
site_icons_["wikipedia"] = IconLoader::Load("wikipedia", IconLoader::Provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EchoNestBiographies::FetchInfo(int id, const Song& metadata) {
|
|
||||||
std::shared_ptr<Request> request(new Request(id));
|
|
||||||
request->artist_->setName(metadata.artist());
|
|
||||||
|
|
||||||
QNetworkReply* reply = request->artist_->fetchBiographies();
|
|
||||||
connect(reply, SIGNAL(finished()), SLOT(RequestFinished()));
|
|
||||||
requests_[reply] = request;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EchoNestBiographies::RequestFinished() {
|
|
||||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
|
||||||
if (!reply || !requests_.contains(reply)) return;
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
RequestPtr request = requests_.take(reply);
|
|
||||||
|
|
||||||
try {
|
|
||||||
request->artist_->parseProfile(reply);
|
|
||||||
}
|
|
||||||
catch (Echonest::ParseError e) {
|
|
||||||
qLog(Warning) << "Error parsing echonest reply:" << e.errorType()
|
|
||||||
<< e.what();
|
|
||||||
}
|
|
||||||
|
|
||||||
QSet<QString> already_seen;
|
|
||||||
|
|
||||||
for (const Echonest::Biography& bio : request->artist_->biographies()) {
|
|
||||||
QString canonical_site = bio.site().toLower();
|
|
||||||
canonical_site.replace(QRegExp("[^a-z]"), "");
|
|
||||||
|
|
||||||
if (already_seen.contains(canonical_site)) continue;
|
|
||||||
already_seen.insert(canonical_site);
|
|
||||||
|
|
||||||
CollapsibleInfoPane::Data data;
|
|
||||||
data.id_ = "echonest/bio/" + bio.site();
|
|
||||||
data.title_ = tr("Biography from %1").arg(bio.site());
|
|
||||||
data.type_ = CollapsibleInfoPane::Data::Type_Biography;
|
|
||||||
|
|
||||||
if (site_relevance_.contains(canonical_site))
|
|
||||||
data.relevance_ = site_relevance_[canonical_site];
|
|
||||||
if (site_icons_.contains(canonical_site))
|
|
||||||
data.icon_ = site_icons_[canonical_site];
|
|
||||||
|
|
||||||
SongInfoTextView* editor = new SongInfoTextView;
|
|
||||||
QString text;
|
|
||||||
// Add a link to the bio webpage at the top if we have one
|
|
||||||
if (!bio.url().isEmpty()) {
|
|
||||||
QString bio_url = bio.url().toEncoded();
|
|
||||||
if (bio.site() == "facebook") {
|
|
||||||
bio_url.replace("graph.facebook.com", "www.facebook.com");
|
|
||||||
}
|
|
||||||
text += "<p><a href=\"" + bio_url + "\">" +
|
|
||||||
tr("Open in your browser") + "</a></p>";
|
|
||||||
}
|
|
||||||
|
|
||||||
text += bio.text();
|
|
||||||
if (bio.site() == "last.fm") {
|
|
||||||
// Echonest lost formatting and it seems there is currently no plans on
|
|
||||||
// Echonest side for changing this.
|
|
||||||
// But with last.fm, we can guess newlines: " " corresponds to a newline
|
|
||||||
// (this seems to be because on last.fm' website, extra blank is inserted
|
|
||||||
// before <br /> tag, and this blank is kept).
|
|
||||||
// This is tricky, but this make the display nicer for last.fm
|
|
||||||
// biographies.
|
|
||||||
text.replace(" ", "<p>");
|
|
||||||
}
|
|
||||||
editor->SetHtml(text);
|
|
||||||
data.contents_ = editor;
|
|
||||||
|
|
||||||
emit InfoReady(request->id_, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit Finished(request->id_);
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
/* This file is part of Clementine.
|
|
||||||
Copyright 2010, 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 ECHONESTBIOGRAPHIES_H
|
|
||||||
#define ECHONESTBIOGRAPHIES_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "songinfoprovider.h"
|
|
||||||
|
|
||||||
class QNetworkReply;
|
|
||||||
|
|
||||||
class EchoNestBiographies : public SongInfoProvider {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
EchoNestBiographies();
|
|
||||||
|
|
||||||
void FetchInfo(int id, const Song& metadata);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void RequestFinished();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QMap<QString, int> site_relevance_;
|
|
||||||
QMap<QString, QIcon> site_icons_;
|
|
||||||
|
|
||||||
struct Request;
|
|
||||||
typedef std::shared_ptr<Request> RequestPtr;
|
|
||||||
|
|
||||||
QMap<QNetworkReply*, RequestPtr> requests_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ECHONESTBIOGRAPHIES_H
|
|
@ -1,76 +0,0 @@
|
|||||||
/* This file is part of Clementine.
|
|
||||||
Copyright 2010, 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 "echonestsimilarartists.h"
|
|
||||||
#include "tagwidget.h"
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "ui/iconloader.h"
|
|
||||||
|
|
||||||
#include <echonest/Artist.h>
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QVector<QString>);
|
|
||||||
|
|
||||||
void EchoNestSimilarArtists::FetchInfo(int id, const Song& metadata) {
|
|
||||||
using Echonest::Artist;
|
|
||||||
|
|
||||||
Artist::SearchParams params;
|
|
||||||
params << Artist::SearchParamEntry(Artist::Name, metadata.artist());
|
|
||||||
params << Artist::SearchParamEntry(Artist::MinHotttnesss, 0.5);
|
|
||||||
|
|
||||||
QNetworkReply* reply = Echonest::Artist::fetchSimilar(params);
|
|
||||||
connect(reply, SIGNAL(finished()), SLOT(RequestFinished()));
|
|
||||||
requests_[reply] = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EchoNestSimilarArtists::RequestFinished() {
|
|
||||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
|
||||||
if (!reply || !requests_.contains(reply)) return;
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
int id = requests_.take(reply);
|
|
||||||
|
|
||||||
Echonest::Artists artists;
|
|
||||||
try {
|
|
||||||
artists = Echonest::Artist::parseSimilar(reply);
|
|
||||||
}
|
|
||||||
catch (Echonest::ParseError e) {
|
|
||||||
qLog(Warning) << "Error parsing echonest reply:" << e.errorType()
|
|
||||||
<< e.what();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!artists.isEmpty()) {
|
|
||||||
CollapsibleInfoPane::Data data;
|
|
||||||
data.id_ = "echonest/similarartists";
|
|
||||||
data.title_ = tr("Similar artists");
|
|
||||||
data.type_ = CollapsibleInfoPane::Data::Type_Similar;
|
|
||||||
data.icon_ = IconLoader::Load("echonest", IconLoader::Provider);
|
|
||||||
|
|
||||||
TagWidget* widget = new TagWidget(TagWidget::Type_Artists);
|
|
||||||
data.contents_ = widget;
|
|
||||||
|
|
||||||
widget->SetIcon(IconLoader::Load("x-clementine-artist", IconLoader::Base));
|
|
||||||
|
|
||||||
for (const Echonest::Artist& artist : artists) {
|
|
||||||
widget->AddTag(artist.name());
|
|
||||||
if (widget->count() >= 10) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit InfoReady(id, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit Finished(id);
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
/* This file is part of Clementine.
|
|
||||||
Copyright 2010, 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 ECHONESTSIMILARARTISTS_H
|
|
||||||
#define ECHONESTSIMILARARTISTS_H
|
|
||||||
|
|
||||||
#include "songinfoprovider.h"
|
|
||||||
|
|
||||||
class QNetworkReply;
|
|
||||||
|
|
||||||
class EchoNestSimilarArtists : public SongInfoProvider {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
void FetchInfo(int id, const Song& metadata);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void RequestFinished();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QMap<QNetworkReply*, int> requests_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ECHONESTSIMILARARTISTS_H
|
|
@ -1,80 +0,0 @@
|
|||||||
/* This file is part of Clementine.
|
|
||||||
Copyright 2010, 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 "echonesttags.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <echonest/Artist.h>
|
|
||||||
|
|
||||||
#include "tagwidget.h"
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "ui/iconloader.h"
|
|
||||||
|
|
||||||
struct EchoNestTags::Request {
|
|
||||||
Request(int id) : id_(id), artist_(new Echonest::Artist) {}
|
|
||||||
|
|
||||||
int id_;
|
|
||||||
std::unique_ptr<Echonest::Artist> artist_;
|
|
||||||
};
|
|
||||||
|
|
||||||
void EchoNestTags::FetchInfo(int id, const Song& metadata) {
|
|
||||||
std::shared_ptr<Request> request(new Request(id));
|
|
||||||
request->artist_->setName(metadata.artist());
|
|
||||||
|
|
||||||
QNetworkReply* reply = request->artist_->fetchTerms();
|
|
||||||
connect(reply, SIGNAL(finished()), SLOT(RequestFinished()));
|
|
||||||
requests_[reply] = request;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EchoNestTags::RequestFinished() {
|
|
||||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
|
||||||
if (!reply || !requests_.contains(reply)) return;
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
RequestPtr request = requests_.take(reply);
|
|
||||||
|
|
||||||
try {
|
|
||||||
request->artist_->parseProfile(reply);
|
|
||||||
}
|
|
||||||
catch (Echonest::ParseError e) {
|
|
||||||
qLog(Warning) << "Error parsing echonest reply:" << e.errorType()
|
|
||||||
<< e.what();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request->artist_->terms().isEmpty()) {
|
|
||||||
CollapsibleInfoPane::Data data;
|
|
||||||
data.id_ = "echonest/artisttags";
|
|
||||||
data.title_ = tr("Artist tags");
|
|
||||||
data.type_ = CollapsibleInfoPane::Data::Type_Tags;
|
|
||||||
data.icon_ = IconLoader::Load("icon_tag", IconLoader::Lastfm);
|
|
||||||
|
|
||||||
TagWidget* widget = new TagWidget(TagWidget::Type_Tags);
|
|
||||||
data.contents_ = widget;
|
|
||||||
|
|
||||||
widget->SetIcon(data.icon_);
|
|
||||||
|
|
||||||
for (const Echonest::Term& term : request->artist_->terms()) {
|
|
||||||
widget->AddTag(term.name());
|
|
||||||
if (widget->count() >= 10) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit InfoReady(request->id_, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit Finished(request->id_);
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/* This file is part of Clementine.
|
|
||||||
Copyright 2010, 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 ECHONESTTAGS_H
|
|
||||||
#define ECHONESTTAGS_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "songinfoprovider.h"
|
|
||||||
|
|
||||||
class QNetworkReply;
|
|
||||||
|
|
||||||
class EchoNestTags : public SongInfoProvider {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
void FetchInfo(int id, const Song& metadata);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void RequestFinished();
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Request;
|
|
||||||
typedef std::shared_ptr<Request> RequestPtr;
|
|
||||||
|
|
||||||
QMap<QNetworkReply*, RequestPtr> requests_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ECHONESTTAGS_H
|
|
@ -21,9 +21,6 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
#include <echonest/Artist.h>
|
|
||||||
#include <echonest/TypeInformation.h>
|
|
||||||
|
|
||||||
#include <qjson/parser.h>
|
#include <qjson/parser.h>
|
||||||
|
|
||||||
#include "core/closure.h"
|
#include "core/closure.h"
|
||||||
@ -31,77 +28,64 @@
|
|||||||
#include "songkickconcertwidget.h"
|
#include "songkickconcertwidget.h"
|
||||||
#include "ui/iconloader.h"
|
#include "ui/iconloader.h"
|
||||||
|
|
||||||
const char* SongkickConcerts::kSongkickArtistBucket = "songkick";
|
namespace {
|
||||||
const char* SongkickConcerts::kSongkickArtistCalendarUrl =
|
const char* kSongkickArtistCalendarUrl =
|
||||||
"https://api.songkick.com/api/3.0/artists/%1/calendar.json?"
|
"https://api.songkick.com/api/3.0/artists/%1/calendar.json";
|
||||||
"per_page=5&"
|
const char* kSongkickArtistSearchUrl =
|
||||||
"apikey=8rgKfy1WU6IlJFfN";
|
"https://api.songkick.com/api/3.0/search/artists.json";
|
||||||
|
const char* kSongkickApiKey = "8rgKfy1WU6IlJFfN";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
SongkickConcerts::SongkickConcerts() {
|
SongkickConcerts::SongkickConcerts() {
|
||||||
Geolocator* geolocator = new Geolocator;
|
Geolocator* geolocator = new Geolocator;
|
||||||
geolocator->Geolocate();
|
geolocator->Geolocate();
|
||||||
connect(geolocator, SIGNAL(Finished(Geolocator::LatLng)),
|
connect(geolocator, SIGNAL(Finished(Geolocator::LatLng)),
|
||||||
SLOT(GeolocateFinished(Geolocator::LatLng)));
|
SLOT(GeolocateFinished(Geolocator::LatLng)));
|
||||||
NewClosure(geolocator, SIGNAL(Finished(Geolocator::LatLng)), geolocator,
|
connect(geolocator, SIGNAL(Finished(Geolocator::LatLng)), geolocator,
|
||||||
SLOT(deleteLater()));
|
SLOT(deleteLater()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongkickConcerts::FetchInfo(int id, const Song& metadata) {
|
void SongkickConcerts::FetchInfo(int id, const Song& metadata) {
|
||||||
Echonest::Artist::SearchParams params;
|
if (metadata.artist().isEmpty()) {
|
||||||
params.push_back(
|
emit Finished(id);
|
||||||
qMakePair(Echonest::Artist::Name, QVariant(metadata.artist())));
|
return;
|
||||||
qLog(Debug) << "Params:" << params;
|
}
|
||||||
QNetworkReply* reply = Echonest::Artist::search(
|
|
||||||
params,
|
QUrl url(kSongkickArtistSearchUrl);
|
||||||
Echonest::ArtistInformation(Echonest::ArtistInformation::NoInformation,
|
url.addQueryItem("apikey", kSongkickApiKey);
|
||||||
QStringList() << kSongkickArtistBucket));
|
url.addQueryItem("query", metadata.artist());
|
||||||
qLog(Debug) << reply->request().url();
|
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
QNetworkReply* reply = network_.get(request);
|
||||||
NewClosure(reply, SIGNAL(finished()), this,
|
NewClosure(reply, SIGNAL(finished()), this,
|
||||||
SLOT(ArtistSearchFinished(QNetworkReply*, int)), reply, id);
|
SLOT(ArtistSearchFinished(QNetworkReply*, int)), reply, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongkickConcerts::ArtistSearchFinished(QNetworkReply* reply, int id) {
|
void SongkickConcerts::ArtistSearchFinished(QNetworkReply* reply, int id) {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
try {
|
|
||||||
Echonest::Artists artists = Echonest::Artist::parseSearch(reply);
|
QJson::Parser parser;
|
||||||
|
QVariantMap json = parser.parse(reply).toMap();
|
||||||
|
|
||||||
|
QVariantMap results_page = json["resultsPage"].toMap();
|
||||||
|
QVariantMap results = results_page["results"].toMap();
|
||||||
|
QVariantList artists = results["artist"].toList();
|
||||||
|
|
||||||
if (artists.isEmpty()) {
|
if (artists.isEmpty()) {
|
||||||
qLog(Debug) << "Failed to find artist in echonest";
|
|
||||||
emit Finished(id);
|
emit Finished(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Echonest::Artist& artist = artists[0];
|
QVariantMap artist = artists.first().toMap();
|
||||||
const Echonest::ForeignIds& foreign_ids = artist.foreignIds();
|
QString artist_id = artist["id"].toString();
|
||||||
QString songkick_id;
|
|
||||||
for (const Echonest::ForeignId& id : foreign_ids) {
|
|
||||||
if (id.catalog == "songkick") {
|
|
||||||
songkick_id = id.foreign_id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (songkick_id.isEmpty()) {
|
FetchSongkickCalendar(artist_id, id);
|
||||||
qLog(Debug) << "Failed to fetch songkick foreign id for artist";
|
|
||||||
emit Finished(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList split = songkick_id.split(':');
|
|
||||||
if (split.count() != 3) {
|
|
||||||
qLog(Error) << "Weird songkick id";
|
|
||||||
emit Finished(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FetchSongkickCalendar(split[2], id);
|
|
||||||
} catch (Echonest::ParseError& e) {
|
|
||||||
qLog(Error) << "Error parsing echonest reply:" << e.errorType() << e.what();
|
|
||||||
emit Finished(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongkickConcerts::FetchSongkickCalendar(const QString& artist_id, int id) {
|
void SongkickConcerts::FetchSongkickCalendar(const QString& artist_id, int id) {
|
||||||
QUrl url(QString(kSongkickArtistCalendarUrl).arg(artist_id));
|
QUrl url(QString(kSongkickArtistCalendarUrl).arg(artist_id));
|
||||||
|
url.addQueryItem("per_page", "5");
|
||||||
|
url.addQueryItem("apikey", kSongkickApiKey);
|
||||||
qLog(Debug) << url;
|
qLog(Debug) << url;
|
||||||
QNetworkReply* reply = network_.get(QNetworkRequest(url));
|
QNetworkReply* reply = network_.get(QNetworkRequest(url));
|
||||||
NewClosure(reply, SIGNAL(finished()), this,
|
NewClosure(reply, SIGNAL(finished()), this,
|
||||||
|
@ -44,9 +44,6 @@ class SongkickConcerts : public SongInfoProvider {
|
|||||||
|
|
||||||
NetworkAccessManager network_;
|
NetworkAccessManager network_;
|
||||||
Geolocator::LatLng latlng_;
|
Geolocator::LatLng latlng_;
|
||||||
|
|
||||||
static const char* kSongkickArtistBucket;
|
|
||||||
static const char* kSongkickArtistCalendarUrl;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user