File indexing completed on 2025-01-19 03:52:56

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2019-03-27
0007  * Description : file copy 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: 2019-2020 by Maik Qualmann <metzpinguin at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "fctask.h"
0018 
0019 // Qt includes
0020 
0021 #include <QDir>
0022 #include <QFile>
0023 #include <QMimeDatabase>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "digikam_debug.h"
0032 #include "digikam_config.h"
0033 #include "dfileoperations.h"
0034 #include "previewloadthread.h"
0035 #include "dmetadata.h"
0036 #include "dimg.h"
0037 #include "metaenginesettings.h"
0038 
0039 namespace DigikamGenericFileCopyPlugin
0040 {
0041 
0042 class Q_DECL_HIDDEN FCTask::Private
0043 {
0044 public:
0045 
0046     explicit Private()
0047     {
0048     }
0049 
0050     QUrl        srcUrl;
0051     FCContainer settings;
0052 };
0053 
0054 FCTask::FCTask(const QUrl& srcUrl,
0055                const FCContainer& settings)
0056     : ActionJob(),
0057       d        (new Private)
0058 {
0059     d->srcUrl   = srcUrl;
0060     d->settings = settings;
0061 }
0062 
0063 FCTask::~FCTask()
0064 {
0065     cancel();
0066     delete d;
0067 }
0068 
0069 void FCTask::run()
0070 {
0071     if (m_cancel)
0072     {
0073         return;
0074     }
0075 
0076     bool ok   = true;
0077     QUrl dest = d->settings.destUrl.adjusted(QUrl::StripTrailingSlash);
0078 
0079     if (d->settings.iface && d->settings.iface->supportAlbums() && d->settings.albumPath)
0080     {
0081         DItemInfo info(d->settings.iface->itemInfo(d->srcUrl));
0082         DAlbumInfo album(d->settings.iface->albumInfo(info.albumId()));
0083 
0084         dest.setPath(dest.path() + album.albumPath());
0085         dest = dest.adjusted(QUrl::StripTrailingSlash);
0086 
0087         if (!QDir(dest.toLocalFile()).exists())
0088         {
0089             ok = QDir().mkpath(dest.toLocalFile());
0090         }
0091     }
0092 
0093     dest.setPath(dest.path() +
0094                  QLatin1Char('/') +
0095                  d->srcUrl.fileName());
0096 
0097     if (d->srcUrl == dest)
0098     {
0099         Q_EMIT signalDone();
0100 
0101         return;
0102     }
0103 
0104     QUrl sidecarDest = DMetadata::sidecarUrl(dest);
0105 
0106     if      (ok && (d->settings.behavior == FCContainer::CopyFile))
0107     {
0108         QFileInfo srcInfo(d->srcUrl.toLocalFile());
0109         QString suffix = srcInfo.suffix().toUpper();
0110 
0111         QMimeDatabase mimeDB;
0112         QString mimeType(mimeDB.mimeTypeForUrl(d->srcUrl).name());
0113 
0114         if (d->settings.changeImageProperties             &&
0115             (
0116              mimeType.startsWith(QLatin1String("image/")) ||
0117              (suffix == QLatin1String("PGF"))             ||
0118              (suffix == QLatin1String("JXL"))             ||
0119              (suffix == QLatin1String("AVIF"))            ||
0120              (suffix == QLatin1String("KRA"))             ||
0121              (suffix == QLatin1String("HIF"))             ||
0122              (suffix == QLatin1String("HEIC"))            ||
0123              (suffix == QLatin1String("HEIF"))
0124             )
0125            )
0126         {
0127             ok = imageResize(d->srcUrl.toLocalFile(), dest);
0128         }
0129         else
0130         {
0131             dest = getUrlOrDelete(dest);
0132             ok   = DFileOperations::copyFile(d->srcUrl.toLocalFile(),
0133                                              dest.toLocalFile());
0134 
0135             if (d->settings.sidecars && DMetadata::hasSidecar(d->srcUrl.toLocalFile()))
0136             {
0137                 sidecarDest = getUrlOrDelete(sidecarDest);
0138                 DFileOperations::copyFile(DMetadata::sidecarUrl(d->srcUrl).toLocalFile(),
0139                                           sidecarDest.toLocalFile());
0140             }
0141 
0142             if (d->settings.writeMetadataToFile &&
0143                 (MetaEngineSettings::instance()->settings().metadataWritingMode == MetaEngine::WRITE_TO_SIDECAR_ONLY))
0144             {
0145                 QScopedPointer<DMetadata> meta(new DMetadata);
0146 
0147                 if (meta->load(d->srcUrl.toLocalFile()))
0148                 {
0149                     meta->setMetadataWritingMode(DMetadata::WRITE_TO_FILE_ONLY);
0150                     meta->save(dest.toLocalFile());
0151                 }
0152             }
0153         }
0154     }
0155     else if (ok                                                     &&
0156              ((d->settings.behavior == FCContainer::FullSymLink)    ||
0157               (d->settings.behavior == FCContainer::RelativeSymLink)))
0158     {
0159 
0160 #ifdef Q_OS_WIN
0161 
0162         dest.setPath(dest.path() + QLatin1String(".lnk"));
0163         sidecarDest.setPath(sidecarDest.path() + QLatin1String(".lnk"));
0164 
0165 #endif
0166 
0167         dest        = getUrlOrDelete(dest);
0168         sidecarDest = getUrlOrDelete(sidecarDest);
0169 
0170         if (d->settings.behavior == FCContainer::FullSymLink)
0171         {
0172             ok = QFile::link(d->srcUrl.toLocalFile(),
0173                              dest.toLocalFile());
0174 
0175             if (d->settings.sidecars && DMetadata::hasSidecar(d->srcUrl.toLocalFile()))
0176             {
0177                 QFile::link(DMetadata::sidecarUrl(d->srcUrl).toLocalFile(),
0178                             sidecarDest.toLocalFile());
0179             }
0180         }
0181         else
0182         {
0183             QDir dir(d->settings.destUrl.toLocalFile());
0184             QString path = dir.relativeFilePath(d->srcUrl.toLocalFile());
0185             QUrl srcUrl  = QUrl::fromLocalFile(path);
0186             ok           = QFile::link(srcUrl.toLocalFile(),
0187                                        dest.toLocalFile());
0188 
0189             if (d->settings.sidecars && DMetadata::hasSidecar(d->srcUrl.toLocalFile()))
0190             {
0191                 QFile::link(DMetadata::sidecarUrl(srcUrl).toLocalFile(),
0192                             sidecarDest.toLocalFile());
0193             }
0194         }
0195     }
0196 
0197     if (ok)
0198     {
0199         Q_EMIT signalUrlProcessed(d->srcUrl, dest);
0200     }
0201 
0202     Q_EMIT signalDone();
0203 }
0204 
0205 bool FCTask::imageResize(const QString& orgPath, QUrl& destUrl)
0206 {
0207     QFileInfo fi(orgPath);
0208 
0209     if (!fi.exists() || !fi.isReadable())
0210     {
0211         qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error opening input file"
0212                                          << fi.filePath();
0213         return false;
0214     }
0215 
0216     QFileInfo destInfo(destUrl.toLocalFile());
0217     QFileInfo tmpDir(destInfo.dir().absolutePath());
0218 
0219     if (!tmpDir.exists() || !tmpDir.isWritable())
0220     {
0221         qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error opening target folder"
0222                                          << tmpDir.dir();
0223         return false;
0224     }
0225 
0226     DImg img = PreviewLoadThread::loadHighQualitySynchronously(orgPath);
0227 
0228     if (img.isNull())
0229     {
0230         img.load(orgPath);
0231     }
0232 
0233     if (!img.isNull())
0234     {
0235         uint sizeFactor = d->settings.imageResize;
0236 
0237         if ((img.width() > sizeFactor) || (img.height() > sizeFactor))
0238         {
0239             DImg scaledImg = img.smoothScale(sizeFactor,
0240                                              sizeFactor,
0241                                              Qt::KeepAspectRatio);
0242 
0243             if ((scaledImg.width() > sizeFactor) || (scaledImg.height() > sizeFactor))
0244             {
0245                 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Cannot resize image";
0246                 return false;
0247             }
0248 
0249             img = scaledImg;
0250         }
0251 
0252         QString destFile = destInfo.path()  +
0253                            QLatin1Char('/') +
0254                            destInfo.completeBaseName();
0255 
0256         if (d->settings.imageFormat == FCContainer::JPEG)
0257         {
0258             destFile.append(QLatin1String(".jpg"));
0259             destUrl  = getUrlOrDelete(QUrl::fromLocalFile(destFile));
0260             destFile = destUrl.toLocalFile();
0261 
0262             if (d->settings.sidecars)
0263             {
0264                 getUrlOrDelete(DMetadata::sidecarUrl(destUrl));
0265             }
0266 
0267             img.setAttribute(QLatin1String("quality"), d->settings.imageCompression);
0268 
0269             if (!img.save(destFile, DImg::JPEG))
0270             {
0271                 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Cannot save resized image (JPEG)";
0272                 return false;
0273             }
0274         }
0275         else if (d->settings.imageFormat == FCContainer::PNG)
0276         {
0277             destFile.append(QLatin1String(".png"));
0278             destUrl  = getUrlOrDelete(QUrl::fromLocalFile(destFile));
0279             destFile = destUrl.toLocalFile();
0280 
0281             if (d->settings.sidecars)
0282             {
0283                 getUrlOrDelete(DMetadata::sidecarUrl(destUrl));
0284             }
0285 
0286             if (!img.save(destFile, DImg::PNG))
0287             {
0288                 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Cannot save resized image (PNG)";
0289                 return false;
0290             }
0291         }
0292 
0293         QScopedPointer<DMetadata> meta(new DMetadata);
0294 
0295         if (!meta->load(destFile))
0296         {
0297             return false;
0298         }
0299 
0300         if (d->settings.removeMetadata)
0301         {
0302             meta->clearExif();
0303             meta->clearIptc();
0304             meta->clearXmp();
0305         }
0306         else
0307         {
0308             meta->setItemOrientation(MetaEngine::ORIENTATION_NORMAL);
0309         }
0310 
0311         if (!meta->save(destFile))
0312         {
0313             return false;
0314         }
0315 
0316         DFileOperations::copyModificationTime(orgPath, destFile);
0317 
0318         return true;
0319     }
0320 
0321     return false;
0322 }
0323 
0324 QUrl FCTask::getUrlOrDelete(const QUrl& fileUrl) const
0325 {
0326     if (d->settings.overwrite              &&
0327         QFile::exists(fileUrl.toLocalFile()))
0328     {
0329         QFile::remove(fileUrl.toLocalFile());
0330 
0331         return fileUrl;
0332     }
0333 
0334     return DFileOperations::getUniqueFileUrl(fileUrl);
0335 }
0336 
0337 } // namespace DigikamGenericFileCopyPlugin
0338 
0339 #include "moc_fctask.cpp"