Add --output-files options for setting tags
This commit is contained in:
parent
c525a6acd9
commit
1a7f87e21d
|
@ -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});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ struct SetTagInfoArgs
|
|||
ApplicationUtilities::Argument indexPosArg;
|
||||
ApplicationUtilities::Argument forceRewriteArg;
|
||||
ApplicationUtilities::Argument valuesArg;
|
||||
ApplicationUtilities::Argument outputFilesArg;
|
||||
ApplicationUtilities::Argument setTagInfoArg;
|
||||
};
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue