Add JSON export
This commit is contained in:
parent
f9e3f8426e
commit
01e57c86d6
|
@ -18,6 +18,7 @@ set(META_NO_TIDY ON)
|
||||||
# add project files
|
# add project files
|
||||||
set(HEADER_FILES
|
set(HEADER_FILES
|
||||||
cli/attachmentinfo.h
|
cli/attachmentinfo.h
|
||||||
|
cli/fieldmapping.h
|
||||||
cli/helper.h
|
cli/helper.h
|
||||||
cli/mainfeatures.h
|
cli/mainfeatures.h
|
||||||
application/knownfieldmodel.h
|
application/knownfieldmodel.h
|
||||||
|
@ -25,6 +26,7 @@ set(HEADER_FILES
|
||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
application/main.cpp
|
application/main.cpp
|
||||||
cli/attachmentinfo.cpp
|
cli/attachmentinfo.cpp
|
||||||
|
cli/fieldmapping.cpp
|
||||||
cli/helper.cpp
|
cli/helper.cpp
|
||||||
cli/mainfeatures.cpp
|
cli/mainfeatures.cpp
|
||||||
application/knownfieldmodel.cpp
|
application/knownfieldmodel.cpp
|
||||||
|
@ -219,6 +221,38 @@ find_package(tagparser 6.4.0 REQUIRED)
|
||||||
use_tag_parser()
|
use_tag_parser()
|
||||||
list(APPEND TEST_LIBRARIES ${TAG_PARSER_SHARED_LIB})
|
list(APPEND TEST_LIBRARIES ${TAG_PARSER_SHARED_LIB})
|
||||||
|
|
||||||
|
# enable experimental JSON export
|
||||||
|
option(ENABLE_JSON_EXPORT "enable JSON export" OFF)
|
||||||
|
if(ENABLE_JSON_EXPORT)
|
||||||
|
# find reflective-rapidjson
|
||||||
|
find_package(reflective_rapidjson REQUIRED)
|
||||||
|
use_reflective_rapidjson()
|
||||||
|
|
||||||
|
# add additional source files
|
||||||
|
list(APPEND HEADER_FILES
|
||||||
|
cli/json.h
|
||||||
|
)
|
||||||
|
list(APPEND SRC_FILES
|
||||||
|
cli/json.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# add generator invocation
|
||||||
|
include(ReflectionGenerator)
|
||||||
|
add_reflection_generator_invocation(
|
||||||
|
INPUT_FILES
|
||||||
|
cli/json.h
|
||||||
|
GENERATORS
|
||||||
|
json
|
||||||
|
OUTPUT_LISTS
|
||||||
|
HEADER_FILES
|
||||||
|
CLANG_OPTIONS_FROM_TARGETS
|
||||||
|
tageditor
|
||||||
|
)
|
||||||
|
|
||||||
|
# add compile definitions
|
||||||
|
list(APPEND META_PRIVATE_COMPILE_DEFINITIONS ${META_PROJECT_VARNAME_UPPER}_JSON_EXPORT)
|
||||||
|
endif()
|
||||||
|
|
||||||
# add Qt modules which can currently not be detected automatically
|
# add Qt modules which can currently not be detected automatically
|
||||||
list(APPEND ADDITIONAL_QT_MODULES Concurrent Network)
|
list(APPEND ADDITIONAL_QT_MODULES Concurrent Network)
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,11 @@ int main(int argc, char *argv[])
|
||||||
extractFieldArg.setSubArguments({&fieldArg, &attachmentArg, &fileArg, &outputFileArg, &verboseArg});
|
extractFieldArg.setSubArguments({&fieldArg, &attachmentArg, &fileArg, &outputFileArg, &verboseArg});
|
||||||
extractFieldArg.setDenotesOperation(true);
|
extractFieldArg.setDenotesOperation(true);
|
||||||
extractFieldArg.setCallback(std::bind(Cli::extractField, std::cref(fieldArg), std::cref(attachmentArg), std::cref(fileArg), std::cref(outputFileArg), std::cref(verboseArg)));
|
extractFieldArg.setCallback(std::bind(Cli::extractField, std::cref(fieldArg), std::cref(attachmentArg), std::cref(fileArg), std::cref(outputFileArg), std::cref(verboseArg)));
|
||||||
|
// export to JSON
|
||||||
|
Argument exportArg("export", 'j', "exports the tag information for the specified files to JSON");
|
||||||
|
exportArg.setSubArguments({&filesArg});
|
||||||
|
exportArg.setDenotesOperation(true);
|
||||||
|
exportArg.setCallback(std::bind(Cli::exportToJson, _1, std::cref(filesArg)));
|
||||||
// file info
|
// file info
|
||||||
Argument validateArg("validate", 'c', "validates the file integrity as accurately as possible; the structure of the file will be parsed completely");
|
Argument validateArg("validate", 'c', "validates the file integrity as accurately as possible; the structure of the file will be parsed completely");
|
||||||
validateArg.setCombinable(true);
|
validateArg.setCombinable(true);
|
||||||
|
@ -233,7 +238,7 @@ int main(int argc, char *argv[])
|
||||||
qtConfigArgs.qtWidgetsGuiArg().setAbbreviation('\0');
|
qtConfigArgs.qtWidgetsGuiArg().setAbbreviation('\0');
|
||||||
qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&defaultFileArg);
|
qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&defaultFileArg);
|
||||||
qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&renamingUtilityArg);
|
qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&renamingUtilityArg);
|
||||||
parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayFileInfoArg, &displayTagInfoArg, &setTagInfoArgs.setTagInfoArg, &extractFieldArg, &genInfoArg, &timeSpanFormatArg, &noColorArg, &helpArg});
|
parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayFileInfoArg, &displayTagInfoArg, &setTagInfoArgs.setTagInfoArg, &extractFieldArg, &exportArg, &genInfoArg, &timeSpanFormatArg, &noColorArg, &helpArg});
|
||||||
// parse given arguments
|
// parse given arguments
|
||||||
parser.parseArgsExt(argc, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::ExitOnFailure);
|
parser.parseArgsExt(argc, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::ExitOnFailure);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include "./fieldmapping.h"
|
||||||
|
|
||||||
|
#include <tagparser/tag.h>
|
||||||
|
|
||||||
|
using namespace Media;
|
||||||
|
|
||||||
|
namespace Cli {
|
||||||
|
namespace FieldMapping {
|
||||||
|
|
||||||
|
static constexpr struct {
|
||||||
|
const char *knownDenotation;
|
||||||
|
KnownField knownField;
|
||||||
|
} fieldMapping[] = {
|
||||||
|
{"title", KnownField::Title},
|
||||||
|
{"album", KnownField::Album},
|
||||||
|
{"artist", KnownField::Artist},
|
||||||
|
{"genre", KnownField::Genre},
|
||||||
|
{"year", KnownField::Year},
|
||||||
|
{"comment", KnownField::Comment},
|
||||||
|
{"bpm", KnownField::Bpm},
|
||||||
|
{"bps", KnownField::Bps},
|
||||||
|
{"lyricist", KnownField::Lyricist},
|
||||||
|
{"track", KnownField::TrackPosition},
|
||||||
|
{"disk", KnownField::DiskPosition},
|
||||||
|
{"part", KnownField::PartNumber},
|
||||||
|
{"totalparts", KnownField::TotalParts},
|
||||||
|
{"encoder", KnownField::Encoder},
|
||||||
|
{"recorddate", KnownField::RecordDate},
|
||||||
|
{"performers", KnownField::Performers},
|
||||||
|
{"duration", KnownField::Length},
|
||||||
|
{"language", KnownField::Language},
|
||||||
|
{"encodersettings", KnownField::EncoderSettings},
|
||||||
|
{"lyrics", KnownField::Lyrics},
|
||||||
|
{"synchronizedlyrics", KnownField::SynchronizedLyrics},
|
||||||
|
{"grouping", KnownField::Grouping},
|
||||||
|
{"recordlabel", KnownField::RecordLabel},
|
||||||
|
{"cover", KnownField::Cover},
|
||||||
|
{"composer", KnownField::Composer},
|
||||||
|
{"rating", KnownField::Rating},
|
||||||
|
{"description", KnownField::Description},
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *fieldDenotation(Media::KnownField knownField)
|
||||||
|
{
|
||||||
|
for(const auto &mapping : fieldMapping) {
|
||||||
|
if(mapping.knownField == knownField) {
|
||||||
|
return mapping.knownDenotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Media::KnownField knownField(const char *fieldDenotation, std::size_t fieldDenotationSize)
|
||||||
|
{
|
||||||
|
for(const auto &mapping : fieldMapping) {
|
||||||
|
if(!strncmp(fieldDenotation, mapping.knownDenotation, fieldDenotationSize)) {
|
||||||
|
return mapping.knownField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return KnownField::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef CLI_FIELDMAPPING
|
||||||
|
#define CLI_FIELDMAPPING
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace Media {
|
||||||
|
enum class KnownField : unsigned int;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Cli {
|
||||||
|
namespace FieldMapping {
|
||||||
|
|
||||||
|
const char *fieldDenotation(Media::KnownField knownField);
|
||||||
|
Media::KnownField knownField(const char *fieldDenotation, std::size_t fieldDenotationSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CLI_FIELDMAPPING
|
|
@ -1,4 +1,5 @@
|
||||||
#include "./helper.h"
|
#include "./helper.h"
|
||||||
|
#include "./fieldmapping.h"
|
||||||
|
|
||||||
#include <tagparser/mediafileinfo.h>
|
#include <tagparser/mediafileinfo.h>
|
||||||
#include <tagparser/matroska/matroskatag.h>
|
#include <tagparser/matroska/matroskatag.h>
|
||||||
|
@ -598,44 +599,11 @@ FieldId FieldId::fromTagDenotation(const char *denotation, size_t denotationSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine KnownField for generic denotation
|
// determine KnownField for generic denotation
|
||||||
static const struct {
|
const auto knownField(FieldMapping::knownField(denotation, denotationSize));
|
||||||
const char *knownDenotation;
|
if (knownField == KnownField::Invalid) {
|
||||||
KnownField knownField;
|
throw ConversionException("generic field name is unknown");
|
||||||
} fieldMapping[] = {
|
|
||||||
{"title", KnownField::Title},
|
|
||||||
{"album", KnownField::Album},
|
|
||||||
{"artist", KnownField::Artist},
|
|
||||||
{"genre", KnownField::Genre},
|
|
||||||
{"year", KnownField::Year},
|
|
||||||
{"comment", KnownField::Comment},
|
|
||||||
{"bpm", KnownField::Bpm},
|
|
||||||
{"bps", KnownField::Bps},
|
|
||||||
{"lyricist", KnownField::Lyricist},
|
|
||||||
{"track", KnownField::TrackPosition},
|
|
||||||
{"disk", KnownField::DiskPosition},
|
|
||||||
{"part", KnownField::PartNumber},
|
|
||||||
{"totalparts", KnownField::TotalParts},
|
|
||||||
{"encoder", KnownField::Encoder},
|
|
||||||
{"recorddate", KnownField::RecordDate},
|
|
||||||
{"performers", KnownField::Performers},
|
|
||||||
{"duration", KnownField::Length},
|
|
||||||
{"language", KnownField::Language},
|
|
||||||
{"encodersettings", KnownField::EncoderSettings},
|
|
||||||
{"lyrics", KnownField::Lyrics},
|
|
||||||
{"synchronizedlyrics", KnownField::SynchronizedLyrics},
|
|
||||||
{"grouping", KnownField::Grouping},
|
|
||||||
{"recordlabel", KnownField::RecordLabel},
|
|
||||||
{"cover", KnownField::Cover},
|
|
||||||
{"composer", KnownField::Composer},
|
|
||||||
{"rating", KnownField::Rating},
|
|
||||||
{"description", KnownField::Description},
|
|
||||||
};
|
|
||||||
for(const auto &mapping : fieldMapping) {
|
|
||||||
if(!strncmp(denotation, mapping.knownDenotation, denotationSize)) {
|
|
||||||
return FieldId(mapping.knownField, nullptr, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw ConversionException("generic field name is unknown");
|
return FieldId(knownField, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldId FieldId::fromTrackDenotation(const char *denotation, size_t denotationSize)
|
FieldId FieldId::fromTrackDenotation(const char *denotation, size_t denotationSize)
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
#include "./json.h"
|
||||||
|
#include "./fieldmapping.h"
|
||||||
|
|
||||||
|
#include <reflective-rapidjson/lib/json/reflector-chronoutilities.h>
|
||||||
|
|
||||||
|
#include <tagparser/mediafileinfo.h>
|
||||||
|
#include <tagparser/tag.h>
|
||||||
|
|
||||||
|
#include <c++utilities/conversion/stringconversion.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace Media;
|
||||||
|
|
||||||
|
namespace ReflectiveRapidJSON {
|
||||||
|
namespace JsonReflector {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Allows moving a RAPIDJSON_NAMESPACE::Value wrapped as Cli::Json::TagValue::ValueAllowedToMove for serialization.
|
||||||
|
* \remarks The const-cast and move operation are ok in this particular case, since the object to be serialized
|
||||||
|
* is not const and not used anymore after serialization.
|
||||||
|
* \todo Add an overload for &&reflectable to Reflective RapidJSON.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
inline void push<Cli::Json::TagValue::ValueAllowedToMove>(
|
||||||
|
const Cli::Json::TagValue::ValueAllowedToMove &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &)
|
||||||
|
{
|
||||||
|
value = const_cast<Cli::Json::TagValue::ValueAllowedToMove &>(reflectable).Move();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace JsonReflector
|
||||||
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
||||||
|
namespace Cli {
|
||||||
|
namespace Json {
|
||||||
|
|
||||||
|
TagValue::TagValue(const Media::TagValue &tagValue, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
|
||||||
|
: mimeType(tagValue.mimeType())
|
||||||
|
{
|
||||||
|
if (tagValue.isEmpty()) {
|
||||||
|
value.SetNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch(tagValue.type()) {
|
||||||
|
case TagDataType::Text:
|
||||||
|
case TagDataType::StandardGenreIndex:
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(tagValue.toString(TagTextEncoding::Utf8), value, allocator);
|
||||||
|
kind = "text";
|
||||||
|
break;
|
||||||
|
case TagDataType::Integer:
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(tagValue.toInteger(), value, allocator);
|
||||||
|
kind = "integer";
|
||||||
|
break;
|
||||||
|
case TagDataType::PositionInSet: {
|
||||||
|
const auto position(tagValue.toPositionInSet());
|
||||||
|
value.SetObject();
|
||||||
|
if (position.position()) {
|
||||||
|
value.AddMember("position", RAPIDJSON_NAMESPACE::Value(position.position()), allocator);
|
||||||
|
}
|
||||||
|
if (position.total()) {
|
||||||
|
value.AddMember("total", RAPIDJSON_NAMESPACE::Value(position.total()), allocator);
|
||||||
|
}
|
||||||
|
kind = "position";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TagDataType::TimeSpan:
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(tagValue.toTimeSpan(), value, allocator);
|
||||||
|
break;
|
||||||
|
case TagDataType::DateTime:
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(tagValue.toDateTime(), value, allocator);
|
||||||
|
break;
|
||||||
|
case TagDataType::Picture:
|
||||||
|
if (tagValue.dataSize() > (1024 * 1024)) {
|
||||||
|
throw ConversionUtilities::ConversionException("size is too big");
|
||||||
|
}
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(ConversionUtilities::encodeBase64(reinterpret_cast<const byte *>(tagValue.dataPointer()), static_cast<uint32>(tagValue.dataSize())), value, allocator);
|
||||||
|
kind = "picture";
|
||||||
|
break;
|
||||||
|
case TagDataType::Binary:
|
||||||
|
if (tagValue.dataSize() > (1024 * 1024)) {
|
||||||
|
throw ConversionUtilities::ConversionException("size is too big");
|
||||||
|
}
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(ConversionUtilities::encodeBase64(reinterpret_cast<const byte *>(tagValue.dataPointer()), static_cast<uint32>(tagValue.dataSize())), value, allocator);
|
||||||
|
kind = "binary";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value.SetNull();
|
||||||
|
}
|
||||||
|
} catch (const ConversionUtilities::ConversionException &e) {
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(e.what(), value, allocator);
|
||||||
|
kind = "error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TagInfo::TagInfo(const Tag &tag, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
|
||||||
|
: format(tag.typeName())
|
||||||
|
, target(tag.targetString())
|
||||||
|
{
|
||||||
|
for (auto field = firstKnownField; field != KnownField::Invalid; field = nextKnownField(field)) {
|
||||||
|
const auto &tagValues(tag.values(field));
|
||||||
|
if (tagValues.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::vector<TagValue> valueObjects;
|
||||||
|
valueObjects.reserve(tagValues.size());
|
||||||
|
for (const auto *tagValue : tagValues) {
|
||||||
|
valueObjects.emplace_back(*tagValue, allocator);
|
||||||
|
}
|
||||||
|
fields.insert(make_pair(FieldMapping::fieldDenotation(field), move(valueObjects)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInfo::FileInfo(const Media::MediaFileInfo &mediaFileInfo, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
|
||||||
|
: fileName(mediaFileInfo.fileName())
|
||||||
|
, size(mediaFileInfo.size())
|
||||||
|
, mimeType(mediaFileInfo.mimeType())
|
||||||
|
, formatSummary(mediaFileInfo.technicalSummary())
|
||||||
|
, duration(mediaFileInfo.duration())
|
||||||
|
{
|
||||||
|
for (const Tag *tag : mediaFileInfo.tags()) {
|
||||||
|
tags.emplace_back(*tag, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "reflection/json.h"
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef CLI_JSON
|
||||||
|
#define CLI_JSON
|
||||||
|
|
||||||
|
#include <reflective-rapidjson/lib/json/serializable.h>
|
||||||
|
|
||||||
|
#include <c++utilities/chrono/timespan.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Media {
|
||||||
|
class MediaFileInfo;
|
||||||
|
class Tag;
|
||||||
|
class TagValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Cli {
|
||||||
|
namespace Json {
|
||||||
|
|
||||||
|
struct TagValue : ReflectiveRapidJSON::JsonSerializable<TagValue> {
|
||||||
|
TagValue(const Media::TagValue &tagValue, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator);
|
||||||
|
|
||||||
|
const char *kind = "undefined";
|
||||||
|
const std::string mimeType;
|
||||||
|
struct ValueAllowedToMove : RAPIDJSON_NAMESPACE::Value {
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TagInfo : ReflectiveRapidJSON::JsonSerializable<TagInfo> {
|
||||||
|
TagInfo(const Media::Tag &tag, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator);
|
||||||
|
|
||||||
|
const char *format = nullptr;
|
||||||
|
std::string target;
|
||||||
|
std::unordered_map<std::string, std::vector<TagValue>> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileInfo : ReflectiveRapidJSON::JsonSerializable<FileInfo> {
|
||||||
|
FileInfo(const Media::MediaFileInfo &mediaFileInfo, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator);
|
||||||
|
|
||||||
|
std::string fileName;
|
||||||
|
std::size_t size;
|
||||||
|
const char *mimeType;
|
||||||
|
std::vector<TagInfo> tags;
|
||||||
|
std::string formatSummary;
|
||||||
|
ChronoUtilities::TimeSpan duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CLI_JSON
|
|
@ -1,6 +1,9 @@
|
||||||
#include "./mainfeatures.h"
|
#include "./mainfeatures.h"
|
||||||
#include "./helper.h"
|
#include "./helper.h"
|
||||||
#include "./attachmentinfo.h"
|
#include "./attachmentinfo.h"
|
||||||
|
#ifdef TAGEDITOR_JSON_EXPORT
|
||||||
|
#include "./json.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "../application/knownfieldmodel.h"
|
#include "../application/knownfieldmodel.h"
|
||||||
#if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
|
#if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
|
||||||
|
@ -15,6 +18,8 @@
|
||||||
#include <tagparser/abstractattachment.h>
|
#include <tagparser/abstractattachment.h>
|
||||||
#include <tagparser/abstractchapter.h>
|
#include <tagparser/abstractchapter.h>
|
||||||
|
|
||||||
|
#include <reflective-rapidjson/lib/json/reflector.h>
|
||||||
|
|
||||||
#include <c++utilities/application/failure.h>
|
#include <c++utilities/application/failure.h>
|
||||||
#include <c++utilities/application/commandlineutils.h>
|
#include <c++utilities/application/commandlineutils.h>
|
||||||
#include <c++utilities/conversion/stringconversion.h>
|
#include <c++utilities/conversion/stringconversion.h>
|
||||||
|
@ -29,6 +34,11 @@
|
||||||
# include <qtutilities/misc/conversion.h>
|
# include <qtutilities/misc/conversion.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TAGEDITOR_JSON_EXPORT
|
||||||
|
#include <rapidjson/ostreamwrapper.h>
|
||||||
|
#include <rapidjson/writer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -843,6 +853,52 @@ void extractField(const Argument &fieldArg, const Argument &attachmentArg, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void exportToJson(const ArgumentOccurrence &, const Argument &filesArg)
|
||||||
|
{
|
||||||
|
CMD_UTILS_START_CONSOLE;
|
||||||
|
|
||||||
|
#ifdef TAGEDITOR_JSON_EXPORT
|
||||||
|
// check whether files have been specified
|
||||||
|
if(!filesArg.isPresent() || filesArg.values().empty()) {
|
||||||
|
cerr << Phrases::Error << "No files have been specified." << Phrases::End;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kArrayType);
|
||||||
|
std::vector<Json::FileInfo> jsonData;
|
||||||
|
MediaFileInfo fileInfo;
|
||||||
|
|
||||||
|
// gather tags for each file
|
||||||
|
for(const char *file : filesArg.values()) {
|
||||||
|
try {
|
||||||
|
// parse tags
|
||||||
|
fileInfo.setPath(file);
|
||||||
|
fileInfo.open(true);
|
||||||
|
fileInfo.parseContainerFormat();
|
||||||
|
fileInfo.parseTags();
|
||||||
|
fileInfo.parseTracks();
|
||||||
|
jsonData.emplace_back(fileInfo, document.GetAllocator());
|
||||||
|
} catch(const Media::Failure &) {
|
||||||
|
cerr << Phrases::Error << "A parsing failure occured when reading the file \"" << file << "\"." << Phrases::EndFlush;
|
||||||
|
} catch(...) {
|
||||||
|
::IoUtilities::catchIoFailure();
|
||||||
|
cerr << Phrases::Error << "An IO failure occured when reading the file \"" << file << "\"." << Phrases::EndFlush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print the gathered data as JSON document
|
||||||
|
ReflectiveRapidJSON::JsonReflector::push(jsonData, document, document.GetAllocator());
|
||||||
|
RAPIDJSON_NAMESPACE::OStreamWrapper osw(cout);
|
||||||
|
RAPIDJSON_NAMESPACE::Writer<RAPIDJSON_NAMESPACE::OStreamWrapper> writer(osw);
|
||||||
|
document.Accept(writer);
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
#else
|
||||||
|
VAR_UNUSED(filesArg);
|
||||||
|
cerr << Phrases::Error << "JSON export has not been enabled when building the tag editor." << Phrases::EndFlush;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void applyGeneralConfig(const Argument &timeSapnFormatArg)
|
void applyGeneralConfig(const Argument &timeSapnFormatArg)
|
||||||
{
|
{
|
||||||
timeSpanOutputFormat = parseTimeSpanOutputFormat(timeSapnFormatArg, TimeSpanOutputFormat::WithMeasures);
|
timeSpanOutputFormat = parseTimeSpanOutputFormat(timeSapnFormatArg, TimeSpanOutputFormat::WithMeasures);
|
||||||
|
|
|
@ -53,6 +53,7 @@ void generateFileInfo(const ApplicationUtilities::ArgumentOccurrence &, const Ap
|
||||||
void displayTagInfo(const ApplicationUtilities::Argument &fieldsArg, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg);
|
void displayTagInfo(const ApplicationUtilities::Argument &fieldsArg, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg);
|
||||||
void setTagInfo(const Cli::SetTagInfoArgs &args);
|
void setTagInfo(const Cli::SetTagInfoArgs &args);
|
||||||
void extractField(const ApplicationUtilities::Argument &fieldArg, const ApplicationUtilities::Argument &attachmentArg, const ApplicationUtilities::Argument &inputFilesArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &verboseArg);
|
void extractField(const ApplicationUtilities::Argument &fieldArg, const ApplicationUtilities::Argument &attachmentArg, const ApplicationUtilities::Argument &inputFilesArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &verboseArg);
|
||||||
|
void exportToJson(const ApplicationUtilities::ArgumentOccurrence &, const ApplicationUtilities::Argument &filesArg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue