File indexing completed on 2024-05-12 05:09:38
0001 /*************************************************************************** 0002 Copyright (C) 2011 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 "moviemeterfetcher.h" 0026 #include "../collections/videocollection.h" 0027 #include "../images/imagefactory.h" 0028 #include "../core/filehandler.h" 0029 #include "../utils/guiproxy.h" 0030 #include "../utils/mapvalue.h" 0031 #include "../tellico_debug.h" 0032 0033 #include <KLocalizedString> 0034 #include <KIO/Job> 0035 #include <KJobUiDelegate> 0036 #include <KJobWidgets/KJobWidgets> 0037 0038 #include <QLabel> 0039 #include <QFile> 0040 #include <QTextStream> 0041 #include <QGridLayout> 0042 #include <QTextCodec> 0043 #include <QJsonDocument> 0044 #include <QJsonObject> 0045 #include <QJsonArray> 0046 #include <QUrlQuery> 0047 0048 namespace { 0049 static const char* MOVIEMETER_API_KEY = "t80a06uf736d0yd00jpynpdsgea255yk"; 0050 static const char* MOVIEMETER_API_URL = "http://www.moviemeter.nl/api/film/"; 0051 } 0052 0053 using namespace Tellico; 0054 using Tellico::Fetch::MovieMeterFetcher; 0055 0056 MovieMeterFetcher::MovieMeterFetcher(QObject* parent_) 0057 : Fetcher(parent_) 0058 , m_started(false) { 0059 } 0060 0061 MovieMeterFetcher::~MovieMeterFetcher() { 0062 } 0063 0064 QString MovieMeterFetcher::source() const { 0065 return m_name.isEmpty() ? defaultName() : m_name; 0066 } 0067 0068 QString MovieMeterFetcher::attribution() const { 0069 return QStringLiteral("<a href=\"http://www.moviemeter.nl\">MovieMeter</a>"); 0070 } 0071 0072 bool MovieMeterFetcher::canSearch(Fetch::FetchKey k) const { 0073 return k == Keyword; 0074 } 0075 0076 bool MovieMeterFetcher::canFetch(int type) const { 0077 return type == Data::Collection::Video; 0078 } 0079 0080 void MovieMeterFetcher::readConfigHook(const KConfigGroup&) { 0081 } 0082 0083 void MovieMeterFetcher::search() { 0084 m_started = true; 0085 0086 QUrl u(QString::fromLatin1(MOVIEMETER_API_URL)); 0087 QUrlQuery q; 0088 q.addQueryItem(QStringLiteral("api_key"), QLatin1String(MOVIEMETER_API_KEY)); 0089 0090 switch(request().key()) { 0091 case Keyword: 0092 q.addQueryItem(QStringLiteral("q"), request().value()); 0093 //u.addQueryItem(QLatin1String("type"), QLatin1String("all")); 0094 break; 0095 0096 case Raw: 0097 q.setQuery(request().value()); 0098 break; 0099 0100 default: 0101 myWarning() << source() << "- key not recognized:" << request().key(); 0102 stop(); 0103 return; 0104 } 0105 u.setQuery(q); 0106 // myDebug() << "url: " << u.url(); 0107 0108 m_job = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo); 0109 KJobWidgets::setWindow(m_job, GUI::Proxy::widget()); 0110 connect(m_job.data(), &KJob::result, this, &MovieMeterFetcher::slotComplete); 0111 } 0112 0113 void MovieMeterFetcher::stop() { 0114 if(!m_started) { 0115 return; 0116 } 0117 if(m_job) { 0118 m_job->kill(); 0119 m_job = nullptr; 0120 } 0121 m_started = false; 0122 emit signalDone(this); 0123 } 0124 0125 Tellico::Data::EntryPtr MovieMeterFetcher::fetchEntryHook(uint uid_) { 0126 Data::EntryPtr entry = m_entries.value(uid_); 0127 if(!entry) { 0128 myWarning() << "no entry in dict"; 0129 return Data::EntryPtr(); 0130 } 0131 0132 QString id = entry->field(QStringLiteral("moviemeter-id")); 0133 if(!id.isEmpty()) { 0134 QUrl u(QString::fromLatin1(MOVIEMETER_API_URL)); 0135 u.setPath(u.path() + id); 0136 QUrlQuery q; 0137 q.addQueryItem(QStringLiteral("api_key"), QLatin1String(MOVIEMETER_API_KEY)); 0138 u.setQuery(q); 0139 // quiet 0140 QByteArray data = FileHandler::readDataFile(u, true); 0141 0142 #if 0 0143 myWarning() << "Remove debug2 from moviemeterfetcher.cpp"; 0144 QFile f(QString::fromLatin1("/tmp/test2.json")); 0145 if(f.open(QIODevice::WriteOnly)) { 0146 QTextStream t(&f); 0147 t.setCodec("UTF-8"); 0148 t << data; 0149 } 0150 f.close(); 0151 #endif 0152 0153 QJsonDocument doc = QJsonDocument::fromJson(data); 0154 populateEntry(entry, doc.object().toVariantMap(), true); 0155 } 0156 0157 // image might still be URL 0158 const QString image_id = entry->field(QStringLiteral("cover")); 0159 if(image_id.contains(QLatin1Char('/'))) { 0160 const QString id = ImageFactory::addImage(QUrl::fromUserInput(image_id), true /* quiet */); 0161 if(id.isEmpty()) { 0162 message(i18n("The cover image could not be loaded."), MessageHandler::Warning); 0163 } 0164 // empty image ID is ok 0165 entry->setField(QStringLiteral("cover"), id); 0166 } 0167 0168 // don't want to include ID field 0169 entry->setField(QStringLiteral("moviemeter-id"), QString()); 0170 0171 return entry; 0172 } 0173 0174 Tellico::Fetch::FetchRequest MovieMeterFetcher::updateRequest(Data::EntryPtr entry_) { 0175 const QString title = entry_->field(QStringLiteral("title")); 0176 if(!title.isEmpty()) { 0177 return FetchRequest(Keyword, title); 0178 } 0179 return FetchRequest(); 0180 } 0181 0182 void MovieMeterFetcher::slotComplete(KJob* job_) { 0183 KIO::StoredTransferJob* job = static_cast<KIO::StoredTransferJob*>(job_); 0184 // myDebug(); 0185 0186 if(job->error()) { 0187 job->uiDelegate()->showErrorMessage(); 0188 stop(); 0189 return; 0190 } 0191 0192 QByteArray data = job->data(); 0193 if(data.isEmpty()) { 0194 myDebug() << "no data"; 0195 stop(); 0196 return; 0197 } 0198 // see bug 319662. If fetcher is cancelled, job is killed 0199 // if the pointer is retained, it gets double-deleted 0200 m_job = nullptr; 0201 0202 #if 0 0203 myWarning() << "Remove debug from moviemeterfetcher.cpp"; 0204 QFile f(QString::fromLatin1("/tmp/test.json")); 0205 if(f.open(QIODevice::WriteOnly)) { 0206 QTextStream t(&f); 0207 t.setCodec("UTF-8"); 0208 t << data; 0209 } 0210 f.close(); 0211 #endif 0212 0213 Data::CollPtr coll(new Data::VideoCollection(true)); 0214 // always add ID for fetchEntryHook 0215 Data::FieldPtr field(new Data::Field(QStringLiteral("moviemeter-id"), QStringLiteral("MovieMeter ID"), Data::Field::Line)); 0216 field->setCategory(i18n("General")); 0217 coll->addField(field); 0218 0219 if(optionalFields().contains(QStringLiteral("moviemeter"))) { 0220 Data::FieldPtr field(new Data::Field(QStringLiteral("moviemeter"), i18n("MovieMeter Link"), Data::Field::URL)); 0221 field->setCategory(i18n("General")); 0222 coll->addField(field); 0223 } 0224 if(optionalFields().contains(QStringLiteral("alttitle"))) { 0225 Data::FieldPtr field(new Data::Field(QStringLiteral("alttitle"), i18n("Alternative Titles"), Data::Field::Table)); 0226 field->setFormatType(FieldFormat::FormatTitle); 0227 coll->addField(field); 0228 } 0229 0230 QJsonDocument doc = QJsonDocument::fromJson(data); 0231 QJsonArray array = doc.array(); 0232 for(int i = 0; i < array.count(); i++) { 0233 // myDebug() << "found result:" << result; 0234 0235 Data::EntryPtr entry(new Data::Entry(coll)); 0236 populateEntry(entry, array.at(i).toObject().toVariantMap(), false); 0237 0238 FetchResult* r = new FetchResult(this, entry); 0239 m_entries.insert(r->uid, entry); 0240 emit signalResultFound(r); 0241 } 0242 0243 stop(); 0244 } 0245 0246 void MovieMeterFetcher::populateEntry(Data::EntryPtr entry_, const QVariantMap& resultMap_, bool fullData_) { 0247 entry_->setField(QStringLiteral("moviemeter-id"), mapValue(resultMap_, "id")); 0248 entry_->setField(QStringLiteral("title"), mapValue(resultMap_, "title")); 0249 entry_->setField(QStringLiteral("year"), mapValue(resultMap_, "year")); 0250 0251 // if we only need cursory data, then we're done 0252 if(!fullData_) { 0253 return; 0254 } 0255 0256 entry_->setField(QStringLiteral("genre"), mapValue(resultMap_, "genres")); 0257 entry_->setField(QStringLiteral("plot"), mapValue(resultMap_, "plot")); 0258 entry_->setField(QStringLiteral("running-time"), mapValue(resultMap_, "duration")); 0259 entry_->setField(QStringLiteral("director"), mapValue(resultMap_, "directors")); 0260 entry_->setField(QStringLiteral("nationality"), mapValue(resultMap_, "countries")); 0261 0262 QStringList castList; 0263 foreach(const QVariant& actor, resultMap_.value(QLatin1String("actors")).toList()) { 0264 castList << mapValue(actor.toMap(), "name"); 0265 } 0266 entry_->setField(QStringLiteral("cast"), castList.join(FieldFormat::rowDelimiterString())); 0267 0268 if(entry_->collection()->hasField(QStringLiteral("moviemeter"))) { 0269 entry_->setField(QStringLiteral("moviemeter"), mapValue(resultMap_, "url")); 0270 } 0271 0272 if(entry_->collection()->hasField(QStringLiteral("alttitle"))) { 0273 entry_->setField(QStringLiteral("alttitle"), mapValue(resultMap_, "alternative_title")); 0274 } 0275 0276 entry_->setField(QStringLiteral("cover"), mapValue(resultMap_.value(QStringLiteral("posters")).toMap(), "small")); 0277 } 0278 0279 Tellico::Fetch::ConfigWidget* MovieMeterFetcher::configWidget(QWidget* parent_) const { 0280 return new MovieMeterFetcher::ConfigWidget(parent_, this); 0281 } 0282 0283 QString MovieMeterFetcher::defaultName() { 0284 return QStringLiteral("MovieMeter"); // no translation 0285 } 0286 0287 QString MovieMeterFetcher::defaultIcon() { 0288 return favIcon("https://www.moviemeter.nl"); 0289 } 0290 0291 Tellico::StringHash MovieMeterFetcher::allOptionalFields() { 0292 StringHash hash; 0293 hash[QStringLiteral("moviemeter")] = i18n("MovieMeter Link"); 0294 hash[QStringLiteral("alttitle")] = i18n("Alternative Titles"); 0295 return hash; 0296 } 0297 0298 MovieMeterFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const MovieMeterFetcher* fetcher_) 0299 : Fetch::ConfigWidget(parent_) { 0300 QVBoxLayout* l = new QVBoxLayout(optionsWidget()); 0301 l->addWidget(new QLabel(i18n("This source has no options."), optionsWidget())); 0302 l->addStretch(); 0303 0304 // now add additional fields widget 0305 addFieldsWidget(MovieMeterFetcher::allOptionalFields(), fetcher_ ? fetcher_->optionalFields() : QStringList()); 0306 } 0307 0308 void MovieMeterFetcher::ConfigWidget::saveConfigHook(KConfigGroup&) { 0309 } 0310 0311 QString MovieMeterFetcher::ConfigWidget::preferredName() const { 0312 return MovieMeterFetcher::defaultName(); 0313 }