diff --git a/CMakeLists.txt b/CMakeLists.txt index 99fb1cf..85cf592 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,8 +11,8 @@ set(META_APP_DESCRIPTION "A tageditor with Qt GUI and command line interface. Su set(META_GUI_OPTIONAL true) set(META_JS_SRC_DIR renamingutility) set(META_VERSION_MAJOR 3) -set(META_VERSION_MINOR 0) -set(META_VERSION_PATCH 1) +set(META_VERSION_MINOR 1) +set(META_VERSION_PATCH 0) # add project files set(HEADER_FILES diff --git a/application/main.cpp b/application/main.cpp index aa21c5f..dac7888 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -164,8 +164,10 @@ int main(int argc, char *argv[]) fieldsArg.setImplicit(true); OperationArgument displayTagInfoArg("get", 'g', "displays the values of all specified tag fields (displays all fields if none specified)", PROJECT_NAME " get title album artist -f /some/dir/*.m4a"); - displayTagInfoArg.setCallback(std::bind(Cli::displayTagInfo, std::cref(fieldsArg), std::cref(filesArg), std::cref(verboseArg))); - displayTagInfoArg.setSubArguments({ &fieldsArg, &filesArg, &verboseArg }); + ConfigValueArgument showUnsupportedArg("show-unsupported", 'u', "shows unsupported fields (has only effect when no field names specified)"); + displayTagInfoArg.setCallback( + std::bind(Cli::displayTagInfo, std::cref(fieldsArg), std::cref(showUnsupportedArg), std::cref(filesArg), std::cref(verboseArg))); + displayTagInfoArg.setSubArguments({ &fieldsArg, &showUnsupportedArg, &filesArg, &verboseArg }); // set tag info Cli::SetTagInfoArgs setTagInfoArgs(filesArg, verboseArg); // extract cover diff --git a/cli/helper.cpp b/cli/helper.cpp index 1967bf2..f5c7890 100644 --- a/cli/helper.cpp +++ b/cli/helper.cpp @@ -213,11 +213,27 @@ void printFieldName(const char *fieldName, size_t fieldNameLen) { cout << " " << fieldName; // also write padding + if (fieldNameLen >= 18) { + // write at least one space + cout << ' '; + return; + } for (auto i = fieldNameLen; i < 18; ++i) { cout << ' '; } } +void printTagValue(const TagValue &value) +{ + try { + cout << value.toString(TagTextEncoding::Utf8); + } catch (const ConversionException &) { + // handle case when value can not be displayed as string + cout << "can't display as string (see --extract)"; + } + cout << '\n'; +} + void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool skipEmpty) { // write field name @@ -243,13 +259,7 @@ void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool s // print values for (const auto &value : values) { printFieldName(fieldName, fieldNameLen); - try { - cout << value->toString(TagTextEncoding::Utf8); - } catch (const ConversionException &) { - // handle case when value can not be displayed as string - cout << "can't display as string (see --extract)"; - } - cout << '\n'; + printTagValue(*value); } } catch (const ConversionException &e) { @@ -259,6 +269,41 @@ void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool s } } +template void printNativeFields(const Tag *tag) +{ + const auto *const concreteTag = static_cast(tag); + for (const auto &field : concreteTag->fields()) { + // skip all fields which are supported anyways + if (concreteTag->knownField(field.first) != KnownField::Invalid) { + continue; + } + + const auto fieldId(ConcreteTag::FieldType::fieldIdToString(field.first)); + printFieldName(fieldId.data(), fieldId.size()); + printTagValue(field.second.value()); + } +} + +void printNativeFields(const Tag *tag) +{ + switch (tag->type()) { + case TagType::Id3v2Tag: + printNativeFields(tag); + break; + case TagType::Mp4Tag: + printNativeFields(tag); + break; + case TagType::MatroskaTag: + printNativeFields(tag); + break; + case TagType::VorbisComment: + case TagType::OggVorbisComment: + printNativeFields(tag); + break; + default:; + } +} + TimeSpanOutputFormat parseTimeSpanOutputFormat(const Argument &timeSpanFormatArg, TimeSpanOutputFormat defaultFormat) { if (timeSpanFormatArg.isPresent()) { diff --git a/cli/helper.h b/cli/helper.h index 5b5802f..e667625 100644 --- a/cli/helper.h +++ b/cli/helper.h @@ -279,7 +279,7 @@ inline void printProperty( } } -template , std::is_floating_point>* = nullptr> +template , std::is_floating_point> * = nullptr> inline void printProperty( const char *propName, const NumberType value, const char *suffix = nullptr, bool force = false, ApplicationUtilities::Indentation indentation = 4) { @@ -289,6 +289,7 @@ inline void printProperty( } void printField(const FieldScope &scope, const Tag *tag, TagType tagType, bool skipEmpty); +void printNativeFields(const Tag *tag); ChronoUtilities::TimeSpanOutputFormat parseTimeSpanOutputFormat( const ApplicationUtilities::Argument &usageArg, ChronoUtilities::TimeSpanOutputFormat defaultFormat); diff --git a/cli/mainfeatures.cpp b/cli/mainfeatures.cpp index 3f50edf..c7f7dbc 100644 --- a/cli/mainfeatures.cpp +++ b/cli/mainfeatures.cpp @@ -301,7 +301,7 @@ void displayFileInfo(const ArgumentOccurrence &, const Argument &filesArg, const } } -void displayTagInfo(const Argument &fieldsArg, const Argument &filesArg, const Argument &verboseArg) +void displayTagInfo(const Argument &fieldsArg, const Argument &showUnsupportedArg, const Argument &filesArg, const Argument &verboseArg) { CMD_UTILS_START_CONSOLE; @@ -325,29 +325,32 @@ void displayTagInfo(const Argument &fieldsArg, const Argument &filesArg, const A fileInfo.parseTags(diag); cout << "Tag information for \"" << file << "\":\n"; const auto tags = fileInfo.tags(); - if (!tags.empty()) { - // iterate through all tags - for (const auto *tag : tags) { - // determine tag type - const TagType tagType = tag->type(); - // write tag name and target, eg. MP4/iTunes tag - cout << " - " << TextAttribute::Bold << tagName(tag) << TextAttribute::Reset << '\n'; - // iterate through fields specified by the user - if (fields.empty()) { - for (auto field = firstKnownField; field != KnownField::Invalid; field = nextKnownField(field)) { - printField(FieldScope(field), tag, tagType, true); - } - } else { - for (const auto &fieldDenotation : fields) { - const FieldScope &denotedScope = fieldDenotation.first; - if (denotedScope.tagType == TagType::Unspecified || (denotedScope.tagType | tagType) != TagType::Unspecified) { - printField(denotedScope, tag, tagType, false); - } + if (tags.empty()) { + cout << " - File has no (supported) tag information.\n"; + continue; + } + // iterate through all tags + for (const auto *tag : tags) { + // determine tag type + const TagType tagType = tag->type(); + // write tag name and target, eg. MP4/iTunes tag + cout << " - " << TextAttribute::Bold << tagName(tag) << TextAttribute::Reset << '\n'; + // iterate through fields specified by the user + if (fields.empty()) { + for (auto field = firstKnownField; field != KnownField::Invalid; field = nextKnownField(field)) { + printField(FieldScope(field), tag, tagType, true); + } + if (showUnsupportedArg.isPresent()) { + printNativeFields(tag); + } + } else { + for (const auto &fieldDenotation : fields) { + const FieldScope &denotedScope = fieldDenotation.first; + if (denotedScope.tagType == TagType::Unspecified || (denotedScope.tagType | tagType) != TagType::Unspecified) { + printField(denotedScope, tag, tagType, false); } } } - } else { - cout << " - File has no (supported) tag information.\n"; } } catch (const TagParser::Failure &) { cerr << Phrases::Error << "A parsing failure occured when reading the file \"" << file << "\"." << Phrases::EndFlush; diff --git a/cli/mainfeatures.h b/cli/mainfeatures.h index 7f455de..9303f06 100644 --- a/cli/mainfeatures.h +++ b/cli/mainfeatures.h @@ -53,8 +53,8 @@ void displayFileInfo(const ApplicationUtilities::ArgumentOccurrence &, const App const ApplicationUtilities::Argument &verboseArg); void generateFileInfo(const ApplicationUtilities::ArgumentOccurrence &, const ApplicationUtilities::Argument &inputFileArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &validateArg); -void displayTagInfo(const ApplicationUtilities::Argument &fieldsArg, const ApplicationUtilities::Argument &filesArg, - const ApplicationUtilities::Argument &verboseArg); +void displayTagInfo(const ApplicationUtilities::Argument &fieldsArg, const ApplicationUtilities::Argument &showUnsupportedArg, + const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg); void setTagInfo(const Cli::SetTagInfoArgs &args); void extractField(const ApplicationUtilities::Argument &fieldArg, const ApplicationUtilities::Argument &attachmentArg, const ApplicationUtilities::Argument &inputFilesArg, const ApplicationUtilities::Argument &outputFileArg,