mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
CCMAIL:taglib-devel@kde.org git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@585155 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
310 lines
8.0 KiB
C++
310 lines
8.0 KiB
C++
/***************************************************************************
|
|
copyright : (C) 2003 by Scott Wheeler
|
|
email : wheeler@kde.org
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* This library is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Lesser General Public License version *
|
|
* 2.1 as published by the Free Software Foundation. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with this library; if not, write to the Free Software *
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
* USA *
|
|
***************************************************************************/
|
|
|
|
#include <tbytevector.h>
|
|
#include <tdebug.h>
|
|
|
|
#include <xiphcomment.h>
|
|
|
|
using namespace TagLib;
|
|
|
|
class Ogg::XiphComment::XiphCommentPrivate
|
|
{
|
|
public:
|
|
FieldListMap fieldListMap;
|
|
String vendorID;
|
|
String commentField;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// public members
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Ogg::XiphComment::XiphComment() : TagLib::Tag()
|
|
{
|
|
d = new XiphCommentPrivate;
|
|
}
|
|
|
|
Ogg::XiphComment::XiphComment(const ByteVector &data) : TagLib::Tag()
|
|
{
|
|
d = new XiphCommentPrivate;
|
|
parse(data);
|
|
}
|
|
|
|
Ogg::XiphComment::~XiphComment()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
String Ogg::XiphComment::title() const
|
|
{
|
|
if(d->fieldListMap["TITLE"].isEmpty())
|
|
return String::null;
|
|
return d->fieldListMap["TITLE"].front();
|
|
}
|
|
|
|
String Ogg::XiphComment::artist() const
|
|
{
|
|
if(d->fieldListMap["ARTIST"].isEmpty())
|
|
return String::null;
|
|
return d->fieldListMap["ARTIST"].front();
|
|
}
|
|
|
|
String Ogg::XiphComment::album() const
|
|
{
|
|
if(d->fieldListMap["ALBUM"].isEmpty())
|
|
return String::null;
|
|
return d->fieldListMap["ALBUM"].front();
|
|
}
|
|
|
|
String Ogg::XiphComment::comment() const
|
|
{
|
|
if(!d->fieldListMap["DESCRIPTION"].isEmpty()) {
|
|
d->commentField = "DESCRIPTION";
|
|
return d->fieldListMap["DESCRIPTION"].front();
|
|
}
|
|
|
|
if(!d->fieldListMap["COMMENT"].isEmpty()) {
|
|
d->commentField = "COMMENT";
|
|
return d->fieldListMap["COMMENT"].front();
|
|
}
|
|
|
|
return String::null;
|
|
}
|
|
|
|
String Ogg::XiphComment::genre() const
|
|
{
|
|
if(d->fieldListMap["GENRE"].isEmpty())
|
|
return String::null;
|
|
return d->fieldListMap["GENRE"].front();
|
|
}
|
|
|
|
TagLib::uint Ogg::XiphComment::year() const
|
|
{
|
|
if(d->fieldListMap["DATE"].isEmpty())
|
|
return 0;
|
|
return d->fieldListMap["DATE"].front().toInt();
|
|
}
|
|
|
|
TagLib::uint Ogg::XiphComment::track() const
|
|
{
|
|
if(d->fieldListMap["TRACKNUMBER"].isEmpty())
|
|
return 0;
|
|
return d->fieldListMap["TRACKNUMBER"].front().toInt();
|
|
}
|
|
|
|
void Ogg::XiphComment::setTitle(const String &s)
|
|
{
|
|
addField("TITLE", s);
|
|
}
|
|
|
|
void Ogg::XiphComment::setArtist(const String &s)
|
|
{
|
|
addField("ARTIST", s);
|
|
}
|
|
|
|
void Ogg::XiphComment::setAlbum(const String &s)
|
|
{
|
|
addField("ALBUM", s);
|
|
}
|
|
|
|
void Ogg::XiphComment::setComment(const String &s)
|
|
{
|
|
addField(d->commentField.isEmpty() ? "DESCRIPTION" : d->commentField, s);
|
|
}
|
|
|
|
void Ogg::XiphComment::setGenre(const String &s)
|
|
{
|
|
addField("GENRE", s);
|
|
}
|
|
|
|
void Ogg::XiphComment::setYear(uint i)
|
|
{
|
|
if(i == 0)
|
|
removeField("DATE");
|
|
else
|
|
addField("DATE", String::number(i));
|
|
}
|
|
|
|
void Ogg::XiphComment::setTrack(uint i)
|
|
{
|
|
if(i == 0)
|
|
removeField("TRACKNUMBER");
|
|
else
|
|
addField("TRACKNUMBER", String::number(i));
|
|
}
|
|
|
|
bool Ogg::XiphComment::isEmpty() const
|
|
{
|
|
FieldListMap::ConstIterator it = d->fieldListMap.begin();
|
|
for(; it != d->fieldListMap.end(); ++it)
|
|
if(!(*it).second.isEmpty())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
TagLib::uint Ogg::XiphComment::fieldCount() const
|
|
{
|
|
uint count = 0;
|
|
|
|
FieldListMap::ConstIterator it = d->fieldListMap.begin();
|
|
for(; it != d->fieldListMap.end(); ++it)
|
|
count += (*it).second.size();
|
|
|
|
return count;
|
|
}
|
|
|
|
const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
|
|
{
|
|
return d->fieldListMap;
|
|
}
|
|
|
|
String Ogg::XiphComment::vendorID() const
|
|
{
|
|
return d->vendorID;
|
|
}
|
|
|
|
void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
|
|
{
|
|
if(replace)
|
|
removeField(key.upper());
|
|
|
|
if(!key.isEmpty())
|
|
d->fieldListMap[key.upper()].append(value);
|
|
}
|
|
|
|
void Ogg::XiphComment::removeField(const String &key, const String &value)
|
|
{
|
|
if(!value.isNull()) {
|
|
StringList::Iterator it = d->fieldListMap[key].begin();
|
|
while(it != d->fieldListMap[key].end()) {
|
|
if(value == *it)
|
|
it = d->fieldListMap[key].erase(it);
|
|
else
|
|
it++;
|
|
}
|
|
}
|
|
else
|
|
d->fieldListMap.erase(key);
|
|
}
|
|
|
|
bool Ogg::XiphComment::contains(const String &key) const
|
|
{
|
|
return d->fieldListMap.contains(key) && !d->fieldListMap[key].isEmpty();
|
|
}
|
|
|
|
ByteVector Ogg::XiphComment::render() const
|
|
{
|
|
return render(true);
|
|
}
|
|
|
|
ByteVector Ogg::XiphComment::render(bool addFramingBit) const
|
|
{
|
|
ByteVector data;
|
|
|
|
// Add the vendor ID length and the vendor ID. It's important to use the
|
|
// lenght of the data(String::UTF8) rather than the lenght of the the string
|
|
// since this is UTF8 text and there may be more characters in the data than
|
|
// in the UTF16 string.
|
|
|
|
ByteVector vendorData = d->vendorID.data(String::UTF8);
|
|
|
|
data.append(ByteVector::fromUInt(vendorData.size(), false));
|
|
data.append(vendorData);
|
|
|
|
// Add the number of fields.
|
|
|
|
data.append(ByteVector::fromUInt(fieldCount(), false));
|
|
|
|
// Iterate over the the field lists. Our iterator returns a
|
|
// std::pair<String, StringList> where the first String is the field name and
|
|
// the StringList is the values associated with that field.
|
|
|
|
FieldListMap::ConstIterator it = d->fieldListMap.begin();
|
|
for(; it != d->fieldListMap.end(); ++it) {
|
|
|
|
// And now iterate over the values of the current list.
|
|
|
|
String fieldName = (*it).first;
|
|
StringList values = (*it).second;
|
|
|
|
StringList::ConstIterator valuesIt = values.begin();
|
|
for(; valuesIt != values.end(); ++valuesIt) {
|
|
ByteVector fieldData = fieldName.data(String::UTF8);
|
|
fieldData.append('=');
|
|
fieldData.append((*valuesIt).data(String::UTF8));
|
|
|
|
data.append(ByteVector::fromUInt(fieldData.size(), false));
|
|
data.append(fieldData);
|
|
}
|
|
}
|
|
|
|
// Append the "framing bit".
|
|
|
|
if(addFramingBit)
|
|
data.append(char(1));
|
|
|
|
return data;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// protected members
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Ogg::XiphComment::parse(const ByteVector &data)
|
|
{
|
|
// The first thing in the comment data is the vendor ID length, followed by a
|
|
// UTF8 string with the vendor ID.
|
|
|
|
int pos = 0;
|
|
|
|
int vendorLength = data.mid(0, 4).toUInt(false);
|
|
pos += 4;
|
|
|
|
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
|
|
pos += vendorLength;
|
|
|
|
// Next the number of fields in the comment vector.
|
|
|
|
int commentFields = data.mid(pos, 4).toUInt(false);
|
|
pos += 4;
|
|
|
|
for(int i = 0; i < commentFields; i++) {
|
|
|
|
// Each comment field is in the format "KEY=value" in a UTF8 string and has
|
|
// 4 bytes before the text starts that gives the length.
|
|
|
|
int commentLength = data.mid(pos, 4).toUInt(false);
|
|
pos += 4;
|
|
|
|
String comment = String(data.mid(pos, commentLength), String::UTF8);
|
|
pos += commentLength;
|
|
|
|
int commentSeparatorPosition = comment.find("=");
|
|
|
|
String key = comment.substr(0, commentSeparatorPosition);
|
|
String value = comment.substr(commentSeparatorPosition + 1);
|
|
|
|
addField(key, value, false);
|
|
}
|
|
}
|