#include <tdebug.h>
#include <tstring.h>
+#include <tpropertymap.h>
#include "mp4atom.h"
#include "mp4tag.h"
#include "id3v1genres.h"
return d->items;
}
+static const char *keyTranslation[][2] = {
+ { "\251nam", "TITLE" },
+ { "\251ART", "ARTIST" },
+ { "\251alb", "ALBUM" },
+ { "\251cmt", "COMMENT" },
+ { "\251gen", "GENRE" },
+ { "\251day", "DATE" },
+ { "\251wrt", "COMPOSER" },
+ { "\251grp", "GROUPING" },
+ { "trkn", "TRACKNUMBER" },
+ { "disk", "DISCNUMBER" },
+ { "cpil", "COMPILATION" },
+ { "tmpo", "BPM" },
+ { "cprt", "COPYRIGHT" },
+ { "\251lyr", "LYRICS" },
+ { "\251too", "ENCODEDBY" },
+ { "soal", "ALBUMSORT" },
+ { "soaa", "ALBUMARTISTSORT" },
+ { "soar", "ARTISTSORT" },
+ { "sonm", "TITLESORT" },
+ { "soco", "COMPOSERSORT" },
+ { "sosn", "SHOWSORT" },
+ { "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
+ { "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
+ { "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
+ { "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
+ { "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
+ { "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
+ { "----:com.apple.iTunes:ASIN", "ASIN" },
+ { "----:com.apple.iTunes:LABEL", "LABEL" },
+ { "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
+ { "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
+ { "----:com.apple.iTunes:REMIXER", "REMIXER" },
+ { "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
+ { "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
+ { "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
+ { "----:com.apple.iTunes:MIXER", "MIXER" },
+ { "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
+ { "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
+ { "----:com.apple.iTunes:MOOD", "MOOD" },
+ { "----:com.apple.iTunes:ISRC", "ISRC" },
+ { "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
+ { "----:com.apple.iTunes:BARCODE", "BARCODE" },
+ { "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
+ { "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
+ { "----:com.apple.iTunes:LICENSE", "LICENSE" },
+ { "----:com.apple.iTunes:MEDIA", "MEDIA" },
+};
+
+PropertyMap MP4::Tag::properties() const
+{
+ static Map<String, String> keyMap;
+ if(keyMap.isEmpty()) {
+ int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
+ for(int i = 0; i < numKeys; i++) {
+ keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
+ }
+ }
+
+ PropertyMap props;
+ MP4::ItemListMap::ConstIterator it = d->items.begin();
+ for(; it != d->items.end(); ++it) {
+ if(keyMap.contains(it->first)) {
+ String key = keyMap[it->first];
+ if(key == "TRACKNUMBER" || key == "DISCNUMBER") {
+ MP4::Item::IntPair ip = it->second.toIntPair();
+ String value = String::number(ip.first);
+ if(ip.second) {
+ value += "/" + String::number(ip.second);
+ }
+ props[key] = value;
+ }
+ else if(key == "BPM") {
+ props[key] = String::number(it->second.toInt());
+ }
+ else if(key == "COMPILATION") {
+ props[key] = String::number(it->second.toBool());
+ }
+ else {
+ props[key] = it->second.toStringList();
+ }
+ }
+ else {
+ props.unsupportedData().append(it->first);
+ }
+ }
+ return props;
+}
+
+void MP4::Tag::removeUnsupportedProperties(const StringList &props)
+{
+ StringList::ConstIterator it = props.begin();
+ for(; it != props.end(); ++it)
+ d->items.erase(*it);
+}
+
+PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
+{
+ static Map<String, String> reverseKeyMap;
+ if(reverseKeyMap.isEmpty()) {
+ int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
+ for(int i = 0; i < numKeys; i++) {
+ reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
+ }
+ }
+
+ PropertyMap origProps = properties();
+ PropertyMap::ConstIterator it = origProps.begin();
+ for(; it != origProps.end(); ++it) {
+ if(!props.contains(it->first) || props[it->first].isEmpty()) {
+ d->items.erase(reverseKeyMap[it->first]);
+ }
+ }
+
+ PropertyMap ignoredProps;
+ it = props.begin();
+ for(; it != props.end(); ++it) {
+ if(reverseKeyMap.contains(it->first)) {
+ String name = reverseKeyMap[it->first];
+ if(it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") {
+ int first = 0, second = 0;
+ StringList parts = StringList::split(it->second.front(), "/");
+ if(parts.size() > 0) {
+ first = parts[0].toInt();
+ if(parts.size() > 1) {
+ second = parts[1].toInt();
+ }
+ d->items[name] = MP4::Item(first, second);
+ }
+ }
+ else if(it->first == "BPM") {
+ int value = it->second.front().toInt();
+ d->items[name] = MP4::Item(value);
+ }
+ else if(it->first == "COMPILATION") {
+ bool value = it->second.front().toInt();
+ d->items[name] = MP4::Item(value > 0);
+ }
+ else {
+ d->items[name] = it->second;
+ }
+ }
+ else {
+ ignoredProps.insert(it->first, it->second);
+ }
+ }
+
+ return ignoredProps;
+}
+
#include <tag.h>
#include <mp4tag.h>
#include <tbytevectorlist.h>
+#include <tpropertymap.h>
#include <mp4atom.h>
#include <mp4file.h>
#include <cppunit/extensions/HelperMacros.h>
CPPUNIT_TEST(testCovrRead);
CPPUNIT_TEST(testCovrWrite);
CPPUNIT_TEST(testCovrRead2);
+ CPPUNIT_TEST(testProperties);
CPPUNIT_TEST_SUITE_END();
public:
delete f;
}
+ void testProperties()
+ {
+ MP4::File f(TEST_FILE_PATH_C("has-tags.m4a"));
+
+ PropertyMap tags = f.properties();
+
+ CPPUNIT_ASSERT_EQUAL(StringList("Test Artist"), tags["ARTIST"]);
+
+ tags["TRACKNUMBER"] = StringList("2/4");
+ tags["DISCNUMBER"] = StringList("3/5");
+ tags["BPM"] = StringList("123");
+ tags["ARTIST"] = StringList("Foo Bar");
+ tags["COMPILATION"] = StringList("1");
+ f.setProperties(tags);
+
+ 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_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_EQUAL(StringList("3/5"), tags["DISCNUMBER"]);
+
+ CPPUNIT_ASSERT(f.tag()->itemListMap().contains("tmpo"));
+ CPPUNIT_ASSERT_EQUAL(123, f.tag()->itemListMap()["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_EQUAL(StringList("Foo Bar"), tags["ARTIST"]);
+
+ CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil"));
+ CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemListMap()["cpil"].toBool());
+ CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["COMPILATION"]);
+
+ tags["COMPILATION"] = StringList("0");
+ f.setProperties(tags);
+
+ tags = f.properties();
+
+ CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil"));
+ CPPUNIT_ASSERT_EQUAL(false, f.tag()->itemListMap()["cpil"].toBool());
+ CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["COMPILATION"]);
+ }
+
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4);