File indexing completed on 2025-01-19 03:59:30
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2012-01-20 0007 * Description : Duplicates items finder. 0008 * 0009 * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2012 by Andi Clemens <andi dot clemens at gmail dot com> 0011 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "duplicatesfinder.h" 0018 0019 // Qt includes 0020 0021 #include <QTimer> 0022 #include <QIcon> 0023 0024 // KDE includes 0025 0026 #include <klocalizedstring.h> 0027 0028 // Local includes 0029 0030 #include "digikam_debug.h" 0031 #include "albummanager.h" 0032 #include "itemlister.h" 0033 #include "dnotificationwrapper.h" 0034 #include "digikamapp.h" 0035 #include "dbjobsthread.h" 0036 #include "dbjobsmanager.h" 0037 #include "applicationsettings.h" 0038 0039 namespace Digikam 0040 { 0041 0042 class Q_DECL_HIDDEN DuplicatesFinder::Private 0043 { 0044 public: 0045 0046 explicit Private() 0047 : minSimilarity (90), 0048 maxSimilarity (100), 0049 albumTagRelation (0), 0050 searchResultRestriction(0), 0051 refSelMethod (HaarIface::RefImageSelMethod::OlderOrLarger), 0052 isAlbumUpdate (false), 0053 job (nullptr) 0054 { 0055 } 0056 0057 int minSimilarity; 0058 int maxSimilarity; 0059 int albumTagRelation; 0060 int searchResultRestriction; 0061 HaarIface::RefImageSelMethod refSelMethod; 0062 bool isAlbumUpdate; 0063 QList<int> albumsIdList; 0064 QList<int> tagsIdList; 0065 QList<int> referenceAlbumsList; 0066 SearchesDBJobsThread* job; 0067 }; 0068 0069 DuplicatesFinder::DuplicatesFinder(const AlbumList& albums, 0070 const AlbumList& tags, 0071 int albumTagRelation, 0072 int minSimilarity, 0073 int maxSimilarity, 0074 int searchResultRestriction, 0075 HaarIface::RefImageSelMethod method, 0076 const AlbumList& referenceImageAlbum, 0077 ProgressItem* const parent) 0078 : MaintenanceTool(QLatin1String("DuplicatesFinder"), parent), 0079 d (new Private) 0080 { 0081 d->minSimilarity = minSimilarity; 0082 d->maxSimilarity = maxSimilarity; 0083 d->albumTagRelation = albumTagRelation; 0084 d->searchResultRestriction = searchResultRestriction; 0085 d->refSelMethod = method; 0086 0087 Q_FOREACH (Album* const a, albums) 0088 { 0089 d->albumsIdList << a->id(); 0090 } 0091 0092 Q_FOREACH (Album* const a, tags) 0093 { 0094 d->tagsIdList << a->id(); 0095 } 0096 0097 Q_FOREACH (Album* const a, referenceImageAlbum) 0098 { 0099 d->referenceAlbumsList << a->id(); 0100 } 0101 } 0102 0103 DuplicatesFinder::~DuplicatesFinder() 0104 { 0105 delete d; 0106 } 0107 0108 void DuplicatesFinder::slotStart() 0109 { 0110 MaintenanceTool::slotStart(); 0111 0112 setLabel(i18n("Find duplicates items")); 0113 setThumbnail(QIcon::fromTheme(QLatin1String("tools-wizard")).pixmap(22)); 0114 0115 ProgressManager::addProgressItem(this); 0116 0117 double minThresh = d->minSimilarity / 100.0; 0118 double maxThresh = d->maxSimilarity / 100.0; 0119 0120 const HaarIface::AlbumTagRelation relation = static_cast<HaarIface::AlbumTagRelation>(d->albumTagRelation); 0121 QSet<qlonglong> imageIds = HaarIface::imagesFromAlbumsAndTags(d->albumsIdList, d->tagsIdList, relation); 0122 QSet<qlonglong> referenceImageIds = HaarIface::imagesFromAlbumsAndTags(d->referenceAlbumsList, {}, HaarIface::AlbumExclusive); 0123 0124 switch(d->refSelMethod) 0125 { 0126 case HaarIface::RefImageSelMethod::ExcludeFolder: 0127 case HaarIface::RefImageSelMethod::PreferFolder: 0128 { 0129 imageIds.unite(referenceImageIds); // All reference images must be also in the search path, otherwise no duplicates are found 0130 break; 0131 } 0132 case HaarIface::RefImageSelMethod::NewerCreationDate: 0133 case HaarIface::RefImageSelMethod::NewerModificationDate: 0134 case HaarIface::RefImageSelMethod::OlderOrLarger: 0135 { 0136 break; 0137 } 0138 } 0139 0140 SearchesDBJobInfo jobInfo(std::move(imageIds), d->isAlbumUpdate, d->refSelMethod, std::move(referenceImageIds)); // Finding the duplicates 0141 0142 jobInfo.setMinThreshold(minThresh); 0143 jobInfo.setMaxThreshold(maxThresh); 0144 jobInfo.setSearchResultRestriction(d->searchResultRestriction); 0145 0146 d->job = DBJobsManager::instance()->startSearchesJobThread(jobInfo); 0147 0148 connect(d->job, SIGNAL(finished()), 0149 this, SLOT(slotDone())); 0150 0151 connect(d->job, SIGNAL(signalProgress(int)), 0152 this, SLOT(slotDuplicatesProgress(int))); 0153 0154 connect(this, SIGNAL(progressItemCanceled(ProgressItem*)), 0155 this, SIGNAL(signalComplete())); 0156 } 0157 0158 void DuplicatesFinder::slotDuplicatesProgress(int percentage) 0159 { 0160 setProgress(percentage); 0161 } 0162 0163 void DuplicatesFinder::slotDone() 0164 { 0165 if (d->job && d->job->hasErrors()) 0166 { 0167 qCWarning(DIGIKAM_GENERAL_LOG) << "Failed to list url: " << d->job->errorsList().first(); 0168 0169 // Pop-up a message about the error. 0170 DNotificationWrapper(QString(), d->job->errorsList().first(), 0171 DigikamApp::instance(), DigikamApp::instance()->windowTitle()); 0172 } 0173 0174 // save the min and max similarity in the configuration. 0175 ApplicationSettings::instance()->setDuplicatesSearchLastMinSimilarity(d->minSimilarity); 0176 ApplicationSettings::instance()->setDuplicatesSearchLastMaxSimilarity(d->maxSimilarity); 0177 ApplicationSettings::instance()->setDuplicatesAlbumTagRelation(d->albumTagRelation); 0178 ApplicationSettings::instance()->setDuplicatesSearchRestrictions(d->searchResultRestriction); 0179 0180 d->job = nullptr; 0181 MaintenanceTool::slotDone(); 0182 } 0183 0184 void DuplicatesFinder::slotCancel() 0185 { 0186 if (d->job) 0187 { 0188 disconnect(d->job, nullptr, this, nullptr); 0189 0190 d->job->cancel(); 0191 d->job = nullptr; 0192 } 0193 0194 MaintenanceTool::slotCancel(); 0195 } 0196 0197 } // namespace Digikam 0198 0199 #include "moc_duplicatesfinder.cpp"