[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [tellico] src: Add data source for IGDB.com
From: Robby Stephenson <null () kde ! org>
Date: 2017-04-30 20:40:14
Message-ID: E1d4vde-0001S4-ID () code ! kde ! org
[Download RAW message or body]
Git commit 9b44d9e11142a82ea09a75628e11b56fcf0c2f27 by Robby Stephenson.
Committed on 30/04/2017 at 20:40.
Pushed by rstephenson into branch 'master'.
Add data source for IGDB.com
A +650 -0 src/fetch/igdbfetcher.cpp [License: GPL (v2/3)]
A +126 -0 src/fetch/igdbfetcher.h [License: GPL (v2/3)]
A +79 -0 src/tests/igdbfetchertest.cpp [License: GPL (v2/3)]
A +40 -0 src/tests/igdbfetchertest.h [License: GPL (v2/3)]
https://commits.kde.org/tellico/9b44d9e11142a82ea09a75628e11b56fcf0c2f27
diff --git a/src/fetch/igdbfetcher.cpp b/src/fetch/igdbfetcher.cpp
new file mode 100644
index 00000000..a9d15316
--- /dev/null
+++ b/src/fetch/igdbfetcher.cpp
@@ -0,0 +1,650 @@
+/***************************************************************************
+ Copyright (C) 2017 Robby Stephenson <robby@periapsis.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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 2 of *
+ * the License or (at your option) version 3 or any later version *
+ * accepted by the membership of KDE e.V. (or its successor approved *
+ * by the membership of KDE e.V.), which shall act as a proxy *
+ * defined in Section 14 of version 3 of the license. *
+ * *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
+ * *
+ ***************************************************************************/
+
+#include "igdbfetcher.h"
+#include "../collections/gamecollection.h"
+#include "../images/imagefactory.h"
+#include "../core/filehandler.h"
+#include "../utils/guiproxy.h"
+#include "../utils/string_utils.h"
+#include "../tellico_debug.h"
+
+#include <KLocalizedString>
+#include <KConfigGroup>
+#include <KJob>
+#include <KJobUiDelegate>
+#include <KJobWidgets/KJobWidgets>
+#include <KIO/StoredTransferJob>
+
+#include <QUrl>
+#include <QLabel>
+#include <QFile>
+#include <QTextStream>
+#include <QGridLayout>
+#include <QTextCodec>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QUrlQuery>
+
+namespace {
+ static const int IGDB_MAX_RETURNS_TOTAL = 20;
+ static const char* IGDB_API_URL = \
"https://igdbcom-internet-game-database-v1.p.mashape.com/"; + static const char* \
IGDB_API_KEY = "Ger6nO0EnKmsh7FCyUPa3GMdeYM5p1sfrPjjsnLYoHdDf19CGG"; +}
+
+using namespace Tellico;
+using Tellico::Fetch::IGDBFetcher;
+
+QHash<int, QString> IGDBFetcher::s_genreHash;
+QHash<int, QString> IGDBFetcher::s_platformHash;
+QHash<QString, QString> IGDBFetcher::s_companyHash;
+QHash<QString, QString> IGDBFetcher::s_esrbHash;
+QHash<QString, QString> IGDBFetcher::s_pegiHash;
+
+IGDBFetcher::IGDBFetcher(QObject* parent_)
+ : Fetcher(parent_), m_started(false), m_apiKey(QLatin1String(IGDB_API_KEY)) {
+ // setLimit(IGDB_MAX_RETURNS_TOTAL);
+ if(s_genreHash.isEmpty()) {
+ populateHashes();
+ }
+}
+
+IGDBFetcher::~IGDBFetcher() {
+ myDebug() << "destroyed IGDBFetcher";
+}
+
+QString IGDBFetcher::source() const {
+ return m_name.isEmpty() ? defaultName() : m_name;
+}
+
+QString IGDBFetcher::attribution() const {
+ return i18n("This information was freely provided by <a \
href=\"http://igdb.com\">IGDB.com</a>."); +}
+
+bool IGDBFetcher::canSearch(FetchKey k) const {
+ return k == Keyword;
+}
+
+bool IGDBFetcher::canFetch(int type) const {
+ return type == Data::Collection::Game;
+}
+
+void IGDBFetcher::readConfigHook(const KConfigGroup& config_) {
+ QString k = config_.readEntry("API Key", IGDB_API_KEY);
+ if(!k.isEmpty()) {
+ m_apiKey = k;
+ }
+}
+
+void IGDBFetcher::search() {
+ continueSearch();
+}
+
+void IGDBFetcher::continueSearch() {
+ m_started = true;
+
+ if(m_apiKey.isEmpty()) {
+ myDebug() << "empty API key";
+ stop();
+ return;
+ }
+
+ QUrl u(QString::fromLatin1(IGDB_API_URL));
+ u.setPath(u.path() + QLatin1String("games/"));
+ QUrlQuery q;
+ switch(request().key) {
+ case Keyword:
+ q.addQueryItem(QLatin1String("search"), request().value);
+ break;
+
+ default:
+ myWarning() << "key not recognized:" << request().key;
+ stop();
+ return;
+ }
+// q.addQueryItem(QLatin1String("fields"), QLatin1String("id,name"));
+ q.addQueryItem(QLatin1String("fields"), QLatin1String("*"));
+ q.addQueryItem(QLatin1String("limit"), QString::number(IGDB_MAX_RETURNS_TOTAL));
+ u.setQuery(q);
+// myDebug() << u;
+
+ m_job = igdbJob(u, m_apiKey);
+ connect(m_job, SIGNAL(result(KJob*)), SLOT(slotComplete(KJob*)));
+}
+
+void IGDBFetcher::stop() {
+ if(!m_started) {
+ return;
+ }
+ if(m_job) {
+ m_job->kill();
+ m_job = nullptr;
+ }
+ m_started = false;
+ emit signalDone(this);
+}
+
+Tellico::Data::EntryPtr IGDBFetcher::fetchEntryHook(uint uid_) {
+ if(!m_entries.contains(uid_)) {
+ myDebug() << "no entry ptr";
+ return Data::EntryPtr();
+ }
+
+ Data::EntryPtr entry = m_entries.value(uid_);
+
+ QStringList publishers;
+ // grab the publisher data
+ if(entry->field(QLatin1String("publisher")).isEmpty()) {
+ foreach(const QString& pid, \
FieldFormat::splitValue(entry->field(QLatin1String("pub-id")))) { + const \
QString publisher = companyName(pid); + if(!publisher.isEmpty()) {
+ publishers << publisher;
+ }
+ }
+ }
+ entry->setField(QLatin1String("publisher"), \
publishers.join(FieldFormat::delimiterString())); +
+ QStringList developers;
+ // grab the developer data
+ if(entry->field(QLatin1String("developer")).isEmpty()) {
+ foreach(const QString& did, \
FieldFormat::splitValue(entry->field(QLatin1String("dev-id")))) { + const \
QString developer = companyName(did); + if(!developer.isEmpty()) {
+ developers << developer;
+ }
+ }
+ }
+ entry->setField(QLatin1String("developer"), \
developers.join(FieldFormat::delimiterString())); +
+ // clear the placeholder fields
+ entry->setField(QLatin1String("pub-id"), QString());
+ entry->setField(QLatin1String("dev-id"), QString());
+ return entry;
+}
+
+Tellico::Fetch::FetchRequest IGDBFetcher::updateRequest(Data::EntryPtr entry_) {
+ QString title = entry_->field(QLatin1String("title"));
+ if(!title.isEmpty()) {
+ return FetchRequest(Keyword, title);
+ }
+ return FetchRequest();
+}
+
+void IGDBFetcher::slotComplete(KJob* job_) {
+ KIO::StoredTransferJob* job = static_cast<KIO::StoredTransferJob*>(job_);
+
+ if(job->error()) {
+ job->ui()->showErrorMessage();
+ stop();
+ return;
+ }
+
+ const QByteArray data = job->data();
+ if(data.isEmpty()) {
+ myDebug() << "no data";
+ stop();
+ return;
+ }
+ // see bug 319662. If fetcher is cancelled, job is killed
+ // if the pointer is retained, it gets double-deleted
+ m_job = nullptr;
+
+#if 0
+ myWarning() << "Remove debug from igdbfetcher.cpp";
+ QFile file(QString::fromLatin1("/tmp/test.json"));
+ if(file.open(QIODevice::WriteOnly)) {
+ QTextStream t(&file);
+ t.setCodec("UTF-8");
+ t << data;
+ }
+ file.close();
+#endif
+
+ Data::CollPtr coll(new Data::GameCollection(true));
+ if(optionalFields().contains(QLatin1String("pegi"))) {
+ QStringList pegi = QString::fromLatin1("PEGI 3, PEGI 7, PEGI 12, PEGI 16, PEGI \
18") + .split(QRegExp(QLatin1String("\\s*,\\s*")), \
QString::SkipEmptyParts); + Data::FieldPtr field(new \
Data::Field(QLatin1String("pegi"), i18n("PEGI Rating"), pegi)); + \
field->setFlags(Data::Field::AllowGrouped); + field->setCategory(i18n("General"));
+ coll->addField(field);
+ }
+ if(optionalFields().contains(QLatin1String("igdb"))) {
+ Data::FieldPtr field(new Data::Field(QLatin1String("igdb"), i18n("IGDB Link"), \
Data::Field::URL)); + field->setCategory(i18n("General"));
+ coll->addField(field);
+ }
+ // placeholder for publisher id, to be removed later
+ Data::FieldPtr f(new Data::Field(QLatin1String("pub-id"), QString(), \
Data::Field::Number)); + f->setFlags(Data::Field::AllowMultiple);
+ coll->addField(f);
+ // placeholder for developer id, to be removed later
+ f = new Data::Field(QLatin1String("dev-id"), QString(), Data::Field::Number);
+ f->setFlags(Data::Field::AllowMultiple);
+ coll->addField(f);
+
+ QJsonDocument doc = QJsonDocument::fromJson(data);
+ foreach(const QVariant& result, doc.array().toVariantList()) {
+ QVariantMap resultMap = result.toMap();
+ Data::EntryPtr entry(new Data::Entry(coll));
+ populateEntry(entry, resultMap);
+
+ FetchResult* r = new FetchResult(Fetcher::Ptr(this), entry);
+ m_entries.insert(r->uid, entry);
+ emit signalResultFound(r);
+ }
+
+ stop();
+}
+
+void IGDBFetcher::populateEntry(Data::EntryPtr entry_, const QVariantMap& \
resultMap_) { + entry_->setField(QLatin1String("title"), value(resultMap_, "name"));
+ entry_->setField(QLatin1String("description"), value(resultMap_, "summary"));
+ entry_->setField(QLatin1String("certification"), \
s_esrbHash.value(value(resultMap_, "esrb", "rating"))); + \
entry_->setField(QLatin1String("pub-id"), value(resultMap_, "publishers")); + \
entry_->setField(QLatin1String("dev-id"), value(resultMap_, "developers")); +
+ QString cover = value(resultMap_, "cover", "url");
+ if(cover.startsWith(QLatin1Char('/'))) {
+ cover.prepend(QLatin1String("https:"));
+ }
+ entry_->setField(QLatin1String("cover"), cover);
+
+ QVariantList genreIDs = resultMap_.value(QLatin1String("genres")).toList();
+ QStringList genres;
+ foreach(const QVariant& id, genreIDs) {
+ QString g = s_genreHash.value(id.toInt());
+ if(!g.isEmpty()) {
+ genres << g;
+ }
+ }
+ entry_->setField(QLatin1String("genre"), \
genres.join(FieldFormat::delimiterString())); +
+ QVariantList releases = resultMap_.value(QLatin1String("release_dates")).toList();
+ if(!releases.isEmpty()) {
+ QVariantMap releaseMap = releases.at(0).toMap();
+ // for now just grab the year of the first release
+ entry_->setField(QLatin1String("year"), value(releaseMap, "y"));
+ const QString platform = \
s_platformHash.value(releaseMap.value(QLatin1String("platform")).toInt()); + \
if(platform == QLatin1String("Nintendo Entertainment System (NES)")) { + \
entry_->setField(QLatin1String("platform"), i18n("Nintendo")); + } else \
if(platform == QLatin1String("Nintendo PlayStation")) { + \
entry_->setField(QLatin1String("platform"), i18n("PlayStation")); + } else \
if(platform == QLatin1String("PlayStation 2")) { + \
entry_->setField(QLatin1String("platform"), i18n("PlayStation2")); + } else \
if(platform == QLatin1String("PlayStation 3")) { + \
entry_->setField(QLatin1String("platform"), i18n("PlayStation3")); + } else \
if(platform == QLatin1String("PlayStation 4")) { + \
entry_->setField(QLatin1String("platform"), i18n("PlayStation4")); + } else \
if(platform == QLatin1String("PlayStation Portable")) { + \
entry_->setField(QLatin1String("platform"), i18nc("PlayStation Portable", "PSP")); + \
} else if(platform == QLatin1String("Wii")) { + \
entry_->setField(QLatin1String("platform"), i18n("Nintendo Wii")); + } else \
if(platform == QLatin1String("Nintendo GameCube")) { + \
entry_->setField(QLatin1String("platform"), i18n("GameCube")); + } else \
if(platform == QLatin1String("PC (Microsoft Windows)")) { + \
entry_->setField(QLatin1String("platform"), i18nc("Windows Platform", "Windows")); + \
} else if(platform == QLatin1String("Mac")) { + \
entry_->setField(QLatin1String("platform"), i18n("Mac OS")); + } else {
+ // TODO all the other platform translations
+ // also make the assumption that if the platform name isn't already in the \
allowed list, it should be added + Data::FieldPtr f = \
entry_->collection()->fieldByName(QLatin1String("platform")); + if(f && \
!f->allowed().contains(platform)) { + f->setAllowed(QStringList(f->allowed()) \
<< platform); + }
+ entry_->setField(QLatin1String("platform"), platform);
+ }
+ }
+
+ if(optionalFields().contains(QLatin1String("pegi"))) {
+ entry_->setField(QLatin1String("pegi"), s_pegiHash.value(value(resultMap_, \
"pegi", "rating"))); + }
+
+ if(optionalFields().contains(QLatin1String("igdb"))) {
+ entry_->setField(QLatin1String("igdb"), value(resultMap_, "url"));
+ }
+}
+
+QString IGDBFetcher::companyName(const QString& companyId_) const {
+ if(s_companyHash.contains(companyId_)) {
+ return s_companyHash.value(companyId_);
+ }
+ QUrl u(QString::fromLatin1(IGDB_API_URL));
+ u.setPath(u.path() + QLatin1String("companies/") + companyId_);
+
+ QUrlQuery q;
+ q.addQueryItem(QLatin1String("fields"), QLatin1String("*"));
+
+ u.setQuery(q);
+
+ QPointer<KIO::StoredTransferJob> job = igdbJob(u, m_apiKey);
+ if(!job->exec()) {
+ myDebug() << job->errorString() << u;
+ return QString();
+ }
+ const QByteArray data = job->data();
+ if(data.isEmpty()) {
+ myDebug() << "no data for" << u;
+ return QString();
+ }
+#if 0
+ myWarning() << "Remove company debug from igdbfetcher.cpp";
+ QFile file(QString::fromLatin1("/tmp/igdb-company.json"));
+ if(file.open(QIODevice::WriteOnly)) {
+ QTextStream t(&file);
+ t.setCodec("UTF-8");
+ t << data;
+ }
+ file.close();
+#endif
+
+ QJsonDocument doc = QJsonDocument::fromJson(data);
+ const QString company = value(doc.array().toVariantList().at(0).toMap(), "name");
+ s_companyHash.insert(companyId_, company);
+ return company;
+}
+
+Tellico::Fetch::ConfigWidget* IGDBFetcher::configWidget(QWidget* parent_) const {
+ return new IGDBFetcher::ConfigWidget(parent_, this);
+}
+
+QString IGDBFetcher::defaultName() {
+ return i18n("Internet Game Database (IGDB.com)");
+}
+
+QString IGDBFetcher::defaultIcon() {
+ return favIcon("http://www.igdb.com");
+}
+
+Tellico::StringHash IGDBFetcher::allOptionalFields() {
+ StringHash hash;
+ hash[QLatin1String("pegi")] = i18n("PEGI Rating");
+ hash[QLatin1String("igdb")] = i18n("IGDB Link");
+ return hash;
+}
+
+IGDBFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const IGDBFetcher* \
fetcher_) + : Fetch::ConfigWidget(parent_) {
+ QGridLayout* l = new QGridLayout(optionsWidget());
+ l->setSpacing(4);
+ l->setColumnStretch(1, 10);
+
+ int row = -1;
+
+ QLabel* al = new QLabel(i18n("Registration is required for accessing the %1 data \
source. " + "If you agree to the terms and conditions, \
<a href='%2'>sign " + "up for an account</a>, and enter \
your information below.", + \
IGDBFetcher::defaultName(), + \
QLatin1String("http://igdb.github.io/api/about/welcome/")), + \
optionsWidget()); + al->setOpenExternalLinks(true);
+ al->setWordWrap(true);
+ ++row;
+ l->addWidget(al, row, 0, 1, 2);
+ // richtext gets weird with size
+ al->setMinimumWidth(al->sizeHint().width());
+
+ QLabel* label = new QLabel(i18n("Access key: "), optionsWidget());
+ l->addWidget(label, ++row, 0);
+
+ m_apiKeyEdit = new QLineEdit(optionsWidget());
+ connect(m_apiKeyEdit, SIGNAL(textChanged(const QString&)), \
SLOT(slotSetModified())); + l->addWidget(m_apiKeyEdit, row, 1);
+ QString w = i18n("The default Tellico key may be used, but searching may fail due \
to reaching access limits."); + label->setWhatsThis(w);
+ m_apiKeyEdit->setWhatsThis(w);
+ label->setBuddy(m_apiKeyEdit);
+
+ l->setRowStretch(++row, 10);
+
+ // now add additional fields widget
+ addFieldsWidget(IGDBFetcher::allOptionalFields(), fetcher_ ? \
fetcher_->optionalFields() : QStringList()); +
+ if(fetcher_) {
+ // only show the key if it is not the default Tellico one...
+ // that way the user is prompted to apply for their own
+ if(fetcher_->m_apiKey != QLatin1String(IGDB_API_KEY)) {
+ m_apiKeyEdit->setText(fetcher_->m_apiKey);
+ }
+ }
+}
+
+void IGDBFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) {
+ const QString apiKey = m_apiKeyEdit->text().trimmed();
+ if(!apiKey.isEmpty()) {
+ config_.writeEntry("API Key", apiKey);
+ }
+}
+
+QString IGDBFetcher::ConfigWidget::preferredName() const {
+ return IGDBFetcher::defaultName();
+}
+
+// static
+QString IGDBFetcher::value(const QVariantMap& map, const char* name) {
+ const QVariant v = map.value(QLatin1String(name));
+ if(v.isNull()) {
+ return QString();
+ } else if(v.canConvert(QVariant::String)) {
+ return v.toString();
+ } else if(v.canConvert(QVariant::StringList)) {
+ return v.toStringList().join(FieldFormat::delimiterString());
+ } else {
+ return QString();
+ }
+}
+
+QString IGDBFetcher::value(const QVariantMap& map, const char* object, const char* \
name) { + const QVariant v = map.value(QLatin1String(object));
+ if(v.isNull()) {
+ return QString();
+ } else if(v.canConvert(QVariant::Map)) {
+ return value(v.toMap(), name);
+ } else if(v.canConvert(QVariant::List)) {
+ QVariantList list = v.toList();
+ return list.isEmpty() ? QString() : value(list.at(0).toMap(), name);
+ } else {
+ return QString();
+ }
+}
+
+// Be lazy. Use static hash for certain field names for now.
+// Don't expect IGDB values to change. This avoids exponentially multiplying the \
number of API calls +void IGDBFetcher::populateHashes() {
+ s_genreHash.insert(2, QLatin1String("Point-and-click"));
+ s_genreHash.insert(4, QLatin1String("Fighting"));
+ s_genreHash.insert(5, QLatin1String("Shooter"));
+ s_genreHash.insert(7, QLatin1String("Music"));
+ s_genreHash.insert(8, QLatin1String("Platform"));
+ s_genreHash.insert(9, QLatin1String("Puzzle"));
+ s_genreHash.insert(10, QLatin1String("Racing"));
+ s_genreHash.insert(11, QLatin1String("Real Time Strategy (RTS)"));
+ s_genreHash.insert(12, QLatin1String("Role-playing (RPG)"));
+ s_genreHash.insert(13, QLatin1String("Simulator"));
+ s_genreHash.insert(14, QLatin1String("Sport"));
+ s_genreHash.insert(15, QLatin1String("Strategy"));
+ s_genreHash.insert(16, QLatin1String("Turn-based strategy (TBS)"));
+ s_genreHash.insert(24, QLatin1String("Tactical"));
+ s_genreHash.insert(25, QLatin1String("Hack and slash/Beat 'em up"));
+ s_genreHash.insert(26, QLatin1String("Quiz/Trivia"));
+ s_genreHash.insert(30, QLatin1String("Pinball"));
+ s_genreHash.insert(31, QLatin1String("Adventure"));
+ s_genreHash.insert(32, QLatin1String("Indie"));
+ s_genreHash.insert(33, QLatin1String("Arcade"));
+
+ s_platformHash.insert(3, QLatin1String("Linux"));
+ s_platformHash.insert(4, QLatin1String("Nintendo 64"));
+ s_platformHash.insert(5, QLatin1String("Wii"));
+ s_platformHash.insert(6, QLatin1String("PC (Microsoft Windows)"));
+ s_platformHash.insert(7, QLatin1String("PlayStation"));
+ s_platformHash.insert(8, QLatin1String("PlayStation 2"));
+ s_platformHash.insert(9, QLatin1String("PlayStation 3"));
+ s_platformHash.insert(11, QLatin1String("Xbox"));
+ s_platformHash.insert(12, QLatin1String("Xbox 360"));
+ s_platformHash.insert(13, QLatin1String("PC DOS"));
+ s_platformHash.insert(14, QLatin1String("Mac"));
+ s_platformHash.insert(15, QLatin1String("Commodore C64/128"));
+ s_platformHash.insert(16, QLatin1String("Amiga"));
+ s_platformHash.insert(18, QLatin1String("Nintendo Entertainment System (NES)"));
+ s_platformHash.insert(19, QLatin1String("Super Nintendo Entertainment System \
(SNES)")); + s_platformHash.insert(20, QLatin1String("Nintendo DS"));
+ s_platformHash.insert(21, QLatin1String("Nintendo GameCube"));
+ s_platformHash.insert(22, QLatin1String("Game Boy Color"));
+ s_platformHash.insert(23, QLatin1String("Dreamcast"));
+ s_platformHash.insert(24, QLatin1String("Game Boy Advance"));
+ s_platformHash.insert(25, QLatin1String("Amstrad CPC"));
+ s_platformHash.insert(26, QLatin1String("ZX Spectrum"));
+ s_platformHash.insert(27, QLatin1String("MSX"));
+ s_platformHash.insert(29, QLatin1String("Sega Mega Drive/Genesis"));
+ s_platformHash.insert(30, QLatin1String("Sega 32X"));
+ s_platformHash.insert(32, QLatin1String("Sega Saturn"));
+ s_platformHash.insert(33, QLatin1String("Game Boy"));
+ s_platformHash.insert(34, QLatin1String("Android"));
+ s_platformHash.insert(35, QLatin1String("Sega Game Gear"));
+ s_platformHash.insert(36, QLatin1String("Xbox Live Arcade"));
+ s_platformHash.insert(37, QLatin1String("Nintendo 3DS"));
+ s_platformHash.insert(38, QLatin1String("PlayStation Portable"));
+ s_platformHash.insert(39, QLatin1String("iOS"));
+ s_platformHash.insert(41, QLatin1String("Wii U"));
+ s_platformHash.insert(42, QLatin1String("N-Gage"));
+ s_platformHash.insert(44, QLatin1String("Tapwave Zodiac"));
+ s_platformHash.insert(45, QLatin1String("PlayStation Network"));
+ s_platformHash.insert(46, QLatin1String("PlayStation Vita"));
+ s_platformHash.insert(47, QLatin1String("Virtual Console (Nintendo)"));
+ s_platformHash.insert(48, QLatin1String("PlayStation 4"));
+ s_platformHash.insert(49, QLatin1String("Xbox One"));
+ s_platformHash.insert(50, QLatin1String("3DO Interactive Multiplayer"));
+ s_platformHash.insert(51, QLatin1String("Family Computer Disk System"));
+ s_platformHash.insert(52, QLatin1String("Arcade"));
+ s_platformHash.insert(53, QLatin1String("MSX2"));
+ s_platformHash.insert(55, QLatin1String("Mobile"));
+ s_platformHash.insert(56, QLatin1String("WiiWare"));
+ s_platformHash.insert(57, QLatin1String("WonderSwan"));
+ s_platformHash.insert(58, QLatin1String("Super Famicom"));
+ s_platformHash.insert(59, QLatin1String("Atari 2600"));
+ s_platformHash.insert(60, QLatin1String("Atari 7800"));
+ s_platformHash.insert(61, QLatin1String("Atari Lynx"));
+ s_platformHash.insert(62, QLatin1String("Atari Jaguar"));
+ s_platformHash.insert(63, QLatin1String("Atari ST/STE"));
+ s_platformHash.insert(64, QLatin1String("Sega Master System"));
+ s_platformHash.insert(65, QLatin1String("Atari 8-bit"));
+ s_platformHash.insert(66, QLatin1String("Atari 5200"));
+ s_platformHash.insert(67, QLatin1String("Intellivision"));
+ s_platformHash.insert(68, QLatin1String("ColecoVision"));
+ s_platformHash.insert(69, QLatin1String("BBC Microcomputer System"));
+ s_platformHash.insert(70, QLatin1String("Vectrex"));
+ s_platformHash.insert(71, QLatin1String("Commodore VIC-20"));
+ s_platformHash.insert(72, QLatin1String("Ouya"));
+ s_platformHash.insert(73, QLatin1String("BlackBerry OS"));
+ s_platformHash.insert(74, QLatin1String("Windows Phone"));
+ s_platformHash.insert(75, QLatin1String("Apple II"));
+ s_platformHash.insert(77, QLatin1String("Sharp X1"));
+ s_platformHash.insert(78, QLatin1String("Sega CD"));
+ s_platformHash.insert(79, QLatin1String("Neo Geo MVS"));
+ s_platformHash.insert(80, QLatin1String("Neo Geo AES"));
+ s_platformHash.insert(82, QLatin1String("Web browser"));
+ s_platformHash.insert(84, QLatin1String("SG-1000"));
+ s_platformHash.insert(85, QLatin1String("Donner Model 30"));
+ s_platformHash.insert(86, QLatin1String("TurboGrafx-16/PC Engine"));
+ s_platformHash.insert(87, QLatin1String("Virtual Boy"));
+ s_platformHash.insert(88, QLatin1String("Odyssey"));
+ s_platformHash.insert(89, QLatin1String("Microvision"));
+ s_platformHash.insert(90, QLatin1String("Commodore PET"));
+ s_platformHash.insert(91, QLatin1String("Bally Astrocade"));
+ s_platformHash.insert(92, QLatin1String("SteamOS"));
+ s_platformHash.insert(93, QLatin1String("Commodore 16"));
+ s_platformHash.insert(94, QLatin1String("Commodore Plus/4"));
+ s_platformHash.insert(95, QLatin1String("PDP-1"));
+ s_platformHash.insert(96, QLatin1String("PDP-10"));
+ s_platformHash.insert(97, QLatin1String("PDP-8"));
+ s_platformHash.insert(98, QLatin1String("DEC GT40"));
+ s_platformHash.insert(99, QLatin1String("Family Computer"));
+ s_platformHash.insert(100, QLatin1String("Analogue electronics"));
+ s_platformHash.insert(101, QLatin1String("Ferranti Nimrod Computer"));
+ s_platformHash.insert(102, QLatin1String("EDSAC"));
+ s_platformHash.insert(103, QLatin1String("PDP-7"));
+ s_platformHash.insert(104, QLatin1String("HP 2100"));
+ s_platformHash.insert(105, QLatin1String("HP 3000"));
+ s_platformHash.insert(106, QLatin1String("SDS Sigma 7"));
+ s_platformHash.insert(107, QLatin1String("Call-A-Computer time-shared mainframe \
computer system")); + s_platformHash.insert(108, QLatin1String("PDP-11"));
+ s_platformHash.insert(109, QLatin1String("CDC Cyber 70"));
+ s_platformHash.insert(110, QLatin1String("PLATO"));
+ s_platformHash.insert(111, QLatin1String("Imlac PDS-1"));
+ s_platformHash.insert(112, QLatin1String("Microcomputer"));
+ s_platformHash.insert(113, QLatin1String("OnLive Game System"));
+ s_platformHash.insert(114, QLatin1String("Amiga CD32"));
+ s_platformHash.insert(115, QLatin1String("Apple IIGS"));
+ s_platformHash.insert(116, QLatin1String("Acorn Archimedes"));
+ s_platformHash.insert(117, QLatin1String("Philips CD-i"));
+ s_platformHash.insert(118, QLatin1String("FM Towns"));
+ s_platformHash.insert(119, QLatin1String("Neo Geo Pocket"));
+ s_platformHash.insert(120, QLatin1String("Neo Geo Pocket Color"));
+ s_platformHash.insert(121, QLatin1String("Sharp X68000"));
+ s_platformHash.insert(122, QLatin1String("Nuon"));
+ s_platformHash.insert(123, QLatin1String("WonderSwan Color"));
+ s_platformHash.insert(124, QLatin1String("SwanCrystal"));
+ s_platformHash.insert(125, QLatin1String("PC-8801"));
+ s_platformHash.insert(126, QLatin1String("TRS-80"));
+ s_platformHash.insert(127, QLatin1String("Fairchild Channel F"));
+ s_platformHash.insert(128, QLatin1String("PC Engine SuperGrafx"));
+ s_platformHash.insert(129, QLatin1String("Texas Instruments TI-99"));
+ s_platformHash.insert(130, QLatin1String("Nintendo Switch"));
+ s_platformHash.insert(131, QLatin1String("Nintendo PlayStation"));
+ s_platformHash.insert(132, QLatin1String("Amazon Fire TV"));
+ s_platformHash.insert(133, QLatin1String("Philips Videopac G7000"));
+ s_platformHash.insert(134, QLatin1String("Acorn Electron"));
+ s_platformHash.insert(135, QLatin1String("Hyper Neo Geo 64"));
+ s_platformHash.insert(136, QLatin1String("Neo Geo CD"));
+
+ // cheat by grabbing i18n values from default collection
+ Data::CollPtr c(new Data::GameCollection(true));
+ QStringList esrb = c->fieldByName(QLatin1String("certification"))->allowed();
+ Q_ASSERT(esrb.size() == 8);
+ while(esrb.size() < 8) {
+ esrb << QString();
+ }
+ s_esrbHash.insert(QLatin1String("1"), esrb.at(7));
+ s_esrbHash.insert(QLatin1String("2"), esrb.at(6));
+ s_esrbHash.insert(QLatin1String("3"), esrb.at(5));
+ s_esrbHash.insert(QLatin1String("4"), esrb.at(4));
+ s_esrbHash.insert(QLatin1String("5"), esrb.at(3));
+ s_esrbHash.insert(QLatin1String("6"), esrb.at(2));
+ s_esrbHash.insert(QLatin1String("7"), esrb.at(1));
+
+ s_pegiHash.insert(QLatin1String("1"), QLatin1String("PEGI 3"));
+ s_pegiHash.insert(QLatin1String("2"), QLatin1String("PEGI 7"));
+ s_pegiHash.insert(QLatin1String("3"), QLatin1String("PEGI 12"));
+ s_pegiHash.insert(QLatin1String("4"), QLatin1String("PEGI 16"));
+ s_pegiHash.insert(QLatin1String("5"), QLatin1String("PEGI 18"));
+}
+
+QPointer<KIO::StoredTransferJob> IGDBFetcher::igdbJob(const QUrl& url_, const \
QString& apiKey_) { + QPointer<KIO::StoredTransferJob> job = KIO::storedGet(url_, \
KIO::NoReload, KIO::HideProgressInfo); + \
job->addMetaData(QLatin1String("customHTTPHeader"), QLatin1String("X-Mashape-Key: ") \
+ apiKey_); + job->addMetaData(QLatin1String("accept"), \
QLatin1String("application/json")); + KJobWidgets::setWindow(job, \
GUI::Proxy::widget()); + return job;
+}
diff --git a/src/fetch/igdbfetcher.h b/src/fetch/igdbfetcher.h
new file mode 100644
index 00000000..c68aaade
--- /dev/null
+++ b/src/fetch/igdbfetcher.h
@@ -0,0 +1,126 @@
+/***************************************************************************
+ Copyright (C) 2017 Robby Stephenson <robby@periapsis.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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 2 of *
+ * the License or (at your option) version 3 or any later version *
+ * accepted by the membership of KDE e.V. (or its successor approved *
+ * by the membership of KDE e.V.), which shall act as a proxy *
+ * defined in Section 14 of version 3 of the license. *
+ * *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
+ * *
+ ***************************************************************************/
+
+#ifndef TELLICO_IGDBFETCHER_H
+#define TELLICO_IGDBFETCHER_H
+
+#include "fetcher.h"
+#include "configwidget.h"
+#include "../datavectors.h"
+
+#include <QLineEdit>
+#include <QPointer>
+#include <QDate>
+
+class KJob;
+namespace KIO {
+ class StoredTransferJob;
+}
+
+namespace Tellico {
+ namespace Fetch {
+
+/**
+ * A fetcher for igdb.com
+ *
+ * @author Robby Stephenson
+ */
+class IGDBFetcher : public Fetcher {
+Q_OBJECT
+
+public:
+ /**
+ */
+ IGDBFetcher(QObject* parent);
+ /**
+ */
+ virtual ~IGDBFetcher();
+
+ /**
+ */
+ virtual QString source() const Q_DECL_OVERRIDE;
+ virtual QString attribution() const Q_DECL_OVERRIDE;
+ virtual bool isSearching() const Q_DECL_OVERRIDE { return m_started; }
+ virtual bool canSearch(FetchKey k) const Q_DECL_OVERRIDE;
+ virtual void stop() Q_DECL_OVERRIDE;
+ virtual Data::EntryPtr fetchEntryHook(uint uid) Q_DECL_OVERRIDE;
+ virtual Type type() const Q_DECL_OVERRIDE { return IGDB; }
+ virtual bool canFetch(int type) const Q_DECL_OVERRIDE;
+ virtual void readConfigHook(const KConfigGroup& config) Q_DECL_OVERRIDE;
+ virtual void continueSearch() Q_DECL_OVERRIDE;
+
+ /**
+ * Returns a widget for modifying the fetcher's config.
+ */
+ virtual Fetch::ConfigWidget* configWidget(QWidget* parent) const Q_DECL_OVERRIDE;
+
+ class ConfigWidget;
+ friend class ConfigWidget;
+
+ static QString defaultName();
+ static QString defaultIcon();
+ static StringHash allOptionalFields();
+
+private Q_SLOTS:
+ void slotComplete(KJob* job);
+
+private:
+ virtual void search() Q_DECL_OVERRIDE;
+ virtual FetchRequest updateRequest(Data::EntryPtr entry) Q_DECL_OVERRIDE;
+ void populateEntry(Data::EntryPtr entry, const QVariantMap& resultMap);
+ QString companyName(const QString& companyId) const;
+
+ static QString value(const QVariantMap& map, const char* name);
+ static QString value(const QVariantMap& map, const char* object, const char* \
name); + static void populateHashes();
+ static QPointer<KIO::StoredTransferJob> igdbJob(const QUrl& url, const QString& \
apiKey); +
+ bool m_started;
+
+ QString m_apiKey;
+ QHash<int, Data::EntryPtr> m_entries;
+ QPointer<KIO::StoredTransferJob> m_job;
+
+ static QHash<int, QString> s_genreHash;
+ static QHash<int, QString> s_platformHash;
+ static QHash<QString, QString> s_companyHash;
+ static QHash<QString, QString> s_esrbHash;
+ static QHash<QString, QString> s_pegiHash;
+};
+
+class IGDBFetcher::ConfigWidget : public Fetch::ConfigWidget {
+Q_OBJECT
+
+public:
+ explicit ConfigWidget(QWidget* parent_, const IGDBFetcher* fetcher = nullptr);
+ virtual void saveConfigHook(KConfigGroup&) Q_DECL_OVERRIDE;
+ virtual QString preferredName() const Q_DECL_OVERRIDE;
+
+private:
+ QLineEdit* m_apiKeyEdit;
+};
+
+ } // end namespace
+} // end namespace
+#endif
diff --git a/src/tests/igdbfetchertest.cpp b/src/tests/igdbfetchertest.cpp
new file mode 100644
index 00000000..5290dbab
--- /dev/null
+++ b/src/tests/igdbfetchertest.cpp
@@ -0,0 +1,79 @@
+/***************************************************************************
+ Copyright (C) 2017 Robby Stephenson <robby@periapsis.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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 2 of *
+ * the License or (at your option) version 3 or any later version *
+ * accepted by the membership of KDE e.V. (or its successor approved *
+ * by the membership of KDE e.V.), which shall act as a proxy *
+ * defined in Section 14 of version 3 of the license. *
+ * *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
+ * *
+ ***************************************************************************/
+
+#undef QT_NO_CAST_FROM_ASCII
+
+#include "igdbfetchertest.h"
+
+#include "../fetch/igdbfetcher.h"
+#include "../collections/gamecollection.h"
+#include "../entry.h"
+#include "../images/imagefactory.h"
+
+#include <KConfig>
+#include <KConfigGroup>
+
+#include <QTest>
+
+QTEST_GUILESS_MAIN( IGDBFetcherTest )
+
+IGDBFetcherTest::IGDBFetcherTest() : AbstractFetcherTest() {
+}
+
+void IGDBFetcherTest::initTestCase() {
+ Tellico::ImageFactory::init();
+}
+
+void IGDBFetcherTest::testKeyword() {
+ KConfig config(QFINDTESTDATA("tellicotest.config"), KConfig::SimpleConfig);
+ QString groupName = QLatin1String("igdb");
+ if(!config.hasGroup(groupName)) {
+ QSKIP("This test requires a config file.", SkipAll);
+ }
+ KConfigGroup cg(&config, groupName);
+
+ Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Game, \
Tellico::Fetch::Keyword, + QLatin1String("Zelda \
Twilight Princess Wii")); + Tellico::Fetch::Fetcher::Ptr fetcher(new \
Tellico::Fetch::IGDBFetcher(this)); + fetcher->readConfig(cg, cg.name());
+
+ Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1);
+
+ QCOMPARE(results.size(), 1);
+
+ Tellico::Data::EntryPtr entry = results.at(0);
+ QVERIFY(entry);
+ QCOMPARE(entry->field("title"), QLatin1String("The Legend of Zelda: Twilight \
Princess")); + QCOMPARE(entry->field("year"), QLatin1String("2006"));
+ QCOMPARE(entry->field("platform"), QLatin1String("Nintendo Wii"));
+ QCOMPARE(entry->field("certification"), QLatin1String("Teen"));
+ QCOMPARE(entry->field("pegi"), QLatin1String("PEGI 12"));
+ QCOMPARE(entry->field("genre"), QLatin1String("Adventure"));
+ QCOMPARE(entry->field("publisher"), QLatin1String("Nintendo"));
+ QCOMPARE(entry->field("developer"), QLatin1String("Nintendo EAD Group No. 3"));
+ QCOMPARE(entry->field("igdb"), \
QLatin1String("https://www.igdb.com/games/the-legend-of-zelda-twilight-princess")); + \
QVERIFY(!entry->field(QLatin1String("description")).isEmpty()); + \
QVERIFY(!entry->field(QLatin1String("cover")).isEmpty()); + \
QVERIFY(!entry->field(QLatin1String("cover")).startsWith('/')); +}
diff --git a/src/tests/igdbfetchertest.h b/src/tests/igdbfetchertest.h
new file mode 100644
index 00000000..1417c38c
--- /dev/null
+++ b/src/tests/igdbfetchertest.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ Copyright (C) 2017 Robby Stephenson <robby@periapsis.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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 2 of *
+ * the License or (at your option) version 3 or any later version *
+ * accepted by the membership of KDE e.V. (or its successor approved *
+ * by the membership of KDE e.V.), which shall act as a proxy *
+ * defined in Section 14 of version 3 of the license. *
+ * *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
+ * *
+ ***************************************************************************/
+
+#ifndef IGDBFETCHERTEST_H
+#define IGDBFETCHERTEST_H
+
+#include "abstractfetchertest.h"
+
+class IGDBFetcherTest : public AbstractFetcherTest {
+Q_OBJECT
+public:
+ IGDBFetcherTest();
+
+private Q_SLOTS:
+ void initTestCase();
+ void testKeyword();
+};
+
+#endif
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic