tageditor/application/main.cpp

235 lines
16 KiB
C++
Raw Normal View History

2016-08-29 20:23:27 +02:00
#include "../cli/mainfeatures.h"
#if defined(TAGEDITOR_GUI_QTWIDGETS)
2018-03-07 01:18:01 +01:00
#include "../gui/initiate.h"
#include "./knownfieldmodel.h"
2016-08-29 20:23:27 +02:00
#endif
#include "resources/config.h"
#if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
2018-03-07 01:18:01 +01:00
#include <qtutilities/misc/conversion.h>
#include <qtutilities/resources/qtconfigarguments.h>
2016-08-29 20:23:27 +02:00
#else
2018-03-07 01:18:01 +01:00
#include <c++utilities/application/fakeqtconfigarguments.h>
2016-08-29 20:23:27 +02:00
#endif
#include <c++utilities/application/commandlineutils.h>
2020-04-16 19:52:54 +02:00
#include <c++utilities/io/ansiescapecodes.h>
2019-06-10 22:49:46 +02:00
#include <c++utilities/misc/parseerror.h>
2016-08-29 20:23:27 +02:00
2018-03-07 01:18:01 +01:00
#include <functional>
2016-08-29 20:23:27 +02:00
#include <iostream>
#include <memory>
using namespace std;
using namespace std::placeholders;
2019-06-10 22:49:46 +02:00
using namespace CppUtilities;
#if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
2019-06-10 22:49:46 +02:00
using namespace QtUtilities;
#endif
2016-08-29 20:23:27 +02:00
namespace Cli {
2018-03-07 01:18:01 +01:00
SetTagInfoArgs::SetTagInfoArgs(Argument &filesArg, Argument &verboseArg)
: filesArg(filesArg)
, verboseArg(verboseArg)
2021-05-27 18:56:42 +02:00
, quietArg("quiet", 'q', "suppress printing progress information")
2018-03-14 18:44:33 +01:00
, docTitleArg("doc-title", 'd', "specifies the document title (has no affect if not supported by the container)",
{ "title of first segment", "title of second segment" })
2018-03-07 01:18:01 +01:00
, removeOtherFieldsArg(
"remove-other-fields", '\0', "removes ALL fields where no value has been provided for (to remove a specific field use eg. \"album=\")")
2018-03-14 18:44:33 +01:00
, treatUnknownFilesAsMp3FilesArg("treat-unknown-as-mp3", '\0', "if present unknown files will be treated as MP3 files")
2018-03-07 01:18:01 +01:00
, id3v1UsageArg("id3v1-usage", '\0',
"specifies the ID3v1 usage (only used when already present by default); only relevant when dealing with MP3 files (or files treated as "
2018-03-14 18:44:33 +01:00
"such)",
{ "always/keepexisting/never" })
2018-03-07 01:18:01 +01:00
, id3v2UsageArg("id3v2-usage", '\0',
2018-03-14 18:44:33 +01:00
"specifies the ID3v2 usage (always used by default); only relevant when dealing with MP3 files (or files treated as such)",
{ "always/keepexisting/never" })
2018-03-07 01:18:01 +01:00
, mergeMultipleSuccessiveTagsArg("merge-successive-tags", '\0', "if present multiple successive ID3v2 tags will be merged")
2018-03-14 18:44:33 +01:00
, id3v2VersionArg("id3v2-version", '\0', "forces a specific ID3v2 version to be used; only relevant when ID3v2 is used", { "1/2/3/4" })
2018-03-07 01:18:01 +01:00
, id3InitOnCreateArg("id3-init-on-create", '\0',
"indicates whether to initialize newly created ID3 tags (according to specified usage) with the values of the already present ID3 tags")
, id3TransferOnRemovalArg("id3-transfer-on-removal", '\0',
"indicates whether values of removed ID3 tags (according to specified usage) should be transferred to remaining ID3 tags (no values will be "
2018-03-07 01:18:01 +01:00
"overwritten)")
2018-03-14 18:44:33 +01:00
, encodingArg("encoding", '\0', "specifies the preferred encoding", { "latin1/utf8/utf16le/utf16be" })
2018-03-07 01:18:01 +01:00
, removeTargetArg("remove-target", '\0', "removes all tags with the specified target")
2018-03-14 18:44:33 +01:00
, addAttachmentArg("add-attachment", '\0', "adds a new attachment", { "path=some/file", "name=Some name", "desc=Some desc", "mime=mime/type" })
, updateAttachmentArg(
"update-attachment", '\0', "updates an existing attachment", { "path=some/file", "name=Some name", "desc=Some desc", "mime=mime/type" })
2018-03-07 01:18:01 +01:00
, removeAttachmentArg("remove-attachment", '\0', "removes an existing attachment")
, removeExistingAttachmentsArg(
"remove-existing-attachments", 'r', "removes ALL existing attachments (to remove a specific attachment use --remove-attachment)")
2018-03-14 18:44:33 +01:00
, minPaddingArg("min-padding", '\0',
"specifies the minimum padding before the media data (enforces rewriting the file is the padding would be less)",
{ "min. padding in byte" })
, maxPaddingArg("max-padding", '\0',
"specifies the maximum padding before the media data (enforces rewriting the file is the padding would be more)",
{ "max. padding in byte" })
, prefPaddingArg("preferred-padding", '\0', "specifies the preferred padding before the media data (used when the file is rewritten)",
{ "preferred padding in byte" })
, tagPosValueArg("value", '\0', "specifies the position, either front, back or current", { "front/back/current" })
2018-03-07 01:18:01 +01:00
, forceTagPosArg("force", '\0', "forces the specified position even if the file needs to be rewritten")
, tagPosArg("tag-pos", '\0', "specifies the preferred tag position")
2018-03-14 18:44:33 +01:00
, indexPosValueArg("value", '\0', "specifies the position, either front, back or current", { "front/back/current" })
2018-03-07 01:18:01 +01:00
, forceIndexPosArg("force", '\0', "forces the specified position even if the file needs to be rewritten")
, indexPosArg("index-pos", '\0', "specifies the preferred index position")
, forceRewriteArg(
"force-rewrite", '\0', "forces the file to rewritten from the scratch which ensures a backup is created and the preferred padding is used")
2018-03-14 18:44:33 +01:00
, valuesArg("values", 'n', "specifies the values to be set", { "title=foo", "album=bar", "cover=/path/to/file" })
, outputFilesArg("output-files", 'o', "specifies the output files; if present, the files specified with --files will not be modified",
{ "path 1", "path 2" })
2018-03-07 01:18:01 +01:00
, backupDirArg("temp-dir", '\0', "specifies the directory for temporary/backup files", { "path" })
2018-03-13 19:20:41 +01:00
, layoutOnlyArg("layout-only", 'l', "confirms layout-only changes")
, preserveModificationTimeArg("preserve-modification-time", '\0', "preserves the file's modification time")
2018-03-07 01:18:01 +01:00
, setTagInfoArg("set", 's', "sets the specified tag information and attachments")
2016-08-29 20:23:27 +02:00
{
2017-09-29 18:26:20 +02:00
docTitleArg.setRequiredValueCount(Argument::varValueCount);
2016-08-29 20:23:27 +02:00
id3v1UsageArg.setPreDefinedCompletionValues("always keepexisting never");
id3v2UsageArg.setPreDefinedCompletionValues("always keepexisting never");
id3v2VersionArg.setPreDefinedCompletionValues("1 2 3 4");
encodingArg.setPreDefinedCompletionValues("latin1 utf8 utf16le utf16be");
2017-09-29 18:26:20 +02:00
removeTargetArg.setRequiredValueCount(Argument::varValueCount);
removeTargetArg.setConstraints(0, Argument::varValueCount);
addAttachmentArg.setRequiredValueCount(Argument::varValueCount);
addAttachmentArg.setConstraints(0, Argument::varValueCount);
2016-08-29 20:23:27 +02:00
addAttachmentArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
addAttachmentArg.setPreDefinedCompletionValues("name id path desc mime");
2017-09-29 18:26:20 +02:00
updateAttachmentArg.setRequiredValueCount(Argument::varValueCount);
updateAttachmentArg.setConstraints(0, Argument::varValueCount);
2016-08-29 20:23:27 +02:00
updateAttachmentArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
updateAttachmentArg.setPreDefinedCompletionValues("name id path desc mime");
removeAttachmentArg.setRequiredValueCount(1);
2018-03-07 01:18:01 +01:00
removeAttachmentArg.setValueNames({ "name=to_remove" });
2016-08-29 20:23:27 +02:00
removeAttachmentArg.setCombinable(true);
2017-09-29 18:26:20 +02:00
removeAttachmentArg.setConstraints(0, Argument::varValueCount);
2016-08-29 20:23:27 +02:00
removeAttachmentArg.setPreDefinedCompletionValues("name id");
removeAttachmentArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
tagPosValueArg.setPreDefinedCompletionValues("front back current");
tagPosValueArg.setImplicit(true);
tagPosValueArg.setRequired(true);
2018-03-07 01:18:01 +01:00
tagPosArg.setSubArguments({ &tagPosValueArg, &forceTagPosArg });
2016-08-29 20:23:27 +02:00
indexPosValueArg.setPreDefinedCompletionValues("front back current");
indexPosValueArg.setImplicit(true);
indexPosValueArg.setRequired(true);
2018-03-13 19:20:41 +01:00
indexPosArg.setExample(PROJECT_NAME " set comment=\"with faststart\" --index-pos front --force --layout-only -f /some/dir/*.m4a");
2018-03-07 01:18:01 +01:00
indexPosArg.setSubArguments({ &indexPosValueArg, &forceIndexPosArg });
2017-09-29 18:26:20 +02:00
valuesArg.setRequiredValueCount(Argument::varValueCount);
2016-08-29 20:23:27 +02:00
valuesArg.setImplicit(true);
valuesArg.setPreDefinedCompletionValues(Cli::fieldNamesForSet);
valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
2017-09-29 18:26:20 +02:00
outputFilesArg.setRequiredValueCount(Argument::varValueCount);
2016-08-29 20:23:27 +02:00
setTagInfoArg.setCallback(std::bind(Cli::setTagInfo, std::cref(*this)));
2018-03-07 01:18:01 +01:00
setTagInfoArg.setExample(PROJECT_NAME
" set title=\"Title of \"{1st,2nd,3rd}\" file\" title=\"Title of \"{4..16}\"th file\" album=\"The Album\" -f /some/dir/*.m4a\n" PROJECT_NAME
2019-02-13 18:15:10 +01:00
" set title=\"Title for track \"{1..25} album=\"Album with 25 tracks on one disk\" track+=1/25 disk=1/1 -f *.m4a" PROJECT_NAME
2018-03-07 01:18:01 +01:00
" set mkv:FOO=bar1 mp4:©foo=bar2 -f file.mkv file.m4a\n" PROJECT_NAME
" set title0=\"Title for both files\" album1=\"Album for 2nd file\" -f file1.ogg file2.mp3\n" PROJECT_NAME
" set target-level=30 target-tracks=3134325680 title=\"Title for track 3134325680\" \\\n"
2019-01-02 17:09:13 +01:00
" --remove-targets target-level=50 , target-level=30 -f file.mka\n" PROJECT_NAME
" set mkv:CUSTOM_FIELD=\"Matroska-only\" vorbis:CUSTOM_FIELD=\"Vorbis-only\" mp4:©ust=\"MP4-only\" \\\n"
" -f file.mkv file.ogg file.m4a\n"
2018-03-07 01:18:01 +01:00
"For more examples and detailed descriptions see " APP_URL "#writing-tags");
2021-06-03 23:18:20 +02:00
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, &backupDirArg, &layoutOnlyArg, &preserveModificationTimeArg,
&verboseArg, &quietArg, &outputFilesArg });
2016-08-29 20:23:27 +02:00
}
2018-03-07 01:18:01 +01:00
} // namespace Cli
2016-08-29 20:23:27 +02:00
int main(int argc, char *argv[])
{
// setup argument parser
ArgumentParser parser;
2016-12-18 17:23:45 +01:00
CMD_UTILS_CONVERT_ARGS_TO_UTF8;
2016-08-29 20:23:27 +02:00
SET_APPLICATION_INFO;
QT_CONFIG_ARGUMENTS qtConfigArgs;
HelpArgument helpArg(parser);
2017-10-17 00:01:58 +02:00
NoColorArgument noColorArg;
2018-03-07 01:18:01 +01:00
ConfigValueArgument timeSpanFormatArg("time-span-format", '\0', "specifies the output format for time spans", { "measures/colons/seconds" });
timeSpanFormatArg.setPreDefinedCompletionValues("measures colons seconds");
2016-08-29 20:23:27 +02:00
// verbose option
2021-05-27 18:56:42 +02:00
ConfigValueArgument verboseArg("verbose", 'v', "be verbose, print debug and info messages");
2016-08-29 20:23:27 +02:00
// input/output file/files
2018-03-14 18:44:33 +01:00
ConfigValueArgument fileArg("file", 'f', "specifies the path of the file to be opened", { "path" });
ConfigValueArgument defaultFileArg(fileArg);
2016-08-29 20:23:27 +02:00
defaultFileArg.setImplicit(true);
fileArg.setRequired(true);
2018-03-14 18:44:33 +01:00
ConfigValueArgument filesArg("files", 'f', "specifies the path of the file(s) to be opened", { "path 1", "path 2" });
2017-09-29 18:26:20 +02:00
filesArg.setRequiredValueCount(Argument::varValueCount);
2018-03-14 18:44:33 +01:00
ConfigValueArgument outputFileArg("output-file", 'o', "specifies the path of the output file", { "path" });
2016-08-29 20:23:27 +02:00
// print field names
2018-03-14 18:44:33 +01:00
OperationArgument printFieldNamesArg("print-field-names", '\0', "lists available field names, track attribute names and modifier");
2016-08-29 20:23:27 +02:00
printFieldNamesArg.setCallback(Cli::printFieldNames);
// display general file info
2018-03-14 18:44:33 +01:00
OperationArgument displayFileInfoArg("info", 'i', "displays general file information", PROJECT_NAME " info -f /some/dir/*.m4a");
2016-08-29 20:23:27 +02:00
displayFileInfoArg.setCallback(std::bind(Cli::displayFileInfo, _1, std::cref(filesArg), std::cref(verboseArg)));
2018-03-07 01:18:01 +01:00
displayFileInfoArg.setSubArguments({ &filesArg, &verboseArg });
2016-08-29 20:23:27 +02:00
// display tag info
2018-03-14 18:44:33 +01:00
ConfigValueArgument fieldsArg("fields", 'n', "specifies the field names to be displayed", { "title", "album", "artist", "trackpos" });
2017-09-29 18:26:20 +02:00
fieldsArg.setRequiredValueCount(Argument::varValueCount);
2016-08-29 20:23:27 +02:00
fieldsArg.setPreDefinedCompletionValues(Cli::fieldNames);
fieldsArg.setImplicit(true);
2018-03-14 18:44:33 +01:00
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");
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 });
2016-08-29 20:23:27 +02:00
// set tag info
Cli::SetTagInfoArgs setTagInfoArgs(filesArg, verboseArg);
// extract cover
2018-03-14 18:44:33 +01:00
ConfigValueArgument fieldArg("field", 'n', "specifies the field to be extracted", { "field name" });
2016-08-29 20:23:27 +02:00
fieldArg.setImplicit(true);
2018-03-14 18:44:33 +01:00
ConfigValueArgument attachmentArg("attachment", 'a', "specifies the attachment to be extracted", { "id=..." });
OperationArgument extractFieldArg("extract", 'e',
2018-03-07 01:18:01 +01:00
"saves the value of the specified field (eg. cover or other binary field) or attachment to the specified file or writes it to stdout if no "
"output file has been specified");
extractFieldArg.setSubArguments({ &fieldArg, &attachmentArg, &fileArg, &outputFileArg, &verboseArg });
extractFieldArg.setExample(PROJECT_NAME " extract cover --output-file the-cover.jpg --file some-file.opus");
2018-03-07 01:18:01 +01:00
extractFieldArg.setCallback(std::bind(
Cli::extractField, std::cref(fieldArg), std::cref(attachmentArg), std::cref(fileArg), std::cref(outputFileArg), std::cref(verboseArg)));
2018-01-15 00:14:53 +01:00
// export to JSON
2018-01-26 18:09:53 +01:00
ConfigValueArgument prettyArg("pretty", '\0', "prints with indentation and spacing");
2018-03-14 18:44:33 +01:00
OperationArgument exportArg("export", 'j', "exports the tag information for the specified files to JSON");
2018-03-07 01:18:01 +01:00
exportArg.setSubArguments({ &filesArg, &prettyArg });
2018-01-26 18:09:53 +01:00
exportArg.setCallback(std::bind(Cli::exportToJson, _1, std::cref(filesArg), std::cref(prettyArg)));
2016-08-29 20:23:27 +02:00
// file info
2018-03-14 18:44:33 +01:00
ConfigValueArgument validateArg(
2018-03-07 01:18:01 +01:00
"validate", 'c', "validates the file integrity as accurately as possible; the structure of the file will be parsed completely");
2018-03-14 18:44:33 +01:00
OperationArgument genInfoArg("html-info", '\0', "generates technical information about the specified file as HTML document");
2018-03-07 01:18:01 +01:00
genInfoArg.setSubArguments({ &fileArg, &validateArg, &outputFileArg });
2016-08-29 20:23:27 +02:00
genInfoArg.setCallback(std::bind(Cli::generateFileInfo, _1, std::cref(fileArg), std::cref(outputFileArg), std::cref(validateArg)));
// renaming utility
2018-03-14 18:44:33 +01:00
ConfigValueArgument renamingUtilityArg("renaming-utility", '\0', "launches the renaming utility instead of the main GUI");
2016-08-29 20:23:27 +02:00
// set arguments to parser
qtConfigArgs.qtWidgetsGuiArg().setAbbreviation('\0');
qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&defaultFileArg);
qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&renamingUtilityArg);
2018-03-07 01:18:01 +01:00
parser.setMainArguments({ &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayFileInfoArg, &displayTagInfoArg,
&setTagInfoArgs.setTagInfoArg, &extractFieldArg, &exportArg, &genInfoArg, &timeSpanFormatArg, &noColorArg, &helpArg });
2016-08-29 20:23:27 +02:00
// parse given arguments
2019-06-10 22:49:46 +02:00
parser.parseArgs(argc, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::ExitOnFailure);
// start GUI/CLI
2018-03-07 01:18:01 +01:00
if (qtConfigArgs.areQtGuiArgsPresent()) {
#if defined(TAGEDITOR_GUI_QTWIDGETS)
2018-03-07 01:18:01 +01:00
return QtGui::runWidgetsGui(argc, argv, qtConfigArgs,
2019-06-12 20:47:44 +02:00
defaultFileArg.isPresent() && !defaultFileArg.values().empty() ? fromNativeFileName(defaultFileArg.values().front()) : QString(),
2018-03-07 01:18:01 +01:00
renamingUtilityArg.isPresent());
2016-08-29 20:23:27 +02:00
#else
CMD_UTILS_START_CONSOLE;
2020-04-24 23:26:52 +02:00
cerr << EscapeCodes::Phrases::Error
<< "The tag editor has not been built with Qt widgets GUI support. Use --help to show the options of the CLI." << endl;
2017-09-29 17:20:54 +02:00
#endif
} else {
// apply general CLI config (concerns currently only the default time span output format)
Cli::applyGeneralConfig(timeSpanFormatArg);
// invoke specified CLI operation via callbacks
parser.invokeCallbacks();
2017-03-29 00:57:01 +02:00
}
2016-08-29 20:23:27 +02:00
return 0;
}