2017-01-15 21:43:46 +01:00
|
|
|
#ifndef CLI_HELPER
|
|
|
|
#define CLI_HELPER
|
|
|
|
|
2017-01-23 00:27:21 +01:00
|
|
|
#include "../application/knownfieldmodel.h"
|
|
|
|
|
2021-04-28 00:49:56 +02:00
|
|
|
#include <tagparser/id3/id3v2tag.h>
|
2017-01-15 21:43:46 +01:00
|
|
|
#include <tagparser/tag.h>
|
2021-04-28 00:49:56 +02:00
|
|
|
#include <tagparser/vorbis/vorbiscomment.h>
|
2017-01-15 21:43:46 +01:00
|
|
|
|
|
|
|
#include <c++utilities/application/commandlineutils.h>
|
|
|
|
#include <c++utilities/chrono/datetime.h>
|
|
|
|
#include <c++utilities/chrono/timespan.h>
|
|
|
|
#include <c++utilities/conversion/stringconversion.h>
|
2019-08-12 20:26:38 +02:00
|
|
|
#include <c++utilities/misc/flagenumclass.h>
|
2017-09-22 00:19:24 +02:00
|
|
|
#include <c++utilities/misc/traits.h>
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2017-01-23 00:27:21 +01:00
|
|
|
#include <functional>
|
2021-04-28 00:49:56 +02:00
|
|
|
#include <string_view>
|
2017-09-22 00:19:24 +02:00
|
|
|
#include <type_traits>
|
2018-03-07 01:18:01 +01:00
|
|
|
#include <unordered_map>
|
|
|
|
#include <vector>
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2019-06-10 22:49:46 +02:00
|
|
|
namespace CppUtilities {
|
2017-01-15 21:43:46 +01:00
|
|
|
class Argument;
|
|
|
|
}
|
|
|
|
|
2018-03-06 23:10:13 +01:00
|
|
|
namespace TagParser {
|
2017-01-15 21:43:46 +01:00
|
|
|
class MediaFileInfo;
|
2018-03-06 02:04:35 +01:00
|
|
|
class Diagnostics;
|
|
|
|
class AbortableProgressFeedback;
|
2017-01-15 21:43:46 +01:00
|
|
|
enum class TagUsage;
|
|
|
|
enum class ElementPosition;
|
2018-03-07 01:18:01 +01:00
|
|
|
} // namespace TagParser
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2018-03-06 23:10:13 +01:00
|
|
|
using namespace TagParser;
|
2017-01-15 21:43:46 +01:00
|
|
|
|
|
|
|
namespace Cli {
|
|
|
|
|
|
|
|
// define enums, operators and structs to handle specified field denotations
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
enum class DenotationType { Normal, Increment, File };
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2019-08-12 20:26:38 +02:00
|
|
|
} // namespace Cli
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2019-08-12 20:26:38 +02:00
|
|
|
CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(Cli, Cli::DenotationType)
|
|
|
|
CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(TagParser, TagParser::TagType)
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2019-08-12 20:26:38 +02:00
|
|
|
namespace Cli {
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2021-04-28 00:49:56 +02:00
|
|
|
using CoverType = std::conditional_t<sizeof(typename Id3v2Tag::FieldType::TypeInfoType) >= sizeof(typename VorbisComment::FieldType::TypeInfoType),
|
|
|
|
typename Id3v2Tag::FieldType::TypeInfoType, typename VorbisComment::FieldType::TypeInfoType>;
|
|
|
|
constexpr auto invalidCoverType = std::numeric_limits<CoverType>::max();
|
|
|
|
const std::vector<std::string_view> &id3v2CoverTypeNames();
|
|
|
|
CoverType id3v2CoverType(std::string_view coverName);
|
|
|
|
std::string_view id3v2CoverName(CoverType coverType);
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
class FieldId {
|
2017-06-14 00:09:10 +02:00
|
|
|
friend struct std::hash<FieldId>;
|
|
|
|
|
2017-01-23 00:27:21 +01:00
|
|
|
public:
|
2021-04-28 00:49:56 +02:00
|
|
|
explicit FieldId(KnownField m_knownField = KnownField::Invalid, const char *denotation = nullptr, std::size_t denotationSize = 0);
|
2017-06-14 00:09:10 +02:00
|
|
|
static FieldId fromTagDenotation(const char *denotation, std::size_t denotationSize);
|
|
|
|
static FieldId fromTrackDenotation(const char *denotation, std::size_t denotationSize);
|
2018-03-07 01:18:01 +01:00
|
|
|
bool operator==(const FieldId &other) const;
|
2017-01-23 00:27:21 +01:00
|
|
|
KnownField knownField() const;
|
|
|
|
const char *name() const;
|
2017-06-14 00:09:10 +02:00
|
|
|
bool denotes(const char *knownDenotation) const;
|
|
|
|
const std::string &denotation() const;
|
2019-01-02 17:12:48 +01:00
|
|
|
std::pair<std::vector<const TagValue *>, bool> values(const Tag *tag, TagType tagType) const;
|
2017-01-23 00:27:21 +01:00
|
|
|
bool setValues(Tag *tag, TagType tagType, const std::vector<TagValue> &values) const;
|
2021-04-28 00:49:56 +02:00
|
|
|
KnownField knownFieldForTag(const Tag *tag, TagType tagType) const;
|
2017-01-23 00:27:21 +01:00
|
|
|
|
|
|
|
private:
|
2019-01-02 17:12:48 +01:00
|
|
|
using GetValuesForNativeFieldType = std::function<std::pair<std::vector<const TagValue *>, bool>(const Tag *, TagType)>;
|
2018-07-11 13:18:36 +02:00
|
|
|
using SetValuesForNativeFieldType = std::function<bool(Tag *, TagType, const std::vector<TagValue> &)>;
|
2021-04-28 00:49:56 +02:00
|
|
|
using KnownFieldForNativeFieldType = std::function<KnownField(const Tag *, TagType)>;
|
|
|
|
FieldId(std::string_view nativeField, GetValuesForNativeFieldType &&valuesForNativeField, SetValuesForNativeFieldType &&setValuesForNativeField,
|
|
|
|
KnownFieldForNativeFieldType &&knownFieldForNativeField);
|
2021-01-30 21:57:09 +01:00
|
|
|
template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType> static FieldId fromNativeField(std::string_view nativeFieldId);
|
2017-01-23 00:27:21 +01:00
|
|
|
|
|
|
|
KnownField m_knownField;
|
2017-06-14 00:09:10 +02:00
|
|
|
std::string m_denotation;
|
|
|
|
std::string m_nativeField;
|
2017-01-23 00:27:21 +01:00
|
|
|
GetValuesForNativeFieldType m_valuesForNativeField;
|
|
|
|
SetValuesForNativeFieldType m_setValuesForNativeField;
|
2021-04-28 00:49:56 +02:00
|
|
|
KnownFieldForNativeFieldType m_knownFieldForNativeField;
|
2017-01-16 22:58:15 +01:00
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
inline FieldId::FieldId(KnownField knownField, const char *denotation, std::size_t denotationSize)
|
|
|
|
: m_knownField(knownField)
|
|
|
|
, m_denotation(denotation, denotationSize)
|
|
|
|
{
|
|
|
|
}
|
2017-01-16 22:58:15 +01:00
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
inline bool FieldId::operator==(const FieldId &other) const
|
2017-01-23 00:27:21 +01:00
|
|
|
{
|
2017-06-14 00:09:10 +02:00
|
|
|
return (m_knownField == other.m_knownField || m_denotation == other.m_denotation) && m_nativeField == other.m_nativeField;
|
2017-01-23 00:27:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
inline KnownField FieldId::knownField() const
|
|
|
|
{
|
|
|
|
return m_knownField;
|
|
|
|
}
|
|
|
|
|
2017-06-14 00:09:10 +02:00
|
|
|
inline const char *FieldId::name() const
|
2017-01-23 00:27:21 +01:00
|
|
|
{
|
2017-06-14 00:09:10 +02:00
|
|
|
return !m_nativeField.empty() ? m_nativeField.data() : Settings::KnownFieldModel::fieldName(m_knownField);
|
2017-01-23 00:27:21 +01:00
|
|
|
}
|
|
|
|
|
2017-06-14 00:09:10 +02:00
|
|
|
inline bool FieldId::denotes(const char *knownDenotation) const
|
|
|
|
{
|
|
|
|
return !std::strncmp(m_denotation.data(), knownDenotation, m_denotation.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
inline const std::string &FieldId::denotation() const
|
2017-01-23 00:27:21 +01:00
|
|
|
{
|
2017-06-14 00:09:10 +02:00
|
|
|
return m_denotation;
|
2017-01-23 00:27:21 +01:00
|
|
|
}
|
2017-01-16 22:58:15 +01:00
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
struct FieldScope {
|
2021-04-28 00:49:56 +02:00
|
|
|
explicit FieldScope(KnownField field = KnownField::Invalid, TagType tagType = TagType::Unspecified, TagTarget tagTarget = TagTarget());
|
2018-03-07 01:18:01 +01:00
|
|
|
bool operator==(const FieldScope &other) const;
|
2017-06-14 00:09:10 +02:00
|
|
|
bool isTrack() const;
|
|
|
|
|
2017-01-23 00:27:21 +01:00
|
|
|
FieldId field;
|
2017-01-15 21:43:46 +01:00
|
|
|
TagType tagType;
|
|
|
|
TagTarget tagTarget;
|
2017-06-14 00:09:10 +02:00
|
|
|
bool allTracks;
|
2019-03-13 19:07:51 +01:00
|
|
|
std::vector<std::uint64_t> trackIds;
|
2017-01-15 21:43:46 +01:00
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
inline FieldScope::FieldScope(KnownField field, TagType tagType, TagTarget tagTarget)
|
|
|
|
: field(field)
|
|
|
|
, tagType(tagType)
|
|
|
|
, tagTarget(tagTarget)
|
|
|
|
, allTracks(false)
|
|
|
|
{
|
|
|
|
}
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
inline bool FieldScope::operator==(const FieldScope &other) const
|
2017-01-15 21:43:46 +01:00
|
|
|
{
|
|
|
|
return field == other.field && tagType == other.tagType && tagTarget == other.tagTarget;
|
|
|
|
}
|
|
|
|
|
2017-06-14 00:09:10 +02:00
|
|
|
inline bool FieldScope::isTrack() const
|
|
|
|
{
|
|
|
|
return allTracks || !trackIds.empty();
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
struct FieldValue {
|
2017-01-15 21:43:46 +01:00
|
|
|
FieldValue(DenotationType type, unsigned int fileIndex, const char *value);
|
|
|
|
DenotationType type;
|
|
|
|
unsigned int fileIndex;
|
|
|
|
std::string value;
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
inline FieldValue::FieldValue(DenotationType type, unsigned int fileIndex, const char *value)
|
|
|
|
: type(type)
|
|
|
|
, fileIndex(fileIndex)
|
|
|
|
, value(value)
|
2017-10-09 19:06:26 +02:00
|
|
|
{
|
2018-03-07 01:18:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class InterruptHandler {
|
2017-10-09 19:06:26 +02:00
|
|
|
public:
|
2021-04-28 00:49:56 +02:00
|
|
|
explicit InterruptHandler(std::function<void()> handler);
|
2017-10-09 19:06:26 +02:00
|
|
|
~InterruptHandler();
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void handler(int signum);
|
|
|
|
|
|
|
|
static std::function<void()> s_handler;
|
|
|
|
static bool s_handlerRegistered;
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
} // namespace Cli
|
2017-01-15 21:43:46 +01:00
|
|
|
|
|
|
|
// define hash functions for custom data types
|
|
|
|
|
|
|
|
namespace std {
|
|
|
|
|
|
|
|
using namespace Cli;
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
template <> struct hash<KnownField> {
|
2017-01-15 21:43:46 +01:00
|
|
|
std::size_t operator()(const KnownField &ids) const
|
|
|
|
{
|
|
|
|
using type = typename std::underlying_type<KnownField>::type;
|
|
|
|
return std::hash<type>()(static_cast<type>(ids));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
template <> struct hash<TagType> {
|
2017-01-15 21:43:46 +01:00
|
|
|
std::size_t operator()(const TagType &ids) const
|
|
|
|
{
|
|
|
|
using type = typename std::underlying_type<TagType>::type;
|
|
|
|
return std::hash<type>()(static_cast<type>(ids));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
template <> struct hash<TagTarget::IdContainerType> {
|
2017-01-15 21:43:46 +01:00
|
|
|
std::size_t operator()(const TagTarget::IdContainerType &ids) const
|
|
|
|
{
|
|
|
|
using std::hash;
|
|
|
|
auto seed = ids.size();
|
2018-03-07 01:18:01 +01:00
|
|
|
for (auto id : ids) {
|
2017-01-15 21:43:46 +01:00
|
|
|
seed ^= id + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
|
|
}
|
|
|
|
return seed;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
template <> struct hash<TagTarget> {
|
2017-01-23 00:27:21 +01:00
|
|
|
std::size_t operator()(const TagTarget &target) const
|
2017-01-15 21:43:46 +01:00
|
|
|
{
|
|
|
|
using std::hash;
|
2019-03-13 19:07:51 +01:00
|
|
|
return ((hash<std::uint64_t>()(target.level()) ^ (hash<TagTarget::IdContainerType>()(target.tracks()) << 1)) >> 1)
|
2018-03-07 01:18:01 +01:00
|
|
|
^ (hash<TagTarget::IdContainerType>()(target.attachments()) << 1);
|
2017-01-15 21:43:46 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
template <> struct hash<FieldId> {
|
2017-01-23 00:27:21 +01:00
|
|
|
std::size_t operator()(const FieldId &id) const
|
|
|
|
{
|
|
|
|
using std::hash;
|
2017-06-14 00:09:10 +02:00
|
|
|
return ((id.knownField() != KnownField::Invalid) ? hash<KnownField>()(id.knownField()) : hash<string>()(id.denotation()))
|
2018-03-07 01:18:01 +01:00
|
|
|
^ (hash<string>()(id.m_nativeField) << 1);
|
2017-01-23 00:27:21 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
template <> struct hash<FieldScope> {
|
2017-01-23 00:27:21 +01:00
|
|
|
std::size_t operator()(const FieldScope &scope) const
|
2017-01-15 21:43:46 +01:00
|
|
|
{
|
|
|
|
using std::hash;
|
2018-03-07 01:18:01 +01:00
|
|
|
return (hash<FieldId>()(scope.field) ^ (hash<TagType>()(scope.tagType) << 1) >> 1)
|
|
|
|
^ (hash<TagTarget>()(scope.tagTarget) ^ (static_cast<unsigned long>(scope.allTracks) << 4)
|
2019-05-04 21:03:32 +02:00
|
|
|
^ (hash<vector<std::uint64_t>>()(scope.trackIds) << 1) >> 1);
|
2017-01-15 21:43:46 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
} // namespace std
|
2017-01-15 21:43:46 +01:00
|
|
|
|
|
|
|
namespace Cli {
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
struct FieldValues {
|
2017-06-14 00:09:10 +02:00
|
|
|
std::vector<FieldValue> allValues;
|
|
|
|
std::vector<FieldValue *> relevantValues;
|
|
|
|
};
|
2018-07-11 13:18:36 +02:00
|
|
|
using FieldDenotations = std::unordered_map<FieldScope, FieldValues>;
|
2017-01-15 21:43:46 +01:00
|
|
|
|
|
|
|
// declare/define actual helpers
|
|
|
|
|
|
|
|
constexpr bool isDigit(char c)
|
|
|
|
{
|
|
|
|
return c >= '0' && c <= '9';
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string incremented(const std::string &str, unsigned int toIncrement = 1);
|
|
|
|
|
2018-03-06 23:10:13 +01:00
|
|
|
void printDiagMessages(const TagParser::Diagnostics &diag, const char *head = nullptr, bool beVerbose = false);
|
2021-01-30 21:57:09 +01:00
|
|
|
void printProperty(const char *propName, std::string_view value, const char *suffix = nullptr, CppUtilities::Indentation indentation = 4);
|
2019-06-12 20:47:44 +02:00
|
|
|
void printProperty(const char *propName, ElementPosition elementPosition, const char *suffix = nullptr, CppUtilities::Indentation indentation = 4);
|
2017-01-15 21:43:46 +01:00
|
|
|
|
2019-06-10 22:49:46 +02:00
|
|
|
extern CppUtilities::TimeSpanOutputFormat timeSpanOutputFormat;
|
2017-11-29 22:57:32 +01:00
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
inline void printProperty(
|
2019-06-10 22:49:46 +02:00
|
|
|
const char *propName, CppUtilities::TimeSpan timeSpan, const char *suffix = nullptr, CppUtilities::Indentation indentation = 4)
|
2017-01-15 21:43:46 +01:00
|
|
|
{
|
2018-03-07 01:18:01 +01:00
|
|
|
if (!timeSpan.isNull()) {
|
2017-11-29 22:57:32 +01:00
|
|
|
printProperty(propName, timeSpan.toString(timeSpanOutputFormat), suffix, indentation);
|
2017-01-15 21:43:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
inline void printProperty(
|
2019-06-10 22:49:46 +02:00
|
|
|
const char *propName, CppUtilities::DateTime dateTime, const char *suffix = nullptr, CppUtilities::Indentation indentation = 4)
|
2017-01-15 21:43:46 +01:00
|
|
|
{
|
2018-03-07 01:18:01 +01:00
|
|
|
if (!dateTime.isNull()) {
|
2017-01-15 21:43:46 +01:00
|
|
|
printProperty(propName, dateTime.toString(), suffix, indentation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-10 22:49:46 +02:00
|
|
|
template <typename NumberType, CppUtilities::Traits::EnableIfAny<std::is_integral<NumberType>, std::is_floating_point<NumberType>> * = nullptr>
|
2018-03-07 01:18:01 +01:00
|
|
|
inline void printProperty(
|
2019-06-10 22:49:46 +02:00
|
|
|
const char *propName, const NumberType value, const char *suffix = nullptr, bool force = false, CppUtilities::Indentation indentation = 4)
|
2017-01-15 21:43:46 +01:00
|
|
|
{
|
2018-03-07 01:18:01 +01:00
|
|
|
if (value != 0 || force) {
|
2019-06-10 22:49:46 +02:00
|
|
|
printProperty(propName, CppUtilities::numberToString<NumberType>(value), suffix, indentation);
|
2017-01-15 21:43:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-23 00:27:21 +01:00
|
|
|
void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool skipEmpty);
|
2018-05-13 00:27:42 +02:00
|
|
|
void printNativeFields(const Tag *tag);
|
2017-01-16 22:58:15 +01:00
|
|
|
|
2019-06-10 22:49:46 +02:00
|
|
|
CppUtilities::TimeSpanOutputFormat parseTimeSpanOutputFormat(
|
|
|
|
const CppUtilities::Argument &usageArg, CppUtilities::TimeSpanOutputFormat defaultFormat);
|
|
|
|
TagUsage parseUsageDenotation(const CppUtilities::Argument &usageArg, TagUsage defaultUsage);
|
|
|
|
TagTextEncoding parseEncodingDenotation(const CppUtilities::Argument &encodingArg, TagTextEncoding defaultEncoding);
|
2019-06-12 20:47:44 +02:00
|
|
|
ElementPosition parsePositionDenotation(const CppUtilities::Argument &posArg, const CppUtilities::Argument &valueArg, ElementPosition defaultPos);
|
2019-06-10 22:49:46 +02:00
|
|
|
std::uint64_t parseUInt64(const CppUtilities::Argument &arg, std::uint64_t defaultValue);
|
2017-01-15 21:43:46 +01:00
|
|
|
TagTarget::IdContainerType parseIds(const std::string &concatenatedIds);
|
|
|
|
bool applyTargetConfiguration(TagTarget &target, const std::string &configStr);
|
2019-06-10 22:49:46 +02:00
|
|
|
FieldDenotations parseFieldDenotations(const CppUtilities::Argument &fieldsArg, bool readOnly);
|
2017-05-18 02:32:51 +02:00
|
|
|
std::string tagName(const Tag *tag);
|
2017-06-14 00:09:10 +02:00
|
|
|
bool stringToBool(const std::string &str);
|
2017-09-22 00:19:24 +02:00
|
|
|
extern bool logLineFinalized;
|
2018-03-06 23:10:13 +01:00
|
|
|
void logNextStep(const TagParser::AbortableProgressFeedback &progress);
|
|
|
|
void logStepPercentage(const TagParser::AbortableProgressFeedback &progress);
|
2017-09-22 00:19:24 +02:00
|
|
|
void finalizeLog();
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
} // namespace Cli
|
2017-01-15 21:43:46 +01:00
|
|
|
|
|
|
|
#endif // CLI_HELPER
|