QImage ID3v2TagHelper::embeddedCover() const { TagLib::ID3v2::FrameList apicList = m_tag->frameListMap()[fieldName( Meta::valHasCover ).toCString()]; TagLib::ID3v2::AttachedPictureFrame *cover = NULL; TagLib::ID3v2::AttachedPictureFrame *otherCover = NULL; for( TagLib::ID3v2::FrameList::ConstIterator it = apicList.begin(); it != apicList.end(); ++it ) { TagLib::ID3v2::AttachedPictureFrame *currFrame = dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it ); if( currFrame->picture().size() < MIN_COVER_SIZE ) continue; if( currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover ) { cover = currFrame; } else if( currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::Other ) { otherCover = currFrame; } } if( !cover && otherCover ) cover = otherCover; if( !cover ) return QImage(); return QImage::fromData( ( uchar * )( cover->picture().data() ), cover->picture().size() ); }
/* GET ALBUM COVER */ const char * tag_data_get_cover (TagDatac * data, uint * len) { TagLib::ID3v2::AttachedPictureFrame * pic = NULL; if (!(reinterpret_cast<TagData *>(data))->id3v2 || (reinterpret_cast<TagData *>(data))->id3v2->frameListMap()["APIC"].isEmpty()) { *len = 0; return NULL; } TagLib::ID3v2::FrameList::ConstIterator it; TagLib::ID3v2::FrameList l = (reinterpret_cast<TagData *>(data))->id3v2->frameListMap()["APIC"]; for(it=l.begin(); it!=l.end(); ++it) { TagLib::ID3v2::AttachedPictureFrame * t = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it); if (pic == NULL) pic = t; if (t->type () == TagLib::ID3v2::AttachedPictureFrame::FrontCover) { pic = t; break; } } if (pic == NULL) { *len = 0; return NULL; } *len = pic->picture ().size (); const char * out = (const char *) malloc (*len); memcpy ((void *) out, pic->picture ().data(), *len); return out; }
bool CTagBase::exportImage(const wchar_t* s) { if (!s) return false; if (_tagFile) { //get picture TagLib::MPEG::File *f = (TagLib::MPEG::File *)_tagFile.get(); if (!f->hasID3v2Tag() || f->ID3v2Tag()->isEmpty()) return false; /* TagLib::ID3v2::FrameList::ConstIterator it = f->ID3v2Tag()->frameList().begin(); for (; it != f->ID3v2Tag()->frameList().end(); it++) { if ((*it)->frameID().operator == (TagLib::ByteVector("APIC"))) { HANDLE hFile = CreateFile(s, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) return false; int nWriteBytes = 0; size_t size = (*it)->size(); ::WriteFile(hFile, (*it)->render().data(), (*it)->size(), (LPDWORD)&nWriteBytes, NULL); ::CloseHandle(hFile); if (nWriteBytes != size) return false; return true; } }*/ if (f->ID3v2Tag()->frameListMap().size() == 0) return false; if (f->ID3v2Tag()->frameListMap().find("APIC") == f->ID3v2Tag()->frameListMap().end()) return false; TagLib::ID3v2::FrameList Flist = f->ID3v2Tag()->frameListMap()["APIC"]; if (Flist.isEmpty()) return false; TagLib::ID3v2::AttachedPictureFrame *p = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(Flist.front()); size_t size = p->picture().size(); CString strPicType = p->mimeType().toCString(true); int nPos = strPicType.Find('/'); CString strTemp = strPicType.Right(strPicType.GetLength() - nPos - 1); //CString strPicPath = s; //if (strTemp == _T("png")) // strPicPath.Append(_T(".png")); //else // strPicPath.Append(_T(".jpg")); HANDLE hFile = CreateFile(s, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) return false; int nWriteBytes = 0; ::WriteFile(hFile, p->picture().data(), size, (LPDWORD)&nWriteBytes, NULL); ::CloseHandle(hFile); if (nWriteBytes != size) return false; return true; } return false; }
/* ** Extracts cover art embedded in ID3v2 tags. ** */ bool QCoverArt::ExtractID3(TagLib::ID3v2::Tag* tag) { const TagLib::ID3v2::FrameList& frameList = tag->frameList("APIC"); if (!frameList.isEmpty()) { // Grab the first image TagLib::ID3v2::AttachedPictureFrame* frame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(frameList.front()); img.loadFromData((const unsigned char*)frame->picture().data(), (int)frame->picture().size()); return true; } return false; }
/** Check if file has an inner picture. */ bool FileHelper::hasCover() const { bool atLeastOnePicture = false; switch (_fileType) { case EXT_MP3: { TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file); if (mpegFile && mpegFile->hasID3v2Tag()) { // Look for picture frames only TagLib::ID3v2::FrameList listOfMp3Frames = mpegFile->ID3v2Tag()->frameListMap()["APIC"]; // It's possible to have more than one picture per file! if (!listOfMp3Frames.isEmpty()) { for (TagLib::ID3v2::FrameList::ConstIterator it = listOfMp3Frames.begin(); it != listOfMp3Frames.end() ; it++) { // Cast a Frame* to AttachedPictureFrame* TagLib::ID3v2::AttachedPictureFrame *pictureFrame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it); atLeastOnePicture = atLeastOnePicture || (pictureFrame != nullptr && !pictureFrame->picture().isEmpty()); } } } break; } case EXT_FLAC: { if (TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file)) { atLeastOnePicture = !flacFile->pictureList().isEmpty(); } } default: break; } return atLeastOnePicture; }
bool MetaDSF::exportPictures(const char *prefix) const { std::map<std::string, unsigned int> counter; TagLib::ID3v2::FrameList l = _i->_file.ID3v2Tag()->frameList("APIC"); TagLib::ID3v2::FrameList::ConstIterator it; for (it = l.begin(); it != l.end(); ++it) { TagLib::ID3v2::AttachedPictureFrame *f = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(*it); std::string fname = prefix; std::string tname = picTypeDesc[f->type()].toCString(); std::string ext = MIMETypeToExtMap[f->mimeType()].toCString(); if (counter.find(tname) == counter.end()) counter[tname] = 1; else counter[tname] += 1; fname += "_"; fname += tname; fname += "_"; fname += std::to_string(counter[tname]); fname += "."; fname += ext; //std::cout << "fname = " << fname << std::endl; writeFileFromVector(fname.c_str(), f->picture()); } return true; }
Tag::Tag(const QString &filename) : m_filename(filename) { TagLib::MPEG::File mpegfile(filename.toLocal8Bit().constData()); TagLib::ID3v2::Tag* id3v2 = mpegfile.ID3v2Tag(); if (id3v2 && !id3v2->isEmpty()) { readRegularTag(id3v2, m_data); int picnum = 0; TagLib::ID3v2::FrameList frames = id3v2->frameListMap()["APIC"]; // attached picture TagLib::ID3v2::FrameList::ConstIterator it = frames.begin(); while (it != frames.end()) { TagLib::ID3v2::AttachedPictureFrame* apic = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it); TagLib::ByteVector bytes = apic->picture(); QImage img = QImage::fromData(reinterpret_cast<const uchar*>(bytes.data()), bytes.size()); if (!img.isNull()) { m_data[QLatin1String("picture") + QString::number(picnum++)] = QVariant(img); } ++it; } } else { TagLib::FileRef fileref(filename.toLocal8Bit().constData()); if (fileref.isNull()) return; TagLib::Tag* tag = fileref.tag(); if (!tag || tag->isEmpty()) return; readRegularTag(tag, m_data); } }
Cover* FileHelper::extractCover() { Cover *cover = nullptr; switch (_fileType) { case EXT_MP3: { TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file); if (mpegFile && mpegFile->hasID3v2Tag()) { // Look for picture frames only TagLib::ID3v2::FrameList listOfMp3Frames = mpegFile->ID3v2Tag()->frameListMap()["APIC"]; // It's possible to have more than one picture per file! if (!listOfMp3Frames.isEmpty()) { for (TagLib::ID3v2::FrameList::ConstIterator it = listOfMp3Frames.begin(); it != listOfMp3Frames.end() ; it++) { // Cast a Frame* to AttachedPictureFrame* TagLib::ID3v2::AttachedPictureFrame *pictureFrame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it); if (pictureFrame) { // Performs a deep copy of the cover QByteArray b = QByteArray(pictureFrame->picture().data(), pictureFrame->picture().size()); cover = new Cover(b, QString(pictureFrame->mimeType().toCString(true))); } } } } else if (mpegFile && mpegFile->hasID3v1Tag()) { qDebug() << Q_FUNC_INFO << "Not implemented for ID3v1Tag"; } break; } case EXT_FLAC: { if (TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file)) { auto list = flacFile->pictureList(); for (auto it = list.begin(); it != list.end() ; it++) { TagLib::FLAC::Picture *p = *it; if (p->type() == TagLib::FLAC::Picture::FrontCover) { // Performs a deep copy of the cover QByteArray b = QByteArray(p->data().data(), p->data().size()); cover = new Cover(b, QString(p->mimeType().toCString(true))); break; } } } break; } default: qDebug() << Q_FUNC_INFO << "Not implemented for this file type" << _fileType << _file << _fileInfo.absoluteFilePath(); break; } return cover; }
bool CoverUtils::coverFromMPEGTags(TagLib::ID3v2::Tag *tag, Album *album) { if (!tag) return false; TagLib::ID3v2::FrameList list = tag->frameList("APIC"); if (list.isEmpty()) return false; TagLib::ID3v2::AttachedPictureFrame *frame = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(list.front()); if (!frame) return false; const int frameSize = frame->picture().size(); if (frameSize <= 0) return false; QImage image; image.loadFromData((const uchar *) frame->picture().data(), frame->picture().size()); if (!isAcceptableImage(image)) return false; return saveImage(image, album); }
/* ** Extracts cover art embedded in ID3v2 tags. ** */ bool CCover::ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target) { const TagLib::ID3v2::FrameList& frameList = tag->frameList("APIC"); if (!frameList.isEmpty()) { // Grab the first image TagLib::ID3v2::AttachedPictureFrame* frame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(frameList.front()); return WriteCover(frame->picture(), target); } return false; }
bool ID3v2TagHelper::hasEmbeddedCover() const { TagLib::ID3v2::FrameList apicList = m_tag->frameListMap()[fieldName( Meta::valHasCover ).toCString()]; for( TagLib::ID3v2::FrameList::ConstIterator it = apicList.begin(); it != apicList.end(); ++it ) { TagLib::ID3v2::AttachedPictureFrame *currFrame = dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it ); if( currFrame->picture().size() < MIN_COVER_SIZE ) continue; if( currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover || currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::Other ) return true; } return false; }
bool SFB::Audio::AddID3v2TagToDictionary(CFMutableDictionaryRef dictionary, std::vector<std::shared_ptr<AttachedPicture>>& attachedPictures, const TagLib::ID3v2::Tag *tag) { if(nullptr == dictionary || nullptr == tag) return false; // Add the basic tags not specific to ID3v2 AddTagToDictionary(dictionary, tag); // Release date auto frameList = tag->frameListMap()["TDRC"]; if(!frameList.isEmpty()) { /* The timestamp fields are based on a subset of ISO 8601. When being as precise as possible the format of a time string is yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of 24), ":", minutes, ":", seconds), but the precision may be reduced by removing as many time indicators as wanted. Hence valid timestamps are yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use the slash character as described in 8601, and for multiple non- contiguous dates, use multiple strings, if allowed by the frame definition. */ TagLib::AddStringToCFDictionary(dictionary, Metadata::kReleaseDateKey, frameList.front()->toString()); } // Extract composer if present frameList = tag->frameListMap()["TCOM"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kComposerKey, frameList.front()->toString()); // Extract album artist frameList = tag->frameListMap()["TPE2"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kAlbumArtistKey, frameList.front()->toString()); // BPM frameList = tag->frameListMap()["TBPM"]; if(!frameList.isEmpty()) { bool ok = false; int BPM = frameList.front()->toString().toInt(&ok); if(ok) AddIntToDictionary(dictionary, Metadata::kBPMKey, BPM); } // Rating TagLib::ID3v2::PopularimeterFrame *popularimeter = nullptr; frameList = tag->frameListMap()["POPM"]; if(!frameList.isEmpty() && nullptr != (popularimeter = dynamic_cast<TagLib::ID3v2::PopularimeterFrame *>(frameList.front()))) AddIntToDictionary(dictionary, Metadata::kRatingKey, popularimeter->rating()); // Extract total tracks if present frameList = tag->frameListMap()["TRCK"]; if(!frameList.isEmpty()) { // Split the tracks at '/' TagLib::String s = frameList.front()->toString(); bool ok; size_t pos = s.find("/", 0); if(TagLib::String::npos() != pos) { int trackNum = s.substr(0, pos).toInt(&ok); if(ok) AddIntToDictionary(dictionary, Metadata::kTrackNumberKey, trackNum); int trackTotal = s.substr(pos + 1).toInt(&ok); if(ok) AddIntToDictionary(dictionary, Metadata::kTrackTotalKey, trackTotal); } else if(s.length()) { int trackNum = s.toInt(&ok); if(ok) AddIntToDictionary(dictionary, Metadata::kTrackNumberKey, trackNum); } } // Extract disc number and total discs frameList = tag->frameListMap()["TPOS"]; if(!frameList.isEmpty()) { // Split the tracks at '/' TagLib::String s = frameList.front()->toString(); bool ok; size_t pos = s.find("/", 0); if(TagLib::String::npos() != pos) { int discNum = s.substr(0, pos).toInt(&ok); if(ok) AddIntToDictionary(dictionary, Metadata::kDiscNumberKey, discNum); int discTotal = s.substr(pos + 1).toInt(&ok); if(ok) AddIntToDictionary(dictionary, Metadata::kDiscTotalKey, discTotal); } else if(s.length()) { int discNum = s.toInt(&ok); if(ok) AddIntToDictionary(dictionary, Metadata::kDiscNumberKey, discNum); } } // Lyrics frameList = tag->frameListMap()["USLT"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kLyricsKey, frameList.front()->toString()); // Extract compilation if present (iTunes TCMP tag) frameList = tag->frameListMap()["TCMP"]; if(!frameList.isEmpty()) // It seems that the presence of this frame indicates a compilation CFDictionarySetValue(dictionary, Metadata::kCompilationKey, kCFBooleanTrue); frameList = tag->frameListMap()["TSRC"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kISRCKey, frameList.front()->toString()); // MusicBrainz auto musicBrainzReleaseIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "MusicBrainz Album Id"); if(musicBrainzReleaseIDFrame) TagLib::AddStringToCFDictionary(dictionary, Metadata::kMusicBrainzReleaseIDKey, musicBrainzReleaseIDFrame->fieldList().back()); auto musicBrainzRecordingIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "MusicBrainz Track Id"); if(musicBrainzRecordingIDFrame) TagLib::AddStringToCFDictionary(dictionary, Metadata::kMusicBrainzRecordingIDKey, musicBrainzRecordingIDFrame->fieldList().back()); // Sorting and grouping frameList = tag->frameListMap()["TSOT"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kTitleSortOrderKey, frameList.front()->toString()); frameList = tag->frameListMap()["TSOA"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kAlbumTitleSortOrderKey, frameList.front()->toString()); frameList = tag->frameListMap()["TSOP"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kArtistSortOrderKey, frameList.front()->toString()); frameList = tag->frameListMap()["TSO2"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kAlbumArtistSortOrderKey, frameList.front()->toString()); frameList = tag->frameListMap()["TSOC"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kComposerSortOrderKey, frameList.front()->toString()); frameList = tag->frameListMap()["TIT1"]; if(!frameList.isEmpty()) TagLib::AddStringToCFDictionary(dictionary, Metadata::kGroupingKey, frameList.front()->toString()); // ReplayGain bool foundReplayGain = false; // Preference is TXXX frames, RVA2 frame, then LAME header auto trackGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_TRACK_GAIN"); auto trackPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_TRACK_PEAK"); auto albumGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_ALBUM_GAIN"); auto albumPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_ALBUM_PEAK"); if(!trackGainFrame) trackGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_track_gain"); if(trackGainFrame) { SFB::CFString str(trackGainFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8); double num = CFStringGetDoubleValue(str); AddDoubleToDictionary(dictionary, Metadata::kTrackGainKey, num); AddDoubleToDictionary(dictionary, Metadata::kReferenceLoudnessKey, 89.0); foundReplayGain = true; } if(!trackPeakFrame) trackPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_track_peak"); if(trackPeakFrame) { SFB::CFString str(trackPeakFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8); double num = CFStringGetDoubleValue(str); AddDoubleToDictionary(dictionary, Metadata::kTrackPeakKey, num); } if(!albumGainFrame) albumGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_album_gain"); if(albumGainFrame) { SFB::CFString str(albumGainFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8); double num = CFStringGetDoubleValue(str); AddDoubleToDictionary(dictionary, Metadata::kAlbumGainKey, num); AddDoubleToDictionary(dictionary, Metadata::kReferenceLoudnessKey, 89.0); foundReplayGain = true; } if(!albumPeakFrame) albumPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_album_peak"); if(albumPeakFrame) { SFB::CFString str(albumPeakFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8); double num = CFStringGetDoubleValue(str); AddDoubleToDictionary(dictionary, Metadata::kAlbumPeakKey, num); } // If nothing found check for RVA2 frame if(!foundReplayGain) { frameList = tag->frameListMap()["RVA2"]; for(auto frameIterator : tag->frameListMap()["RVA2"]) { TagLib::ID3v2::RelativeVolumeFrame *relativeVolume = dynamic_cast<TagLib::ID3v2::RelativeVolumeFrame *>(frameIterator); if(!relativeVolume) continue; // Attempt to use the master volume if present auto channels = relativeVolume->channels(); auto channelType = TagLib::ID3v2::RelativeVolumeFrame::MasterVolume; // Fall back on whatever else exists in the frame if(!channels.contains(TagLib::ID3v2::RelativeVolumeFrame::MasterVolume)) channelType = channels.front(); float volumeAdjustment = relativeVolume->volumeAdjustment(channelType); if(TagLib::String("track", TagLib::String::Latin1) == relativeVolume->identification()) { if((int)volumeAdjustment) AddFloatToDictionary(dictionary, Metadata::kTrackGainKey, volumeAdjustment); } else if(TagLib::String("album", TagLib::String::Latin1) == relativeVolume->identification()) { if((int)volumeAdjustment) AddFloatToDictionary(dictionary, Metadata::kAlbumGainKey, volumeAdjustment); } // Fall back to track gain if identification is not specified else { if((int)volumeAdjustment) AddFloatToDictionary(dictionary, Metadata::kTrackGainKey, volumeAdjustment); } } } // Extract album art if present for(auto it : tag->frameListMap()["APIC"]) { TagLib::ID3v2::AttachedPictureFrame *frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame *>(it); if(frame) { SFB::CFData data((const UInt8 *)frame->picture().data(), (CFIndex)frame->picture().size()); SFB::CFString description; if(!frame->description().isEmpty()) description = CFString(frame->description().toCString(true), kCFStringEncodingUTF8); attachedPictures.push_back(std::make_shared<AttachedPicture>(data, (AttachedPicture::Type)frame->type(), description)); } } return true; }
/// <summary> /// Tries to set the cover based on the Tags of the specified file. /// </summary> /// <param name="filePath">Path to the file to get the cover from.</param> bool CoverArt::SetCoverFromTag(LPCWSTR filePath) { LPCWSTR extension = wcsrchr(filePath, L'.'); if (extension == nullptr) { return false; } auto ParseImage = [this] (const BYTE * data, UINT size) { IWICImagingFactory *factory = nullptr; IWICBitmapDecoder *decoder = nullptr; IWICBitmapFrameDecode *source = nullptr; HRESULT hr = E_FAIL; IStream *stream = SHCreateMemStream(data, size); if (stream) { hr = Factories::GetWICFactory(reinterpret_cast<LPVOID*>(&factory)); if (SUCCEEDED(hr)) { hr = factory->CreateDecoderFromStream(stream, nullptr, WICDecodeMetadataCacheOnDemand, &decoder); } if (SUCCEEDED(hr)) { hr = decoder->GetFrame(0, &source); } if (SUCCEEDED(hr)) { SendMessage(gLSModule.GetMessageWindow(), WM_COVERARTUPDATE, (WPARAM)this, (LPARAM)source); } SAFERELEASE(decoder); SAFERELEASE(stream); } return hr == S_OK; }; ++extension; if (_wcsicmp(extension, L"mp3") == 0) { TagLib::ID3v2::AttachedPictureFrame *pictureFrame = nullptr; BYTE picturePriority = 0xFF; TagLib::MPEG::File mp3File(filePath); auto tag = mp3File.ID3v2Tag(); if (tag && tag->frameListMap().contains("APIC")) { for (auto frame : mp3File.ID3v2Tag()->frameListMap()["APIC"]) { auto picFrame = (TagLib::ID3v2::AttachedPictureFrame *)frame; BYTE priority = mID3CoverTypePriority[picFrame->type()]; if (priority < picturePriority) { pictureFrame = picFrame; picturePriority = priority; } } if (pictureFrame != nullptr) { return ParseImage((const BYTE *)pictureFrame->picture().data(), pictureFrame->picture().size()); } } } else if (_wcsicmp(extension, L"flac") == 0) { TagLib::FLAC::File flacFile(filePath); for (auto &picture : flacFile.pictureList()) { if (picture->type() == TagLib::FLAC::Picture::FrontCover) { return ParseImage((const BYTE *)picture->data().data(), picture->data().size()); } } } else if (_wcsicmp(extension, L"mp4") == 0 || _wcsicmp(extension, L"m4a") == 0) { TagLib::MP4::File mp4File(filePath); if (mp4File.tag()->itemListMap().contains("covr")) { auto map = mp4File.tag()->itemListMap()["covr"]; auto list = map.toCoverArtList(); auto cover = list.front(); return ParseImage((const BYTE *)cover.data().data(), cover.data().size()); } } return false; }
bool mediaMP3(Artwork *art, const char *filePath) { TagLib::MPEG::File f(filePath); if (!f.tag()) { return false; } if (!mediaTag(art, &f)) { return false; } art->filetype = FILETYPE_MP3; TagLib::ID3v2::Tag *id3v2tag = f.ID3v2Tag(); if (!id3v2tag) { return true; } // Don't know if this is the right way to interact with // TagLib, but for now it work!!! TagLib::ID3v2::FrameList::ConstIterator it; TagLib::ByteVector id; QString str; for(it = id3v2tag->frameList().begin(); it != id3v2tag->frameList().end(); it ++) { id = (*it)->frameID(); str = TStringToQString((*it)->toString()); if (id == "APIC") // Album cover { if (!art->hasCover()) { TagLib::ID3v2::AttachedPictureFrame *f = (TagLib::ID3v2::AttachedPictureFrame*) (*it); saveImage(art, (const char *) f->picture().data(), f->picture().size()); } } else if (id == "TCOM") // Composer { // composer } else if (id == "TPE2") // Album Artist { art->albumArtist = str; } else if (id == "TCMP" || id == "TCP") // iTunes, compilation { art->type = (str != "" && str != "0") ? ARTWORK_COMPILATION : ARTWORK_NORMAL; } else if (id == "TRCK") // iTunes, track # / total { iTunesSet(str, art->trackNo, art->trackCount); } else if (id == "TPOS") // iTunes, disk # / total { iTunesSet(str, art->discNo, art->discCount); } else if (id == "TSOA") // iTunes, SORT album { art->sAlbum = str; } else if (id == "TSPO") // iTunes, SORT artist { art->sArtist = str; } else if (id == "TSO2") // iTunes, SORT album artist { // art->sAlbumArtist = str; } else if (id == "TSOC") // iTunes, SORT composer { art->sComposer = str; } else if (id == "TDAT") // iTunes, ?????? { // ... what is this?? } else if (id == "TSIZ") // iTunes, ?????? { // ... what is this?? } } return true; }
void saveFile(string stdPath, DataEditors* editors) { const char* charPath = stdPath.c_str(); ifstream fileCheck(charPath); if(!fileCheck.good()) { QWidget *w = new QWidget(); QLabel *l = new QLabel("Invalid file!", w); QPushButton *b = new QPushButton("OK", w); QVBoxLayout *lay = new QVBoxLayout(); lay->addWidget(l); lay->addWidget(b); w->setLayout(lay); QWidget::connect(b, SIGNAL(clicked()), w, SLOT(close())); w->show(); return; } TagLib::FileName name = charPath; TagLib::FileRef file(name); if(editors->artist->isEnabled()) file.tag()->setArtist(editors->artist->text().toStdString()); if(editors->album->isEnabled()) file.tag()->setAlbum(editors->album->text().toStdString()); if(editors->track->isEnabled()) file.tag()->setTrack(editors->track->text().toInt()); if(editors->title->isEnabled()) file.tag()->setTitle(editors->title->text().toStdString()); if(editors->year->isEnabled()) file.tag()->setYear(editors->year->text().toInt()); if(editors->genre->isEnabled()) file.tag()->setGenre(editors->genre->currentText().toStdString()); if(editors->comment->isEnabled()) file.tag()->setComment(editors->comment->toPlainText().toStdString()); file.save(); if(editors->picture->isEnabled()) { TagLib::MPEG::File mpegFile(name); TagLib::ID3v2::Tag *tag = mpegFile.ID3v2Tag(true); TagLib::ID3v2::AttachedPictureFrame *frame = new TagLib::ID3v2::AttachedPictureFrame; QString picture = editors->picture->text(); if(picture == "<Attached picture>") { TagLib::ID3v2::AttachedPictureFrame *f = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(editors->mpegFile->ID3v2Tag(true)->frameList("APIC").front()); frame->setMimeType(f->mimeType()); frame->setPicture(f->picture()); tag->removeFrames("APIC"); tag->addFrame(frame); mpegFile.save(); } else if(picture == "") { tag->removeFrames("APIC"); mpegFile.save(); } else { frame->setMimeType("image/jpeg"); string stdPic = picture.toStdString(); const char* charPicture = stdPic.c_str(); ifstream fileCheck(charPicture); if(!fileCheck.good()) { QWidget *w = new QWidget(); QLabel *l = new QLabel("Invalid picture!", w); QPushButton *b = new QPushButton("OK", w); QVBoxLayout *lay = new QVBoxLayout(); lay->addWidget(l); lay->addWidget(b); w->setLayout(lay); QWidget::connect(b, SIGNAL(clicked()), w, SLOT(close())); w->show(); delete w; return; } ImageFile imageTagLibFile(charPicture); frame->setPicture(imageTagLibFile.data()); tag->removeFrames("APIC"); tag->addFrame(frame); mpegFile.save(); } } }
Meta::FieldHash ID3v2TagHelper::tags() const { Meta::FieldHash data = TagHelper::tags(); TagLib::ID3v2::FrameList list = m_tag->frameList(); for( TagLib::ID3v2::FrameList::ConstIterator it = list.begin(); it != list.end(); ++it ) { qint64 field; TagLib::String frameName = TagLib::String( ( *it )->frameID() ); if( ( field = fieldName( frameName ) ) ) { if( field == Meta::valUniqueId ) { TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast< TagLib::ID3v2::UniqueFileIdentifierFrame * >( *it ); if( !frame ) continue; QString identifier = TStringToQString( TagLib::String( frame->identifier() ) ); if( identifier.isEmpty() ) continue; if( frame->owner() == uidFieldName( UIDAFT ) && isValidUID( identifier, UIDAFT ) ) data.insert( Meta::valUniqueId, identifier ); continue; } else if( field == Meta::valHasCover ) { TagLib::ID3v2::AttachedPictureFrame *frame = dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it ); if( !frame ) continue; if( ( frame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover || frame->type() == TagLib::ID3v2::AttachedPictureFrame::Other ) && frame->picture().size() > MIN_COVER_SIZE ) // must be at least 1kb { data.insert( Meta::valHasCover, true ); } continue; } TagLib::ID3v2::TextIdentificationFrame *frame = dynamic_cast< TagLib::ID3v2::TextIdentificationFrame * >( *it ); if( !frame ) continue; QString value = TStringToQString( frame->fieldList().toString( '\n' ) ); if( field == Meta::valDiscNr ) { int disc; if( ( disc = splitDiscNr( value ).first ) ) data.insert( field, disc ); } else if( field == Meta::valBpm ) data.insert( field, value.toFloat() ); else data.insert( field, value ); } else if( frameName == POPM_Frame ) { TagLib::ID3v2::PopularimeterFrame *frame = dynamic_cast< TagLib::ID3v2::PopularimeterFrame * >( *it ); if( !frame ) continue; if( TStringToQString( frame->email() ).isEmpty() ) // only read anonymous ratings { // FMPS tags have precedence if( !data.contains( Meta::valRating ) && frame->rating() != 0 ) data.insert( Meta::valRating, qRound( frame->rating() / 256.0 * 10.0 ) ); if( !data.contains( Meta::valPlaycount ) && frame->counter() < 10000 ) data.insert( Meta::valPlaycount, frame->counter() ); } } else if( frameName == TXXX_Frame ) { TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast< TagLib::ID3v2::UserTextIdentificationFrame * >( *it ); if( !frame ) continue; // the value of the user text frame is stored in the // second and following fields. TagLib::StringList fields = frame->fieldList(); if( fields.size() >= 2 ) { QString value = TStringToQString( fields[1] ); if( fields[0] == fmpsFieldName( FMPSRating ) ) data.insert( Meta::valRating, qRound( value.toFloat() * 10.0 ) ); else if( fields[0] == fmpsFieldName( FMPSScore ) ) data.insert( Meta::valScore, value.toFloat() * 100.0 ); else if( fields[0] == fmpsFieldName( FMPSPlayCount ) ) data.insert( Meta::valPlaycount, value.toFloat() ); } } } return data; }
QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const { if (filename.isEmpty()) return QByteArray(); qLog(Debug) << "Loading art from" << filename; #ifdef Q_OS_WIN32 TagLib::FileRef ref(filename.toStdWString().c_str()); #else TagLib::FileRef ref(QFile::encodeName(filename).constData()); #endif if (ref.isNull() || !ref.file()) return QByteArray(); // MP3 TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(ref.file()); if (file && file->ID3v2Tag()) { TagLib::ID3v2::FrameList apic_frames = file->ID3v2Tag()->frameListMap()["APIC"]; if (apic_frames.isEmpty()) return QByteArray(); TagLib::ID3v2::AttachedPictureFrame* pic = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(apic_frames.front()); return QByteArray((const char*)pic->picture().data(), pic->picture().size()); } // Ogg vorbis/speex TagLib::Ogg::XiphComment* xiph_comment = dynamic_cast<TagLib::Ogg::XiphComment*>(ref.file()->tag()); if (xiph_comment) { TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap(); // Other than the below mentioned non-standard COVERART, // METADATA_BLOCK_PICTURE // is the proposed tag for cover pictures. // (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE) if (map.contains("METADATA_BLOCK_PICTURE")) { TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"]; for (std::list<TagLib::String>::iterator it = pict_list.begin(); it != pict_list.end(); ++it) { QByteArray data(QByteArray::fromBase64(it->toCString())); TagLib::ByteVector tdata(data.data(), data.size()); TagLib::FLAC::Picture p(tdata); if (p.type() == TagLib::FLAC::Picture::FrontCover) return QByteArray(p.data().data(), p.data().size()); } // If there was no specific front cover, just take the first picture QByteArray data(QByteArray::fromBase64( map["METADATA_BLOCK_PICTURE"].front().toCString())); TagLib::ByteVector tdata(data.data(), data.size()); TagLib::FLAC::Picture p(tdata); return QByteArray(p.data().data(), p.data().size()); } // Ogg lacks a definitive standard for embedding cover art, but it seems // b64 encoding a field called COVERART is the general convention if (!map.contains("COVERART")) return QByteArray(); return QByteArray::fromBase64(map["COVERART"].toString().toCString()); } #ifdef TAGLIB_HAS_FLAC_PICTURELIST // Flac TagLib::FLAC::File* flac_file = dynamic_cast<TagLib::FLAC::File*>(ref.file()); if (flac_file && flac_file->xiphComment()) { TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList(); if (!pics.isEmpty()) { // Use the first picture in the file - this could be made cleverer and // pick the front cover if it's present. std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin(); TagLib::FLAC::Picture* picture = *it; return QByteArray(picture->data().data(), picture->data().size()); } } #endif // MP4/AAC TagLib::MP4::File* aac_file = dynamic_cast<TagLib::MP4::File*>(ref.file()); if (aac_file) { TagLib::MP4::Tag* tag = aac_file->tag(); const TagLib::MP4::ItemListMap& items = tag->itemListMap(); TagLib::MP4::ItemListMap::ConstIterator it = items.find("covr"); if (it != items.end()) { const TagLib::MP4::CoverArtList& art_list = it->second.toCoverArtList(); if (!art_list.isEmpty()) { // Just take the first one for now const TagLib::MP4::CoverArt& art = art_list.front(); return QByteArray(art.data().data(), art.data().size()); } } } return QByteArray(); }
void cMediaInfo::readTagV2(ID3v2::Tag* lpTag) { m_iID3v2Version = lpTag->header()->majorVersion(); m_iID3v2Revision = lpTag->header()->revisionNumber(); m_iID3v2Size = lpTag->header()->tagSize(); for(ID3v2::FrameList::ConstIterator it = lpTag->frameList().begin();it != lpTag->frameList().end();it++) { QString szID = QString("%1").arg((*it)->frameID().data()).left(4); if(!szID.compare("TIT1")) m_szContentGroupDescription = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TIT2")) m_szTitle = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TIT3")) m_szSubTitle = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TALB")) m_szAlbum = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TOAL")) m_szOriginalAlbum = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TRCK")) m_szTrackNumber = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TPOS")) m_szPartOfSet = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TSST")) m_szSubTitleOfSet = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TSRC")) m_szInternationalStandardRecordingCode = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TPE1")) m_szLeadArtist = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TPE2")) m_szBand = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TPE3")) m_szConductor = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TPE4")) m_szInterpret = QString::fromStdWString((*it)->toString().toWString()).split("\r\n"); else if(!szID.compare("TOPE")) m_szOriginalArtist = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TEXT")) m_szTextWriter = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TOLY")) m_szOriginalTextWriter = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TCOM")) m_szComposer = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TENC")) m_szEncodedBy = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TBPM")) m_iBeatsPerMinute = QString::fromStdWString((*it)->toString().toWString()).toInt(); else if(!szID.compare("TLEN") && m_iLength == 0) m_iLength = QString::fromStdWString((*it)->toString().toWString()).toInt(); else if(!szID.compare("TLAN")) m_szLanguage = QString::fromStdWString((*it)->toString().toWString()).split("\r\n"); else if(!szID.compare("TCON")) m_szContentType = QString::fromStdWString((*it)->toString().toWString()).split("\r\n"); else if(!szID.compare("TFLT")) m_szFileType = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TMED")) m_szMediaType = QString::fromStdWString((*it)->toString().toWString()).split("\r\n"); else if(!szID.compare("TMOO")) m_szMood = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TCOP")) m_szCopyright = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TPRO")) m_szProducedNotice = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TPUB")) m_szPublisher = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TOWN")) m_szFileOwner = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TRSN")) m_szInternetRadioStationName = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TRSO")) m_szInternetRadioStationOwner = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TOFN")) m_szOriginalFilename = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TDLY")) m_iPlaylistDelay = QString::fromStdWString((*it)->toString().toWString()).toInt(); else if(!szID.compare("TDEN")) m_encodingTime = str2TS(QString::fromStdWString((*it)->toString().toWString())); else if(!szID.compare("TDOR")) m_originalReleaseTime = str2TS(QString::fromStdWString((*it)->toString().toWString())); else if(!szID.compare("TDRC")) m_recordingTime = str2TS(QString::fromStdWString((*it)->toString().toWString())); else if(!szID.compare("TDRL")) m_releaseTime = str2TS(QString::fromStdWString((*it)->toString().toWString())); else if(!szID.compare("TDTG")) m_taggingTime = str2TS(QString::fromStdWString((*it)->toString().toWString())); else if(!szID.compare("TSSE")) m_szswhwSettings = QString(QString::fromStdWString((*it)->toString().toWString())).split("\r\n"); else if(!szID.compare("TSOA")) m_szAlbumSortOrder = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TSOP")) m_szPerformerSortOrder = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("TSOT")) m_szTitleSortOrder = QString::fromStdWString((*it)->toString().toWString()); else if(!szID.compare("SYLT")) { TagLib::ID3v2::SynchronizedLyricsFrame* lpLyrics = static_cast<TagLib::ID3v2::SynchronizedLyricsFrame *> (*it); m_szSynchronizedLyricsLanguage = QString(lpLyrics->language().data()).left(3); String::Type type = lpLyrics->textEncoding(); m_szSynchronizedLyricsDescription = QString::fromStdWString(lpLyrics->description().toWString()); TagLib::ID3v2::SynchronizedLyricsFrame::SynchedTextList list = lpLyrics->synchedText(); for(ID3v2::SynchronizedLyricsFrame::SynchedTextList::ConstIterator it1 = list.begin(); it1 != list.end();it1++) { ID3v2::SynchronizedLyricsFrame::SynchedText t = *(it1); m_szSynchronizedLyrics.add(t.time, QString::fromStdWString(t.text.toWString())); } } else if(!szID.compare("USLT")) { TagLib::ID3v2::UnsynchronizedLyricsFrame* lpLyrics = static_cast<TagLib::ID3v2::UnsynchronizedLyricsFrame *> (*it); m_szUnsynchronizedLyricsLanguage = QString(lpLyrics->language().data()).left(3); String::Type type = lpLyrics->textEncoding(); m_szUnsynchronizedLyricsDescription = QString::fromStdWString(lpLyrics->description().toWString()); QString szText = QString::fromStdWString(lpLyrics->text().toWString()); if(szText.contains("\r\n")) m_szUnsynchronizedLyrics = szText.split("\r\n"); else if(szText.contains("\r")) m_szUnsynchronizedLyrics = szText.split("\r"); else if(szText.contains("\n")) m_szUnsynchronizedLyrics = szText.split("\n"); else m_szUnsynchronizedLyrics.append(szText); } else if(!szID.compare("APIC")) { TagLib::ID3v2::AttachedPictureFrame* lpPicture = static_cast<TagLib::ID3v2::AttachedPictureFrame *> (*it); TagLib::ID3v2::AttachedPictureFrame::Type t = lpPicture->type(); QString szDescription; szDescription = QString::fromStdWString(lpPicture->description().toWString()); QByteArray pictureData = QByteArray(lpPicture->picture().data(), lpPicture->picture().size()); m_pixmapList.add(pictureData, m_szFileName, (cPixmap::ImageType)t, szDescription); } } }