File indexing completed on 2024-05-12 05:09:29
0001 /*************************************************************************** 0002 Copyright (C) 2019 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "comicvinefetcher.h" 0026 #include "../translators/xslthandler.h" 0027 #include "../translators/tellicoimporter.h" 0028 #include "../utils/mapvalue.h" 0029 #include "../core/tellico_strings.h" 0030 #include "../tellico_debug.h" 0031 0032 #include <KLocalizedString> 0033 #include <KConfigGroup> 0034 0035 #include <QLabel> 0036 #include <QFile> 0037 #include <QTextStream> 0038 #include <QGridLayout> 0039 #include <QDomDocument> 0040 #include <QTextCodec> 0041 #include <QUrlQuery> 0042 #include <QJsonDocument> 0043 #include <QJsonObject> 0044 0045 namespace { 0046 static const int COMICVINE_MAX_RETURNS_TOTAL = 20; 0047 static const char* COMICVINE_API_URL = "https://www.comicvine.com/api"; 0048 static const char* COMICVINE_API_KEY = "6e4b19eeb8ccec8e2f026169d19adf57850d378e"; 0049 } 0050 0051 using namespace Tellico; 0052 using Tellico::Fetch::ComicVineFetcher; 0053 0054 ComicVineFetcher::ComicVineFetcher(QObject* parent_) 0055 : XMLFetcher(parent_) 0056 , m_total(-1) 0057 , m_apiKey(QLatin1String(COMICVINE_API_KEY)) { 0058 setLimit(COMICVINE_MAX_RETURNS_TOTAL); 0059 setXSLTFilename(QStringLiteral("comicvine2tellico.xsl")); 0060 } 0061 0062 ComicVineFetcher::~ComicVineFetcher() { 0063 } 0064 0065 QString ComicVineFetcher::source() const { 0066 return m_name.isEmpty() ? defaultName() : m_name; 0067 } 0068 0069 QString ComicVineFetcher::attribution() const { 0070 return i18n(providedBy, QLatin1String("https://comicvine.gamespot.com"), QLatin1String("Comic Vine")); 0071 } 0072 0073 bool ComicVineFetcher::canFetch(int type) const { 0074 return type == Data::Collection::ComicBook; 0075 } 0076 0077 void ComicVineFetcher::readConfigHook(const KConfigGroup& config_) { 0078 QString k = config_.readEntry("API Key", COMICVINE_API_KEY); 0079 if(!k.isEmpty()) { 0080 m_apiKey = k; 0081 } 0082 } 0083 0084 void ComicVineFetcher::resetSearch() { 0085 m_total = -1; 0086 } 0087 0088 QUrl ComicVineFetcher::searchUrl() { 0089 QUrl u(QString::fromLatin1(COMICVINE_API_URL)); 0090 QUrlQuery q; 0091 q.addQueryItem(QStringLiteral("format"), QStringLiteral("xml")); 0092 q.addQueryItem(QStringLiteral("api_key"), m_apiKey); 0093 0094 switch(request().key()) { 0095 case Keyword: 0096 u.setPath(u.path() + QStringLiteral("/search")); 0097 q.addQueryItem(QStringLiteral("query"), request().value()); 0098 q.addQueryItem(QStringLiteral("resources"), QStringLiteral("issue")); 0099 break; 0100 0101 default: 0102 myWarning() << source() << "- key not recognized:" << request().key(); 0103 return QUrl(); 0104 } 0105 u.setQuery(q); 0106 0107 // myDebug() << "url: " << u.url(); 0108 return u; 0109 } 0110 0111 void ComicVineFetcher::parseData(QByteArray& data_) { 0112 Q_UNUSED(data_); 0113 } 0114 0115 Tellico::Data::EntryPtr ComicVineFetcher::fetchEntryHookData(Data::EntryPtr entry_) { 0116 Q_ASSERT(entry_); 0117 0118 const QString url = entry_->field(QStringLiteral("comicvine-api")); 0119 if(url.isEmpty()) { 0120 myDebug() << "no comicvine api url found"; 0121 return entry_; 0122 } 0123 0124 QUrl u(url); 0125 QUrlQuery q; 0126 q.addQueryItem(QStringLiteral("format"), QStringLiteral("xml")); 0127 q.addQueryItem(QStringLiteral("api_key"), m_apiKey); 0128 u.setQuery(q); 0129 // myDebug() << "url: " << u; 0130 0131 // quiet 0132 QString output = FileHandler::readXMLFile(u, true); 0133 0134 #if 0 0135 myWarning() << "Remove output debug from comicvinefetcher.cpp"; 0136 QFile f(QStringLiteral("/tmp/test2.xml")); 0137 if(f.open(QIODevice::WriteOnly)) { 0138 QTextStream t(&f); 0139 t.setCodec("UTF-8"); 0140 t << output; 0141 } 0142 f.close(); 0143 #endif 0144 0145 Import::TellicoImporter imp(xsltHandler()->applyStylesheet(output)); 0146 // be quiet when loading images 0147 imp.setOptions(imp.options() ^ Import::ImportShowImageErrors); 0148 Data::CollPtr coll = imp.collection(); 0149 if(!coll || coll->entryCount() == 0) { 0150 myWarning() << "no collection pointer"; 0151 return entry_; 0152 } 0153 0154 if(coll->entryCount() > 1) { 0155 myDebug() << "weird, more than one entry found"; 0156 } 0157 0158 // grab the publisher from the volume link 0159 const QString volUrl = entry_->field(QStringLiteral("comicvine-volume-api")); 0160 if(!volUrl.isEmpty()) { 0161 QUrl vu(volUrl); 0162 // easier to use JSON here 0163 QUrlQuery q; 0164 q.addQueryItem(QStringLiteral("format"), QStringLiteral("json")); 0165 q.addQueryItem(QStringLiteral("api_key"), m_apiKey); 0166 vu.setQuery(q); 0167 // myDebug() << "volume url: " << vu; 0168 0169 QByteArray data = FileHandler::readDataFile(vu, true /* quiet */); 0170 #if 0 0171 myWarning() << "Remove JSON output debug from comicvinefetcher.cpp"; 0172 QFile f2(QStringLiteral("/tmp/test2.json")); 0173 if(f2.open(QIODevice::WriteOnly)) { 0174 QTextStream t(&f2); 0175 t << data; 0176 } 0177 f2.close(); 0178 #endif 0179 QJsonDocument doc = QJsonDocument::fromJson(data); 0180 QVariantMap map = doc.object().toVariantMap().value(QLatin1String("results")).toMap(); 0181 const QString pub = mapValue(map, "publisher", "name"); 0182 if(!pub.isEmpty()) { 0183 Data::EntryPtr e = coll->entries().front(); 0184 if(e) { 0185 e->setField(QStringLiteral("publisher"), pub); 0186 } 0187 } 0188 } 0189 0190 // don't want to include api link 0191 coll->removeField(QStringLiteral("comicvine-api")); 0192 coll->removeField(QStringLiteral("comicvine-volume-api")); 0193 return coll->entries().front(); 0194 } 0195 0196 Tellico::Fetch::FetchRequest ComicVineFetcher::updateRequest(Data::EntryPtr entry_) { 0197 QString title = entry_->field(QStringLiteral("title")); 0198 if(!title.isEmpty()) { 0199 return FetchRequest(Keyword, title); 0200 } 0201 return FetchRequest(); 0202 } 0203 0204 Tellico::Fetch::ConfigWidget* ComicVineFetcher::configWidget(QWidget* parent_) const { 0205 return new ComicVineFetcher::ConfigWidget(parent_, this); 0206 } 0207 0208 QString ComicVineFetcher::defaultName() { 0209 return QStringLiteral("Comic Vine"); 0210 } 0211 0212 QString ComicVineFetcher::defaultIcon() { 0213 return favIcon("https://comicvine.gamespot.com"); 0214 } 0215 0216 Tellico::StringHash ComicVineFetcher::allOptionalFields() { 0217 StringHash hash; 0218 // hash[QStringLiteral("colorist")] = i18n("Colorist"); 0219 hash[QStringLiteral("comicvine")] = i18n("Comic Vine Link"); 0220 return hash; 0221 } 0222 0223 ComicVineFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const ComicVineFetcher* fetcher_) 0224 : Fetch::ConfigWidget(parent_) { 0225 QGridLayout* l = new QGridLayout(optionsWidget()); 0226 l->setSpacing(4); 0227 l->setColumnStretch(1, 10); 0228 0229 int row = -1; 0230 0231 QLabel* al = new QLabel(i18n("Registration is required for accessing this data source. " 0232 "If you agree to the terms and conditions, <a href='%1'>sign " 0233 "up for an account</a>, and enter your information below.", 0234 QLatin1String("http://api.comicvine.com")), 0235 optionsWidget()); 0236 al->setOpenExternalLinks(true); 0237 al->setWordWrap(true); 0238 ++row; 0239 l->addWidget(al, row, 0, 1, 2); 0240 // richtext gets weird with size 0241 al->setMinimumWidth(al->sizeHint().width()); 0242 0243 QLabel* label = new QLabel(i18n("Access key: "), optionsWidget()); 0244 l->addWidget(label, ++row, 0); 0245 0246 m_apiKeyEdit = new QLineEdit(optionsWidget()); 0247 connect(m_apiKeyEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotSetModified); 0248 l->addWidget(m_apiKeyEdit, row, 1); 0249 QString w = i18n("The default Tellico key may be used, but searching may fail due to reaching access limits."); 0250 label->setWhatsThis(w); 0251 m_apiKeyEdit->setWhatsThis(w); 0252 label->setBuddy(m_apiKeyEdit); 0253 0254 l->setRowStretch(++row, 10); 0255 0256 // now add additional fields widget 0257 addFieldsWidget(ComicVineFetcher::allOptionalFields(), fetcher_ ? fetcher_->optionalFields() : QStringList()); 0258 0259 if(fetcher_) { 0260 // only show the key if it is not the default Tellico one... 0261 // that way the user is prompted to apply for their own 0262 if(fetcher_->m_apiKey != QLatin1String(COMICVINE_API_KEY)) { 0263 m_apiKeyEdit->setText(fetcher_->m_apiKey); 0264 } 0265 } 0266 } 0267 0268 void ComicVineFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) { 0269 QString apiKey = m_apiKeyEdit->text().trimmed(); 0270 if(!apiKey.isEmpty()) { 0271 config_.writeEntry("API Key", apiKey); 0272 } 0273 } 0274 0275 QString ComicVineFetcher::ConfigWidget::preferredName() const { 0276 return ComicVineFetcher::defaultName(); 0277 }