Add --output-files options for setting tags

This commit is contained in:
Martchus 2016-08-07 22:03:52 +02:00
parent c525a6acd9
commit 1a7f87e21d
4 changed files with 53 additions and 6 deletions

View File

@ -54,6 +54,7 @@ SetTagInfoArgs::SetTagInfoArgs(Argument &filesArg, Argument &verboseArg) :
indexPosArg("index-pos", '\0', "specifies the preferred index position"),
forceRewriteArg("force-rewrite", '\0', "forces the file to rewritten from the scratch"),
valuesArg("values", 'n', "specifies the values to be set"),
outputFilesArg("output-files", 'o', "specifies the output files; if present, the files specified with --files will not be modified"),
setTagInfoArg("set", 's', "sets the specified tag information and attachments")
{
docTitleArg.setCombinable(true);
@ -132,12 +133,15 @@ SetTagInfoArgs::SetTagInfoArgs(Argument &filesArg, Argument &verboseArg) :
valuesArg.setImplicit(true);
valuesArg.setPreDefinedCompletionValues(Cli::fieldNamesForSet);
valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
outputFilesArg.setValueNames({"path 1", "path 2"});
outputFilesArg.setRequiredValueCount(-1);
outputFilesArg.setCombinable(true);
setTagInfoArg.setDenotesOperation(true);
setTagInfoArg.setCallback(std::bind(Cli::setTagInfo, std::cref(*this)));
setTagInfoArg.setSubArguments({&valuesArg, &filesArg, &docTitleArg, &removeOtherFieldsArg, &treatUnknownFilesAsMp3FilesArg, &id3v1UsageArg, &id3v2UsageArg, &id3InitOnCreateArg, &id3TransferOnRemovalArg,
&mergeMultipleSuccessiveTagsArg, &id3v2VersionArg, &encodingArg, &removeTargetArg, &addAttachmentArg, &updateAttachmentArg, &removeAttachmentArg,
&removeExistingAttachmentsArg, &minPaddingArg, &maxPaddingArg, &prefPaddingArg, &tagPosArg,
&indexPosArg, &forceRewriteArg, &verboseArg});
&indexPosArg, &forceRewriteArg, &verboseArg, &outputFilesArg});
}
}

View File

@ -1022,6 +1022,12 @@ void setTagInfo(const SetTagInfoArgs &args)
cerr << "Error: No files have been specified." << endl;
return;
}
if(args.outputFilesArg.isPresent() && args.outputFilesArg.values().size() != args.filesArg.values().size()) {
cerr << "Error: The number of output files does not match the number of input files." << endl;
return;
}
auto &outputFiles = args.outputFilesArg.isPresent() ? args.outputFilesArg.values() : vector<const char *>();
auto currentOutputFile = outputFiles.cbegin(), noMoreOutputFiles = outputFiles.cend();
auto fields = parseFieldDenotations(args.valuesArg, false);
if(fields.empty()
&& (!args.removeTargetArg.isPresent() || args.removeTargetArg.values().empty())
@ -1246,6 +1252,7 @@ void setTagInfo(const SetTagInfoArgs &args)
if(!tags.empty() || docTitleModified || attachmentsModified) {
try {
// save parsing notifications because notifications of sub objects like tags, tracks, ... will be gone after applying changes
fileInfo.setSaveFilePath(currentOutputFile != noMoreOutputFiles ? string(*currentOutputFile) : string());
fileInfo.gatherRelatedNotifications(notifications);
fileInfo.invalidateNotifications();
fileInfo.applyChanges();
@ -1264,7 +1271,11 @@ void setTagInfo(const SetTagInfoArgs &args)
cerr << "Error: An IO failure occured when reading/writing the file \"" << file << "\"." << endl;
}
printNotifications(notifications, "Notifications:", args.verboseArg.isPresent());
++fileIndex;
if(currentOutputFile != noMoreOutputFiles) {
++currentOutputFile;
}
}
}

View File

@ -44,6 +44,7 @@ struct SetTagInfoArgs
ApplicationUtilities::Argument indexPosArg;
ApplicationUtilities::Argument forceRewriteArg;
ApplicationUtilities::Argument valuesArg;
ApplicationUtilities::Argument outputFilesArg;
ApplicationUtilities::Argument setTagInfoArg;
};

