File indexing completed on 2024-05-12 15:51:16

0001 // SPDX-FileCopyrightText: 2015 Dan Leinir Turthra Jensen <admin@leinir.dk>
0002 // SPDX-License-Identifier: LGPL-2.1-only or LGPL-3.0-only or LicenseRef-KDE-Accepted-LGPL
0003 
0004 #include "baloocontentlister.h"
0005 
0006 #include <Baloo/File>
0007 #include <Baloo/IndexerConfig>
0008 #include <KFileMetaData/PropertyInfo>
0009 #include <KFileMetaData/UserMetaData>
0010 
0011 #include <QDateTime>
0012 #include <QDebug>
0013 #include <QFileInfo>
0014 #include <QList>
0015 #include <QMimeDatabase>
0016 #include <QProcess>
0017 #include <QThreadPool>
0018 
0019 #include "contentquery.h"
0020 
0021 class BalooContentLister::Private
0022 {
0023 public:
0024     Private(BalooContentLister *qq)
0025         : q(qq)
0026     {
0027     }
0028 
0029     BalooContentLister *q = nullptr;
0030 
0031     Baloo::QueryRunnable *createQuery(ContentQuery *contentQuery, const QString &location = QString{});
0032 
0033     QStringList locations;
0034     QString searchString;
0035     QList<Baloo::QueryRunnable *> queries;
0036     QList<QString> queryLocations;
0037 
0038     QMimeDatabase mimeDatabase;
0039 };
0040 
0041 BalooContentLister::BalooContentLister(QObject *parent)
0042     : ContentListerBase(parent)
0043     , d(std::make_unique<Private>(this))
0044 {
0045 }
0046 
0047 BalooContentLister::~BalooContentLister()
0048 {
0049     QThreadPool::globalInstance()->waitForDone();
0050 }
0051 
0052 bool BalooContentLister::balooEnabled() const
0053 {
0054     // Baloo is not intended to be used outside of Plasma sessions
0055     // and so we can bypass all the testing if we are not actually
0056     // in a full KDE session.
0057     bool result{qEnvironmentVariableIsSet("KDE_FULL_SESSION")};
0058 
0059     if (result) {
0060         Baloo::IndexerConfig config;
0061         result = config.fileIndexingEnabled();
0062 
0063         if (result) {
0064             // It would be terribly nice with a bit of baloo engine exporting, so
0065             // we can ask the database about whether or not it is accessible...
0066             // But, this is a catch-all check anyway, so we get a complete "everything's broken"
0067             // result if anything is broken... guess it will do :)
0068             QProcess statuscheck;
0069             statuscheck.start(QStringLiteral("balooctl"), QStringList() << QStringLiteral("status"));
0070             statuscheck.waitForFinished();
0071             if (statuscheck.exitStatus() == QProcess::CrashExit || statuscheck.exitCode() != 0) {
0072                 result = false;
0073             }
0074         }
0075     }
0076 
0077     return result;
0078 }
0079 
0080 void BalooContentLister::startSearch(const QList<ContentQuery *> &queries)
0081 {
0082     for (const auto &query : queries) {
0083         const auto locations = query->locations();
0084         for (const auto &location : locations) {
0085             d->queries.append(d->createQuery(query, location));
0086         }
0087 
0088         if (query->locations().isEmpty()) {
0089             d->queries.append(d->createQuery(query));
0090         }
0091     }
0092 
0093     if (!d->queries.empty()) {
0094         qWarning() << "starting query" << d->queries.first();
0095         QThreadPool::globalInstance()->start(d->queries.first());
0096     }
0097 }
0098 
0099 void BalooContentLister::queryCompleted(Baloo::QueryRunnable *query)
0100 {
0101     d->queries.removeAll(query);
0102     if (d->queries.empty()) {
0103         Q_EMIT searchCompleted();
0104     } else {
0105         qWarning() << "starting query" << d->queries.first();
0106         QThreadPool::globalInstance()->start(d->queries.first());
0107     }
0108 }
0109 
0110 void BalooContentLister::queryResult(const ContentQuery *query, const QString &location, const QString &file)
0111 {
0112     Q_UNUSED(location)
0113     if (knownFiles.contains(file)) {
0114         return;
0115     }
0116 
0117     // Like the one above, this is also not nice: apparently Baloo can return results to
0118     // files that no longer exist on the file system. So we have to check manually whether
0119     // the results provided are actually sensible results...
0120     if (!QFileInfo::exists(file)) {
0121         return;
0122     }
0123 
0124     // It would be nice if Baloo could do mime type filtering on its own...
0125     if (!query->mimeTypes().isEmpty()) {
0126         const auto &mimeType = d->mimeDatabase.mimeTypeForFile(file).name();
0127         if (!query->mimeTypes().contains(mimeType))
0128             return;
0129     }
0130 
0131     auto metadata = metaDataForFile(file);
0132 
0133     Baloo::File balooFile(file);
0134     balooFile.load();
0135     auto properties = balooFile.properties();
0136     auto it = properties.constBegin();
0137     for (; it != properties.constEnd(); it++) {
0138         KFileMetaData::PropertyInfo propInfo(it.key());
0139         metadata[propInfo.name()] = it.value();
0140     }
0141 
0142     knownFiles << file;
0143     Q_EMIT fileFound(file, metadata);
0144 }
0145 
0146 Baloo::QueryRunnable *BalooContentLister::Private::createQuery(ContentQuery *contentQuery, const QString &location)
0147 {
0148     auto balooQuery = Baloo::Query{};
0149 
0150     switch (contentQuery->type()) {
0151     case ContentQuery::Audio:
0152         balooQuery.setType(QStringLiteral("Audio"));
0153         break;
0154     case ContentQuery::Documents:
0155     case ContentQuery::Epub:
0156         balooQuery.setType(QStringLiteral("Document"));
0157         break;
0158     case ContentQuery::Images:
0159         balooQuery.setType(QStringLiteral("Image"));
0160         break;
0161     case ContentQuery::Video:
0162         balooQuery.setType(QStringLiteral("Video"));
0163         break;
0164         break;
0165     default:
0166         break;
0167     }
0168 
0169     balooQuery.setSearchString(QStringLiteral("epub"));
0170 
0171     auto runnable = new Baloo::QueryRunnable{balooQuery};
0172     connect(runnable, &Baloo::QueryRunnable::queryResult, q, [this, contentQuery, location](QRunnable *, const QString &file) {
0173         q->queryResult(contentQuery, location, file);
0174     });
0175     connect(runnable, &Baloo::QueryRunnable::finished, q, &BalooContentLister::queryCompleted);
0176 
0177     return runnable;
0178 }
0179 
0180 #include "moc_baloocontentlister.cpp"