Allow setting covers of special types with description via CLI

See https://github.com/Martchus/tageditor/issues/64
This commit is contained in:
Martchus 2021-04-28 00:49:56 +02:00
parent 8258a14089
commit 41fb2069f3
4 changed files with 226 additions and 39 deletions

View File

@ -270,6 +270,24 @@ Here are some Bash examples which illustrate getting and setting tag information
**Note**: The *+* sign after the field name *track* which indicates that the field value should be increased after **Note**: The *+* sign after the field name *track* which indicates that the field value should be increased after
a file has been processed. a file has been processed.
* Sets a cover of a special type with a description:
```
tageditor set cover=":front-cover" cover0="/path/to/back-cover.jpg:back-cover:The description" -f foo.mp3
```
- The syntax is `path:cover-type:description`. The cover type and description are optional.
- In this example the front cover is removed (by passing an empty path) and the back cover set to the specified
file. Other cover types are not affected.
- When specifying a cover without type, all existing covers are replaced and the new cover will be of the
type "other".
- To replace all existing covers when specifying a cover type
use e.g. `… cover= cover0="/path/to/back-cover.jpg:back-cover"`.
- The names of all cover types can be shown via `tageditor --print-field-names`.
- The `0` after the 2nd `cover` is required. Otherwise the 2nd cover would only be set in the 2nd file (which
is not even specified in this example).
- This is only supported by the tag formats ID3v2 and Vorbis Comment. The type and description are ignored
when dealing with a different format.
## Text encoding / unicode support ## Text encoding / unicode support
1. It is possible to set the preferred encoding used *within* the tags via CLI option ``--encoding`` 1. It is possible to set the preferred encoding used *within* the tags via CLI option ``--encoding``
and in the GUI settings. and in the GUI settings.

View File

