mirror of
https://github.com/taglib/taglib.git
synced 2025-06-03 09:08:09 -04:00
Restructured and simplified ID3v2Tag::fromDict().
This commit is contained in:
parent
2d31075047
commit
0c2ca20ec2
@ -182,6 +182,10 @@ namespace TagLib {
|
||||
return deprecationMap().contains(id);
|
||||
}
|
||||
|
||||
String prepareTagName(const String &s) {
|
||||
int pos = s.find("::");
|
||||
return ((pos != -1) ? s.substr(pos+2) : s).upper();
|
||||
}
|
||||
/*
|
||||
* The following _parseXXX functions are to be replaced by implementations of a virtual
|
||||
* function in ID3v2::Frame ASAP.
|
||||
@ -194,12 +198,15 @@ namespace TagLib {
|
||||
// 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);
|
||||
return KeyValuePair(prepareTagName(tagName), l);
|
||||
}
|
||||
|
||||
Frame *_createUserTextIdentificationFrame(const String &tag, const StringList &values)
|
||||
{
|
||||
UserTextIdentificationFrame* frame = new UserTextIdentificationFrame();
|
||||
frame->setDescription(tag);
|
||||
frame->setText(values);
|
||||
return frame;
|
||||
}
|
||||
|
||||
KeyValuePair _parseTextIdentificationFrame(const TextIdentificationFrame *frame)
|
||||
@ -230,6 +237,21 @@ namespace TagLib {
|
||||
return KeyValuePair(tagName, l);
|
||||
}
|
||||
|
||||
Frame *_createTextIdentificationFrame(const String &tag, const StringList &values)
|
||||
{
|
||||
StringList newValues(values); // create a copy because the following might modify
|
||||
// the easiest case: a normal text frame
|
||||
if (tag == "DATE") {
|
||||
// Handle ISO8601 date format
|
||||
for (StringList::Iterator lit = newValues.begin(); lit != newValues.end(); ++lit)
|
||||
if (lit->length() > 10 && (*lit)[10] == ' ')
|
||||
(*lit)[10] = 'T';
|
||||
}
|
||||
TextIdentificationFrame *frame = new TextIdentificationFrame(tagNameToFrameID(tag));
|
||||
frame->setText(newValues);
|
||||
return frame;
|
||||
}
|
||||
|
||||
KeyValuePair _parseUserUrlLinkFrame(const UserUrlLinkFrame *frame)
|
||||
{
|
||||
String tagName = frame->description().upper();
|
||||
@ -238,11 +260,32 @@ namespace TagLib {
|
||||
return KeyValuePair(tagName, frame->url());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a UserUrlLinkFrame. Note that this is valid only if values.size() == 1.
|
||||
*/
|
||||
Frame *_createUserUrlLinkFrame(const String &tag, const StringList &values)
|
||||
{
|
||||
UserUrlLinkFrame* frame = new UserUrlLinkFrame();
|
||||
frame->setDescription(tag);
|
||||
frame->setUrl(values[0]);
|
||||
return frame;
|
||||
}
|
||||
|
||||
KeyValuePair _parseUrlLinkFrame(const UrlLinkFrame *frame)
|
||||
{
|
||||
return KeyValuePair(frameIDToTagName(frame->frameID()) , frame->url());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a rUrlLinkFrame. Note that this is valid only if values.size() == 1.
|
||||
*/
|
||||
Frame *_createUrlLinkFrame(const String &tag, const StringList &values)
|
||||
{
|
||||
UrlLinkFrame *frame = new UrlLinkFrame(tagNameToFrameID(tag));
|
||||
frame->setUrl(values[0]);
|
||||
return frame;
|
||||
}
|
||||
|
||||
KeyValuePair _parseCommentsFrame(const CommentsFrame *frame)
|
||||
{
|
||||
String tagName = frame->description().upper();
|
||||
@ -251,11 +294,26 @@ namespace TagLib {
|
||||
return KeyValuePair(tagName, frame->text());
|
||||
}
|
||||
|
||||
Frame *_createCommentsFrame(const String &tag, const StringList &values)
|
||||
{
|
||||
CommentsFrame *frame = new CommentsFrame(String::UTF8);
|
||||
frame->setText(values[0]);
|
||||
return frame;
|
||||
}
|
||||
|
||||
KeyValuePair _parseUnsynchronizedLyricsFrame(const UnsynchronizedLyricsFrame *frame)
|
||||
{
|
||||
return KeyValuePair("LYRICS", frame->text());
|
||||
}
|
||||
|
||||
Frame *_createUnsynchronizedLyricsFrame(const String &tag, const StringList &values)
|
||||
{
|
||||
UnsynchronizedLyricsFrame* frame = new UnsynchronizedLyricsFrame();
|
||||
frame->setDescription("");
|
||||
frame->setText(values[0]);
|
||||
return frame;
|
||||
}
|
||||
|
||||
KeyValuePair parseFrame(const Frame *frame)
|
||||
{
|
||||
const ByteVector &id = frame->frameID();
|
||||
@ -276,5 +334,24 @@ namespace TagLib {
|
||||
return KeyValuePair("UNKNOWNID3TAG", frame->toString());
|
||||
}
|
||||
}
|
||||
|
||||
Frame *createFrame(const String &tag, const StringList &values)
|
||||
{
|
||||
ByteVector id = tagNameToFrameID(tag);
|
||||
if (id == "TXXX" ||
|
||||
((id[0] == 'W' || id == "COMM" || id == "USLT") && values.size() > 1))
|
||||
return _createUserTextIdentificationFrame(tag, values);
|
||||
else if (id[0] == 'T')
|
||||
return _createTextIdentificationFrame(tag, values);
|
||||
else if (id == "WXXX")
|
||||
return _createUserUrlLinkFrame(tag, values);
|
||||
else if (id[0] == 'W')
|
||||
return _createUrlLinkFrame(tag, values);
|
||||
else if (id == "COMM")
|
||||
return _createCommentsFrame(tag, values);
|
||||
else if (id == "USLT")
|
||||
return _createUnsynchronizedLyricsFrame(tag, values);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,8 @@ namespace TagLib {
|
||||
// forward declaration
|
||||
class Frame;
|
||||
/*!
|
||||
* Returns an appropriate ID3 frame ID for the given free-form tag name.
|
||||
* Returns an appropriate ID3 frame ID for the given free-form tag name. This method
|
||||
* will return TXXX if no specialized translation is found.
|
||||
*/
|
||||
ByteVector TAGLIB_EXPORT tagNameToFrameID(const String &);
|
||||
|
||||
@ -69,6 +70,16 @@ namespace TagLib {
|
||||
*/
|
||||
KeyValuePair parseFrame(const Frame*);
|
||||
|
||||
/*!
|
||||
* Create an appropriate ID3v2::Frame for the given tag name and values.
|
||||
*/
|
||||
Frame *createFrame(const String &tag, const StringList &values);
|
||||
/*!
|
||||
* prepare the given tag name for use in a unified dictionary: make it uppercase and
|
||||
* removes prefixes set by the ExFalso/QuodLibet package.
|
||||
*/
|
||||
String prepareTagName(const String &);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -357,192 +357,24 @@ TagDict ID3v2::Tag::toDict() const
|
||||
void ID3v2::Tag::fromDict(const TagDict &dict)
|
||||
{
|
||||
FrameList toRemove;
|
||||
// first record what frames to remove; we do not remove in-place
|
||||
// first find out what frames to remove; we do not remove in-place
|
||||
// because that would invalidate FrameListMap iterators.
|
||||
//
|
||||
for (FrameListMap::ConstIterator it = frameListMap().begin(); it != frameListMap().end(); ++it) {
|
||||
// ignore empty map entries (does this ever happen?)
|
||||
if (it->second.size() == 0)
|
||||
continue;
|
||||
for (FrameListMap::ConstIterator it = frameListMap().begin(); it != frameListMap().end(); ++it)
|
||||
// Remove all frames which are not ignored
|
||||
if (it->second.size() == 0 || !isIgnored(it->first))
|
||||
toRemove.append(it->second);
|
||||
|
||||
// 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()))
|
||||
toRemove.append(frame);
|
||||
}
|
||||
}
|
||||
else if (it->first == "WXXX") { // handle user URL frames specially
|
||||
for (FrameList::ConstIterator fit = it->second.begin(); fit != it->second.end(); ++fit) {
|
||||
UserUrlLinkFrame* frame = dynamic_cast<ID3v2::UserUrlLinkFrame* >(*fit);
|
||||
String tagName = frame->description().upper();
|
||||
if (!(tagName == "URL") || !dict.contains("URL") || dict["URL"].size() > 1)
|
||||
toRemove.append(frame);
|
||||
}
|
||||
}
|
||||
else if (it->first == "COMM") {
|
||||
for (FrameList::ConstIterator fit = it->second.begin(); fit != it->second.end(); ++fit) {
|
||||
CommentsFrame* frame = dynamic_cast< CommentsFrame* >(*fit);
|
||||
String tagName = frame->description().upper();
|
||||
// policy: use comment frame only with empty description and only if a comment tag
|
||||
// is present in the dictionary and only if there's no more than one comment
|
||||
// (COMM is not specified for multiple values)
|
||||
if ( !(tagName == "") || !dict.contains("COMMENT") || dict["COMMENT"].size() > 1)
|
||||
toRemove.append(frame);
|
||||
}
|
||||
}
|
||||
else if (it->first == "USLT") {
|
||||
for (FrameList::ConstIterator fit = it->second.begin(); fit != it->second.end(); ++fit) {
|
||||
UnsynchronizedLyricsFrame *frame
|
||||
= dynamic_cast< UnsynchronizedLyricsFrame* >(*fit);
|
||||
String tagName = frame->description().upper();
|
||||
if ( !(tagName == "") || !dict.contains("LYRICS") || dict["LYRICS"].size() > 1)
|
||||
toRemove.append(frame);
|
||||
}
|
||||
}
|
||||
else if (it->first[0] == 'T') { // a normal text frame
|
||||
if (!dict.contains(frameIDToTagName(it->first)))
|
||||
toRemove.append(it->second);
|
||||
|
||||
} else
|
||||
debug("file contains unknown tag" + it->first + ", not touching it...");
|
||||
}
|
||||
|
||||
// now remove the frames that have been determined above
|
||||
for (FrameList::ConstIterator it = toRemove.begin(); it != toRemove.end(); it++)
|
||||
removeFrame(*it);
|
||||
|
||||
// now sync in the "forward direction"
|
||||
// now create new frames from the TagDict and add them.
|
||||
for (TagDict::ConstIterator it = dict.begin(); it != dict.end(); ++it) {
|
||||
const String &tagName = it->first;
|
||||
ByteVector id = tagNameToFrameID(tagName);
|
||||
if (id[0] == 'T' && id != "TXXX") {
|
||||
// the easiest case: a normal text frame
|
||||
StringList values = it->second;
|
||||
const FrameList &framelist = frameList(id);
|
||||
if (tagName == "DATE") {
|
||||
// Handle ISO8601 date format (see above)
|
||||
for (StringList::Iterator lit = values.begin(); lit != values.end(); ++lit) {
|
||||
if (lit->length() > 10 && (*lit)[10] == ' ')
|
||||
(*lit)[10] = 'T';
|
||||
}
|
||||
}
|
||||
if (framelist.size() > 0) { // there exists already a frame for this tag
|
||||
const TextIdentificationFrame *frame = dynamic_cast<const TextIdentificationFrame *>(framelist[0]);
|
||||
if (values == frame->fieldList())
|
||||
continue; // equal tag values -> everything ok
|
||||
}
|
||||
// if there was no frame for this tag, or there was one but the values aren't equal,
|
||||
// we start from scratch and create a new one
|
||||
//
|
||||
removeFrames(id);
|
||||
TextIdentificationFrame *frame = new TextIdentificationFrame(id);
|
||||
frame->setText(values);
|
||||
addFrame(frame);
|
||||
}
|
||||
else if (id == "TXXX" ||
|
||||
((id == "WXXX" || id == "COMM" || id == "USLT") && it->second.size() > 1)) {
|
||||
// In all those cases, we store the tag as TXXX frame.
|
||||
// First we search for existing TXXX frames with correct description
|
||||
FrameList existingFrames;
|
||||
FrameList l = frameList("TXXX");
|
||||
|
||||
for (FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++) {
|
||||
String desc= dynamic_cast< UserTextIdentificationFrame* >(*fit)->description();
|
||||
int pos = desc.find("::");
|
||||
String tagName = (pos == -1) ? desc.upper() : desc.substr(pos+2).upper();
|
||||
if (tagName == it->first)
|
||||
existingFrames.append(*fit);
|
||||
}
|
||||
|
||||
bool needsInsert = false;
|
||||
if (existingFrames.size() > 1) { //several tags with same key, remove all and reinsert
|
||||
for (FrameList::ConstIterator it = existingFrames.begin(); it != existingFrames.end(); ++it)
|
||||
removeFrame(*it);
|
||||
needsInsert = true;
|
||||
}
|
||||
else if (existingFrames.isEmpty()) // no frame -> needs insert
|
||||
needsInsert = true;
|
||||
else {
|
||||
if (!(dynamic_cast< UserTextIdentificationFrame*>(existingFrames[0])->fieldList() == it->second)) {
|
||||
needsInsert = true;
|
||||
removeFrame(existingFrames[0]);
|
||||
}
|
||||
}
|
||||
if (needsInsert) { // create and insert new frame
|
||||
UserTextIdentificationFrame* frame = new UserTextIdentificationFrame();
|
||||
frame->setDescription(it->first);
|
||||
frame->setText(it->second);
|
||||
addFrame(frame);
|
||||
}
|
||||
}
|
||||
else if (id == "WXXX") {
|
||||
// we know that it->second.size()==1, since the other cases are handled above
|
||||
bool needsInsert = true;
|
||||
FrameList existingFrames = frameList(id);
|
||||
if (existingFrames.size() > 1 ) // do not allow several WXXX frames
|
||||
removeFrames(id);
|
||||
else if (existingFrames.size() == 1) {
|
||||
needsInsert = !(dynamic_cast< UserUrlLinkFrame* >(existingFrames[0])->url() == it->second[0]);
|
||||
if (needsInsert)
|
||||
removeFrames(id);
|
||||
}
|
||||
if (needsInsert) {
|
||||
UserUrlLinkFrame* frame = new ID3v2::UserUrlLinkFrame();
|
||||
frame->setDescription(it->first);
|
||||
frame->setUrl(it->second[0]);
|
||||
addFrame(frame);
|
||||
}
|
||||
}
|
||||
else if (id == "COMM") {
|
||||
FrameList existingFrames = frameList(id);
|
||||
bool needsInsert = true;
|
||||
if (existingFrames.size() > 1) // do not allow several COMM frames
|
||||
removeFrames(id);
|
||||
else if (existingFrames.size() == 1) {
|
||||
needsInsert = !(dynamic_cast< CommentsFrame* >(existingFrames[0])->text() == it->second[0]);
|
||||
if (needsInsert)
|
||||
removeFrames(id);
|
||||
}
|
||||
|
||||
if (needsInsert) {
|
||||
CommentsFrame* frame = new CommentsFrame();
|
||||
frame->setDescription(""); // most software players use empty description COMM frames for comments
|
||||
frame->setText(it->second[0]);
|
||||
addFrame(frame);
|
||||
}
|
||||
}
|
||||
else if (id == "USLT") {
|
||||
FrameList existingFrames = frameList(id);
|
||||
bool needsInsert = true;
|
||||
if (existingFrames.size() > 1) // do not allow several USLT frames
|
||||
removeFrames(id);
|
||||
else if (existingFrames.size() == 1) {
|
||||
needsInsert = !(dynamic_cast< UnsynchronizedLyricsFrame* >(existingFrames[0])->text() == it->second[0]);
|
||||
if (needsInsert)
|
||||
removeFrames(id);
|
||||
}
|
||||
|
||||
if (needsInsert) {
|
||||
UnsynchronizedLyricsFrame* frame = new UnsynchronizedLyricsFrame();
|
||||
frame->setDescription("");
|
||||
frame->setText(it->second[0]);
|
||||
addFrame(frame);
|
||||
}
|
||||
}
|
||||
Frame *newFrame = createFrame(it->first, it->second);
|
||||
if (newFrame)
|
||||
addFrame(newFrame);
|
||||
else
|
||||
debug("ERROR: Don't know how to translate tag " + it->first + " to ID3v2!");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user