File indexing completed on 2024-12-15 04:14:22
0001 /* 0002 * Copyright (C) 2015 Dan Leinir Turthra Jensen <admin@leinir.dk> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Lesser General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2.1 of the License, or (at your option) version 3, or any 0008 * later version accepted by the membership of KDE e.V. (or its 0009 * successor approved by the membership of KDE e.V.), which shall 0010 * act as a proxy defined in Section 6 of version 3 of the license. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Lesser General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Lesser General Public 0018 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 0019 * 0020 */ 0021 0022 #include "BalooContentLister.h" 0023 0024 #include <Baloo/IndexerConfig> 0025 #include <Baloo/File> 0026 #include <KFileMetaData/PropertyInfo> 0027 #include <KFileMetaData/UserMetaData> 0028 0029 #include <QDateTime> 0030 #include <QList> 0031 #include <QFileInfo> 0032 #include <QProcess> 0033 #include <QThreadPool> 0034 #include <QMimeDatabase> 0035 0036 #include "ContentQuery.h" 0037 0038 class BalooContentLister::Private 0039 { 0040 public: 0041 Private(BalooContentLister* qq) : q(qq) {} 0042 0043 BalooContentLister* q = nullptr; 0044 0045 Baloo::QueryRunnable* createQuery(ContentQuery* contentQuery, const QString& location = QString{}); 0046 0047 QStringList locations; 0048 QString searchString; 0049 QList<Baloo::QueryRunnable*> queries; 0050 QList<QString> queryLocations; 0051 0052 QMimeDatabase mimeDatabase; 0053 }; 0054 0055 BalooContentLister::BalooContentLister(QObject* parent) 0056 : ContentListerBase(parent) 0057 , d(new Private(this)) 0058 { 0059 } 0060 0061 BalooContentLister::~BalooContentLister() 0062 { 0063 QThreadPool::globalInstance()->waitForDone(); 0064 delete d; 0065 } 0066 0067 bool BalooContentLister::balooEnabled() const 0068 { 0069 // Baloo is not intended to be used outside of Plasma sessions 0070 // and so we can bypass all the testing if we are not actually 0071 // in a full KDE session. 0072 bool result{qEnvironmentVariableIsSet("KDE_FULL_SESSION")}; 0073 0074 if (result) { 0075 Baloo::IndexerConfig config; 0076 result = config.fileIndexingEnabled(); 0077 0078 if(result) 0079 { 0080 // It would be terribly nice with a bit of baloo engine exporting, so 0081 // we can ask the database about whether or not it is accessible... 0082 // But, this is a catch-all check anyway, so we get a complete "everything's broken" 0083 // result if anything is broken... guess it will do :) 0084 QProcess statuscheck; 0085 statuscheck.start("balooctl", QStringList() << "status"); 0086 statuscheck.waitForFinished(); 0087 QString output = statuscheck.readAll(); 0088 if(statuscheck.exitStatus() == QProcess::CrashExit || statuscheck.exitCode() != 0) 0089 { 0090 result = false; 0091 } 0092 } 0093 } 0094 0095 return result; 0096 } 0097 0098 void BalooContentLister::startSearch(const QList<ContentQuery*>& queries) 0099 { 0100 for(const auto& query : queries) 0101 { 0102 for(const auto& location : query->locations()) 0103 { 0104 d->queries.append(d->createQuery(query, location)); 0105 } 0106 0107 if(query->locations().isEmpty()) 0108 d->queries.append(d->createQuery(query)); 0109 } 0110 0111 if(!d->queries.empty()) 0112 { 0113 QThreadPool::globalInstance()->start(d->queries.first()); 0114 } 0115 } 0116 0117 void BalooContentLister::queryCompleted(Baloo::QueryRunnable* query) 0118 { 0119 d->queries.removeAll(query); 0120 if(d->queries.empty()) 0121 { 0122 emit searchCompleted(); 0123 } 0124 else 0125 { 0126 QThreadPool::globalInstance()->start(d->queries.first()); 0127 } 0128 } 0129 0130 void BalooContentLister::queryResult(const ContentQuery* query, const QString& location, const QString& file) 0131 { 0132 if(knownFiles.contains(file)) { 0133 return; 0134 } 0135 0136 // wow, this isn't nice... why is baloo not limiting searches like it's supposed to? 0137 if(!file.startsWith(location)) { 0138 return; 0139 } 0140 0141 // Like the one above, this is also not nice: apparently Baloo can return results to 0142 // files that no longer exist on the file system. So we have to check manually whether 0143 // the results provided are actually sensible results... 0144 if(!QFile::exists(file)) { 0145 return; 0146 } 0147 0148 // It would be nice if Baloo could do mime type filtering on its own... 0149 if(!query->mimeTypes().isEmpty()) { 0150 const auto &mimeType = d->mimeDatabase.mimeTypeForFile(file).name(); 0151 if(!query->mimeTypes().contains(mimeType)) 0152 return; 0153 } 0154 0155 auto metadata = metaDataForFile(file); 0156 0157 Baloo::File balooFile(file); 0158 balooFile.load(); 0159 KFileMetaData::PropertyMap properties = balooFile.properties(); 0160 KFileMetaData::PropertyMap::const_iterator it = properties.constBegin(); 0161 for (; it != properties.constEnd(); it++) 0162 { 0163 KFileMetaData::PropertyInfo propInfo(it.key()); 0164 metadata[propInfo.name()] = it.value(); 0165 } 0166 0167 emit fileFound(file, metadata); 0168 } 0169 0170 Baloo::QueryRunnable* BalooContentLister::Private::createQuery(ContentQuery* contentQuery, const QString& location) 0171 { 0172 auto balooQuery = Baloo::Query{}; 0173 if(!location.isEmpty()) 0174 balooQuery.setIncludeFolder(location); 0175 0176 switch(contentQuery->type()) 0177 { 0178 case ContentQuery::Audio: 0179 balooQuery.setType("Audio"); 0180 break; 0181 case ContentQuery::Documents: 0182 balooQuery.setType("Document"); 0183 break; 0184 case ContentQuery::Images: 0185 balooQuery.setType("Image"); 0186 break; 0187 case ContentQuery::Video: 0188 balooQuery.setType("Video"); 0189 break; 0190 default: 0191 break; 0192 } 0193 0194 if(!contentQuery->searchString().isEmpty()) 0195 balooQuery.setSearchString(contentQuery->searchString()); 0196 0197 auto runnable = new Baloo::QueryRunnable{balooQuery}; 0198 connect(runnable, &Baloo::QueryRunnable::queryResult, q, [this, contentQuery, location](QRunnable*, const QString& file) { 0199 q->queryResult(contentQuery, location, file); 0200 }); 0201 connect(runnable, &Baloo::QueryRunnable::finished, q, &BalooContentLister::queryCompleted); 0202 0203 return runnable; 0204 }