]> granicus.if.org Git - taglib/commitdiff
Fixed handling of UnknownFrames in ID3v2.
authorMichael Helmling <helmling@mathematik.uni-kl.de>
Sun, 26 Feb 2012 18:21:57 +0000 (19:21 +0100)
committerMichael Helmling <helmling@mathematik.uni-kl.de>
Sun, 26 Feb 2012 18:21:57 +0000 (19:21 +0100)
- If an unknown frame with id "XXXX" occurs, an entry
"UNKNOWN/XXXX" is added to unsupportedData().
The removeUnsupportedProperties() method in turn
removes all unknown frames with id "XXXX" if it
encounters a string "UNKNOWN/XXXX" in the given list.

- Implemented findByDescription() to UnsynchronizedLyricsFrame
in order to support removal of lyrics frames with unsupported
keys.

- Adapted id3v2 test case to new QuodLibet policy.

taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h
taglib/mpeg/id3v2/id3v2frame.cpp
taglib/mpeg/id3v2/id3v2tag.cpp
taglib/mpeg/id3v2/id3v2tag.h
tests/test_id3v2.cpp

index 6719a9b3e04907d82926a0d4094aab6ee34fe47c..9d76164da9c5eb0ed04f4ef72385b4e4b13e999c 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "unsynchronizedlyricsframe.h"
 #include <tbytevectorlist.h>
+#include <id3v2tag.h>
 #include <tdebug.h>
 #include <tpropertymap.h>
 
@@ -125,6 +126,17 @@ PropertyMap UnsynchronizedLyricsFrame::asProperties() const
   return map;
 }
 
+UnsynchronizedLyricsFrame *UnsynchronizedLyricsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
+{
+  ID3v2::FrameList lyrics = tag->frameList("USLT");
+
+  for(ID3v2::FrameList::ConstIterator it = lyrics.begin(); it != lyrics.end(); ++it){
+    UnsynchronizedLyricsFrame *frame = dynamic_cast<UnsynchronizedLyricsFrame *>(*it);
+    if(frame && frame->description() == d)
+      return frame;
+  }
+  return 0;
+}
 ////////////////////////////////////////////////////////////////////////////////
 // protected members
 ////////////////////////////////////////////////////////////////////////////////
