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"