mirror of
https://github.com/taglib/taglib.git
synced 2026-02-08 00:10:15 -05:00
Set Matroska tags which are not in the property map using complex properties
Also all attached files can be accessed and modified using complex properties.
This commit is contained in:
@ -138,25 +138,23 @@ namespace {
|
||||
if(attachedFile.mediaType().startsWith("image/")) {
|
||||
return "PICTURE";
|
||||
}
|
||||
if(!attachedFile.mediaType().isEmpty()) {
|
||||
return attachedFile.mediaType();
|
||||
}
|
||||
if(!attachedFile.fileName().isEmpty()) {
|
||||
return attachedFile.fileName();
|
||||
}
|
||||
return String::fromLongLong(attachedFile.uid());
|
||||
if(!attachedFile.mediaType().isEmpty()) {
|
||||
return attachedFile.mediaType();
|
||||
}
|
||||
return String::fromULongLong(attachedFile.uid());
|
||||
}
|
||||
|
||||
unsigned long long stringToULongLong(const String &str, bool *ok)
|
||||
bool keyMatchesAttachedFile(const String &key, const Matroska::AttachedFile &attachedFile)
|
||||
{
|
||||
const wchar_t *beginPtr = str.toCWString();
|
||||
wchar_t *endPtr;
|
||||
errno = 0;
|
||||
const unsigned long long value = ::wcstoull(beginPtr, &endPtr, 10);
|
||||
if(ok) {
|
||||
*ok = errno == 0 && endPtr > beginPtr && *endPtr == L'\0';
|
||||
}
|
||||
return value;
|
||||
return !key.isEmpty() && (
|
||||
(key == "PICTURE" && attachedFile.mediaType().startsWith("image/")) ||
|
||||
key == attachedFile.fileName() ||
|
||||
key == attachedFile.mediaType() ||
|
||||
key == String::fromULongLong(attachedFile.uid())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -182,7 +180,7 @@ List<VariantMap> Matroska::File::complexProperties(const String &key) const
|
||||
if(d->attachments) {
|
||||
const auto &attachedFiles = d->attachments->attachedFileList();
|
||||
for(const auto &attachedFile : attachedFiles) {
|
||||
if(keyForAttachedFile(attachedFile) == key) {
|
||||
if(keyMatchesAttachedFile(key, attachedFile)) {
|
||||
VariantMap property;
|
||||
property.insert("data", attachedFile.data());
|
||||
property.insert("mimeType", attachedFile.mediaType());
|
||||
@ -202,8 +200,19 @@ bool Matroska::File::setComplexProperties(const String &key, const List<VariantM
|
||||
return true;
|
||||
}
|
||||
|
||||
attachments(true)->clear();
|
||||
List<AttachedFile> &files = attachments(true)->attachedFiles();
|
||||
for(auto it = files.begin(); it != files.end();) {
|
||||
if(keyMatchesAttachedFile(key, *it)) {
|
||||
it = files.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto &property : value) {
|
||||
if(property.isEmpty())
|
||||
continue;
|
||||
auto mimeType = property.value("mimeType").value<String>();
|
||||
auto data = property.value("data").value<ByteVector>();
|
||||
auto fileName = property.value("fileName").value<String>();
|
||||
@ -220,16 +229,26 @@ bool Matroska::File::setComplexProperties(const String &key, const List<VariantM
|
||||
else if(fileName.isEmpty() && key.find(".") != -1) {
|
||||
fileName = key;
|
||||
}
|
||||
else if(!uid && ((uidKey = stringToULongLong(key, &ok))) && ok) {
|
||||
else if(!uid && ((uidKey = key.toULongLong(&ok))) && ok) {
|
||||
uid = uidKey;
|
||||
}
|
||||
AttachedFile attachedFile;
|
||||
attachedFile.setData(data);
|
||||
attachedFile.setMediaType(mimeType);
|
||||
attachedFile.setDescription(property.value("description").value<String>());
|
||||
attachedFile.setFileName(fileName);
|
||||
attachedFile.setUID(uid);
|
||||
d->attachments->addAttachedFile(attachedFile);
|
||||
if(fileName.isEmpty() && !mimeType.isEmpty()) {
|
||||
int slashPos = mimeType.rfind('/');
|
||||
String ext = mimeType.substr(slashPos + 1);
|
||||
if(ext == "jpeg") {
|
||||
ext = "jpg";
|
||||
}
|
||||
fileName = "attachment." + ext;
|
||||
}
|
||||
if(!mimeType.isEmpty() && !fileName.isEmpty()) {
|
||||
AttachedFile attachedFile;
|
||||
attachedFile.setData(data);
|
||||
attachedFile.setMediaType(mimeType);
|
||||
attachedFile.setDescription(property.value("description").value<String>());
|
||||
attachedFile.setFileName(fileName);
|
||||
attachedFile.setUID(uid);
|
||||
d->attachments->addAttachedFile(attachedFile);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -333,7 +333,7 @@ namespace
|
||||
);
|
||||
if(it != simpleTagsTranslation.end())
|
||||
return { std::get<1>(*it), std::get<2>(*it), std::get<3>(*it) };
|
||||
if (!key.isEmpty() && !key.startsWith("_"))
|
||||
if (!key.isEmpty())
|
||||
return { key, Matroska::SimpleTag::TargetTypeValue::Track, false };
|
||||
return { String(), Matroska::SimpleTag::TargetTypeValue::None, false };
|
||||
}
|
||||
@ -352,8 +352,7 @@ namespace
|
||||
return it != simpleTagsTranslation.end()
|
||||
? String(std::get<0>(*it), String::UTF8)
|
||||
: (targetTypeValue == Matroska::SimpleTag::TargetTypeValue::Track ||
|
||||
targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None) &&
|
||||
!name.startsWith("_")
|
||||
targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None)
|
||||
? name
|
||||
: String();
|
||||
}
|
||||
@ -399,6 +398,21 @@ String Matroska::Tag::TagPrivate::getTag(const String &key) const
|
||||
return it != tags.end() ? it->toString() : String();
|
||||
}
|
||||
|
||||
PropertyMap Matroska::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
for(const auto &simpleTag : std::as_const(d->tags)) {
|
||||
if(simpleTag.type() == SimpleTag::StringType) {
|
||||
String key = translateTag(simpleTag.name(), simpleTag.targetTypeValue());
|
||||
if(!key.isEmpty())
|
||||
properties[key].append(simpleTag.toString());
|
||||
else
|
||||
properties.addUnsupportedData(simpleTag.name());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
PropertyMap Matroska::Tag::setProperties(const PropertyMap &propertyMap)
|
||||
{
|
||||
// Remove all simple tags which would be returned in properties()
|
||||
@ -442,7 +456,8 @@ StringList Matroska::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
for(const SimpleTag &t : std::as_const(d->tags)) {
|
||||
if(t.type() == SimpleTag::BinaryType) {
|
||||
if(t.type() != SimpleTag::StringType ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty()) {
|
||||
keys.append(t.name());
|
||||
}
|
||||
}
|
||||
@ -454,9 +469,16 @@ List<VariantMap> Matroska::Tag::complexProperties(const String& key) const
|
||||
List<VariantMap> props;
|
||||
if(key.upper() != "PICTURE") { // Pictures are handled at the file level
|
||||
for(const SimpleTag &t : std::as_const(d->tags)) {
|
||||
if(t.type() == SimpleTag::BinaryType) {
|
||||
if(t.name() == key &&
|
||||
(t.type() != SimpleTag::StringType ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty())) {
|
||||
VariantMap property;
|
||||
property.insert("data", t.toByteVector());
|
||||
if(t.type() != SimpleTag::StringType) {
|
||||
property.insert("data", t.toByteVector());
|
||||
}
|
||||
else {
|
||||
property.insert("value", t.toString());
|
||||
}
|
||||
property.insert("name", t.name());
|
||||
property.insert("targetTypeValue", t.targetTypeValue());
|
||||
property.insert("language", t.language());
|
||||
@ -476,36 +498,39 @@ bool Matroska::Tag::setComplexProperties(const String& key, const List<VariantMa
|
||||
}
|
||||
d->removeSimpleTags(
|
||||
[&key](const SimpleTag &t) {
|
||||
return t.name() == key && t.type() == SimpleTag::BinaryType;
|
||||
return t.name() == key &&
|
||||
(t.type() != SimpleTag::StringType ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty());
|
||||
}
|
||||
);
|
||||
bool result = false;
|
||||
for(const auto &property : value) {
|
||||
if(property.value("name").value<String>() == key && property.contains("data")) {
|
||||
d->tags.append(SimpleTag(
|
||||
key,
|
||||
property.value("data").value<ByteVector>(),
|
||||
static_cast<SimpleTag::TargetTypeValue>(
|
||||
property.value("targetTypeValue", 0).value<int>()),
|
||||
property.value("language").value<String>(),
|
||||
property.value("defaultLanguage", true).value<bool>()));
|
||||
if(property.value("name").value<String>() == key &&
|
||||
(property.contains("data") || property.contains("value") )) {
|
||||
SimpleTag::TargetTypeValue targetTypeValue;
|
||||
Variant targetTypeValueVar = property.value("targetTypeValue", 0);
|
||||
switch(targetTypeValueVar.type()) {
|
||||
case Variant::UInt:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<unsigned int>());
|
||||
break;
|
||||
case Variant::LongLong:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<long long>());
|
||||
break;
|
||||
case Variant::ULongLong:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<unsigned long long>());
|
||||
break;
|
||||
default:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<int>());
|
||||
}
|
||||
auto language = property.value("language").value<String>();
|
||||
bool defaultLanguage = property.value("defaultLanguage", true).value<bool>();
|
||||
d->tags.append(property.contains("data")
|
||||
? SimpleTag(key, property.value("data").value<ByteVector>(),
|
||||
targetTypeValue, language, defaultLanguage)
|
||||
: SimpleTag(key, property.value("value").value<String>(),
|
||||
targetTypeValue, language, defaultLanguage));
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PropertyMap Matroska::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
for(const auto &simpleTag : std::as_const(d->tags)) {
|
||||
if(simpleTag.type() == SimpleTag::StringType) {
|
||||
String key = translateTag(simpleTag.name(), simpleTag.targetTypeValue());
|
||||
if(!key.isEmpty())
|
||||
properties[key].append(simpleTag.toString());
|
||||
else
|
||||
properties.addUnsupportedData(simpleTag.name());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
@ -495,6 +495,30 @@ int String::toInt(bool *ok) const
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
|
||||
long long String::toLongLong(bool *ok, int base) const
|
||||
{
|
||||
const wchar_t *beginPtr = d->data.c_str();
|
||||
wchar_t *endPtr;
|
||||
errno = 0;
|
||||
const long long value = ::wcstoll(beginPtr, &endPtr, base);
|
||||
if(ok) {
|
||||
*ok = errno == 0 && endPtr > beginPtr && *endPtr == L'\0';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned long long String::toULongLong(bool *ok, int base) const
|
||||
{
|
||||
const wchar_t *beginPtr = d->data.c_str();
|
||||
wchar_t *endPtr;
|
||||
errno = 0;
|
||||
const unsigned long long value = ::wcstoull(beginPtr, &endPtr, base);
|
||||
if(ok) {
|
||||
*ok = errno == 0 && endPtr > beginPtr && *endPtr == L'\0';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
String String::stripWhiteSpace() const
|
||||
{
|
||||
static const wchar_t *WhiteSpaceChars = L"\t\n\f\r ";
|
||||
@ -527,6 +551,11 @@ String String::fromLongLong(long long n) // static
|
||||
return std::to_string(n);
|
||||
}
|
||||
|
||||
String String::fromULongLong(unsigned long long n) // static
|
||||
{
|
||||
return std::to_string(n);
|
||||
}
|
||||
|
||||
wchar_t &String::operator[](int i)
|
||||
{
|
||||
detach();
|
||||
|
||||
@ -359,6 +359,24 @@ namespace TagLib {
|
||||
*/
|
||||
int toInt(bool *ok = nullptr) const;
|
||||
|
||||
/*!
|
||||
* Convert the string to an integer.
|
||||
*
|
||||
* If the conversion was successful, it sets the value of \a *ok to
|
||||
* \c true and returns the integer. Otherwise it sets \a *ok to \c false
|
||||
* and the result is undefined.
|
||||
*/
|
||||
long long toLongLong(bool *ok = nullptr, int base = 10) const;
|
||||
|
||||
/*!
|
||||
* Convert the string to an integer.
|
||||
*
|
||||
* If the conversion was successful, it sets the value of \a *ok to
|
||||
* \c true and returns the integer. Otherwise it sets \a *ok to \c false
|
||||
* and the result is undefined.
|
||||
*/
|
||||
unsigned long long toULongLong(bool *ok = nullptr, int base = 10) const;
|
||||
|
||||
/*!
|
||||
* Returns a string with the leading and trailing whitespace stripped.
|
||||
*/
|
||||
@ -384,6 +402,11 @@ namespace TagLib {
|
||||
*/
|
||||
static String fromLongLong(long long n);
|
||||
|
||||
/*!
|
||||
* Converts the base-10 integer \a n to a string.
|
||||
*/
|
||||
static String fromULongLong(unsigned long long n);
|
||||
|
||||
/*!
|
||||
* Returns a reference to the character at position \a i.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user