QString FileHelper::extractFlacFeature(const QString &featureToExtract) const { QString feature; if (TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file)) { if (flacFile->ID3v2Tag()) { QString key = this->convertKeyToID3v2Key(featureToExtract); TagLib::ID3v2::FrameList l = flacFile->ID3v2Tag()->frameListMap()[key.toStdString().data()]; // Fallback to the generic map in case we didn't find the matching key if (l.isEmpty()) { TagLib::StringList list = flacFile->properties()[featureToExtract.toStdString()]; if (!list.isEmpty()) { feature = list.front().toCString(true); } } else { feature = QString(l.front()->toString().toCString(true)); } } else if (flacFile->ID3v1Tag()) { qDebug() << Q_FUNC_INFO << "Not yet implemented for ID3v1Tag FLAC file"; } else if (flacFile->xiphComment()) { const TagLib::Ogg::FieldListMap map = flacFile->xiphComment()->fieldListMap(); if (!map[featureToExtract.toStdString().data()].isEmpty()) { feature = QString(map[featureToExtract.toStdString().data()].front().toCString(true)); } } } return feature; }
/** Convert the existing rating number into a smaller range from 1 to 5. */ int FileHelper::rating() const { int r = -1; /// TODO other types? switch (_fileType) { case EXT_MP3: { TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file); if (mpegFile && mpegFile->hasID3v2Tag()) { r = this->ratingForID3v2(mpegFile->ID3v2Tag()); } break; } case EXT_FLAC: { if (TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file)) { if (flacFile->hasID3v2Tag()) { r = this->ratingForID3v2(flacFile->ID3v2Tag()); } else if (flacFile->hasID3v1Tag()) { qDebug() << Q_FUNC_INFO << "Not implemented (FLAC ID3v1)"; } else if (flacFile->hasXiphComment()) { TagLib::StringList list = flacFile->xiphComment()->fieldListMap()["RATING"]; if (!list.isEmpty()) { r = list.front().toInt(); } } } break; } default: break; } return r; }
int meta_addTag(char* filename, TagLib::String tag) { TagLib::StringList tags; if (meta_getTags(filename, &tags) != EXIT_SUCCESS) return EXIT_FAILURE; tags.append(optarg); return meta_setTags(filename, tags); }
int MetaDSF::setTag(const TagLib::String &key, const TagLib::String &val, bool replace) { TagLib::StringList vals; vals.append(val); return setTag(key, vals, replace); }
int meta_delTag(char* filename, TagLib::String tag) { TagLib::StringList tags; if (meta_getTags(filename, &tags) != EXIT_SUCCESS) return EXIT_FAILURE; TagLib::StringList::Iterator it = tags.find(tag); if (it != tags.end()) tags.erase(it); return meta_setTags(filename, tags); }
QString FileHelper::extractGenericFeature(const QString &featureToExtract) const { QString feature; TagLib::PropertyMap p = _file->properties(); if (p.contains(featureToExtract.toStdString().data())) { TagLib::StringList list = p[featureToExtract.toStdString().data()]; if (!list.isEmpty()) { feature = list.front().toCString(true); } } return feature; }
QStringList MetaBundle::genreList() //static { QStringList list; TagLib::StringList genres = TagLib::ID3v1::genreList(); for( TagLib::StringList::ConstIterator it = genres.begin(), end = genres.end(); it != end; ++it ) list += TStringToQString( (*it) ); list.sort(); return list; }
void FileHelper::setArtistAlbum(const QString &artistAlbum) { switch (_fileType) { case EXT_FLAC: { this->setFlacAttribute("ALBUMARTIST", artistAlbum); break; } case EXT_MP4:{ TagLib::StringList l; l.append(artistAlbum.toStdString().data()); TagLib::MP4::Item item(l); this->setMp4Attribute("aART", item); break; } case EXT_MPC: //mpcFile = static_cast<MPC::File*>(f); qDebug() << Q_FUNC_INFO << "Not implemented for MPC"; break; case EXT_MP3:{ TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file); if (mpegFile->hasID3v2Tag()) { TagLib::ID3v2::Tag *tag = mpegFile->ID3v2Tag(); QString convertedKey = this->convertKeyToID3v2Key("ARTISTALBUM"); TagLib::ID3v2::FrameList l = tag->frameListMap()[convertedKey.toStdString().data()]; if (!l.isEmpty()) { tag->removeFrame(l.front()); } TagLib::ID3v2::TextIdentificationFrame *tif = new TagLib::ID3v2::TextIdentificationFrame(TagLib::ByteVector(convertedKey.toStdString().data())); tif->setText(artistAlbum.toStdString().data()); tag->addFrame(tif); } else if (mpegFile->hasID3v1Tag()) { qDebug() << Q_FUNC_INFO << "Not implemented for ID3v1Tag"; } break; } case EXT_OGG: { TagLib::Ogg::XiphComment *xiphComment = static_cast<TagLib::Ogg::XiphComment*>(_file->tag()); if (xiphComment) { xiphComment->addField("ALBUMARTIST", artistAlbum.toStdString().data()); } else { qDebug() << Q_FUNC_INFO << "Not implemented for this OGG file"; } break; } default: qDebug() << Q_FUNC_INFO << "Not implemented for this type of file"; break; } }
QPixmap VorbisMetaDataModel::cover() { TagLib::Ogg::Vorbis::File file(m_path.toLocal8Bit().constData()); TagLib::Ogg::XiphComment *tag = file.tag(); if(!tag) return QPixmap(); TagLib::StringList list = tag->fieldListMap()["METADATA_BLOCK_PICTURE"]; if(list.isEmpty()) return QPixmap(); for(uint i = 0; i < list.size(); ++i) { TagLib::String value = list[i]; QByteArray block = QByteArray::fromBase64(TStringToQString_qt4(value).toAscii()); if(block.size() < 32) continue; qint64 pos = 0; if(readPictureBlockField(block, pos) != 3) //picture type, use front cover only continue; pos += 4; int mimeLength = readPictureBlockField(block, pos); //mime type length pos += 4; pos += mimeLength; //skip mime type int descLength = readPictureBlockField(block, pos); //description length pos += 4; pos += descLength; //skip description pos += 4; //width pos += 4; //height pos += 4; //color depth pos += 4; //the number of colors used int length = readPictureBlockField(block, pos); //picture size pos += 4; QPixmap cover; cover.loadFromData(block.mid(pos, length)); //read binary picture data return cover; } return QPixmap(); }
QString FileHelper::extractMp4Feature(const QString &featureToExtract) const { QString feature; if (TagLib::MP4::File *mp4File = static_cast<TagLib::MP4::File*>(_file)) { if (mp4File->tag()) { TagLib::MP4::ItemListMap &items = mp4File->tag()->itemListMap(); if (items.contains(featureToExtract.toStdString().data())) { TagLib::MP4::Item item = items[featureToExtract.toStdString().data()]; TagLib::StringList list = item.toStringList(); if (list.size() > 0) { feature = list[0].toCString(true); } /*for (uint i = 0; i < list.size(); i++) { TagLib::String s = list[i]; qDebug() << Q_FUNC_INFO << s.toCString(true); }*/ } /*for (auto it = items.begin(); it != items.end(); ++it) { qDebug() << Q_FUNC_INFO << QString(it->first.toCString(false)); }*/ } } return feature; }
// Utility function to format tags so that they can be correctly parsed back string formatString(const TagLib::StringList& strList) { TagLib::String str = strList.toString(";"); if (str.isEmpty()) return ""; string result = str.to8Bit(true); // heuristic to detect wrongly encoded tags (ie: twice latin-1 to utf-8, mostly) // we should encode everything ourselves to utf-8, but sometimes it might happen // that someone already did that, but told us the string was in latin-1. // A way to detect that is if the string contains only latin-1 chars, when // converting it to latin-1 it contains code chars, this probably means it was // previously encoded in utf-8 if (isLatin1(str) && containsControlChars(str.to8Bit(false))) { result = str.to8Bit(false); } // fix invalid utf-8 characters result = fixInvalidUTF8(result); return result; }
/*! * \copydoc MetaIO::read() */ MusicMetadata *MetaIOID3::read(const QString &filename) { if (!OpenFile(filename)) return nullptr; TagLib::ID3v2::Tag *tag = GetID3v2Tag(true); // Create tag if none are found // if there is no ID3v2 tag, try to read the ID3v1 tag and copy it to // the ID3v2 tag structure if (tag->isEmpty()) { TagLib::ID3v1::Tag *tag_v1 = GetID3v1Tag(); if (!tag_v1) return nullptr; if (!tag_v1->isEmpty()) { tag->setTitle(tag_v1->title()); tag->setArtist(tag_v1->artist()); tag->setAlbum(tag_v1->album()); tag->setTrack(tag_v1->track()); tag->setYear(tag_v1->year()); tag->setGenre(tag_v1->genre()); } } MusicMetadata *metadata = new MusicMetadata(filename); ReadGenericMetadata(tag, metadata); bool compilation = false; // Compilation Artist (TPE4 Remix) or fallback to (TPE2 Band) // N.B. The existance of a either frame is NOT an indication that this // is a compilation, but if it is then one of them will probably hold // the compilation artist. TextIdentificationFrame *tpeframe = nullptr; TagLib::ID3v2::FrameList tpelist = tag->frameListMap()["TPE4"]; if (tpelist.isEmpty() || tpelist.front()->toString().isEmpty()) tpelist = tag->frameListMap()["TPE2"]; if (!tpelist.isEmpty()) tpeframe = (TextIdentificationFrame *)tpelist.front(); if (tpeframe && !tpeframe->toString().isEmpty()) { QString compilation_artist = TStringToQString(tpeframe->toString()) .trimmed(); metadata->setCompilationArtist(compilation_artist); } // Rating and playcount, stored in POPM frame PopularimeterFrame *popm = findPOPM(tag, ""); // Global (all apps) tag // If no 'global' tag exists, look for the MythTV specific one if (!popm) { popm = findPOPM(tag, email); } // Fallback to using any POPM tag we can find if (!popm) { if (!tag->frameListMap()["POPM"].isEmpty()) popm = dynamic_cast<PopularimeterFrame *> (tag->frameListMap()["POPM"].front()); } if (popm) { int rating = popm->rating(); rating = lroundf(static_cast<float>(rating) / 255.0f * 10.0f); metadata->setRating(rating); metadata->setPlaycount(popm->counter()); } // Look for MusicBrainz Album+Artist ID in TXXX Frame UserTextIdentificationFrame *musicbrainz = find(tag, "MusicBrainz Album Artist Id"); if (musicbrainz) { // If the MusicBrainz ID is the special "Various Artists" ID // then compilation is TRUE if (!compilation && !musicbrainz->fieldList().isEmpty()) { TagLib::StringList l = musicbrainz->fieldList(); for (TagLib::StringList::ConstIterator it = l.begin(); it != l.end(); it++) { QString ID = TStringToQString((*it)); if (ID == MYTH_MUSICBRAINZ_ALBUMARTIST_UUID) { compilation = true; break; } } } } // TLEN - Ignored intentionally, some encoders write bad values // e.g. Lame under certain circumstances will always write a length of // 27 hours // Length if (!tag->frameListMap()["TLEN"].isEmpty()) { int length = tag->frameListMap()["TLEN"].front()->toString().toInt(); LOG(VB_FILE, LOG_DEBUG, QString("MetaIOID3::read: Length for '%1' from tag is '%2'\n").arg(filename).arg(length)); } metadata->setCompilation(compilation); metadata->setLength(getTrackLength(m_file)); // The number of tracks on the album, if supplied if (!tag->frameListMap()["TRCK"].isEmpty()) { QString trackFrame = TStringToQString( tag->frameListMap()["TRCK"].front()->toString()) .trimmed(); int trackCount = trackFrame.section('/', -1).toInt(); if (trackCount > 0) metadata->setTrackCount(trackCount); } LOG(VB_FILE, LOG_DEBUG, QString("MetaIOID3::read: Length for '%1' from properties is '%2'\n").arg(filename).arg(metadata->Length())); // Look for MythTVLastPlayed in TXXX Frame UserTextIdentificationFrame *lastplayed = find(tag, "MythTVLastPlayed"); if (lastplayed) { QString lastPlayStr = TStringToQString(lastplayed->toString()); metadata->setLastPlay(QDateTime::fromString(lastPlayStr, Qt::ISODate)); } // Part of a set if (!tag->frameListMap()["TPOS"].isEmpty()) { QString pos = TStringToQString( tag->frameListMap()["TPOS"].front()->toString()).trimmed(); int discNumber = pos.section('/', 0, 0).toInt(); int discCount = pos.section('/', -1).toInt(); if (discNumber > 0) metadata->setDiscNumber(discNumber); if (discCount > 0) metadata->setDiscCount(discCount); } return metadata; }
bool SoundSource::processXiphComment(TagLib::Ogg::XiphComment* xiph) { if (s_bDebugMetadata) { for (TagLib::Ogg::FieldListMap::ConstIterator it = xiph->fieldListMap().begin(); it != xiph->fieldListMap().end(); ++it) { qDebug() << "XIPH" << TStringToQString((*it).first) << "-" << TStringToQString((*it).second.toString()); } } // Some tags use "BPM" so check for that. if (xiph->fieldListMap().contains("BPM")) { TagLib::StringList bpmString = xiph->fieldListMap()["BPM"]; QString sBpm = TStringToQString(bpmString.toString()); processBpmString("XIPH-BPM", sBpm); } // Give preference to the "TEMPO" tag which seems to be more standard if (xiph->fieldListMap().contains("TEMPO")) { TagLib::StringList bpmString = xiph->fieldListMap()["TEMPO"]; QString sBpm = TStringToQString(bpmString.toString()); processBpmString("XIPH-TEMPO", sBpm); } if (xiph->fieldListMap().contains("REPLAYGAIN_ALBUM_GAIN")) { TagLib::StringList rgainString = xiph->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"]; QString sReplayGain = TStringToQString(rgainString.toString()); parseReplayGainString(sReplayGain); } if (xiph->fieldListMap().contains("REPLAYGAIN_TRACK_GAIN")) { TagLib::StringList rgainString = xiph->fieldListMap()["REPLAYGAIN_TRACK_GAIN"]; QString sReplayGain = TStringToQString(rgainString.toString()); parseReplayGainString(sReplayGain); } /* * Reading key code information * Unlike, ID3 tags, there's no standard or recommendation on how to store 'key' code * * Luckily, there are only a few tools for that, e.g., Rapid Evolution (RE). * Assuming no distinction between start and end key, RE uses a "INITIALKEY" * or a "KEY" vorbis comment. */ if (xiph->fieldListMap().contains("KEY")) { TagLib::StringList keyStr = xiph->fieldListMap()["KEY"]; QString key = TStringToQString(keyStr.toString()); setKey(key); } if (getKey() == "" && xiph->fieldListMap().contains("INITIALKEY")) { TagLib::StringList keyStr = xiph->fieldListMap()["INITIALKEY"]; QString key = TStringToQString(keyStr.toString()); setKey(key); } return true; }
BlockResult Block::eval(const TagLib::FileRef& file) { TagLib::PropertyMap metadata = file.tag()->properties(); TagLib::StringList list; std::stringstream ss; // TODO: Needs to be refactored // TODO: Add more ss << file.audioProperties()->length(); list.append(ss.str()); metadata.insert("length", list); list.clear(); ss.clear(); ss << file.audioProperties()->bitrate(); list.append(ss.str()); metadata.insert("bitrate", list); list.clear(); ss.clear(); if (file.audioProperties()->channels() == 1) { list.append("mono"); } else if (file.audioProperties()->channels() == 2) { list.append("stereo"); } //TODO: Add more channels names metadata.insert("channels", list); list.clear(); ss.clear(); ss << file.audioProperties()->sampleRate(); list.append(ss.str()); metadata.insert("samplerate", list); list.clear(); ss.clear(); list.append(file.file()->name()); metadata.insert("filename", list); list.clear(); ss.clear(); return this->eval(metadata); }
void TagLibExtractor::extract(ExtractionResult* result) { const QString fileUrl = result->inputUrl(); const QString mimeType = result->inputMimetype(); TagLib::FileRef file(fileUrl.toUtf8().constData(), true); if (file.isNull()) { return; } TagLib::Tag* tags = file.tag(); result->addType(Type::Audio); TagLib::String artists; TagLib::String albumArtists; TagLib::String composers; TagLib::String lyricists; TagLib::StringList genres; // Handling multiple tags in mpeg files. if ((mimeType == QLatin1String("audio/mpeg")) || (mimeType == QLatin1String("audio/mpeg3")) || (mimeType == QLatin1String("audio/x-mpeg"))) { TagLib::MPEG::File mpegFile(fileUrl.toUtf8().constData(), true); if (mpegFile.ID3v2Tag() && !mpegFile.ID3v2Tag()->isEmpty()) { TagLib::ID3v2::FrameList lstID3v2; // Artist. lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["TPE1"]; if (!lstID3v2.isEmpty()) { for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { if (!artists.isEmpty()) { artists += ", "; } artists += (*it)->toString(); } } // Album Artist. lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["TPE2"]; if (!lstID3v2.isEmpty()) { for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { if (!albumArtists.isEmpty()) { albumArtists += ", "; } albumArtists += (*it)->toString(); } } // Composer. lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["TCOM"]; if (!lstID3v2.isEmpty()) { for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { if (!composers.isEmpty()) { composers += ", "; } composers += (*it)->toString(); } } // Lyricist. lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["TEXT"]; if (!lstID3v2.isEmpty()) { for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { if (!lyricists.isEmpty()) { lyricists += ", "; } lyricists += (*it)->toString(); } } // Genre. lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["TCON"]; if (!lstID3v2.isEmpty()) { for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { genres.append((*it)->toString()); } } } } // Handling multiple tags in Ogg containers. { TagLib::Ogg::FieldListMap lstOgg; // FLAC files. if (mimeType == QLatin1String("audio/flac")) { TagLib::FLAC::File flacFile(fileUrl.toUtf8().constData(), true); if (flacFile.xiphComment() && !flacFile.xiphComment()->isEmpty()) { lstOgg = flacFile.xiphComment()->fieldListMap(); } } // Vorbis files. if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { TagLib::Ogg::Vorbis::File oggFile(fileUrl.toUtf8().constData(), true); if (oggFile.tag() && !oggFile.tag()->isEmpty()) { lstOgg = oggFile.tag()->fieldListMap(); } } // Opus files. if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { TagLib::Ogg::Opus::File opusFile(fileUrl.toUtf8().constData(), true); if (opusFile.tag() && !opusFile.tag()->isEmpty()) { lstOgg = opusFile.tag()->fieldListMap(); } } // Handling OGG container tags. if (!lstOgg.isEmpty()) { TagLib::Ogg::FieldListMap::ConstIterator itOgg; // Artist. itOgg = lstOgg.find("ARTIST"); if (itOgg != lstOgg.end()) { if (!artists.isEmpty()) { artists += ", "; } artists += (*itOgg).second.toString(", "); } // Album Artist. itOgg = lstOgg.find("ALBUMARTIST"); if (itOgg != lstOgg.end()) { if (!albumArtists.isEmpty()) { albumArtists += ", "; } albumArtists += (*itOgg).second.toString(", "); } // Composer. itOgg = lstOgg.find("COMPOSER"); if (itOgg != lstOgg.end()) { if (!composers.isEmpty()) { composers += ", "; } composers += (*itOgg).second.toString(", "); } // Lyricist. itOgg = lstOgg.find("LYRICIST"); if (itOgg != lstOgg.end()) { if (!lyricists.isEmpty()) { lyricists += ", "; } lyricists += (*itOgg).second.toString(", "); } // Genre. itOgg = lstOgg.find("GENRE"); if (itOgg != lstOgg.end()) { genres.append((*itOgg).second); } } } // Handling multiple tags in Musepack files. if (mimeType == QLatin1String("audio/x-musepack")) { TagLib::MPC::File mpcFile(fileUrl.toUtf8().constData(), true); if (mpcFile.tag() && !mpcFile.tag()->isEmpty()) { TagLib::APE::ItemListMap lstMusepack = mpcFile.APETag()->itemListMap(); TagLib::APE::ItemListMap::ConstIterator itMPC; // Artist. itMPC = lstMusepack.find("ARTIST"); if (itMPC != lstMusepack.end()) { if (!artists.isEmpty()) { artists += ", "; } artists += (*itMPC).second.toString(); } // Album Artist. itMPC = lstMusepack.find("ALBUMARTIST"); if (itMPC != lstMusepack.end()) { if(!albumArtists.isEmpty()) { albumArtists += ", "; } albumArtists += (*itMPC).second.toString(); } // Composer. itMPC = lstMusepack.find("COMPOSER"); if (itMPC != lstMusepack.end()) { if (!composers.isEmpty()) { composers += ", "; } composers += (*itMPC).second.toString(); } // Lyricist. itMPC = lstMusepack.find("LYRICIST"); if (itMPC != lstMusepack.end()) { if (!lyricists.isEmpty()) { lyricists += ", "; } lyricists += (*itMPC).second.toString(); } // Genre. itMPC = lstMusepack.find("GENRE"); if (itMPC != lstMusepack.end()) { genres.append((*itMPC).second.toString()); } } } if (!tags->isEmpty()) { QString title = t2q(tags->title()); if (!title.isEmpty()) { result->add(Property::Title, title); } QString comment = t2q(tags->comment()); if (!comment.isEmpty()) { result->add(Property::Comment, comment); } if (genres.isEmpty()) { genres.append(tags->genre()); } for (uint i = 0; i < genres.size(); i++) { QString genre = t2q(genres[i]).trimmed(); // Convert from int bool ok = false; int genreNum = genre.toInt(&ok); if (ok) { genre = t2q(TagLib::ID3v1::genre(genreNum)); } result->add(Property::Genre, genre); } QString artistString; if (artists.isEmpty()) { artistString = t2q(tags->artist()); } else { artistString = t2q(artists).trimmed(); } QStringList artists = contactsFromString(artistString); foreach(const QString& artist, artists) { result->add(Property::Artist, artist); }
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; }