mirror of
https://github.com/taglib/taglib.git
synced 2026-04-01 03:32:47 -04:00
Preserve track UID in simple tags
Some encoders write track specific DURATION tags, which should not be removed.
This commit is contained in:
@ -44,6 +44,11 @@ int main(int argc, char *argv[])
|
||||
PRINT_PRETTY("Target Type Value",
|
||||
targetTypeValue == 0 ? "None" : TagLib::Utils::formatString("%i", targetTypeValue).toCString(false)
|
||||
);
|
||||
if(auto trackUid = t.trackUid()) {
|
||||
PRINT_PRETTY("Track UID",
|
||||
TagLib::Utils::formatString("%llu",trackUid).toCString(false)
|
||||
);
|
||||
}
|
||||
const TagLib::String &language = t.language();
|
||||
PRINT_PRETTY("Language", !language.isEmpty() ? language.toCString(false) : "Not set");
|
||||
|
||||
|
||||
@ -82,6 +82,7 @@ std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileMediaType);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCodecID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagTargetTypeValue);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagTrackUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagsLanguageDefault);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSeekPosition);
|
||||
|
||||
@ -40,6 +40,7 @@ namespace TagLib::EBML {
|
||||
MkTag = 0x7373,
|
||||
MkTagTargets = 0x63C0,
|
||||
MkTagTargetTypeValue = 0x68CA,
|
||||
MkTagTrackUID = 0x63C5,
|
||||
MkSimpleTag = 0x67C8,
|
||||
MkTagName = 0x45A3,
|
||||
MkTagLanguage = 0x447A,
|
||||
@ -168,6 +169,7 @@ namespace TagLib::EBML {
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileMediaType> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCodecID> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTargetTypeValue> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTrackUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeekPosition> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTimestampScale> { using type = UIntElement; };
|
||||
|
||||
@ -54,6 +54,7 @@ std::unique_ptr<Matroska::Tag> EBML::MkTags::parse()
|
||||
|
||||
// Parse the <Targets> element
|
||||
Matroska::SimpleTag::TargetTypeValue targetTypeValue = Matroska::SimpleTag::TargetTypeValue::None;
|
||||
unsigned long long trackUid = 0;
|
||||
if(targets) {
|
||||
for(const auto &targetsChild : *targets) {
|
||||
Id id = targetsChild->getId();
|
||||
@ -63,6 +64,9 @@ std::unique_ptr<Matroska::Tag> EBML::MkTags::parse()
|
||||
element_cast<Id::MkTagTargetTypeValue>(targetsChild)->getValue()
|
||||
);
|
||||
}
|
||||
else if(id == Id::MkTagTrackUID) {
|
||||
trackUid = element_cast<Id::MkTagTrackUID>(targetsChild)->getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,9 +96,11 @@ std::unique_ptr<Matroska::Tag> EBML::MkTags::parse()
|
||||
|
||||
mTag->addSimpleTag(tagValueString
|
||||
? Matroska::SimpleTag(tagName, *tagValueString,
|
||||
targetTypeValue, language, defaultLanguageFlag)
|
||||
targetTypeValue, language, defaultLanguageFlag,
|
||||
trackUid)
|
||||
: Matroska::SimpleTag(tagName, *tagValueBinary,
|
||||
targetTypeValue, language, defaultLanguageFlag));
|
||||
targetTypeValue, language, defaultLanguageFlag,
|
||||
trackUid));
|
||||
}
|
||||
}
|
||||
return mTag;
|
||||
|
||||
@ -30,16 +30,19 @@ class Matroska::SimpleTag::SimpleTagPrivate
|
||||
{
|
||||
public:
|
||||
explicit SimpleTagPrivate(const String &name, const String& value,
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) :
|
||||
value(value), name(name), language(language),
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid) :
|
||||
value(value), name(name), language(language), trackUid(trackUid),
|
||||
targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {}
|
||||
explicit SimpleTagPrivate(const String &name, const ByteVector& value,
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) :
|
||||
value(value), name(name), language(language),
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid) :
|
||||
value(value), name(name), language(language), trackUid(trackUid),
|
||||
targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {}
|
||||
const std::variant<String, ByteVector> value;
|
||||
const String name;
|
||||
const String language;
|
||||
const unsigned long long trackUid;
|
||||
const TargetTypeValue targetTypeValue;
|
||||
const bool defaultLanguageFlag;
|
||||
};
|
||||
@ -49,16 +52,20 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(const String &name, const String &value,
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) :
|
||||
TargetTypeValue targetTypeValue,
|
||||
const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid) :
|
||||
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
|
||||
language, defaultLanguage))
|
||||
language, defaultLanguage, trackUid))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(const String &name, const ByteVector &value,
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) :
|
||||
TargetTypeValue targetTypeValue,
|
||||
const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid) :
|
||||
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
|
||||
language, defaultLanguage))
|
||||
language, defaultLanguage, trackUid))
|
||||
{
|
||||
}
|
||||
|
||||
@ -106,6 +113,11 @@ bool Matroska::SimpleTag::defaultLanguageFlag() const
|
||||
return d->defaultLanguageFlag;
|
||||
}
|
||||
|
||||
unsigned long long Matroska::SimpleTag::trackUid() const
|
||||
{
|
||||
return d->trackUid;
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::ValueType Matroska::SimpleTag::type() const
|
||||
{
|
||||
return std::holds_alternative<ByteVector>(d->value) ? BinaryType : StringType;
|
||||
|
||||
@ -55,14 +55,16 @@ namespace TagLib {
|
||||
*/
|
||||
SimpleTag(const String &name, const String &value,
|
||||
TargetTypeValue targetTypeValue = None,
|
||||
const String &language = String(), bool defaultLanguage = true);
|
||||
const String &language = String(), bool defaultLanguage = true,
|
||||
unsigned long long trackUid = 0);
|
||||
|
||||
/*!
|
||||
* Construct a binary simple tag.
|
||||
*/
|
||||
SimpleTag(const String &name, const ByteVector &value,
|
||||
TargetTypeValue targetTypeValue = None,
|
||||
const String &language = String(), bool defaultLanguage = true);
|
||||
const String &language = String(), bool defaultLanguage = true,
|
||||
unsigned long long trackUid = 0);
|
||||
|
||||
/*!
|
||||
* Construct a simple tag as a copy of \a other.
|
||||
@ -114,6 +116,12 @@ namespace TagLib {
|
||||
*/
|
||||
bool defaultLanguageFlag() const;
|
||||
|
||||
/*!
|
||||
* Returns the UID that identifies the track that the tags belong to,
|
||||
* zero if not defined, the tag applies to all tracks
|
||||
*/
|
||||
unsigned long long trackUid() const;
|
||||
|
||||
/*!
|
||||
* Returns the type of the value.
|
||||
*/
|
||||
|
||||
@ -92,11 +92,13 @@ void Matroska::Tag::addSimpleTag(const SimpleTag &tag)
|
||||
}
|
||||
|
||||
void Matroska::Tag::removeSimpleTag(const String &name,
|
||||
SimpleTag::TargetTypeValue targetTypeValue)
|
||||
SimpleTag::TargetTypeValue targetTypeValue,
|
||||
unsigned long long trackUid)
|
||||
{
|
||||
auto it = std::find_if(d->tags.begin(), d->tags.end(),
|
||||
[&name, targetTypeValue](const SimpleTag &t) {
|
||||
return t.name() == name && t.targetTypeValue() == targetTypeValue;
|
||||
[&name, targetTypeValue, trackUid](const SimpleTag &t) {
|
||||
return t.name() == name && t.targetTypeValue() == targetTypeValue &&
|
||||
t.trackUid() == trackUid;
|
||||
}
|
||||
);
|
||||
if(it != d->tags.end()) {
|
||||
@ -221,11 +223,13 @@ ByteVector Matroska::Tag::renderInternal()
|
||||
// Build target-based list
|
||||
for(const auto &tag : std::as_const(d->tags)) {
|
||||
auto targetTypeValue = tag.targetTypeValue();
|
||||
auto trackUid = tag.trackUid();
|
||||
auto it = std::find_if(targetList.begin(),
|
||||
targetList.end(),
|
||||
[&](const auto &list) {
|
||||
const auto &simpleTag = list.front();
|
||||
return simpleTag.targetTypeValue() == targetTypeValue;
|
||||
return simpleTag.targetTypeValue() == targetTypeValue &&
|
||||
simpleTag.trackUid() == trackUid;
|
||||
}
|
||||
);
|
||||
if(it == targetList.end()) {
|
||||
@ -239,6 +243,7 @@ ByteVector Matroska::Tag::renderInternal()
|
||||
for(const auto &list : targetList) {
|
||||
const auto &frontTag = list.front();
|
||||
auto targetTypeValue = frontTag.targetTypeValue();
|
||||
auto trackUid = frontTag.trackUid();
|
||||
auto tag = EBML::make_unique_element<EBML::Element::Id::MkTag>();
|
||||
|
||||
// Build <Tag Targets> element
|
||||
@ -248,6 +253,11 @@ ByteVector Matroska::Tag::renderInternal()
|
||||
element->setValue(static_cast<unsigned int>(targetTypeValue));
|
||||
targets->appendElement(std::move(element));
|
||||
}
|
||||
if(trackUid != 0) {
|
||||
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagTrackUID>();
|
||||
element->setValue(trackUid);
|
||||
targets->appendElement(std::move(element));
|
||||
}
|
||||
tag->appendElement(std::move(targets));
|
||||
|
||||
// Build <Simple Tag> element
|
||||
@ -401,7 +411,8 @@ String Matroska::Tag::TagPrivate::getTag(const String &key) const
|
||||
return t.name() == name
|
||||
&& t.type() == SimpleTag::StringType
|
||||
&& (t.targetTypeValue() == targetTypeValue ||
|
||||
(t.targetTypeValue() == SimpleTag::TargetTypeValue::None && !strict));
|
||||
(t.targetTypeValue() == SimpleTag::TargetTypeValue::None && !strict))
|
||||
&& t.trackUid() == 0;
|
||||
}
|
||||
);
|
||||
return it != tags.end() ? it->toString() : String();
|
||||
@ -411,7 +422,7 @@ PropertyMap Matroska::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
for(const auto &simpleTag : std::as_const(d->tags)) {
|
||||
if(simpleTag.type() == SimpleTag::StringType) {
|
||||
if(simpleTag.type() == SimpleTag::StringType && simpleTag.trackUid() == 0) {
|
||||
String key = translateTag(simpleTag.name(), simpleTag.targetTypeValue());
|
||||
if(!key.isEmpty())
|
||||
properties[key].append(simpleTag.toString());
|
||||
@ -428,6 +439,7 @@ PropertyMap Matroska::Tag::setProperties(const PropertyMap &propertyMap)
|
||||
for(auto it = d->tags.begin(); it != d->tags.end();) {
|
||||
String key;
|
||||
if(it->type() == SimpleTag::StringType &&
|
||||
it->trackUid() == 0 &&
|
||||
!(key = translateTag(it->name(), it->targetTypeValue())).isEmpty()) {
|
||||
it = d->tags.erase(it);
|
||||
setNeedsRender(true);
|
||||
@ -468,6 +480,7 @@ StringList Matroska::Tag::complexPropertyKeys() const
|
||||
StringList keys;
|
||||
for(const SimpleTag &t : std::as_const(d->tags)) {
|
||||
if(t.type() != SimpleTag::StringType ||
|
||||
t.trackUid() != 0 ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty()) {
|
||||
keys.append(t.name());
|
||||
}
|
||||
@ -482,6 +495,7 @@ List<VariantMap> Matroska::Tag::complexProperties(const String& key) const
|
||||
for(const SimpleTag &t : std::as_const(d->tags)) {
|
||||
if(t.name() == key &&
|
||||
(t.type() != SimpleTag::StringType ||
|
||||
t.trackUid() != 0 ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty())) {
|
||||
VariantMap property;
|
||||
if(t.type() != SimpleTag::StringType) {
|
||||
@ -491,7 +505,12 @@ List<VariantMap> Matroska::Tag::complexProperties(const String& key) const
|
||||
property.insert("value", t.toString());
|
||||
}
|
||||
property.insert("name", t.name());
|
||||
property.insert("targetTypeValue", t.targetTypeValue());
|
||||
if(t.targetTypeValue() != SimpleTag::TargetTypeValue::None) {
|
||||
property.insert("targetTypeValue", t.targetTypeValue());
|
||||
}
|
||||
if(t.trackUid()) {
|
||||
property.insert("trackUid", t.trackUid());
|
||||
}
|
||||
property.insert("language", t.language());
|
||||
property.insert("defaultLanguage", t.defaultLanguageFlag());
|
||||
props.append(property);
|
||||
@ -511,6 +530,7 @@ bool Matroska::Tag::setComplexProperties(const String& key, const List<VariantMa
|
||||
[&key](const SimpleTag &t) {
|
||||
return t.name() == key &&
|
||||
(t.type() != SimpleTag::StringType ||
|
||||
t.trackUid() != 0 ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty());
|
||||
}
|
||||
);
|
||||
@ -535,11 +555,12 @@ bool Matroska::Tag::setComplexProperties(const String& key, const List<VariantMa
|
||||
}
|
||||
auto language = property.value("language").value<String>();
|
||||
bool defaultLanguage = property.value("defaultLanguage", true).value<bool>();
|
||||
auto trackUid = property.value("trackUid", 0ULL).value<unsigned long long>();
|
||||
d->tags.append(property.contains("data")
|
||||
? SimpleTag(key, property.value("data").value<ByteVector>(),
|
||||
targetTypeValue, language, defaultLanguage)
|
||||
targetTypeValue, language, defaultLanguage, trackUid)
|
||||
: SimpleTag(key, property.value("value").value<String>(),
|
||||
targetTypeValue, language, defaultLanguage));
|
||||
targetTypeValue, language, defaultLanguage, trackUid));
|
||||
setNeedsRender(true);
|
||||
result = true;
|
||||
}
|
||||
|
||||
@ -89,7 +89,8 @@ namespace TagLib {
|
||||
bool setComplexProperties(const String& key, const List<VariantMap>& value) override;
|
||||
|
||||
void addSimpleTag(const SimpleTag &tag);
|
||||
void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue);
|
||||
void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue,
|
||||
unsigned long long trackUid = 0);
|
||||
void clearSimpleTags();
|
||||
const SimpleTagsList &simpleTagsList() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user