]> granicus.if.org Git - taglib/commitdiff
Add accessors to manipulate MP4 tags without modifying the internal structure
authorScott Wheeler <scott@directededge.com>
Mon, 18 May 2015 19:18:33 +0000 (21:18 +0200)
committerScott Wheeler <scott@directededge.com>
Mon, 18 May 2015 19:18:33 +0000 (21:18 +0200)
This brings the MP4 API in line closer to other tag formats and makes it
possible to access the tag data from const functions.

"ItemListMap" has been renamed to "ItemMap" (with the old version
deprecated).  It seems that the "ListMap" notion was copied probably
from Allan's ApeTag implementation, which incorrectly copied the term
from the XiphTag.  Notably, in XiphTag, because a field can have multiple
values, the "ListMap" is a map of lists.  Calling things a "ListMap" where
there can be only one value doesn't fit.

Closes #255

taglib/mp4/mp4tag.cpp
taglib/mp4/mp4tag.h
tests/test_mp4.cpp

index 08e5f411a9cea1c36d3f20a7b556195507b9dda5..8e58a1d3516a3d2f0aff5c64520edf3c0080efb2 100644 (file)
@@ -39,7 +39,7 @@ public:
   ~TagPrivate() {}
   TagLib::File *file;
   Atoms *atoms;
-  ItemListMap items;
+  ItemMap items;
 };
 
 MP4::Tag::Tag()
