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"