File indexing completed on 2024-05-12 05:09:45
0001 /*************************************************************************** 0002 Copyright (C) 2017-2021 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 "videogamegeekfetcher.h" 0026 #include "../translators/xslthandler.h" 0027 #include "../translators/tellicoimporter.h" 0028 #include "../utils/string_utils.h" 0029 #include "../core/tellico_strings.h" 0030 #include "../gui/combobox.h" 0031 #include "../tellico_debug.h" 0032 0033 #include <KLocalizedString> 0034 #include <KConfigGroup> 0035 0036 #include <QLabel> 0037 #include <QFile> 0038 #include <QTextStream> 0039 #include <QGridLayout> 0040 #include <QTextCodec> 0041 #include <QUrlQuery> 0042 0043 namespace { 0044 // a lot of overlap with boardgamegeekimporter.h 0045 static const int BGG_MAX_RETURNS_TOTAL = 10; 0046 // interchangeable with boardgamegeek.com 0047 static const char* BGG_SEARCH_URL = "https://videogamegeek.com/xmlapi2/search"; 0048 static const char* BGG_THING_URL = "https://videogamegeek.com/xmlapi2/thing"; 0049 } 0050 0051 using namespace Tellico; 0052 using Tellico::Fetch::VideoGameGeekFetcher; 0053 0054 VideoGameGeekFetcher::VideoGameGeekFetcher(QObject* parent_) 0055 : XMLFetcher(parent_), m_imageSize(SmallImage) { 0056 setLimit(BGG_MAX_RETURNS_TOTAL); 0057 setXSLTFilename(QStringLiteral("boardgamegeek2tellico.xsl")); 0058 } 0059 0060 VideoGameGeekFetcher::~VideoGameGeekFetcher() { 0061 } 0062 0063 QString VideoGameGeekFetcher::source() const { 0064 return m_name.isEmpty() ? defaultName() : m_name; 0065 } 0066 0067 // https://boardgamegeek.com/wiki/page/XML_API_Terms_of_Use 0068 QString VideoGameGeekFetcher::attribution() const { 0069 return i18n(providedBy, QLatin1String("https://boardgamegeek.com"), QLatin1String("BoardGameGeek")); 0070 } 0071 0072 bool VideoGameGeekFetcher::canSearch(Fetch::FetchKey k) const { 0073 return k == Title || k == Keyword; 0074 } 0075 0076 bool VideoGameGeekFetcher::canFetch(int type) const { 0077 return type == Data::Collection::Game; 0078 } 0079 0080 void VideoGameGeekFetcher::readConfigHook(const KConfigGroup& config_) { 0081 const int imageSize = config_.readEntry("Image Size", -1); 0082 if(imageSize > -1) { 0083 m_imageSize = static_cast<ImageSize>(imageSize); 0084 } 0085 } 0086 0087 QUrl VideoGameGeekFetcher::searchUrl() { 0088 QUrl u(QString::fromLatin1(BGG_SEARCH_URL)); 0089 0090 QUrlQuery q; 0091 switch(request().key()) { 0092 case Title: 0093 q.addQueryItem(QStringLiteral("query"), request().value()); 0094 q.addQueryItem(QStringLiteral("type"), QStringLiteral("videogame,videogameexpansion")); 0095 q.addQueryItem(QStringLiteral("exact"), QStringLiteral("1")); 0096 break; 0097 0098 case Keyword: 0099 q.addQueryItem(QStringLiteral("query"), request().value()); 0100 q.addQueryItem(QStringLiteral("type"), QStringLiteral("videogame,videogameexpansion")); 0101 break; 0102 0103 case Raw: 0104 u.setUrl(QLatin1String(BGG_THING_URL)); 0105 q.addQueryItem(QStringLiteral("id"), request().value()); 0106 q.addQueryItem(QStringLiteral("type"), QStringLiteral("videogame,videogameexpansion")); 0107 break; 0108 0109 default: 0110 myWarning() << source() << "- key not recognized:" << request().key(); 0111 return QUrl(); 0112 } 0113 u.setQuery(q); 0114 0115 // myDebug() << "url: " << u.url(); 0116 return u; 0117 } 0118 0119 Tellico::Data::EntryPtr VideoGameGeekFetcher::fetchEntryHookData(Data::EntryPtr entry_) { 0120 Q_ASSERT(entry_); 0121 0122 const QString id = entry_->field(QStringLiteral("bggid")); 0123 if(id.isEmpty()) { 0124 myDebug() << "no bgg id found"; 0125 return entry_; 0126 } 0127 0128 QUrl u(QString::fromLatin1(BGG_THING_URL)); 0129 QUrlQuery q; 0130 q.addQueryItem(QStringLiteral("id"), id); 0131 q.addQueryItem(QStringLiteral("type"), QStringLiteral("videogame,videogameexpansion")); 0132 u.setQuery(q); 0133 // myDebug() << "url: " << u; 0134 0135 // quiet 0136 QString output = FileHandler::readXMLFile(u, true); 0137 0138 #if 0 0139 myWarning() << "Remove output debug from videogamegeekfetcher.cpp"; 0140 QFile f(QStringLiteral("/tmp/test-videogamegeek.xml")); 0141 if(f.open(QIODevice::WriteOnly)) { 0142 QTextStream t(&f); 0143 t.setCodec("UTF-8"); 0144 t << output; 0145 } 0146 f.close(); 0147 #endif 0148 0149 auto handler = xsltHandler(); 0150 handler->addStringParam("image-size", QByteArray::number(m_imageSize)); 0151 0152 Import::TellicoImporter imp(handler->applyStylesheet(output)); 0153 // be quiet when loading images 0154 imp.setOptions(imp.options() ^ Import::ImportShowImageErrors); 0155 Data::CollPtr coll = imp.collection(); 0156 if(!coll) { 0157 myWarning() << "no collection pointer"; 0158 return entry_; 0159 } 0160 if(coll->entryCount() == 0) { 0161 myWarning() << "no entries"; 0162 return entry_; 0163 } 0164 0165 if(coll->entryCount() > 1) { 0166 myDebug() << "weird, more than one entry found"; 0167 } 0168 0169 // don't want to include id 0170 coll->removeField(QStringLiteral("bggid")); 0171 return coll->entries().front(); 0172 } 0173 0174 Tellico::Fetch::FetchRequest VideoGameGeekFetcher::updateRequest(Data::EntryPtr entry_) { 0175 QString bggid = entry_->field(QStringLiteral("bggid")); 0176 if(!bggid.isEmpty()) { 0177 return FetchRequest(Raw, bggid); 0178 } 0179 0180 QString title = entry_->field(QStringLiteral("title")); 0181 if(!title.isEmpty()) { 0182 return FetchRequest(Title, title); 0183 } 0184 return FetchRequest(); 0185 } 0186 0187 Tellico::Fetch::ConfigWidget* VideoGameGeekFetcher::configWidget(QWidget* parent_) const { 0188 return new VideoGameGeekFetcher::ConfigWidget(parent_, this); 0189 } 0190 0191 QString VideoGameGeekFetcher::defaultName() { 0192 return QStringLiteral("VideoGameGeek"); 0193 } 0194 0195 QString VideoGameGeekFetcher::defaultIcon() { 0196 return favIcon("https://cf.geekdo-static.com/icons/favicon2.ico"); 0197 } 0198 0199 Tellico::StringHash VideoGameGeekFetcher::allOptionalFields() { 0200 StringHash hash; 0201 hash[QStringLiteral("videogamegeek-link")] = i18n("VideoGameGeek Link"); 0202 return hash; 0203 } 0204 0205 VideoGameGeekFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const VideoGameGeekFetcher* fetcher_) 0206 : Fetch::ConfigWidget(parent_) { 0207 QGridLayout* l = new QGridLayout(optionsWidget()); 0208 l->setSpacing(4); 0209 l->setColumnStretch(1, 10); 0210 0211 int row = -1; 0212 0213 QLabel* label = new QLabel(i18n("&Image size: "), optionsWidget()); 0214 l->addWidget(label, ++row, 0); 0215 m_imageCombo = new GUI::ComboBox(optionsWidget()); 0216 m_imageCombo->addItem(i18n("No Image"), NoImage); 0217 m_imageCombo->addItem(i18n("Small Image"), SmallImage); 0218 m_imageCombo->addItem(i18n("Large Image"), LargeImage); 0219 void (GUI::ComboBox::* activatedInt)(int) = &GUI::ComboBox::activated; 0220 connect(m_imageCombo, activatedInt, this, &ConfigWidget::slotSetModified); 0221 l->addWidget(m_imageCombo, row, 1); 0222 label->setBuddy(m_imageCombo); 0223 0224 l->setRowStretch(++row, 10); 0225 0226 // now add additional fields widget 0227 addFieldsWidget(VideoGameGeekFetcher::allOptionalFields(), fetcher_ ? fetcher_->optionalFields() : QStringList()); 0228 0229 if(fetcher_) { 0230 m_imageCombo->setCurrentData(fetcher_->m_imageSize); 0231 } else { // defaults 0232 m_imageCombo->setCurrentData(SmallImage); 0233 } 0234 } 0235 0236 void VideoGameGeekFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) { 0237 const int n = m_imageCombo->currentData().toInt(); 0238 config_.writeEntry("Image Size", n); 0239 } 0240 0241 QString VideoGameGeekFetcher::ConfigWidget::preferredName() const { 0242 return VideoGameGeekFetcher::defaultName(); 0243 }