@@ -451,7 +451,7 @@ bool
 MP4::Tag::save()
 {
   ByteVector data;
-  for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
+  for(MP4::ItemMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
     const String name = i->first;
     if(name.startsWith("----")) {
       data.append(renderFreeForm(name, i->second));
@@ -765,12 +765,36 @@ bool MP4::Tag::isEmpty() const
   return d->items.isEmpty();
 }
 
-MP4::ItemListMap &
-MP4::Tag::itemListMap()
+MP4::ItemMap &MP4::Tag::itemListMap()
 {
   return d->items;
 }
 
+const MP4::ItemMap &MP4::Tag::itemMap() const
+{
+  return d->items;
+}
+
+MP4::Item MP4::Tag::item(const String &key) const
+{
+  return d->items[key];
+}
+
+void MP4::Tag::setItem(const String &key, const Item &value)
+{
+  d->items[key] = value;
+}
+
+void MP4::Tag::removeItem(const String &key)
+{
+  d->items.erase(key);
+}
+
+bool MP4::Tag::contains(const String &key) const
+{
+  return d->items.contains(key);
+}
+
 static const char *keyTranslation[][2] = {
   { "\251nam", "TITLE" },
   { "\251ART", "ARTIST" },
@@ -832,7 +856,7 @@ PropertyMap MP4::Tag::properties() const
   }
 
   PropertyMap props;
-  MP4::ItemListMap::ConstIterator it = d->items.begin();
+  MP4::ItemMap::ConstIterator it = d->items.begin();
   for(; it != d->items.end(); ++it) {
     if(keyMap.contains(it->first)) {
       String key = keyMap[it->first];
index cde68964e36676dd38e5f6f36c022264fa7a4d79..f74cb7cd82c4ba5cb0ae9e65d691462f09850987 100644 (file)
@@ -39,7 +39,11 @@ namespace TagLib {
 
   namespace MP4 {
 
+    /*!
+     * \deprecated
+     */
     typedef TagLib::Map<String, Item> ItemListMap;
+    typedef TagLib::Map<String, Item> ItemMap;
 
     class TAGLIB_EXPORT Tag: public TagLib::Tag
     {
@@ -67,7 +71,36 @@ namespace TagLib {
 
         virtual bool isEmpty() const;
 
-        ItemListMap &itemListMap();
+        /*!
+         * \deprecated Use the item() and setItem() API instead
+         */
+        ItemMap &itemListMap();
+
+        /*!
+         * Returns a string-keyed map of the MP4::Items for this tag.
+         */
+        const ItemMap &itemMap() const;
+
+        /*!
+         * \return The item, if any, corresponding to \a key.
+         */
+        Item item(const String &key) const;
+
+        /*!
+         * Sets the value of \a key to \a value, overwriting any previous value.
+         */
+        void setItem(const String &key, const Item &value);
+
+        /*!
+         * Removes the entry with \a key from the tag, or does nothing if it does
+         * not exist.
+         */
+        void removeItem(const String &key);
+
+        /*!
+         * \return True if the tag contains an entry for \a key.
+         */
+        bool contains(const String &key) const;
 
         PropertyMap properties() const;
         void removeUnsupportedProperties(const StringList& properties);
index 9bd2deb7ab176be8438e4f6d3a66140c73b3acfa..dd6e3a038e30f61b2ab0810ef3cf0160d221884e 100644 (file)
@@ -71,7 +71,7 @@ public:
     CPPUNIT_ASSERT(!t1.isEmpty());
 
     MP4::Tag t2;
-    t2.itemListMap()["foo"] = "bar";
+    t2.setItem("foo", "bar");
     CPPUNIT_ASSERT(!t2.isEmpty());
   }
 
@@ -128,14 +128,15 @@ public:
     string filename = copy.fileName();
 
     MP4::File *f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("----:com.apple.iTunes:iTunNORM"));
-    f->tag()->itemListMap()["----:org.kde.TagLib:Foo"] = StringList("Bar");
+    CPPUNIT_ASSERT(f->tag()->contains("----:com.apple.iTunes:iTunNORM"));
+    f->tag()->setItem("----:org.kde.TagLib:Foo", StringList("Bar"));
     f->save();
     delete f;
 
     f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("----:org.kde.TagLib:Foo"));
-    CPPUNIT_ASSERT_EQUAL(String("Bar"), f->tag()->itemListMap()["----:org.kde.TagLib:Foo"].toStringList()[0]);
+    CPPUNIT_ASSERT(f->tag()->contains("----:org.kde.TagLib:Foo"));
+    CPPUNIT_ASSERT_EQUAL(String("Bar"),
+                         f->tag()->item("----:org.kde.TagLib:Foo").toStringList().front());
     f->save();
     delete f;
   }
@@ -146,14 +147,16 @@ public:
     string filename = copy.fileName();
 
     MP4::File *f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT_EQUAL(String("82,164"), f->tag()->itemListMap()["----:com.apple.iTunes:replaygain_track_minmax"].toStringList()[0]);
+    CPPUNIT_ASSERT_EQUAL(String("82,164"),
+                         f->tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front());
     CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f->tag()->artist());
     f->tag()->setComment("foo");
     f->save();
     delete f;
 
     f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT_EQUAL(String("82,164"), f->tag()->itemListMap()["----:com.apple.iTunes:replaygain_track_minmax"].toStringList()[0]);
+    CPPUNIT_ASSERT_EQUAL(String("82,164"),
+                         f->tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front());
     CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f->tag()->artist());
     CPPUNIT_ASSERT_EQUAL(String("foo"), f->tag()->comment());
     delete f;
@@ -165,20 +168,20 @@ public:
     string filename = copy.fileName();
 
     MP4::File *f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["cpil"].toBool());
+    CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemMap()["cpil"].toBool());
 
     MP4::Atoms *atoms = new MP4::Atoms(f);
     MP4::Atom *moov = atoms->atoms[0];
     CPPUNIT_ASSERT_EQUAL(long(77), moov->length);
 
-    f->tag()->itemListMap()["pgap"] = true;
+    f->tag()->setItem("pgap", true);
     f->save();
     delete atoms;
     delete f;
 
     f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["cpil"].toBool());
-    CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["pgap"].toBool());
+    CPPUNIT_ASSERT_EQUAL(true, f->tag()->item("cpil").toBool());
+    CPPUNIT_ASSERT_EQUAL(true, f->tag()->item("pgap").toBool());
 
     atoms = new MP4::Atoms(f);
     moov = atoms->atoms[0];
@@ -198,8 +201,8 @@ public:
   void testCovrRead()
   {
     MP4::File *f = new MP4::File(TEST_FILE_PATH_C("has-tags.m4a"));
-    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
-    MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
+    CPPUNIT_ASSERT(f->tag()->contains("covr"));
+    MP4::CoverArtList l = f->tag()->item("covr").toCoverArtList();
     CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), l.size());
     CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
     CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
