mirror of
https://github.com/taglib/taglib.git
synced 2026-08-14 06:17:00 -04:00
ASF: Improve PropertyMap interface (#1401)
Support more property keys, also with numeric and Guid types. --------- Co-authored-by: Urs Fleisch <[email protected]>
This commit is contained in:
@@ -162,6 +162,13 @@ ASF::Picture ASF::Attribute::toPicture() const
|
||||
return d->pictureValue;
|
||||
}
|
||||
|
||||
ASF::Attribute ASF::Attribute::fromGuid(const ByteVector& guid)
|
||||
{
|
||||
Attribute attr(guid);
|
||||
attr.d->type = GuidType;
|
||||
return attr;
|
||||
}
|
||||
|
||||
String ASF::Attribute::parse(ASF::File &file, int kind)
|
||||
{
|
||||
unsigned int size, nameLength;
|
||||
|
||||
@@ -165,6 +165,11 @@ namespace TagLib
|
||||
*/
|
||||
Picture toPicture() const;
|
||||
|
||||
/*!
|
||||
* Create an attribute holding a Guid.
|
||||
*/
|
||||
static Attribute fromGuid(const ByteVector &guid);
|
||||
|
||||
/*!
|
||||
* Returns the language number, or 0 is no stream number was set.
|
||||
*/
|
||||
|
||||
+182
-74
@@ -26,6 +26,7 @@
|
||||
#include "asftag.h"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "tpropertymap.h"
|
||||
@@ -228,71 +229,139 @@ bool ASF::Tag::isEmpty() const
|
||||
namespace
|
||||
{
|
||||
constexpr std::array keyTranslation {
|
||||
std::pair("WM/AlbumTitle", "ALBUM"),
|
||||
std::pair("WM/AlbumArtist", "ALBUMARTIST"),
|
||||
std::pair("WM/AuthorURL", "ARTISTWEBPAGE"),
|
||||
std::pair("WM/Composer", "COMPOSER"),
|
||||
std::pair("WM/Writer", "LYRICIST"),
|
||||
std::pair("WM/Conductor", "CONDUCTOR"),
|
||||
std::pair("WM/ModifiedBy", "REMIXER"),
|
||||
std::pair("WM/Year", "DATE"),
|
||||
std::pair("WM/OriginalAlbumTitle", "ORIGINALALBUM"),
|
||||
std::pair("WM/OriginalArtist", "ORIGINALARTIST"),
|
||||
std::pair("WM/OriginalFilename", "ORIGINALFILENAME"),
|
||||
std::pair("WM/OriginalLyricist", "ORIGINALLYRICIST"),
|
||||
std::pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
|
||||
std::pair("WM/Producer", "PRODUCER"),
|
||||
std::pair("WM/ContentGroupDescription", "WORK"),
|
||||
std::pair("WM/SubTitle", "SUBTITLE"),
|
||||
std::pair("WM/SetSubTitle", "DISCSUBTITLE"),
|
||||
std::pair("WM/TrackNumber", "TRACKNUMBER"),
|
||||
std::pair("WM/PartOfSet", "DISCNUMBER"),
|
||||
std::pair("WM/Genre", "GENRE"),
|
||||
std::pair("WM/BeatsPerMinute", "BPM"),
|
||||
std::pair("WM/Mood", "MOOD"),
|
||||
std::pair("WM/InitialKey", "INITIALKEY"),
|
||||
std::pair("WM/ISRC", "ISRC"),
|
||||
std::pair("WM/Lyrics", "LYRICS"),
|
||||
std::pair("WM/Media", "MEDIA"),
|
||||
std::pair("WM/Publisher", "LABEL"),
|
||||
std::pair("WM/CatalogNo", "CATALOGNUMBER"),
|
||||
std::pair("WM/Barcode", "BARCODE"),
|
||||
std::pair("WM/EncodedBy", "ENCODEDBY"),
|
||||
std::pair("WM/EncodingSettings", "ENCODING"),
|
||||
std::pair("WM/EncodingTime", "ENCODINGTIME"),
|
||||
std::pair("WM/AudioFileURL", "FILEWEBPAGE"),
|
||||
std::pair("WM/AlbumSortOrder", "ALBUMSORT"),
|
||||
std::pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
|
||||
std::pair("WM/ArtistSortOrder", "ARTISTSORT"),
|
||||
std::pair("WM/TitleSortOrder", "TITLESORT"),
|
||||
std::pair("WM/Script", "SCRIPT"),
|
||||
std::pair("WM/Language", "LANGUAGE"),
|
||||
std::pair("WM/ARTISTS", "ARTISTS"),
|
||||
std::pair("ASIN", "ASIN"),
|
||||
std::pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
|
||||
std::pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
|
||||
std::pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
|
||||
std::pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
|
||||
std::pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
|
||||
std::pair("MusicBrainz/Album Status", "RELEASESTATUS"),
|
||||
std::pair("MusicBrainz/Album Type", "RELEASETYPE"),
|
||||
std::pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
|
||||
std::pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
|
||||
std::pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
|
||||
std::pair("MusicIP/PUID", "MUSICIP_PUID"),
|
||||
std::pair("Acoustid/Id", "ACOUSTID_ID"),
|
||||
std::pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"),
|
||||
std::tuple("WM/AlbumTitle", "ALBUM", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/AlbumArtist", "ALBUMARTIST", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/AuthorURL", "ARTISTWEBPAGE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Composer", "COMPOSER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Writer", "LYRICIST", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Conductor", "CONDUCTOR", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ModifiedBy", "REMIXER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Year", "DATE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/OriginalAlbumTitle", "ORIGINALALBUM", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/OriginalArtist", "ORIGINALARTIST", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/OriginalFilename", "ORIGINALFILENAME", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/OriginalLyricist", "ORIGINALLYRICIST", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/OriginalReleaseYear", "ORIGINALDATE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Producer", "PRODUCER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ContentGroupDescription", "WORK", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/SubTitle", "SUBTITLE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/SetSubTitle", "DISCSUBTITLE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/TrackNumber", "TRACKNUMBER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/PartOfSet", "DISCNUMBER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Genre", "GENRE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/BeatsPerMinute", "BPM", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Mood", "MOOD", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/InitialKey", "INITIALKEY", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ISRC", "ISRC", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Lyrics", "LYRICS", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Media", "MEDIA", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Publisher", "LABEL", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/CatalogNo", "CATALOGNUMBER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Barcode", "BARCODE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/EncodedBy", "ENCODEDBY", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/EncodingSettings", "ENCODING", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/EncodingTime", "ENCODINGTIME", ASF::Attribute::QWordType),
|
||||
std::tuple("WM/AudioFileURL", "FILEWEBPAGE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/AlbumSortOrder", "ALBUMSORT", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ArtistSortOrder", "ARTISTSORT", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/TitleSortOrder", "TITLESORT", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Script", "SCRIPT", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Language", "LANGUAGE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ARTISTS", "ARTISTS", ASF::Attribute::UnicodeType),
|
||||
std::tuple("ASIN", "ASIN", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Album Release Country", "RELEASECOUNTRY", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Album Status", "RELEASESTATUS", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Album Type", "RELEASETYPE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MusicIP/PUID", "MUSICIP_PUID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("Acoustid/Id", "ACOUSTID_ID", ASF::Attribute::UnicodeType),
|
||||
std::tuple("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT", ASF::Attribute::UnicodeType),
|
||||
std::tuple("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN", ASF::Attribute::UnicodeType),
|
||||
std::tuple("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK", ASF::Attribute::UnicodeType),
|
||||
std::tuple("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN", ASF::Attribute::UnicodeType),
|
||||
std::tuple("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/MediaClassPrimaryID", "MEDIACLASSPRIMARYID", ASF::Attribute::GuidType),
|
||||
std::tuple("WM/MediaClassSecondaryID", "MEDIACLASSSECONDARYID", ASF::Attribute::GuidType),
|
||||
std::tuple("WM/WMCollectionGroupID", "COLLECTIONGROUPID", ASF::Attribute::GuidType),
|
||||
std::tuple("WM/WMCollectionID", "COLLECTIONID", ASF::Attribute::GuidType),
|
||||
std::tuple("WM/WMContentID", "CONTENTID", ASF::Attribute::GuidType),
|
||||
std::tuple("WM/ContentDistributor", "CONTENTDISTRIBUTOR", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ParentalRating", "PARENTALRATING", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Period", "PERIOD", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/PromotionURL", "PROMOTIONURL", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ToolName", "TOOLNAME", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/ToolVersion", "TOOLVERSION", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/Provider", "PROVIDER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WM/UniqueFileIdentifier", "UNIQUEFILEIDENTIFIER", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WMFSDKVersion", "WMFSDKVERSION", ASF::Attribute::UnicodeType),
|
||||
std::tuple("WMFSDKNeeded", "WMFSDKNEEDED", ASF::Attribute::UnicodeType),
|
||||
std::tuple("DeviceConformanceTemplate", "DEVICECONFORMANCETEMPLATE", ASF::Attribute::UnicodeType),
|
||||
std::tuple("MediaFoundationVersion", "MEDIAFOUNDATIONVERSION", ASF::Attribute::UnicodeType),
|
||||
std::tuple("IsVBR", "ISVBR", ASF::Attribute::BoolType),
|
||||
std::tuple("PeakValue", "PEAKVALUE", ASF::Attribute::DWordType),
|
||||
std::tuple("AverageLevel", "AVERAGELEVEL", ASF::Attribute::DWordType),
|
||||
};
|
||||
|
||||
// Attribute names are matched case-insensitively; taggers disagree on the
|
||||
// casing of names not defined by Windows Media (e.g. replaygain_track_gain).
|
||||
String translateKey(const String &key)
|
||||
{
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
if(key == k)
|
||||
return t;
|
||||
const String upperKey = key.upper();
|
||||
for(const auto &[k, v, t] : keyTranslation) {
|
||||
if(upperKey == String(k).upper())
|
||||
return v;
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
void eraseAttribute(ASF::AttributeListMap &attributeListMap, const String &name)
|
||||
{
|
||||
const String upperName = name.upper();
|
||||
StringList keys;
|
||||
for(const auto &[k, attributes] : std::as_const(attributeListMap)) {
|
||||
if(k.upper() == upperName)
|
||||
keys.append(k);
|
||||
}
|
||||
for(const auto &k : keys)
|
||||
attributeListMap.erase(k);
|
||||
}
|
||||
|
||||
String attributeToString(const ASF::Attribute &attr)
|
||||
{
|
||||
switch(attr.type()) {
|
||||
case ASF::Attribute::WordType:
|
||||
case ASF::Attribute::DWordType:
|
||||
case ASF::Attribute::QWordType:
|
||||
case ASF::Attribute::BoolType:
|
||||
return String(std::to_string(attr.toULongLong()));
|
||||
case ASF::Attribute::GuidType:
|
||||
if(const ByteVector data = attr.toByteVector(); data.size() == 16) {
|
||||
String str;
|
||||
for(int i = 0; i < 16; ++i) {
|
||||
if(i == 4 || i == 6 || i == 8 || i == 10) {
|
||||
str += '-';
|
||||
}
|
||||
const auto c = static_cast<unsigned char>(data[i]);
|
||||
unsigned char d = c >> 4;
|
||||
str += static_cast<char>(d >= 10 ? d - 10 + 'A' : d + '0');
|
||||
d = c & 0x0f;
|
||||
str += static_cast<char>(d >= 10 ? d - 10 + 'A' : d + '0');
|
||||
}
|
||||
return str;
|
||||
}
|
||||
return {};
|
||||
default:
|
||||
return attr.toString();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
PropertyMap ASF::Tag::properties() const
|
||||
@@ -315,14 +384,12 @@ PropertyMap ASF::Tag::properties() const
|
||||
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
|
||||
if(const String key = translateKey(k); !key.isEmpty()) {
|
||||
for(const auto &attr : attributes) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
if(attr.type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(attr.toUInt()));
|
||||
else
|
||||
props.insert(key, attr.toString());
|
||||
}
|
||||
else {
|
||||
props.insert(key, attr.toString());
|
||||
// The same attribute can occur in both the extended content
|
||||
// description object and the metadata (library) object, skip exact
|
||||
// duplicates (e.g. a second identical IsVBR).
|
||||
if(const String value = attributeToString(attr);
|
||||
!props.value(key).contains(value)) {
|
||||
props.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,10 +408,10 @@ void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
|
||||
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
{
|
||||
static const Map<String, String> reverseKeyMap = [] {
|
||||
Map<String, String> map;
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
map[t] = k;
|
||||
static const Map<String, std::pair<String, Attribute::AttributeTypes>> reverseKeyMap = [] {
|
||||
Map<String, std::pair<String, Attribute::AttributeTypes>> map;
|
||||
for(const auto &[k, v, t] : keyTranslation) {
|
||||
map[v] = {k, t};
|
||||
}
|
||||
return map;
|
||||
}();
|
||||
@@ -365,7 +432,7 @@ PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
d->copyright.clear();
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[prop]);
|
||||
eraseAttribute(d->attributeListMap, reverseKeyMap[prop].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -373,10 +440,51 @@ PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
PropertyMap ignoredProps;
|
||||
for(const auto &[prop, attributes] : props) {
|
||||
if(reverseKeyMap.contains(prop)) {
|
||||
String name = reverseKeyMap[prop];
|
||||
removeItem(name);
|
||||
for(const auto &attr : attributes) {
|
||||
addAttribute(name, attr);
|
||||
const auto &[name, type] = reverseKeyMap[prop];
|
||||
eraseAttribute(d->attributeListMap, name);
|
||||
for(const auto &str : attributes) {
|
||||
switch(type) {
|
||||
case Attribute::WordType:
|
||||
addAttribute(name, static_cast<unsigned short>(str.toULongLong()));
|
||||
break;
|
||||
case Attribute::DWordType:
|
||||
addAttribute(name, static_cast<unsigned int>(str.toULongLong()));
|
||||
break;
|
||||
case Attribute::QWordType:
|
||||
addAttribute(name, str.toULongLong());
|
||||
break;
|
||||
case Attribute::BoolType:
|
||||
addAttribute(name, !str.isEmpty() && str != "0" && str.upper() != "TRUE");
|
||||
break;
|
||||
case Attribute::GuidType: {
|
||||
ByteVector data;
|
||||
String hexStr;
|
||||
for(wchar_t c : str) {
|
||||
if(c >= 'a' && c <= 'f') {
|
||||
hexStr += static_cast<wchar_t>(c + 'A' - 'a');
|
||||
} else if((c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')) {
|
||||
hexStr += c;
|
||||
}
|
||||
}
|
||||
if(hexStr.length() == 32) {
|
||||
unsigned char buf[16];
|
||||
unsigned char* bufPtr = buf;
|
||||
for(int i = 0; i < 32;) {
|
||||
auto h = static_cast<unsigned char>(hexStr[i++]);
|
||||
auto l = static_cast<unsigned char>(hexStr[i++]);
|
||||
*bufPtr++ = static_cast<unsigned char>(
|
||||
((h >= 'A' ? h + 10 - 'A' : h - '0') << 4) |
|
||||
(l >= 'A' ? l + 10 - 'A' : l - '0'));
|
||||
}
|
||||
data = ByteVector(reinterpret_cast<char*>(buf), 16);
|
||||
}
|
||||
addAttribute(name, Attribute::fromGuid(data));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
addAttribute(name, str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(prop == "TITLE") {
|
||||
|
||||
Binary file not shown.
+186
-2
@@ -52,6 +52,8 @@ class TestASF : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testSaveMultiplePictures);
|
||||
CPPUNIT_TEST(testProperties);
|
||||
CPPUNIT_TEST(testPropertiesAllSupported);
|
||||
CPPUNIT_TEST(testPropertiesRealFile);
|
||||
CPPUNIT_TEST(testCaseInsensitiveAttributeNames);
|
||||
CPPUNIT_TEST(testRepeatedSave);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
@@ -331,7 +333,7 @@ public:
|
||||
tags["DISCSUBTITLE"] = StringList("Disc Subtitle");
|
||||
tags["ENCODEDBY"] = StringList("Encoded by");
|
||||
tags["ENCODING"] = StringList("Encoding");
|
||||
tags["ENCODINGTIME"] = StringList("2021-01-03 11:52:19");
|
||||
tags["ENCODINGTIME"] = StringList("131452413620000000");
|
||||
tags["FILEWEBPAGE"] = StringList("File Webpage");
|
||||
tags["GENRE"] = StringList("Genre");
|
||||
tags["WORK"] = StringList("Grouping");
|
||||
@@ -366,6 +368,30 @@ public:
|
||||
tags["TITLE"] = StringList("Title");
|
||||
tags["TITLESORT"] = StringList("Title Sort");
|
||||
tags["TRACKNUMBER"] = StringList("2/4");
|
||||
tags["REPLAYGAIN_TRACK_GAIN"] = StringList("-4.25 dB");
|
||||
tags["REPLAYGAIN_TRACK_PEAK"] = StringList("0.985412");
|
||||
tags["REPLAYGAIN_ALBUM_GAIN"] = StringList("-3.80 dB");
|
||||
tags["REPLAYGAIN_ALBUM_PEAK"] = StringList("0.998201");
|
||||
tags["MEDIACLASSPRIMARYID"] = StringList("D1607DBC-E323-4BE2-86A1-48A42A28441E");
|
||||
tags["MEDIACLASSSECONDARYID"] = StringList("F24FF731-96FC-4D0F-A2F5-5A3483682B1A");
|
||||
tags["COLLECTIONGROUPID"] = StringList("A81F04B3-2B1B-4B52-8700-6F8091DF3B41");
|
||||
tags["COLLECTIONID"] = StringList("3F2504E0-4F89-11D3-9A0C-0305E82C3301");
|
||||
tags["CONTENTID"] = StringList("6B29FC40-CA47-1067-B31D-00DD010662DA");
|
||||
tags["CONTENTDISTRIBUTOR"] = StringList("Universal Music Group");
|
||||
tags["PARENTALRATING"] = StringList("Explicit");
|
||||
tags["PERIOD"] = StringList("Baroque");
|
||||
tags["PROMOTIONURL"] = StringList("https://www.artistwebsite.com/store/album-special-edition");
|
||||
tags["TOOLNAME"] = StringList("Windows Media Encoder");
|
||||
tags["TOOLVERSION"] = StringList("9.00.00.2980");
|
||||
tags["PROVIDER"] = StringList("AMG");
|
||||
tags["UNIQUEFILEIDENTIFIER"] = StringList("AMGI_0000000000012345");
|
||||
tags["WMFSDKVERSION"] = StringList("12.0.19041.1");
|
||||
tags["WMFSDKNEEDED"] = StringList("9.0.0.4503");
|
||||
tags["DEVICECONFORMANCETEMPLATE"] = StringList("L1");
|
||||
tags["MEDIAFOUNDATIONVERSION"] = StringList("2.0");
|
||||
tags["ISVBR"] = StringList("1");
|
||||
tags["PEAKVALUE"] = StringList("32104");
|
||||
tags["AVERAGELEVEL"] = StringList("2450");
|
||||
|
||||
ScopedFileCopy copy("silence-1", ".wma");
|
||||
{
|
||||
@@ -385,10 +411,168 @@ public:
|
||||
{
|
||||
const ASF::File f(copy.fileName().c_str());
|
||||
PropertyMap properties = f.properties();
|
||||
if (tags != properties) {
|
||||
if(tags != properties) {
|
||||
CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString());
|
||||
}
|
||||
CPPUNIT_ASSERT(tags == properties);
|
||||
|
||||
const ASF::Tag *asfTag = f.tag();
|
||||
CPPUNIT_ASSERT(asfTag);
|
||||
std::map<String, ASF::Attribute::AttributeTypes> expected = {
|
||||
{"ASIN", ASF::Attribute::UnicodeType},
|
||||
{"Acoustid/Fingerprint", ASF::Attribute::UnicodeType},
|
||||
{"Acoustid/Id", ASF::Attribute::UnicodeType},
|
||||
{"AverageLevel", ASF::Attribute::DWordType},
|
||||
{"DeviceConformanceTemplate", ASF::Attribute::UnicodeType},
|
||||
{"IsVBR", ASF::Attribute::BoolType},
|
||||
{"MediaFoundationVersion", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Album Artist Id", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Album Id", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Album Release Country", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Album Status", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Album Type", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Artist Id", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Release Group Id", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Release Track Id", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Track Id", ASF::Attribute::UnicodeType},
|
||||
{"MusicBrainz/Work Id", ASF::Attribute::UnicodeType},
|
||||
{"MusicIP/PUID", ASF::Attribute::UnicodeType},
|
||||
{"PeakValue", ASF::Attribute::DWordType},
|
||||
{"WM/ARTISTS", ASF::Attribute::UnicodeType},
|
||||
{"WM/AlbumArtist", ASF::Attribute::UnicodeType},
|
||||
{"WM/AlbumArtistSortOrder", ASF::Attribute::UnicodeType},
|
||||
{"WM/AlbumSortOrder", ASF::Attribute::UnicodeType},
|
||||
{"WM/AlbumTitle", ASF::Attribute::UnicodeType},
|
||||
{"WM/ArtistSortOrder", ASF::Attribute::UnicodeType},
|
||||
{"WM/AudioFileURL", ASF::Attribute::UnicodeType},
|
||||
{"WM/AuthorURL", ASF::Attribute::UnicodeType},
|
||||
{"WM/Barcode", ASF::Attribute::UnicodeType},
|
||||
{"WM/BeatsPerMinute", ASF::Attribute::UnicodeType},
|
||||
{"WM/CatalogNo", ASF::Attribute::UnicodeType},
|
||||
{"WM/Composer", ASF::Attribute::UnicodeType},
|
||||
{"WM/Conductor", ASF::Attribute::UnicodeType},
|
||||
{"WM/ContentDistributor", ASF::Attribute::UnicodeType},
|
||||
{"WM/ContentGroupDescription", ASF::Attribute::UnicodeType},
|
||||
{"WM/EncodedBy", ASF::Attribute::UnicodeType},
|
||||
{"WM/EncodingSettings", ASF::Attribute::UnicodeType},
|
||||
{"WM/EncodingTime", ASF::Attribute::QWordType},
|
||||
{"WM/Genre", ASF::Attribute::UnicodeType},
|
||||
{"WM/ISRC", ASF::Attribute::UnicodeType},
|
||||
{"WM/InitialKey", ASF::Attribute::UnicodeType},
|
||||
{"WM/Language", ASF::Attribute::UnicodeType},
|
||||
{"WM/Lyrics", ASF::Attribute::UnicodeType},
|
||||
{"WM/Media", ASF::Attribute::UnicodeType},
|
||||
{"WM/MediaClassPrimaryID", ASF::Attribute::GuidType},
|
||||
{"WM/MediaClassSecondaryID", ASF::Attribute::GuidType},
|
||||
{"WM/ModifiedBy", ASF::Attribute::UnicodeType},
|
||||
{"WM/Mood", ASF::Attribute::UnicodeType},
|
||||
{"WM/OriginalAlbumTitle", ASF::Attribute::UnicodeType},
|
||||
{"WM/OriginalArtist", ASF::Attribute::UnicodeType},
|
||||
{"WM/OriginalFilename", ASF::Attribute::UnicodeType},
|
||||
{"WM/OriginalLyricist", ASF::Attribute::UnicodeType},
|
||||
{"WM/OriginalReleaseYear", ASF::Attribute::UnicodeType},
|
||||
{"WM/ParentalRating", ASF::Attribute::UnicodeType},
|
||||
{"WM/PartOfSet", ASF::Attribute::UnicodeType},
|
||||
{"WM/Period", ASF::Attribute::UnicodeType},
|
||||
{"WM/Producer", ASF::Attribute::UnicodeType},
|
||||
{"WM/PromotionURL", ASF::Attribute::UnicodeType},
|
||||
{"WM/Provider", ASF::Attribute::UnicodeType},
|
||||
{"WM/Publisher", ASF::Attribute::UnicodeType},
|
||||
{"WM/Script", ASF::Attribute::UnicodeType},
|
||||
{"WM/SetSubTitle", ASF::Attribute::UnicodeType},
|
||||
{"WM/SubTitle", ASF::Attribute::UnicodeType},
|
||||
{"WM/TitleSortOrder", ASF::Attribute::UnicodeType},
|
||||
{"WM/ToolName", ASF::Attribute::UnicodeType},
|
||||
{"WM/ToolVersion", ASF::Attribute::UnicodeType},
|
||||
{"WM/TrackNumber", ASF::Attribute::UnicodeType},
|
||||
{"WM/UniqueFileIdentifier", ASF::Attribute::UnicodeType},
|
||||
{"WM/WMCollectionGroupID", ASF::Attribute::GuidType},
|
||||
{"WM/WMCollectionID", ASF::Attribute::GuidType},
|
||||
{"WM/WMContentID", ASF::Attribute::GuidType},
|
||||
{"WM/Writer", ASF::Attribute::UnicodeType},
|
||||
{"WM/Year", ASF::Attribute::UnicodeType},
|
||||
{"WMFSDKNeeded", ASF::Attribute::UnicodeType},
|
||||
{"WMFSDKVersion", ASF::Attribute::UnicodeType},
|
||||
{"replaygain_album_gain", ASF::Attribute::UnicodeType},
|
||||
{"replaygain_album_peak", ASF::Attribute::UnicodeType},
|
||||
{"replaygain_track_gain", ASF::Attribute::UnicodeType},
|
||||
{"replaygain_track_peak", ASF::Attribute::UnicodeType},
|
||||
};
|
||||
std::map<String, ASF::Attribute::AttributeTypes> actual;
|
||||
for(const auto &[name, attr] : asfTag->attributeListMap()) {
|
||||
if(!attr.isEmpty()) {
|
||||
actual[name] = attr[0].type();
|
||||
}
|
||||
}
|
||||
if(expected != actual) {
|
||||
auto mapToStrings = [](const std::map<String, ASF::Attribute::AttributeTypes> &map){
|
||||
StringList result;
|
||||
for(const auto &[name, attr] : map) {
|
||||
result.append(name + ":" + String::number(attr));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const StringList expectedStrs = mapToStrings(expected);
|
||||
const StringList actualStrs = mapToStrings(actual);
|
||||
CPPUNIT_ASSERT_EQUAL(expectedStrs, actualStrs);
|
||||
}
|
||||
CPPUNIT_ASSERT(expected == actual);
|
||||
}
|
||||
}
|
||||
|
||||
void testPropertiesRealFile()
|
||||
{
|
||||
ASF::File f(TEST_FILE_PATH_C("real_example.wma"));
|
||||
|
||||
PropertyMap tags = f.properties();
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Wake Up, Get Up, Get Out There"), tags["TITLE"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Shoji Meguro"), tags["ARTIST"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Persona 5: Sounds of Rebellion"), tags["ALBUM"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("-8.27 dB"), tags["REPLAYGAIN_TRACK_GAIN"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("1.000000"), tags["REPLAYGAIN_TRACK_PEAK"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("131452413620000000"), tags["ENCODINGTIME"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("32673"), tags["PEAKVALUE"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("8315"), tags["AVERAGELEVEL"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["ISVBR"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("12.0.15063.332"), tags["WMFSDKVERSION"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("0.0.0.0000"), tags["WMFSDKNEEDED"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("User Feedback"), tags["PROVIDER"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("L1"), tags["DEVICECONFORMANCETEMPLATE"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("2.112"), tags["MEDIAFOUNDATIONVERSION"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["TRACKNUMBER"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Alternative"), tags["GENRE"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Shoji Meguro"), tags["ALBUMARTIST"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("00000000-0000-0000-0000-000000000000"), tags["COLLECTIONGROUPID"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("00000000-0000-0000-0000-000000000000"), tags["COLLECTIONID"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("00000000-0000-0000-0000-000000000000"), tags["CONTENTID"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("BC7D60D1-23E3-E24B-86A1-48A42A28441E"), tags["MEDIACLASSPRIMARYID"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("00000000-0000-0000-0000-000000000000"), tags["MEDIACLASSSECONDARYID"]);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList(";"), tags["UNIQUEFILEIDENTIFIER"]);
|
||||
}
|
||||
|
||||
void testCaseInsensitiveAttributeNames()
|
||||
{
|
||||
ScopedFileCopy copy("silence-1", ".wma");
|
||||
{
|
||||
ASF::File f(copy.fileName().c_str());
|
||||
f.tag()->setAttribute("REPLAYGAIN_TRACK_GAIN", String("-6.00 dB"));
|
||||
f.save();
|
||||
}
|
||||
{
|
||||
ASF::File f(copy.fileName().c_str());
|
||||
PropertyMap tags = f.properties();
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("-6.00 dB"), tags["REPLAYGAIN_TRACK_GAIN"]);
|
||||
|
||||
// Replacing the value must not leave a second, differently cased
|
||||
// attribute behind.
|
||||
tags["REPLAYGAIN_TRACK_GAIN"] = StringList("-7.00 dB");
|
||||
f.setProperties(tags);
|
||||
CPPUNIT_ASSERT(!f.tag()->contains("REPLAYGAIN_TRACK_GAIN"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("-7.00 dB"),
|
||||
f.tag()->attribute("replaygain_track_gain").front().toString());
|
||||
tags = f.properties();
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("-7.00 dB"), tags["REPLAYGAIN_TRACK_GAIN"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -224,6 +224,7 @@ class TestFileRefDetectByContent : public CppUnit::TestFixture
|
||||
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
CPPUNIT_TEST(testLosslessWma);
|
||||
CPPUNIT_TEST(testRealExampleWma);
|
||||
CPPUNIT_TEST(testSilence1Wma);
|
||||
#endif
|
||||
|
||||
@@ -414,6 +415,7 @@ public:
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
// -- ASF::File --
|
||||
void testLosslessWma() { detectByContent<ASF::File>("lossless.wma"); }
|
||||
void testRealExampleWma() { detectByContent<ASF::File>("real_example.wma"); }
|
||||
void testSilence1Wma() { detectByContent<ASF::File>("silence-1.wma"); }
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user