diff --git a/autotests/taglibwritertest.cpp b/autotests/taglibwritertest.cpp --- a/autotests/taglibwritertest.cpp +++ b/autotests/taglibwritertest.cpp @@ -80,9 +80,15 @@ data.add(Property::Comment, QString(QStringLiteral("Comment1") + stringSuffix)); data.add(Property::Copyright, QString(QStringLiteral("Copyright1") + stringSuffix)); + QFile testFile(testFilePath("test.jpg")); + testFile.open(QIODevice::ReadOnly); + const auto pictureData = testFile.readAll(); + + data.add(Property::FrontCover, pictureData); + writerPlugin.write(data); - KFileMetaData::SimpleExtractionResult result(temporaryFileName, mimeType, KFileMetaData::ExtractionResult::ExtractMetaData); + KFileMetaData::SimpleExtractionResult result(temporaryFileName, mimeType, ExtractionResult::ExtractMetaData | ExtractionResult::ExtractImageData); extractResult(mimeType, result); QCOMPARE(result.properties().value(Property::Title), QVariant(QStringLiteral("Title1") + stringSuffix)); @@ -96,6 +102,7 @@ QCOMPARE(result.properties().value(Property::Genre), QVariant(QStringLiteral("Genre1") + stringSuffix)); QCOMPARE(result.properties().value(Property::Comment), QVariant(QStringLiteral("Comment1") + stringSuffix)); QCOMPARE(result.properties().value(Property::Copyright), QVariant(QStringLiteral("Copyright1") + stringSuffix)); + QCOMPARE(result.properties().value(Property::FrontCover), pictureData); QFile::remove(temporaryFileName); } diff --git a/src/writers/taglibwriter.cpp b/src/writers/taglibwriter.cpp --- a/src/writers/taglibwriter.cpp +++ b/src/writers/taglibwriter.cpp @@ -21,12 +21,16 @@ #include "taglibwriter.h" #include "kfilemetadata_debug.h" +#include +#include + #include #include #include #include #include #include +#include #include #include #include @@ -71,9 +75,14 @@ 0, 1, 13, 54, 64, 118, 128, 186, 196, 242, 255 }; - using namespace KFileMetaData; +TagLib::String determineMimeTypeOfPicture(const QByteArray &pictureData) +{ + const QMimeDatabase mimeDb; + return QStringToTString(mimeDb.mimeTypeForData(pictureData).name()); +} + void writeID3v2Tags(TagLib::ID3v2::Tag *id3Tags, const PropertyMap &newProperties) { if (newProperties.contains(Property::Rating)) { @@ -86,6 +95,26 @@ id3Tags->addFrame(ratingFrame); } } + if (newProperties.contains(Property::FrontCover)) { + // Try to update existing front cover frame first + TagLib::ID3v2::FrameList lstID3v2; + lstID3v2 = id3Tags->frameListMap()["APIC"]; + const auto pictureData = newProperties.value(Property::FrontCover).toByteArray(); + for (auto& frame : qAsConst(lstID3v2)) + { + auto *frontCoverFrame = static_cast(frame); + if (frontCoverFrame->type() == frontCoverFrame->FrontCover) { + frontCoverFrame->setPicture(TagLib::ByteVector(static_cast(pictureData.data()), pictureData.size())); + frontCoverFrame->setMimeType(determineMimeTypeOfPicture(pictureData)); + return; + } + } + auto pictureFrame = new TagLib::ID3v2::AttachedPictureFrame; + pictureFrame->setPicture(TagLib::ByteVector(pictureData.data(), pictureData.size())); + pictureFrame->setType(pictureFrame->FrontCover); + pictureFrame->setMimeType(determineMimeTypeOfPicture(pictureData)); + id3Tags->addFrame(pictureFrame); + } } void writeApeTags(TagLib::PropertyMap &oldProperties, const PropertyMap &newProperties) @@ -119,13 +148,79 @@ } asfTags->setAttribute("WM/SharedUserRating", TagLib::String::number(rating)); } + if (properties.contains(Property::FrontCover)) { + TagLib::ASF::AttributeList lstASF = asfTags->attribute("WM/Picture"); + const auto pictureData = properties.value(Property::FrontCover).toByteArray(); + for (const auto& attribute : qAsConst(lstASF)) { + TagLib::ASF::Picture picture = attribute.toPicture(); + if (picture.type() == TagLib::ASF::Picture::FrontCover) { + picture.setPicture(TagLib::ByteVector(pictureData.constData(), pictureData.size())); + picture.setMimeType(determineMimeTypeOfPicture(pictureData)); + TagLib::ByteVector pictureData = picture.picture(); + return; + } + } + TagLib::ASF::Picture picture; + picture.setPicture(TagLib::ByteVector(pictureData.constData(), pictureData.size())); + picture.setType(TagLib::ASF::Picture::FrontCover); + lstASF.append(picture); + } } void writeMp4Tags(TagLib::MP4::Tag *mp4Tags, const PropertyMap &newProperties) { if (newProperties.contains(Property::Rating)) { mp4Tags->setItem("rate", TagLib::StringList(TagLib::String::number(newProperties.value(Property::Rating).toInt() * 10))); } + if (newProperties.contains(Property::FrontCover)) { + TagLib::MP4::Item coverArtItem = mp4Tags->item("covr"); + TagLib::MP4::CoverArtList coverArtList; + const auto pictureData = newProperties.value(Property::FrontCover).toByteArray(); + TagLib::MP4::CoverArt coverArt(TagLib::MP4::CoverArt::Format::Unknown, TagLib::ByteVector(pictureData.data(), pictureData.size())); + if (coverArtItem.isValid()) + { + coverArtList = coverArtItem.toCoverArtList(); + coverArtList.clear(); + } + coverArtList.append(coverArt); + mp4Tags->setItem("covr", coverArtList); + } +} + +void writeFlacPicture(TagLib::List lstPic, const PropertyMap &newProperties) { + if (newProperties.contains(Property::FrontCover)) { + const auto pictureData = newProperties.value(Property::FrontCover).toByteArray(); + for (const auto &picture : qAsConst(lstPic)) { + if (picture->type() == picture->FrontCover) { + picture->setData(TagLib::ByteVector(pictureData.data(), pictureData.size())); + picture->setMimeType(determineMimeTypeOfPicture(pictureData)); + return ; + } + } + auto flacPicture = new TagLib::FLAC::Picture; + flacPicture->setMimeType(determineMimeTypeOfPicture(pictureData)); + flacPicture->setData(TagLib::ByteVector(pictureData.data(), pictureData.size())); + lstPic.append(flacPicture); + } +} + +void writeApePicture(TagLib::APE::Tag* apeTags, const PropertyMap &newProperties) { + if (newProperties.contains(Property::FrontCover)) { + /* The cover art tag for APEv2 tags starts with the filename as string + * with zero termination followed by the actual picture data */ + TagLib::ByteVector imageData; + TagLib::String name; + const auto pictureData = newProperties.value(Property::FrontCover).toByteArray(); + if (determineMimeTypeOfPicture(pictureData) == TagLib::String("image/png")) { + name = "frontCover.png"; + } else { + name = "frontCover.jpeg"; + } + imageData.append(name.data(TagLib::String::UTF8)); + imageData.append('\0'); + imageData.append(TagLib::ByteVector(pictureData.constData(), pictureData.size())); + apeTags->setData("COVER ART (FRONT)", imageData); + } } } // anonymous namespace @@ -273,6 +368,9 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); writeApeTags(savedProperties, properties); + if (file.hasAPETag()) { + writeApePicture(file.APETag(), properties); + } file.setProperties(savedProperties); file.save(); } @@ -282,6 +380,9 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); writeApeTags(savedProperties, properties); + if (file.hasAPETag()) { + writeApePicture(file.APETag(), properties); + } file.setProperties(savedProperties); file.save(); } @@ -291,6 +392,9 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); writeApeTags(savedProperties, properties); + if (file.hasAPETag()) { + writeApePicture(file.APETag(), properties); + } file.setProperties(savedProperties); file.save(); } @@ -312,6 +416,7 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); writeVorbisTags(savedProperties, properties); + writeFlacPicture(file.pictureList(), properties); file.setProperties(savedProperties); file.save(); } @@ -321,6 +426,9 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); writeVorbisTags(savedProperties, properties); + if (file.tag()) { + writeFlacPicture(file.tag()->pictureList(), properties); + } file.setProperties(savedProperties); file.save(); } @@ -330,6 +438,9 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); writeVorbisTags(savedProperties, properties); + if (file.tag()) { + writeFlacPicture(file.tag()->pictureList(), properties); + } file.setProperties(savedProperties); file.save(); } @@ -339,6 +450,9 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); writeVorbisTags(savedProperties, properties); + if (file.tag()) { + writeFlacPicture(file.tag()->pictureList(), properties); + } file.setProperties(savedProperties); file.save(); }