diff --git a/README.md b/README.md index 75b1931..274f617 100644 --- a/README.md +++ b/README.md @@ -369,12 +369,13 @@ Here are some Bash examples which illustrate getting and setting tag information - The script needs to be ECMAScript as supported by the Qt framework. - This feature requires the tag editor to be configured with Qt QML as JavaScript provider at compile time. Checkout the build instructions under "Building with Qt GUI" for details. - - The script needs to export a `main()` function. This function gets executed for every file and - passed an object representing this file as first argument. + - The script needs to export a `main()` function. This function is invoked for every file and + passed an object representing the current file as first argument. - Checkout the file `testfiles/set-tags.js` in this repository for an example that applies basic fixes and tries to fetch lyrics and cover art. - - The option `--pedantic debug` is not required but useful for debugging. You may also add - `--script-settings dryRun=1` and use that setting within the script as shown in the example. + - For debugging, the option `--pedantic debug` is very useful. You may also add + `--script-settings dryRun=1` and check for that setting within the script as shown in the + mentioned example script. - Common tag fields are exposed as object properties as shown in the mentioned example. - Only properties for fields that are supported by the tag are added to the "fields" object. - Adding properties of unsupported fields manually does not work; those will just be ignored. @@ -394,6 +395,11 @@ Here are some Bash examples which illustrate getting and setting tag information - The script is executed before any other modifications are applied. So if you also specify values as usual (via `--values`) then these values have precedence over values set by the script. + - It is also possible to rename the file (via e.g. `file.rename(newPath)`). This will be done + immediately and also if `main()` returns a falsy value (so it is possible to only rename a + file without modifying it by returning a falsy value). If the specified path is relative, it + is interpreted relative to current directory of the file (and *not* to the current working + directory of the tag editor). ##### Further useful commands * Let the tag editor return with a non-zero exit code even if only non-fatal problems have been encountered diff --git a/cli/mainfeatures.cpp b/cli/mainfeatures.cpp index 13a48b4..a8e3073 100644 --- a/cli/mainfeatures.cpp +++ b/cli/mainfeatures.cpp @@ -494,6 +494,7 @@ public: private: static void addWarnings(Diagnostics &diag, const std::string &context, const QList &warnings); + const SetTagInfoArgs &args; int argc; QCoreApplication app; Diagnostics diag; @@ -510,7 +511,8 @@ private: * - Logs status/problems directly in accordance with other parts of the CLI. */ JavaScriptProcessor::JavaScriptProcessor(const SetTagInfoArgs &args) - : argc(0) + : args(args) + , argc(0) , app(argc, nullptr) , utility(new UtilityObject(&engine)) { @@ -567,7 +569,7 @@ JavaScriptProcessor::JavaScriptProcessor(const SetTagInfoArgs &args) */ QJSValue JavaScriptProcessor::callMain(MediaFileInfo &mediaFileInfo, Diagnostics &diag) { - auto fileInfoObject = MediaFileInfoObject(mediaFileInfo, diag, &engine); + auto fileInfoObject = MediaFileInfoObject(mediaFileInfo, diag, &engine, args.quietArg.isPresent()); auto fileInfoObjectValue = engine.newQObject(&fileInfoObject); auto context = argsToString("executing JavaScript for ", mediaFileInfo.fileName()); utility->setDiag(&context, &diag); diff --git a/cli/scriptapi.cpp b/cli/scriptapi.cpp index 6b22493..9316143 100644 --- a/cli/scriptapi.cpp +++ b/cli/scriptapi.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include @@ -33,6 +35,7 @@ #include #include +#include #include #include #include @@ -558,11 +561,13 @@ void TagObject::applyChanges() } } -MediaFileInfoObject::MediaFileInfoObject(TagParser::MediaFileInfo &mediaFileInfo, TagParser::Diagnostics &diag, QJSEngine *engine, QObject *parent) +MediaFileInfoObject::MediaFileInfoObject( + TagParser::MediaFileInfo &mediaFileInfo, TagParser::Diagnostics &diag, QJSEngine *engine, bool quiet, QObject *parent) : QObject(parent) , m_f(mediaFileInfo) , m_diag(diag) , m_engine(engine) + , m_quiet(quiet) { } @@ -621,4 +626,27 @@ void MediaFileInfoObject::applyChanges() } } +bool MediaFileInfoObject::rename(const QString &newPath) +{ + const auto from = m_f.path(); + const auto fromNative = std::filesystem::path(CppUtilities::makeNativePath(from)); + const auto toRelUtf8 = newPath.toUtf8(); + const auto toRelView = CppUtilities::PathStringView(toRelUtf8.data(), static_cast(toRelUtf8.size())); + const auto toNative = fromNative.parent_path().append(CppUtilities::makeNativePath(toRelView)); + const auto toView = CppUtilities::extractNativePath(toNative.native()); + try { + m_f.stream().close(); + std::filesystem::rename(fromNative, toNative); + m_f.reportPathChanged(toView); + m_f.stream().open(m_f.path(), std::ios_base::in | std::ios_base::out | std::ios_base::binary); + } catch (const std::runtime_error &e) { + m_diag.emplace_back(TagParser::DiagLevel::Critical, e.what(), CppUtilities::argsToString("renaming \"", from, "\" to \"", toView)); + return false; + } + if (!m_quiet) { + std::cout << " - Renamed \"" << from << "\" to \"" << toView << "\"\n"; + } + return true; +} + } // namespace Cli diff --git a/cli/scriptapi.h b/cli/scriptapi.h index 525bf13..9492f75 100644 --- a/cli/scriptapi.h +++ b/cli/scriptapi.h @@ -193,7 +193,8 @@ class MediaFileInfoObject : public QObject { Q_PROPERTY(QList tags READ tags) public: - explicit MediaFileInfoObject(TagParser::MediaFileInfo &mediaFileInfo, TagParser::Diagnostics &diag, QJSEngine *engine, QObject *parent = nullptr); + explicit MediaFileInfoObject( + TagParser::MediaFileInfo &mediaFileInfo, TagParser::Diagnostics &diag, QJSEngine *engine, bool quiet, QObject *parent = nullptr); ~MediaFileInfoObject() override; TagParser::MediaFileInfo &fileInfo(); @@ -207,12 +208,14 @@ public: public Q_SLOTS: void applyChanges(); + bool rename(const QString &newPath); private: TagParser::MediaFileInfo &m_f; TagParser::Diagnostics &m_diag; QJSEngine *m_engine; QList m_tags; + bool m_quiet; }; inline TagParser::MediaFileInfo &MediaFileInfoObject::fileInfo()