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"