File indexing completed on 2025-03-09 03:58:51
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-02-06 0007 * Description : Thread actions task. 0008 * 0009 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2012 by Pankaj Kumar <me at panks dot me> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "task.h" 0017 0018 // Qt includes 0019 0020 #include <QFileInfo> 0021 0022 // KDE includes 0023 0024 #include <klocalizedstring.h> 0025 0026 // Local includes 0027 0028 #include "digikam_debug.h" 0029 #include "digikam_config.h" 0030 #include "dimg.h" 0031 #include "dmetadata.h" 0032 #include "iteminfo.h" 0033 #include "batchtool.h" 0034 #include "batchtoolsfactory.h" 0035 #include "dfileoperations.h" 0036 0037 namespace Digikam 0038 { 0039 0040 class Q_DECL_HIDDEN Task::Private 0041 { 0042 public: 0043 0044 explicit Private() 0045 : cancel(false), 0046 tool (nullptr) 0047 { 0048 } 0049 0050 bool cancel; 0051 0052 BatchTool* tool; 0053 0054 QueueSettings settings; 0055 AssignedBatchTools tools; 0056 }; 0057 0058 // ------------------------------------------------------- 0059 0060 Task::Task() 0061 : ActionJob(), 0062 d (new Private) 0063 { 0064 } 0065 0066 Task::~Task() 0067 { 0068 slotCancel(); 0069 delete d; 0070 } 0071 0072 void Task::setSettings(const QueueSettings& settings) 0073 { 0074 d->settings = settings; 0075 } 0076 0077 void Task::setItem(const AssignedBatchTools& tools) 0078 { 0079 d->tools = tools; 0080 } 0081 0082 void Task::slotCancel() 0083 { 0084 if (d->tool) 0085 { 0086 d->tool->cancel(); 0087 } 0088 0089 d->cancel = true; 0090 } 0091 0092 void Task::removeTempFiles(const QList<QUrl>& tmpList) 0093 { 0094 Q_FOREACH (const QUrl& url, tmpList) 0095 { 0096 QString tmpPath(url.toLocalFile()); 0097 QFile::remove(tmpPath); 0098 0099 tmpPath = DMetadata::sidecarPath(tmpPath); 0100 0101 if (QFile::exists(tmpPath)) 0102 { 0103 QFile::remove(tmpPath); 0104 } 0105 } 0106 } 0107 0108 void Task::emitActionData(ActionData::ActionStatus st, 0109 const QString& mess, 0110 const QUrl& dest, 0111 bool noWrite) 0112 { 0113 ActionData ad; 0114 ad.fileUrl = d->tools.m_itemUrl; 0115 ad.status = st; 0116 ad.message = mess; 0117 ad.destUrl = dest; 0118 ad.noWrite = noWrite; 0119 0120 Q_EMIT signalFinished(ad); 0121 } 0122 0123 void Task::run() 0124 { 0125 if (d->cancel) 0126 { 0127 return; 0128 } 0129 0130 emitActionData(ActionData::BatchStarted); 0131 0132 // Loop with all batch tools operations to apply on item. 0133 0134 bool success = false; 0135 int index = 0; 0136 QUrl outUrl = d->tools.m_itemUrl; 0137 QUrl workUrl = !d->settings.useOrgAlbum ? d->settings.workingUrl 0138 : d->tools.m_itemUrl.adjusted(QUrl::RemoveFilename); 0139 workUrl = workUrl.adjusted(QUrl::StripTrailingSlash); 0140 QUrl inUrl; 0141 QList<QUrl> tmp2del; 0142 DImg tmpImage; 0143 QString errMsg; 0144 0145 // ItemInfo must be tread-safe. 0146 0147 ItemInfo source = ItemInfo::fromUrl(d->tools.m_itemUrl); 0148 bool noWriteMetadata = false; 0149 bool timeAdjust = false; 0150 0151 Q_FOREACH (const BatchToolSet& set, d->tools.m_toolsList) 0152 { 0153 BatchTool* const tool = BatchToolsFactory::instance()->findTool(set.name, set.group); 0154 0155 if (!tool) 0156 { 0157 emitActionData(ActionData::BatchFailed, i18n("Failed to find tool...")); 0158 removeTempFiles(tmp2del); 0159 0160 Q_EMIT signalDone(); 0161 0162 return; 0163 } 0164 0165 d->tool = tool->clone(); 0166 d->tool->setToolIcon(tool->toolIcon()); 0167 d->tool->setToolTitle(tool->toolTitle()); 0168 d->tool->setToolDescription(tool->toolDescription()); 0169 0170 // Only true if it is also the last tool 0171 0172 noWriteMetadata = ((set.name == QLatin1String("ApplyMetadata")) || 0173 (set.name == QLatin1String("RemoveMetadata"))); 0174 timeAdjust |= (set.name == QLatin1String("TimeAdjust")); 0175 inUrl = outUrl; 0176 index = set.index + 1; 0177 0178 qCDebug(DIGIKAM_GENERAL_LOG) << "Tool : index= " << index 0179 << " :: name= " << set.name 0180 << " :: group= " << set.group 0181 << " :: wurl= " << workUrl; 0182 0183 d->tool->setImageData(tmpImage); 0184 d->tool->setItemInfo(source); 0185 d->tool->setInputUrl(inUrl); 0186 d->tool->setWorkingUrl(workUrl); 0187 d->tool->setSettings(set.settings); 0188 d->tool->setIOFileSettings(d->settings.ioFileSettings); 0189 d->tool->setRawLoadingRules(d->settings.rawLoadingRule); 0190 d->tool->setDRawDecoderSettings(d->settings.rawDecodingSettings); 0191 d->tool->setResetExifOrientationAllowed(d->settings.exifSetOrientation); 0192 0193 if (index == d->tools.m_toolsList.count()) 0194 { 0195 d->tool->setLastChainedTool(true); 0196 } 0197 else if (d->tools.m_toolsList[index].group == BatchTool::CustomTool) 0198 { 0199 // If the next tool is under the custom group (user script) 0200 // treat as the last chained tool, i.e. save image to file 0201 0202 d->tool->setLastChainedTool(true); 0203 } 0204 else 0205 { 0206 d->tool->setLastChainedTool(false); 0207 } 0208 0209 d->tool->setSaveAsNewVersion(d->settings.saveAsNewVersion); 0210 d->tool->setOutputUrlFromInputUrl(); 0211 d->tool->setBranchHistory(true); 0212 0213 outUrl = d->tool->outputUrl(); 0214 success = d->tool->apply(); 0215 tmpImage = d->tool->imageData(); 0216 errMsg = d->tool->errorDescription(); 0217 tmp2del.append(outUrl); 0218 0219 delete d->tool; 0220 d->tool = nullptr; 0221 0222 if (d->cancel) 0223 { 0224 emitActionData(ActionData::BatchCanceled); 0225 removeTempFiles(tmp2del); 0226 0227 Q_EMIT signalDone(); 0228 0229 return; 0230 } 0231 else if (!success) 0232 { 0233 emitActionData(ActionData::BatchFailed, errMsg); 0234 break; 0235 } 0236 } 0237 0238 // Clean up all tmp url. 0239 0240 // We don't remove last output tmp url. 0241 0242 tmp2del.removeAll(outUrl); 0243 removeTempFiles(tmp2del); 0244 0245 // Move processed temp file to target 0246 0247 QString renameMess; 0248 QUrl dest = workUrl; 0249 dest.setPath(dest.path() + QLatin1Char('/') + d->tools.m_destFileName); 0250 0251 if (QFileInfo::exists(dest.toLocalFile())) 0252 { 0253 if (d->settings.conflictRule == FileSaveConflictBox::OVERWRITE) 0254 { 0255 renameMess = i18n("(overwritten)"); 0256 } 0257 else if (d->settings.conflictRule == FileSaveConflictBox::DIFFNAME) 0258 { 0259 dest = DFileOperations::getUniqueFileUrl(dest); 0260 renameMess = i18n("(renamed to %1)", dest.fileName()); 0261 } 0262 else 0263 { 0264 emitActionData(ActionData::BatchSkipped, 0265 i18n("Item exists and was skipped"), dest); 0266 0267 removeTempFiles(QList<QUrl>() << outUrl); 0268 0269 Q_EMIT signalDone(); 0270 0271 return; 0272 } 0273 } 0274 0275 if (QFileInfo(outUrl.toLocalFile()).size() == 0) 0276 { 0277 removeTempFiles(QList<QUrl>() << outUrl); 0278 dest.clear(); 0279 } 0280 0281 if (!dest.isEmpty()) 0282 { 0283 if (DMetadata::hasSidecar(outUrl.toLocalFile())) 0284 { 0285 if (!DFileOperations::localFileRename(d->tools.m_itemUrl.toLocalFile(), 0286 DMetadata::sidecarPath(outUrl.toLocalFile()), 0287 DMetadata::sidecarPath(dest.toLocalFile()), 0288 timeAdjust)) 0289 { 0290 emitActionData(ActionData::BatchFailed, i18n("Failed to create sidecar file..."), dest); 0291 } 0292 } 0293 0294 if (DFileOperations::localFileRename(d->tools.m_itemUrl.toLocalFile(), 0295 outUrl.toLocalFile(), 0296 dest.toLocalFile(), 0297 timeAdjust)) 0298 { 0299 emitActionData(ActionData::BatchDone, i18n("Item processed successfully %1", renameMess), 0300 dest, noWriteMetadata); 0301 } 0302 else 0303 { 0304 emitActionData(ActionData::BatchFailed, i18n("Failed to create file..."), dest); 0305 } 0306 } 0307 else 0308 { 0309 emitActionData(ActionData::BatchFailed, i18n("Failed to create file..."), dest); 0310 } 0311 0312 Q_EMIT signalDone(); 0313 } 0314 0315 } // namespace Digikam 0316 0317 #include "moc_task.cpp"