File indexing completed on 2025-01-19 03:53:32
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2015-06-01 0007 * Description : DB Jobs thread for listing and scanning 0008 * 0009 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "dbjobsthread.h" 0016 0017 // Local includes 0018 0019 #include "coredbaccess.h" 0020 #include "duplicatesprogressobserver.h" 0021 #include "digikam_debug.h" 0022 0023 namespace Digikam 0024 { 0025 0026 DBJobsThread::DBJobsThread(QObject* const parent) 0027 : ActionThreadBase(parent) 0028 { 0029 setObjectName(QLatin1String("DBJobsThread")); 0030 } 0031 0032 DBJobsThread::~DBJobsThread() 0033 { 0034 } 0035 0036 bool DBJobsThread::hasErrors() 0037 { 0038 return !m_errorsList.isEmpty(); 0039 } 0040 0041 QList<QString>& DBJobsThread::errorsList() 0042 { 0043 return m_errorsList; 0044 } 0045 0046 void DBJobsThread::connectFinishAndErrorSignals(DBJob* const j) 0047 { 0048 connect(j, SIGNAL(signalDone()), 0049 this, SIGNAL(finished())); 0050 0051 connect(j, SIGNAL(error(QString)), 0052 this, SLOT(error(QString))); 0053 } 0054 0055 void DBJobsThread::error(const QString& errString) 0056 { 0057 m_errorsList.append(errString); 0058 } 0059 0060 // ------------------------------------------------- 0061 0062 AlbumsDBJobsThread::AlbumsDBJobsThread(QObject* const parent) 0063 : DBJobsThread(parent) 0064 { 0065 } 0066 0067 AlbumsDBJobsThread::~AlbumsDBJobsThread() 0068 { 0069 } 0070 0071 void AlbumsDBJobsThread::albumsListing(const AlbumsDBJobInfo& info) 0072 { 0073 AlbumsJob* const j = new AlbumsJob(info); 0074 0075 connectFinishAndErrorSignals(j); 0076 0077 if (info.isFoldersJob()) 0078 { 0079 connect(j, SIGNAL(foldersData(QHash<int,int>)), 0080 this, SIGNAL(foldersData(QHash<int,int>))); 0081 } 0082 else 0083 { 0084 connect(j, SIGNAL(data(QList<ItemListerRecord>)), 0085 this, SIGNAL(data(QList<ItemListerRecord>))); 0086 } 0087 0088 ActionJobCollection collection; 0089 collection.insert(j, 0); 0090 0091 appendJobs(collection); 0092 } 0093 0094 // ------------------------------------------------- 0095 0096 TagsDBJobsThread::TagsDBJobsThread(QObject* const parent) 0097 : DBJobsThread(parent) 0098 { 0099 } 0100 0101 TagsDBJobsThread::~TagsDBJobsThread() 0102 { 0103 } 0104 0105 void TagsDBJobsThread::tagsListing(const TagsDBJobInfo& info) 0106 { 0107 TagsJob* const j = new TagsJob(info); 0108 0109 connectFinishAndErrorSignals(j); 0110 0111 if (info.isFoldersJob()) 0112 { 0113 connect(j, SIGNAL(foldersData(QHash<int,int>)), 0114 this, SIGNAL(foldersData(QHash<int,int>))); 0115 } 0116 else if (info.isFaceFoldersJob()) 0117 { 0118 connect(j, SIGNAL(faceFoldersData(QMap<QString,QHash<int,int> >)), // krazy:exclude=normalize 0119 this, SIGNAL(faceFoldersData(QMap<QString,QHash<int,int> >))); // krazy:exclude=normalize 0120 } 0121 else 0122 { 0123 connect(j, SIGNAL(data(QList<ItemListerRecord>)), 0124 this, SIGNAL(data(QList<ItemListerRecord>))); 0125 } 0126 0127 ActionJobCollection collection; 0128 collection.insert(j, 0); 0129 0130 appendJobs(collection); 0131 } 0132 0133 // ------------------------------------------------- 0134 0135 DatesDBJobsThread::DatesDBJobsThread(QObject* const parent) 0136 : DBJobsThread(parent) 0137 { 0138 } 0139 0140 DatesDBJobsThread::~DatesDBJobsThread() 0141 { 0142 } 0143 0144 void DatesDBJobsThread::datesListing(const DatesDBJobInfo& info) 0145 { 0146 DatesJob* const j = new DatesJob(info); 0147 0148 connectFinishAndErrorSignals(j); 0149 0150 if (info.isFoldersJob()) 0151 { 0152 connect(j, SIGNAL(foldersData(QHash<QDateTime,int>)), 0153 this, SIGNAL(foldersData(QHash<QDateTime,int>))); 0154 } 0155 else 0156 { 0157 connect(j, SIGNAL(data(QList<ItemListerRecord>)), 0158 this, SIGNAL(data(QList<ItemListerRecord>))); 0159 } 0160 0161 ActionJobCollection collection; 0162 collection.insert(j, 0); 0163 0164 appendJobs(collection); 0165 } 0166 0167 // ------------------------------------------------- 0168 0169 GPSDBJobsThread::GPSDBJobsThread(QObject* const parent) 0170 : DBJobsThread(parent) 0171 { 0172 } 0173 0174 GPSDBJobsThread::~GPSDBJobsThread() 0175 { 0176 } 0177 0178 void GPSDBJobsThread::GPSListing(const GPSDBJobInfo& info) 0179 { 0180 GPSJob* const j = new GPSJob(info); 0181 0182 connectFinishAndErrorSignals(j); 0183 0184 if (info.isDirectQuery()) 0185 { 0186 connect(j, SIGNAL(directQueryData(QList<QVariant>)), 0187 this, SIGNAL(directQueryData(QList<QVariant>))); 0188 } 0189 else 0190 { 0191 connect(j, SIGNAL(data(QList<ItemListerRecord>)), 0192 this, SIGNAL(data(QList<ItemListerRecord>))); 0193 } 0194 0195 ActionJobCollection collection; 0196 collection.insert(j, 0); 0197 0198 appendJobs(collection); 0199 } 0200 0201 // ------------------------------------------------- 0202 0203 SearchesDBJobsThread::SearchesDBJobsThread(QObject* const parent) 0204 : DBJobsThread(parent), 0205 m_isAlbumUpdate(false), 0206 m_processedImages(0), 0207 m_totalImages2Scan(0) 0208 { 0209 } 0210 0211 SearchesDBJobsThread::~SearchesDBJobsThread() 0212 { 0213 } 0214 0215 void SearchesDBJobsThread::searchesListing(const SearchesDBJobInfo& info) 0216 { 0217 ActionJobCollection collection; 0218 0219 if (info.isDuplicatesJob()) 0220 { 0221 m_results.clear(); 0222 m_haarIface.reset(new HaarIface(info.imageIds())); 0223 m_isAlbumUpdate = info.isAlbumUpdate(); 0224 m_processedImages = 0; 0225 m_totalImages2Scan = info.imageIds().count(); 0226 0227 const int threadsCount = (m_totalImages2Scan < 200) ? 1 : qMax(1, maximumNumberOfThreads()); 0228 const int images2ScanPerThread = m_totalImages2Scan / threadsCount; 0229 0230 QSet<qlonglong>::const_iterator begin = info.imageIds().constBegin(); 0231 QSet<qlonglong>::const_iterator end = info.imageIds().constBegin(); 0232 0233 // Split job on multiple threads 0234 for (int i = 0; i < threadsCount; ++i) 0235 { 0236 // The last thread should read until the end of the list. 0237 if (i == threadsCount - 1) 0238 { 0239 end = info.imageIds().constEnd(); 0240 } 0241 else 0242 { 0243 // TODO: port to std::advance https://en.cppreference.com/w/cpp/iterator/advance 0244 for (int j = 0; end != info.imageIds().constEnd() && j < images2ScanPerThread; ++j, ++end); 0245 } 0246 0247 SearchesJob* const job = new SearchesJob(info, begin, end, m_haarIface.data()); 0248 0249 begin = end; 0250 0251 connect(job, &SearchesJob::signalDuplicatesResults, 0252 this, &SearchesDBJobsThread::slotDuplicatesResults); 0253 0254 connect(job, &SearchesJob::signalImageProcessed, 0255 this, &SearchesDBJobsThread::slotImageProcessed); 0256 0257 collection.insert(job, 0); 0258 } 0259 } 0260 else 0261 { 0262 SearchesJob* const job = new SearchesJob(info); 0263 connectFinishAndErrorSignals(job); 0264 0265 connect(job, SIGNAL(data(QList<ItemListerRecord>)), 0266 this, SIGNAL(data(QList<ItemListerRecord>))); 0267 0268 collection.insert(job, 0); 0269 } 0270 0271 appendJobs(collection); 0272 } 0273 0274 void SearchesDBJobsThread::slotImageProcessed() 0275 { 0276 Q_EMIT signalProgress((++m_processedImages * 100) / m_totalImages2Scan); 0277 } 0278 0279 void SearchesDBJobsThread::slotDuplicatesResults(const HaarIface::DuplicatesResultsMap& incoming) 0280 { 0281 auto searchResults = [&](qlonglong imageId) -> HaarIface::DuplicatesResultsMap::iterator 0282 { 0283 for (auto it = m_results.begin() ; it != m_results.end() ; ++it) 0284 { 0285 if ((imageId == it.key()) || (it->second.contains(imageId))) 0286 { 0287 return it; 0288 } 0289 } 0290 0291 return m_results.end(); 0292 }; 0293 0294 for (const auto& referenceImage : incoming.keys()) 0295 { 0296 if (searchResults(referenceImage) == m_results.end()) 0297 { 0298 m_results.insert(referenceImage, incoming.value(referenceImage)); 0299 } 0300 } 0301 0302 if (m_processedImages != m_totalImages2Scan) 0303 { 0304 return; 0305 } 0306 0307 HaarIface::rebuildDuplicatesAlbums(m_results, m_isAlbumUpdate); 0308 0309 Q_EMIT finished(); 0310 } 0311 0312 } // namespace Digikam 0313 0314 #include "moc_dbjobsthread.cpp"