From 2d310750477d2427622d6e18855cdbd1e71df4bc Mon Sep 17 00:00:00 2001 From: Michael Helmling Date: Sun, 11 Sep 2011 18:22:15 +0200 Subject: [PATCH] Splitted ID3v2Tag::toDict() into several functions. This should simplify future transition to virtual functions. --- taglib/mpeg/id3v2/id3v2dicttools.cpp | 152 +++++++++++++++++++++++---- taglib/mpeg/id3v2/id3v2dicttools.h | 27 ++++- taglib/mpeg/id3v2/id3v2tag.cpp | 104 +++--------------- 3 files changed, 174 insertions(+), 109 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2dicttools.cpp b/taglib/mpeg/id3v2/id3v2dicttools.cpp index 903c9372..9fef9ad0 100644 --- a/taglib/mpeg/id3v2/id3v2dicttools.cpp +++ b/taglib/mpeg/id3v2/id3v2dicttools.cpp @@ -25,6 +25,35 @@ #include "tdebug.h" #include "id3v2dicttools.h" #include "tmap.h" + +#include "frames/textidentificationframe.h" +#include "frames/commentsframe.h" +#include "frames/urllinkframe.h" +#include "frames/uniquefileidentifierframe.h" +#include "frames/unsynchronizedlyricsframe.h" +#include "id3v1genres.h" + +using namespace TagLib; +using namespace ID3v2; + +// list of deprecated frames and their successors +static const uint deprecatedFramesSize = 4; +static const char *deprecatedFrames[][2] = { + {"TRDA", "TDRC"}, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3) + {"TDAT", "TDRC"}, // 2.3 -> 2.4 + {"TYER", "TDRC"}, // 2.3 -> 2.4 + {"TIME", "TDRC"}, // 2.3 -> 2.4 +}; + +FrameIDMap deprecationMap() +{ + static FrameIDMap depMap; + if (depMap.isEmpty()) + for(uint i = 0; i < deprecatedFramesSize; ++i) + depMap[deprecatedFrames[i][0]] = deprecatedFrames[i][1]; + return depMap; +} + namespace TagLib { namespace ID3v2 { @@ -110,16 +139,9 @@ namespace TagLib { "UFID", // unique file identifier }; - // list of deprecated frames and their successors - static const uint deprecatedFramesSize = 4; - static const char *deprecatedFrames[][2] = { - {"TRDA", "TDRC"}, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3) - {"TDAT", "TDRC"}, // 2.3 -> 2.4 - {"TYER", "TDRC"}, // 2.3 -> 2.4 - {"TIME", "TDRC"}, // 2.3 -> 2.4 - }; - String frameIDToTagName(const ByteVector &id) { + String frameIDToTagName(const ByteVector &id) + { static Map m; if (m.isEmpty()) for (size_t i = 0; i < numid3frames; ++i) @@ -133,7 +155,8 @@ namespace TagLib { return "UNKNOWNID3TAG"; //TODO: implement this nicer } - ByteVector tagNameToFrameID(const String &s) { + ByteVector tagNameToFrameID(const String &s) + { static Map m; if (m.isEmpty()) for (size_t i = 0; i < numid3frames; ++i) @@ -143,7 +166,8 @@ namespace TagLib { return "TXXX"; } - bool isIgnored(const ByteVector& id) { + bool isIgnored(const ByteVector& id) + { List ignoredList; if (ignoredList.isEmpty()) for (uint i = 0; i < ignoredFramesSize; ++i) @@ -151,16 +175,106 @@ namespace TagLib { return ignoredList.contains(id); } - FrameIDMap deprecationMap() { - static FrameIDMap depMap; - if (depMap.isEmpty()) - for(uint i = 0; i < deprecatedFramesSize; ++i) - depMap[deprecatedFrames[i][0]] = deprecatedFrames[i][1]; - return depMap; + + + bool isDeprecated(const ByteVector& id) + { + return deprecationMap().contains(id); } - bool isDeprecated(const ByteVector& id) { - return deprecationMap().contains(id); + /* + * The following _parseXXX functions are to be replaced by implementations of a virtual + * function in ID3v2::Frame ASAP. + */ + KeyValuePair _parseUserTextIdentificationFrame(const UserTextIdentificationFrame *frame) + { + String tagName = frame->description(); + StringList l(frame->fieldList()); + // this is done because taglib stores the description also as first entry + // in the field list. (why?) + if (l.contains(tagName)) + l.erase(l.find(tagName)); + // handle user text frames set by the QuodLibet / exFalso package, + // which sets the description to QuodLibet:: instead of simply + // . + int pos = tagName.find("::"); + tagName = (pos != -1) ? tagName.substr(pos+2) : tagName; + return KeyValuePair(tagName.upper(), l); + } + + KeyValuePair _parseTextIdentificationFrame(const TextIdentificationFrame *frame) + { + String tagName = frameIDToTagName(frame->frameID()); + StringList l = frame->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 lit = l.begin(); lit != l.end(); ++lit) { + bool ok = false; + int test = lit->toInt(&ok); // test if the genre value is an integer + if (ok) + *lit = ID3v1::genre(test); + } + } + else if (tagName == "DATE") { + for (StringList::Iterator lit = l.begin(); lit != l.end(); ++lit) { + // 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 = lit->find("T"); + if (tpos != -1) + (*lit)[tpos] = ' '; + } + } + return KeyValuePair(tagName, l); + } + + KeyValuePair _parseUserUrlLinkFrame(const UserUrlLinkFrame *frame) + { + String tagName = frame->description().upper(); + if (tagName == "") + tagName = "URL"; + return KeyValuePair(tagName, frame->url()); + } + + KeyValuePair _parseUrlLinkFrame(const UrlLinkFrame *frame) + { + return KeyValuePair(frameIDToTagName(frame->frameID()) , frame->url()); + } + + KeyValuePair _parseCommentsFrame(const CommentsFrame *frame) + { + String tagName = frame->description().upper(); + if (tagName.isEmpty()) + tagName = "COMMENT"; + return KeyValuePair(tagName, frame->text()); + } + + KeyValuePair _parseUnsynchronizedLyricsFrame(const UnsynchronizedLyricsFrame *frame) + { + return KeyValuePair("LYRICS", frame->text()); + } + + KeyValuePair parseFrame(const Frame *frame) + { + const ByteVector &id = frame->frameID(); + if (id == "TXXX") + return _parseUserTextIdentificationFrame(dynamic_cast< const UserTextIdentificationFrame* >(frame)); + else if (id[0] == 'T') + return _parseTextIdentificationFrame(dynamic_cast(frame)); + else if (id == "WXXX") + return _parseUserUrlLinkFrame(dynamic_cast< const UserUrlLinkFrame* >(frame)); + else if (id[0] == 'W') + return _parseUrlLinkFrame(dynamic_cast< const UrlLinkFrame* >(frame)); + else if (id == "COMM") + return _parseCommentsFrame(dynamic_cast< const CommentsFrame* >(frame)); + else if (id == "USLT") + return _parseUnsynchronizedLyricsFrame(dynamic_cast< const UnsynchronizedLyricsFrame* >(frame)); + else { + debug("parsing unknown ID3 frame: " + id); + return KeyValuePair("UNKNOWNID3TAG", frame->toString()); + } } } } diff --git a/taglib/mpeg/id3v2/id3v2dicttools.h b/taglib/mpeg/id3v2/id3v2dicttools.h index 3e7a329f..7dfdda2f 100644 --- a/taglib/mpeg/id3v2/id3v2dicttools.h +++ b/taglib/mpeg/id3v2/id3v2dicttools.h @@ -29,6 +29,7 @@ #include "tstringlist.h" #include "taglib_export.h" #include "tmap.h" +#include namespace TagLib { namespace ID3v2 { @@ -36,18 +37,38 @@ namespace TagLib { * This file contains methods used by the unified dictionary interface for ID3v2 tags * (tag name conversion, handling of un-translatable frameIDs, ...). */ - typedef Map FrameIDMap; + typedef Map FrameIDMap; + typedef std::pair KeyValuePair; + + // forward declaration + class Frame; + /*! + * Returns an appropriate ID3 frame ID for the given free-form tag name. + */ ByteVector TAGLIB_EXPORT tagNameToFrameID(const String &); + /*! + * Returns a free-form tag name for the given ID3 frame ID. Note that this does not work + * for general frame IDs such as TXXX or WXXX. + */ String TAGLIB_EXPORT frameIDToTagName(const ByteVector &); + /*! + * Tell if the given frame ID is ignored by the unified dictionary subsystem. This is true + * for frames that don't admit a textual representation, such as pictures or other binary + * information. + */ bool TAGLIB_EXPORT isIgnored(const ByteVector &); - FrameIDMap TAGLIB_EXPORT deprecationMap(); - bool TAGLIB_EXPORT isDeprecated(const ByteVector&); + /*! + * Parse the ID3v2::Frame *Frame* to a pair of a human-readable key (e.g. ARTIST) and + * a StringList containing the values. + */ + KeyValuePair parseFrame(const Frame*); + } } diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index 83406cdb..29dce9ad 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -340,92 +340,16 @@ TagDict ID3v2::Tag::toDict() const for (; frameIt != frameList().end(); ++frameIt) { ByteVector id = (*frameIt)->frameID(); - if (isIgnored(id)) { - debug("found ignored id3 frame " + id); - continue; + if (isIgnored(id)) + debug("toDict() found ignored id3 frame: " + id); + else if (isDeprecated(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(*frameIt); + dict[kvp.first].append(kvp.second); } - if (isDeprecated(id)) { - debug("found deprecated id3 frame " + id); - continue; - } - if (id[0] == 'T') { - if (id == "TXXX") { - const UserTextIdentificationFrame *uframe - = dynamic_cast< const UserTextIdentificationFrame* >(*frameIt); - String tagName = uframe->description(); - StringList l(uframe->fieldList()); - // this is done because taglib stores the description also as first entry - // in the field list. (why?) - // - if (l.contains(tagName)) - l.erase(l.find(tagName)); - // handle user text frames set by the QuodLibet / exFalso package, - // which sets the description to QuodLibet:: instead of simply - // . - int pos = tagName.find("::"); - tagName = (pos != -1) ? tagName.substr(pos+2) : tagName; - dict[tagName.upper()].append(l); - } - else { - const TextIdentificationFrame* tframe - = dynamic_cast< const TextIdentificationFrame* >(*frameIt); - String tagName = frameIDToTagName(id); - StringList l = tframe->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 lit = l.begin(); lit != l.end(); ++lit) { - bool ok = false; - int test = lit->toInt(&ok); // test if the genre value is an integer - if (ok) { - *lit = ID3v1::genre(test); - } - } - } - else if (tagName == "DATE") { - for (StringList::Iterator lit = l.begin(); lit != l.end(); ++lit) { - // 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 = lit->find("T"); - if (tpos != -1) - (*lit)[tpos] = ' '; - } - } - dict[tagName].append(l); - } - continue; - } - if (id[0] == 'W') { - if (id == "WXXX") { - const UserUrlLinkFrame *uframe = dynamic_cast< const UserUrlLinkFrame* >(*frameIt); - String tagname = uframe->description().upper(); - if (tagname == "") - tagname = "URL"; - dict[tagname].append(uframe->url()); - } - else { - const UrlLinkFrame* uframe = dynamic_cast< const UrlLinkFrame* >(*frameIt); - dict[frameIDToTagName(id)].append(uframe->url()); - } - continue; - } - if (id == "COMM") { - const CommentsFrame *cframe = dynamic_cast< const CommentsFrame* >(*frameIt); - String tagName = cframe->description().upper(); - if (tagName.isEmpty()) - tagName = "COMMENT"; - dict[tagName].append(cframe->text()); - continue; - } - if (id == "USLT") { - const UnsynchronizedLyricsFrame *uframe - = dynamic_cast< const UnsynchronizedLyricsFrame* >(*frameIt); - dict["LYRICS"].append(uframe->text()); - continue; - } - debug("unknown frame ID: " + id); } return dict; } @@ -437,15 +361,21 @@ void ID3v2::Tag::fromDict(const TagDict &dict) // because that would invalidate FrameListMap iterators. // for (FrameListMap::ConstIterator it = frameListMap().begin(); it != frameListMap().end(); ++it) { - if (it->second.size() == 0) // ignore empty map entries (does this ever happen?) + // ignore empty map entries (does this ever happen?) + if (it->second.size() == 0) continue; - if (isDeprecated(it->first))// automatically remove deprecated frames + + // automatically remove deprecated frames + else if (isDeprecated(it->first)) toRemove.append(it->second); else if (it->first == "TXXX") { // handle user text frames specially for (FrameList::ConstIterator fit = it->second.begin(); fit != it->second.end(); ++fit) { UserTextIdentificationFrame* frame = dynamic_cast< UserTextIdentificationFrame* >(*fit); String tagName = frame->description(); + // handle user text frames set by the QuodLibet / exFalso package, + // which sets the description to QuodLibet:: instead of simply + // . int pos = tagName.find("::"); tagName = (pos == -1) ? tagName : tagName.substr(pos+2); if (!dict.contains(tagName.upper()))