File indexing completed on 2025-09-21 05:07:58
0001 /* 0002 * SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org> 0003 * SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include "comic.h" 0009 0010 #include <QDate> 0011 #include <QDebug> 0012 #include <QFileInfo> 0013 #include <QImage> 0014 #include <QNetworkInformation> 0015 #include <QSettings> 0016 #include <QStandardPaths> 0017 0018 #include <KPackage/PackageLoader> 0019 #include <qloggingcategory.h> 0020 0021 #include "cachedprovider.h" 0022 #include "comic_debug.h" 0023 #include "comicprovider.h" 0024 #include "comicproviderkross.h" 0025 0026 ComicEngine::ComicEngine(QObject *parent) 0027 : QObject(parent) 0028 , mEmptySuffix(false) 0029 { 0030 QNetworkInformation::instance()->loadBackendByFeatures(QNetworkInformation::Feature::Reachability); 0031 loadProviders(); 0032 } 0033 0034 QList<ComicProviderInfo> ComicEngine::loadProviders() 0035 { 0036 mProviders.clear(); 0037 const auto comics = KPackage::PackageLoader::self()->listKPackages(QStringLiteral("Plasma/Comic")); 0038 QList<ComicProviderInfo> providers; 0039 for (auto comicPackage : comics) { 0040 const KPluginMetaData comic = comicPackage.metadata(); 0041 qCDebug(PLASMA_COMIC) << "ComicEngine::loadProviders() service name=" << comic.name(); 0042 ComicProviderInfo data; 0043 data.pluginId = comic.pluginId(); 0044 data.name = comic.name(); 0045 QFileInfo file(comic.iconName()); 0046 if (file.isRelative()) { 0047 data.icon = 0048 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString::fromLatin1("plasma/comics/%1/%2").arg(comic.pluginId(), comic.iconName())); 0049 } else { 0050 data.icon = comic.iconName(); 0051 } 0052 mProviders << comic.pluginId(); 0053 providers << data; 0054 } 0055 return providers; 0056 } 0057 0058 void ComicEngine::setMaxComicLimit(int maxComicLimit) 0059 { 0060 CachedProvider::setMaxComicLimit(maxComicLimit); 0061 } 0062 0063 bool ComicEngine::requestSource(const QString &identifier) 0064 { 0065 if (m_jobs.contains(identifier)) { 0066 return true; 0067 } 0068 0069 const QStringList parts = identifier.split(QLatin1Char(':'), Qt::KeepEmptyParts); 0070 0071 // check whether it is cached, make sure second part present 0072 if (parts.count() > 1 && (CachedProvider::isCached(identifier) || !isOnline())) { 0073 ComicProvider *provider = new CachedProvider(this, KPluginMetaData{}, IdentifierType::StringIdentifier, identifier); 0074 m_jobs[identifier] = provider; 0075 connect(provider, &ComicProvider::finished, this, &ComicEngine::finished); 0076 connect(provider, &ComicProvider::error, this, &ComicEngine::error); 0077 return true; 0078 } 0079 0080 // ... start a new query otherwise 0081 if (parts.count() < 2) { 0082 Q_EMIT requestFinished(ComicMetaData{.error = true}); 0083 qWarning() << "Less than two arguments specified."; 0084 return false; 0085 } 0086 if (!mProviders.contains(parts[0])) { 0087 // User might have installed more from GHNS 0088 loadProviders(); 0089 if (!mProviders.contains(parts[0])) { 0090 Q_EMIT requestFinished(ComicMetaData{.error = true}); 0091 qWarning() << identifier << "comic plugin does not seem to be installed."; 0092 return false; 0093 } 0094 } 0095 0096 // check if there is a connection 0097 if (!isOnline()) { 0098 qCDebug(PLASMA_COMIC) << "Currently offline, requested identifier was" << mIdentifierError; 0099 mIdentifierError = identifier; 0100 ComicMetaData data; 0101 data.error = true; 0102 data.errorAutomaticallyFixable = true; 0103 data.identifier = identifier; 0104 data.previousIdentifier = lastCachedIdentifier(identifier); 0105 Q_EMIT requestFinished(data); 0106 qCDebug(PLASMA_COMIC) << "No internet connection, using cached data"; 0107 return true; 0108 } 0109 0110 KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Comic"), parts[0]); 0111 0112 bool isCurrentComic = parts[1].isEmpty(); 0113 0114 ComicProvider *provider = nullptr; 0115 0116 QVariant data; 0117 const IdentifierType identifierType = stringToIdentifierType(pkg.metadata().value(QStringLiteral("X-KDE-PlasmaComicProvider-SuffixType"))); 0118 if (identifierType == IdentifierType::DateIdentifier) { 0119 QDate date = QDate::fromString(parts[1], Qt::ISODate); 0120 if (!date.isValid()) { 0121 date = QDate::currentDate(); 0122 } 0123 0124 data = date; 0125 } else if (identifierType == IdentifierType::NumberIdentifier) { 0126 data = parts[1].toInt(); 0127 } else if (identifierType == IdentifierType::StringIdentifier) { 0128 data = parts[1]; 0129 } 0130 provider = new ComicProviderKross(this, pkg.metadata(), identifierType, data); 0131 provider->setIsCurrent(isCurrentComic); 0132 0133 m_jobs[identifier] = provider; 0134 0135 connect(provider, &ComicProvider::finished, this, &ComicEngine::finished); 0136 connect(provider, &ComicProvider::error, this, &ComicEngine::error); 0137 return true; 0138 } 0139 0140 void ComicEngine::finished(ComicProvider *provider) 0141 { 0142 // sets the data 0143 if (provider->image().isNull()) { 0144 qCWarning(PLASMA_COMIC) << "Provider returned null image" << provider->name(); 0145 error(provider); 0146 return; 0147 } 0148 0149 ComicMetaData data = metaDataFromProvider(provider); 0150 0151 // different comic -- with no error yet -- has been chosen, old error is invalidated 0152 QString temp = mIdentifierError.left(mIdentifierError.indexOf(QLatin1Char(':')) + 1); 0153 if (!mIdentifierError.isEmpty() && provider->identifier().indexOf(temp) == -1) { 0154 mIdentifierError.clear(); 0155 } 0156 // comic strip with error worked now 0157 if (!mIdentifierError.isEmpty() && (mIdentifierError == provider->identifier())) { 0158 mIdentifierError.clear(); 0159 } 0160 0161 // store in cache if it's not the response of a CachedProvider, 0162 if (!provider->inherits("CachedProvider") && !provider->image().isNull()) { 0163 CachedProvider::storeInCache(provider->identifier(), provider->image(), data); 0164 } 0165 provider->deleteLater(); 0166 0167 const QString key = m_jobs.key(provider); 0168 if (!key.isEmpty()) { 0169 m_jobs.remove(key); 0170 } 0171 Q_EMIT requestFinished(data); 0172 } 0173 0174 void ComicEngine::error(ComicProvider *provider) 0175 { 0176 QString identifier(provider->identifier()); 0177 mIdentifierError = identifier; 0178 0179 qWarning() << identifier << "plugging reported an error."; 0180 0181 ComicMetaData data = metaDataFromProvider(provider); 0182 data.error = true; 0183 0184 // if there was an error loading the last cached comic strip, do not return its id anymore 0185 const QString lastCachedId = lastCachedIdentifier(identifier); 0186 if (lastCachedId != provider->identifier().mid(provider->identifier().indexOf(QLatin1Char(':')) + 1)) { 0187 // sets the previousIdentifier to the identifier of a strip that has been cached before 0188 data.previousIdentifier = lastCachedId; 0189 } 0190 data.nextIdentifier = QString(); 0191 0192 const QString key = m_jobs.key(provider); 0193 if (!key.isEmpty()) { 0194 m_jobs.remove(key); 0195 } 0196 0197 provider->deleteLater(); 0198 Q_EMIT requestFinished(data); 0199 } 0200 0201 ComicMetaData ComicEngine::metaDataFromProvider(ComicProvider *provider) 0202 { 0203 QString identifier(provider->identifier()); 0204 0205 /** 0206 * Requests for the current day have no suffix (date or id) 0207 * set initially, so we have to remove the 'faked' suffix 0208 * here again to not confuse the applet. 0209 */ 0210 if (provider->isCurrent()) { 0211 identifier = identifier.left(identifier.indexOf(QLatin1Char(':')) + 1); 0212 } 0213 0214 ComicMetaData data; 0215 data.imageUrl = provider->imageUrl(); 0216 data.image = provider->image(); 0217 data.websiteUrl = provider->websiteUrl(); 0218 data.shopUrl = provider->shopUrl(); 0219 data.nextIdentifier = provider->nextIdentifier(); 0220 data.previousIdentifier = provider->previousIdentifier(); 0221 data.comicAuthor = provider->comicAuthor(); 0222 data.additionalText = provider->additionalText(); 0223 data.stripTitle = provider->stripTitle(); 0224 data.firstStripIdentifier = provider->firstStripIdentifier(); 0225 data.identifier = identifier; 0226 data.providerName = provider->name(); 0227 data.identifierType = provider->identifierType(); 0228 data.isLeftToRight = provider->isLeftToRight(); 0229 data.isTopToBottom = provider->isTopToBottom(); 0230 0231 return data; 0232 } 0233 0234 QString ComicEngine::lastCachedIdentifier(const QString &identifier) const 0235 { 0236 const QString id = identifier.left(identifier.indexOf(QLatin1Char(':'))); 0237 QString data = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/plasma_engine_comic/"); 0238 data += QString::fromLatin1(QUrl::toPercentEncoding(id)); 0239 QSettings settings(data + QLatin1String(".conf"), QSettings::IniFormat); 0240 QString previousIdentifier = settings.value(QLatin1String("lastCachedStripIdentifier"), QString()).toString(); 0241 0242 return previousIdentifier; 0243 } 0244 0245 bool ComicEngine::isOnline() const 0246 { 0247 return QNetworkInformation::instance()->reachability() == QNetworkInformation::Reachability::Online; 0248 }