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
129 ~FileMetaDataProviderPrivate()
133 void insertEditableData();
135 void processFileItems();
143 void insertSingleFileBasicData();
148 void insertFilesListBasicData();
160 void slotFileFetchFinished(
KJob *job);
163void FileMetaDataProviderPrivate::slotFileFetchFinished(
KJob *job)
165 auto fetchJob =
static_cast<FileFetchJob *
>(job);
170 if (files.
size() > 1) {
171 Baloo::Private::mergeCommonData(m_data, files);
173 m_data = unite(m_data, files.
first());
175 extractDerivedProperties(m_data);
176 m_readOnly = !fetchJob->canEditAll();
178 insertEditableData();
182void FileMetaDataProviderPrivate::insertSingleFileBasicData()
186 Q_ASSERT(m_fileItems.
count() == 1);
193 const QPair<int, int> counts = subDirectoriesCount(item.
url().
path());
194 const int count = counts.first;
196 QString itemCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", count);
197 m_data.insert(QStringLiteral(
"kfileitem#size"), itemCountString);
199 const int hiddenCount = counts.second;
200 if (hiddenCount > 0) {
202 QString hiddenCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", hiddenCount);
203 m_data.insert(QStringLiteral(
"kfileitem#hiddenItems"), hiddenCountString);
218 m_data.insert(QStringLiteral(
"kfileitem#type"), item.
mimeComment());
220 m_data.insert(QStringLiteral(
"kfileitem#linkDest"), item.
linkDest());
225 QDateTime modificationTime = item.
time(KFileItem::ModificationTime);
226 if (modificationTime.
isValid()) {
227 m_data.insert(QStringLiteral(
"kfileitem#modified"), modificationTime);
231 m_data.insert(QStringLiteral(
"kfileitem#created"), creationTime);
235 m_data.insert(QStringLiteral(
"kfileitem#accessed"), accessTime);
238 m_data.insert(QStringLiteral(
"kfileitem#owner"), item.
user());
239 m_data.insert(QStringLiteral(
"kfileitem#group"), item.
group());
240 m_data.insert(QStringLiteral(
"kfileitem#permissions"), item.
permissionsString());
243 for (
int i = 0; i < extraFields.count(); ++i) {
244 const auto &field = extraFields.at(i);
245 if (field.type == KProtocolInfo::ExtraField::Invalid) {
256 if (field.type == KProtocolInfo::ExtraField::DateTime) {
262 m_data.insert(key, date);
264 m_data.insert(key, text);
270void FileMetaDataProviderPrivate::insertFilesListBasicData()
273 Q_ASSERT(m_fileItems.
count() > 1);
274 bool allDirectories =
true;
275 for (
const KFileItem &item : std::as_const(m_fileItems)) {
276 allDirectories &= item.
isDir();
277 if (!allDirectories) {
282 if (allDirectories) {
285 for (
const KFileItem &item : std::as_const(m_fileItems)) {
289 const QPair<int, int> counts = subDirectoriesCount(item.
url().
path());
290 const int subcount = counts.first;
291 if (subcount == -1) {
295 hiddenCount += counts.second;
297 QString itemCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", count);
298 if (hiddenCount > 0) {
300 QString hiddenCountString =
i18ncp(
"@item:intable",
"%1 item",
"%1 items", hiddenCount);
301 m_data.insert(QStringLiteral(
"kfileitem#hiddenItems"), hiddenCountString);
303 m_data.insert(QStringLiteral(
"kfileitem#totalSize"), itemCountString);
307 quint64 totalSize = 0;
308 for (
const KFileItem &item : std::as_const(m_fileItems)) {
310 totalSize += item.
size();
314 m_data.insert(QStringLiteral(
"kfileitem#totalSize"), format.
formatByteSize(totalSize));
318void FileMetaDataProviderPrivate::insertEditableData()
321 if (!m_data.contains(QStringLiteral(
"tags"))) {
322 m_data.insert(QStringLiteral(
"tags"),
QVariant());
324 if (!m_data.contains(QStringLiteral(
"rating"))) {
325 m_data.insert(QStringLiteral(
"rating"), 0);
327 if (!m_data.contains(QStringLiteral(
"userComment"))) {
328 m_data.insert(QStringLiteral(
"userComment"),
QVariant());
333FileMetaDataProvider::FileMetaDataProvider(
QObject *parent)
335 , d(new FileMetaDataProviderPrivate(this))
339FileMetaDataProvider::~FileMetaDataProvider() =
default;
341void FileMetaDataProviderPrivate::processFileItems()
353 bool singleFileMode = m_fileItems.
size() <= 1;
359 for (
const KFileItem &item : std::as_const(m_fileItems)) {
366 if (singleFileMode) {
367 insertSingleFileBasicData();
369 insertFilesListBasicData();
374 bool canEdit = (urls.
size() == m_fileItems.
size());
377 auto indexingMode = FileFetchJob::UseRealtimeIndexing::Disabled;
379 if (singleFileMode) {
381 indexingMode = FileFetchJob::UseRealtimeIndexing::Fallback;
383 if (!m_config.fileIndexingEnabled() || !m_config.
shouldBeIndexed(urls.
first()) || m_config.onlyBasicIndexing()) {
385 indexingMode = FileFetchJob::UseRealtimeIndexing::Only;
389 auto job =
new FileFetchJob(urls, canEdit, indexingMode,
this);
402 d->m_fileItems = items;
408 d->processFileItems();
415 {QStringLiteral(
"kfileitem#comment"),
i18nc(
"@label",
"Comment")},
416 {QStringLiteral(
"kfileitem#created"),
i18nc(
"@label",
"Created")},
417 {QStringLiteral(
"kfileitem#accessed"),
i18nc(
"@label",
"Accessed")},
418 {QStringLiteral(
"kfileitem#modified"),
i18nc(
"@label",
"Modified")},
419 {QStringLiteral(
"kfileitem#owner"),
i18nc(
"@label",
"Owner")},
420 {QStringLiteral(
"kfileitem#group"),
i18nc(
"@label",
"Group")},
421 {QStringLiteral(
"kfileitem#permissions"),
i18nc(
"@label",
"Permissions")},
422 {QStringLiteral(
"kfileitem#rating"),
i18nc(
"@label",
"Rating")},
423 {QStringLiteral(
"kfileitem#size"),
i18nc(
"@label",
"Size")},
424 {QStringLiteral(
"kfileitem#tags"),
i18nc(
"@label",
"Tags")},
425 {QStringLiteral(
"kfileitem#totalSize"),
i18nc(
"@label",
"Total Size")},
426 {QStringLiteral(
"kfileitem#hiddenItems"),
i18nc(
"@label",
"Hidden items")},
427 {QStringLiteral(
"kfileitem#type"),
i18nc(
"@label",
"Type")},
428 {QStringLiteral(
"kfileitem#linkDest"),
i18nc(
"@label",
"Link to")},
429 {QStringLiteral(
"kfileitem#targetUrl"),
i18nc(
"@label",
"Points to")},
430 {QStringLiteral(
"tags"),
i18nc(
"@label",
"Tags")},
431 {QStringLiteral(
"rating"),
i18nc(
"@label",
"Rating")},
432 {QStringLiteral(
"userComment"),
i18nc(
"@label",
"Comment")},
433 {QStringLiteral(
"originUrl"),
i18nc(
"@label",
"Downloaded From")},
434 {QStringLiteral(
"dimensions"),
i18nc(
"@label",
"Dimensions")},
435 {QStringLiteral(
"gpsLocation"),
i18nc(
"@label",
"GPS Location")},
440 static const auto extraPrefix = QStringLiteral(
"kfileitem#extra_");
442#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
443 const auto parts = metaDataLabel.splitRef(
QLatin1Char(
'_'));
447 Q_ASSERT(parts.count() == 3);
448 const auto protocol = parts.at(1);
449 const int extraNumber = parts.at(2).toInt() - 1;
453#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
458 const auto extraFields = KProtocolInfo::extraFields(url);
459 auto field = extraFields.value(extraNumber);
460 if (field.type != KProtocolInfo::ExtraField::Invalid) {
478 {QStringLiteral(
"kfileitem#type"), QStringLiteral(
"0FileItemA")},
479 {QStringLiteral(
"kfileitem#linkDest"), QStringLiteral(
"0FileItemB")},
480 {QStringLiteral(
"kfileitem#size"), QStringLiteral(
"0FileItemC")},
481 {QStringLiteral(
"kfileitem#totalSize"), QStringLiteral(
"0FileItemC")},
482 {QStringLiteral(
"kfileitem#hiddenItems"), QStringLiteral(
"0FileItemD")},
483 {QStringLiteral(
"kfileitem#modified"), QStringLiteral(
"0FileItemE")},
484 {QStringLiteral(
"kfileitem#accessed"), QStringLiteral(
"0FileItemF")},
485 {QStringLiteral(
"kfileitem#created"), QStringLiteral(
"0FileItemG")},
486 {QStringLiteral(
"kfileitem#owner"), QStringLiteral(
"0FileItemH")},
487 {QStringLiteral(
"kfileitem#group"), QStringLiteral(
"0FileItemI")},
488 {QStringLiteral(
"kfileitem#permissions"), QStringLiteral(
"0FileItemJ")},
491 {QStringLiteral(
"tags"), QStringLiteral(
"1EditableDataA")},
492 {QStringLiteral(
"rating"), QStringLiteral(
"1EditableDataB")},
493 {QStringLiteral(
"userComment"), QStringLiteral(
"1EditableDataC")},
496 {QStringLiteral(
"width"), QStringLiteral(
"2ImageA")},
497 {QStringLiteral(
"height"), QStringLiteral(
"2ImageB")},
498 {QStringLiteral(
"dimensions"), QStringLiteral(
"2ImageCA")},
499 {QStringLiteral(
"photoFNumber"), QStringLiteral(
"2ImageC")},
500 {QStringLiteral(
"photoExposureTime"), QStringLiteral(
"2ImageD")},
501 {QStringLiteral(
"photoExposureBiasValue"), QStringLiteral(
"2ImageE")},
502 {QStringLiteral(
"photoISOSpeedRatings"), QStringLiteral(
"2ImageF")},
503 {QStringLiteral(
"photoFocalLength"), QStringLiteral(
"2ImageG")},
504 {QStringLiteral(
"photoFocalLengthIn35mmFilm"), QStringLiteral(
"2ImageH")},
505 {QStringLiteral(
"photoFlash"), QStringLiteral(
"2ImageI")},
506 {QStringLiteral(
"imageOrientation"), QStringLiteral(
"2ImageJ")},
507 {QStringLiteral(
"photoGpsLocation"), QStringLiteral(
"2ImageK")},
508 {QStringLiteral(
"photoGpsLatitude"), QStringLiteral(
"2ImageL")},
509 {QStringLiteral(
"photoGpsLongitude"), QStringLiteral(
"2ImageM")},
510 {QStringLiteral(
"photoGpsAltitude"), QStringLiteral(
"2ImageN")},
511 {QStringLiteral(
"manufacturer"), QStringLiteral(
"2ImageO")},
512 {QStringLiteral(
"model"), QStringLiteral(
"2ImageP")},
515 {QStringLiteral(
"title"), QStringLiteral(
"3MediaA")},
516 {QStringLiteral(
"artist"), QStringLiteral(
"3MediaB")},
517 {QStringLiteral(
"album"), QStringLiteral(
"3MediaC")},
518 {QStringLiteral(
"albumArtist"), QStringLiteral(
"3MediaD")},
519 {QStringLiteral(
"genre"), QStringLiteral(
"3MediaE")},
520 {QStringLiteral(
"trackNumber"), QStringLiteral(
"3MediaF")},
521 {QStringLiteral(
"discNumber"), QStringLiteral(
"3MediaG")},
522 {QStringLiteral(
"releaseYear"), QStringLiteral(
"3MediaH")},
523 {QStringLiteral(
"duration"), QStringLiteral(
"3MediaI")},
524 {QStringLiteral(
"sampleRate"), QStringLiteral(
"3MediaJ")},
525 {QStringLiteral(
"bitRate"), QStringLiteral(
"3MediaK")},
528 {QStringLiteral(
"originUrl"), QStringLiteral(
"4MiscA")},
533 return QStringLiteral(
"lastGroup");
540 return d->m_fileItems;
545 d->m_readOnly = readOnly;
548bool FileMetaDataProvider::isReadOnly()
const
550 return d->m_readOnly;
558#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
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)