Merge branch 'refs/heads/cli'
This commit is contained in:
commit
a60bbe532a
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<typename intType>
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())));
|
||||
|
|
Loading…
Reference in New Issue