Allow renaming file via `set --script …`

This allows doing what the GUI renaming utilitiy does on the command-line.
If only renaming is wanted
This commit is contained in:
Martchus 2023-11-10 15:44:24 +01:00
parent b5f9158106
commit 72dcbbbd81
4 changed files with 47 additions and 8 deletions

View File

@ -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

View File

@ -494,6 +494,7 @@ public:
private:
static void addWarnings(Diagnostics &diag, const std::string &context, const QList<QQmlError> &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);

View File

@ -20,6 +20,8 @@
#include <c++utilities/conversion/binaryconversion.h>
#include <c++utilities/conversion/conversionexception.h>
#include <c++utilities/conversion/stringbuilder.h>
#include <c++utilities/io/path.h>
#include <qtutilities/misc/compat.h>
@ -33,6 +35,7 @@
#include <QJSValueIterator>
#include <QRegularExpression>
#include <filesystem>
#include <iostream>
#include <limits>
#include <type_traits>
@ -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<std::size_t>(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

View File

@ -193,7 +193,8 @@ class MediaFileInfoObject : public QObject {
Q_PROPERTY(QList<TagObject *> 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<TagObject *> m_tags;
bool m_quiet;
};
inline TagParser::MediaFileInfo &MediaFileInfoObject::fileInfo()