Git commit 937109fb0f32192308258437ccad7f8dac536acd by Maik Qualmann. Committed on 01/04/2015 at 16:40. Pushed by mqualmann into branch 'master'.
apply patch #91806 from Kristian Karl to make an optional rescan of files when files are modified BUGS: 341772 FIXED-IN: 4.9.0 GUI: M +2 -1 NEWS M +9 -1 libs/database/collectionscanner.cpp M +3 -0 libs/dmetadata/metadatasettingscontainer.cpp M +1 -0 libs/dmetadata/metadatasettingscontainer.h M +17 -0 tests/CMakeLists.txt A +232 -0 tests/timestampupdatetest.cpp [License: GPL (v2+)] A +48 -0 tests/timestampupdatetest.h [License: GPL (v2+)] A +- -- tests/timestampupdatetestimages/1.jpg M +19 -7 utilities/setup/setupmetadata.cpp http://commits.kde.org/digikam/937109fb0f32192308258437ccad7f8dac536acd diff --git a/NEWS b/NEWS index 7412009..29de43c 100644 --- a/NEWS +++ b/NEWS @@ -33,4 +33,5 @@ BUGFIXES FROM KDE BUGZILLA (https://www.digikam.org/changelog): 025 ==> 338230 - digiKam stops refreshing download progress after de-selecting pictures. 026 ==> 345648 - Progress bar disappeared while importing pictures before importing finished. 027 ==> 340139 - digiKam hangs after selecting folder with pictures. -028 ==> +028 ==> 341772 - Re-read metadata when image file timestamp has changed [patch]. +029 ==> diff --git a/libs/database/collectionscanner.cpp b/libs/database/collectionscanner.cpp index ce2c923..5122388 100644 --- a/libs/database/collectionscanner.cpp +++ b/libs/database/collectionscanner.cpp @@ -65,6 +65,7 @@ #include "imagecopyright.h" #include "imageinfo.h" #include "imagescanner.h" +#include "metadatasettings.h" #include "tagscache.h" #include "thumbnaildatabaseaccess.h" #include "thumbnaildb.h" @@ -1199,7 +1200,14 @@ void CollectionScanner::scanFileNormal(const QFileInfo& fi, const ItemScanInfo& if (!modificationDateEquals(fi.lastModified(), scanInfo.modificationDate) || fi.size() != scanInfo.fileSize) { - scanModifiedFile(fi, scanInfo); + if (MetadataSettings::instance()->settings().rescanImageIfModified) + { + rescanFile(fi, scanInfo); + } + else + { + scanModifiedFile(fi, scanInfo); + } } } diff --git a/libs/dmetadata/metadatasettingscontainer.cpp b/libs/dmetadata/metadatasettingscontainer.cpp index 8f18e8a..a39f38b 100644 --- a/libs/dmetadata/metadatasettingscontainer.cpp +++ b/libs/dmetadata/metadatasettingscontainer.cpp @@ -50,6 +50,7 @@ MetadataSettingsContainer::MetadataSettingsContainer() useXMPSidecar4Reading = false; metadataWritingMode = KExiv2::WRITETOIMAGEONLY; updateFileTimeStamp = true; + rescanImageIfModified = false; rotationBehavior = RotatingFlags | RotateByLosslessRotation; } @@ -73,6 +74,7 @@ void MetadataSettingsContainer::readFromConfig(KConfigGroup& group) metadataWritingMode = (KExiv2::MetadataWritingMode) group.readEntry("Metadata Writing Mode", (int)KExiv2::WRITETOIMAGEONLY); updateFileTimeStamp = group.readEntry("Update File Timestamp", true); + rescanImageIfModified = group.readEntry("Rescan File If Modified", false); rotationBehavior = NoRotation; @@ -116,6 +118,7 @@ void MetadataSettingsContainer::writeToConfig(KConfigGroup& group) const group.writeEntry("Use XMP Sidecar For Reading", useXMPSidecar4Reading); group.writeEntry("Metadata Writing Mode", (int)metadataWritingMode); group.writeEntry("Update File Timestamp", updateFileTimeStamp); + group.writeEntry("Rescan File If Modified", rescanImageIfModified); group.writeEntry("Rotate By Internal Flag", bool(rotationBehavior & RotateByInternalFlag)); group.writeEntry("Rotate By Metadata Flag", bool(rotationBehavior & RotateByMetadataFlag)); diff --git a/libs/dmetadata/metadatasettingscontainer.h b/libs/dmetadata/metadatasettingscontainer.h index 38d3053..158b6d7 100644 --- a/libs/dmetadata/metadatasettingscontainer.h +++ b/libs/dmetadata/metadatasettingscontainer.h @@ -102,6 +102,7 @@ public: bool writeRawFiles; bool updateFileTimeStamp; + bool rescanImageIfModified; bool useXMPSidecar4Reading; KExiv2::MetadataWritingMode metadataWritingMode; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 271170c..6dd0f56 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -107,6 +107,23 @@ endif() #------------------------------------------------------------------------ +set(timestampupdate_SRCS + timestampupdatetest.cpp + ) + +KDE4_ADD_UNIT_TEST(timestampupdatetest ${timestampupdate_SRCS}) + +target_link_libraries(timestampupdatetest + ${KDE4_KDECORE_LIBS} + ${QT_QTGUI_LIBRARY} + ${QT_QTTEST_LIBRARY} + ${KEXIV2_LIBRARIES} + digikamcore + digikamdatabase + ) + +#------------------------------------------------------------------------ + set(statesavingobject_SRCS statesavingobjecttest.cpp ) diff --git a/tests/timestampupdatetest.cpp b/tests/timestampupdatetest.cpp new file mode 100644 index 0000000..95e2ea3 --- /dev/null +++ b/tests/timestampupdatetest.cpp @@ -0,0 +1,232 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2015 + * Description : a test for timestamp trigger for re-reading metadata from image + * + * Copyright (C) 2015 by Kristian Karl <kristian dot hermann dot karl at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#include "timestampupdatetest.h" + +#include <libkexiv2/kexiv2.h> + +#include "albumdb.h" +#include "collectionlocation.h" +#include "collectionmanager.h" +#include "collectionscanner.h" +#include "imageinfo.h" +#include "metadatasettings.h" + +#include <QtTest> +#include <QFileInfo> +#include <QDBusConnection> + +#include <kdemacros.h> + +const QString originalImageFolder(KDESRCDIR"timestampupdatetestimages"); +const QString originalImageFile(KDESRCDIR"timestampupdatetestimages/1.jpg"); + +QTEST_MAIN(TimeStampUpdateTest); + +using namespace Digikam; + +QString TimeStampUpdateTest::tempFileName(const QString& purpose) const +{ + return QString("digikamtests-") + metaObject()->className() + '-' + purpose + '-' + QTime::currentTime().toString(); +} + +QString TimeStampUpdateTest::tempFilePath(const QString& purpose) const +{ + return QDir::tempPath() + '/' + tempFileName(purpose); +} + +/* + * Create a nd simulate a new startup of Digikam. + * A new temporary database is created. + * A collection is added, and scanned. + */ +void TimeStampUpdateTest::initTestCase() +{ + // Setup the collection folder + QDir collectionDir = QDir(originalImageFolder); + QVERIFY(collectionDir.exists()); + + + // Create new temporary database + dbFile = tempFilePath("database"); + DatabaseParameters params("QSQLITE", dbFile, "QSQLITE", dbFile); + DatabaseAccess::setParameters(params, DatabaseAccess::MainApplication); + QVERIFY(DatabaseAccess::checkReadyForUse(0)); + QVERIFY(QFile(dbFile).exists()); + + + // Add collection and scan + CollectionManager::instance()->addLocation(collectionDir.path()); + CollectionScanner().completeScan(); + + + // Verify that the scanned collection is correct + QList<AlbumShortInfo> albums = DatabaseAccess().db()->getAlbumShortInfos(); + QVERIFY(albums.size() == 1); + QStringList readOnlyImages; + foreach(const AlbumShortInfo& album, albums) + { + readOnlyImages << DatabaseAccess().db()->getItemURLsInAlbum(album.id); + } + foreach(const QString& file, readOnlyImages) + { + ids << ImageInfo::fromLocalFile(file).id(); + } + QVERIFY(!ids.contains(-1)); + QVERIFY(ids.size() == 1); +} + +/* + * Remove the database file + */ +void TimeStampUpdateTest::cleanupTestCase() +{ + QFile(dbFile).remove(); +} + +/* + * Re-set the database and image file to it's original metadata state + */ +void TimeStampUpdateTest::cleanup() +{ + KExiv2Iface::KExiv2 meta; + meta.setMetadataWritingMode(KExiv2Iface::KExiv2::WRITETOIMAGEONLY); + meta.setUpdateFileTimeStamp(true); + meta.load(originalImageFile); + meta.removeExifTag("Exif.Image.Model", false); + QVERIFY2(meta.applyChanges(), "Exif.Image.Model is removed"); + QVERIFY(meta.getExifTagString("Exif.Image.Model").isEmpty()); + + CollectionScanner().scanFile(originalImageFile, CollectionScanner::Rescan); + + // Check that Exif.Image.Model in database is empty + QVariantList dbModel = DatabaseAccess().db()->getImageMetadata(ids[0], DatabaseFields::Model); + QVERIFY2(dbModel.at(0).toString().isEmpty(), "Exif.Image.Model should be empty"); +} + +/* + * This test manipulates the Exif.Image.Model without updating + * the database. + * A CollectionScanner().completeScan() is then launched, simulating a + * startup of Digikam. + * The test verifies that the change in the file is detected and + * that new value of Exif.Image.Model is read into the database. + */ +void TimeStampUpdateTest::testRescanImageIfModifiedSet2True() +{ + // Setup metadata settings + MetadataSettingsContainer set; + set.updateFileTimeStamp = true; // Deafult value + set.rescanImageIfModified = true; + MetadataSettings::instance()->setSettings(set); + + + // Load the test image and verify that it's there + QFileInfo originalFileInfo(originalImageFile); + QVERIFY(originalFileInfo.isReadable()); + + + // Check that Exif.Image.Model in database is empty + QVariantList dbModel = DatabaseAccess().db()->getImageMetadata(ids[0], DatabaseFields::Model); + QVERIFY2(dbModel.at(0).toString().isEmpty(), "Exif.Image.Model should be empty"); + + + // Verify that Exif.Image.Model in image file is empty + KExiv2Iface::KExiv2 meta; + meta.setMetadataWritingMode(KExiv2Iface::KExiv2::WRITETOIMAGEONLY); + meta.setUpdateFileTimeStamp(true); + meta.load(originalImageFile); + QString model = meta.getExifTagString("Exif.Image.Model"); + QVERIFY(model.isEmpty()); + + + // Change the metadata in image file + meta.setExifTagString("Exif.Image.Model", "TimeStampUpdateTestCamera", false); + QVERIFY2(meta.applyChanges(), "Exif.Image.Model is added"); + QVERIFY(meta.getExifTagString("Exif.Image.Model") == "TimeStampUpdateTestCamera"); + + + // Simulate restart of Digikam + // The scan should detect that image file has changed + CollectionScanner().completeScan(); + + + // Verify that the change is detected, and no exists in the database + dbModel = DatabaseAccess().db()->getImageMetadata(ids[0], DatabaseFields::Model); + QVERIFY(dbModel.at(0).toString() == "TimeStampUpdateTestCamera"); +} + + +/* + * This test manipulates the Exif.Image.Model without updating + * the database. + * A CollectionScanner().completeScan() is then launched, simulating a + * startup of Digikam. + * The test verifies that the change in the file is disregarden and + * that the value of Exif.Image.Model is unchanged the database. + */ +void TimeStampUpdateTest::testRescanImageIfModifiedSet2False() +{ + // Setup metadata settings + MetadataSettingsContainer set; + set.updateFileTimeStamp = true; // Deafult value + set.rescanImageIfModified = false; // Deafult value + MetadataSettings::instance()->setSettings(set); + + + // Load the test image and verify that it's there + QFileInfo originalFileInfo(originalImageFile); + QVERIFY(originalFileInfo.isReadable()); + + + // Check that Exif.Image.Model in database is empty + QVariantList dbModel = DatabaseAccess().db()->getImageMetadata(ids[0], DatabaseFields::Model); + QVERIFY2(dbModel.at(0).toString().isEmpty(), "Exif.Image.Model should be empty"); + + + // Verify that Exif.Image.Model in image file is empty + KExiv2Iface::KExiv2 meta; + meta.setMetadataWritingMode(KExiv2Iface::KExiv2::WRITETOIMAGEONLY); + meta.setUpdateFileTimeStamp(true); + meta.load(originalImageFile); + QString model = meta.getExifTagString("Exif.Image.Model"); + QVERIFY(model.isEmpty()); + + + // Change the metadata in image file + meta.setExifTagString("Exif.Image.Model", "TimeStampUpdateTestCamera", false); + QVERIFY2(meta.applyChanges(), "Exif.Image.Model is added"); + QVERIFY(meta.getExifTagString("Exif.Image.Model") == "TimeStampUpdateTestCamera"); + + + // Simulate restart of Digikam + // The scan should detect that image file has changed + CollectionScanner().completeScan(); + + + // Verify that the changed image did not change the database + dbModel = DatabaseAccess().db()->getImageMetadata(ids[0], DatabaseFields::Model); + QVERIFY(dbModel.at(0).toString().isEmpty()); +} + +#include "timestampupdatetest.moc" diff --git a/tests/timestampupdatetest.h b/tests/timestampupdatetest.h new file mode 100644 index 0000000..79927fd --- /dev/null +++ b/tests/timestampupdatetest.h @@ -0,0 +1,48 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2015 + * Description : a test for timestamp trigger for re-reading metadata from image + * + * Copyright (C) 2015 by Kristian Karl <kristian dot hermann dot karl at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef TIMESTAMPUPDATETEST_H +#define TIMESTAMPUPDATETEST_H + +#include <QtTest/QtTest> + +class TimeStampUpdateTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void cleanup(); + void testRescanImageIfModifiedSet2True(); + void testRescanImageIfModifiedSet2False(); + +private: + QString tempFileName(const QString& purpose) const; + QString tempFilePath(const QString& purpose) const; + + QString dbFile; + QList<qlonglong> ids; +}; + +#endif // TIMESTAMPUPDATETEST_H diff --git a/tests/timestampupdatetestimages/1.jpg b/tests/timestampupdatetestimages/1.jpg new file mode 100644 index 0000000..3b376ce Binary files /dev/null and b/tests/timestampupdatetestimages/1.jpg differ diff --git a/utilities/setup/setupmetadata.cpp b/utilities/setup/setupmetadata.cpp index 984128f..bfdac00 100644 --- a/utilities/setup/setupmetadata.cpp +++ b/utilities/setup/setupmetadata.cpp @@ -95,6 +95,7 @@ public: writeXMPSidecarBox(0), readXMPSidecarBox(0), updateFileTimeStampBox(0), + rescanImageIfModifiedBox(0), writingModeCombo(0), rotateByFlag(0), rotateByContents(0), @@ -132,6 +133,7 @@ public: QCheckBox* writeXMPSidecarBox; QCheckBox* readXMPSidecarBox; QCheckBox* updateFileTimeStampBox; + QCheckBox* rescanImageIfModifiedBox; KComboBox* writingModeCombo; QRadioButton* rotateByFlag; @@ -279,13 +281,21 @@ SetupMetadata::SetupMetadata(QWidget* const parent) "Note: disabling this option can introduce some dysfunctions with applications which use file timestamps properties to " "detect file modifications automatically.")); - readWriteLayout->addWidget(readWriteIconLabel, 0, 0); - readWriteLayout->addWidget(readWriteLabel, 0, 1); - readWriteLayout->addWidget(d->readXMPSidecarBox, 1, 0, 1, 3); - readWriteLayout->addWidget(d->writeXMPSidecarBox, 2, 0, 1, 3); - readWriteLayout->addWidget(d->writingModeCombo, 3, 1, 1, 2); - readWriteLayout->addWidget(d->writeRawFilesBox, 4, 0, 1, 3); - readWriteLayout->addWidget(d->updateFileTimeStampBox, 5, 0, 1, 3); + d->rescanImageIfModifiedBox = new QCheckBox; + d->rescanImageIfModifiedBox->setText(i18nc("@option:check", "&Rescan file when files are modified")); + d->rescanImageIfModifiedBox->setWhatsThis(i18nc("@info:whatsthis", + "Turning this option on, will force Digikam to rescan files that has been modified outside Digikam. " + "If a file has changed it's file size or if the last modified timestamp has changed, a rescan of that" + "file will be performed when Digikam starts.")); + + readWriteLayout->addWidget(readWriteIconLabel, 0, 0); + readWriteLayout->addWidget(readWriteLabel, 0, 1); + readWriteLayout->addWidget(d->readXMPSidecarBox, 1, 0, 1, 3); + readWriteLayout->addWidget(d->writeXMPSidecarBox, 2, 0, 1, 3); + readWriteLayout->addWidget(d->writingModeCombo, 3, 1, 1, 2); + readWriteLayout->addWidget(d->writeRawFilesBox, 4, 0, 1, 3); + readWriteLayout->addWidget(d->updateFileTimeStampBox, 5, 0, 1, 3); + readWriteLayout->addWidget(d->rescanImageIfModifiedBox, 6, 0, 1, 3); readWriteLayout->setColumnStretch(3, 1); d->readWriteGroup->setLayout(readWriteLayout); @@ -616,6 +626,7 @@ void SetupMetadata::applySettings() } set.updateFileTimeStamp = d->updateFileTimeStampBox->isChecked(); + set.rescanImageIfModified = d->rescanImageIfModifiedBox->isChecked(); mSettings->setSettings(set); @@ -676,6 +687,7 @@ void SetupMetadata::readSettings() d->writeRawFilesBox->setChecked(set.writeRawFiles); d->readXMPSidecarBox->setChecked(set.useXMPSidecar4Reading); d->updateFileTimeStampBox->setChecked(set.updateFileTimeStamp); + d->rescanImageIfModifiedBox->setChecked(set.rescanImageIfModified); if (set.metadataWritingMode == KExiv2::WRITETOIMAGEONLY) {
