mirror of
https://github.com/taglib/taglib.git
synced 2025-06-03 00:58:12 -04:00
Add support for reading MusicBrainz IDs from ID3v2 tags to PropertyMap
This commit is contained in:
parent
15b601f053
commit
e75d6f616c
@ -381,18 +381,12 @@ void UserTextIdentificationFrame::setDescription(const String &s)
|
||||
|
||||
PropertyMap UserTextIdentificationFrame::asProperties() const
|
||||
{
|
||||
String tagName = description();
|
||||
|
||||
PropertyMap map;
|
||||
String key = tagName.upper();
|
||||
if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list
|
||||
map.unsupportedData().append(L"TXXX/" + description());
|
||||
else {
|
||||
StringList v = fieldList();
|
||||
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
|
||||
if(*it != description())
|
||||
map.insert(key, *it);
|
||||
}
|
||||
String tagName = txxxToKey(description());
|
||||
StringList v = fieldList();
|
||||
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
|
||||
if(it != v.begin())
|
||||
map.insert(tagName, *it);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,10 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "uniquefileidentifierframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@ -87,6 +89,34 @@ String UniqueFileIdentifierFrame::toString() const
|
||||
return String::null;
|
||||
}
|
||||
|
||||
PropertyMap UniqueFileIdentifierFrame::asProperties() const
|
||||
{
|
||||
PropertyMap map;
|
||||
if(d->owner == "http://musicbrainz.org") {
|
||||
map.insert("MUSICBRAINZ_RECORDINGID", String(d->identifier));
|
||||
}
|
||||
else {
|
||||
map.unsupportedData().append(frameID() + String("/") + d->owner);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) // static
|
||||
{
|
||||
ID3v2::FrameList comments = tag->frameList("UFID");
|
||||
|
||||
for(ID3v2::FrameList::ConstIterator it = comments.begin();
|
||||
it != comments.end();
|
||||
++it)
|
||||
{
|
||||
UniqueFileIdentifierFrame *frame = dynamic_cast<UniqueFileIdentifierFrame *>(*it);
|
||||
if(frame && frame->owner() == o)
|
||||
return frame;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UniqueFileIdentifierFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
if(data.size() < 1) {
|
||||
|
@ -94,6 +94,16 @@ namespace TagLib {
|
||||
|
||||
virtual String toString() const;
|
||||
|
||||
PropertyMap asProperties() const;
|
||||
|
||||
/*!
|
||||
* UFID frames each have a unique owner. This searches for a UFID
|
||||
* frame with the owner \a o and returns a pointer to it.
|
||||
*
|
||||
* \see owner()
|
||||
*/
|
||||
static UniqueFileIdentifierFrame *findByOwner(const Tag *tag, const String &o);
|
||||
|
||||
protected:
|
||||
virtual void parseFields(const ByteVector &data);
|
||||
virtual ByteVector renderFields() const;
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "frames/urllinkframe.h"
|
||||
#include "frames/unsynchronizedlyricsframe.h"
|
||||
#include "frames/commentsframe.h"
|
||||
#include "frames/uniquefileidentifierframe.h"
|
||||
#include "frames/unknownframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@ -126,6 +127,10 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
if(key == "MUSICBRAINZ_RECORDINGID" && values.size() == 1) {
|
||||
UniqueFileIdentifierFrame *frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8));
|
||||
return frame;
|
||||
}
|
||||
// now we check if it's one of the "special" cases:
|
||||
// -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS)
|
||||
if((key == "LYRICS" || key.startsWith(lyricsPrefix)) && values.size() == 1){
|
||||
@ -151,7 +156,7 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
|
||||
return frame;
|
||||
}
|
||||
// if non of the above cases apply, we use a TXXX frame with the key as description
|
||||
return new UserTextIdentificationFrame(key, values, String::UTF8);
|
||||
return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8);
|
||||
}
|
||||
|
||||
Frame::~Frame()
|
||||
@ -387,6 +392,18 @@ static const char *frameTranslation[][2] = {
|
||||
//{ "USLT", "LYRICS" }, handled specially
|
||||
};
|
||||
|
||||
static const TagLib::uint txxxFrameTranslationSize = 7;
|
||||
static const char *txxxFrameTranslation[][2] = {
|
||||
{ "MusicBrainz Album Id", "MUSICBRAINZ_RELEASEID" },
|
||||
{ "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz Album Artist Id", "MUSICBRAINZ_RELEASEARTISTID" },
|
||||
{ "MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "Acoustid Id", "ACOUSTID_ID" },
|
||||
{ "Acoustid Fingerprint", "ACOUSTID_FINGERPRINT" },
|
||||
{ "MusicIP PUID", "MUSICIP_PUID" },
|
||||
};
|
||||
|
||||
Map<ByteVector, String> &idMap()
|
||||
{
|
||||
static Map<ByteVector, String> m;
|
||||
@ -396,6 +413,18 @@ Map<ByteVector, String> &idMap()
|
||||
return m;
|
||||
}
|
||||
|
||||
Map<String, String> &txxxMap()
|
||||
{
|
||||
static Map<String, String> m;
|
||||
if(m.isEmpty()) {
|
||||
for(size_t i = 0; i < txxxFrameTranslationSize; ++i) {
|
||||
String key = String(txxxFrameTranslation[i][0]).upper();
|
||||
m[key] = txxxFrameTranslation[i][1];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
// list of deprecated frames and their successors
|
||||
static const TagLib::uint deprecatedFramesSize = 4;
|
||||
static const char *deprecatedFrames[][2] = {
|
||||
@ -435,6 +464,26 @@ ByteVector Frame::keyToFrameID(const String &s)
|
||||
return ByteVector::null;
|
||||
}
|
||||
|
||||
String Frame::txxxToKey(const String &description)
|
||||
{
|
||||
Map<String, String> &m = txxxMap();
|
||||
String d = description.upper();
|
||||
if(m.contains(d))
|
||||
return m[d];
|
||||
return d;
|
||||
}
|
||||
|
||||
String Frame::keyToTXXX(const String &s)
|
||||
{
|
||||
static Map<String, String> m;
|
||||
if(m.isEmpty())
|
||||
for(size_t i = 0; i < txxxFrameTranslationSize; ++i)
|
||||
m[txxxFrameTranslation[i][1]] = txxxFrameTranslation[i][0];
|
||||
if(m.contains(s.upper()))
|
||||
return m[s];
|
||||
return s;
|
||||
}
|
||||
|
||||
PropertyMap Frame::asProperties() const
|
||||
{
|
||||
if(dynamic_cast< const UnknownFrame *>(this)) {
|
||||
@ -456,6 +505,8 @@ PropertyMap Frame::asProperties() const
|
||||
return dynamic_cast< const CommentsFrame* >(this)->asProperties();
|
||||
else if(id == "USLT")
|
||||
return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
|
||||
else if(id == "UFID")
|
||||
return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties();
|
||||
PropertyMap m;
|
||||
m.unsupportedData().append(id);
|
||||
return m;
|
||||
|
@ -274,6 +274,15 @@ namespace TagLib {
|
||||
*/
|
||||
static String frameIDToKey(const ByteVector &);
|
||||
|
||||
/*!
|
||||
* Returns an appropriate TXXX frame description for the given free-form tag key.
|
||||
*/
|
||||
static String keyToTXXX(const String &);
|
||||
|
||||
/*!
|
||||
* Returns a free-form tag name for the given ID3 frame description.
|
||||
*/
|
||||
static String txxxToKey(const String &);
|
||||
|
||||
/*!
|
||||
* This helper function splits the PropertyMap \a original into three ProperytMaps
|
||||
|
@ -379,10 +379,12 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++)
|
||||
if (dynamic_cast<const UnknownFrame *>(*fit) != 0)
|
||||
removeFrame(*fit);
|
||||
} else if(it->size() == 4){
|
||||
}
|
||||
else if(it->size() == 4){
|
||||
ByteVector id = it->data(String::Latin1);
|
||||
removeFrames(id);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
ByteVector id = it->substr(0,4).data(String::Latin1);
|
||||
if(it->size() <= 5)
|
||||
continue; // invalid specification
|
||||
@ -396,6 +398,8 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
frame = CommentsFrame::findByDescription(this, description);
|
||||
else if(id == "USLT")
|
||||
frame = UnsynchronizedLyricsFrame::findByDescription(this, description);
|
||||
else if(id == "UFID")
|
||||
frame = UniqueFileIdentifierFrame::findByOwner(this, description);
|
||||
if(frame)
|
||||
removeFrame(frame);
|
||||
}
|
||||
|
@ -40,6 +40,54 @@ namespace TagLib {
|
||||
* Note that most metadata formats pose additional conditions on the tag keys. The
|
||||
* most popular ones (Vorbis, APE, ID3v2) should support all ASCII only words of
|
||||
* length between 2 and 16.
|
||||
*
|
||||
* This class can contain any tags, but here is a list of "well-known" tags that
|
||||
* you might want to use:
|
||||
*
|
||||
* Basic tags:
|
||||
*
|
||||
* - TITLE
|
||||
* - ALBUM
|
||||
* - ARTIST
|
||||
* - ALBUMARTIST
|
||||
* - SUBTITLE
|
||||
* - TRACKNUMBER
|
||||
* - DISCNUMBER
|
||||
* - DATE
|
||||
* - ORIGINALDATE
|
||||
* - GENRE
|
||||
* - COMMENT
|
||||
*
|
||||
* Credits:
|
||||
*
|
||||
* - COMPOSER
|
||||
* - LYRICIST
|
||||
* - CONDUCTOR
|
||||
* - REMIXER
|
||||
* - PERFORMER:<XXXX>
|
||||
*
|
||||
* Other tags:
|
||||
*
|
||||
* - ISRC
|
||||
* - ASIN
|
||||
* - BPM
|
||||
* - COPYRIGHT
|
||||
* - ENCODEDBY
|
||||
* - MOOD
|
||||
* - COMMENT
|
||||
*
|
||||
* MusicBrainz identifiers:
|
||||
*
|
||||
* - MUSICBRAINZ_RECORDINGID
|
||||
* - MUSICBRAINZ_RELEASEID
|
||||
* - MUSICBRAINZ_RELEASEGROUPID
|
||||
* - MUSICBRAINZ_WORKID
|
||||
* - MUSICBRAINZ_ARTISTID
|
||||
* - MUSICBRAINZ_RELEASEARTISTID
|
||||
* - ACOUSTID_ID
|
||||
* - ACOUSTID_FINGERPRINT
|
||||
* - MUSICIP_PUID
|
||||
*
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap
|
||||
|
@ -624,7 +624,7 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(String("A COMMENT"), dict["COMMENT"].front());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1u, dict.unsupportedData().size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("UFID"), dict.unsupportedData().front());
|
||||
CPPUNIT_ASSERT_EQUAL(String("UFID/supermihi@web.de"), dict.unsupportedData().front());
|
||||
}
|
||||
|
||||
void testPropertyInterface2()
|
||||
@ -657,11 +657,23 @@ public:
|
||||
frame5->setText(tmclData);
|
||||
tag.addFrame(frame5);
|
||||
|
||||
ID3v2::UniqueFileIdentifierFrame *frame6 = new ID3v2::UniqueFileIdentifierFrame("http://musicbrainz.org", "152454b9-19ba-49f3-9fc9-8fc26545cf41");
|
||||
tag.addFrame(frame6);
|
||||
|
||||
ID3v2::UniqueFileIdentifierFrame *frame7 = new ID3v2::UniqueFileIdentifierFrame("http://example.com", "123");
|
||||
tag.addFrame(frame7);
|
||||
|
||||
ID3v2::UserTextIdentificationFrame *frame8 = new ID3v2::UserTextIdentificationFrame();
|
||||
frame8->setDescription("MusicBrainz Album Id");
|
||||
frame8->setText("95c454a5-d7e0-4d8f-9900-db04aca98ab3");
|
||||
tag.addFrame(frame8);
|
||||
|
||||
PropertyMap properties = tag.properties();
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(2u, properties.unsupportedData().size());
|
||||
CPPUNIT_ASSERT_EQUAL(3u, properties.unsupportedData().size());
|
||||
CPPUNIT_ASSERT(properties.unsupportedData().contains("TIPL"));
|
||||
CPPUNIT_ASSERT(properties.unsupportedData().contains("APIC"));
|
||||
CPPUNIT_ASSERT(properties.unsupportedData().contains("UFID/http://example.com"));
|
||||
|
||||
CPPUNIT_ASSERT(properties.contains("PERFORMER:VIOLIN"));
|
||||
CPPUNIT_ASSERT(properties.contains("PERFORMER:PIANO"));
|
||||
@ -671,9 +683,17 @@ public:
|
||||
CPPUNIT_ASSERT(properties.contains("LYRICS"));
|
||||
CPPUNIT_ASSERT(properties.contains("LYRICS:TEST"));
|
||||
|
||||
CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_RECORDINGID"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("152454b9-19ba-49f3-9fc9-8fc26545cf41"), properties["MUSICBRAINZ_RECORDINGID"].front());
|
||||
|
||||
CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_RELEASEID"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("95c454a5-d7e0-4d8f-9900-db04aca98ab3"), properties["MUSICBRAINZ_RELEASEID"].front());
|
||||
|
||||
tag.removeUnsupportedProperties(properties.unsupportedData());
|
||||
CPPUNIT_ASSERT(tag.frameList("APIC").isEmpty());
|
||||
CPPUNIT_ASSERT(tag.frameList("TIPL").isEmpty());
|
||||
CPPUNIT_ASSERT_EQUAL((ID3v2::UniqueFileIdentifierFrame *)0, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://example.com"));
|
||||
CPPUNIT_ASSERT_EQUAL(frame6, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://musicbrainz.org"));
|
||||
}
|
||||
|
||||
void testDeleteFrame()
|
||||
|
Loading…
x
Reference in New Issue
Block a user