index 2a3c5f9a41bb333e62a74c6e3c04ddf456e3eb43..3af354fcd5909319dbe5a3d92275dfbced6a9c0b 100644 (file)
@@ -147,6 +147,15 @@ namespace TagLib {
        */
       PropertyMap asProperties() const;
 
+      /*!
+       * LyricsFrames each have a unique description.  This searches for a lyrics
+       * frame with the decription \a d and returns a pointer to it.  If no
+       * frame is found that matches the given description null is returned.
+       *
+       * \see description()
+       */
+      static UnsynchronizedLyricsFrame *findByDescription(const Tag *tag, const String &d);
+
     protected:
       // Reimplementations.
 
index ed023123871b6f603d5f995f318237b5b554c76e..6b45f223746829e1c5ca8e95d9ce2453c9a95c8a 100644 (file)
@@ -43,6 +43,7 @@
 #include "frames/urllinkframe.h"
 #include "frames/unsynchronizedlyricsframe.h"
 #include "frames/commentsframe.h"
+#include "frames/unknownframe.h"
 
 using namespace TagLib;
 using namespace ID3v2;
@@ -429,18 +430,16 @@ ByteVector Frame::keyToFrameID(const String &s)
 
 PropertyMap Frame::asProperties() const
 {
+  if(dynamic_cast< const UnknownFrame *>(this)) {
+    PropertyMap m;
+    m.unsupportedData().append("UNKNOWN/" + frameID());
+    return m;
+  }
   const ByteVector &id = frameID();
   // workaround until this function is virtual
-  if(id == "TXXX") {
-    const UserTextIdentificationFrame *txxxFrame = dynamic_cast< const UserTextIdentificationFrame* >(this);
-    if(txxxFrame != NULL)
-      return txxxFrame->asProperties();
-    else {
-      PropertyMap m;
-      m.unsupportedData().append("UNKNOWN");
-      return m;
-    }
-  } else if(id[0] == 'T')
+  if(id == "TXXX")
+    return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
+  else if(id[0] == 'T')
     return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
   else if(id == "WXXX")
     return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
index 49c0c517800ce41dedf0287a69d28c5d18577e49..ce22833071fdac12fa6a0a0d1a351d71f44ba30a 100644 (file)
@@ -346,35 +346,36 @@ PropertyMap ID3v2::Tag::properties() const
 
 void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
 {
-  // entries of unsupportedData() are usually frame IDs which are not supported
-  // by the PropertyMap interface. Three special cases exist: TXXX, WXXX, and COMM
-  // frames may also be unsupported if their description() is not a valid key.
   for(StringList::ConstIterator it = properties.begin(); it != properties.end(); ++it){
-    if(*it == "UNKNOWN") {
-      // delete all unknown frames
-      FrameList l = frameList();
+    if(it->startsWith("UNKNOWN/")) {
+      String frameID = it->substr(String("UNKNOWN/").size());
+      if(frameID.size() != 4)
+        continue; // invalid specification
+      ByteVector id = frameID.data(String::Latin1);
+      // delete all unknown frames of given type
+      FrameList l = frameList(id);
       for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++)
         if (dynamic_cast<const UnknownFrame *>(*fit) != NULL)
           removeFrame(*fit);
+    } else if(it->size() == 4){
+      ByteVector id = it->data(String::Latin1);
+      removeFrames(id);
     } else {
       ByteVector id = it->substr(0,4).data(String::Latin1);
-      if(id == "TXXX") {
-        String description = it->substr(5);
-        Frame *frame = UserTextIdentificationFrame::find(this, description);
-        if(frame)
-          removeFrame(frame);
-      } else if(id == "WXXX") {
-        String description = it->substr(5);
-        Frame *frame = UserUrlLinkFrame::find(this, description);
-        if(frame)
-          removeFrame(frame);
-      } else if(id == "COMM") {
-        String description = it->substr(5);
-        Frame *frame = CommentsFrame::findByDescription(this, description);
-        if(frame)
-          removeFrame(frame);
-      } else
-        removeFrames(id); // there should be only one frame with "id"
+      if(it->size() <= 5)
+        continue; // invalid specification
+      String description = it->substr(5);
+      Frame *frame;
+      if(id == "TXXX")
+        frame = UserTextIdentificationFrame::find(this, description);
+      else if(id == "WXXX")
+        frame = UserUrlLinkFrame::find(this, description);
+      else if(id == "COMM")
+        frame = CommentsFrame::findByDescription(this, description);
+      else if(id == "USLT")
+        frame = UnsynchronizedLyricsFrame::findByDescription(this, description);
+      if(frame)
+        removeFrame(frame);
     }
   }
 }
index 3728868af2a414fc8af63c2ad16b32c56cd03cea..94784e7614e4c63e34f5a60029489776bf931fd5 100644 (file)
@@ -293,9 +293,14 @@ namespace TagLib {
 
       /*!
        * Removes unsupported frames given by \a properties. The elements of
-       * \a properties must be taken from properties().unsupportedData() and
-       * are the four-byte frame IDs of ID3 frames which are not compatible
-       * with the PropertyMap schema.
+       * \a properties must be taken from properties().unsupportedData(); they
+       * are of one of the following forms:
+       *  - a four-character frame ID, if the ID3 specification allows only one
+       *    frame with that ID (thus, the frame is uniquely determined)
+       *  - frameID + "/" + description(), when the ID is one of "TXXX", "WXXX",
+       *    "COMM", or "USLT",
+       *  - "UNKNOWN/" + frameID, for frames that could not be parsed by TagLib.
+       *    In that case, *all* unknown frames with the given ID will be removed.
        */
       void removeUnsupportedProperties(const StringList &properties);
 
index 530b3f3d9ba485955af643ce617c7482a7bddc21..49ffef0d899d85aa5e4616429f467b48730f3c7f 100644 (file)
@@ -558,10 +558,15 @@ public:
     MPEG::File f(newname.c_str());
     PropertyMap dict = f.ID3v2Tag(false)->properties();
     CPPUNIT_ASSERT_EQUAL(uint(6), dict.size());
+
+    CPPUNIT_ASSERT(dict.contains("USERTEXTDESCRIPTION1"));
+    CPPUNIT_ASSERT(dict.contains("QuodLibet::USERTEXTDESCRIPTION2"));
+    CPPUNIT_ASSERT_EQUAL(uint(2), dict["USERTEXTDESCRIPTION1"].size());
+    CPPUNIT_ASSERT_EQUAL(uint(2), dict["QuodLibet::USERTEXTDESCRIPTION2"].size());
     CPPUNIT_ASSERT_EQUAL(String("userTextData1"), dict["USERTEXTDESCRIPTION1"][0]);
     CPPUNIT_ASSERT_EQUAL(String("userTextData2"), dict["USERTEXTDESCRIPTION1"][1]);
-    CPPUNIT_ASSERT_EQUAL(String("userTextData1"), dict["USERTEXTDESCRIPTION2"][0]);
-    CPPUNIT_ASSERT_EQUAL(String("userTextData2"), dict["USERTEXTDESCRIPTION2"][1]);
+    CPPUNIT_ASSERT_EQUAL(String("userTextData1"), dict["QuodLibet::USERTEXTDESCRIPTION2"][0]);
+    CPPUNIT_ASSERT_EQUAL(String("userTextData2"), dict["QuodLibet::USERTEXTDESCRIPTION2"][1]);
 
     CPPUNIT_ASSERT_EQUAL(String("Pop"), dict["GENRE"].front());