1
/***************************************************************************
2
copyright : (C) 2002 - 2008 by Scott Wheeler
3
email : wheeler@kde.org
4
***************************************************************************/
6
/***************************************************************************
7
* This library is free software; you can redistribute it and/or modify *
8
* it under the terms of the GNU Lesser General Public License version *
9
* 2.1 as published by the Free Software Foundation. *
11
* This library is distributed in the hope that it will be useful, but *
12
* WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14
* Lesser General Public License for more details. *
16
* You should have received a copy of the GNU Lesser General Public *
17
* License along with this library; if not, write to the Free Software *
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
21
* Alternatively, this file is available under the Mozilla Public *
22
* License Version 1.1. You may obtain a copy of the License at *
23
* http://www.mozilla.org/MPL/ *
24
***************************************************************************/
26
#include <tbytevector.h>
29
#include <xiphcomment.h>
31
using namespace TagLib;
33
class Ogg::XiphComment::XiphCommentPrivate
36
FieldListMap fieldListMap;
41
////////////////////////////////////////////////////////////////////////////////
43
////////////////////////////////////////////////////////////////////////////////
45
Ogg::XiphComment::XiphComment() : TagLib::Tag()
47
d = new XiphCommentPrivate;
50
Ogg::XiphComment::XiphComment(const ByteVector &data) : TagLib::Tag()
52
d = new XiphCommentPrivate;
56
Ogg::XiphComment::~XiphComment()
61
String Ogg::XiphComment::title() const
63
if(d->fieldListMap["TITLE"].isEmpty())
65
return d->fieldListMap["TITLE"].front();
68
String Ogg::XiphComment::artist() const
70
if(d->fieldListMap["ARTIST"].isEmpty())
72
return d->fieldListMap["ARTIST"].front();
75
String Ogg::XiphComment::album() const
77
if(d->fieldListMap["ALBUM"].isEmpty())
79
return d->fieldListMap["ALBUM"].front();
82
String Ogg::XiphComment::comment() const
84
if(!d->fieldListMap["DESCRIPTION"].isEmpty()) {
85
d->commentField = "DESCRIPTION";
86
return d->fieldListMap["DESCRIPTION"].front();
89
if(!d->fieldListMap["COMMENT"].isEmpty()) {
90
d->commentField = "COMMENT";
91
return d->fieldListMap["COMMENT"].front();
97
String Ogg::XiphComment::genre() const
99
if(d->fieldListMap["GENRE"].isEmpty())
101
return d->fieldListMap["GENRE"].front();
104
TagLib::uint Ogg::XiphComment::year() const
106
if(d->fieldListMap["DATE"].isEmpty())
108
return d->fieldListMap["DATE"].front().toInt();
111
TagLib::uint Ogg::XiphComment::track() const
113
if(d->fieldListMap["TRACKNUMBER"].isEmpty())
115
return d->fieldListMap["TRACKNUMBER"].front().toInt();
118
void Ogg::XiphComment::setTitle(const String &s)
120
addField("TITLE", s);
123
void Ogg::XiphComment::setArtist(const String &s)
125
addField("ARTIST", s);
128
void Ogg::XiphComment::setAlbum(const String &s)
130
addField("ALBUM", s);
133
void Ogg::XiphComment::setComment(const String &s)
135
addField(d->commentField.isEmpty() ? "DESCRIPTION" : d->commentField, s);
138
void Ogg::XiphComment::setGenre(const String &s)
140
addField("GENRE", s);
143
void Ogg::XiphComment::setYear(uint i)
148
addField("DATE", String::number(i));
151
void Ogg::XiphComment::setTrack(uint i)
154
removeField("TRACKNUMBER");
156
addField("TRACKNUMBER", String::number(i));
159
bool Ogg::XiphComment::isEmpty() const
161
FieldListMap::ConstIterator it = d->fieldListMap.begin();
162
for(; it != d->fieldListMap.end(); ++it)
163
if(!(*it).second.isEmpty())
169
TagLib::uint Ogg::XiphComment::fieldCount() const
173
FieldListMap::ConstIterator it = d->fieldListMap.begin();
174
for(; it != d->fieldListMap.end(); ++it)
175
count += (*it).second.size();
180
const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
182
return d->fieldListMap;
185
String Ogg::XiphComment::vendorID() const
190
void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
193
removeField(key.upper());
195
if(!key.isEmpty() && !value.isEmpty())
196
d->fieldListMap[key.upper()].append(value);
199
void Ogg::XiphComment::removeField(const String &key, const String &value)
201
if(!value.isNull()) {
202
StringList::Iterator it = d->fieldListMap[key].begin();
203
while(it != d->fieldListMap[key].end()) {
205
it = d->fieldListMap[key].erase(it);
211
d->fieldListMap.erase(key);
214
bool Ogg::XiphComment::contains(const String &key) const
216
return d->fieldListMap.contains(key) && !d->fieldListMap[key].isEmpty();
219
ByteVector Ogg::XiphComment::render() const
224
ByteVector Ogg::XiphComment::render(bool addFramingBit) const
228
// Add the vendor ID length and the vendor ID. It's important to use the
229
// length of the data(String::UTF8) rather than the length of the the string
230
// since this is UTF8 text and there may be more characters in the data than
231
// in the UTF16 string.
233
ByteVector vendorData = d->vendorID.data(String::UTF8);
235
data.append(ByteVector::fromUInt(vendorData.size(), false));
236
data.append(vendorData);
238
// Add the number of fields.
240
data.append(ByteVector::fromUInt(fieldCount(), false));
242
// Iterate over the the field lists. Our iterator returns a
243
// std::pair<String, StringList> where the first String is the field name and
244
// the StringList is the values associated with that field.
246
FieldListMap::ConstIterator it = d->fieldListMap.begin();
247
for(; it != d->fieldListMap.end(); ++it) {
249
// And now iterate over the values of the current list.
251
String fieldName = (*it).first;
252
StringList values = (*it).second;
254
StringList::ConstIterator valuesIt = values.begin();
255
for(; valuesIt != values.end(); ++valuesIt) {
256
ByteVector fieldData = fieldName.data(String::UTF8);
257
fieldData.append('=');
258
fieldData.append((*valuesIt).data(String::UTF8));
260
data.append(ByteVector::fromUInt(fieldData.size(), false));
261
data.append(fieldData);
265
// Append the "framing bit".
268
data.append(char(1));
273
////////////////////////////////////////////////////////////////////////////////
275
////////////////////////////////////////////////////////////////////////////////
277
void Ogg::XiphComment::parse(const ByteVector &data)
279
// The first thing in the comment data is the vendor ID length, followed by a
280
// UTF8 string with the vendor ID.
284
int vendorLength = data.mid(0, 4).toUInt(false);
287
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
290
// Next the number of fields in the comment vector.
292
int commentFields = data.mid(pos, 4).toUInt(false);
295
for(int i = 0; i < commentFields; i++) {
297
// Each comment field is in the format "KEY=value" in a UTF8 string and has
298
// 4 bytes before the text starts that gives the length.
300
int commentLength = data.mid(pos, 4).toUInt(false);
303
String comment = String(data.mid(pos, commentLength), String::UTF8);
304
pos += commentLength;
306
int commentSeparatorPosition = comment.find("=");
308
String key = comment.substr(0, commentSeparatorPosition);
309
String value = comment.substr(commentSeparatorPosition + 1);
311
addField(key, value, false);