View File

@ -35,6 +35,7 @@ class CliTests : public TestFixture
CPPUNIT_TEST(testHandlingOfTargets);
CPPUNIT_TEST(testId3SpecificOptions);
CPPUNIT_TEST(testMultipleFiles);
CPPUNIT_TEST(testOutputFile);
CPPUNIT_TEST(testMultipleValuesPerField);
CPPUNIT_TEST(testHandlingAttachments);
CPPUNIT_TEST(testDisplayingInfo);
@ -52,6 +53,7 @@ public:
void testHandlingOfTargets();
void testId3SpecificOptions();
void testMultipleFiles();
void testOutputFile();
void testMultipleValuesPerField();
void testHandlingAttachments();
void testDisplayingInfo();
@ -130,7 +132,6 @@ void CliTests::testBasicReadingAndWriting()
CPPUNIT_ASSERT(stdout.find("Comment") == string::npos);
CPPUNIT_ASSERT(stdout.find("Genre") == string::npos);
// clear working copies if all tests have been
remove(mkvFile.c_str());
remove(mkvFileBackup.data());
}
@ -175,8 +176,6 @@ void CliTests::testHandlingOfTargets()
"Encoder likely some AAC encoder"
}));
remove(mkvFileBackup.data());
// clear working copies if all tests have been
remove(mkvFile.c_str());
}
@ -313,6 +312,40 @@ void CliTests::testMultipleFiles()
remove((mkvFile1 + ".bak").c_str()), remove((mkvFile2 + ".bak").c_str()), remove((mkvFile3 + ".bak").c_str());
}
/*!
* \brief Tests reading and writing multiple files at once with output files are specified.
*/
void CliTests::testOutputFile()
{
cout << "\nReading and writing multiple files at once with output files specified" << endl;
string stdout, stderr;
const string mkvFile1(workingCopyPath("matroska_wave1/test1.mkv"));
const string mkvFile2(workingCopyPath("matroska_wave1/test2.mkv"));
const char *const args1[] = {"tageditor", "set", "target-level=30", "title=test1", "title=test2","-f", mkvFile1.data(), mkvFile2.data(), "-o", "/tmp/test1.mkv", "/tmp/test2.mkv", nullptr};
TESTUTILS_ASSERT_EXEC(args1);
// original files have not been modified
const char *const args2[] = {"tageditor", "get", "-f", mkvFile1.data(), mkvFile2.data(), nullptr};
TESTUTILS_ASSERT_EXEC(args2);
CPPUNIT_ASSERT(stdout.find("Matroska tag targeting") != string::npos);
CPPUNIT_ASSERT(stdout.find("Title test1") == string::npos);
CPPUNIT_ASSERT(stdout.find("Title test2") == string::npos);
// specified output files contain new titles
const char *const args3[] = {"tageditor", "get", "-f", "/tmp/test1.mkv", "/tmp/test2.mkv", nullptr};
TESTUTILS_ASSERT_EXEC(args3);
CPPUNIT_ASSERT(containsSubstrings(stdout, {
"Matroska tag targeting \"level 30 'track, song, chapter'\"\n"
" Title test1\n",
"Matroska tag targeting \"level 30 'track, song, chapter'\"\n"
" Title test2\n"
}));
remove(mkvFile1.data()), remove(mkvFile2.data());
remove("/tmp/test1.mkv"), remove("/tmp/test2.mkv");
}
/*!
* \brief Tests tagging multiple values per field.
* \remarks Fails because feature has not been implemented yet.
@ -334,7 +367,6 @@ void CliTests::testMultipleValuesPerField()
"Artist test3"
}));
// clear working copies if all tests have been
remove(mkvFile.c_str());
remove((mkvFile + ".bak").c_str());
}
@ -400,7 +432,6 @@ void CliTests::testHandlingAttachments()
CPPUNIT_ASSERT(stdout.find("Attachments:") == string::npos);
CPPUNIT_ASSERT(stdout.find("Name test2.mkv") == string::npos);
// clear working copies if all tests have been
remove(mkvFile1.data());
remove(mkvFile1Backup.data());
}