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        : 2013-08-09
0007  * Description : Thread actions manager for maintenance tools.
0008  *
0009  * SPDX-FileCopyrightText: 2013-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2017-2018 by Mario Frank <mario dot frank at uni minus potsdam dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "maintenancethread.h"
0017 
0018 // Qt includes
0019 
0020 #include <QQueue>
0021 #include <QMutex>
0022 
0023 // Local includes
0024 
0025 #include "digikam_debug.h"
0026 #include "thumbstask.h"
0027 #include "metadatasynctask.h"
0028 #include "fingerprintstask.h"
0029 #include "autotagsassignmentask.h"
0030 #include "imagequalitytask.h"
0031 #include "imagequalitycontainer.h"
0032 #include "databasetask.h"
0033 #include "maintenancedata.h"
0034 #include "imagequalityparser.h"
0035 #include "metadataremovetask.h"
0036 
0037 namespace Digikam
0038 {
0039 
0040 MaintenanceThread::MaintenanceThread(QObject* const parent)
0041     : ActionThreadBase(parent),
0042       data(new MaintenanceData)
0043 {
0044     setObjectName(QLatin1String("MaintenanceThread"));
0045 
0046     connect(this, SIGNAL(finished()),
0047             this, SLOT(slotThreadFinished()));
0048 }
0049 
0050 MaintenanceThread::~MaintenanceThread()
0051 {
0052     cancel();
0053     wait();
0054     delete data;
0055 }
0056 
0057 void MaintenanceThread::setUseMultiCore(const bool b)
0058 {
0059     if (!b)
0060     {
0061         setMaximumNumberOfThreads(1);
0062     }
0063     else
0064     {
0065         setDefaultMaximumNumberOfThreads();
0066         //setMaximumNumberOfThreads(qMax( maximumNumberOfThreads() / 2, 1));
0067     }
0068 }
0069 
0070 void MaintenanceThread::syncMetadata(const ItemInfoList& items, MetadataSynchronizer::SyncDirection dir, bool tagsOnly)
0071 {
0072     ActionJobCollection collection;
0073 
0074     data->setItemInfos(items);
0075 
0076     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0077     {
0078         MetadataSyncTask* const t = new MetadataSyncTask();
0079         t->setTagsOnly(tagsOnly);
0080         t->setDirection(dir);
0081         t->setMaintenanceData(data);
0082 
0083         connect(t, SIGNAL(signalFinished(QImage)),
0084                 this, SIGNAL(signalAdvance(QImage)));
0085 
0086         collection.insert(t, 0);
0087 
0088         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a metadata task for synchronizing metadata";
0089     }
0090 
0091     appendJobs(collection);
0092 }
0093 
0094 void MaintenanceThread::removeMetadata(const ItemInfoList& items, MetadataRemover::RemoveAction action)
0095 {
0096     ActionJobCollection collection;
0097 
0098     data->setItemInfos(items);
0099 
0100     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0101     {
0102         MetadataRemoveTask* const t = new MetadataRemoveTask();
0103         t->setRemoveAction(action);
0104         t->setMaintenanceData(data);
0105 
0106         connect(t, SIGNAL(signalFinished(QImage)),
0107                 this, SIGNAL(signalAdvance(QImage)));
0108 
0109         collection.insert(t, 0);
0110 
0111         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a remover task for removing metadata";
0112     }
0113 
0114     appendJobs(collection);
0115 }
0116 
0117 void MaintenanceThread::generateThumbs(const QStringList& paths)
0118 {
0119     ActionJobCollection collection;
0120 
0121     data->setImagePaths(paths);
0122 
0123     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0124     {
0125         ThumbsTask* const t = new ThumbsTask();
0126 
0127         t->setMaintenanceData(data);
0128 
0129         connect(t, SIGNAL(signalFinished(QImage)),
0130                 this, SIGNAL(signalAdvance(QImage)));
0131 
0132         collection.insert(t, 0);
0133 
0134         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a thumbnails task for generating thumbnails";
0135     }
0136 
0137     appendJobs(collection);
0138 }
0139 
0140 void MaintenanceThread::generateFingerprints(const QList<qlonglong>& itemIds, bool rebuildAll)
0141 {
0142     ActionJobCollection collection;
0143 
0144     data->setImageIds(itemIds);
0145     data->setRebuildAllFingerprints(rebuildAll);
0146 
0147     for (int i = 1 ; i <= (maximumNumberOfThreads()) ; ++i)
0148     {
0149         FingerprintsTask* const t = new FingerprintsTask();
0150 
0151         t->setMaintenanceData(data);
0152 
0153         connect(t, SIGNAL(signalFinished(QImage)),
0154                 this, SIGNAL(signalAdvance(QImage)));
0155 
0156         collection.insert(t, 0);
0157 
0158         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a fingerprints task for generating fingerprints";
0159     }
0160 
0161     appendJobs(collection);
0162 }
0163 
0164 void MaintenanceThread::generateTags(const QStringList& paths, int modelType)
0165 {
0166     ActionJobCollection collection;
0167 
0168     data->setImagePaths(paths);
0169 
0170     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0171     {
0172         AutotagsAssignmentTask* const t = new AutotagsAssignmentTask();
0173 
0174         t->setMaintenanceData(data);
0175         t->setModelType(modelType);
0176 
0177         connect(t, SIGNAL(signalFinished(QImage)),
0178                 this, SIGNAL(signalAdvance(QImage)));
0179 
0180         collection.insert(t, 0);
0181 
0182         qCDebug(DIGIKAM_AUTOTAGSENGINE_LOG) << "Creating an autotagging task for autotags assignment items";
0183     }
0184 
0185     appendJobs(collection);
0186 }
0187 
0188 void MaintenanceThread::sortByImageQuality(const QStringList& paths, const ImageQualityContainer& quality)
0189 {
0190     ActionJobCollection collection;
0191 
0192     data->setImagePaths(paths);
0193 
0194     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0195     {
0196         ImageQualityTask* const t = new ImageQualityTask();
0197         t->setQuality(quality);
0198         t->setMaintenanceData(data);
0199 
0200         connect(t, SIGNAL(signalFinished(QImage)),
0201                 this, SIGNAL(signalAdvance(QImage)));
0202 
0203         connect(this, SIGNAL(signalCanceled()),
0204                 t, SLOT(slotCancel()), Qt::QueuedConnection);
0205 
0206         collection.insert(t, 0);
0207 
0208         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a image quality task for sorting items.";
0209     }
0210 
0211     appendJobs(collection);
0212 }
0213 
0214 void MaintenanceThread::computeDatabaseJunk(bool thumbsDb, bool facesDb, bool similarityDb)
0215 {
0216     ActionJobCollection collection;
0217 
0218     DatabaseTask* const t = new DatabaseTask();
0219     t->setMode(DatabaseTask::Mode::ComputeDatabaseJunk);
0220     t->computeDatabaseJunk(thumbsDb, facesDb, similarityDb);
0221 
0222     connect(t, SIGNAL(signalFinished()),
0223             this, SIGNAL(signalAdvance()));
0224 
0225     connect(t, SIGNAL(signalAddItemsToProcess(int)),
0226             this, SIGNAL(signalAddItemsToProcess(int)));
0227 
0228     connect(t, SIGNAL(signalData(QList<qlonglong>,QList<int>,QList<Identity>,QList<qlonglong>)),
0229             this, SIGNAL(signalData(QList<qlonglong>,QList<int>,QList<Identity>,QList<qlonglong>)));
0230 
0231     collection.insert(t, 0);
0232 
0233     appendJobs(collection);
0234 }
0235 
0236 void MaintenanceThread::cleanCoreDb(const QList<qlonglong>& imageIds)
0237 {
0238     ActionJobCollection collection;
0239 
0240     data->setImageIds(imageIds);
0241 
0242     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0243     {
0244         DatabaseTask* const t = new DatabaseTask();
0245 
0246         t->setMaintenanceData(data);
0247         t->setMode(DatabaseTask::Mode::CleanCoreDb);
0248 
0249         connect(t, SIGNAL(signalFinished()),
0250                 this, SIGNAL(signalAdvance()));
0251 
0252         collection.insert(t, 0);
0253 
0254         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a database task for removing stale items.";
0255     }
0256 
0257     appendJobs(collection);
0258 }
0259 
0260 void MaintenanceThread::cleanThumbsDb(const QList<int>& thumbnailIds)
0261 {
0262     ActionJobCollection collection;
0263 
0264     data->setThumbnailIds(thumbnailIds);
0265 
0266     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0267     {
0268         DatabaseTask* const t = new DatabaseTask();
0269 
0270         t->setMaintenanceData(data);
0271         t->setMode(DatabaseTask::Mode::CleanThumbsDb);
0272 
0273         connect(t, SIGNAL(signalFinished()),
0274                 this, SIGNAL(signalAdvance()));
0275 
0276         collection.insert(t, 0);
0277 
0278         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a database task for removing stale thumbnails.";
0279     }
0280 
0281     appendJobs(collection);
0282 }
0283 
0284 void MaintenanceThread::cleanFacesDb(const QList<Identity>& staleIdentities)
0285 {
0286     ActionJobCollection collection;
0287 
0288     data->setIdentities(staleIdentities);
0289 
0290     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0291     {
0292         DatabaseTask* const t = new DatabaseTask();
0293 
0294         t->setMaintenanceData(data);
0295         t->setMode(DatabaseTask::Mode::CleanRecognitionDb);
0296 
0297         connect(t, SIGNAL(signalFinished()),
0298                 this, SIGNAL(signalAdvance()));
0299 
0300         collection.insert(t, 0);
0301 
0302         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a database task for removing stale identities.";
0303     }
0304 
0305     appendJobs(collection);
0306 }
0307 
0308 void MaintenanceThread::cleanSimilarityDb(const QList<qlonglong>& imageIds)
0309 {
0310     ActionJobCollection collection;
0311 
0312     data->setSimilarityImageIds(imageIds);
0313 
0314     for (int i = 1 ; i <= maximumNumberOfThreads() ; ++i)
0315     {
0316         DatabaseTask* const t = new DatabaseTask();
0317 
0318         t->setMaintenanceData(data);
0319         t->setMode(DatabaseTask::Mode::CleanSimilarityDb);
0320 
0321         connect(t, SIGNAL(signalFinished()),
0322                 this, SIGNAL(signalAdvance()));
0323 
0324         collection.insert(t, 0);
0325 
0326         qCDebug(DIGIKAM_GENERAL_LOG) << "Creating a database task for removing stale image id entries from similarity db.";
0327     }
0328 
0329     appendJobs(collection);
0330 }
0331 
0332 void MaintenanceThread::shrinkDatabases()
0333 {
0334     ActionJobCollection collection;
0335 
0336     DatabaseTask* const t = new DatabaseTask();
0337     t->setMode(DatabaseTask::Mode::ShrinkDatabases);
0338 
0339     connect(t, SIGNAL(signalStarted()),
0340             this, SIGNAL(signalStarted()));
0341 
0342     connect(t, SIGNAL(signalFinished(bool,bool)),
0343             this, SIGNAL(signalFinished(bool,bool)));
0344 
0345     collection.insert(t, 0);
0346 
0347     appendJobs(collection);
0348 }
0349 
0350 void MaintenanceThread::cancel()
0351 {
0352     if (isRunning())
0353     {
0354         Q_EMIT signalCanceled();
0355     }
0356 
0357     ActionThreadBase::cancel();
0358 }
0359 
0360 void MaintenanceThread::slotThreadFinished()
0361 {
0362     if (isEmpty())
0363     {
0364         ImageQualityParser::unloadDLModel();
0365         qCDebug(DIGIKAM_GENERAL_LOG) << "List of Pending Jobs is empty";
0366         Q_EMIT signalCompleted();
0367     }
0368 }
0369 
0370 /*
0371 int MaintenanceThread::getChunkSize(int elementCount)
0372 {
0373     int chunkSize = elementCount;
0374 
0375     if (maximumNumberOfThreads() > 1)
0376     {
0377         chunkSize = elementCount / (maximumNumberOfThreads() - 1);
0378     }
0379 
0380     // add a factor to the chunk size (do it dynamically depending on free mem?)
0381     chunkSize = chunkSize * 0.5;
0382 
0383     return chunkSize;
0384 }
0385 */
0386 
0387 } // namespace Digikam
0388 
0389 #include "moc_maintenancethread.cpp"