diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 73347e01..e7cb7452 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -63,6 +63,7 @@ SET(frames_SRCS mpeg/id3v2/frames/attachedpictureframe.cpp mpeg/id3v2/frames/commentsframe.cpp mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp +mpeg/id3v2/frames/popularimeterframe.cpp mpeg/id3v2/frames/relativevolumeframe.cpp mpeg/id3v2/frames/textidentificationframe.cpp mpeg/id3v2/frames/uniquefileidentifierframe.cpp diff --git a/taglib/mpeg/id3v2/frames/CMakeLists.txt b/taglib/mpeg/id3v2/frames/CMakeLists.txt index 1fb1e208..caa08561 100644 --- a/taglib/mpeg/id3v2/frames/CMakeLists.txt +++ b/taglib/mpeg/id3v2/frames/CMakeLists.txt @@ -2,6 +2,7 @@ INSTALL(FILES attachedpictureframe.h commentsframe.h generalencapsulatedobjectframe.h + popularimeterframe.h relativevolumeframe.h textidentificationframe.h uniquefileidentifierframe.h diff --git a/taglib/mpeg/id3v2/frames/Makefile.am b/taglib/mpeg/id3v2/frames/Makefile.am index 517cd753..416e4abc 100644 --- a/taglib/mpeg/id3v2/frames/Makefile.am +++ b/taglib/mpeg/id3v2/frames/Makefile.am @@ -10,6 +10,7 @@ libframes_la_SOURCES = \ attachedpictureframe.cpp \ commentsframe.cpp \ generalencapsulatedobjectframe.cpp \ + popularimeterframe.cpp \ relativevolumeframe.cpp \ textidentificationframe.cpp \ uniquefileidentifierframe.cpp \ @@ -21,6 +22,7 @@ taglib_include_HEADERS = \ attachedpictureframe.h \ commentsframe.h \ generalencapsulatedobjectframe.h \ + popularimeterframe.h \ relativevolumeframe.h \ textidentificationframe.h \ uniquefileidentifierframe.h \ diff --git a/taglib/mpeg/id3v2/frames/popularimeterframe.cpp b/taglib/mpeg/id3v2/frames/popularimeterframe.cpp new file mode 100644 index 00000000..027ea848 --- /dev/null +++ b/taglib/mpeg/id3v2/frames/popularimeterframe.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + copyright : (C) 2008 by Lukas Lalinsky + email : lalinsky@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * 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 * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include + +#include "popularimeterframe.h" + +using namespace TagLib; +using namespace ID3v2; + +class PopularimeterFrame::PopularimeterFramePrivate +{ +public: + PopularimeterFramePrivate() : rating(0), counter(0) {} + String email; + int rating; + TagLib::uint counter; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +PopularimeterFrame::PopularimeterFrame() : Frame("POPM") +{ + d = new PopularimeterFramePrivate; +} + +PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data) +{ + d = new PopularimeterFramePrivate; + setData(data); +} + +PopularimeterFrame::~PopularimeterFrame() +{ + delete d; +} + +String PopularimeterFrame::toString() const +{ + return d->email + " rating=" + String::number(d->rating) + " counter=" + String::number(d->counter); +} + +String PopularimeterFrame::email() const +{ + return d->email; +} + +void PopularimeterFrame::setEmail(const String &s) +{ + d->email = s; +} + +int PopularimeterFrame::rating() const +{ + return d->rating; +} + +void PopularimeterFrame::setRating(int s) +{ + d->rating = s; +} + +TagLib::uint PopularimeterFrame::counter() const +{ + return d->counter; +} + +void PopularimeterFrame::setCounter(TagLib::uint s) +{ + d->counter = s; +} + +//////////////////////////////////////////////////////////////////////////////// +// protected members +//////////////////////////////////////////////////////////////////////////////// + +void PopularimeterFrame::parseFields(const ByteVector &data) +{ + int pos = 0, size = int(data.size()); + + d->email = readStringField(data, String::Latin1, &pos); + + d->rating = 0; + d->counter = 0; + if(pos < size) { + d->rating = data[pos++]; + if(pos < size) { + d->counter = data.mid(pos, 4).toUInt(); + } + } +} + +ByteVector PopularimeterFrame::renderFields() const +{ + ByteVector data; + + data.append(d->email.data(String::Latin1)); + data.append(textDelimiter(String::Latin1)); + data.append(char(d->rating)); + data.append(ByteVector::fromUInt(d->counter)); + + return data; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h) +{ + d = new PopularimeterFramePrivate; + parseFields(fieldData(data)); +} diff --git a/taglib/mpeg/id3v2/frames/popularimeterframe.h b/taglib/mpeg/id3v2/frames/popularimeterframe.h new file mode 100644 index 00000000..2d626f4d --- /dev/null +++ b/taglib/mpeg/id3v2/frames/popularimeterframe.h @@ -0,0 +1,132 @@ +/*************************************************************************** + copyright : (C) 2008 by Lukas Lalinsky + email : lalinsky@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * 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 * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_POPULARIMETERFRAME_H +#define TAGLIB_POPULARIMETERFRAME_H + +#include +#include "taglib_export.h" + +namespace TagLib { + + namespace ID3v2 { + + //! An implementation of ID3v2 "popularimeter" + + /*! + * This implements the ID3v2 popularimeter (POPM frame). It concists of + * an email, a rating and an optional counter. + */ + + class TAGLIB_EXPORT PopularimeterFrame : public Frame + { + friend class FrameFactory; + + public: + /*! + * Construct an empty popularimeter frame. + */ + explicit PopularimeterFrame(); + + /*! + * Construct a popularimeter based on the data in \a data. + */ + explicit PopularimeterFrame(const ByteVector &data); + + /*! + * Destroys this PopularimeterFrame instance. + */ + virtual ~PopularimeterFrame(); + + /*! + * Returns the text of this popularimeter. + * + * \see text() + */ + virtual String toString() const; + + /*! + * Returns the email. + * + * \see setEmail() + */ + String email() const; + + /*! + * Set the email. + * + * \see email() + */ + void setEmail(const String &email); + + /*! + * Returns the rating. + * + * \see setRating() + */ + int rating() const; + + /*! + * Set the rating. + * + * \see rating() + */ + void setRating(int rating); + + /*! + * Returns the counter. + * + * \see setCounter() + */ + uint counter() const; + + /*! + * Set the counter. + * + * \see counter() + */ + void setCounter(uint counter); + + protected: + // Reimplementations. + + virtual void parseFields(const ByteVector &data); + virtual ByteVector renderFields() const; + + private: + /*! + * The constructor used by the FrameFactory. + */ + PopularimeterFrame(const ByteVector &data, Header *h); + PopularimeterFrame(const PopularimeterFrame &); + PopularimeterFrame &operator=(const PopularimeterFrame &); + + class PopularimeterFramePrivate; + PopularimeterFramePrivate *d; + }; + + } +} +#endif diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 48a20221..1a4d36ae 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -41,6 +41,7 @@ #include "frames/generalencapsulatedobjectframe.h" #include "frames/urllinkframe.h" #include "frames/unsynchronizedlyricsframe.h" +#include "frames/popularimeterframe.h" using namespace TagLib; using namespace ID3v2; @@ -220,6 +221,12 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) return f; } + // Popularimeter (frames 4.17) + + if(frameID == "POPM") { + return new PopularimeterFrame(data, header); + } + return new UnknownFrame(data, header); } diff --git a/taglib/taglib.pro b/taglib/taglib.pro index 66bf4d6c..76be4c45 100644 --- a/taglib/taglib.pro +++ b/taglib/taglib.pro @@ -93,6 +93,7 @@ HEADERS += audioproperties.h \ mpeg/id3v2/frames/attachedpictureframe.h \ mpeg/id3v2/frames/commentsframe.h \ mpeg/id3v2/frames/generalencapsulatedobjectframe.h \ + mpeg/id3v2/frames/popularimeterframe.h \ mpeg/id3v2/frames/relativevolumeframe.h \ mpeg/id3v2/frames/textidentificationframe.h \ mpeg/id3v2/frames/uniquefileidentifierframe.h \ @@ -148,6 +149,7 @@ SOURCES += audioproperties.cpp \ mpeg/id3v2/frames/attachedpictureframe.cpp \ mpeg/id3v2/frames/commentsframe.cpp \ mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp \ + mpeg/id3v2/frames/popularimeterframe.cpp \ mpeg/id3v2/frames/relativevolumeframe.cpp \ mpeg/id3v2/frames/textidentificationframe.cpp \ mpeg/id3v2/frames/uniquefileidentifierframe.cpp \ diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index ed5e0b00..027ecacc 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "utils.h" @@ -37,6 +38,11 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testParseAPIC); CPPUNIT_TEST(testParseAPIC_UTF16_BOM); CPPUNIT_TEST(testParseGEOB); + CPPUNIT_TEST(testPOPMtoString); + CPPUNIT_TEST(testParsePOPM); + CPPUNIT_TEST(testParsePOPMWithoutCounter); + CPPUNIT_TEST(testRenderPOPM); + CPPUNIT_TEST(testPOPMFromFile); CPPUNIT_TEST(testParseRelativeVolumeFrame); CPPUNIT_TEST(testParseUniqueFileIdentifierFrame); CPPUNIT_TEST(testParseEmptyUniqueFileIdentifierFrame); @@ -138,6 +144,75 @@ public: CPPUNIT_ASSERT_EQUAL(String("d"), f.description()); } + void testParsePOPM() + { + ID3v2::PopularimeterFrame f(ByteVector("POPM" + "\x00\x00\x00\x17" + "\x00\x00" + "email@example.com\x00" + "\x02" + "\x00\x00\x00\x03", 33)); + CPPUNIT_ASSERT_EQUAL(String("email@example.com"), f.email()); + CPPUNIT_ASSERT_EQUAL(2, f.rating()); + CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), f.counter()); + } + + void testParsePOPMWithoutCounter() + { + ID3v2::PopularimeterFrame f(ByteVector("POPM" + "\x00\x00\x00\x13" + "\x00\x00" + "email@example.com\x00" + "\x02", 29)); + CPPUNIT_ASSERT_EQUAL(String("email@example.com"), f.email()); + CPPUNIT_ASSERT_EQUAL(2, f.rating()); + CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f.counter()); + } + + void testRenderPOPM() + { + ID3v2::PopularimeterFrame f; + f.setEmail("email@example.com"); + f.setRating(2); + f.setCounter(3); + CPPUNIT_ASSERT_EQUAL( + ByteVector("POPM" + "\x00\x00\x00\x17" + "\x00\x00" + "email@example.com\x00" + "\x02" + "\x00\x00\x00\x03", 33), + f.render()); + } + + void testPOPMtoString() + { + ID3v2::PopularimeterFrame f; + f.setEmail("email@example.com"); + f.setRating(2); + f.setCounter(3); + CPPUNIT_ASSERT_EQUAL( + String("email@example.com rating=2 counter=3"), f.toString()); + } + + void testPOPMFromFile() + { + string newname = copyFile("xing", ".mp3"); + + ID3v2::PopularimeterFrame *f = new ID3v2::PopularimeterFrame(); + f->setEmail("email@example.com"); + f->setRating(2); + f->setCounter(3); + + MPEG::File foo(newname.c_str()); + foo.ID3v2Tag()->addFrame(f); + foo.save(); + + MPEG::File bar(newname.c_str()); + CPPUNIT_ASSERT_EQUAL(String("email@example.com"), dynamic_cast(bar.ID3v2Tag()->frameList("POPM").front())->email()); + deleteFile(newname); + } + // http://bugs.kde.org/show_bug.cgi?id=150481 void testParseRelativeVolumeFrame() {