Skip to content

Commit

Permalink
Support FLAC picture properties, document removal
Browse files Browse the repository at this point in the history
  • Loading branch information
ufleisch committed Oct 10, 2023
1 parent 8f99875 commit 38c593d
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 23 deletions.
50 changes: 28 additions & 22 deletions examples/tagwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void usage()
cout << " -R <tagname> <tagvalue>" << endl;
cout << " -I <tagname> <tagvalue>" << endl;
cout << " -D <tagname>" << endl;
cout << " -p <picturefile> <description>" << endl;
cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << endl;
cout << endl;

exit(1);
Expand Down Expand Up @@ -174,29 +174,35 @@ int main(int argc, char *argv[])
}
case 'p': {
if(i + 2 < argc) {
if(!isFile(value.toCString())) {
cout << value.toCString() << " not found." << endl;
return 1;
}
ifstream picture;
picture.open(value.toCString());
stringstream buffer;
buffer << picture.rdbuf();
picture.close();
TagLib::String buf(buffer.str());
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
TagLib::String mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
? "image/png" : "image/jpeg";
TagLib::String description(argv[i + 2]);
numArgsConsumed = 3;
t->setComplexProperties("PICTURE", {
{
{"data", data},
{"pictureType", "Front Cover"},
{"mimeType", mimeType},
{"description", description}
if(!value.isEmpty()) {
if(!isFile(value.toCString())) {
cout << value.toCString() << " not found." << endl;
return 1;
}
});
ifstream picture;
picture.open(value.toCString());
stringstream buffer;
buffer << picture.rdbuf();
picture.close();
TagLib::String buf(buffer.str());
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
TagLib::String mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
? "image/png" : "image/jpeg";
TagLib::String description(argv[i + 2]);
it->file()->setComplexProperties("PICTURE", {
{
{"data", data},
{"pictureType", "Front Cover"},
{"mimeType", mimeType},
{"description", description}
}
});
}
else {
// empty value, remove pictures
it->file()->setComplexProperties("PICTURE", {});
}
}
else {
usage();
Expand Down
63 changes: 63 additions & 0 deletions taglib/flac/flacfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,69 @@ PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
return xiphComment(true)->setProperties(properties);
}

StringList FLAC::File::complexPropertyKeys() const
{
StringList keys = TagLib::File::complexPropertyKeys();
if(!keys.contains("PICTURE")) {
for(const auto &block : std::as_const(d->blocks)) {
if(dynamic_cast<Picture *>(block) != nullptr) {
keys.append("PICTURE");
break;
}
}
}
return keys;
}

List<VariantMap> FLAC::File::complexProperties(const String &key) const
{
if(key == "PICTURE") {
List<VariantMap> properties;
for(const auto &block : std::as_const(d->blocks)) {
if(auto picture = dynamic_cast<Picture *>(block)) {
VariantMap property;
property.insert("data", picture->data());
property.insert("mimeType", picture->mimeType());
property.insert("description", picture->description());
property.insert("pictureType",
FLAC::Picture::typeToString(picture->type()));
property.insert("width", picture->width());
property.insert("height", picture->height());
property.insert("numColors", picture->numColors());
property.insert("colorDepth", picture->colorDepth());
properties.append(property);
}
}
return properties;
}
return TagLib::File::complexProperties(key);
}

bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(key == "PICTURE") {
removePictures();

for(auto property : value) {
FLAC::Picture *picture = new FLAC::Picture;
picture->setData(property.value("data").value<ByteVector>());
picture->setMimeType(property.value("mimeType").value<String>());
picture->setDescription(property.value("description").value<String>());
picture->setType(FLAC::Picture::typeFromString(
property.value("pictureType").value<String>()));
picture->setWidth(property.value("width").value<int>());
picture->setHeight(property.value("height").value<int>());
picture->setNumColors(property.value("numColors").value<int>());
picture->setColorDepth(property.value("colorDepth").value<int>());
addPicture(picture);
}
}
else {
return TagLib::File::setComplexProperties(key, value);
}
return true;
}

FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties.get();
Expand Down
17 changes: 17 additions & 0 deletions taglib/flac/flacfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,23 @@ namespace TagLib {
*/
PropertyMap setProperties(const PropertyMap &) override;

/*!
* Returns ["PICTURE"] if any picture is stored in METADATA_BLOCK_PICTURE.
*/
StringList complexPropertyKeys() const override;

/*!
* Get the pictures stored in METADATA_BLOCK_PICTURE as complex properties
* for \a key "PICTURE".
*/
List<VariantMap> complexProperties(const String &key) const override;

/*!
* Set the complex properties \a value as pictures in METADATA_BLOCK_PICTURE
* for \a key "PICTURE".
*/
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;

/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
Expand Down
1 change: 1 addition & 0 deletions taglib/tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ namespace TagLib {
/*!
* Set all complex properties for a given \a key using variant maps as
* \a value with the same format as returned by complexProperties().
* An empty list as \a value to removes all complex properties for \a key.
*/
virtual bool setComplexProperties(const String &key, const List<VariantMap> &value);

Expand Down
2 changes: 1 addition & 1 deletion taglib/toolkit/tvariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ class Variant::VariantPrivate
{
public:
VariantPrivate() = default;
VariantPrivate(const StdVariantType &v) : data(v) {}
VariantPrivate(StdVariantType v) : data(v) {}
StdVariantType data;
};

Expand Down
53 changes: 53 additions & 0 deletions tests/test_complexproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
***************************************************************************/

#include "asfpicture.h"
#include "flacpicture.h"
#include "flacfile.h"
#include "tbytevector.h"
#include "tvariant.h"
#include "tzlib.h"
Expand Down Expand Up @@ -69,6 +71,7 @@ class TestComplexProperties : public CppUnit::TestFixture
CPPUNIT_TEST(testReadMp3Picture);
CPPUNIT_TEST(testReadM4aPicture);
CPPUNIT_TEST(testReadOggPicture);
CPPUNIT_TEST(testReadWriteFlacPicture);
CPPUNIT_TEST(testReadWriteMultipleProperties);
CPPUNIT_TEST(testSetGetId3Geob);
CPPUNIT_TEST(testSetGetId3Picture);
Expand Down Expand Up @@ -165,6 +168,56 @@ class TestComplexProperties : public CppUnit::TestFixture
CPPUNIT_ASSERT_EQUAL(6, picture.value("height").value<int>());
}

void testReadWriteFlacPicture()
{
VariantMap picture(TEST_PICTURE);
picture.insert("colorDepth", 8);
picture.insert("numColors", 1);
picture.insert("width", 1);
picture.insert("height", 1);

ScopedFileCopy copy("no-tags", ".flac");

{
FLAC::File f(copy.fileName().c_str(), false);
CPPUNIT_ASSERT(f.complexPropertyKeys().isEmpty());
CPPUNIT_ASSERT(f.pictureList().isEmpty());
CPPUNIT_ASSERT(f.setComplexProperties(PICTURE_KEY, {picture}));
f.save();
}
{
FLAC::File f(copy.fileName().c_str(), false);
CPPUNIT_ASSERT_EQUAL(StringList(PICTURE_KEY), f.complexPropertyKeys());
CPPUNIT_ASSERT_EQUAL(picture, f.complexProperties(PICTURE_KEY).front());
auto flacPictures = f.pictureList();
CPPUNIT_ASSERT_EQUAL(1U, flacPictures.size());
auto flacPicture = flacPictures.front();
CPPUNIT_ASSERT_EQUAL(picture.value("data").value<ByteVector>(),
flacPicture->data());
CPPUNIT_ASSERT_EQUAL(picture.value("mimeType").value<String>(),
flacPicture->mimeType());
CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, flacPicture->type());
CPPUNIT_ASSERT_EQUAL(picture.value("description").value<String>(),
flacPicture->description());
CPPUNIT_ASSERT_EQUAL(picture.value("colorDepth").value<int>(),
flacPicture->colorDepth());
CPPUNIT_ASSERT_EQUAL(picture.value("numColors").value<int>(),
flacPicture->numColors());
CPPUNIT_ASSERT_EQUAL(picture.value("width").value<int>(),
flacPicture->width());
CPPUNIT_ASSERT_EQUAL(picture.value("height").value<int>(),
flacPicture->height());

CPPUNIT_ASSERT(f.setComplexProperties(PICTURE_KEY, {}));
f.save();
}
{
FLAC::File f(copy.fileName().c_str(), false);
CPPUNIT_ASSERT(f.complexPropertyKeys().isEmpty());
CPPUNIT_ASSERT(f.pictureList().isEmpty());
}
}

void testReadWriteMultipleProperties()
{
const VariantMap picture2 {
Expand Down

0 comments on commit 38c593d

Please sign in to comment.