#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 {
"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<ByteVector, String> m;
if (m.isEmpty())
for (size_t i = 0; i < numid3frames; ++i)
return "UNKNOWNID3TAG"; //TODO: implement this nicer
}
- ByteVector tagNameToFrameID(const String &s) {
+ ByteVector tagNameToFrameID(const String &s)
+ {
static Map<String, ByteVector> m;
if (m.isEmpty())
for (size_t i = 0; i < numid3frames; ++i)
return "TXXX";
}
- bool isIgnored(const ByteVector& id) {
+ bool isIgnored(const ByteVector& id)
+ {
List<ByteVector> ignoredList;
if (ignoredList.isEmpty())
for (uint i = 0; i < ignoredFramesSize; ++i)
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) {
+
+ 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::<tagName> instead of simply
+ // <tagName>.
+ 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<const TextIdentificationFrame* >(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());
+ }
+ }
}
}
for (; frameIt != frameList().end(); ++frameIt) {
ByteVector id = (*frameIt)->frameID();
- if (isIgnored(id)) {
- debug("found ignored id3 frame " + id);
- continue;
- }
- 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::<tagName> instead of simply
- // <tagName>.
- 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 (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 (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;
}
// 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::<tagName> instead of simply
+ // <tagName>.
int pos = tagName.find("::");
tagName = (pos == -1) ? tagName : tagName.substr(pos+2);
if (!dict.contains(tagName.upper()))