Add image fetching from wikipedia

#5416
This commit is contained in:
John Maguire 2016-06-28 15:06:46 +01:00
parent 7c0ef13bb7
commit 55af2b1d3b
6 changed files with 187 additions and 2 deletions

View File

@ -7,6 +7,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++0x")
set(SOURCES
core/closure.cpp
core/latch.cpp
core/logging.cpp
core/messagehandler.cpp
core/messagereply.cpp
@ -16,6 +17,7 @@ set(SOURCES
set(HEADERS
core/closure.h
core/latch.h
core/messagehandler.h
core/messagereply.h
core/workerpool.h

View File

@ -0,0 +1,36 @@
/* This file is part of Clementine.
Copyright 2016, John Maguire <john.maguire@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "latch.h"
#include "core/logging.h"
CountdownLatch::CountdownLatch() : count_(0) {}
void CountdownLatch::Wait() {
QMutexLocker l(&mutex_);
++count_;
}
void CountdownLatch::CountDown() {
QMutexLocker l(&mutex_);
Q_ASSERT(count_ > 0);
--count_;
qLog(Debug) << "Decrement:" << count_;
if (count_ == 0) {
emit Done();
}
}

View File

@ -0,0 +1,38 @@
/* This file is part of Clementine.
Copyright 2016, John Maguire <john.maguire@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef CORE_LATCH_H
#define CORE_LATCH_H
#include <QMutex>
#include <QObject>
class CountdownLatch : public QObject {
Q_OBJECT
public:
CountdownLatch();
void Wait();
void CountDown();
signals:
void Done();
private:
QMutex mutex_;
int count_;
};
#endif // CORE_LATCH_H

View File

@ -22,11 +22,18 @@
#include <qjson/parser.h>
#include "core/closure.h"
#include "core/latch.h"
#include "core/logging.h"
#include "core/network.h"
#include "songinfo/songinfotextview.h"
namespace {
const char* kArtistBioUrl = "https://data.clementine-player.org/fetchbio";
const char* kWikipediaImageListUrl =
"https://en.wikipedia.org/w/api.php?action=query&prop=images&format=json";
const char* kWikipediaImageInfoUrl =
"https://en.wikipedia.org/w/"
"api.php?action=query&prop=imageinfo&iiprop=url&format=json";
QString GetLocale() {
QLocale locale;
@ -75,6 +82,105 @@ void ArtistBiography::FetchInfo(int id, const Song& metadata) {
editor->SetHtml(text);
data.contents_ = editor;
emit InfoReady(id, data);
emit Finished(id);
if (url.contains("wikipedia.org")) {
FetchWikipediaImages(id, url);
} else {
emit Finished(id);
}
});
}
namespace {
QStringList ExtractImageTitles(const QVariantMap& json) {
QStringList ret;
for (auto it = json.constBegin(); it != json.constEnd(); ++it) {
if (it.value().type() == QVariant::Map) {
ret.append(ExtractImageTitles(it.value().toMap()));
} else if (it.key() == "images" && it.value().type() == QVariant::List) {
QVariantList images = it.value().toList();
for (QVariant i : images) {
QVariantMap image = i.toMap();
QString image_title = image["title"].toString();
if (!image_title.isEmpty() &&
(
// SVGs tend to be irrelevant icons.
image_title.endsWith(".jpg", Qt::CaseInsensitive) ||
image_title.endsWith(".png", Qt::CaseInsensitive))) {
ret.append(image_title);
}
}
}
}
return ret;
}
QString ExtractImageUrl(const QVariantMap& json) {
for (auto it = json.constBegin(); it != json.constEnd(); ++it) {
if (it.value().type() == QVariant::Map) {
QString r = ExtractImageUrl(it.value().toMap());
if (!r.isEmpty()) {
return r;
}
} else if (it.key() == "imageinfo") {
QVariantList imageinfos = it.value().toList();
QVariantMap imageinfo = imageinfos.first().toMap();
return imageinfo["url"].toString();
}
}
return QString::null;
}
} // namespace
void ArtistBiography::FetchWikipediaImages(int id,
const QString& wikipedia_url) {
QRegExp regex("/wiki/(.*)");
if (regex.indexIn(wikipedia_url) == -1) {
emit Finished(id);
return;
}
QString wiki_title = regex.cap(1);
QUrl url(kWikipediaImageListUrl);
url.addQueryItem("titles", wiki_title);
qLog(Debug) << "Wikipedia images:" << url;
QNetworkRequest request(url);
QNetworkReply* reply = network_->get(request);
NewClosure(reply, SIGNAL(finished()), [this, id, reply]() {
reply->deleteLater();
QJson::Parser parser;
QVariantMap response = parser.parse(reply).toMap();
QStringList image_titles = ExtractImageTitles(response);
CountdownLatch* latch = new CountdownLatch;
NewClosure(latch, SIGNAL(Done()), [this, latch, id]() {
latch->deleteLater();
emit Finished(id);
});
for (const QString& image_title : image_titles) {
latch->Wait();
QUrl url(kWikipediaImageInfoUrl);
url.addQueryItem("titles", image_title);
QNetworkRequest request(url);
QNetworkReply* reply = network_->get(request);
NewClosure(reply, SIGNAL(finished()), [this, id, reply, latch]() {
reply->deleteLater();
QJson::Parser parser;
QVariantMap json = parser.parse(reply).toMap();
QString url = ExtractImageUrl(json);
qLog(Debug) << "Found wikipedia image url:" << url;
if (!url.isEmpty()) {
emit ImageReady(id, QUrl(url));
}
latch->CountDown();
});
}
});
}

View File

@ -34,6 +34,8 @@ class ArtistBiography : public SongInfoProvider {
void FetchInfo(int id, const Song& metadata) override;
private:
void FetchWikipediaImages(int id, const QString& title);
std::unique_ptr<NetworkAccessManager> network_;
};

View File

@ -88,7 +88,8 @@ void PrettyImage::ImageFetched(RedirectFollower* follower) {
QImage image = QImage::fromData(reply->readAll());
if (image.isNull()) {
qLog(Debug) << "Image failed to load" << reply->request().url();
qLog(Debug) << "Image failed to load" << reply->request().url()
<< reply->error();
deleteLater();
} else {
state_ = State_CreatingThumbnail;