9#include "filemetadataprovider.h"
10#include "filemetadatautil_p.h"
11#include "filefetchjob.h"
13#include <Baloo/IndexerConfig>
14#include <KFileMetaData/PropertyInfo>
16#include <KLocalizedString>
17#include <KProtocolInfo>
41QVariantMap unite(
const QVariantMap &v1,
const QVariantMap &v2)
45 while (it.hasNext()) {
48 v[it.key()] = it.value();
57QPair<int, int> subDirectoriesCount(
const QString &path)
63 return QPair<int, int>(count, hiddenCount);
74 struct dirent *dirEntry =
nullptr;
75 while ((dirEntry = ::readdir(dir))) {
76 if (dirEntry->d_name[0] ==
'.') {
77 if (dirEntry->d_name[1] ==
'\0') {
81 if (dirEntry->d_name[1] ==
'.' && dirEntry->d_name[2] ==
'\0') {
93 return QPair<int, int>(count, hiddenCount);
100void extractDerivedProperties(QVariantMap &data)
102 const auto width = data.value(QStringLiteral(
"width"));
103 const auto height = data.value(QStringLiteral(
"height"));
104 if (!width.isNull() && !height.isNull()) {
105 data.insert(QStringLiteral(
"dimensions"),
QSize(width.toInt(), height.toInt()));
109 const auto gpsLatitude = data.value(QStringLiteral(
"photoGpsLatitude")).toFloat(&okLatitude);
111 const auto gpsLongitude = data.value(QStringLiteral(
"photoGpsLongitude")).toFloat(&okLongitude);
113 if (okLatitude && okLongitude) {
114 data.insert(QStringLiteral(
"gpsLocation"),
QVariant::fromValue(QPair<float, float>(gpsLatitude, gpsLongitude)));
119class Q_DECL_HIDDEN
Baloo::FileMetaDataProviderPrivate :
public QObject
126 , m_fetchJob(nullptr)
130 ~FileMetaDataProviderPrivate()
135 void insertEditableData();
137 void processFileItems();
145 void insertSingleFileBasicData();
150 void insertFilesListBasicData();
161 FileFetchJob *m_fetchJob;
164 void slotFileFetchFinished(
KJob *job);
167void FileMetaDataProviderPrivate::slotFileFetchFinished(
KJob *job)
169 auto fetchJob =
static_cast<FileFetchJob *
>(job);
174 if (files.
size() > 1) {
175 Baloo::Private::mergeCommonData(m_data, files);
177 m_data = unite(m_data, files.
first());
179 extractDerivedProperties(m_data);
180 m_readOnly = !fetchJob->canEditAll();
182 insertEditableData();
185 m_fetchJob =
nullptr;
190void FileMetaDataProviderPrivate::insertSingleFileBasicData()
194 Q_ASSERT(m_fileItems.
count() == 1);
201 const QPair<int, int> counts = subDirectoriesCount(item.
url().
path());
202 const int count = counts.first;
204 QString itemCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", count);
205 m_data.insert(QStringLiteral(
"kfileitem#size"), itemCountString);
207 const int hiddenCount = counts.second;
208 if (hiddenCount > 0) {
210 QString hiddenCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", hiddenCount);
211 m_data.insert(QStringLiteral(
"kfileitem#hiddenItems"), hiddenCountString);
226 m_data.insert(QStringLiteral(
"kfileitem#type"), item.
mimeComment());
228 m_data.insert(QStringLiteral(
"kfileitem#linkDest"), item.
linkDest());
233 QDateTime modificationTime = item.
time(KFileItem::ModificationTime);
234 if (modificationTime.
isValid()) {
235 m_data.insert(QStringLiteral(
"kfileitem#modified"), modificationTime);
239 m_data.insert(QStringLiteral(
"kfileitem#created"), creationTime);
243 m_data.insert(QStringLiteral(
"kfileitem#accessed"), accessTime);
246 m_data.insert(QStringLiteral(
"kfileitem#owner"), item.
user());
247 m_data.insert(QStringLiteral(
"kfileitem#group"), item.
group());
248 m_data.insert(QStringLiteral(
"kfileitem#permissions"), item.
permissionsString());
251 for (
int i = 0; i < extraFields.count(); ++i) {
252 const auto &field = extraFields.at(i);
253 if (field.type == KProtocolInfo::ExtraField::Invalid) {
264 if (field.type == KProtocolInfo::ExtraField::DateTime) {
270 m_data.insert(key, date);
272 m_data.insert(key, text);
278void FileMetaDataProviderPrivate::insertFilesListBasicData()
281 Q_ASSERT(m_fileItems.
count() > 1);
282 bool allDirectories =
true;
283 for (
const KFileItem &item : std::as_const(m_fileItems)) {
284 allDirectories &= item.
isDir();
285 if (!allDirectories) {
290 if (allDirectories) {
293 for (
const KFileItem &item : std::as_const(m_fileItems)) {
297 const QPair<int, int> counts = subDirectoriesCount(item.
url().
path());
298 const int subcount = counts.first;
299 if (subcount == -1) {
303 hiddenCount += counts.second;
305 QString itemCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", count);
306 if (hiddenCount > 0) {
308 QString hiddenCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", hiddenCount);
309 m_data.insert(QStringLiteral(
"kfileitem#hiddenItems"), hiddenCountString);
311 m_data.insert(QStringLiteral(
"kfileitem#totalSize"), itemCountString);
315 quint64 totalSize = 0;
316 for (
const KFileItem &item : std::as_const(m_fileItems)) {
318 totalSize += item.
size();
322 m_data.insert(QStringLiteral(
"kfileitem#totalSize"), format.
formatByteSize(totalSize));
326void FileMetaDataProviderPrivate::insertEditableData()
329 if (!m_data.contains(QStringLiteral(
"tags"))) {
330 m_data.insert(QStringLiteral(
"tags"),
QVariant());
332 if (!m_data.contains(QStringLiteral(
"rating"))) {
333 m_data.insert(QStringLiteral(
"rating"), 0);
335 if (!m_data.contains(QStringLiteral(
"userComment"))) {
336 m_data.insert(QStringLiteral(
"userComment"),
QVariant());
341FileMetaDataProvider::FileMetaDataProvider(
QObject *parent)
343 , d(new FileMetaDataProviderPrivate(this))
347FileMetaDataProvider::~FileMetaDataProvider() =
default;
349void FileMetaDataProviderPrivate::processFileItems()
361 bool singleFileMode = m_fileItems.
size() <= 1;
367 for (
const KFileItem &item : std::as_const(m_fileItems)) {
374 if (singleFileMode) {
375 insertSingleFileBasicData();
377 insertFilesListBasicData();
382 bool canEdit = (urls.
size() == m_fileItems.
size());
385 auto indexingMode = FileFetchJob::UseRealtimeIndexing::Disabled;
387 if (singleFileMode) {
389 indexingMode = FileFetchJob::UseRealtimeIndexing::Fallback;
391 if (!m_config.fileIndexingEnabled() || !m_config.
shouldBeIndexed(urls.
first()) || m_config.onlyBasicIndexing()) {
393 indexingMode = FileFetchJob::UseRealtimeIndexing::Only;
397 auto job =
new FileFetchJob(urls, canEdit, indexingMode,
this);
414 auto job = d->m_fetchJob;
415 d->m_fetchJob =
nullptr;
422 d->m_fileItems = items;
432 if (d->m_fileItems.
isEmpty()) {
435 d->processFileItems();
442 {QStringLiteral(
"kfileitem#comment"),
i18nc(
"@label",
"Comment")},
443 {QStringLiteral(
"kfileitem#created"),
i18nc(
"@label",
"Created")},
444 {QStringLiteral(
"kfileitem#accessed"),
i18nc(
"@label",
"Accessed")},
445 {QStringLiteral(
"kfileitem#modified"),
i18nc(
"@label",
"Modified")},
446 {QStringLiteral(
"kfileitem#owner"),
i18nc(
"@label",
"Owner")},
447 {QStringLiteral(
"kfileitem#group"),
i18nc(
"@label",
"Group")},
448 {QStringLiteral(
"kfileitem#permissions"),
i18nc(
"@label",
"Permissions")},
449 {QStringLiteral(
"kfileitem#rating"),
i18nc(
"@label",
"Rating")},
450 {QStringLiteral(
"kfileitem#size"),
i18nc(
"@label",
"Size")},
451 {QStringLiteral(
"kfileitem#tags"),
i18nc(
"@label",
"Tags")},
452 {QStringLiteral(
"kfileitem#totalSize"),
i18nc(
"@label",
"Total Size")},
453 {QStringLiteral(
"kfileitem#hiddenItems"),
i18nc(
"@label",
"Hidden items")},
454 {QStringLiteral(
"kfileitem#type"),
i18nc(
"@label",
"Type")},
455 {QStringLiteral(
"kfileitem#linkDest"),
i18nc(
"@label",
"Link to")},
456 {QStringLiteral(
"kfileitem#targetUrl"),
i18nc(
"@label",
"Points to")},
457 {QStringLiteral(
"tags"),
i18nc(
"@label",
"Tags")},
458 {QStringLiteral(
"rating"),
i18nc(
"@label",
"Rating")},
459 {QStringLiteral(
"userComment"),
i18nc(
"@label",
"Comment")},
460 {QStringLiteral(
"originUrl"),
i18nc(
"@label",
"Downloaded From")},
461 {QStringLiteral(
"dimensions"),
i18nc(
"@label",
"Dimensions")},
462 {QStringLiteral(
"gpsLocation"),
i18nc(
"@label",
"GPS Location")},
467 static const auto extraPrefix = QStringLiteral(
"kfileitem#extra_");
469#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
470 const auto parts = metaDataLabel.splitRef(
QLatin1Char(
'_'));
474 Q_ASSERT(parts.count() == 3);
475 const auto protocol = parts.at(1);
476 const int extraNumber = parts.at(2).toInt() - 1;
480#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
485 const auto extraFields = KProtocolInfo::extraFields(url);
486 auto field = extraFields.value(extraNumber);
487 if (field.type != KProtocolInfo::ExtraField::Invalid) {
505 {QStringLiteral(
"kfileitem#type"), QStringLiteral(
"0FileItemA")},
506 {QStringLiteral(
"kfileitem#linkDest"), QStringLiteral(
"0FileItemB")},
507 {QStringLiteral(
"kfileitem#size"), QStringLiteral(
"0FileItemC")},
508 {QStringLiteral(
"kfileitem#totalSize"), QStringLiteral(
"0FileItemC")},
509 {QStringLiteral(
"kfileitem#hiddenItems"), QStringLiteral(
"0FileItemD")},
510 {QStringLiteral(
"kfileitem#modified"), QStringLiteral(
"0FileItemE")},
511 {QStringLiteral(
"kfileitem#accessed"), QStringLiteral(
"0FileItemF")},
512 {QStringLiteral(
"kfileitem#created"), QStringLiteral(
"0FileItemG")},
513 {QStringLiteral(
"kfileitem#owner"), QStringLiteral(
"0FileItemH")},
514 {QStringLiteral(
"kfileitem#group"), QStringLiteral(
"0FileItemI")},
515 {QStringLiteral(
"kfileitem#permissions"), QStringLiteral(
"0FileItemJ")},
518 {QStringLiteral(
"tags"), QStringLiteral(
"1EditableDataA")},
519 {QStringLiteral(
"rating"), QStringLiteral(
"1EditableDataB")},
520 {QStringLiteral(
"userComment"), QStringLiteral(
"1EditableDataC")},
523 {QStringLiteral(
"width"), QStringLiteral(
"2ImageA")},
524 {QStringLiteral(
"height"), QStringLiteral(
"2ImageB")},
525 {QStringLiteral(
"dimensions"), QStringLiteral(
"2ImageCA")},
526 {QStringLiteral(
"photoFNumber"), QStringLiteral(
"2ImageC")},
527 {QStringLiteral(
"photoExposureTime"), QStringLiteral(
"2ImageD")},
528 {QStringLiteral(
"photoExposureBiasValue"), QStringLiteral(
"2ImageE")},
529 {QStringLiteral(
"photoISOSpeedRatings"), QStringLiteral(
"2ImageF")},
530 {QStringLiteral(
"photoFocalLength"), QStringLiteral(
"2ImageG")},
531 {QStringLiteral(
"photoFocalLengthIn35mmFilm"), QStringLiteral(
"2ImageH")},
532 {QStringLiteral(
"photoFlash"), QStringLiteral(
"2ImageI")},
533 {QStringLiteral(
"imageOrientation"), QStringLiteral(
"2ImageJ")},
534 {QStringLiteral(
"photoGpsLocation"), QStringLiteral(
"2ImageK")},
535 {QStringLiteral(
"photoGpsLatitude"), QStringLiteral(
"2ImageL")},
536 {QStringLiteral(
"photoGpsLongitude"), QStringLiteral(
"2ImageM")},
537 {QStringLiteral(
"photoGpsAltitude"), QStringLiteral(
"2ImageN")},
538 {QStringLiteral(
"manufacturer"), QStringLiteral(
"2ImageO")},
539 {QStringLiteral(
"model"), QStringLiteral(
"2ImageP")},
542 {QStringLiteral(
"title"), QStringLiteral(
"3MediaA")},
543 {QStringLiteral(
"artist"), QStringLiteral(
"3MediaB")},
544 {QStringLiteral(
"album"), QStringLiteral(
"3MediaC")},
545 {QStringLiteral(
"albumArtist"), QStringLiteral(
"3MediaD")},
546 {QStringLiteral(
"genre"), QStringLiteral(
"3MediaE")},
547 {QStringLiteral(
"trackNumber"), QStringLiteral(
"3MediaF")},
548 {QStringLiteral(
"discNumber"), QStringLiteral(
"3MediaG")},
549 {QStringLiteral(
"releaseYear"), QStringLiteral(
"3MediaH")},
550 {QStringLiteral(
"duration"), QStringLiteral(
"3MediaI")},
551 {QStringLiteral(
"sampleRate"), QStringLiteral(
"3MediaJ")},
552 {QStringLiteral(
"bitRate"), QStringLiteral(
"3MediaK")},
555 {QStringLiteral(
"originUrl"), QStringLiteral(
"4MiscA")},
560 return QStringLiteral(
"lastGroup");
567 return d->m_fileItems;
572 d->m_readOnly = readOnly;
575bool FileMetaDataProvider::isReadOnly()
const
577 return d->m_readOnly;
585#include "moc_filemetadataprovider.cpp"
bool shouldBeIndexed(const QString &path) const
KIO::filesize_t size() const
Q_INVOKABLE QDateTime time(KFileItem::FileTimes which) const
KIO::filesize_t recursiveSize() const
KIO::UDSEntry entry() const
QString permissionsString() const
QString mimeComment() const
QString stringValue(uint field) const
bool contains(uint field) const
virtual Q_SCRIPTABLE void start()=0
bool kill(KJob::KillVerbosity verbosity=KJob::Quietly)
static ExtraFieldList extraFields(const QUrl &url)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KCOREADDONS_EXPORT QString tildeCollapse(const QString &path)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
QByteArray encodeName(const QString &fileName)
T value(const Key &key) const const
qsizetype count() const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qsizetype count() const const
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isLocalFile() const const
QString path(ComponentFormattingOptions options) const const
QString scheme() const const
void setScheme(const QString &scheme)
QString toDisplayString(FormattingOptions options) const const
QString toLocalFile() const const
QVariant fromValue(T &&value)