Warning, file /plasma/kdeplasma-addons/applets/comic/engine/comic.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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