return &d->tag;
}
-TagLib::TagDict MPC::File::toDict(void) const
+PropertyMap MPC::File::properties() const
{
- // once Tag::toDict() is virtual, this case distinction could actually be done
- // within TagUnion.
- if (d->hasAPE)
- return d->tag.access<APE::Tag>(APEIndex, false)->toDict();
- if (d->hasID3v1)
- return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->toDict();
- return TagLib::TagDict();
+ if(d->hasAPE)
+ return d->tag.access<APE::Tag>(APEIndex, false)->properties();
+ if(d->hasID3v1)
+ return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->properties();
+ return PropertyMap();
+}
+
+void MPC::File::removeUnsupportedProperties(const StringList &properties)
+{
+ if(d->hasAPE)
+ d->tag.access<APE::Tag>(APEIndex, false)->removeUnsupportedProperties(properties);
+ if(d->hasID3v1)
+ d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
}
-void MPC::File::fromDict(const TagDict &dict)
+PropertyMap MPC::File::setProperties(const PropertyMap &properties)
{
- if (d->hasAPE)
- d->tag.access<APE::Tag>(APEIndex, false)->fromDict(dict);
- else if (d->hasID3v1)
- d->tag.access<ID3v1::Tag>(ID3v1Index, false)->fromDict(dict);
+ if(d->hasAPE)
+ return d->tag.access<APE::Tag>(APEIndex, false)->setProperties(properties);
+ else if(d->hasID3v1)
+ return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
else
- d->tag.access<APE::Tag>(APEIndex, true)->fromDict(dict);
+ return d->tag.access<APE::Tag>(APE, true)->setProperties(properties);
}
+
MPC::Properties *MPC::File::audioProperties() const
{
return d->properties;
virtual TagLib::Tag *tag() const;
/*!
- * Implements the unified tag dictionary interface -- export function.
+ * Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only the APE
- * tag will be converted to the TagDict.
+ * tag will be converted to the PropertyMap.
*/
- TagDict toDict() const;
+ PropertyMap properties() const;
+
+ void removeUnsupportedProperties(const StringList &properties);
/*!
- * Implements the unified tag dictionary interface -- import function.
+ * Implements the unified property interface -- import function.
* As with the export, only one tag is taken into account. If the file
- * has no tag at all, APE will be created.
+ * has no tag at all, an APE tag will be created.
*/
- void fromDict(const TagDict &);
+ PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MPC::Properties for this file. If no audio properties
TextIdentificationFrame::setText(l);
}
+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.
+ 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());
+ else
+ map.insert(key, l);
+ return map;
+}
+
UserTextIdentificationFrame *UserTextIdentificationFrame::find(
ID3v2::Tag *tag, const String &description) // static
{
void setText(const String &text);
void setText(const StringList &fields);
+ /*!
+ * Reimplement function.
+ */
+ PropertyMap asProperties() const;
+
/*!
* Searches for the user defined text frame with the description \a description
* in \a tag. This returns null if no matching frames were found.
return checkEncoding(fields, encoding, header()->version());
}
+static const uint frameTranslationSize = 55;
+static const char *frameTranslation[][2] = {
+ // Text information frames
+ { "TALB", "ALBUM"},
+ { "TBPM", "BPM" },
+ { "TCOM", "COMPOSER" },
+ { "TCON", "GENRE" },
+ { "TCOP", "COPYRIGHT" },
+ { "TDEN", "ENCODINGTIME" },
+ { "TDLY", "PLAYLISTDELAY" },
+ { "TDOR", "ORIGINALDATE" },
+ { "TDRC", "DATE" },
+ // { "TRDA", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+ // { "TDAT", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+ // { "TYER", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+ // { "TIME", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+ { "TDRL", "RELEASEDATE" },
+ { "TDTG", "TAGGINGDATE" },
+ { "TENC", "ENCODEDBY" },
+ { "TEXT", "LYRICIST" },
+ { "TFLT", "FILETYPE" },
+ { "TIPL", "INVOLVEDPEOPLE" },
+ { "TIT1", "CONTENTGROUP" },
+ { "TIT2", "TITLE"},
+ { "TIT3", "SUBTITLE" },
+ { "TKEY", "INITIALKEY" },
+ { "TLAN", "LANGUAGE" },
+ { "TLEN", "LENGTH" },
+ { "TMCL", "MUSICIANCREDITS" },
+ { "TMED", "MEDIATYPE" },
+ { "TMOO", "MOOD" },
+ { "TOAL", "ORIGINALALBUM" },
+ { "TOFN", "ORIGINALFILENAME" },
+ { "TOLY", "ORIGINALLYRICIST" },
+ { "TOPE", "ORIGINALARTIST" },
+ { "TOWN", "OWNER" },
+ { "TPE1", "ARTIST"},
+ { "TPE2", "ALBUMARTIST" }, // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST'
+ { "TPE3", "CONDUCTOR" },
+ { "TPE4", "REMIXER" }, // could also be ARRANGER
+ { "TPOS", "DISCNUMBER" },
+ { "TPRO", "PRODUCEDNOTICE" },
+ { "TPUB", "PUBLISHER" },
+ { "TRCK", "TRACKNUMBER" },
+ { "TRSN", "RADIOSTATION" },
+ { "TRSO", "RADIOSTATIONOWNER" },
+ { "TSOA", "ALBUMSORT" },
+ { "TSOP", "ARTISTSORT" },
+ { "TSOT", "TITLESORT" },
+ { "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes
+ { "TSRC", "ISRC" },
+ { "TSSE", "ENCODING" },
+ // URL frames
+ { "WCOP", "COPYRIGHTURL" },
+ { "WOAF", "FILEWEBPAGE" },
+ { "WOAR", "ARTISTWEBPAGE" },
+ { "WOAS", "AUDIOSOURCEWEBPAGE" },
+ { "WORS", "RADIOSTATIONWEBPAGE" },
+ { "WPAY", "PAYMENTWEBPAGE" },
+ { "WPUB", "PUBLISHERWEBPAGE" },
+ { "WXXX", "URL"},
+ // Other frames
+ { "COMM", "COMMENT" },
+ { "USLT", "LYRICS" },
+};
+
+Map<ByteVector, String> &idMap()
+{
+ static Map<ByteVector, String> m;
+ if(m.isEmpty())
+ for(size_t i = 0; i < frameTranslationSize; ++i)
+ m[frameTranslation[i][0]] = frameTranslation[i][1];
+ return m;
+}
+
+// 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;
+}
+
+String Frame::frameIDToKey(const ByteVector &id)
+{
+ Map<ByteVector, String> &m = idMap();
+ if(m.contains(id))
+ return m[id];
+ if(deprecationMap().contains(id))
+ return m[deprecationMap()[id]];
+ return String::null;
+}
+
+ByteVector Frame::keyToFrameID(const String &s)
+{
+ static Map<String, ByteVector> m;
+ if(m.isEmpty())
+ for(size_t i = 0; i < frameTranslationSize; ++i)
+ m[frameTranslation[i][1]] = frameTranslation[i][0];
+ if(m.contains(s.upper()))
+ return m[s];
+ return ByteVector::null;
+}
+
+PropertyMap Frame::asProperties() const
+{
+ const ByteVector &id = frameID();
+ // workaround until this function is virtual
+ if(id == "TXXX")
+ return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
+ else if(id[0] == 'T')
+ return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
+ else if(id == "WXXX")
+ return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
+ else if(id[0] == 'W')
+ return dynamic_cast< const UrlLinkFrame* >(this)->asProperties();
+ else if(id == "COMM")
+ return dynamic_cast< const CommentsFrame* >(this)->asProperties();
+ else if(id == "USLT")
+ return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
+ else {
+ PropertyMap m;
+ m.unsupportedData().append(id);
+ return m;
+ }
+}
////////////////////////////////////////////////////////////////////////////////
// Frame::Header class
////////////////////////////////////////////////////////////////////////////////
String::Type checkTextEncoding(const StringList &fields,
String::Type encoding) const;
+
+ /*!
+ * Parses the contents of this frame as PropertyMap. If that fails, the returend
+ * PropertyMap will be empty, and its unsupportedData() will contain this frame's
+ * ID.
+ * BIC: Will be a virtual function in future releases.
+ */
+ PropertyMap asProperties() const;
+
+ /*!
+ * Returns an appropriate ID3 frame ID for the given free-form tag key. This method
+ * will return ByteVector::null if no specialized translation is found.
+ */
+ static ByteVector keyToFrameID(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; in such a case String::null is returned.
+ */
+ static String frameIDToKey(const ByteVector &);
+
private:
Frame(const Frame &);
Frame &operator=(const Frame &);
#include "id3v2extendedheader.h"
#include "id3v2footer.h"
#include "id3v2synchdata.h"
-#include "id3v2dicttools.h"
#include "tbytevector.h"
#include "id3v1genres.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;
removeFrame(*it, true);
}
-TagDict ID3v2::Tag::toDict(StringList *ignoreInfo) const
+PropertyMap ID3v2::Tag::properties() const
{
- TagDict dict;
- FrameList::ConstIterator frameIt = frameList().begin();
- for (; frameIt != frameList().end(); ++frameIt) {
- ByteVector id = (*frameIt)->frameID();
-
+ 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);
- if (ignoreInfo)
- ignoreInfo->append(String(id) + " frame is not supported");
} else if (deprecated(id)) {
debug("toDict() found deprecated id3 frame: " + id);
- if (ignoreInfo)
- ignoreInfo->append(String(id) + " frame is deprecated");
} else {
// in the future, something like dict[frame->tagName()].append(frame->values())
// might replace the following lines.
- KeyValuePair kvp = parseFrame(*frameIt);
+ KeyValuePair kvp = parseFrame(*it);
dict[kvp.first].append(kvp.second);
}
}
void removeFrames(const ByteVector &id);
/*!
- * Implements the unified tag dictionary interface -- export function.
+ * Implements the unified property interface -- export function.
* This function does some work to translate the hard-specified ID3v2
- * frame types into a free-form string-to-stringlist dictionary.
+ * frame types into a free-form string-to-stringlist PropertyMap.
*
- * If the optional pointer to a StringList is given, that list will
- * be filled with a descriptive text for each ID3v2 frame that could
- * not be incorporated into the dict interface (binary data, unsupported
- * frames, ...).
*/
- TagDict toDict(StringList *ignoredInfo = 0) const;
+ PropertyMap properties() const;
/*!
- * Implements the unified tag dictionary interface -- import function.
- * See the comments in toDict().
+ * Removes unsupported frames given by \a properties. The elements of
+ * \a properties must be taken from properties().unsupportedData() and
+ * are the four-byte frame IDs of ID3 frames which are not compatible
+ * with the PropertyMap schema.
*/
- void fromDict(const TagDict &);
+ void removeUnsupportedProperties(const StringList &properties);
+
+ /*!
+ * Implements the unified property interface -- import function.
+ * See the comments in properties().
+ */
+ PropertyMap setProperties(const PropertyMap &);
/*!
* Render the tag back to binary data, suitable to be written to disk.
return &d->tag;
}
-TagLib::TagDict MPEG::File::toDict(void) const
+PropertyMap MPEG::File::properties() const
{
- // once Tag::toDict() is virtual, this case distinction could actually be done
+ // once Tag::properties() is virtual, this case distinction could actually be done
// within TagUnion.
- if (d->hasID3v2)
- return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->toDict();
- if (d->hasAPE)
- return d->tag.access<APE::Tag>(APEIndex, false)->toDict();
- if (d->hasID3v1)
- return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->toDict();
- return TagLib::TagDict();
+ if(d->hasID3v2)
+ return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
+ if(d->hasAPE)
+ return d->tag.access<APE::Tag>(APEIndex, false)->properties();
+ if(d->hasID3v1)
+ return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->properties();
+ return PropertyMap();
}
-void MPEG::File::fromDict(const TagDict &dict)
+void MPEG::File::removeUnsupportedProperties(const StringList &properties)
{
- if (d->hasID3v2)
- d->tag.access<ID3v2::Tag>(ID3v2Index, false)->fromDict(dict);
- else if (d->hasAPE)
- d->tag.access<APE::Tag>(APEIndex, false)->fromDict(dict);
- else if (d->hasID3v1)
- d->tag.access<ID3v1::Tag>(ID3v1Index, false)->fromDict(dict);
+ if(d->hasID3v2)
+ d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(properties);
+ else if(d->hasAPE)
+ d->tag.access<APE::Tag>(APEIndex, false)->removeUnsupportedProperties(properties);
+ else if(d->hasID3v1)
+ d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
+}
+PropertyMap MPEG::File::setProperties(const PropertyMap &properties)
+{
+ if(d->hasID3v2)
+ return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->setProperties(properties);
+ else if(d->hasAPE)
+ return d->tag.access<APE::Tag>(APEIndex, false)->setProperties(properties);
+ else if(d->hasID3v1)
+ return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
else
- d->tag.access<ID3v2::Tag>(ID3v2Index, true)->fromDict(dict);
+ return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
}
MPEG::Properties *MPEG::File::audioProperties() const
virtual Tag *tag() const;
/*!
- * Implements the unified tag dictionary interface -- export function.
- * If the file contains more than one tag (e.g. ID3v2 and v1), only the
+ * Implements the unified property interface -- export function.
+ * If the file contains more than one tag, only the
* first one (in the order ID3v2, APE, ID3v1) will be converted to the
- * TagDict.
+ * PropertyMap.
*/
- TagDict toDict() const;
+ PropertyMap properties() const;
+
+ void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified tag dictionary interface -- import function.
* As with the export, only one tag is taken into account. If the file
* has no tag at all, ID3v2 will be created.
*/
- void fromDict(const TagDict &);
+ PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MPEG::Properties for this file. If no audio properties
using namespace TagLib;
-typedef Map<String,StringList> supertype;
-PropertyMap::PropertyMap() : Map<String,StringList>()
+PropertyMap::PropertyMap() : SimplePropertyMap()
{
}
-PropertyMap::PropertyMap(const PropertyMap &m) : Map<String,StringList>(m)
+PropertyMap::PropertyMap(const PropertyMap &m) : SimplePropertyMap(m)
{
}
if(realKey.isNull())
return false;
- Iterator result = supertype::find(realKey);
+ Iterator result = SimplePropertyMap::find(realKey);
if(result == end())
- supertype::insert(realKey, values);
+ SimplePropertyMap::insert(realKey, values);
else
- supertype::operator[](realKey).append(values);
+ SimplePropertyMap::operator[](realKey).append(values);
return true;
}
String realKey = prepareKey(key);
if(realKey.isNull())
return false;
- supertype::erase(realKey);
- supertype::insert(realKey, values);
+ SimplePropertyMap::erase(realKey);
+ SimplePropertyMap::insert(realKey, values);
return true;
}
String realKey = prepareKey(key);
if(realKey.isNull())
return end();
- return supertype::find(realKey);
+ return SimplePropertyMap::find(realKey);
}
PropertyMap::ConstIterator PropertyMap::find(const String &key) const
String realKey = prepareKey(key);
if(realKey.isNull())
return end();
- return supertype::find(realKey);
+ return SimplePropertyMap::find(realKey);
}
bool PropertyMap::contains(const String &key) const
{
String realKey = prepareKey(key);
// we consider keys with empty value list as not present
- if(realKey.isNull() || supertype::operator[](realKey).isEmpty())
+ if(realKey.isNull() || SimplePropertyMap::operator[](realKey).isEmpty())
return false;
- return supertype::contains(realKey);
+ return SimplePropertyMap::contains(realKey);
}
-/*!
- * Erase the \a key and its values from the map.
- */
PropertyMap &PropertyMap::erase(const String &key)
{
String realKey = prepareKey(key);
- if (realKey.isNull())
+ if(realKey.isNull())
return *this;
- supertype::erase(realKey);
+ SimplePropertyMap::erase(realKey);
+ return *this;
+}
+
+PropertyMap &PropertyMap::merge(const PropertyMap &other)
+{
+ for(PropertyMap::ConstIterator it = other.begin(); it != other.end(); ++it) {
+ insert(it->first, it->second);
+ }
+ unsupported.append(other.unsupported);
return *this;
}
const StringList &PropertyMap::operator[](const String &key) const
{
String realKey = prepareKey(key);
- return supertype::operator[](realKey);
+ return SimplePropertyMap::operator[](realKey);
}
StringList &PropertyMap::operator[](const String &key)
{
String realKey = prepareKey(key);
- return supertype::operator[](realKey);
+ return SimplePropertyMap::operator[](realKey);
}
void PropertyMap::removeEmpty()
return unsupported;
}
+const StringList &PropertyMap::unsupportedData() const
+{
+ return unsupported;
+}
+
static String PropertyMap::prepareKey(const String &proposed) {
if(proposed.isEmpty())
return String::null;
namespace TagLib {
+ typedef Map<String,StringList> SimplePropertyMap;
//! A map for format-independent <key,valuelist> tag representations.
*
*/
- class TAGLIB_EXPORT PropertyMap: public Map<String,StringList>
+ class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap
{
public:
- typedef Map<String,StringList>::Iterator Iterator;
- typedef Map<String,StringList>::ConstIterator ConstIterator;
+ typedef SimplePropertyMap::Iterator Iterator;
+ typedef SimplePropertyMap::ConstIterator ConstIterator;
PropertyMap();
*/
PropertyMap &erase(const String &key);
+ /*!
+ * Merge the contents of \a other into this PropertyMap.
+ * If a key is contained in both maps, the values of the second
+ * are appended to that of the first.
+ * The unsupportedData() lists are concatenated as well.
+ */
+ PropertyMap &merge(const PropertyMap &other);
+
/*!
* Returns a reference to the value associated with \a key.
*
* same PropertyMap as argument.
*/
StringList &unsupportedData();
+ const StringList &unsupportedData() const;
/*!
* Removes all entries which have an empty value list.