File indexing completed on 2025-01-19 03:51:26

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-12-31
0007  * Description : time adjust actions using threads.
0008  *
0009  * SPDX-FileCopyrightText: 2012      by Smit Mehta <smit dot meh at gmail dot com>
0010  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2018-2021 by Maik Qualmann <metzpinguin at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "timeadjusttask.h"
0018 
0019 // Qt includes
0020 
0021 #include <QFile>
0022 #include <QScopedPointer>
0023 #include <qplatformdefs.h>
0024 
0025 // Local includes
0026 
0027 #include "dmetadata.h"
0028 #include "digikam_debug.h"
0029 #include "dinfointerface.h"
0030 #include "dfileoperations.h"
0031 #include "timeadjustlist.h"
0032 #include "metaenginesettings.h"
0033 
0034 namespace DigikamGenericTimeAdjustPlugin
0035 {
0036 
0037 class Q_DECL_HIDDEN TimeAdjustTask::Private
0038 {
0039 public:
0040 
0041     explicit Private()
0042       : thread(nullptr)
0043     {
0044     }
0045 
0046     // Settings from GUI.
0047 
0048     TimeAdjustContainer   settings;
0049 
0050     TimeAdjustThread*     thread;
0051 
0052     QUrl                  url;
0053 };
0054 
0055 TimeAdjustTask::TimeAdjustTask(const QUrl& url, TimeAdjustThread* const thread)
0056     : ActionJob(),
0057       d(new Private)
0058 {
0059     d->url    = url;
0060     d->thread = thread;
0061 }
0062 
0063 TimeAdjustTask::~TimeAdjustTask()
0064 {
0065     cancel();
0066     delete d;
0067 }
0068 
0069 void TimeAdjustTask::setSettings(const TimeAdjustContainer& settings)
0070 {
0071     d->settings = settings;
0072 }
0073 
0074 void TimeAdjustTask::run()
0075 {
0076     if (m_cancel)
0077     {
0078         return;
0079     }
0080 
0081     Q_EMIT signalProcessStarted(d->url);
0082 
0083     QDateTime org = d->thread->readTimestamp(d->url);
0084     QDateTime adj = d->settings.calculateAdjustedDate(org, d->thread->indexForUrl(d->url));
0085 
0086     if (!adj.isValid())
0087     {
0088         Q_EMIT signalProcessEnded(d->url, org, adj, TimeAdjustList::META_TIME_ERROR);
0089         Q_EMIT signalDone();
0090         return;
0091     }
0092 
0093     if (m_cancel)
0094     {
0095         return;
0096     }
0097 
0098     bool metadataChanged               = false;
0099     bool writeToSidecar                = (MetaEngineSettings::instance()->settings()
0100                                           .metadataWritingMode != DMetadata::WRITE_TO_FILE_ONLY);
0101     bool writeWithExifTool             = (MetaEngineSettings::instance()->settings().writeWithExifTool);
0102 
0103     int status                         = TimeAdjustList::NOPROCESS_ERROR;
0104 
0105     QString exifDateTimeFormat         = QLatin1String("yyyy:MM:dd hh:mm:ss");
0106     QString xmpDateTimeFormat          = QLatin1String("yyyy-MM-ddThh:mm:ss");
0107 
0108     const QMap<QString, bool>& tagsMap = d->settings.getDateTimeTagsMap();
0109     QMap<QString, bool>::const_iterator it;
0110 
0111     QScopedPointer<DMetadata> meta(new DMetadata);
0112 
0113     if (meta->load(d->url.toLocalFile()))
0114     {
0115         for (it = tagsMap.constBegin() ; it != tagsMap.constEnd() ; ++it)
0116         {
0117             if (!it.value())
0118             {
0119                 continue;
0120             }
0121 
0122             bool ret = true;
0123 
0124             if      (it.key().startsWith(QLatin1String("Exif.")) &&
0125                      (meta->canWriteExif(d->url.toLocalFile())   ||
0126                       writeWithExifTool                          ||
0127                       writeToSidecar)
0128                     )
0129             {
0130                 if (!d->settings.updIfAvailable ||
0131                     !meta->getExifTagString(it.key().toLatin1().constData()).isEmpty())
0132                 {
0133                     ret &= meta->setExifTagString(it.key().toLatin1().constData(),
0134                                                   adj.toString(exifDateTimeFormat));
0135 
0136                     metadataChanged = true;
0137                 }
0138             }
0139             else if (it.key().startsWith(QLatin1String("Iptc.")) &&
0140                      (meta->canWriteIptc(d->url.toLocalFile())   ||
0141                       writeWithExifTool                          ||
0142                       writeToSidecar)
0143                     )
0144             {
0145                 if (!d->settings.updIfAvailable ||
0146                     !meta->getIptcTagString(it.key().toLatin1().constData()).isEmpty())
0147                 {
0148                     if      (it.key().contains(QLatin1String("Date")))
0149                     {
0150                         ret &= meta->setIptcTagString(it.key().toLatin1().constData(),
0151                                                       adj.date().toString(Qt::ISODate));
0152 
0153                         metadataChanged = true;
0154                     }
0155                     else if (it.key().contains(QLatin1String("Time")))
0156                     {
0157                         ret &= meta->setIptcTagString(it.key().toLatin1().constData(),
0158                                                       adj.time().toString(Qt::ISODate));
0159 
0160                         metadataChanged = true;
0161                     }
0162                 }
0163             }
0164             else if (it.key().startsWith(QLatin1String("Xmp.")) &&
0165                      (meta->canWriteXmp(d->url.toLocalFile())   ||
0166                       writeWithExifTool                         ||
0167                       writeToSidecar)
0168                      )
0169             {
0170                 if (!d->settings.updIfAvailable ||
0171                     !meta->getXmpTagString(it.key().toLatin1().constData()).isEmpty())
0172                 {
0173                     ret &= meta->setXmpTagString(it.key().toLatin1().constData(),
0174                                                  adj.toString(xmpDateTimeFormat));
0175 
0176                     metadataChanged = true;
0177                 }
0178             }
0179 
0180             if (!ret)
0181             {
0182                 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Failed to set metadata for tag" << it.key();
0183 
0184                 status |= TimeAdjustList::META_TIME_ERROR;
0185 
0186                 break;
0187             }
0188         }
0189 
0190         if ((status == TimeAdjustList::NOPROCESS_ERROR) && metadataChanged)
0191         {
0192             if (!meta->save(d->url.toLocalFile()))
0193             {
0194                 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Failed to update metadata in file" << d->url.fileName();
0195 
0196                 status |= TimeAdjustList::META_TIME_ERROR;
0197             }
0198         }
0199     }
0200     else
0201     {
0202         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Failed to load metadata from file" << d->url.fileName();
0203 
0204         status |= TimeAdjustList::META_TIME_ERROR;
0205     }
0206 
0207     if (d->settings.updFileModDate)
0208     {
0209         if (!DFileOperations::setModificationTime(d->url.toLocalFile(), adj))
0210         {
0211             status |= TimeAdjustList::FILE_TIME_ERROR;
0212         }
0213     }
0214 
0215     if (d->settings.updFileModDate && writeToSidecar && DMetadata::hasSidecar(d->url.toLocalFile()))
0216     {
0217         if (!DFileOperations::setModificationTime(DMetadata::sidecarPath(d->url.toLocalFile()), adj))
0218         {
0219             status |= TimeAdjustList::FILE_TIME_ERROR;
0220         }
0221     }
0222 
0223     if ((status & TimeAdjustList::META_TIME_ERROR) != TimeAdjustList::META_TIME_ERROR)
0224     {
0225         Q_EMIT signalDateTimeForUrl(d->url, adj, d->settings.updFileModDate);
0226     }
0227 
0228     Q_EMIT signalProcessEnded(d->url, org, adj, status);
0229     Q_EMIT signalDone();
0230 }
0231 
0232 // ------------------------------------------------------------------
0233 
0234 class Q_DECL_HIDDEN TimePreviewTask::Private
0235 {
0236 public:
0237 
0238     explicit Private()
0239       : thread(nullptr)
0240     {
0241     }
0242 
0243     // Settings from GUI.
0244 
0245     TimeAdjustContainer   settings;
0246 
0247     TimeAdjustThread*     thread;
0248 
0249     QUrl                  url;
0250 };
0251 
0252 TimePreviewTask::TimePreviewTask(const QUrl& url, TimeAdjustThread* const thread)
0253     : ActionJob(),
0254       d(new Private)
0255 {
0256     d->url    = url;
0257     d->thread = thread;
0258 }
0259 
0260 TimePreviewTask::~TimePreviewTask()
0261 {
0262     cancel();
0263     delete d;
0264 }
0265 
0266 void TimePreviewTask::setSettings(const TimeAdjustContainer& settings)
0267 {
0268     d->settings = settings;
0269 }
0270 
0271 void TimePreviewTask::run()
0272 {
0273     if (m_cancel)
0274     {
0275         return;
0276     }
0277 
0278     QDateTime org = d->thread->readTimestamp(d->url);
0279     QDateTime adj = d->settings.calculateAdjustedDate(org, d->thread->indexForUrl(d->url));
0280 
0281     Q_EMIT signalPreviewReady(d->url, org, adj);
0282     Q_EMIT signalDone();
0283 }
0284 
0285 } // namespace DigikamGenericTimeAdjustPlugin
0286 
0287 #include "moc_timeadjusttask.cpp"