diff --git a/autotests/taglibextractortest.cpp b/autotests/taglibextractortest.cpp --- a/autotests/taglibextractortest.cpp +++ b/autotests/taglibextractortest.cpp @@ -101,7 +101,7 @@ QVERIFY(plugin.mimetypes().contains(mimeType)); - SimpleExtractionResult result(fileName, mimeType); + SimpleExtractionResult result(fileName, mimeType, ExtractionResult::ExtractMetaData | ExtractionResult::ExtractImageData); plugin.extract(&result); QCOMPARE(result.types().size(), 1); @@ -114,6 +114,13 @@ QCOMPARE(result.properties().value(Property::Comment), QVariant(QStringLiteral("Comment"))); QCOMPARE(result.properties().value(Property::TrackNumber).toInt(), 1); QCOMPARE(result.properties().value(Property::ReleaseYear).toInt(), 2015); + + QByteArray originalFrontCoverImage; + QFile testFile(testFilePath("cover.jpg")); + testFile.open(QIODevice::ReadOnly); + originalFrontCoverImage = testFile.readAll(); + + QCOMPARE(result.properties().value(Property::FrontCover).toByteArray(), originalFrontCoverImage); } void TagLibExtractorTest::testCommonData_data() diff --git a/src/extractors/taglibextractor.cpp b/src/extractors/taglibextractor.cpp --- a/src/extractors/taglibextractor.cpp +++ b/src/extractors/taglibextractor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -299,6 +300,19 @@ } result->add(Property::Rating, rating); } + if (result->inputFlags() & ExtractionResult::ExtractImageData) { + TagLib::ID3v2::FrameList lstID3v2; + // Attached Front Picture. + lstID3v2 = Id3Tags->frameListMap()["APIC"]; + for (const auto& frame : qAsConst(lstID3v2)) + { + const auto *frontCoverFrame = static_cast(frame); + if (frontCoverFrame->type() == frontCoverFrame->FrontCover) { + result->add(Property::FrontCover, + QByteArray(frontCoverFrame->picture().data(), frontCoverFrame->picture().size())); + } + } + } } void extractMp4Tags(TagLib::MP4::Tag* mp4Tags, ExtractionResult* result) @@ -317,6 +331,15 @@ if (itRating != allTags.end()) { result->add(Property::Rating, itRating->second.toStringList().toString().toInt() / 10); } + if (result->inputFlags() & ExtractionResult::ExtractImageData) { + TagLib::MP4::Item coverArtItem = mp4Tags->item("covr"); + if (coverArtItem.isValid()) + { + TagLib::MP4::CoverArtList coverArtList = coverArtItem.toCoverArtList(); + TagLib::MP4::CoverArt& frontCover = coverArtList.front(); + result->add(Property::FrontCover, QByteArray(frontCover.data().data(), frontCover.data().size())); + } + } } void extractAsfTags(TagLib::ASF::Tag* asfTags, ExtractionResult* result) @@ -364,6 +387,52 @@ const auto attribute = lstASF.front(); result->add(Property::Publisher, TStringToQString(attribute.toString()).trimmed()); } + + if (result->inputFlags() & ExtractionResult::ExtractImageData) { + TagLib::ASF::AttributeList lstASF = asfTags->attribute("WM/Picture"); + for (const auto& attribute : qAsConst(lstASF)) { + TagLib::ASF::Picture picture = attribute.toPicture(); + if (picture.type() == TagLib::ASF::Picture::FrontCover) { + TagLib::ByteVector pictureData = picture.picture(); + result->add(Property::FrontCover, QByteArray(pictureData.data(), pictureData.size())); + } + } + } +} + +void extractApeTags(TagLib::APE::Tag* apeTags, ExtractionResult* result) +{ + if (apeTags->isEmpty()) { + return; + } + if (result->inputFlags() & ExtractionResult::ExtractImageData) { + TagLib::APE::ItemListMap lstApe = apeTags->itemListMap(); + TagLib::APE::ItemListMap::ConstIterator itApe; + + /* The cover art tag for APEv2 tags starts with the filename as string + * with zero termination followed by the actual picture data */ + itApe = lstApe.find("COVER ART (FRONT)"); + if (itApe != lstApe.end()) { + TagLib::ByteVector pictureData = (*itApe).second.binaryData(); + int position = pictureData.find('\0'); + if (position >= 0) { + position += 1; + result->add(Property::FrontCover, + QByteArray(pictureData.data() + position, pictureData.size() - position)); + } + } + } +} + +void extractCoverFromFlacPicture(TagLib::List lstPic, ExtractionResult* result) +{ + if (result->inputFlags() & ExtractionResult::ExtractImageData) { + for (const auto &picture : qAsConst(lstPic)) { + if (picture->type() == picture->FrontCover) { + result->add(Property::FrontCover, QByteArray(picture->data().data(), picture->data().size())); + } + } + } } } // anonymous namespace @@ -427,18 +496,27 @@ if (file.isValid()) { extractAudioProperties(&file, result); readGenericProperties(file.properties(), result); + if (file.hasAPETag()) { + extractApeTags(file.APETag(), result); + } } } else if (mimeType == QLatin1String("audio/x-ape")) { TagLib::APE::File file(&stream, true); if (file.isValid()) { extractAudioProperties(&file, result); readGenericProperties(file.properties(), result); + if (file.hasAPETag()) { + extractApeTags(file.APETag(), result); + } } } else if (mimeType == QLatin1String("audio/x-wavpack")) { TagLib::WavPack::File file(&stream, true); if (file.isValid()) { extractAudioProperties(&file, result); readGenericProperties(file.properties(), result); + if (file.hasAPETag()) { + extractApeTags(file.APETag(), result); + } } } else if (mimeType == QLatin1String("audio/mp4")) { TagLib::MP4::File file(&stream, true); @@ -452,26 +530,30 @@ if (file.isValid()) { extractAudioProperties(&file, result); readGenericProperties(file.properties(), result); + extractCoverFromFlacPicture(file.pictureList(), result); } } else if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { TagLib::Ogg::Vorbis::File file(&stream, true); if (file.isValid()) { extractAudioProperties(&file, result); readGenericProperties(file.properties(), result); + extractCoverFromFlacPicture(file.tag()->pictureList(), result); } } else if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { TagLib::Ogg::Opus::File file(&stream, true); if (file.isValid()) { extractAudioProperties(&file, result); readGenericProperties(file.properties(), result); + extractCoverFromFlacPicture(file.tag()->pictureList(), result); } } else if (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex+ogg")) { TagLib::Ogg::Speex::File file(&stream, true); // Workaround for buggy taglib: // isValid() returns true for invalid files, but XiphComment* tag() returns a nullptr if (file.isValid() && file.tag()) { extractAudioProperties(&file, result); readGenericProperties(file.properties(), result); + extractCoverFromFlacPicture(file.tag()->pictureList(), result); } } else if (mimeType == QLatin1String("audio/x-ms-wma")) { TagLib::ASF::File file(&stream, true);