mirror of
https://github.com/taglib/taglib.git
synced 2025-06-04 01:28:21 -04:00
Add accessors to manipulate MP4 tags without modifying the internal structure
This brings the MP4 API in line closer to other tag formats and makes it possible to access the tag data from const functions. "ItemListMap" has been renamed to "ItemMap" (with the old version deprecated). It seems that the "ListMap" notion was copied probably from Allan's ApeTag implementation, which incorrectly copied the term from the XiphTag. Notably, in XiphTag, because a field can have multiple values, the "ListMap" is a map of lists. Calling things a "ListMap" where there can be only one value doesn't fit. Closes #255
This commit is contained in:
parent
451d23ca37
commit
bba562b557
@ -39,7 +39,7 @@ public:
|
||||
~TagPrivate() {}
|
||||
TagLib::File *file;
|
||||
Atoms *atoms;
|
||||
ItemListMap items;
|
||||
ItemMap items;
|
||||
};
|
||||
|
||||
MP4::Tag::Tag()
|
||||
@ -451,7 +451,7 @@ bool
|
||||
MP4::Tag::save()
|
||||
{
|
||||
ByteVector data;
|
||||
for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
|
||||
for(MP4::ItemMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
|
||||
const String name = i->first;
|
||||
if(name.startsWith("----")) {
|
||||
data.append(renderFreeForm(name, i->second));
|
||||
@ -765,12 +765,36 @@ bool MP4::Tag::isEmpty() const
|
||||
return d->items.isEmpty();
|
||||
}
|
||||
|
||||
MP4::ItemListMap &
|
||||
MP4::Tag::itemListMap()
|
||||
MP4::ItemMap &MP4::Tag::itemListMap()
|
||||
{
|
||||
return d->items;
|
||||
}
|
||||
|
||||
const MP4::ItemMap &MP4::Tag::itemMap() const
|
||||
{
|
||||
return d->items;
|
||||
}
|
||||
|
||||
MP4::Item MP4::Tag::item(const String &key) const
|
||||
{
|
||||
return d->items[key];
|
||||
}
|
||||
|
||||
void MP4::Tag::setItem(const String &key, const Item &value)
|
||||
{
|
||||
d->items[key] = value;
|
||||
}
|
||||
|
||||
void MP4::Tag::removeItem(const String &key)
|
||||
{
|
||||
d->items.erase(key);
|
||||
}
|
||||
|
||||
bool MP4::Tag::contains(const String &key) const
|
||||
{
|
||||
return d->items.contains(key);
|
||||
}
|
||||
|
||||
static const char *keyTranslation[][2] = {
|
||||
{ "\251nam", "TITLE" },
|
||||
{ "\251ART", "ARTIST" },
|
||||
@ -832,7 +856,7 @@ PropertyMap MP4::Tag::properties() const
|
||||
}
|
||||
|
||||
PropertyMap props;
|
||||
MP4::ItemListMap::ConstIterator it = d->items.begin();
|
||||
MP4::ItemMap::ConstIterator it = d->items.begin();
|
||||
for(; it != d->items.end(); ++it) {
|
||||
if(keyMap.contains(it->first)) {
|
||||
String key = keyMap[it->first];
|
||||
|
@ -39,7 +39,11 @@ namespace TagLib {
|
||||
|
||||
namespace MP4 {
|
||||
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
typedef TagLib::Map<String, Item> ItemListMap;
|
||||
typedef TagLib::Map<String, Item> ItemMap;
|
||||
|
||||
class TAGLIB_EXPORT Tag: public TagLib::Tag
|
||||
{
|
||||
@ -67,7 +71,36 @@ namespace TagLib {
|
||||
|
||||
virtual bool isEmpty() const;
|
||||
|
||||
ItemListMap &itemListMap();
|
||||
/*!
|
||||
* \deprecated Use the item() and setItem() API instead
|
||||
*/
|
||||
ItemMap &itemListMap();
|
||||
|
||||
/*!
|
||||
* Returns a string-keyed map of the MP4::Items for this tag.
|
||||
*/
|
||||
const ItemMap &itemMap() const;
|
||||
|
||||
/*!
|
||||
* \return The item, if any, corresponding to \a key.
|
||||
*/
|
||||
Item item(const String &key) const;
|
||||
|
||||
/*!
|
||||
* Sets the value of \a key to \a value, overwriting any previous value.
|
||||
*/
|
||||
void setItem(const String &key, const Item &value);
|
||||
|
||||
/*!
|
||||
* Removes the entry with \a key from the tag, or does nothing if it does
|
||||
* not exist.
|
||||
*/
|
||||
void removeItem(const String &key);
|
||||
|
||||
/*!
|
||||
* \return True if the tag contains an entry for \a key.
|
||||
*/
|
||||
bool contains(const String &key) const;
|
||||
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
CPPUNIT_ASSERT(!t1.isEmpty());
|
||||
|
||||
MP4::Tag t2;
|
||||
t2.itemListMap()["foo"] = "bar";
|
||||
t2.setItem("foo", "bar");
|
||||
CPPUNIT_ASSERT(!t2.isEmpty());
|
||||
}
|
||||
|
||||
@ -128,14 +128,15 @@ public:
|
||||
string filename = copy.fileName();
|
||||
|
||||
MP4::File *f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("----:com.apple.iTunes:iTunNORM"));
|
||||
f->tag()->itemListMap()["----:org.kde.TagLib:Foo"] = StringList("Bar");
|
||||
CPPUNIT_ASSERT(f->tag()->contains("----:com.apple.iTunes:iTunNORM"));
|
||||
f->tag()->setItem("----:org.kde.TagLib:Foo", StringList("Bar"));
|
||||
f->save();
|
||||
delete f;
|
||||
|
||||
f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("----:org.kde.TagLib:Foo"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("Bar"), f->tag()->itemListMap()["----:org.kde.TagLib:Foo"].toStringList()[0]);
|
||||
CPPUNIT_ASSERT(f->tag()->contains("----:org.kde.TagLib:Foo"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("Bar"),
|
||||
f->tag()->item("----:org.kde.TagLib:Foo").toStringList().front());
|
||||
f->save();
|
||||
delete f;
|
||||
}
|
||||
@ -146,14 +147,16 @@ public:
|
||||
string filename = copy.fileName();
|
||||
|
||||
MP4::File *f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(String("82,164"), f->tag()->itemListMap()["----:com.apple.iTunes:replaygain_track_minmax"].toStringList()[0]);
|
||||
CPPUNIT_ASSERT_EQUAL(String("82,164"),
|
||||
f->tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f->tag()->artist());
|
||||
f->tag()->setComment("foo");
|
||||
f->save();
|
||||
delete f;
|
||||
|
||||
f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(String("82,164"), f->tag()->itemListMap()["----:com.apple.iTunes:replaygain_track_minmax"].toStringList()[0]);
|
||||
CPPUNIT_ASSERT_EQUAL(String("82,164"),
|
||||
f->tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f->tag()->artist());
|
||||
CPPUNIT_ASSERT_EQUAL(String("foo"), f->tag()->comment());
|
||||
delete f;
|
||||
@ -165,20 +168,20 @@ public:
|
||||
string filename = copy.fileName();
|
||||
|
||||
MP4::File *f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["cpil"].toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemMap()["cpil"].toBool());
|
||||
|
||||
MP4::Atoms *atoms = new MP4::Atoms(f);
|
||||
MP4::Atom *moov = atoms->atoms[0];
|
||||
CPPUNIT_ASSERT_EQUAL(long(77), moov->length);
|
||||
|
||||
f->tag()->itemListMap()["pgap"] = true;
|
||||
f->tag()->setItem("pgap", true);
|
||||
f->save();
|
||||
delete atoms;
|
||||
delete f;
|
||||
|
||||
f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["cpil"].toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["pgap"].toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(true, f->tag()->item("cpil").toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(true, f->tag()->item("pgap").toBool());
|
||||
|
||||
atoms = new MP4::Atoms(f);
|
||||
moov = atoms->atoms[0];
|
||||
@ -198,8 +201,8 @@ public:
|
||||
void testCovrRead()
|
||||
{
|
||||
MP4::File *f = new MP4::File(TEST_FILE_PATH_C("has-tags.m4a"));
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
|
||||
CPPUNIT_ASSERT(f->tag()->contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->item("covr").toCoverArtList();
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), l.size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
|
||||
@ -214,16 +217,16 @@ public:
|
||||
string filename = copy.fileName();
|
||||
|
||||
MP4::File *f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
|
||||
CPPUNIT_ASSERT(f->tag()->contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->item("covr").toCoverArtList();
|
||||
l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo"));
|
||||
f->tag()->itemListMap()["covr"] = l;
|
||||
f->tag()->setItem("covr", l);
|
||||
f->save();
|
||||
delete f;
|
||||
|
||||
f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
|
||||
l = f->tag()->itemListMap()["covr"].toCoverArtList();
|
||||
CPPUNIT_ASSERT(f->tag()->contains("covr"));
|
||||
l = f->tag()->item("covr").toCoverArtList();
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), l.size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
|
||||
@ -237,8 +240,8 @@ public:
|
||||
void testCovrRead2()
|
||||
{
|
||||
MP4::File *f = new MP4::File(TEST_FILE_PATH_C("covr-junk.m4a"));
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
|
||||
CPPUNIT_ASSERT(f->tag()->contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->item("covr").toCoverArtList();
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), l.size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
|
||||
@ -264,26 +267,26 @@ public:
|
||||
|
||||
tags = f.properties();
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->itemListMap().contains("trkn"));
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.tag()->itemListMap()["trkn"].toIntPair().first);
|
||||
CPPUNIT_ASSERT_EQUAL(4, f.tag()->itemListMap()["trkn"].toIntPair().second);
|
||||
CPPUNIT_ASSERT(f.tag()->contains("trkn"));
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.tag()->item("trkn").toIntPair().first);
|
||||
CPPUNIT_ASSERT_EQUAL(4, f.tag()->item("trkn").toIntPair().second);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("2/4"), tags["TRACKNUMBER"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->itemListMap().contains("disk"));
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.tag()->itemListMap()["disk"].toIntPair().first);
|
||||
CPPUNIT_ASSERT_EQUAL(5, f.tag()->itemListMap()["disk"].toIntPair().second);
|
||||
CPPUNIT_ASSERT(f.tag()->contains("disk"));
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.tag()->item("disk").toIntPair().first);
|
||||
CPPUNIT_ASSERT_EQUAL(5, f.tag()->item("disk").toIntPair().second);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("3/5"), tags["DISCNUMBER"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->itemListMap().contains("tmpo"));
|
||||
CPPUNIT_ASSERT_EQUAL(123, f.tag()->itemListMap()["tmpo"].toInt());
|
||||
CPPUNIT_ASSERT(f.tag()->contains("tmpo"));
|
||||
CPPUNIT_ASSERT_EQUAL(123, f.tag()->item("tmpo").toInt());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->itemListMap().contains("\251ART"));
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), f.tag()->itemListMap()["\251ART"].toStringList());
|
||||
CPPUNIT_ASSERT(f.tag()->contains("\251ART"));
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), f.tag()->item("\251ART").toStringList());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil"));
|
||||
CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemListMap()["cpil"].toBool());
|
||||
CPPUNIT_ASSERT(f.tag()->contains("cpil"));
|
||||
CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("cpil").toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["COMPILATION"]);
|
||||
|
||||
tags["COMPILATION"] = StringList("0");
|
||||
@ -291,8 +294,8 @@ public:
|
||||
|
||||
tags = f.properties();
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil"));
|
||||
CPPUNIT_ASSERT_EQUAL(false, f.tag()->itemListMap()["cpil"].toBool());
|
||||
CPPUNIT_ASSERT(f.tag()->contains("cpil"));
|
||||
CPPUNIT_ASSERT_EQUAL(false, f.tag()->item("cpil").toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["COMPILATION"]);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user