]> granicus.if.org Git - taglib/commitdiff
Implemented asProperties() in all relevant textual frames.
authorMichael Helmling <helmling@mathematik.uni-kl.de>
Sun, 22 Jan 2012 16:08:02 +0000 (17:08 +0100)
committerMichael Helmling <helmling@mathematik.uni-kl.de>
Sun, 22 Jan 2012 16:08:02 +0000 (17:08 +0100)
16 files changed:
taglib/CMakeLists.txt
taglib/mpeg/id3v2/frames/commentsframe.cpp
taglib/mpeg/id3v2/frames/commentsframe.h
taglib/mpeg/id3v2/frames/textidentificationframe.cpp
taglib/mpeg/id3v2/frames/textidentificationframe.h
taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h
taglib/mpeg/id3v2/frames/urllinkframe.cpp
taglib/mpeg/id3v2/frames/urllinkframe.h
taglib/mpeg/id3v2/id3v2frame.cpp
taglib/mpeg/id3v2/id3v2frame.h
taglib/mpeg/id3v2/id3v2tag.cpp
taglib/mpeg/mpegfile.cpp
taglib/tagunion.cpp
taglib/tagunion.h
taglib/toolkit/tpropertymap.h

index a7777a5b97ee008c83e12eca3d1130cf06a114d6..ca62f3a460196eecf24f7487dda9cf9dd15e2b86 100644 (file)
@@ -55,7 +55,6 @@ set(tag_HDRS
   mpeg/xingheader.h
   mpeg/id3v1/id3v1tag.h
   mpeg/id3v1/id3v1genres.h
-  mpeg/id3v2/id3v2dicttools.h
   mpeg/id3v2/id3v2extendedheader.h
   mpeg/id3v2/id3v2frame.h
   mpeg/id3v2/id3v2header.h
@@ -139,7 +138,6 @@ set(id3v1_SRCS
 )
 
 set(id3v2_SRCS
-  mpeg/id3v2/id3v2dicttools.cpp
   mpeg/id3v2/id3v2framefactory.cpp
   mpeg/id3v2/id3v2synchdata.cpp
   mpeg/id3v2/id3v2tag.cpp
index 377a7ee3c3b97b50034f85ae4d1db05c48908e7f..5730b7531115e1771a03f22b83c5e3d2f9937d8b 100644 (file)
@@ -109,6 +109,19 @@ void CommentsFrame::setTextEncoding(String::Type encoding)
   d->textEncoding = encoding;
 }
 
+PropertyMap CommentsFrame::asDescription() const
+{
+  String key = PropertyMap::prepareKey(description());
+  PropertyMap map;
+  if(key.isEmpty())
+    key = "COMMENT";
+  if(key.isNull())
+    map.unsupportedData().append(L"COMM/" + description());
+  else
+    map.insert(key, text());
+  return map;
+}
+
 CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
 {
   ID3v2::FrameList comments = tag->frameList("COMM");
index def01dcfcf2a260158e47c19a75215006c64be90..30d21ca1effafc342f52ac0f6b778bed0009d2ef 100644 (file)
@@ -136,6 +136,17 @@ namespace TagLib {
        */
       void setTextEncoding(String::Type encoding);
 
+      /*!
+       * Parses this frame as PropertyMap.
+       * - description() will be used as key
+       * - if description() is empty, the key will be "COMMENT"
+       * - if description() is not a valid PropertyMap key, the frame will be
+       *   marked unsupported by an entry "COMM/<description>" in the unsupportedData()
+       *   attribute of the returned map.
+       * - The single value will be the frame's text().
+       */
+      PropertyMap asDescription() const;
+
       /*!
        * Comments each have a unique description.  This searches for a comment
        * frame with the decription \a d and returns a pointer to it.  If no
index 06489ea342972f2e2e8ca4389b53ca3249c396bf..44528f33e72ee5fd355a81cdbb7cf90e2891b494 100644 (file)
@@ -92,6 +92,40 @@ void TextIdentificationFrame::setTextEncoding(String::Type encoding)
   d->textEncoding = encoding;
 }
 
+PropertyMap TextIdentificationFrame::asProperties() const
+{
+  if(frameID() == "TIPL")
+    return makeTIPLProperties();
+  if(frameID() == "TMCL")
+    return makeTMCLProperties();
+  PropertyMap map;
+  String tagName = frameIDToTagName(frameID());
+  if(tagName.isNull()) {
+    map.unsupportedData().append(frameID());
+    return map;
+  }
+  StringList values = fieldList();
+  if(tagName == "GENRE") {
+    // Special case: Support ID3v1-style genre numbers. They are not officially supported in
+    // ID3v2, however it seems that still a lot of programs use them.
+    for(StringList::Iterator it = values.begin(); it != values.end(); ++it) {
+      bool ok = false;
+      int test = it->toInt(&ok); // test if the genre value is an integer
+      if(ok)
+        *it = ID3v1::genre(test);
+    }
+  } else if(tagName == "DATE") {
+    for (StringList::Iterator it = values.begin(); it != values.end(); ++it) {
+      // ID3v2 specifies ISO8601 timestamps which contain a 'T' as separator between date and time.
+      // Since this is unusual in other formats, the T is removed.
+      int tpos = it->find("T");
+      if (tpos != -1)
+        (*it)[tpos] = ' ';
+    }
+  }
+  return KeyValuePair(tagName, values);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // TextIdentificationFrame protected members
 ////////////////////////////////////////////////////////////////////////////////
@@ -170,6 +204,63 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header
   parseFields(fieldData(data));
 }
 
+// array of allowed TIPL prefixes and their corresponding key value
+static const uint involvedPeopleSize = 5;
+static const char* involvedPeople[2] = {
+    {"ARRANGER", "ARRANGER"},
+    {"ENGINEER", "ENGINEER"},
+    {"PRODUCER", "PRODUCER"},
+    {"DJ-MIX", "DJMIXER"},
+    {"MIX", "MIXER"}
+};
+
+PropertyMap TextIdentificationFrame::makeTIPLProperties() const
+{
+  PropertyMap map;
+  if(fieldList().size() % 2 != 0){
+    // according to the ID3 spec, TIPL must contain an even number of entries
+    map.unsupportedData().append(frameID());
+    return map;
+  }
+  for(StringList::ConstIterator it = fieldList().begin(); it != fieldList().end(); ++it) {
+    bool found = false;
+    for(uint i = 0; i < involvedPeopleSize; ++i)
+      if(*it == involvedPeople[i][0]) {
+        map.insert(involvedPeople[i][1], (++it).split(","));
+        found = true;
+        break;
+      }
+    if(!found){
+      // invalid involved role -> mark whole frame as unsupported in order to be consisten with writing
+      map.clear();
+      map.unsupportedData().append(frameID());
+      return map;
+    }
+  }
+  return map;
+}
+
+PropertyMap TextIdentificationFrame::makeTMCLProperties() const
+{
+  PropertyMap map;
+  if(fieldList().size() % 2 != 0){
+    // according to the ID3 spec, TMCL must contain an even number of entries
+    map.unsupportedData().append(frameID());
+    return map;
+  }
+  for(StringList::ConstIterator it = fieldList().begin(); it != fieldList().end(); ++it) {
+    String key = PropertyMap::prepareKey(*it);
+    if(key.isNull()) {
+      // instrument is not a valid key -> frame unsupported
+      map.clear();
+      map.unsupportedData().append(frameID());
+      return map;
+    }
+    map.insert(key, (++it).split(","));
+  }
+  return map;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // UserTextIdentificationFrame public members
 ////////////////////////////////////////////////////////////////////////////////
@@ -241,22 +332,17 @@ void UserTextIdentificationFrame::setDescription(const String &s)
 PropertyMap UserTextIdentificationFrame::asProperties() const
 {
   String tagName = description();
-  StringList l(fieldList());
-  // this is done because taglib stores the description also as first entry
-  // in the field list. (why?)
-  StringList::Iterator tagIt = l.find(tagName);
-  if(tagIt != l.end())
-     l.erase(tagIt);
-  // Quodlibet/Exfalso use QuodLibet::<tagname> if you set an arbitrary ID3
-  // tag.
+  // Quodlibet/Exfalso use QuodLibet::<tagname> if you set an arbitrary ID3 tag.
   int pos = tagName.find("::");
   tagName = (pos != -1) ? tagName.substr(pos+2).upper() : tagName.upper();
   PropertyMap map;
   String key = map.prepareKey(tagName);
   if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list
-    map.unsupportedData().append("TXXX/" + description());
+    map.unsupportedData().append(L"TXXX/" + description());
   else
-    map.insert(key, l);
+    for(StringList::ConstIterator it = fieldList().begin(); it != fieldList().end(); ++it)
+      if(*it != description())
+        map.insert(key, *it);
   return map;
 }
 
index d4049d716a9b8aa0078f6b3dd163819bda5f371c..f934843830ddc24e135e2b17dc8b5d74734ddff8 100644 (file)
@@ -173,6 +173,8 @@ namespace TagLib {
        */
       StringList fieldList() const;
 
+      PropertyMap asProperties() const;
+
     protected:
       // Reimplementations.
 
@@ -188,6 +190,16 @@ namespace TagLib {
       TextIdentificationFrame(const TextIdentificationFrame &);
       TextIdentificationFrame &operator=(const TextIdentificationFrame &);
 
+      /*!
+       * Parses the special structure of a TIPL frame
+       * Only the whitelisted roles "ARRANGER", "ENGINEER", "PRODUCER",
+       * "DJMIXER" (ID3: "DJ-MIX") and "MIXER" (ID3: "MIX") are allowed.
+       */
+      PropertyMap makeTIPLProperties() const;
+      /*!
+       * Parses the special structure of a TMCL frame.
+       */
+      PropertyMap makeTMCLProperties() const;
       class TextIdentificationFramePrivate;
       TextIdentificationFramePrivate *d;
     };
@@ -237,7 +249,17 @@ namespace TagLib {
       void setText(const StringList &fields);
 
       /*!
-       * Reimplement function.
+       * A UserTextIdentificationFrame is parsed into a PropertyMap as follows:
+       * - the key is the frame's description, uppercased
+       * - if the description contains '::', only the substring after that
+       *   separator is considered as key (compatibility with exfalso)
+       * - if the above rules don't yield a valid key (e.g. containing non-ASCII
+       *   characters), the returned map will contain an entry "TXXX/<description>"
+       *   in its unsupportedData() list.
+       * - The values will be copies of the fieldList().
+       * - If the description() appears as value in fieldList(), it will be omitted
+       *   in the value list, in order to be compatible with TagLib which copies
+       *   the description() into the fieldList().
        */
       PropertyMap asProperties() const;
 
index 0a8927e7f74d0e60f9965501f11178428fc0bd22..b3a9b3daf1bfa9a3532709d4ce9ec2d3f7b6da68 100644 (file)
@@ -111,6 +111,13 @@ void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
   d->textEncoding = encoding;
 }
 
+PropertyMap UnsynchronizedLyricsFrame::asProperties() const
+{
+  PropertyMap map;
+  map.insert("LYRICS", text());
+  return map;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // protected members
 ////////////////////////////////////////////////////////////////////////////////
index 0f8260e47d689ea2aa9c7d92a0d0e28c77f2c94c..f13134c4b2e43355f8006d48d0e5261230374b71 100644 (file)
@@ -134,6 +134,13 @@ namespace TagLib {
        */
       void setTextEncoding(String::Type encoding);
 
+
+      /*!
+       * Parses this frame as PropertyMap. The returned map will contain a single key
+       * "LYRICS" with the text() as single value.
+       */
+      PropertyMap asProperties() const;
+
     protected:
       // Reimplementations.
 
index 09edec40d65fa700e205cd9ad89d9484e2d1d0eb..32bfda972fe17131b0e6674037d22ee97d7e9ab8 100644 (file)
@@ -78,6 +78,18 @@ String UrlLinkFrame::toString() const
   return url();
 }
 
+PropertyMap UrlLinkFrame::asProperties() const
+{
+  String key = frameIDToKey(frameID());
+  PropertyMap map;
+  if(key.isNull())
+    // unknown W*** frame - this normally shouldn't happen
+    map.unsupportedData().append(frameID());
+  else
+    map.insert(key, url());
+  return map;
+}
+
 void UrlLinkFrame::parseFields(const ByteVector &data)
 {
   d->url = String(data);
@@ -139,6 +151,19 @@ void UserUrlLinkFrame::setDescription(const String &s)
   d->description = s;
 }
 
+PropertyMap UserUrlLinkFrame::asProperties() const
+{
+  String key = PropertyMap::prepareKey(description());
+  PropertyMap map;
+  if(key.isEmpty())
+     key = "URL";
+  if(key.isNull())
+    map.unsupportedData().append(L"WXXX/" + description());
+  else
+    map.insert(key, url());
+  return map;
+}
+
 void UserUrlLinkFrame::parseFields(const ByteVector &data)
 {
   if(data.size() < 2) {
index f89faad0a85e760de50b81e9768d2555eb5bce2e..4eea36b3ed6cc034b6d7957f4bf06908a9347d7e 100644 (file)
@@ -68,6 +68,7 @@ namespace TagLib {
 
       virtual void setText(const String &s);
       virtual String toString() const;
+      PropertyMap asProperties() const;
 
     protected:
       virtual void parseFields(const ByteVector &data);
@@ -150,6 +151,16 @@ namespace TagLib {
        */
       void setDescription(const String &s);
 
+      /*!
+       * Parses the UserUrlLinkFrame as PropertyMap. The description() is taken as key,
+       * and the URL as single value.
+       * - if description() is empty, the key will be "URL".
+       * - otherwise, if description() is not a valid key (e.g. containing non-ASCII
+       *   characters), the returned map will contain an entry "WXXX/<description>"
+       *   in its unsupportedData() list.
+       */
+      PropertyMap asProperties() const;
+
     protected:
       virtual void parseFields(const ByteVector &data);
       virtual ByteVector renderFields() const;
index 3fcc8c8090241a421fa38588db8ebfcde5b4bf12..9ec536e0fb0d065ab69aa231b8b3124430d926f7 100644 (file)
@@ -38,6 +38,7 @@
 
 #include "id3v2frame.h"
 #include "id3v2synchdata.h"
+#include "tpropertymap.h"
 
 using namespace TagLib;
 using namespace ID3v2;
@@ -262,7 +263,7 @@ String::Type Frame::checkTextEncoding(const StringList &fields, String::Type enc
   return checkEncoding(fields, encoding, header()->version());
 }
 
-static const uint frameTranslationSize = 55;
+static const uint frameTranslationSize = 53;
 static const char *frameTranslation[][2] = {
   // Text information frames
   { "TALB", "ALBUM"},
@@ -283,14 +284,14 @@ static const char *frameTranslation[][2] = {
   { "TENC", "ENCODEDBY" },
   { "TEXT", "LYRICIST" },
   { "TFLT", "FILETYPE" },
-  { "TIPL", "INVOLVEDPEOPLE" },
+  //{ "TIPL", "INVOLVEDPEOPLE" }, handled separately
   { "TIT1", "CONTENTGROUP" },
   { "TIT2", "TITLE"},
   { "TIT3", "SUBTITLE" },
   { "TKEY", "INITIALKEY" },
   { "TLAN", "LANGUAGE" },
   { "TLEN", "LENGTH" },
-  { "TMCL", "MUSICIANCREDITS" },
+  //{ "TMCL", "MUSICIANCREDITS" }, handled separately
   { "TMED", "MEDIATYPE" },
   { "TMOO", "MOOD" },
   { "TOAL", "ORIGINALALBUM" },
index 8901295a01095a3b7734114067925634331cb109..8efe68708b0ed0fab6c2756763ef06765cb7c9a5 100644 (file)
@@ -33,6 +33,7 @@
 namespace TagLib {
 
   class StringList;
+  class PropertyMap;
 
   namespace ID3v2 {
 
index 397fe07f13c631ac58c38b2f19a31d7b1a2242b5..0d7f5c8d11a015a00b35e9a23b5ba70f20aa25e8 100644 (file)
 #include "id3v2synchdata.h"
 #include "tbytevector.h"
 #include "id3v1genres.h"
+#include "tpropertymap.h"
+
+#include "frames/textidentificationframe.h"
+#include "frames/commentsframe.h"
+#include "frames/urllinkframe.h"
+#include "frames/uniquefileidentifierframe.h"
+#include "frames/unsynchronizedlyricsframe.h"
+#include "frames/unknownframe.h"
 
 using namespace TagLib;
 using namespace ID3v2;
@@ -329,23 +337,12 @@ void ID3v2::Tag::removeFrames(const ByteVector &id)
 PropertyMap ID3v2::Tag::properties() const
 {
   PropertyMap properties;
-  for(FrameList::ConstIterator it = frameList().begin(); it != frameList().end(); ++it) {
-    ByteVector id = (*it)->frameID();
-    if (ignored(id)) {
-      debug("toDict() found ignored id3 frame: " + id);
-    } else if (deprecated(id)) {
-      debug("toDict() found deprecated id3 frame: " + id);
-    } else {
-        // in the future, something like dict[frame->tagName()].append(frame->values())
-        // might replace the following lines.
-        KeyValuePair kvp = parseFrame(*it);
-        dict[kvp.first].append(kvp.second);
-    }
-  }
-  return dict;
+  for(FrameList::ConstIterator it = frameList().begin(); it != frameList().end(); ++it)
+    properties.merge((*it)->asProperties());
+  return properties;
 }
 
-void ID3v2::Tag::fromDict(const TagDict &dict)
+PropertyMap ID3v2::Tag::setProperties(const PropertyMap &properties)
 {
   FrameList toRemove;
   // first find out what frames to remove; we do not remove in-place
index 3a7be29e0c7d0505abc9d857b413cc2b11f5a405..28b8fca747e60603e989a1b18f6e49ff1b7b4963 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "mpegfile.h"
 #include "mpegheader.h"
+#include "tpropertymap.h"
 
 using namespace TagLib;
 
index 2ecdd6d996a58e8a74d5ae4332135a0fd96b864f..52d7136b464fac2a147115bb7392e9d1cbbb8ec7 100644 (file)
@@ -171,21 +171,6 @@ void TagUnion::setTrack(uint i)
 {
   setUnion(Track, i);
 }
-TagDict TagUnion::toDict() const
-{
-  for (int i = 0; i < 3; ++i)
-    if (d->tags[i])
-      return d->tags[i]->toDict();
-  TagDict dict;
-  return dict;
-}
-
-void TagUnion::fromDict(const TagDict &dict)
-{
-  for (int i = 0; i < 3; ++i)
-    if (d->tags[i])
-      d->tags[i]->fromDict(dict);
-}
 
 bool TagUnion::isEmpty() const
 {
index 20771fe8e09450808de0e7d34e880cc91c901161..e94d523a3bd64575e49a189509a24611b5c947ad 100644 (file)
@@ -73,9 +73,6 @@ namespace TagLib {
     virtual void setTrack(uint i);
     virtual bool isEmpty() const;
 
-    virtual TagDict toDict() const;
-    virtual void fromDict(const TagDict &);
-
     template <class T> T *access(int index, bool create)
     {
       if(!create || tag(index))
index e41c190bb90124dab87026a72e8e63267746b200..1fbcb5845e86641428d7a953895c39da566dfd2f 100644 (file)
@@ -35,7 +35,7 @@ namespace TagLib {
    * This map implements a generic representation of textual audio metadata
    * ("tags") realized as pairs of a case-insensitive key
    * and a nonempty list of corresponding values, each value being an an arbitrary
-   * Unicode String.
+   * unicode String.
    * The key has the same restrictions as in the vorbis comment specification,
    * i.e. it must contain at least one character; all printable ASCII characters
    * except '=' and '~' are allowed.