File indexing completed on 2025-01-05 03:53:10

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-03-15
0007  * Description : a tool to create panorama by fusion of several images.
0008  *
0009  * SPDX-FileCopyrightText: 2012-2016 by Benjamin Girault <benjamin dot girault at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "copyfilestask.h"
0016 
0017 // Qt includes
0018 
0019 #include <QFileInfo>
0020 #include <QDateTime>
0021 #include <QScopedPointer>
0022 
0023 // KDE includes
0024 
0025 #include <klocalizedstring.h>
0026 
0027 // Local includes
0028 
0029 #include "digikam_debug.h"
0030 #include "drawdecoder.h"
0031 #include "panomanager.h"
0032 
0033 namespace DigikamGenericPanoramaPlugin
0034 {
0035 
0036 CopyFilesTask::CopyFilesTask(const QString& workDirPath,
0037                              const QUrl& panoUrl,
0038                              const QUrl& finalPanoUrl,
0039                              const QUrl& ptoUrl,
0040                              const PanoramaItemUrlsMap& urls,
0041                              bool sPTO,
0042                              bool GPlusMetadata)
0043     : PanoTask(PANO_COPY, workDirPath),
0044       panoUrl(panoUrl),
0045       finalPanoUrl(finalPanoUrl),
0046       ptoUrl(ptoUrl),
0047       urlList(&urls),
0048       savePTO(sPTO),
0049       addGPlusMetadata(GPlusMetadata)
0050 {
0051 }
0052 
0053 CopyFilesTask::~CopyFilesTask()
0054 {
0055 }
0056 
0057 void CopyFilesTask::run(ThreadWeaver::JobPointer, ThreadWeaver::Thread*)
0058 {
0059     QFile     panoFile(panoUrl.toLocalFile());
0060     QFile     finalPanoFile(finalPanoUrl.toLocalFile());
0061 
0062     QFileInfo fi(finalPanoUrl.toLocalFile());
0063     QUrl      finalPTOUrl = finalPanoUrl.adjusted(QUrl::RemoveFilename);
0064     finalPTOUrl.setPath(finalPTOUrl.path() + fi.completeBaseName() + QLatin1String(".pto"));
0065 
0066     QFile     ptoFile(ptoUrl.toLocalFile());
0067     QFile     finalPTOFile(finalPTOUrl.toLocalFile());
0068 
0069     if (!panoFile.exists())
0070     {
0071         errString = i18n("Temporary panorama file does not exists.");
0072         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Temporary panorama file does not exists: " << panoUrl;
0073         successFlag = false;
0074         return;
0075     }
0076 
0077     if (finalPanoFile.exists())
0078     {
0079         errString = i18n("A panorama file named <filename>%1</filename> already exists.", finalPanoUrl.fileName());
0080         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Final panorama file already exists: " << finalPanoUrl;
0081         successFlag = false;
0082         return;
0083     }
0084 
0085     if (savePTO && !ptoFile.exists())
0086     {
0087         errString = i18n("Temporary project file does not exist.");
0088         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Temporary project file does not exists: " << ptoUrl;
0089         successFlag = false;
0090         return;
0091     }
0092 
0093     if (savePTO && finalPTOFile.exists())
0094     {
0095         errString = i18n("A project file named <filename>%1</filename> already exists.", finalPTOUrl.fileName());
0096         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Final project file already exists: " << finalPTOUrl;
0097         successFlag = false;
0098         return;
0099     }
0100 
0101     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Copying GPS info...";
0102 
0103     // Find first src image which contain geolocation and save it to target pano file.
0104 
0105     double lat, lng, alt;
0106     QScopedPointer<DMetadata> meta(new DMetadata);
0107 
0108     for (PanoramaItemUrlsMap::const_iterator i = urlList->constBegin() ; i != urlList->constEnd() ; ++i)
0109     {
0110         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << i.key();
0111 
0112         meta->load(i.key().toLocalFile());
0113 
0114         if (meta->getGPSInfo(alt, lat, lng))
0115         {
0116             qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "GPS info found and saved in " << panoUrl;
0117             meta->load(panoUrl.toLocalFile());
0118             meta->setGPSInfo(alt, lat, lng);
0119             meta->applyChanges(true);
0120             break;
0121         }
0122     }
0123 
0124     // Restore usual and common metadata from first shot.
0125 
0126     meta->load(urlList->constBegin().key().toLocalFile());
0127     QByteArray iptc = meta->getIptc();
0128     QByteArray xmp  = meta->getXmp();
0129     QString make    = meta->getExifTagString("Exif.Image.Make");
0130     QString model   = meta->getExifTagString("Exif.Image.Model");
0131     QDateTime dt    = meta->getItemDateTime();
0132 
0133     meta->load(panoUrl.toLocalFile());
0134     meta->setIptc(iptc);
0135     meta->setXmp(xmp);
0136     meta->setXmpTagString("Xmp.tiff.Make",   make);
0137     meta->setXmpTagString("Xmp.tiff.Model", model);
0138     meta->setImageDateTime(dt);
0139 
0140     QString filesList;
0141 
0142     for (PanoramaItemUrlsMap::const_iterator i = urlList->constBegin() ; i != urlList->constEnd() ; ++i)
0143     {
0144         filesList.append(i.key().fileName() + QLatin1String(" ; "));
0145     }
0146 
0147     filesList.truncate(filesList.length()-3);
0148 
0149     meta->setXmpTagString("Xmp.digiKam.PanoramaInputFiles", filesList);
0150 
0151     // NOTE : See https://developers.google.com/photo-sphere/metadata/ for details
0152 
0153     if (addGPlusMetadata)
0154     {
0155         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Adding PhotoSphere metadata...";
0156         meta->registerXmpNameSpace(QLatin1String("http://ns.google.com/photos/1.0/panorama/"),      // krazy:exclude=insecurenet
0157                                    QLatin1String("GPano"));
0158         meta->setXmpTagString("Xmp.GPano.UsePanoramaViewer", QLatin1String("True"));
0159         meta->setXmpTagString("Xmp.GPano.StitchingSoftware", QLatin1String("Panorama digiKam tool with Hugin"));
0160         meta->setXmpTagString("Xmp.GPano.ProjectionType",    QLatin1String("equirectangular"));
0161     }
0162 
0163     meta->applyChanges(true);
0164 
0165     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Copying panorama file...";
0166 
0167     if (!panoFile.copy(finalPanoUrl.toLocalFile()) || !panoFile.remove())
0168     {
0169         errString   = i18n("Cannot move panorama from <filename>%1</filename> to <filename>%2</filename>.",
0170                            panoUrl.toLocalFile(),
0171                            finalPanoUrl.toLocalFile());
0172         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Cannot move panorama: QFile error = " << panoFile.error();
0173         successFlag = false;
0174 
0175         return;
0176     }
0177 
0178     if (savePTO)
0179     {
0180         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Copying project file...";
0181 
0182         if (!ptoFile.copy(finalPTOUrl.toLocalFile()))
0183         {
0184             errString   = i18n("Cannot move project file from <filename>%1</filename> to <filename>%2</filename>.",
0185                                panoUrl.toLocalFile(),
0186                                finalPanoUrl.toLocalFile());
0187             successFlag = false;
0188 
0189             return;
0190         }
0191 
0192         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Copying converted RAW files...";
0193 
0194         for (PanoramaItemUrlsMap::const_iterator i = urlList->constBegin() ; i != urlList->constEnd() ; ++i)
0195         {
0196             if (DRawDecoder::isRawFile(i.key()))
0197             {
0198                 QUrl finalImgUrl = finalPanoUrl.adjusted(QUrl::RemoveFilename);
0199                 finalImgUrl.setPath(finalImgUrl.path() + i->preprocessedUrl.fileName());
0200                 QFile finalImgFile(finalImgUrl.toLocalFile());
0201                 QFile imgFile(i->preprocessedUrl.toLocalFile());
0202 
0203                 if (finalImgFile.exists())
0204                 {
0205                     continue;
0206                 }
0207 
0208                 if (!imgFile.copy(finalImgUrl.toLocalFile()))
0209                 {
0210                     errString   = i18n("Cannot copy converted image file from <filename>%1</filename> to <filename>%2</filename>.",
0211                                        i->preprocessedUrl.toLocalFile(),
0212                                        finalImgUrl.toLocalFile());
0213                     successFlag = false;
0214 
0215                     return;
0216                 }
0217             }
0218         }
0219     }
0220 
0221     Q_EMIT PanoManager::instance()->updateHostApp(finalPanoUrl);
0222 
0223     successFlag = true;
0224 }
0225 
0226 } // namespace DigikamGenericPanoramaPlugin