Allow dealing with multiple fields values in JavaScript

This commit is contained in:
Martchus 2023-07-30 17:20:23 +02:00
parent 9cb8702d13
commit c3af3d43e6
4 changed files with 81 additions and 42 deletions

View File

@ -360,8 +360,8 @@ Here are some Bash examples which illustrate getting and setting tag information
- 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.
- The content of fields that are absent in the tag is set to `undefined`. You may also set
the content of fields to `undefined` to delete them (`null` works as well).
- The property for fields that are absent in the tag have an empty array assigned. You may
also assign an empty array to fields to delete them.
- The content of binary fields is exposed as `ArrayBuffer`. Use must also use an `ArrayBuffer`
to set the value of binary fields such as the cover.
- The content of other fields is mostly exposed as `String` or `Number`. Use must also use

View File

@ -218,24 +218,6 @@ QString TagObject::type() const
return Utility::qstr(m_tag.typeName());
}
QJSValue &TagObject::fields()
{
if (!m_fields.isUndefined()) {
return m_fields;
}
static const auto fieldRegex = QRegularExpression(QStringLiteral("\\s(\\w)"));
m_fields = m_engine->newObject();
for (auto field = TagParser::firstKnownField; field != TagParser::KnownField::Invalid; field = TagParser::nextKnownField(field)) {
if (!m_tag.supportsField(field)) {
continue;
}
if (const auto propertyName = propertyNameForField(field); !propertyName.isEmpty()) {
m_fields.setProperty(propertyName, m_engine->newQObject(new TagValueObject(m_tag.value(field), m_engine, this)));
}
}
return m_fields;
}
QString TagObject::propertyNameForField(TagParser::KnownField field)
{
static const auto reverseMapping = [] {
@ -254,6 +236,30 @@ QString TagObject::propertyNameForField(TagParser::KnownField field)
return reverseMapping.value(field, QString());
}
QJSValue &TagObject::fields()
{
if (!m_fields.isUndefined()) {
return m_fields;
}
static const auto fieldRegex = QRegularExpression(QStringLiteral("\\s(\\w)"));
m_fields = m_engine->newObject();
for (auto field = TagParser::firstKnownField; field != TagParser::KnownField::Invalid; field = TagParser::nextKnownField(field)) {
if (!m_tag.supportsField(field)) {
continue;
}
if (const auto propertyName = propertyNameForField(field); !propertyName.isEmpty()) {
const auto values = m_tag.values(field);
const auto size = Utility::sizeToInt<quint32>(values.size());
auto array = m_engine->newArray(size);
for (auto i = quint32(); i != size; ++i) {
array.setProperty(i, m_engine->newQObject(new TagValueObject(m_tag.value(field), m_engine, this)));
}
m_fields.setProperty(propertyName, array);
}
}
return m_fields;
}
void TagObject::applyChanges()
{
auto context = !m_tag.target().isEmpty() || m_tag.type() == TagParser::TagType::MatroskaTag
@ -273,22 +279,35 @@ void TagObject::applyChanges()
continue;
}
auto propertyValue = m_fields.property(propertyName);
auto fieldDisplayName = Settings::KnownFieldModel::fieldName(field);
if (const auto *const tagValueObj = qobject_cast<const TagValueObject *>(propertyValue.toQObject())) {
if (!tagValueObj->isInitial()) {
auto value = tagValueObj->toTagValue(encoding);
m_diag.emplace_back(TagParser::DiagLevel::Debug,
value.isNull()
? CppUtilities::argsToString(" - delete '", fieldDisplayName, '\'')
: CppUtilities::argsToString(" - change '", fieldDisplayName, "' from '",
tagValueObj->initialContent().toString().toStdString(), "' to '", tagValueObj->content().toString().toStdString(), '\''),
std::string());
m_tag.setValue(field, std::move(value));
}
} else {
m_engine->throwError(QJSValue::TypeError, QStringLiteral("invalid value assigned to field ") + propertyName);
if (!propertyValue.isArray()) {
m_engine->throwError(QJSValue::TypeError, QStringLiteral("non-array assigned to field ") + propertyName);
goto end;
}
const auto size = propertyValue.property(QStringLiteral("length")).toUInt();
auto values = std::vector<TagParser::TagValue>();
values.reserve(size);
for (auto i = quint32(); i != size; ++i) {
const auto *const tagValueObj = qobject_cast<const TagValueObject *>(propertyValue.property(i).toQObject());
if (!tagValueObj) {
m_engine->throwError(QJSValue::TypeError, QStringLiteral("invalid value present in value-array of field ") + propertyName);
goto end;
}
if (tagValueObj->isInitial()) {
continue;
}
const auto &value = values.emplace_back(tagValueObj->toTagValue(encoding));
m_diag.emplace_back(TagParser::DiagLevel::Debug,
value.isNull() ? CppUtilities::argsToString(" - delete ", propertyName.toStdString(), '[', i, ']')
: (tagValueObj->initialContent().isNull() ? CppUtilities::argsToString(
" - set ", propertyName.toStdString(), '[', i, "] to '", tagValueObj->content().toString().toStdString(), '\'')
: CppUtilities::argsToString(" - change ", propertyName.toStdString(), '[',
i, "] from '", tagValueObj->initialContent().toString().toStdString(),
"' to '", tagValueObj->content().toString().toStdString(), '\'')),
std::string());
}
m_tag.setValues(field, values);
}
end:;
}
MediaFileInfoObject::MediaFileInfoObject(TagParser::MediaFileInfo &mediaFileInfo, TagParser::Diagnostics &diag, QJSEngine *engine, QObject *parent)

View File

@ -34,9 +34,9 @@ void parseFileName(const QString &fileName, QString &title, int &trackNumber);
QString printModel(QAbstractItemModel *model);
void printModelIndex(const QModelIndex &index, QString &res, int level);
constexpr int sizeToInt(std::size_t size)
template <typename IntType = int> constexpr IntType sizeToInt(std::size_t size)
{
return size > std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : static_cast<int>(size);
return size > std::numeric_limits<IntType>::max() ? std::numeric_limits<IntType>::max() : static_cast<IntType>(size);
}
constexpr int containerSizeToInt(typename QStringList::size_type size)

View File

@ -14,13 +14,20 @@ export function main(file) {
return false;
}
const mainTextFields = ["title", "artist", "album"];
const personalFields = ["comment", "rating"];
function isString(value) {
return typeof(value) === "string" || value instanceof String;
}
function changeTagFields(tag) {
// log supported fields
// log tag type and supported fields
const fields = tag.fields;
utility.diag("debug", tag.type, "tag");
utility.diag("debug", Object.keys(fields).join(", "), "supported fields");
// log tag type and fields for debugging purposes
// log fields for debugging purposes
for (const [key, value] of Object.entries(fields)) {
const content = value.content;
if (content !== undefined && content != null && !(content instanceof ArrayBuffer)) {
@ -28,9 +35,22 @@ function changeTagFields(tag) {
}
}
// change some fields
fields.title.content = "foo";
fields.artist.content = "bar";
// apply fixes to main text fields
for (const key of mainTextFields) {
for (const value of fields[key]) {
if (isString(value.content)) {
value.content = value.content.trim();
value.content = utility.fixUmlauts(value.content);
value.content = utility.formatName(value.content);
}
}
}
// ensure personal fields are cleared
for (const key of personalFields) {
fields[key] = [];
}
// set some other fields
fields.track.content = "4/17";
fields.comment.clear();
}