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.
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 #ifndef DIGIKAM_EXIFTOOL_PROCESS_H
28 #define DIGIKAM_EXIFTOOL_PROCESS_H
29 
30 // Qt Core
31 
32 #include <QObject>
33 #include <QString>
34 #include <QProcess>
35 #include <QMutex>
36 
37 // Local includes
38 
39 #include "digikam_export.h"
40 
41 namespace Digikam
42 {
43 
44 class DIGIKAM_EXPORT ExifToolProcess : public QObject
45 {
46     Q_OBJECT
47 
48 public:
49 
50     /**
51      * ExifTool actions to process.
52      */
53     enum Action
54     {
55         LOAD_METADATA       = 0,                    ///< Load all metadata from a file with ExifTool.
56         LOAD_CHUNKS,                                ///< Load Exif, Iptc, and Xmp chunks from a file as byte-array for MetaEngine.
57         APPLY_CHANGES,                              ///< Apply tag changes in a file with ExifTool.
58         APPLY_CHANGES_EXV,                          ///< Apply tag changes in a file with ExifTool using an EXV container.
59         READ_FORMATS,                               ///< Return the list of readable ExifTool file formats.
60         WRITE_FORMATS,                              ///< Return the list of writable ExifTool file formats.
61         TRANSLATIONS_LIST,                          ///< List of ExifTool languages available for translations.
62         TAGS_DATABASE,                              ///< List of ExifTool tags from database.
63         VERSION_STRING,                             ///< Return the ExifTool version as string.
64         COPY_TAGS,                                  ///< Copy tags from one file to another one. See CopyTagsSource enum for details.
65         TRANS_TAGS,                                 ///< Translate tags in file. See TranslateTagsOps enum for details.
66         NO_ACTION                                   ///< Last value from this list. Do nothing.
67     };
68 
69     /**
70      * Possible copying tags operations to OR combine with COPY_TAGS action.
71      */
72     enum CopyTagsSource
73     {
74         COPY_EXIF           = 0x01,                 ///< Copy all Exif Tags from source file.
75         COPY_MAKERNOTES     = 0x02,                 ///< Copy all Makernotes tags from source file.
76         COPY_IPTC           = 0x04,                 ///< Copy all Iptc tags from source file.
77         COPY_XMP            = 0x08,                 ///< Copy all Xmp tags from source file.
78         COPY_ICC            = 0x10,                 ///< Copy ICC profile from source file.
79         COPY_ALL            = 0x20,                 ///< Copy all tags from source file.
80         COPY_NONE           = 0x40                  ///< No copy operation.
81     };
82 
83     /**
84      * Possible writing tags mode to OR combine with COPY_TAGS action.
85      */
86     enum WritingTagsMode
87     {
88         WRITE_EXISTING_TAGS = 0x01,                 ///< Overwrite existing tags.
89         CREATE_NEW_TAGS     = 0x02,                 ///< Create new tags.
90         CREATE_NEW_GROUPS   = 0x04,                 ///< Create new groups if necessary.
91         ALL_MODES           = WRITE_EXISTING_TAGS |
92                               CREATE_NEW_TAGS     |
93                               CREATE_NEW_GROUPS
94     };
95 
96     /**
97      * Possible translating tags operations to OR combine with COPY_TAGS action.
98      */
99     enum TranslateTagsOps
100     {
101         TRANS_ALL_XMP       = 0x01,                 ///< Translate all existing Tags from source file to Xmp.
102         TRANS_ALL_IPTC      = 0x02,                 ///< Translate all existing Tags from source file to Iptc.
103         TRANS_ALL_EXIF      = 0x04                  ///< Translate all existing Tags from source file to Exif.
104     };
105 
106 public:
107 
108     /**
109      * Constructs a ExifToolProcess object with the given parent.
110      */
111     explicit ExifToolProcess(QObject* const parent);
112 
113     /**
114      * Destructs the ExifToolProcess object, i.e., killing the process.
115      * Note that this function will not return until the process is terminated.
116      */
117     ~ExifToolProcess();
118 
119 public:
120 
121     /**
122      * Setup the ExifTool configuration. This function must be called before start().
123      */
124     void setProgram(const QString& etExePath,
125                     const QString& perlExePath = QString());
126 
127     QString program() const;
128 
129     bool checkExifToolProgram();
130 
131     /**
132      * Starts exiftool in a new process.
133      */
134     bool start();
135 
136     /**
137      * Attempts to terminate the process.
138      */
139     void terminate();
140 
141     /**
142      * Kills the current process, causing it to exit immediately.
143      * On Windows, kill() uses TerminateProcess, and on Unix and macOS,
144      * the SIGKILL signal is sent to the process.
145      */
146     void kill();
147 
148 public:
149 
150     /**
151      * Returns true if ExifToolProcess is running (process state == Running)
152      */
153     bool                   isRunning()                  const;
154 
155     /**
156      * Returns true if a command is running
157      */
158     bool                   isBusy()                     const;
159 
160     /**
161      * Returns the native process identifier for the running process, if available.
162      * If no process is currently running, 0 is returned.
163      */
164     qint64                 processId()                  const;
165 
166     /**
167      * Returns the current state of the process.
168      */
169     QProcess::ProcessState state()                      const;
170 
171     /**
172      * Returns the type of error that occurred last.
173      */
174     QProcess::ProcessError error()                      const;
175 
176     /**
177      * Returns an error message.
178      */
179     QString                errorString()                const;
180 
181     /**
182      * Returns the exit status of the last process that finished.
183      */
184     QProcess::ExitStatus   exitStatus()                 const;
185 
186     int                    exitCode()                   const;
187 
188     /**
189      * Blocks until the process has started and the started() signal has been emitted,
190      * or until msecs milliseconds have passed.
191      */
192     bool waitForStarted(int msecs = 30000)              const;
193 
194     /**
195      * Blocks until the process has finished and the finished() signal has been emitted,
196      * or until msecs milliseconds have passed.
197      */
198     bool waitForFinished(int msecs = 30000)             const;
199 
200     /**
201      * Send a command to exiftool process
202      * Return 0: ExitTool not running, write channel is closed or args is empty
203      */
204     int command(const QByteArrayList& args, Action ac);
205 
206 Q_SIGNALS:
207 
208     void signalStarted(int cmdAction);
209 
210     void signalStateChanged(int cmdAction,
211                             QProcess::ProcessState newState);
212 
213     void signalErrorOccurred(int cmdAction,
214                              QProcess::ProcessError error);
215 
216     void signalFinished(int cmdAction,
217                         int exitCode,
218                         QProcess::ExitStatus exitStatus);
219 
220     void signalCmdCompleted(int cmdAction,
221                             int execTime,
222                             const QByteArray& cmdOutputChannel,
223                             const QByteArray& cmdErrorChannel);
224 
225 private Q_SLOTS:
226 
227     void slotStarted();
228     void slotStateChanged(QProcess::ProcessState newState);
229     void slotErrorOccurred(QProcess::ProcessError error);
230     void slotReadyReadStandardOutput();
231     void slotReadyReadStandardError();
232     void slotFinished(int exitCode,
233                       QProcess::ExitStatus exitStatus);
234 
235 private:
236 
237     QString exifToolBin()                               const;
238 
239 private:
240 
241     class Private;
242     Private* const d;
243 };
244 
245 } // namespace Digikam
246 
247 #endif // DIGIKAM_EXIFTOOL_PROCESS_H
248