From: Lukáš Lalinský Date: Fri, 23 Nov 2012 08:32:00 +0000 (+0100) Subject: Implement the PropertyMap interface for WMA X-Git-Tag: v1.9~94^2~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=812f63502b6b1aaf475edb555458b73357eee7f7;p=taglib Implement the PropertyMap interface for WMA --- diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp index 96b8706f..eb000924 100644 --- a/taglib/asf/asffile.cpp +++ b/taglib/asf/asffile.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include "asffile.h" #include "asftag.h" @@ -403,6 +404,21 @@ ASF::Tag *ASF::File::tag() const return d->tag; } +PropertyMap ASF::File::properties() const +{ + return d->tag->properties(); +} + +void ASF::File::removeUnsupportedProperties(const StringList &properties) +{ + d->tag->removeUnsupportedProperties(properties); +} + +PropertyMap ASF::File::setProperties(const PropertyMap &properties) +{ + return d->tag->setProperties(properties); +} + ASF::Properties *ASF::File::audioProperties() const { return d->properties; diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h index 3a7eef56..7d0e3bc7 100644 --- a/taglib/asf/asffile.h +++ b/taglib/asf/asffile.h @@ -90,6 +90,22 @@ namespace TagLib { */ virtual Tag *tag() const; + /*! + * Implements the unified property interface -- export function. + */ + PropertyMap properties() const; + + /*! + * Removes unsupported properties. Forwards to the actual Tag's + * removeUnsupportedProperties() function. + */ + void removeUnsupportedProperties(const StringList &properties); + + /*! + * Implements the unified property interface -- import function. + */ + PropertyMap setProperties(const PropertyMap &); + /*! * Returns the ASF audio properties for this file. */ diff --git a/taglib/asf/asftag.cpp b/taglib/asf/asftag.cpp index b7b541fc..1cbd16eb 100644 --- a/taglib/asf/asftag.cpp +++ b/taglib/asf/asftag.cpp @@ -27,6 +27,7 @@ #include #endif +#include #include "asftag.h" using namespace TagLib; @@ -196,3 +197,162 @@ bool ASF::Tag::isEmpty() const d->attributeListMap.isEmpty(); } +static const char *keyTranslation[][2] = { + { "WM/AlbumTitle", "ALBUM" }, + { "WM/Composer", "COMPOSER" }, + { "WM/Writer", "WRITER" }, + { "WM/Conductor", "CONDUCTOR" }, + { "WM/ModifiedBy", "REMIXER" }, + { "WM/Year", "DATE" }, + { "WM/OriginalReleaseYear", "ORIGINALDATE" }, + { "WM/Producer", "PRODUCER" }, + { "WM/ContentGroupDescription", "GROUPING" }, + { "WM/SubTitle", "SUBTITLE" }, + { "WM/SetSubTitle", "DISCSUBTITLE" }, + { "WM/TrackNumber", "TRACKNUMBER" }, + { "WM/PartOfSet", "DISCNUMBER" }, + { "WM/Genre", "GENRE" }, + { "WM/BeatsPerMinute", "BPM" }, + { "WM/Mood", "MOOD" }, + { "WM/ISRC", "ISRC" }, + { "WM/Lyrics", "LYRICS" }, + { "WM/Media", "MEDIA" }, + { "WM/Publisher", "LABEL" }, + { "WM/CatalogNo", "CATALOGNUMBER" }, + { "WM/Barcode", "BARCODE" }, + { "WM/EncodedBy", "ENCODEDBY" }, + { "WM/AlbumSortOrder", "ALBUMSORT" }, + { "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" }, + { "WM/ArtistSortOrder", "ARTISTSORT" }, + { "WM/TitleSortOrder", "TITLESORT" }, + { "WM/Script", "SCRIPT" }, + { "WM/Language", "LANGUAGE" }, + { "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" }, + { "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" }, + { "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" }, + { "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, + { "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" }, + { "MusicIP/PUID", "MUSICIP_PUID" }, + { "Acoustid/Id", "ACOUSTID_ID" }, + { "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" }, +}; + +PropertyMap ASF::Tag::properties() const +{ + static Map keyMap; + if(keyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + keyMap[keyTranslation[i][0]] = keyTranslation[i][1]; + } + } + + PropertyMap props; + + if(!d->title.isEmpty()) { + props["TITLE"] = d->title; + } + if(!d->artist.isEmpty()) { + props["ARTIST"] = d->artist; + } + if(!d->copyright.isEmpty()) { + props["COPYRIGHT"] = d->copyright; + } + if(!d->comment.isEmpty()) { + props["COMMENT"] = d->comment; + } + + ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin(); + for(; it != d->attributeListMap.end(); ++it) { + if(keyMap.contains(it->first)) { + String key = keyMap[it->first]; + AttributeList::ConstIterator it2 = it->second.begin(); + for(; it2 != it->second.end(); ++it2) { + if(key == "TRACKNUMBER") { + if(it2->type() == ASF::Attribute::DWordType) + props.insert(key, String::number(it2->toUInt())); + else + props.insert(key, it2->toString()); + } + else { + props.insert(key, it2->toString()); + } + } + } + else { + props.unsupportedData().append(it->first); + } + } + return props; +} + +void ASF::Tag::removeUnsupportedProperties(const StringList &props) +{ + StringList::ConstIterator it = props.begin(); + for(; it != props.end(); ++it) + d->attributeListMap.erase(*it); +} + +PropertyMap ASF::Tag::setProperties(const PropertyMap &props) +{ + static Map reverseKeyMap; + if(reverseKeyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0]; + } + } + + PropertyMap origProps = properties(); + PropertyMap::ConstIterator it = origProps.begin(); + for(; it != origProps.end(); ++it) { + if(!props.contains(it->first) || props[it->first].isEmpty()) { + if(it->first == "TITLE") { + d->title = String::null; + } + else if(it->first == "ARTIST") { + d->artist = String::null; + } + else if(it->first == "COMMENT") { + d->comment = String::null; + } + else if(it->first == "COPYRIGHT") { + d->copyright = String::null; + } + else { + d->attributeListMap.erase(reverseKeyMap[it->first]); + } + } + } + + PropertyMap ignoredProps; + it = props.begin(); + for(; it != props.end(); ++it) { + if(reverseKeyMap.contains(it->first)) { + String name = reverseKeyMap[it->first]; + removeItem(name); + StringList::ConstIterator it2 = it->second.begin(); + for(; it2 != it->second.end(); ++it2) { + addAttribute(name, *it2); + } + } + else if(it->first == "TITLE") { + d->title = it->second.toString(); + } + else if(it->first == "ARTIST") { + d->artist = it->second.toString(); + } + else if(it->first == "COMMENT") { + d->comment = it->second.toString(); + } + else if(it->first == "COPYRIGHT") { + d->copyright = it->second.toString(); + } + else { + ignoredProps.insert(it->first, it->second); + } + } + + return ignoredProps; +} diff --git a/taglib/asf/asftag.h b/taglib/asf/asftag.h index 6b2d2bf3..f68579d8 100644 --- a/taglib/asf/asftag.h +++ b/taglib/asf/asftag.h @@ -176,6 +176,10 @@ namespace TagLib { */ void addAttribute(const String &name, const Attribute &attribute); + PropertyMap properties() const; + void removeUnsupportedProperties(const StringList& properties); + PropertyMap setProperties(const PropertyMap &properties); + private: class TagPrivate; diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index f94a074e..e2b84311 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -368,7 +368,7 @@ static const char *frameTranslation[][2] = { { "TPE4", "REMIXER" }, // could also be ARRANGER { "TPOS", "DISCNUMBER" }, { "TPRO", "PRODUCEDNOTICE" }, - { "TPUB", "PUBLISHER" }, + { "TPUB", "LABEL" }, { "TRCK", "TRACKNUMBER" }, { "TRSN", "RADIOSTATION" }, { "TRSO", "RADIOSTATIONOWNER" }, diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 24579496..7251f3f7 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -155,9 +155,8 @@ PropertyMap File::properties() const return dynamic_cast(this)->properties(); if(dynamic_cast(this)) return dynamic_cast(this)->properties(); - // no specialized implementation available -> use generic one - // - ASF: ugly format, largely undocumented, not worth implementing - // dict interface ... + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); return tag()->properties(); } @@ -195,6 +194,8 @@ void File::removeUnsupportedProperties(const StringList &properties) dynamic_cast(this)->removeUnsupportedProperties(properties); else if(dynamic_cast(this)) dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); else tag()->removeUnsupportedProperties(properties); } @@ -235,6 +236,8 @@ PropertyMap File::setProperties(const PropertyMap &properties) return dynamic_cast(this)->setProperties(properties); else if(dynamic_cast(this)) return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); else return tag()->setProperties(properties); } diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h index 51d5df6f..2be49ddb 100644 --- a/taglib/toolkit/tpropertymap.h +++ b/taglib/toolkit/tpropertymap.h @@ -83,6 +83,7 @@ namespace TagLib { * - MOOD * - COMMENT * - MEDIA + * - LABEL * - CATALOGNUMBER * - BARCODE * diff --git a/tests/test_asf.cpp b/tests/test_asf.cpp index 0cd1f3d7..8610c24b 100644 --- a/tests/test_asf.cpp +++ b/tests/test_asf.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include "utils.h" @@ -13,7 +14,7 @@ using namespace TagLib; class TestASF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestASF); - CPPUNIT_TEST(testProperties); + CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testRead); CPPUNIT_TEST(testSaveMultipleValues); CPPUNIT_TEST(testSaveStream); @@ -22,11 +23,12 @@ class TestASF : public CppUnit::TestFixture CPPUNIT_TEST(testSaveLargeValue); CPPUNIT_TEST(testSavePicture); CPPUNIT_TEST(testSaveMultiplePictures); + CPPUNIT_TEST(testProperties); CPPUNIT_TEST_SUITE_END(); public: - void testProperties() + void testAudioProperties() { ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->length()); @@ -215,6 +217,39 @@ public: delete f; } + void testProperties() + { + ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); + + PropertyMap tags = f.properties(); + + tags["TRACKNUMBER"] = StringList("2"); + tags["DISCNUMBER"] = StringList("3"); + tags["BPM"] = StringList("123"); + tags["ARTIST"] = StringList("Foo Bar"); + f.setProperties(tags); + + tags = f.properties(); + + CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.tag()->artist()); + CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]); + + CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/BeatsPerMinute")); + CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/BeatsPerMinute"].size()); + CPPUNIT_ASSERT_EQUAL(String("123"), f.tag()->attributeListMap()["WM/BeatsPerMinute"].front().toString()); + CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]); + + CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/TrackNumber")); + CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/TrackNumber"].size()); + CPPUNIT_ASSERT_EQUAL(String("2"), f.tag()->attributeListMap()["WM/TrackNumber"].front().toString()); + CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["TRACKNUMBER"]); + + CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/PartOfSet")); + CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/PartOfSet"].size()); + CPPUNIT_ASSERT_EQUAL(String("3"), f.tag()->attributeListMap()["WM/PartOfSet"].front().toString()); + CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["DISCNUMBER"]); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestASF);