Commit febc2618 authored by Gilles Caulier's avatar Gilles Caulier 🗼
Browse files

Add ExifTool backend to load metadata for image or video.

Exiv2 is always used in priority. If it fail, ExifTool is used instead.
ExifTool has the capability to create an EXV container for Exiv2. All metadata extracted with ExifTool
are concatened to the EXV container with all possible translations with Exif, Iptc, and Xmp tags.
Of course tags supported by ExifTool but not supported by Exiv2 are lost.
CCBUGS: 385726
CCBUGS: 416516
CCBUGS: 360714
CCBUGS: 264210
CCBUGS: 426938
CCBUGS: 419801
CCBUGS: 419375
CCBUGS: 103247
parent bb232bd7
......@@ -47,6 +47,7 @@ set(libdmetadata_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/dmetadata/dmetadata.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dmetadata/dmetadata_video.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dmetadata/dmetadata_exiftool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dmetadata/dmetadata_libraw.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dmetadata/dmetadata_imagemagick.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dmetadata/dmetadata_fileio.cpp
......
......@@ -409,6 +409,13 @@ private:
* ImageMagick helper methods.
*/
bool loadUsingImageMagick(const QString& filePath);
private:
/**
* ExifTool helper methods.
*/
bool loadUsingExifTool(const QString& filePath);
};
} // namespace Digikam
......
/* ============================================================
*
* This file is a part of digiKam project
* https://www.digikam.org
*
* Date : 2006-02-23
* Description : item metadata interface - Exiftool helpers.
*
* Copyright (C) 2006-2022 by Gilles Caulier <caulier dot gilles at gmail dot com>
* Copyright (C) 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
* Copyright (C) 2011 by Leif Huhn <leif at dkstat dot com>
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* ============================================================ */
#include "dmetadata.h"
// Qt includes
#include <QString>
#include <QScopedPointer>
// Local includes
#include "digikam_config.h"
#include "digikam_debug.h"
#include "exiftoolparser.h"
namespace Digikam
{
bool DMetadata::loadUsingExifTool(const QString& filePath)
{
ExifToolParser* const parser = new ExifToolParser(nullptr);
if (parser->exifToolAvailable())
{
bool ret = parser->loadChunk(filePath);
if (!ret)
{
qCCritical(DIGIKAM_METAENGINE_LOG) << "Load metadata using ExifTool failed...";
delete parser;
return false;
}
ExifToolParser::ExifToolData chunk = parser->currentData();
qCDebug(DIGIKAM_METAENGINE_LOG) << "Metadata chunk loaded with ExifTool";
ExifToolParser::ExifToolData::iterator it = chunk.find(QLatin1String("EXV"));
if (it == chunk.end())
{
qCWarning(DIGIKAM_METAENGINE_LOG) << "Metadata chunk loaded with ExifTool is empty";
delete parser;
return false;
}
QVariantList varLst = it.value();
QByteArray exv = varLst[0].toByteArray();
if (!exv.isEmpty())
{
qCDebug(DIGIKAM_METAENGINE_LOG) << "EXV chunk size loaded with ExifTool:" << exv.size();
loadFromData(exv);
}
}
else
{
qCWarning(DIGIKAM_METAENGINE_LOG) << "ExifTool is not available to load metadata...";
delete parser;
return false;
}
delete parser;
return true;
}
} // namespace Digikam
......@@ -59,33 +59,47 @@ bool DMetadata::load(const QString& filePath, Backend* backend)
(QFileInfo(filePath).suffix().toUpper() != QLatin1String("INSV"))
)
{
// Process images only with Exiv2 backend first, or libraw in second for RAW files, or with libheif, or at end with ImageMagick.
// Never process video files with Exiv2, the backend is very unstable.
// Process images only with Exiv2 backend first, or Exiftool in 2nd, or libraw for RAW files,
// or with libheif, or at end with ImageMagick.
// Never process video files with Exiv2, the backend is very unstable and this feature will be removed.
if (!(hasLoaded = MetaEngine::load(filePath)))
{
if (!(hasLoaded = loadUsingRawEngine(filePath)))
if (!(hasLoaded = loadUsingExifTool(filePath)))
{
if (!(hasLoaded = loadUsingRawEngine(filePath)))
{
if (!(hasLoaded =
if (!(hasLoaded =
#ifdef HAVE_HEIF
loadUsingLibheif(filePath)
loadUsingLibheif(filePath)
#else
false
false
#endif
))
{
hasLoaded = loadUsingImageMagick(filePath);
usedBackend = ImageMagickBackend;
))
{
if (!(hasLoaded = loadUsingImageMagick(filePath)))
{
usedBackend = NoBackend;
}
else
{
usedBackend = ImageMagickBackend;
}
}
else
{
usedBackend = LibHeifBackend;
}
}
else
{
usedBackend = LibHeifBackend;
usedBackend = LibRawBackend;
}
}
else
{
usedBackend = LibRawBackend;
usedBackend = ExifToolBackend;
}
}
else
......@@ -97,17 +111,28 @@ bool DMetadata::load(const QString& filePath, Backend* backend)
{
// No image files (aka video or audio), process with ffmpeg backend.
hasLoaded = loadUsingFFmpeg(filePath);
if (hasLoaded)
if (!(hasLoaded = loadUsingExifTool(filePath)))
{
if (!(hasLoaded = loadUsingFFmpeg(filePath)))
{
usedBackend = NoBackend;
}
else
{
usedBackend = FFMpegBackend;
}
}
else
{
usedBackend = FFMpegBackend;
usedBackend = ExifToolBackend;
}
hasLoaded |= loadFromSidecarAndMerge(filePath);
}
qCDebug(DIGIKAM_METAENGINE_LOG) << "Loading metadata with" << backendName(usedBackend) << "backend from" << filePath;
qCDebug(DIGIKAM_METAENGINE_LOG) << "Loading metadata with"
<< backendName(usedBackend)
<< "backend from" << filePath;
if (backend)
{
......
......@@ -122,12 +122,13 @@ public:
*/
enum Backend
{
Exiv2Backend = 0, ///< Default backend used by MetaEngine
LibRawBackend, ///< DMetadata only
LibHeifBackend, ///< DMetadata only
ImageMagickBackend, ///< DMetadata only
FFMpegBackend, ///< DMetadata only
NoBackend ///< No backend used (aka file cannot be read)
Exiv2Backend = 0, ///< Default backend used by MetaEngine.
LibRawBackend, ///< DMetadata only.
LibHeifBackend, ///< DMetadata only.
ImageMagickBackend, ///< DMetadata only.
FFMpegBackend, ///< DMetadata only.
ExifToolBackend, ///< DMetadata only.
NoBackend ///< No backend used (aka file cannot be read).
};
/**
......
......@@ -113,6 +113,11 @@ QString MetaEngine::backendName(Backend t)
return QLatin1String("FFMpeg");
}
case ExifToolBackend:
{
return QLatin1String("ExifTool");
}
case NoBackend:
{
return QLatin1String("No Backend");
......
......@@ -110,8 +110,10 @@ bool ExifToolParser::loadChunk(const QString& path)
QByteArrayList cmdArgs;
cmdArgs << QByteArray("-TagsFromFile");
cmdArgs << d->filePathEncoding(fileInfo);
cmdArgs << QByteArray("-all:all");
cmdArgs << QByteArray("'-xmp:all<all'");
cmdArgs << QByteArray("'-all:all<all:all'");
cmdArgs << QByteArray("'-xmp:all<all:all'");
cmdArgs << QByteArray("'-iptc:all<all:all'");
cmdArgs << QByteArray("'-exif:all<all:all'");
cmdArgs << QByteArray("-o");
cmdArgs << QByteArray("-.exv");
d->currentPath = fileInfo.filePath();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment