From 35e219d8332fbd9bac40beb0bd7b499ea3a853c1 Mon Sep 17 00:00:00 2001 From: Martchus Date: Wed, 14 Oct 2015 19:49:48 +0200 Subject: [PATCH] added --doc-title --- README.md | 2 +- application/main.cpp | 8 +++-- cli/mainfeatures.cpp | 75 ++++++++++++++++++++++++++++++++++++++------ cli/mainfeatures.h | 2 +- misc/htmlinfo.cpp | 10 ++++-- 5 files changed, 82 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 915eace..664fb0d 100644 --- a/README.md +++ b/README.md @@ -95,4 +95,4 @@ add "CONFIG+=forcewebkit" to the qmake arguments. - Use padding to prevent rewriting the entire file to save tags. - Support more tag formats (EXIF, PDF metadata, ...). - Set tag information concurrently if multiple files have been specified. -- Extracting/adding/removing attachments via CLI. +- Do tests with Matroska files which have multiple segments. diff --git a/application/main.cpp b/application/main.cpp index 26b7321..691a6d3 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -68,6 +68,10 @@ int main(int argc, char *argv[]) displayTagInfoArg.setValueNames({"title", "album", "artist", "trackpos"}); displayTagInfoArg.setSecondaryArguments({&filesArg, &verboseArg}); // set tag info + Argument docTitleArg("doc-title", "d", "specifies the document title (has no affect if not supported by the container)"); + docTitleArg.setCombinable(true); + docTitleArg.setRequiredValueCount(-1); + docTitleArg.setValueNames({"title of first segment", "title of second segment"}); Argument removeOtherFieldsArg("remove-other-fields", string(), "if present ALL unspecified tag fields will be removed (to remove a specific field use eg. \"album=\")"); removeOtherFieldsArg.setCombinable(true); Argument treatUnknownFilesAsMp3FilesArg("treat-unknown-as-mp3", string(), "if present unknown files will be treatet as MP3 files"); @@ -102,10 +106,10 @@ int main(int argc, char *argv[]) removeExistingAttachmentsArg.setCombinable(true); Argument setTagInfoArg("set-tag-info", "set", "sets the values of all specified tag fields"); setTagInfoArg.setDenotesOperation(true); - setTagInfoArg.setCallback(std::bind(Cli::setTagInfo, _1, std::cref(filesArg), std::cref(removeOtherFieldsArg), std::cref(treatUnknownFilesAsMp3FilesArg), std::cref(id3v1UsageArg), std::cref(id3v2UsageArg), std::cref(mergeMultipleSuccessiveTagsArg), std::cref(id3v2VersionArg), std::cref(encodingArg), std::cref(removeTargetsArg), std::cref(attachmentsArg), std::cref(removeExistingAttachmentsArg), std::cref(verboseArg))); + setTagInfoArg.setCallback(std::bind(Cli::setTagInfo, _1, std::cref(filesArg), std::cref(docTitleArg), std::cref(removeOtherFieldsArg), std::cref(treatUnknownFilesAsMp3FilesArg), std::cref(id3v1UsageArg), std::cref(id3v2UsageArg), std::cref(mergeMultipleSuccessiveTagsArg), std::cref(id3v2VersionArg), std::cref(encodingArg), std::cref(removeTargetsArg), std::cref(attachmentsArg), std::cref(removeExistingAttachmentsArg), std::cref(verboseArg))); setTagInfoArg.setRequiredValueCount(-1); setTagInfoArg.setValueNames({"title=foo", "album=bar", "cover=/path/to/file"}); - setTagInfoArg.setSecondaryArguments({&filesArg, &removeOtherFieldsArg, &treatUnknownFilesAsMp3FilesArg, &id3v1UsageArg, &id3v2UsageArg, &mergeMultipleSuccessiveTagsArg, &id3v2VersionArg, &encodingArg, &removeTargetsArg, &attachmentsArg, &removeExistingAttachmentsArg, &verboseArg}); + setTagInfoArg.setSecondaryArguments({&filesArg, &docTitleArg, &removeOtherFieldsArg, &treatUnknownFilesAsMp3FilesArg, &id3v1UsageArg, &id3v2UsageArg, &mergeMultipleSuccessiveTagsArg, &id3v2VersionArg, &encodingArg, &removeTargetsArg, &attachmentsArg, &removeExistingAttachmentsArg, &verboseArg}); // extract cover Argument extractFieldArg("extract", "ext", "extracts the specified field from the specified file"); extractFieldArg.setRequiredValueCount(1); diff --git a/cli/mainfeatures.cpp b/cli/mainfeatures.cpp index 35e80c9..aa35566 100644 --- a/cli/mainfeatures.cpp +++ b/cli/mainfeatures.cpp @@ -585,6 +585,20 @@ void printProperty(const char *propName, const string &value, const char *suffix printProperty(propName, value.data(), suffix, intention); } +void printProperty(const char *propName, TimeSpan timeSpan, const char *suffix = nullptr, size_t intention = 4) +{ + if(!timeSpan.isNull()) { + printProperty(propName, timeSpan.toString(TimeSpanOutputFormat::WithMeasures), suffix, intention); + } +} + +void printProperty(const char *propName, DateTime dateTime, const char *suffix = nullptr, size_t intention = 4) +{ + if(!dateTime.isNull()) { + printProperty(propName, dateTime.toString(), suffix, intention); + } +} + template void printProperty(const char *propName, const intType value, const char *suffix = nullptr, bool force = false, size_t intention = 4) { @@ -611,6 +625,30 @@ void displayFileInfo(const StringVector &, const Argument &filesArg, const Argum fileInfo.parseChapters(); cout << "Technical information for \"" << file << "\":" << endl; cout << " Container format: " << fileInfo.containerFormatName() << endl; + { + if(const auto container = fileInfo.container()) { + size_t segmentIndex = 0; + for(const auto &title : container->titles()) { + if(segmentIndex) { + printProperty("Title", title + " (segment " + numberToString(++segmentIndex) + ")"); + } else { + ++segmentIndex; + printProperty("Title", title); + } + } + printProperty("Document type", container->documentType()); + printProperty("Read version", container->readVersion()); + printProperty("Version", container->version()); + printProperty("Document read version", container->doctypeReadVersion()); + printProperty("Document version", container->doctypeVersion()); + printProperty("Duration", container->duration()); + printProperty("Creation time", container->creationTime()); + printProperty("Modification time", container->modificationTime()); + } + if(fileInfo.paddingSize()) { + printProperty("Padding", dataSizeToString(fileInfo.paddingSize())); + } + } { // tracks const auto tracks = fileInfo.tracks(); if(!tracks.empty()) { @@ -629,9 +667,7 @@ void displayFileInfo(const StringVector &, const Argument &filesArg, const Argum if(track->size()) { printProperty("Size", dataSizeToString(track->size(), true)); } - if(!track->duration().isNull()) { - printProperty("Duration", track->duration().toString(TimeSpanOutputFormat::WithMeasures)); - } + printProperty("Duration", track->duration()); printProperty("FPS", track->fps()); if(track->channelConfigString()) { printProperty("Channel config", track->channelConfigString()); @@ -646,6 +682,8 @@ void displayFileInfo(const StringVector &, const Argument &filesArg, const Argum printProperty("Sampling frequency", track->samplingFrequency(), "Hz"); printProperty("Extension sampling frequency", track->extensionSamplingFrequency(), "Hz"); printProperty("Sample count", track->sampleCount()); + printProperty("Creation time", track->creationTime()); + printProperty("Modification time", track->modificationTime()); cout << endl; } } else { @@ -794,10 +832,10 @@ void displayTagInfo(const StringVector ¶meterValues, const Argument &filesAr } } -void setTagInfo(const StringVector ¶meterValues, const Argument &filesArg, const Argument &removeOtherFieldsArg, - const Argument &treatUnknownFilesAsMp3FilesArg, const Argument &id3v1UsageArg, const Argument &id3v2UsageArg, - const Argument &mergeMultipleSuccessiveTagsArg, const Argument &id3v2VersionArg, const Argument &encodingArg, - const Argument &removeTargetsArg, +void setTagInfo(const StringVector ¶meterValues, const Argument &filesArg, const Argument &docTitleArg, + const Argument &removeOtherFieldsArg, const Argument &treatUnknownFilesAsMp3FilesArg, + const Argument &id3v1UsageArg, const Argument &id3v2UsageArg, const Argument &mergeMultipleSuccessiveTagsArg, + const Argument &id3v2VersionArg, const Argument &encodingArg, const Argument &removeTargetsArg, const Argument &attachmentsArg, const Argument &removeExistingAttachmentsArg, const Argument &verboseArg) { CMD_UTILS_START_CONSOLE; @@ -806,7 +844,7 @@ void setTagInfo(const StringVector ¶meterValues, const Argument &filesArg, c return; } auto fields = parseFieldDenotations(parameterValues, false); - if(fields.empty() && !attachmentsArg.valueCount()) { + if(fields.empty() && attachmentsArg.values().empty() && docTitleArg.values().empty()) { cout << "Error: No fields/attachments have been specified." << endl; return; } @@ -875,6 +913,23 @@ void setTagInfo(const StringVector ¶meterValues, const Argument &filesArg, c // create new tags according to settings fileInfo.createAppropriateTags(treatUnknownFilesAsMp3FilesArg.isPresent(), id3v1Usage, id3v2Usage, mergeMultipleSuccessiveTagsArg.isPresent(), !id3v2VersionArg.isPresent(), id3v2Version, requiredTargets); auto container = fileInfo.container(); + bool docTitleModified = false; + if(!docTitleArg.values().empty()) { + if(container) { + size_t segmentIndex = 0, segmentCount = container->titles().size(); + for(const auto &newTitle : docTitleArg.values()) { + if(segmentIndex < segmentCount) { + container->setTitle(newTitle, segmentIndex); + docTitleModified = true; + } else { + cout << "Warning: The specified document title \"" << newTitle << "\" can not be set because the file has not that many segments or document titles are not supported." << endl; + } + ++segmentIndex; + } + } else { + cout << "Warning: Setting the document title is not supported for the file." << endl; + } + } fileInfo.tags(tags); if(!tags.empty()) { // iterate through all tags @@ -990,7 +1045,7 @@ void setTagInfo(const StringVector ¶meterValues, const Argument &filesArg, c // notification will be added by the file info automatically } } - if(!tags.empty() || attachmentsModified) { + if(!tags.empty() || docTitleModified || attachmentsModified) { try { // save parsing notifications because notifications of sub objects like tags, tracks, ... will be gone after applying changes fileInfo.gatherRelatedNotifications(notifications); @@ -1001,6 +1056,8 @@ void setTagInfo(const StringVector ¶meterValues, const Argument &filesArg, c } catch(const ApplicationUtilities::Failure &) { cout << "Error: Failed to apply changes." << endl; } + } else { + cout << "Warning: No changed to be applied." << endl; } } catch(const ios_base::failure &) { cout << "Error: An IO failure occured when reading/writing the file \"" << file << "\"." << endl; diff --git a/cli/mainfeatures.h b/cli/mainfeatures.h index 18e7070..3c24a7d 100644 --- a/cli/mainfeatures.h +++ b/cli/mainfeatures.h @@ -18,7 +18,7 @@ void printFieldNames(const ApplicationUtilities::StringVector ¶meterValues); void displayFileInfo(const ApplicationUtilities::StringVector &, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg); void generateFileInfo(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &inputFileArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &validateArg); void displayTagInfo(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg); -void setTagInfo(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &removeOtherFieldsArg, const ApplicationUtilities::Argument &treatUnknownFilesAsMp3FilesArg, const ApplicationUtilities::Argument &id3v1UsageArg, const ApplicationUtilities::Argument &id3v2UsageArg, const ApplicationUtilities::Argument &mergeMultipleSuccessiveTagsArg, const ApplicationUtilities::Argument &id3v2VersionArg, const ApplicationUtilities::Argument &encodingArg, const ApplicationUtilities::Argument &removeTargetsArg, const ApplicationUtilities::Argument &attachmentsArg, const ApplicationUtilities::Argument &removeExistingAttachmentsArg, const ApplicationUtilities::Argument &verboseArg); +void setTagInfo(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &docTitleArg, const ApplicationUtilities::Argument &removeOtherFieldsArg, const ApplicationUtilities::Argument &treatUnknownFilesAsMp3FilesArg, const ApplicationUtilities::Argument &id3v1UsageArg, const ApplicationUtilities::Argument &id3v2UsageArg, const ApplicationUtilities::Argument &mergeMultipleSuccessiveTagsArg, const ApplicationUtilities::Argument &id3v2VersionArg, const ApplicationUtilities::Argument &encodingArg, const ApplicationUtilities::Argument &removeTargetsArg, const ApplicationUtilities::Argument &attachmentsArg, const ApplicationUtilities::Argument &removeExistingAttachmentsArg, const ApplicationUtilities::Argument &verboseArg); void extractField(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &inputFileArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &verboseArg); void removeBackupFiles(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &recursiveArg); diff --git a/misc/htmlinfo.cpp b/misc/htmlinfo.cpp index e03cd3f..53c58bc 100644 --- a/misc/htmlinfo.cpp +++ b/misc/htmlinfo.cpp @@ -640,8 +640,14 @@ QByteArray generateInfo(const MediaFileInfo &file, NotificationList &originalNot res.append(mkExtendedSection(QStringLiteral("containerMore"))); } if(container) { - if(!container->title().empty()) { - res.append(mkRow(QCoreApplication::translate("HtmlInfo", "Title"), qstr(container->title()))); + size_t segmentIndex = 0; + for(const auto &title : container->titles()) { + if(segmentIndex) { + res.append(mkRow(QCoreApplication::translate("HtmlInfo", "Title (segment %1)").arg(++segmentIndex), qstr(title))); + } else { + ++segmentIndex; + res.append(mkRow(QCoreApplication::translate("HtmlInfo", "Title"), qstr(title))); + } } if(container->version()) { res.append(mkRow(QCoreApplication::translate("HtmlInfo", "Version"), QString::number(container->version())));