Changeset View
Changeset View
Standalone View
Standalone View
src/contentlist/BalooContentLister.cpp
Show All 26 Lines | |||||
27 | #include <KFileMetaData/UserMetaData> | 27 | #include <KFileMetaData/UserMetaData> | ||
28 | 28 | | |||
29 | #include <QDateTime> | 29 | #include <QDateTime> | ||
30 | #include <QDebug> | 30 | #include <QDebug> | ||
31 | #include <QList> | 31 | #include <QList> | ||
32 | #include <QFileInfo> | 32 | #include <QFileInfo> | ||
33 | #include <QProcess> | 33 | #include <QProcess> | ||
34 | #include <QThreadPool> | 34 | #include <QThreadPool> | ||
35 | #include <QMimeDatabase> | ||||
36 | | ||||
37 | #include "ContentQuery.h" | ||||
35 | 38 | | |||
36 | class BalooContentLister::Private | 39 | class BalooContentLister::Private | ||
37 | { | 40 | { | ||
38 | public: | 41 | public: | ||
39 | Private() {} | 42 | Private(BalooContentLister* qq) : q(qq) {} | ||
43 | | ||||
44 | BalooContentLister* q = nullptr; | ||||
45 | | ||||
46 | Baloo::QueryRunnable* createQuery(ContentQuery* contentQuery, const QString& location = QString{}); | ||||
47 | | ||||
40 | QStringList knownFiles; | 48 | QStringList knownFiles; | ||
41 | QStringList locations; | 49 | QStringList locations; | ||
42 | QString searchString; | 50 | QString searchString; | ||
43 | QList<Baloo::QueryRunnable*> queries; | 51 | QList<Baloo::QueryRunnable*> queries; | ||
44 | QList<QString> queryLocations; | 52 | QList<QString> queryLocations; | ||
53 | | ||||
54 | QMimeDatabase mimeDatabase; | ||||
45 | }; | 55 | }; | ||
46 | 56 | | |||
47 | BalooContentLister::BalooContentLister(QObject* parent) | 57 | BalooContentLister::BalooContentLister(QObject* parent) | ||
48 | : ContentListerBase(parent) | 58 | : ContentListerBase(parent) | ||
49 | , d(new Private) | 59 | , d(new Private(this)) | ||
50 | { | 60 | { | ||
51 | } | 61 | } | ||
52 | 62 | | |||
53 | BalooContentLister::~BalooContentLister() | 63 | BalooContentLister::~BalooContentLister() | ||
54 | { | 64 | { | ||
65 | QThreadPool::globalInstance()->waitForDone(); | ||||
55 | delete d; | 66 | delete d; | ||
56 | } | 67 | } | ||
57 | 68 | | |||
58 | bool BalooContentLister::balooEnabled() const | 69 | bool BalooContentLister::balooEnabled() const | ||
59 | { | 70 | { | ||
60 | Baloo::IndexerConfig config; | 71 | Baloo::IndexerConfig config; | ||
61 | bool result = config.fileIndexingEnabled(); | 72 | bool result = config.fileIndexingEnabled(); | ||
62 | 73 | | |||
63 | if(result) | 74 | if(result) | ||
64 | { | 75 | { | ||
65 | // It would be terribly nice with a bit of baloo engine exporting, so | 76 | // It would be terribly nice with a bit of baloo engine exporting, so | ||
66 | // we can ask the database about whether or not it is accessible... | 77 | // we can ask the database about whether or not it is accessible... | ||
67 | // But, this is a catch-all check anyway, so we get a complete "everything's broken" | 78 | // But, this is a catch-all check anyway, so we get a complete "everything's broken" | ||
68 | // result if anything is broken... guess it will do :) | 79 | // result if anything is broken... guess it will do :) | ||
69 | QProcess statuscheck; | 80 | QProcess statuscheck; | ||
70 | statuscheck.start("balooctl", QStringList() << "status"); | 81 | statuscheck.start("balooctl", QStringList() << "status"); | ||
71 | statuscheck.waitForFinished(); | 82 | statuscheck.waitForFinished(); | ||
72 | QString output = statuscheck.readAll(); | 83 | QString output = statuscheck.readAll(); | ||
73 | qDebug() << "Baloo status check says:" << output; | | |||
74 | if(statuscheck.exitStatus() == QProcess::CrashExit || statuscheck.exitCode() != 0) | 84 | if(statuscheck.exitStatus() == QProcess::CrashExit || statuscheck.exitCode() != 0) | ||
75 | { | 85 | { | ||
76 | result = false; | 86 | result = false; | ||
77 | } | 87 | } | ||
78 | } | 88 | } | ||
79 | 89 | | |||
80 | return result; | 90 | return result; | ||
81 | } | 91 | } | ||
82 | 92 | | |||
83 | void BalooContentLister::addLocation(QString path) | 93 | void BalooContentLister::startSearch(const QList<ContentQuery*>& queries) | ||
84 | { | 94 | { | ||
85 | d->locations.append(path); | 95 | for(const auto& query : queries) | ||
86 | } | | |||
87 | | ||||
88 | void BalooContentLister::addMimetype(QString mimetype) | | |||
89 | { | 96 | { | ||
90 | Q_UNUSED(mimetype) | 97 | for(const auto& location : query->locations()) | ||
91 | // yes, unsatisfactory... we're using this to find comic books for now, and their mimetypes are terrible | | |||
92 | } | | |||
93 | | ||||
94 | void BalooContentLister::setSearchString(const QString& searchString) | | |||
95 | { | | |||
96 | d->searchString = searchString; | | |||
97 | } | | |||
98 | | ||||
99 | void BalooContentLister::setKnownFiles(QStringList knownFiles) | | |||
100 | { | 98 | { | ||
101 | d->knownFiles = knownFiles; | 99 | d->queries.append(d->createQuery(query, location)); | ||
102 | } | 100 | } | ||
103 | 101 | | |||
104 | void BalooContentLister::startSearch() | 102 | if(query->locations().isEmpty()) | ||
105 | { | 103 | d->queries.append(d->createQuery(query)); | ||
106 | Q_FOREACH(const QString& location, d->locations) | | |||
107 | { | | |||
108 | Baloo::Query query; | | |||
109 | query.setSearchString(d->searchString); | | |||
110 | query.setIncludeFolder(location); | | |||
111 | | ||||
112 | Baloo::QueryRunnable *runnable = new Baloo::QueryRunnable(query); | | |||
113 | connect(runnable, SIGNAL(queryResult(Baloo::QueryRunnable*, QString)), | | |||
114 | this, SLOT(queryResult(Baloo::QueryRunnable*, QString)), Qt::QueuedConnection); | | |||
115 | connect(runnable, SIGNAL(finished(Baloo::QueryRunnable*)), | | |||
116 | this, SLOT(queryCompleted(Baloo::QueryRunnable*))); | | |||
117 | | ||||
118 | d->queries.append(runnable); | | |||
119 | d->queryLocations.append(location); | | |||
120 | } | 104 | } | ||
121 | // This ensures that, should we decide to search more stuff later, we can do so granularly | | |||
122 | d->locations.clear(); | | |||
123 | 105 | | |||
124 | if(!d->queries.empty()) | 106 | if(!d->queries.empty()) | ||
125 | { | 107 | { | ||
126 | QThreadPool::globalInstance()->start(d->queries.first()); | 108 | QThreadPool::globalInstance()->start(d->queries.first()); | ||
127 | } | 109 | } | ||
128 | } | 110 | } | ||
129 | 111 | | |||
130 | void BalooContentLister::queryCompleted(Baloo::QueryRunnable* query) | 112 | void BalooContentLister::queryCompleted(Baloo::QueryRunnable* query) | ||
131 | { | 113 | { | ||
132 | d->queries.removeAll(query); | 114 | d->queries.removeAll(query); | ||
133 | d->queryLocations.takeFirst(); | | |||
134 | if(d->queries.empty()) | 115 | if(d->queries.empty()) | ||
135 | { | 116 | { | ||
136 | emit searchCompleted(); | 117 | emit searchCompleted(); | ||
137 | } | 118 | } | ||
138 | else | 119 | else | ||
139 | { | 120 | { | ||
140 | QThreadPool::globalInstance()->start(d->queries.first()); | 121 | QThreadPool::globalInstance()->start(d->queries.first()); | ||
141 | } | 122 | } | ||
142 | } | 123 | } | ||
143 | 124 | | |||
144 | void BalooContentLister::queryResult(Baloo::QueryRunnable* query, QString file) | 125 | void BalooContentLister::queryResult(const ContentQuery* query, const QString& location, const QString& file) | ||
145 | { | 126 | { | ||
146 | Q_UNUSED(query) | | |||
147 | | ||||
148 | if(d->knownFiles.contains(file)) { | 127 | if(d->knownFiles.contains(file)) { | ||
149 | return; | 128 | return; | ||
150 | } | 129 | } | ||
151 | 130 | | |||
152 | // wow, this isn't nice... why is baloo not limiting searches like it's supposed to? | 131 | // wow, this isn't nice... why is baloo not limiting searches like it's supposed to? | ||
153 | if(!file.startsWith(d->queryLocations.first())) { | 132 | if(!file.startsWith(location)) { | ||
leinir: This line crashes for me, when attempting to read the __location property (also, a quick check… | |||||
It seems Baloo passes an invalid runnable. :( Fixed now by using a lambda as discussed on IRC. :) ahiemstra: It seems Baloo passes an invalid runnable. :( Fixed now by using a lambda as discussed on IRC. | |||||
Silly Baloo... Oh well, this is nice and clean anyway, so that works! :) Yay for lamdas ;) leinir: Silly Baloo... Oh well, this is nice and clean anyway, so that works! :) Yay for lamdas ;) | |||||
154 | return; | 133 | return; | ||
155 | } | 134 | } | ||
156 | 135 | | |||
157 | QVariantHash metadata; | 136 | // Like the one above, this is also not nice: apparently Baloo can return results to | ||
137 | // files that no longer exist on the file system. So we have to check manually whether | ||||
138 | // the results provided are actually sensible results... | ||||
139 | if(!QFile::exists(file)) { | ||||
140 | return; | ||||
141 | } | ||||
142 | | ||||
143 | // It would be nice if Baloo could do mime type filtering on its own... | ||||
144 | auto mimeType = d->mimeDatabase.mimeTypeForFile(file).name(); | ||||
145 | if(!query->mimeTypes().isEmpty() && !query->mimeTypes().contains(mimeType)) | ||||
146 | return; | ||||
147 | | ||||
148 | auto metadata = metaDataForFile(file); | ||||
158 | 149 | | |||
159 | Baloo::File balooFile(file); | 150 | Baloo::File balooFile(file); | ||
160 | balooFile.load(); | 151 | balooFile.load(); | ||
161 | KFileMetaData::PropertyMap properties = balooFile.properties(); | 152 | KFileMetaData::PropertyMap properties = balooFile.properties(); | ||
162 | KFileMetaData::PropertyMap::const_iterator it = properties.constBegin(); | 153 | KFileMetaData::PropertyMap::const_iterator it = properties.constBegin(); | ||
163 | for (; it != properties.constEnd(); it++) | 154 | for (; it != properties.constEnd(); it++) | ||
164 | { | 155 | { | ||
165 | KFileMetaData::PropertyInfo propInfo(it.key()); | 156 | KFileMetaData::PropertyInfo propInfo(it.key()); | ||
166 | metadata[propInfo.name()] = it.value(); | 157 | metadata[propInfo.name()] = it.value(); | ||
167 | // qDebug() << KFileMetaData::PropertyInfo(it.key()).name() << " --> " | | |||
168 | // << it.value().toString() << " (" << it.value().typeName() << ")\n"; | | |||
169 | } | 158 | } | ||
170 | QFileInfo info(file); | | |||
171 | metadata["lastModified"] = info.lastModified(); | | |||
172 | metadata["created"] = info.created(); | | |||
173 | metadata["lastRead"] = info.lastRead(); | | |||
174 | | ||||
175 | KFileMetaData::UserMetaData data(file); | | |||
176 | int currentPage = data.attribute("peruse.currentPage").toInt(); | | |||
177 | metadata["currentPage"] = QVariant::fromValue<int>(currentPage); | | |||
178 | int totalPages = data.attribute("peruse.totalPages").toInt(); | | |||
179 | metadata["totalPages"] = QVariant::fromValue<int>(totalPages); | | |||
180 | 159 | | |||
181 | emit fileFound(file, metadata); | 160 | emit fileFound(file, metadata); | ||
182 | } | 161 | } | ||
162 | | ||||
163 | Baloo::QueryRunnable* BalooContentLister::Private::createQuery(ContentQuery* contentQuery, const QString& location) | ||||
164 | { | ||||
165 | auto balooQuery = Baloo::Query{}; | ||||
166 | if(!location.isEmpty()) | ||||
167 | balooQuery.setIncludeFolder(location); | ||||
168 | | ||||
169 | switch(contentQuery->type()) | ||||
170 | { | ||||
171 | case ContentQuery::Audio: | ||||
172 | balooQuery.setType("Audio"); | ||||
173 | break; | ||||
174 | case ContentQuery::Documents: | ||||
175 | balooQuery.setType("Document"); | ||||
176 | break; | ||||
177 | case ContentQuery::Images: | ||||
178 | balooQuery.setType("Image"); | ||||
179 | break; | ||||
180 | case ContentQuery::Video: | ||||
181 | balooQuery.setType("Video"); | ||||
182 | break; | ||||
183 | default: | ||||
184 | break; | ||||
185 | } | ||||
186 | | ||||
187 | if(!contentQuery->searchString().isEmpty()) | ||||
188 | balooQuery.setSearchString(contentQuery->searchString()); | ||||
189 | | ||||
190 | auto runnable = new Baloo::QueryRunnable{balooQuery}; | ||||
191 | connect(runnable, &Baloo::QueryRunnable::queryResult, [this, contentQuery, location](QRunnable*, const QString& file) { | ||||
192 | q->queryResult(contentQuery, location, file); | ||||
193 | }); | ||||
194 | connect(runnable, &Baloo::QueryRunnable::finished, q, &BalooContentLister::queryCompleted); | ||||
195 | | ||||
196 | return runnable; | ||||
197 | } |
This line crashes for me, when attempting to read the __location property (also, a quick check and it looks to be crashing on the very first result):
Thread 1 (Thread 0x7ffff7f9a8c0 (LWP 27790)):