@ -28,6 +28,39 @@ using namespace Settings;
namespace Cli { namespace Cli {
const std::vector<std::string_view> &id3v2CoverTypeNames()
{
static const auto t
= std::vector<std::string_view>{ "other"sv, "file-icon"sv, "other-file-icon"sv, "front-cover"sv, "back-cover"sv, "leaflet-page"sv, "media"sv,
"lead-performer"sv, "artist"sv, "conductor"sv, "band"sv, "composer"sv, "lyricist"sv, "recording-location"sv, "during-recording"sv,
"during-performance"sv, "movie-screen-capture"sv, "bright-colored-fish"sv, "illustration"sv, "artist-logotype"sv, "publisher"sv };
return t;
}
CoverType id3v2CoverType(std::string_view coverName)
{
static const auto mapping = [] {
const auto &names = id3v2CoverTypeNames();
auto map = std::map<std::string_view, CoverType>();
auto index = CoverType();
for (const auto name : names) {
map[name] = index++;
}
return map;
}();
if (const auto i = mapping.find(coverName); i != mapping.end()) {
return i->second;
} else {
return invalidCoverType;
}
}
std::string_view id3v2CoverName(CoverType coverType)
{
const auto &names = id3v2CoverTypeNames();
return coverType < names.size() ? names[coverType] : "?"sv;
}
CppUtilities::TimeSpanOutputFormat timeSpanOutputFormat = TimeSpanOutputFormat::WithMeasures; CppUtilities::TimeSpanOutputFormat timeSpanOutputFormat = TimeSpanOutputFormat::WithMeasures;
/*! /*!
@ -208,16 +241,16 @@ void printProperty(const char *propName, ElementPosition elementPosition, const
} }
} }
void printFieldName(const char *fieldName, size_t fieldNameLen) void printFieldName(std::string_view fieldName)
{ {
cout << " " << fieldName; cout << " " << fieldName;
// also write padding // also write padding
if (fieldNameLen >= 18) { if (fieldName.size() >= 18) {
// write at least one space // write at least one space
cout << ' '; cout << ' ';
return; return;
} }
for (auto i = fieldNameLen; i < 18; ++i) { for (auto i = fieldName.size(); i < 18; ++i) {
cout << ' '; cout << ' ';
} }
} }
@ -233,13 +266,30 @@ void printTagValue(const TagValue &value)
cout << '\n'; cout << '\n';
} }
template <class TagType> static void printId3v2CoverValues(TagType *tag)
{
const auto &fields = tag->fields();
const auto id = tag->fieldId(KnownField::Cover);
for (auto range = fields.equal_range(id); range.first != range.second; ++range.first) {
const auto &field = range.first->second;
printFieldName(argsToString("Cover (", id3v2CoverName(static_cast<CoverType>(field.typeInfo())), ")"));
printTagValue(field.value());
}
}
void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool skipEmpty) void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool skipEmpty)
{ {
// write field name const auto fieldName = std::string_view(scope.field.name());
const char *fieldName = scope.field.name();
const auto fieldNameLen = strlen(fieldName);
try { try {
if (scope.field.knownFieldForTag(tag, tagType) == KnownField::Cover) {
if (tagType == TagType::Id3v2Tag) {
printId3v2CoverValues(static_cast<const Id3v2Tag *>(tag));
} else {
printId3v2CoverValues(static_cast<const VorbisComment *>(tag));
}
return;
}
// parse field denotation // parse field denotation
const auto &values = scope.field.values(tag, tagType); const auto &values = scope.field.values(tag, tagType);
@ -255,20 +305,20 @@ void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool s
// print empty value (if not prevented) // print empty value (if not prevented)
if (values.first.empty()) { if (values.first.empty()) {
printFieldName(fieldName, fieldNameLen); printFieldName(fieldName);
cout << "none\n"; cout << "none\n";
return; return;
} }
// print values // print values
for (const auto &value : values.first) { for (const auto &value : values.first) {
printFieldName(fieldName, fieldNameLen); printFieldName(fieldName);
printTagValue(*value); printTagValue(*value);
} }
} catch (const ConversionException &e) { } catch (const ConversionException &e) {
// handle conversion error which might happen when parsing field denotation // handle conversion error which might happen when parsing field denotation
printFieldName(fieldName, fieldNameLen); printFieldName(fieldName);
cout << "unable to parse - " << e.what() << '\n'; cout << "unable to parse - " << e.what() << '\n';
} }
} }
@ -283,7 +333,7 @@ template <typename ConcreteTag> void printNativeFields(const Tag *tag)
} }
const auto fieldId(ConcreteTag::FieldType::fieldIdToString(field.first)); const auto fieldId(ConcreteTag::FieldType::fieldIdToString(field.first));
printFieldName(fieldId.data(), fieldId.size()); printFieldName(fieldId);
printTagValue(field.second.value()); printTagValue(field.second.value());
} }
} }
@ -613,7 +663,7 @@ FieldDenotations parseFieldDenotations(const Argument &fieldsArg, bool readOnly)
} }
template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType> template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType>
std::pair<std::vector<const TagValue *>, bool> valuesForNativeField(std::string_view idString, const Tag *tag, TagType tagType) static std::pair<std::vector<const TagValue *>, bool> valuesForNativeField(std::string_view idString, const Tag *tag, TagType tagType)
{ {
auto res = make_pair<std::vector<const TagValue *>, bool>({}, false); auto res = make_pair<std::vector<const TagValue *>, bool>({}, false);
if (!(tagType & tagTypeMask)) { if (!(tagType & tagTypeMask)) {
@ -625,7 +675,7 @@ std::pair<std::vector<const TagValue *>, bool> valuesForNativeField(std::string_
} }
template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType> template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType>
bool setValuesForNativeField(std::string_view idString, Tag *tag, TagType tagType, const std::vector<TagValue> &values) static bool setValuesForNativeField(std::string_view idString, Tag *tag, TagType tagType, const std::vector<TagValue> &values)
{ {
if (!(tagType & tagTypeMask)) { if (!(tagType & tagTypeMask)) {
return false; return false;
@ -633,20 +683,35 @@ bool setValuesForNativeField(std::string_view idString, Tag *tag, TagType tagTyp
return static_cast<ConcreteTag *>(tag)->setValues(ConcreteTag::FieldType::fieldIdFromString(idString), values); return static_cast<ConcreteTag *>(tag)->setValues(ConcreteTag::FieldType::fieldIdFromString(idString), values);
} }
inline FieldId::FieldId( template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType>
std::string_view nativeField, const GetValuesForNativeFieldType &valuesForNativeField, const SetValuesForNativeFieldType &setValuesForNativeField) static KnownField knownFieldForNativeField(std::string_view idString, const Tag *tag, TagType tagType)
{
if (!(tagType & tagTypeMask)) {
return KnownField::Invalid;
}
try {
return static_cast<const ConcreteTag *>(tag)->knownField(ConcreteTag::FieldType::fieldIdFromString(idString));
} catch (const ConversionException &) {
return KnownField::Invalid;
}
}
inline FieldId::FieldId(std::string_view nativeField, GetValuesForNativeFieldType &&valuesForNativeField,
SetValuesForNativeFieldType &&setValuesForNativeField, KnownFieldForNativeFieldType &&knownFieldForNativeField)
: m_knownField(KnownField::Invalid) : m_knownField(KnownField::Invalid)
, m_nativeField(nativeField) , m_nativeField(nativeField)
, m_valuesForNativeField(valuesForNativeField) , m_valuesForNativeField(std::move(valuesForNativeField))
, m_setValuesForNativeField(setValuesForNativeField) , m_setValuesForNativeField(std::move(setValuesForNativeField))
, m_knownFieldForNativeField(std::move(knownFieldForNativeField))
{ {
} }
/// \remarks This wrapper is required because specifying c'tor template args is not possible. /// \remarks This wrapper is required because specifying c'tor template args is not possible.
template <class ConcreteTag, TagType tagTypeMask> FieldId FieldId::fromNativeField(std::string_view nativeFieldId) template <class ConcreteTag, TagType tagTypeMask> FieldId FieldId::fromNativeField(std::string_view nativeFieldId)
{ {
return FieldId(nativeFieldId, bind(&valuesForNativeField<ConcreteTag, tagTypeMask>, nativeFieldId, _1, _2), return FieldId(nativeFieldId, std::bind(&valuesForNativeField<ConcreteTag, tagTypeMask>, nativeFieldId, _1, _2),
bind(&setValuesForNativeField<ConcreteTag, tagTypeMask>, nativeFieldId, _1, _2, _3)); std::bind(&setValuesForNativeField<ConcreteTag, tagTypeMask>, nativeFieldId, _1, _2, _3),
std::bind(&knownFieldForNativeField<ConcreteTag, tagTypeMask>, nativeFieldId, _1, _2));
} }
FieldId FieldId::fromTagDenotation(const char *denotation, size_t denotationSize) FieldId FieldId::fromTagDenotation(const char *denotation, size_t denotationSize)
@ -700,6 +765,15 @@ bool FieldId::setValues(Tag *tag, TagType tagType, const std::vector<TagValue> &
} }
} }
KnownField FieldId::knownFieldForTag(const Tag *tag, TagType tagType) const
{
if (!m_nativeField.empty()) {
return m_knownFieldForNativeField(tag, tagType);
} else {
return m_knownField;
}
}
string tagName(const Tag *tag) string tagName(const Tag *tag)
{ {
stringstream ss; stringstream ss;

View File

@ -3,7 +3,9 @@
#include "../application/knownfieldmodel.h" #include "../application/knownfieldmodel.h"
#include <tagparser/id3/id3v2tag.h>
#include <tagparser/tag.h> #include <tagparser/tag.h>
#include <tagparser/vorbis/vorbiscomment.h>
#include <c++utilities/application/commandlineutils.h> #include <c++utilities/application/commandlineutils.h>
#include <c++utilities/chrono/datetime.h> #include <c++utilities/chrono/datetime.h>
@ -13,6 +15,7 @@
#include <c++utilities/misc/traits.h> #include <c++utilities/misc/traits.h>
#include <functional> #include <functional>
#include <string_view>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -44,11 +47,18 @@ CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(TagParser, TagParser::TagType)
namespace Cli { namespace Cli {
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);
class FieldId { class FieldId {
friend struct std::hash<FieldId>; friend struct std::hash<FieldId>;
public: public:
FieldId(KnownField m_knownField = KnownField::Invalid, const char *denotation = nullptr, std::size_t denotationSize = 0); explicit FieldId(KnownField m_knownField = KnownField::Invalid, const char *denotation = nullptr, std::size_t denotationSize = 0);
static FieldId fromTagDenotation(const char *denotation, std::size_t denotationSize); static FieldId fromTagDenotation(const char *denotation, std::size_t denotationSize);
static FieldId fromTrackDenotation(const char *denotation, std::size_t denotationSize); static FieldId fromTrackDenotation(const char *denotation, std::size_t denotationSize);
bool operator==(const FieldId &other) const; bool operator==(const FieldId &other) const;
@ -58,12 +68,14 @@ public:
const std::string &denotation() const; const std::string &denotation() const;
std::pair<std::vector<const TagValue *>, bool> values(const Tag *tag, TagType tagType) const; std::pair<std::vector<const TagValue *>, bool> values(const Tag *tag, TagType tagType) const;
bool setValues(Tag *tag, TagType tagType, const std::vector<TagValue> &values) const; bool setValues(Tag *tag, TagType tagType, const std::vector<TagValue> &values) const;
KnownField knownFieldForTag(const Tag *tag, TagType tagType) const;
private: private:
using GetValuesForNativeFieldType = std::function<std::pair<std::vector<const TagValue *>, bool>(const Tag *, TagType)>; using GetValuesForNativeFieldType = std::function<std::pair<std::vector<const TagValue *>, bool>(const Tag *, TagType)>;
using SetValuesForNativeFieldType = std::function<bool(Tag *, TagType, const std::vector<TagValue> &)>; using SetValuesForNativeFieldType = std::function<bool(Tag *, TagType, const std::vector<TagValue> &)>;
FieldId(std::string_view nativeField, const GetValuesForNativeFieldType &valuesForNativeField, using KnownFieldForNativeFieldType = std::function<KnownField(const Tag *, TagType)>;
const SetValuesForNativeFieldType &setValuesForNativeField); FieldId(std::string_view nativeField, GetValuesForNativeFieldType &&valuesForNativeField, SetValuesForNativeFieldType &&setValuesForNativeField,
KnownFieldForNativeFieldType &&knownFieldForNativeField);
template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType> static FieldId fromNativeField(std::string_view nativeFieldId); template <class ConcreteTag, TagType tagTypeMask = ConcreteTag::tagType> static FieldId fromNativeField(std::string_view nativeFieldId);
KnownField m_knownField; KnownField m_knownField;
@ -71,6 +83,7 @@ private:
std::string m_nativeField; std::string m_nativeField;
GetValuesForNativeFieldType m_valuesForNativeField; GetValuesForNativeFieldType m_valuesForNativeField;
SetValuesForNativeFieldType m_setValuesForNativeField; SetValuesForNativeFieldType m_setValuesForNativeField;
KnownFieldForNativeFieldType m_knownFieldForNativeField;
}; };
inline FieldId::FieldId(KnownField knownField, const char *denotation, std::size_t denotationSize) inline FieldId::FieldId(KnownField knownField, const char *denotation, std::size_t denotationSize)
@ -105,7 +118,7 @@ inline const std::string &FieldId::denotation() const
} }
struct FieldScope { struct FieldScope {
FieldScope(KnownField field = KnownField::Invalid, TagType tagType = TagType::Unspecified, TagTarget tagTarget = TagTarget()); explicit FieldScope(KnownField field = KnownField::Invalid, TagType tagType = TagType::Unspecified, TagTarget tagTarget = TagTarget());
bool operator==(const FieldScope &other) const; bool operator==(const FieldScope &other) const;
bool isTrack() const; bool isTrack() const;
@ -150,7 +163,7 @@ inline FieldValue::FieldValue(DenotationType type, unsigned int fileIndex, const
class InterruptHandler { class InterruptHandler {
public: public:
InterruptHandler(std::function<void()> handler); explicit InterruptHandler(std::function<void()> handler);
~InterruptHandler(); ~InterruptHandler();
private: private:

View File

@ -18,11 +18,13 @@
#include <tagparser/abstracttrack.h> #include <tagparser/abstracttrack.h>
#include <tagparser/backuphelper.h> #include <tagparser/backuphelper.h>
#include <tagparser/diagnostics.h> #include <tagparser/diagnostics.h>
#include <tagparser/id3/id3v2tag.h>
#include <tagparser/localehelper.h> #include <tagparser/localehelper.h>
#include <tagparser/mediafileinfo.h> #include <tagparser/mediafileinfo.h>
#include <tagparser/progressfeedback.h> #include <tagparser/progressfeedback.h>
#include <tagparser/tag.h> #include <tagparser/tag.h>
#include <tagparser/tagvalue.h> #include <tagparser/tagvalue.h>
#include <tagparser/vorbis/vorbiscomment.h>
#ifdef TAGEDITOR_JSON_EXPORT #ifdef TAGEDITOR_JSON_EXPORT
#include <reflective_rapidjson/json/reflector.h> #include <reflective_rapidjson/json/reflector.h>
@ -52,6 +54,7 @@
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string_view>
using namespace std; using namespace std;
using namespace CppUtilities; using namespace CppUtilities;
@ -93,6 +96,9 @@ void printFieldNames(const ArgumentOccurrence &)
" - Tag modifier: " TAG_MODIFIER "\n" " - Tag modifier: " TAG_MODIFIER "\n"
" - Track modifier: track=id1,id2,id3,... track=all\n" " - Track modifier: track=id1,id2,id3,... track=all\n"
" - Target modifier:\n " TARGET_MODIFIER "\n" " - Target modifier:\n " TARGET_MODIFIER "\n"
"ID3v2 cover types:\n"
<< joinStrings<std::remove_reference_t<decltype(id3v2CoverTypeNames())>, std::string>(id3v2CoverTypeNames(), "\n"sv, false, " - "sv, ""sv)
<< '\n'
<< flush; << flush;
} }
@ -379,6 +385,41 @@ void displayTagInfo(const Argument &fieldsArg, const Argument &showUnsupportedAr
} }
} }
template <class TagType>
bool fieldPredicate(CoverType coverType, const std::pair<typename TagType::IdentifierType, typename TagType::FieldType> &pair)
{
return pair.second.isTypeInfoAssigned() ? (pair.second.typeInfo() == static_cast<typename TagType::FieldType::TypeInfoType>(coverType))
: (coverType == 0);
}
template <class TagType> static void setId3v2CoverValues(TagType *tag, std::vector<std::pair<TagValue, CoverType>> &&values)
{
auto &fields = tag->fields();
const auto id = tag->fieldId(KnownField::Cover);
const auto range = fields.equal_range(id);
const auto first = range.first;
for (auto &[tagValue, coverType] : values) {
// check whether there is already a tag value with the current index/type
auto pair = find_if(first, range.second, std::bind(fieldPredicate<TagType>, coverType, placeholders::_1));
if (pair != range.second) {
// there is already a tag value with the current index/type
// -> update this value
pair->second.setValue(tagValue);
// check whether there are more values with the current index/type assigned
while ((pair = find_if(++pair, range.second, std::bind(fieldPredicate<TagType>, coverType, placeholders::_1))) != range.second) {
// -> remove these values as we only support one value of a type in the same tag
pair->second.setValue(TagValue());
}
} else if (!tagValue.isEmpty()) {
using FieldType = typename TagType::FieldType;
auto newField = FieldType(id, tagValue);
newField.setTypeInfo(static_cast<typename FieldType::TypeInfoType>(coverType));
fields.insert(std::pair(id, std::move(newField)));
}
}
}
void setTagInfo(const SetTagInfoArgs &args) void setTagInfo(const SetTagInfoArgs &args)
{ {
CMD_UTILS_START_CONSOLE; CMD_UTILS_START_CONSOLE;
@ -603,7 +644,8 @@ void setTagInfo(const SetTagInfoArgs &args)
continue; continue;
} }
// convert the values to TagValue // convert the values to TagValue
vector<TagValue> convertedValues; auto convertedValues = std::vector<TagValue>();
auto convertedValuesWithCoverType = std::vector<std::pair<TagValue, CoverType>>();
for (const FieldValue *relevantDenotedValue : fieldDenotation.second.relevantValues) { for (const FieldValue *relevantDenotedValue : fieldDenotation.second.relevantValues) {
// assign an empty TagValue to remove the field if denoted value is empty // assign an empty TagValue to remove the field if denoted value is empty
if (relevantDenotedValue->value.empty()) { if (relevantDenotedValue->value.empty()) {
@ -616,32 +658,72 @@ void setTagInfo(const SetTagInfoArgs &args)
continue; continue;
} }
// add value from file // add value from file
const auto parts = splitStringSimple<std::vector<std::string_view>>(relevantDenotedValue->value, ":", 3);
const auto path = parts.empty() ? std::string_view() : parts.front();
try { try {
// assume the file refers to a picture // assume the file refers to a picture
MediaFileInfo coverFileInfo(relevantDenotedValue->value); auto value = TagValue();
Diagnostics coverDiag; if (!path.empty()) {
AbortableProgressFeedback coverProgress; // FIXME: actually use the progress object auto coverFileInfo = MediaFileInfo(path);
coverFileInfo.open(true); auto coverDiag = Diagnostics();
coverFileInfo.parseContainerFormat(coverDiag, coverProgress); auto coverProgress = AbortableProgressFeedback(); // FIXME: actually use the progress object
auto buff = make_unique<char[]>(coverFileInfo.size()); coverFileInfo.open(true);
coverFileInfo.stream().seekg(static_cast<streamoff>(coverFileInfo.containerOffset())); coverFileInfo.parseContainerFormat(coverDiag, coverProgress);
coverFileInfo.stream().read(buff.get(), static_cast<streamoff>(coverFileInfo.size())); auto buff = make_unique<char[]>(coverFileInfo.size());
TagValue value(move(buff), coverFileInfo.size(), TagDataType::Picture); coverFileInfo.stream().seekg(static_cast<streamoff>(coverFileInfo.containerOffset()));
value.setMimeType(coverFileInfo.mimeType()); coverFileInfo.stream().read(buff.get(), static_cast<streamoff>(coverFileInfo.size()));
convertedValues.emplace_back(move(value)); value = TagValue(std::move(buff), coverFileInfo.size(), TagDataType::Picture);
value.setMimeType(coverFileInfo.mimeType());
}
if (parts.size() > 2) {
value.setDescription(parts[2], TagTextEncoding::Utf8);
}
if (parts.size() > 1 && denotedScope.field.knownFieldForTag(tag, tagType) == KnownField::Cover
&& (tagType == TagType::Id3v2Tag || tagType == TagType::VorbisComment)) {
const auto coverType = id3v2CoverType(parts[1]);
if (coverType == invalidCoverType) {
diag.emplace_back(DiagLevel::Warning,
argsToString("Specified cover type \"", parts[1], "\" is invalid. Ignoring the specified field/value."),
context);
} else {
convertedValuesWithCoverType.emplace_back(std::pair(std::move(value), coverType));
}
} else {
if (parts.size() > 1) {
diag.emplace_back(DiagLevel::Warning,
argsToString("Ignoring cover type \"", parts[1], "\" for ", tag->typeName(),
". It is only supported by the cover field and the tag formats ID3v2 and Vorbis Comment."),
context);
}
convertedValues.emplace_back(std::move(value));
}
} catch (const TagParser::Failure &) { } catch (const TagParser::Failure &) {
diag.emplace_back(DiagLevel::Critical, "Unable to parse specified cover file.", context); diag.emplace_back(DiagLevel::Critical, "Unable to parse specified cover file.", context);
} catch (const std::ios_base::failure &) { } catch (const std::ios_base::failure &e) {
diag.emplace_back(DiagLevel::Critical, "An IO error occured when parsing the specified cover file.", context); diag.emplace_back(DiagLevel::Critical,
argsToString("An IO error occured when parsing the specified cover file: ", e.what()), context);
} }
} }
// finally set the values // finally set the values
try { try {
denotedScope.field.setValues(tag, tagType, convertedValues); if (!convertedValues.empty() || convertedValuesWithCoverType.empty()) {
denotedScope.field.setValues(tag, tagType, convertedValues);
}
} catch (const ConversionException &e) { } catch (const ConversionException &e) {
diag.emplace_back(DiagLevel::Critical, diag.emplace_back(DiagLevel::Critical,
argsToString("Unable to parse denoted field ID \"", denotedScope.field.name(), "\": ", e.what()), context); argsToString("Unable to parse denoted field ID \"", denotedScope.field.name(), "\": ", e.what()), context);
} }
if (!convertedValuesWithCoverType.empty()) {
switch (tagType) {
case TagType::Id3v2Tag:
setId3v2CoverValues(static_cast<Id3v2Tag *>(tag), std::move(convertedValuesWithCoverType));
break;
case TagType::VorbisComment:
setId3v2CoverValues(static_cast<VorbisComment *>(tag), std::move(convertedValuesWithCoverType));
break;
default:;
}
}
} }
} }
} }