Splitted ID3v2Tag::toDict() into several functions.

This should simplify future transition to virtual functions.
This commit is contained in:
Michael Helmling 2011-09-11 18:22:15 +02:00
parent 0356249368
commit 2d31075047
3 changed files with 174 additions and 109 deletions

View File

@ -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<ByteVector, String> 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<String, ByteVector> 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<ByteVector> 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::<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());
}
}
}
}

View File

@ -29,6 +29,7 @@
#include "tstringlist.h"
#include "taglib_export.h"
#include "tmap.h"
#include <utility>
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<ByteVector, ByteVector> FrameIDMap;
typedef Map<ByteVector, ByteVector> FrameIDMap;
typedef std::pair<String, StringList> 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*);
}
}

View File

@ -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::<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 (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::<tagName> instead of simply
// <tagName>.
int pos = tagName.find("::");
tagName = (pos == -1) ? tagName : tagName.substr(pos+2);
if (!dict.contains(tagName.upper()))