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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2021-02-18
0007  * Description : Qt5 and Qt6 interface for exiftool - private container.
0008  *               Based on ZExifTool Qt interface published at 18 Feb 2021
0009  *               https://github.com/philvl/ZExifTool
0010  *
0011  * SPDX-FileCopyrightText: 2021-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  * SPDX-FileCopyrightText: 2021 by Philippe Vianney Liaud <philvl dot dev at gmail dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "exiftoolprocess_p.h"
0019 
0020 namespace Digikam
0021 {
0022 
0023 ExifToolProcess::Private::Private(ExifToolProcess* const q)
0024     : QObject             (q),
0025       pp                  (q),
0026       cmdNumber           (0),
0027       cmdAction           (ExifToolProcess::NO_ACTION),
0028       writeChannelIsClosed(true),
0029       processError        (QProcess::UnknownError),
0030       nextCmdId           (CMD_ID_MIN)
0031 {
0032     outAwait[0] = false;
0033     outAwait[1] = false;
0034     outReady[0] = false;
0035     outReady[1] = false;
0036 }
0037 
0038 void ExifToolProcess::Private::slotExecNextCmd()
0039 {
0040     if ((pp->state() != QProcess::Running) ||
0041         writeChannelIsClosed)
0042     {
0043         qCWarning(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::execNextCmd(): ExifTool is not running";
0044         return;
0045     }
0046 
0047     QMutexLocker locker(&cmdMutex);
0048 
0049     if (cmdNumber || cmdQueue.isEmpty())
0050     {
0051         return;
0052     }
0053 
0054     // Clear QProcess buffers
0055 
0056     pp->readAllStandardOutput();
0057     pp->readAllStandardError();
0058 
0059     // Clear internal buffers
0060 
0061     outBuff[0]      = QByteArray();
0062     outBuff[1]      = QByteArray();
0063     outAwait[0]     = false;
0064     outAwait[1]     = false;
0065     outReady[0]     = false;
0066     outReady[1]     = false;
0067 
0068     // Exec Command
0069 
0070     execTimer.start();
0071 
0072     Command command = cmdQueue.takeFirst();
0073     cmdNumber       = command.id;
0074     cmdAction       = command.ac;
0075 
0076     pp->write(command.argsStr);
0077 }
0078 
0079 void ExifToolProcess::Private::readOutput(const QProcess::ProcessChannel channel)
0080 {
0081     if (cmdNumber == 0)
0082     {
0083         return;
0084     }
0085 
0086     pp->setReadChannel(channel);
0087 
0088     while (pp->canReadLine() && !outReady[channel])
0089     {
0090         QByteArray line = pp->readLine();
0091 /*
0092         qCDebug(DIGIKAM_METAENGINE_LOG) << channel << line;
0093 */
0094         if (!outAwait[channel])
0095         {
0096             if (line.startsWith(QByteArray("{await")))
0097             {
0098                 if      (line.endsWith(QByteArray("}\n")))
0099                 {
0100                     outAwait[channel] = line.mid(6, line.size() - 8).toInt();
0101                 }
0102                 else if (line.endsWith(QByteArray("}\r\n")))
0103                 {
0104                     outAwait[channel] = line.mid(6, line.size() - 9).toInt();
0105                 }
0106             }
0107 
0108             continue;
0109         }
0110 
0111         outBuff[channel] += line;
0112 
0113         if      (line.endsWith(QByteArray("{ready}\n")))
0114         {
0115             outBuff[channel].chop(8);
0116             outReady[channel] = true;
0117 
0118             break;
0119         }
0120         else if (line.endsWith(QByteArray("{ready}\r\n")))
0121         {
0122             outBuff[channel].chop(9);
0123             outReady[channel] = true;
0124 
0125             break;
0126         }
0127     }
0128 
0129     // Check if outputChannel and errorChannel are both ready
0130 
0131     if (!(outReady[QProcess::StandardOutput] &&
0132         outReady[QProcess::StandardError]))
0133     {
0134 /*
0135         qCWarning(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::readOutput(): ExifTool read channels are not ready";
0136 */
0137         return;
0138     }
0139 
0140     if (
0141         (cmdNumber != outAwait[QProcess::StandardOutput]) ||
0142         (cmdNumber != outAwait[QProcess::StandardError])
0143        )
0144     {
0145         qCCritical(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::readOutput: Sync error between CmdID("
0146                                            << cmdNumber
0147                                            << "), outChannel("
0148                                            << outAwait[0]
0149                                            << ") and errChannel("
0150                                            << outAwait[1]
0151                                            << ")";
0152 
0153         setProcessErrorAndEmit(QProcess::ReadError, i18n("Synchronization error between the channels"));
0154     }
0155     else
0156     {
0157         qCDebug(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::readOutput(): ExifTool command completed";
0158 
0159         setCommandResult(ExifToolProcess::COMMAND_RESULT);
0160     }
0161 
0162     Q_EMIT pp->signalExecNextCmd(); // Exec next command
0163 }
0164 
0165 void ExifToolProcess::Private::setProcessErrorAndEmit(QProcess::ProcessError error, const QString& description)
0166 {
0167     processError = error;
0168     errorString  = description;
0169 
0170     setCommandResult(ExifToolProcess::ERROR_RESULT);
0171 }
0172 
0173 void ExifToolProcess::Private::setCommandResult(int cmdStatus)
0174 {
0175     QMutexLocker locker(&mutex);
0176 
0177     ExifToolProcess::Result result;
0178 
0179     result.waitError = false;
0180     result.cmdStatus = cmdStatus;
0181     result.cmdAction = cmdAction;
0182     result.cmdNumber = cmdNumber;
0183     result.elapsed   = execTimer.elapsed();
0184     result.output    = outBuff[QProcess::StandardOutput];
0185 
0186     resultMap.insert(cmdNumber, result);
0187 
0188     Q_EMIT pp->signalExifToolResult(cmdNumber);
0189 
0190     cmdNumber        = 0;
0191     cmdAction        = ExifToolProcess::NO_ACTION;
0192 
0193     condVar.wakeAll();
0194 }
0195 
0196 } // namespace Digikam
0197 
0198 #include "moc_exiftoolprocess_p.cpp"