@@ -214,16 +217,16 @@ public:
     string filename = copy.fileName();
 
     MP4::File *f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
-    MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
+    CPPUNIT_ASSERT(f->tag()->contains("covr"));
+    MP4::CoverArtList l = f->tag()->item("covr").toCoverArtList();
     l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo"));
-    f->tag()->itemListMap()["covr"] = l;
+    f->tag()->setItem("covr", l);
     f->save();
     delete f;
 
     f = new MP4::File(filename.c_str());
-    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
-    l = f->tag()->itemListMap()["covr"].toCoverArtList();
+    CPPUNIT_ASSERT(f->tag()->contains("covr"));
+    l = f->tag()->item("covr").toCoverArtList();
     CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), l.size());
     CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
     CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
@@ -237,8 +240,8 @@ public:
   void testCovrRead2()
   {
     MP4::File *f = new MP4::File(TEST_FILE_PATH_C("covr-junk.m4a"));
-    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
-    MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
+    CPPUNIT_ASSERT(f->tag()->contains("covr"));
+    MP4::CoverArtList l = f->tag()->item("covr").toCoverArtList();
     CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), l.size());
     CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
     CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
@@ -264,26 +267,26 @@ public:
 
     tags = f.properties();
 
-    CPPUNIT_ASSERT(f.tag()->itemListMap().contains("trkn"));
-    CPPUNIT_ASSERT_EQUAL(2, f.tag()->itemListMap()["trkn"].toIntPair().first);
-    CPPUNIT_ASSERT_EQUAL(4, f.tag()->itemListMap()["trkn"].toIntPair().second);
+    CPPUNIT_ASSERT(f.tag()->contains("trkn"));
+    CPPUNIT_ASSERT_EQUAL(2, f.tag()->item("trkn").toIntPair().first);
+    CPPUNIT_ASSERT_EQUAL(4, f.tag()->item("trkn").toIntPair().second);
     CPPUNIT_ASSERT_EQUAL(StringList("2/4"), tags["TRACKNUMBER"]);
 
-    CPPUNIT_ASSERT(f.tag()->itemListMap().contains("disk"));
-    CPPUNIT_ASSERT_EQUAL(3, f.tag()->itemListMap()["disk"].toIntPair().first);
-    CPPUNIT_ASSERT_EQUAL(5, f.tag()->itemListMap()["disk"].toIntPair().second);
+    CPPUNIT_ASSERT(f.tag()->contains("disk"));
+    CPPUNIT_ASSERT_EQUAL(3, f.tag()->item("disk").toIntPair().first);
+    CPPUNIT_ASSERT_EQUAL(5, f.tag()->item("disk").toIntPair().second);
     CPPUNIT_ASSERT_EQUAL(StringList("3/5"), tags["DISCNUMBER"]);
 
-    CPPUNIT_ASSERT(f.tag()->itemListMap().contains("tmpo"));
-    CPPUNIT_ASSERT_EQUAL(123, f.tag()->itemListMap()["tmpo"].toInt());
+    CPPUNIT_ASSERT(f.tag()->contains("tmpo"));
+    CPPUNIT_ASSERT_EQUAL(123, f.tag()->item("tmpo").toInt());
     CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]);
 
-    CPPUNIT_ASSERT(f.tag()->itemListMap().contains("\251ART"));
-    CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), f.tag()->itemListMap()["\251ART"].toStringList());
+    CPPUNIT_ASSERT(f.tag()->contains("\251ART"));
+    CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), f.tag()->item("\251ART").toStringList());
     CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]);
 
-    CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil"));
-    CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemListMap()["cpil"].toBool());
+    CPPUNIT_ASSERT(f.tag()->contains("cpil"));
+    CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("cpil").toBool());
     CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["COMPILATION"]);
 
     tags["COMPILATION"] = StringList("0");
@@ -291,8 +294,8 @@ public:
 
     tags = f.properties();
 
-    CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil"));
-    CPPUNIT_ASSERT_EQUAL(false, f.tag()->itemListMap()["cpil"].toBool());
+    CPPUNIT_ASSERT(f.tag()->contains("cpil"));
+    CPPUNIT_ASSERT_EQUAL(false, f.tag()->item("cpil").toBool());
     CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["COMPILATION"]);
   }