File indexing completed on 2025-01-19 03:59:31

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-01-31
0007  * Description : maintenance manager
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  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "maintenancemngr.h"
0017 
0018 // Qt includes
0019 
0020 #include <QString>
0021 #include <QElapsedTimer>
0022 #include <QApplication>
0023 
0024 // KDE includes
0025 
0026 #include <klocalizedstring.h>
0027 
0028 // Local includes
0029 
0030 #include "digikam_debug.h"
0031 #include "digikam_config.h"
0032 #include "albummanager.h"
0033 #include "applicationsettings.h"
0034 #include "maintenancesettings.h"
0035 #include "newitemsfinder.h"
0036 #include "thumbsgenerator.h"
0037 #include "fingerprintsgenerator.h"
0038 #include "duplicatesfinder.h"
0039 #include "autotagsassignment.h"
0040 #include "imagequalitysorter.h"
0041 #include "metadatasynchronizer.h"
0042 #include "dnotificationwrapper.h"
0043 #include "progressmanager.h"
0044 #include "facesdetector.h"
0045 #include "dbcleaner.h"
0046 
0047 namespace Digikam
0048 {
0049 
0050 class Q_DECL_HIDDEN MaintenanceMngr::Private
0051 {
0052 public:
0053 
0054     Private() = default;
0055 
0056 public:
0057 
0058     bool                   running               = false;
0059 
0060     QElapsedTimer          duration;
0061 
0062     MaintenanceSettings    settings;
0063 
0064     NewItemsFinder*        newItemsFinder        = nullptr;
0065     ThumbsGenerator*       thumbsGenerator       = nullptr;
0066     FingerPrintsGenerator* fingerPrintsGenerator = nullptr;
0067     DuplicatesFinder*      duplicatesFinder      = nullptr;
0068     MetadataSynchronizer*  metadataSynchronizer  = nullptr;
0069     AutotagsAssignment*    autotagsAssignment    = nullptr;
0070     ImageQualitySorter*    imageQualitySorter    = nullptr;
0071     FacesDetector*         facesDetector         = nullptr;
0072     DbCleaner*             databaseCleaner       = nullptr;
0073 };
0074 
0075 MaintenanceMngr::MaintenanceMngr(QObject* const parent)
0076     : QObject(parent),
0077       d(new Private)
0078 {
0079     connect(ProgressManager::instance(), SIGNAL(progressItemCompleted(ProgressItem*)),
0080             this, SLOT(slotToolCompleted(ProgressItem*)));
0081 
0082     connect(ProgressManager::instance(), SIGNAL(progressItemCanceled(ProgressItem*)),
0083             this, SLOT(slotToolCanceled(ProgressItem*)));
0084 }
0085 
0086 MaintenanceMngr::~MaintenanceMngr()
0087 {
0088     delete d;
0089 }
0090 
0091 bool MaintenanceMngr::isRunning() const
0092 {
0093     return d->running;
0094 }
0095 
0096 void MaintenanceMngr::setSettings(const MaintenanceSettings& settings)
0097 {
0098     d->settings = settings;
0099     qCDebug(DIGIKAM_GENERAL_LOG) << d->settings;
0100 
0101     d->duration.start();
0102     stage1();
0103 }
0104 
0105 void MaintenanceMngr::slotToolCompleted(ProgressItem* tool)
0106 {
0107     // At each stage, relevant tool instance is set to zero to prevent redondant call to this slot
0108     // from ProgressManager. This will disable multiple triggering in this method.
0109     // There is no memory leak. Each tool instance are delete later by ProgressManager.
0110 
0111     if      (tool == dynamic_cast<ProgressItem*>(d->newItemsFinder))
0112     {
0113         d->newItemsFinder = nullptr;
0114 
0115         // Update albums and tags for the other tools
0116 
0117         if (d->settings.wholeAlbums)
0118         {
0119             d->settings.albums = AlbumManager::instance()->allPAlbums();
0120         }
0121 
0122         if (d->settings.wholeTags)
0123         {
0124             d->settings.tags = AlbumManager::instance()->allTAlbums();
0125         }
0126 
0127         stage2();
0128     }
0129     else if (tool == dynamic_cast<ProgressItem*>(d->databaseCleaner))
0130     {
0131         d->databaseCleaner = nullptr;
0132         stage3();
0133     }
0134     else if (tool == dynamic_cast<ProgressItem*>(d->thumbsGenerator))
0135     {
0136         d->thumbsGenerator = nullptr;
0137         stage4();
0138     }
0139     else if (tool == dynamic_cast<ProgressItem*>(d->fingerPrintsGenerator))
0140     {
0141         d->fingerPrintsGenerator = nullptr;
0142         stage5();
0143     }
0144     else if (tool == dynamic_cast<ProgressItem*>(d->duplicatesFinder))
0145     {
0146         d->duplicatesFinder = nullptr;
0147         stage6();
0148     }
0149     else if (tool == dynamic_cast<ProgressItem*>(d->facesDetector))
0150     {
0151         d->facesDetector = nullptr;
0152         stage7();
0153     }
0154     else if(tool == dynamic_cast<ProgressItem*>(d->autotagsAssignment))
0155     {
0156         d->autotagsAssignment = nullptr;
0157         stage8();
0158     }
0159    else if (tool == dynamic_cast<ProgressItem*>(d->imageQualitySorter))
0160     {
0161         d->imageQualitySorter = nullptr;
0162         stage9();
0163     }
0164     else if (tool == dynamic_cast<ProgressItem*>(d->metadataSynchronizer))
0165     {
0166         d->metadataSynchronizer = nullptr;
0167         done();
0168     }
0169 }
0170 
0171 void MaintenanceMngr::slotToolCanceled(ProgressItem* tool)
0172 {
0173     if ((tool == dynamic_cast<ProgressItem*>(d->newItemsFinder))        ||
0174         (tool == dynamic_cast<ProgressItem*>(d->thumbsGenerator))       ||
0175         (tool == dynamic_cast<ProgressItem*>(d->fingerPrintsGenerator)) ||
0176         (tool == dynamic_cast<ProgressItem*>(d->duplicatesFinder))      ||
0177         (tool == dynamic_cast<ProgressItem*>(d->databaseCleaner))       ||
0178         (tool == dynamic_cast<ProgressItem*>(d->facesDetector))         ||
0179         (tool == dynamic_cast<ProgressItem*>(d->imageQualitySorter))    ||
0180         (tool == dynamic_cast<ProgressItem*>(d->metadataSynchronizer))  ||
0181         (tool == dynamic_cast<ProgressItem*>(d->autotagsAssignment)))
0182     {
0183         cancel();
0184     }
0185 }
0186 
0187 void MaintenanceMngr::stage1()
0188 {
0189     qCDebug(DIGIKAM_GENERAL_LOG) << "stage1";
0190 
0191     if (d->settings.newItems)
0192     {
0193         if (d->settings.wholeAlbums)
0194         {
0195             d->newItemsFinder = new NewItemsFinder();
0196         }
0197         else
0198         {
0199             QStringList paths;
0200 
0201             Q_FOREACH (Album* const a, d->settings.albums)
0202             {
0203                 PAlbum* const palbum = dynamic_cast<PAlbum*>(a);
0204 
0205                 if (palbum)
0206                 {
0207                     paths << palbum->folderPath();
0208                 }
0209             }
0210 
0211             d->newItemsFinder = new NewItemsFinder(NewItemsFinder::ScheduleCollectionScan, paths);
0212         }
0213 
0214         d->newItemsFinder->setNotificationEnabled(false);
0215         d->newItemsFinder->start();
0216     }
0217     else
0218     {
0219         stage2();
0220     }
0221 }
0222 
0223 void MaintenanceMngr::stage2()
0224 {
0225     qCDebug(DIGIKAM_GENERAL_LOG) << "stage2";
0226 
0227     if (d->settings.databaseCleanup)
0228     {
0229         d->databaseCleaner = new DbCleaner(d->settings.cleanThumbDb,
0230                                            d->settings.cleanFacesDb,
0231                                            d->settings.cleanSimilarityDb,
0232                                            d->settings.shrinkDatabases);
0233         d->databaseCleaner->setNotificationEnabled(false);
0234         d->databaseCleaner->setUseMultiCoreCPU(d->settings.useMutiCoreCPU);
0235         d->databaseCleaner->start();
0236     }
0237     else
0238     {
0239         stage3();
0240     }
0241 }
0242 
0243 void MaintenanceMngr::stage3()
0244 {
0245     qCDebug(DIGIKAM_GENERAL_LOG) << "stage3";
0246 
0247     if (d->settings.thumbnails)
0248     {
0249         bool rebuildAll = (d->settings.scanThumbs == false);
0250         AlbumList list;
0251         list << d->settings.albums;
0252         list << d->settings.tags;
0253 
0254         d->thumbsGenerator = new ThumbsGenerator(rebuildAll, list);
0255         d->thumbsGenerator->setNotificationEnabled(false);
0256         d->thumbsGenerator->setUseMultiCoreCPU(d->settings.useMutiCoreCPU);
0257         d->thumbsGenerator->start();
0258     }
0259     else
0260     {
0261         stage4();
0262     }
0263 }
0264 
0265 void MaintenanceMngr::stage4()
0266 {
0267     qCDebug(DIGIKAM_GENERAL_LOG) << "stage4";
0268 
0269     if (d->settings.fingerPrints)
0270     {
0271         bool rebuildAll = (d->settings.scanFingerPrints == false);
0272         AlbumList list;
0273         list << d->settings.albums;
0274         list << d->settings.tags;
0275 
0276         d->fingerPrintsGenerator = new FingerPrintsGenerator(rebuildAll, list);
0277         d->fingerPrintsGenerator->setNotificationEnabled(false);
0278         d->fingerPrintsGenerator->setUseMultiCoreCPU(d->settings.useMutiCoreCPU);
0279         d->fingerPrintsGenerator->start();
0280     }
0281     else
0282     {
0283         stage5();
0284     }
0285 }
0286 
0287 void MaintenanceMngr::stage5()
0288 {
0289     qCDebug(DIGIKAM_GENERAL_LOG) << "stage5";
0290 
0291     if (d->settings.duplicates)
0292     {
0293         d->duplicatesFinder = new DuplicatesFinder(d->settings.albums, d->settings.tags, (int)HaarIface::AlbumTagRelation::NoMix,
0294                                                    d->settings.minSimilarity, d->settings.maxSimilarity,(int)d->settings.duplicatesRestriction);
0295         d->duplicatesFinder->setNotificationEnabled(false);
0296         d->duplicatesFinder->start();
0297     }
0298     else
0299     {
0300         stage6();
0301     }
0302 }
0303 
0304 void MaintenanceMngr::stage6()
0305 {
0306     qCDebug(DIGIKAM_GENERAL_LOG) << "stage6";
0307 
0308     if (d->settings.faceManagement)
0309     {
0310         // NOTE : Use multi-core CPU option is passed through FaceScanSettings
0311         d->settings.faceSettings.wholeAlbums = d->settings.wholeAlbums;
0312         d->settings.faceSettings.useFullCpu  = d->settings.useMutiCoreCPU;
0313         d->settings.faceSettings.useYoloV3   = ApplicationSettings::instance()->getFaceDetectionYoloV3();
0314         d->settings.faceSettings.accuracy    = ApplicationSettings::instance()->getFaceDetectionAccuracy();
0315         d->facesDetector                     = new FacesDetector(d->settings.faceSettings);
0316         d->facesDetector->setNotificationEnabled(false);
0317         d->facesDetector->start();
0318     }
0319     else
0320     {
0321         stage7();
0322     }
0323 }
0324 
0325 void MaintenanceMngr::stage7()
0326 {
0327     qCDebug(DIGIKAM_GENERAL_LOG) << "stage7";
0328 
0329     if (d->settings.autotagsAssignment)
0330     {
0331         AlbumList list;
0332         list << d->settings.albums;
0333         list << d->settings.tags;
0334 
0335         d->autotagsAssignment = new AutotagsAssignment((AutotagsAssignment::AutotagsAssignmentScanMode)d->settings.autotaggingScanMode, list, d->settings.modelSelectionMode);
0336         d->autotagsAssignment->setNotificationEnabled(false);
0337         d->autotagsAssignment->setUseMultiCoreCPU(d->settings.useMutiCoreCPU);
0338         d->autotagsAssignment->start();
0339     }
0340     else
0341     {
0342         stage8();
0343     }
0344 }
0345 
0346 void MaintenanceMngr::stage8()
0347 {
0348     qCDebug(DIGIKAM_GENERAL_LOG) << "stage8";
0349 
0350     if (d->settings.qualitySort)
0351     {
0352         AlbumList list;
0353         list << d->settings.albums;
0354         list << d->settings.tags;
0355 
0356         d->imageQualitySorter = new ImageQualitySorter((ImageQualitySorter::QualityScanMode)d->settings.qualityScanMode, list, d->settings.quality);
0357         d->imageQualitySorter->setNotificationEnabled(false);
0358         d->imageQualitySorter->setUseMultiCoreCPU(d->settings.useMutiCoreCPU);
0359         d->imageQualitySorter->start();
0360     }
0361     else
0362     {
0363         stage9();
0364     }
0365 }
0366 
0367 void MaintenanceMngr::stage9()
0368 {
0369     qCDebug(DIGIKAM_GENERAL_LOG) << "stage9";
0370 
0371     if (d->settings.metadataSync)
0372     {
0373         AlbumList list;
0374         list << d->settings.albums;
0375         list << d->settings.tags;
0376         d->metadataSynchronizer = new MetadataSynchronizer(list, MetadataSynchronizer::SyncDirection(d->settings.syncDirection));
0377         d->metadataSynchronizer->setNotificationEnabled(false);
0378         // See Bug #329091 : Multicore CPU support with Exiv2 sound problematic, even with 0.25 release.
0379         d->metadataSynchronizer->setUseMultiCoreCPU(false);
0380         d->metadataSynchronizer->start();
0381     }
0382     else
0383     {
0384         done();
0385     }
0386 }
0387 
0388 void MaintenanceMngr::done()
0389 {
0390     d->running = false;
0391     QTime t    = QTime::fromMSecsSinceStartOfDay(d->duration.elapsed());
0392 
0393     // Pop-up a message to bring user when all is done.
0394     DNotificationWrapper(QLatin1String("digiKam Maintenance"), // not i18n
0395                          i18n("All operations are done.\nDuration: %1", t.toString()),
0396                          qApp->activeWindow(), i18n("digiKam Maintenance"));
0397 
0398     Q_EMIT signalComplete();
0399 }
0400 
0401 void MaintenanceMngr::cancel()
0402 {
0403     d->running = false;
0404     Q_EMIT signalComplete();
0405 }
0406 
0407 } // namespace Digikam
0408 
0409 #include "moc_maintenancemngr.cpp"