File indexing completed on 2024-04-28 04:58:02
0001 0002 /* 0003 * This file is part of the KDE KIO-Extras project 0004 * SPDX-FileCopyrightText: 2009 Vytautas Mickus <vmickus@gmail.com> 0005 * SPDX-FileCopyrightText: 2016 Anthony Fieroni <bvbfan@abv.com> 0006 * 0007 * SPDX-License-Identifier: LGPL-2.1-only 0008 */ 0009 0010 #include "audiocreator.h" 0011 0012 #include <QFile> 0013 #include <QImage> 0014 #include <QMimeDatabase> 0015 #include <QMimeType> 0016 0017 #include <KPluginFactory> 0018 0019 #include <aifffile.h> 0020 #include <apefile.h> 0021 #include <apetag.h> 0022 #include <attachedpictureframe.h> 0023 #include <fileref.h> 0024 #include <flacfile.h> 0025 #include <flacpicture.h> 0026 #include <id3v2tag.h> 0027 #include <mp4file.h> 0028 #include <mp4tag.h> 0029 #include <mpcfile.h> 0030 #include <mpegfile.h> 0031 #include <wavfile.h> 0032 #include <wavpackfile.h> 0033 #include <xiphcomment.h> 0034 0035 K_PLUGIN_CLASS_WITH_JSON(AudioCreator, "audiothumbnail.json") 0036 0037 AudioCreator::AudioCreator(QObject *parent, const QVariantList &args) 0038 : KIO::ThumbnailCreator(parent, args) 0039 { 0040 } 0041 0042 AudioCreator::~AudioCreator() 0043 { 0044 } 0045 0046 namespace TagLib 0047 { 0048 namespace RIFF 0049 { 0050 namespace AIFF 0051 { 0052 struct FileExt : public File { 0053 using File::File; 0054 ID3v2::Tag *ID3v2Tag() const 0055 { 0056 return tag(); 0057 } 0058 }; 0059 } 0060 } 0061 } 0062 0063 template<class T> 0064 static KIO::ThumbnailResult parseID3v2Tag(T &file) 0065 { 0066 if (!file.hasID3v2Tag() || !file.ID3v2Tag()) { 0067 return KIO::ThumbnailResult::fail(); 0068 } 0069 const auto &map = file.ID3v2Tag()->frameListMap(); 0070 if (map["APIC"].isEmpty()) { 0071 return KIO::ThumbnailResult::fail(); 0072 } 0073 auto apicFrame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame *>(map["APIC"].front()); 0074 if (!apicFrame) { 0075 return KIO::ThumbnailResult::fail(); 0076 } 0077 const auto coverData = apicFrame->picture(); 0078 QImage img; 0079 bool okay = img.loadFromData((uchar *)coverData.data(), coverData.size()); 0080 return okay ? KIO::ThumbnailResult::pass(img) : KIO::ThumbnailResult::fail(); 0081 } 0082 0083 template<class T> 0084 static KIO::ThumbnailResult parseFlacTag(T &file) 0085 { 0086 const auto pictureList = file.pictureList(); 0087 for (const auto &picture : pictureList) { 0088 if (picture->type() != TagLib::FLAC::Picture::FrontCover) { 0089 continue; 0090 } 0091 const auto coverData = picture->data(); 0092 QImage img; 0093 bool okay = img.loadFromData((uchar *)coverData.data(), coverData.size()); 0094 return okay ? KIO::ThumbnailResult::pass(img) : KIO::ThumbnailResult::fail(); 0095 } 0096 return KIO::ThumbnailResult::fail(); 0097 } 0098 0099 template<class T> 0100 static KIO::ThumbnailResult parseMP4Tag(T &file) 0101 { 0102 if (!file.hasMP4Tag() || !file.tag()) { 0103 return KIO::ThumbnailResult::fail(); 0104 } 0105 const auto &map = file.tag()->itemMap(); 0106 for (const auto &coverList : map) { 0107 auto coverArtList = coverList.second.toCoverArtList(); 0108 if (coverArtList.isEmpty()) { 0109 continue; 0110 } 0111 const auto coverData = coverArtList[0].data(); 0112 QImage img; 0113 bool okay = img.loadFromData((uchar *)coverData.data(), coverData.size()); 0114 return okay ? KIO::ThumbnailResult::pass(img) : KIO::ThumbnailResult::fail(); 0115 } 0116 return KIO::ThumbnailResult::fail(); 0117 } 0118 0119 template<class T> 0120 static KIO::ThumbnailResult parseAPETag(T &file) 0121 { 0122 if (!file.hasAPETag() || !file.APETag()) { 0123 return KIO::ThumbnailResult::fail(); 0124 } 0125 const auto &map = file.APETag()->itemListMap(); 0126 for (const auto &item : map) { 0127 if (item.second.type() != TagLib::APE::Item::Binary) { 0128 continue; 0129 } 0130 const auto coverData = item.second.binaryData(); 0131 const auto data = coverData.data(); 0132 const auto size = coverData.size(); 0133 for (size_t i = 0; i < size; ++i) { 0134 if (data[i] == '\0' && (i + 1) < size) { 0135 const auto start = data + i + 1; 0136 QImage img; 0137 bool okay = img.loadFromData((uchar *)start, size - (start - data)); 0138 return okay ? KIO::ThumbnailResult::pass(img) : KIO::ThumbnailResult::fail(); 0139 ; 0140 } 0141 } 0142 } 0143 return KIO::ThumbnailResult::fail(); 0144 } 0145 0146 KIO::ThumbnailResult AudioCreator::create(const KIO::ThumbnailRequest &request) 0147 { 0148 QMimeDatabase db; 0149 QMimeType type = db.mimeTypeForName(request.mimeType()); 0150 0151 const QByteArray fileNameBytes = QFile::encodeName(request.url().toLocalFile()); 0152 const char *fileName = fileNameBytes.constData(); 0153 0154 if (!type.isValid()) { 0155 return KIO::ThumbnailResult::fail(); 0156 } 0157 0158 if (type.inherits("audio/mpeg")) { 0159 TagLib::MPEG::File file(fileName); 0160 0161 if (auto result = parseID3v2Tag(file); result.isValid()) { 0162 return result; 0163 } 0164 0165 return parseAPETag(file); 0166 } 0167 if (type.inherits("audio/x-flac") || type.inherits("audio/flac")) { 0168 TagLib::FLAC::File file(fileName); 0169 0170 if (auto result = parseFlacTag(file); result.isValid()) { 0171 return result; 0172 } 0173 0174 return parseID3v2Tag(file); 0175 } 0176 if (type.inherits("audio/mp4") || type.inherits("audio/x-m4a") || type.inherits("audio/vnd.audible.aax")) { 0177 TagLib::MP4::File file(fileName); 0178 return parseMP4Tag(file); 0179 } 0180 if (type.inherits("audio/x-ape")) { 0181 TagLib::APE::File file(fileName); 0182 return parseAPETag(file); 0183 } 0184 if (type.inherits("audio/x-wavpack") || type.inherits("audio/x-vw")) { 0185 TagLib::WavPack::File file(fileName); 0186 return parseAPETag(file); 0187 } 0188 if (type.inherits("audio/x-musepack")) { 0189 TagLib::MPC::File file(fileName); 0190 return parseAPETag(file); 0191 } 0192 if (type.inherits("audio/ogg") || type.inherits("audio/vorbis")) { 0193 TagLib::FileRef fileRef(fileName); 0194 if (fileRef.isNull()) { 0195 return KIO::ThumbnailResult::fail(); 0196 } 0197 auto xiphComment = dynamic_cast<TagLib::Ogg::XiphComment *>(fileRef.tag()); 0198 if (!xiphComment || xiphComment->isEmpty()) { 0199 return KIO::ThumbnailResult::fail(); 0200 } 0201 return parseFlacTag(*xiphComment); 0202 } 0203 if (type.inherits("audio/x-aiff") || type.inherits("audio/x-aifc")) { 0204 TagLib::RIFF::AIFF::FileExt file(fileName); 0205 return parseID3v2Tag(file); 0206 } 0207 if (type.inherits("audio/x-wav")) { 0208 TagLib::RIFF::WAV::File file(fileName); 0209 return parseID3v2Tag(file); 0210 } 0211 return KIO::ThumbnailResult::fail(); 0212 } 0213 0214 #include "audiocreator.moc" 0215 #include "moc_audiocreator.cpp"