1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2021-02-18
7 * Description : Qt5 and Qt6 interface for exiftool - private container.
8 * Based on ZExifTool Qt interface published at 18 Feb 2021
9 * https://github.com/philvl/ZExifTool
10 *
11 * Copyright (C) 2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
12 * Copyright (c) 2021 by Philippe Vianney Liaud <philvl dot dev at gmail dot com>
13 *
14 * This program is free software; you can redistribute it
15 * and/or modify it under the terms of the GNU General
16 * Public License as published by the Free Software Foundation;
17 * either version 2, or (at your option)
18 * any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * ============================================================ */
26
27 #include "exiftoolprocess_p.h"
28
29 namespace Digikam
30 {
31
32 QMutex ExifToolProcess::Private::s_cmdIdMutex;
33 int ExifToolProcess::Private::s_nextCmdId = ExifToolProcess::Private::CMD_ID_MIN;
34
Private(ExifToolProcess * const q)35 ExifToolProcess::Private::Private(ExifToolProcess* const q)
36 : pp (q),
37 process (nullptr),
38 cmdRunning (0),
39 cmdAction (ExifToolProcess::LOAD_METADATA),
40 writeChannelIsClosed(true),
41 processError (QProcess::UnknownError)
42 {
43 outAwait[0] = false;
44 outAwait[1] = false;
45 outReady[0] = false;
46 outReady[1] = false;
47 }
48
execNextCmd()49 void ExifToolProcess::Private::execNextCmd()
50 {
51 if ((process->state() != QProcess::Running) ||
52 writeChannelIsClosed)
53 {
54 qCWarning(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::execNextCmd(): ExifTool is not running";
55 return;
56 }
57
58 if (cmdRunning || cmdQueue.isEmpty())
59 {
60 return;
61 }
62
63 // Clear QProcess buffers
64
65 process->readAllStandardOutput();
66 process->readAllStandardError();
67
68 // Clear internal buffers
69
70 outBuff[0] = QByteArray();
71 outBuff[1] = QByteArray();
72 outAwait[0] = false;
73 outAwait[1] = false;
74 outReady[0] = false;
75 outReady[1] = false;
76
77 // Exec Command
78
79 execTimer.start();
80
81 Command command = cmdQueue.takeFirst();
82 cmdRunning = command.id;
83 cmdAction = command.ac;
84
85 process->write(command.argsStr);
86 }
87
readOutput(const QProcess::ProcessChannel channel)88 void ExifToolProcess::Private::readOutput(const QProcess::ProcessChannel channel)
89 {
90 process->setReadChannel(channel);
91
92 while (process->canReadLine() && !outReady[channel])
93 {
94 QByteArray line = process->readLine();
95
96 if (line.endsWith(QByteArray("\r\n")))
97 {
98 line.remove(line.size() - 2, 1); // Remove '\r' character
99 }
100 /*
101 qCDebug(DIGIKAM_METAENGINE_LOG) << channel << line;
102 */
103 if (!outAwait[channel])
104 {
105 if (line.startsWith(QByteArray("{await")) && line.endsWith(QByteArray("}\n")))
106 {
107 outAwait[channel] = line.mid(6, line.size() - 8).toInt();
108 }
109
110 continue;
111 }
112
113 outBuff[channel] += line;
114
115 if (line.endsWith(QByteArray("{ready}\n")))
116 {
117 outBuff[channel].chop(8);
118 outReady[channel] = true;
119
120 break;
121 }
122 }
123
124 // Check if outputChannel and errorChannel are both ready
125
126 if (!(outReady[QProcess::StandardOutput] &&
127 outReady[QProcess::StandardError]))
128 {
129 /*
130 qCWarning(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::readOutput(): ExifTool read channels are not ready";
131 */
132 return;
133 }
134
135 if (
136 (cmdRunning != outAwait[QProcess::StandardOutput]) ||
137 (cmdRunning != outAwait[QProcess::StandardError])
138 )
139 {
140 qCCritical(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::readOutput: Sync error between CmdID("
141 << cmdRunning
142 << "), outChannel("
143 << outAwait[0]
144 << ") and errChannel("
145 << outAwait[1]
146 << ")";
147 }
148 else
149 {
150 qCDebug(DIGIKAM_METAENGINE_LOG) << "ExifToolProcess::readOutput(): ExifTool command completed";
151
152 Q_EMIT pp->signalCmdCompleted(cmdAction,
153 execTimer.elapsed(),
154 outBuff[QProcess::StandardOutput],
155 outBuff[QProcess::StandardError]);
156 }
157
158 cmdRunning = 0; // No command is running
159
160 execNextCmd(); // Exec next command
161 }
162
setProcessErrorAndEmit(QProcess::ProcessError error,const QString & description)163 void ExifToolProcess::Private::setProcessErrorAndEmit(QProcess::ProcessError error, const QString& description)
164 {
165 processError = error;
166 errorString = description;
167
168 Q_EMIT pp->signalErrorOccurred(cmdAction, error);
169 }
170
171 } // namespace Digikam
172