12#include <spdlog/spdlog.h>
13#include <exiv2/exiv2.hpp>
16#include <magic_enum/magic_enum.hpp>
17#include <unordered_map>
22 std::unique_ptr<IXmpProvider> xmp_provider,
23 std::unique_ptr<IXmpPathStrategy> xmp_path_strategy)
24 : m_xmp_provider(std::move(xmp_provider))
25 , m_xmp_path_strategy(std::move(xmp_path_strategy))
27 if (!m_xmp_provider || !m_xmp_path_strategy) {
28 spdlog::error(
"FileSerializerReader: Constructor received a null IXmpProvider or IXmpPathStrategy.");
29 throw std::invalid_argument(
"FileSerializerReader: IXmpProvider and IXmpPathStrategy cannot be null.");
31 spdlog::debug(
"FileSerializerReader: Constructed with IXmpProvider and IXmpPathStrategy.");
36 if (source_image_path.empty()) {
37 spdlog::error(
"FileSerializerReader::loadFromFile: Source image path is empty.");
40 spdlog::debug(
"FileSerializerReader::loadFromFile: Attempting to load operations for image: {}", source_image_path);
46 std::string xmp_file_path { m_xmp_path_strategy->getXmpPathForImage(source_image_path) };
47 spdlog::debug(
"FileSerializerReader::loadFromFile: Determined XMP file path: {}", xmp_file_path);
50 std::string xmp_packet { m_xmp_provider->readXmp(xmp_file_path) };
51 if (xmp_packet.empty()) {
52 spdlog::warn(
"FileSerializerReader::loadFromFile: IXmpProvider returned an empty XMP packet for file: {} (associated with image: {}). Assuming no operations to load.", xmp_file_path, source_image_path);
57 std::string source_path_from_xmp;
58 std::vector<Operations::OperationDescriptor> operations { parseXmpPacket(xmp_packet, source_path_from_xmp) };
60 if (operations.empty()) {
61 spdlog::warn(
"FileSerializerReader::loadFromFile: Parsing XMP packet from file: {} (associated with image: {}) resulted in an empty list of operations.", xmp_file_path, source_image_path);
63 spdlog::info(
"FileSerializerReader::loadFromFile: Successfully loaded {} operations from XMP file: {} for image: {}", operations.size(), xmp_file_path, source_image_path);
69std::vector<Operations::OperationDescriptor> FileSerializerReader::parseXmpPacket(
const std::string& xmp_packet, std::string& source_image_path_from_xmp)
const
71 spdlog::debug(
"FileSerializerReader::parseXmpPacket: Parsing XMP packet (size {}).", xmp_packet.size());
73 Exiv2::XmpData xmp_data;
77 if (Exiv2::XmpParser::decode(xmp_data, xmp_packet.c_str()) != 0) {
78 spdlog::error(
"FileSerializerReader::parseXmpPacket: Failed to decode XMP packet.");
83 std::string serialized_by { xmp_data[
"Xmp.cm.serializedBy"].toString() };
84 if (serialized_by !=
"CaptureMoment") {
85 spdlog::warn(
"FileSerializerReader::parseXmpPacket: XMP packet is not marked as serialized by CaptureMoment (found '{}'). Skipping.", serialized_by);
90 source_image_path_from_xmp = xmp_data[
"Xmp.cm.sourceImagePath"].toString();
91 spdlog::debug(
"FileSerializerReader::parseXmpPacket: Found source image path in XMP: '{}'", source_image_path_from_xmp);
93 std::vector<Operations::OperationDescriptor> operations;
98 std::string index_str { std::to_string(index) };
99 std::string type_key_str {
"Xmp.cm.operation[" + index_str +
"].type"};
100 std::string name_key_str {
"Xmp.cm.operation[" + index_str +
"].name"};
101 std::string enabled_key_str {
"Xmp.cm.operation[" + index_str +
"].enabled"};
104 Exiv2::XmpKey type_key(type_key_str);
105 Exiv2::XmpKey name_key(name_key_str);
106 Exiv2::XmpKey enabled_key(enabled_key_str);
109 auto type_kv { xmp_data.findKey(type_key) };
110 auto name_kv { xmp_data.findKey(name_key) };
111 auto enabled_kv { xmp_data.findKey(enabled_key) };
113 if (type_kv == xmp_data.end()) {
118 Operations::OperationDescriptor op_desc;
121 std::string type_str { type_kv->toString() };
122 auto type_opt = magic_enum::enum_cast<Operations::OperationType>(type_str);
123 if (!type_opt.has_value()) {
124 spdlog::warn(
"FileSerializerReader::parseXmpPacket: Unknown OperationType '{}' found for operation index {}. Skipping operation.", type_str, index);
128 op_desc.type = type_opt.value();
131 if (name_kv != xmp_data.end()) {
132 op_desc.name = name_kv->toString();
135 op_desc.name = type_str +
" (from XMP)";
139 if (enabled_kv != xmp_data.end())
141 std::string enabled_str { enabled_kv->toString() };
143 if (enabled_str ==
"true" || enabled_str ==
"True" || enabled_str ==
"TRUE" || enabled_str ==
"1") {
144 op_desc.enabled =
true;
145 }
else if (enabled_str ==
"false" || enabled_str ==
"False" || enabled_str ==
"FALSE" || enabled_str ==
"0") {
146 op_desc.enabled =
false;
148 spdlog::warn(
"FileSerializerReader::parseXmpPacket: Invalid boolean value '{}' for enabled flag of operation index {}. Using default (true).", enabled_str, index);
149 op_desc.enabled =
true;
152 op_desc.enabled =
true;
157 std::string param_prefix {
"Xmp.cm.operation[" + index_str +
"].param." };
158 for (
const auto& kv : xmp_data) {
159 std::string key = kv.key();
160 if (key.starts_with(param_prefix))
162 std::string param_name { key.substr(param_prefix.length()) };
163 std::string param_value_str { kv.toString() };
169 op_desc.params[param_name] = parsed_value;
171 spdlog::debug(
"FileSerializerReader::parseXmpPacket: Parsed parameter '{}' for operation '{}'.", param_name, op_desc.name);
175 operations.push_back(op_desc);
176 spdlog::debug(
"FileSerializerReader::parseXmpPacket: Parsed operation index {}: type={}, name={}, enabled={}", index, magic_enum::enum_name(op_desc.type), op_desc.name, op_desc.enabled);
180 spdlog::debug(
"FileSerializerReader::parseXmpPacket: Successfully parsed {} operations from XMP packet.", operations.size());
183 }
catch (
const Exiv2::Error& e) {
184 spdlog::error(
"FileSerializerReader::parseXmpPacket: Exiv2 error during parsing: {}", e.what());
186 }
catch (
const std::exception& e) {
187 spdlog::error(
"FileSerializerReader::parseXmpPacket: General error during parsing: {}", e.what());
static void initialize()
Initializes the Exiv2 XMP parser if not already done. This function is thread-safe.
std::vector< Operations::OperationDescriptor > loadFromFile(std::string_view source_image_path) const override
Loads a list of operations from an XMP file determined by the path strategy.
FileSerializerReader(std::unique_ptr< IXmpProvider > xmp_provider, std::unique_ptr< IXmpPathStrategy > xmp_path_strategy)
Constructs a FileSerializerReader.
Declaration of Exiv2Initializer class.
Declaration of FileSerializerReader class.
Operations::OperationValue deserializeParameter(std::string_view value_str)
Deserializes a string representation back into an OperationValue. This function attempts to infer the...
Declaration of OperationSerialization interface.