mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Merge branch 'master' into taglib2
Conflicts: taglib/mp4/mp4tag.cpp taglib/mp4/mp4tag.h taglib/toolkit/tfile.cpp
This commit is contained in:
commit
db892c43e7
@ -23,10 +23,12 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -49,7 +51,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
TagLib::Tag *tag = f.tag();
|
||||
|
||||
cout << "-- TAG --" << endl;
|
||||
cout << "-- TAG (basic) --" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
@ -57,6 +59,23 @@ int main(int argc, char *argv[])
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
|
||||
TagLib::PropertyMap tags = f.file()->properties();
|
||||
|
||||
unsigned int longest = 0;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
if (i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
|
||||
cout << "-- TAG (properties) --" << endl;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!f.isNull() && f.audioProperties()) {
|
||||
|
@ -132,6 +132,7 @@ namespace TagLib {
|
||||
* has no tag at all, APE will be created.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the APE::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include "asffile.h"
|
||||
#include "asftag.h"
|
||||
@ -403,6 +404,21 @@ ASF::Tag *ASF::File::tag() const
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void ASF::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
ASF::Properties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
|
@ -90,6 +90,22 @@ namespace TagLib {
|
||||
*/
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the ASF audio properties for this file.
|
||||
*/
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tpropertymap.h>
|
||||
#include "asftag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@ -196,3 +197,162 @@ bool ASF::Tag::isEmpty() const
|
||||
d->attributeListMap.isEmpty();
|
||||
}
|
||||
|
||||
static const char *keyTranslation[][2] = {
|
||||
{ "WM/AlbumTitle", "ALBUM" },
|
||||
{ "WM/Composer", "COMPOSER" },
|
||||
{ "WM/Writer", "WRITER" },
|
||||
{ "WM/Conductor", "CONDUCTOR" },
|
||||
{ "WM/ModifiedBy", "REMIXER" },
|
||||
{ "WM/Year", "DATE" },
|
||||
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
|
||||
{ "WM/Producer", "PRODUCER" },
|
||||
{ "WM/ContentGroupDescription", "GROUPING" },
|
||||
{ "WM/SubTitle", "SUBTITLE" },
|
||||
{ "WM/SetSubTitle", "DISCSUBTITLE" },
|
||||
{ "WM/TrackNumber", "TRACKNUMBER" },
|
||||
{ "WM/PartOfSet", "DISCNUMBER" },
|
||||
{ "WM/Genre", "GENRE" },
|
||||
{ "WM/BeatsPerMinute", "BPM" },
|
||||
{ "WM/Mood", "MOOD" },
|
||||
{ "WM/ISRC", "ISRC" },
|
||||
{ "WM/Lyrics", "LYRICS" },
|
||||
{ "WM/Media", "MEDIA" },
|
||||
{ "WM/Publisher", "LABEL" },
|
||||
{ "WM/CatalogNo", "CATALOGNUMBER" },
|
||||
{ "WM/Barcode", "BARCODE" },
|
||||
{ "WM/EncodedBy", "ENCODEDBY" },
|
||||
{ "WM/AlbumSortOrder", "ALBUMSORT" },
|
||||
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
|
||||
{ "WM/ArtistSortOrder", "ARTISTSORT" },
|
||||
{ "WM/TitleSortOrder", "TITLESORT" },
|
||||
{ "WM/Script", "SCRIPT" },
|
||||
{ "WM/Language", "LANGUAGE" },
|
||||
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "MusicIP/PUID", "MUSICIP_PUID" },
|
||||
{ "Acoustid/Id", "ACOUSTID_ID" },
|
||||
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
|
||||
};
|
||||
|
||||
PropertyMap ASF::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;
|
||||
|
||||
if(!d->title.isEmpty()) {
|
||||
props["TITLE"] = d->title;
|
||||
}
|
||||
if(!d->artist.isEmpty()) {
|
||||
props["ARTIST"] = d->artist;
|
||||
}
|
||||
if(!d->copyright.isEmpty()) {
|
||||
props["COPYRIGHT"] = d->copyright;
|
||||
}
|
||||
if(!d->comment.isEmpty()) {
|
||||
props["COMMENT"] = d->comment;
|
||||
}
|
||||
|
||||
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
|
||||
for(; it != d->attributeListMap.end(); ++it) {
|
||||
if(keyMap.contains(it->first)) {
|
||||
String key = keyMap[it->first];
|
||||
AttributeList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
if(it2->type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(it2->toUInt()));
|
||||
else
|
||||
props.insert(key, it2->toString());
|
||||
}
|
||||
else {
|
||||
props.insert(key, it2->toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
props.unsupportedData().append(it->first);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
StringList::ConstIterator it = props.begin();
|
||||
for(; it != props.end(); ++it)
|
||||
d->attributeListMap.erase(*it);
|
||||
}
|
||||
|
||||
PropertyMap ASF::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()) {
|
||||
if(it->first == "TITLE") {
|
||||
d->title = String::null;
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = String::null;
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = String::null;
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = String::null;
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[it->first]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
it = props.begin();
|
||||
for(; it != props.end(); ++it) {
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
removeItem(name);
|
||||
StringList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
addAttribute(name, *it2);
|
||||
}
|
||||
}
|
||||
else if(it->first == "TITLE") {
|
||||
d->title = it->second.toString();
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = it->second.toString();
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = it->second.toString();
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = it->second.toString();
|
||||
}
|
||||
else {
|
||||
ignoredProps.insert(it->first, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
return ignoredProps;
|
||||
}
|
||||
|
@ -176,6 +176,10 @@ namespace TagLib {
|
||||
*/
|
||||
void addAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
private:
|
||||
|
||||
class TagPrivate;
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "mp4file.h"
|
||||
@ -90,6 +91,21 @@ MP4::File::tag() const
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap MP4::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void MP4::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap MP4::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
MP4::Properties *
|
||||
MP4::File::audioProperties() const
|
||||
{
|
||||
|
@ -88,6 +88,22 @@ namespace TagLib {
|
||||
*/
|
||||
Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the MP4 audio properties for this file.
|
||||
*/
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "id3v1genres.h"
|
||||
@ -769,3 +770,153 @@ MP4::Tag::toString() const
|
||||
return desc.toString("\n");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,10 @@ namespace TagLib {
|
||||
|
||||
String toString() const;
|
||||
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
private:
|
||||
AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
|
||||
TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
|
||||
|
@ -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_TRACKID", 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_TRACKID" && 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()
|
||||
@ -350,7 +355,7 @@ static const char *frameTranslation[][2] = {
|
||||
{ "TLAN", "LANGUAGE" },
|
||||
{ "TLEN", "LENGTH" },
|
||||
//{ "TMCL", "MUSICIANCREDITS" }, handled separately
|
||||
{ "TMED", "MEDIATYPE" },
|
||||
{ "TMED", "MEDIA" },
|
||||
{ "TMOO", "MOOD" },
|
||||
{ "TOAL", "ORIGINALALBUM" },
|
||||
{ "TOFN", "ORIGINALFILENAME" },
|
||||
@ -363,7 +368,7 @@ static const char *frameTranslation[][2] = {
|
||||
{ "TPE4", "REMIXER" }, // could also be ARRANGER
|
||||
{ "TPOS", "DISCNUMBER" },
|
||||
{ "TPRO", "PRODUCEDNOTICE" },
|
||||
{ "TPUB", "PUBLISHER" },
|
||||
{ "TPUB", "LABEL" },
|
||||
{ "TRCK", "TRACKNUMBER" },
|
||||
{ "TRSN", "RADIOSTATION" },
|
||||
{ "TRSO", "RADIOSTATIONOWNER" },
|
||||
@ -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_ALBUMID" },
|
||||
{ "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "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
|
||||
|
@ -380,10 +380,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
|
||||
@ -397,6 +399,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);
|
||||
}
|
||||
|
@ -69,6 +69,7 @@
|
||||
#include "s3mfile.h"
|
||||
#include "itfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "mp4file.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
|
@ -49,6 +49,8 @@ namespace {
|
||||
|
||||
// Using native file handles instead of file descriptors for reducing the resource consumption.
|
||||
|
||||
const HANDLE InvalidFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
HANDLE openFile(const FileName &path, bool readOnly)
|
||||
{
|
||||
DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
|
||||
@ -79,6 +81,8 @@ namespace {
|
||||
|
||||
// For non-Windows
|
||||
|
||||
FILE *const InvalidFile = 0;
|
||||
|
||||
struct FileNameHandle : public std::string
|
||||
{
|
||||
FileNameHandle(FileName name) : std::string(name) {}
|
||||
@ -116,7 +120,7 @@ public:
|
||||
};
|
||||
|
||||
FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openReadOnly) :
|
||||
file(0),
|
||||
file(InvalidFile),
|
||||
name(fileName),
|
||||
readOnly(true),
|
||||
size(0)
|
||||
@ -126,12 +130,12 @@ FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openRea
|
||||
if(!openReadOnly)
|
||||
file = openFile(name, false);
|
||||
|
||||
if(file)
|
||||
if(file != InvalidFile)
|
||||
readOnly = false;
|
||||
else
|
||||
file = openFile(name, true);
|
||||
|
||||
if(!file) {
|
||||
if(file == InvalidFile) {
|
||||
debug("Could not open file " + String((const char *) name));
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,65 @@ 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
|
||||
*
|
||||
* Sort names:
|
||||
*
|
||||
* - TITLESORT
|
||||
* - ALBUMSORT
|
||||
* - ARTISTSORT
|
||||
* - ALBUMARTISTSORT
|
||||
*
|
||||
* Credits:
|
||||
*
|
||||
* - COMPOSER
|
||||
* - LYRICIST
|
||||
* - CONDUCTOR
|
||||
* - REMIXER
|
||||
* - PERFORMER:<XXXX>
|
||||
*
|
||||
* Other tags:
|
||||
*
|
||||
* - ISRC
|
||||
* - ASIN
|
||||
* - BPM
|
||||
* - COPYRIGHT
|
||||
* - ENCODEDBY
|
||||
* - MOOD
|
||||
* - COMMENT
|
||||
* - MEDIA
|
||||
* - LABEL
|
||||
* - CATALOGNUMBER
|
||||
* - BARCODE
|
||||
*
|
||||
* MusicBrainz identifiers:
|
||||
*
|
||||
* - MUSICBRAINZ_TRACKID
|
||||
* - MUSICBRAINZ_ALBUMID
|
||||
* - MUSICBRAINZ_RELEASEGROUPID
|
||||
* - MUSICBRAINZ_WORKID
|
||||
* - MUSICBRAINZ_ARTISTID
|
||||
* - MUSICBRAINZ_ALBUMARTISTID
|
||||
* - ACOUSTID_ID
|
||||
* - ACOUSTID_FINGERPRINT
|
||||
* - MUSICIP_PUID
|
||||
*
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <asffile.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include "utils.h"
|
||||
@ -13,7 +14,7 @@ using namespace TagLib;
|
||||
class TestASF : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestASF);
|
||||
CPPUNIT_TEST(testProperties);
|
||||
CPPUNIT_TEST(testAudioProperties);
|
||||
CPPUNIT_TEST(testRead);
|
||||
CPPUNIT_TEST(testSaveMultipleValues);
|
||||
CPPUNIT_TEST(testSaveStream);
|
||||
@ -22,11 +23,12 @@ class TestASF : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testSaveLargeValue);
|
||||
CPPUNIT_TEST(testSavePicture);
|
||||
CPPUNIT_TEST(testSaveMultiplePictures);
|
||||
CPPUNIT_TEST(testProperties);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
||||
void testProperties()
|
||||
void testAudioProperties()
|
||||
{
|
||||
ASF::File f(TEST_FILE_PATH_C("silence-1.wma"));
|
||||
CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->length());
|
||||
@ -215,6 +217,39 @@ public:
|
||||
delete f;
|
||||
}
|
||||
|
||||
void testProperties()
|
||||
{
|
||||
ASF::File f(TEST_FILE_PATH_C("silence-1.wma"));
|
||||
|
||||
PropertyMap tags = f.properties();
|
||||
|
||||
tags["TRACKNUMBER"] = StringList("2");
|
||||
tags["DISCNUMBER"] = StringList("3");
|
||||
tags["BPM"] = StringList("123");
|
||||
tags["ARTIST"] = StringList("Foo Bar");
|
||||
f.setProperties(tags);
|
||||
|
||||
tags = f.properties();
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.tag()->artist());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/BeatsPerMinute"));
|
||||
CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/BeatsPerMinute"].size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("123"), f.tag()->attributeListMap()["WM/BeatsPerMinute"].front().toString());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/TrackNumber"));
|
||||
CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/TrackNumber"].size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("2"), f.tag()->attributeListMap()["WM/TrackNumber"].front().toString());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["TRACKNUMBER"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/PartOfSet"));
|
||||
CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/PartOfSet"].size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("3"), f.tag()->attributeListMap()["WM/PartOfSet"].front().toString());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["DISCNUMBER"]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestASF);
|
||||
|
@ -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_TRACKID"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("152454b9-19ba-49f3-9fc9-8fc26545cf41"), properties["MUSICBRAINZ_TRACKID"].front());
|
||||
|
||||
CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_ALBUMID"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("95c454a5-d7e0-4d8f-9900-db04aca98ab3"), properties["MUSICBRAINZ_ALBUMID"].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()
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <tag.h>
|
||||
#include <mp4tag.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <mp4atom.h>
|
||||
#include <mp4file.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
@ -25,6 +26,7 @@ class TestMP4 : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testCovrRead);
|
||||
CPPUNIT_TEST(testCovrWrite);
|
||||
CPPUNIT_TEST(testCovrRead2);
|
||||
CPPUNIT_TEST(testProperties);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -224,6 +226,55 @@ 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);
|
||||
|
Loading…
Reference in New Issue
Block a user