bool APETagHelper::setTags( const Meta::FieldHash &changes ) { bool modified = TagHelper::setTags( changes ); foreach( const qint64 key, changes.keys() ) { QVariant value = changes.value( key ); TagLib::String field = fieldName( key ); if( !field.isNull() && !field.isEmpty() ) { if( key == Meta::valRating ) m_tag->addValue( field, Qt4QStringToTString( QString::number( value.toFloat() / 10.0 ) ) ); else if( key == Meta::valScore ) m_tag->addValue( field, Qt4QStringToTString( QString::number( value.toFloat() / 100.0 ) ) ); else m_tag->addValue( field, Qt4QStringToTString( value.toString() ) ); modified = true; } else if( key == Meta::valUniqueId ) { QPair < UIDType, QString > uidPair = splitUID( value.toString() ); if( uidPair.first == UIDInvalid ) continue; m_tag->addValue( uidFieldName( uidPair.first ), Qt4QStringToTString( uidPair.second ) ); modified = true; } } return modified; }
TrackList ClementineProvider::artistTracks( const QString &artistName ) { const QString query = "SELECT filename, title, artist, album, composer, year, track, " "disc, rating, lastplayed, playcount FROM songs WHERE artist = :artist"; QVariantMap bindValues; bindValues.insert( ":artist", artistName ); const QList<qint64> fields = QList<qint64>() << Meta::valTitle << Meta::valArtist << Meta::valAlbum << Meta::valComposer << Meta::valYear << Meta::valTrackNr << Meta::valDiscNr << Meta::valRating << Meta::valLastPlayed << Meta::valPlaycount; TrackList result; foreach( const QVariantList &row, m_connection->query( query, bindValues ) ) { const QVariant &filename = row[0]; Meta::FieldHash metadata; for( int i = 0; i < fields.size(); ++i ) metadata.insert( fields[i], row[i + 1] ); result << TrackPtr( new ClementineTrack( filename, m_connection, metadata ) ); } return result; }
TrackList BansheeProvider::artistTracks( const QString &artistName ) { // Due to Banshee's peculiar track info storage, to avoid massive amount of confusion // we only take tracks from PrimarySource: MusicLibrarySource-Library (always ID 1) const QString query = "SELECT TrackID, TRIM(t.Title), ar.Name, al.Title, " "TRIM(t.Composer), t.Year, t.TrackNumber, t.Disc, t.Rating, " "t.LastPlayedStamp, t.PlayCount " "FROM coretracks t " "INNER JOIN coreartists ar USING(ArtistID) " "LEFT JOIN corealbums al USING(AlbumID) " "WHERE ar.Name = :artist AND t.PrimarySourceID = 1"; QVariantMap bindValues; bindValues.insert( ":artist", artistName ); const QList<qint64> fields = QList<qint64>() << Meta::valTitle << Meta::valArtist << Meta::valAlbum << Meta::valComposer << Meta::valYear << Meta::valTrackNr << Meta::valDiscNr << Meta::valRating << Meta::valLastPlayed << Meta::valPlaycount; TrackList result; foreach( const QVariantList &row, m_connection->query( query, bindValues ) ) { const qint64 trackId = row[0].toLongLong(); Meta::FieldHash metadata; for( int i = 0; i < fields.size(); ++i ) metadata.insert( fields[i], row[i + 1] ); result << TrackPtr( new BansheeTrack( trackId, m_connection, metadata ) ); } return result; }
Meta::FieldHash MP4TagHelper::tags() const { Meta::FieldHash data = TagHelper::tags(); TagLib::MP4::ItemListMap map = m_tag->itemListMap(); for( TagLib::MP4::ItemListMap::ConstIterator it = map.begin(); it != map.end(); ++it ) { qint64 field; QString value = TStringToQString( it->second.toStringList().toString( '\n' ) ); if( ( field = fieldName( it->first ) ) ) { if( field == Meta::valHasCover ) { TagLib::MP4::CoverArtList coverList = it->second.toCoverArtList(); for( TagLib::MP4::CoverArtList::ConstIterator it = coverList.begin(); it != coverList.end(); ++it ) if( it->data().size() > MIN_COVER_SIZE ) { data.insert( field, true ); break; } } // http://gitorious.org/~jefferai/xdg-specs/jefferais-xdg-specs/blobs/mediaspecs/specifications/FMPSpecs/specification.txt sais that mp4 tags should be saved as strings else if( field == Meta::valPlaycount ) data.insert( field, value.toInt() ); else if( field == Meta::valRating ) data.insert( field, qRound( value.toFloat() * 10.0 ) ); else if( field == Meta::valScore ) data.insert( field, value.toFloat() * 100.0 ); else if( field == Meta::valBpm ) data.insert( field, it->second.toInt() ); else if( field == Meta::valDiscNr ) data.insert( field, it->second.toIntPair().first ); else if( field == Meta::valCompilation ) data.insert( field, it->second.toBool() ); else data.insert( field, value ); } else if( it->first == uidFieldName( UIDAFT ) && isValidUID( value, UIDAFT ) ) data.insert( Meta::valUniqueId, value ); } return data; }
Meta::FieldHash APETagHelper::tags() const { Meta::FieldHash data = TagHelper::tags(); TagLib::APE::ItemListMap map = m_tag->itemListMap(); for( TagLib::APE::ItemListMap::ConstIterator it = map.begin(); it != map.end(); ++it ) { qint64 field; QString value = TStringToQString( it->second.toString() ); if( ( field = fieldName( it->first ) ) ) { if( field == Meta::valRating ) data.insert( field, qRound( value.toFloat() * 10.0 ) ); else if( field == Meta::valScore ) data.insert( field, value.toFloat() * 100.0 ); else data.insert( field, value ); } else if( it->first == uidFieldName( UIDAFT ) && isValidUID( value, UIDAFT ) ) data.insert( Meta::valUniqueId, value ); else if( it->first == uidFieldName( UIDMusicBrainz ) && isValidUID( value, UIDMusicBrainz ) ) { if( !data.contains( Meta::valUniqueId ) ) // we prefere AFT uids data.insert( Meta::valUniqueId, value.prepend( "mb-" ) ); } } return data; }
bool ASFTagHelper::setTags( const Meta::FieldHash &changes ) { bool modified = TagHelper::setTags( changes ); foreach( const qint64 key, changes.keys() ) { QVariant value = changes.value( key ); TagLib::String field = fieldName( key ); if( !field.isNull() && !field.isEmpty() ) { if( key == Meta::valHasCover ) continue; // http://gitorious.org/~jefferai/xdg-specs/jefferais-xdg-specs/blobs/mediaspecs/specifications/FMPSpecs/specification.txt sais that mp4 tags should be saved as strings if( key == Meta::valHasCover ) continue; else if( key == Meta::valRating ) m_tag->setAttribute( field, TagLib::ASF::Attribute( Qt4QStringToTString( QString::number( value.toFloat() / 10.0 ) ) ) ); else if( key == Meta::valScore ) m_tag->setAttribute( field, TagLib::ASF::Attribute( Qt4QStringToTString( QString::number( value.toFloat() / 100.0 ) ) ) ); else if( key == Meta::valBpm || key == Meta::valDiscNr ) m_tag->setAttribute( field, TagLib::ASF::Attribute( value.toUInt() ) ); else if( key == Meta::valCompilation ) m_tag->setAttribute( field, TagLib::ASF::Attribute( value.toBool() ) ); else m_tag->setAttribute( field, TagLib::ASF::Attribute( Qt4QStringToTString( value.toString() ) ) ); modified = true; } else if( key == Meta::valUniqueId ) { QPair < UIDType, QString > uidPair = splitUID( value.toString() ); if( uidPair.first == UIDInvalid ) continue; m_tag->setAttribute( uidFieldName( uidPair.first ), TagLib::ASF::Attribute( Qt4QStringToTString( uidPair.second ) ) ); modified = true; } } return modified; }
Meta::FieldHash TagHelper::tags() const { Meta::FieldHash data; data.insert( Meta::valTitle, TStringToQString( m_tag->title() ) ); data.insert( Meta::valArtist, TStringToQString( m_tag->artist() ) ); data.insert( Meta::valAlbum, TStringToQString( m_tag->album() ) ); data.insert( Meta::valTrackNr, m_tag->track() ); data.insert( Meta::valYear, m_tag->year() ); data.insert( Meta::valGenre, TStringToQString( m_tag->genre() ) ); data.insert( Meta::valComment, TStringToQString( m_tag->comment() ) ); return data; }
void IpodCopyTracksJob::run() { if( !m_coll ) return; // destructed behind our back float totalSafeCapacity = m_coll.data()->totalCapacity() - m_coll.data()->capacityMargin(); QByteArray mountPoint = QFile::encodeName( m_coll.data()->mountPoint() ); QString collectionPrettyName = m_coll.data()->prettyName(); int trackCount = m_sources.size(); QString operationText; if( m_transcodingConfig.isJustCopy() ) operationText = i18np( "Transferring one track to %2", "Transferring %1 tracks to %2", trackCount, collectionPrettyName ); else operationText = i18np( "Transcoding one track to %2", "Transcoding %1 tracks to %2", trackCount, collectionPrettyName ); Amarok::Components::logger()->newProgressOperation( this, operationText, trackCount, this, SLOT(abort()) ); itdb_start_sync( m_coll.data()->m_itdb ); QMapIterator<Meta::TrackPtr, KUrl> it( m_sources ); while( it.hasNext() ) { if( m_aborted || !m_coll ) break; it.next(); Meta::TrackPtr track = it.key(); KUrl sourceUrl = it.value(); emit startDuplicateTrackSearch( track ); // wait for searching to finish: m_searchingForDuplicates.acquire( 1 ); if( m_duplicateTrack ) { trackProcessed( Duplicate, track, m_duplicateTrack ); continue; } if( !m_coll ) break; // destructed behind our back if( m_transcodingConfig.isJustCopy() // if not copying, we catch big files later && track->filesize() > totalSafeCapacity - m_coll.data()->usedCapacity() ) { // this is a best effort check, we do one definite one after the file is copied debug() << "Refusing to copy" << track->prettyUrl() << "to iPod: there are only" << totalSafeCapacity - m_coll.data()->usedCapacity() << "free bytes (not" << "counting a safety margin) on iPod and track has" << track->filesize() << "bytes."; trackProcessed( ExceededingSafeCapacity, track ); continue; } QString fileExtension; if( m_transcodingConfig.isJustCopy() ) fileExtension = track->type(); else fileExtension = Amarok::Components::transcodingController()->format( m_transcodingConfig.encoder() )->fileExtension(); if( !m_coll.data()->supportedFormats().contains( fileExtension ) ) { m_notPlayableFormats.insert( fileExtension ); trackProcessed( NotPlayable, track ); continue; } QByteArray fakeSrcName( "filename." ); // only for file extension fakeSrcName.append( QFile::encodeName( fileExtension ) ); /* determine destination filename; we cannot use ipodTrack because as it has no itdb * (and thus mountpoint) set */ GError *error = 0; gchar *destFilename = itdb_cp_get_dest_filename( 0, mountPoint, fakeSrcName, &error ); if( error ) { warning() << "Cannot construct iPod track filename:" << error->message; g_error_free( error ); error = 0; } if( !destFilename ) { trackProcessed( InternalError, track ); continue; } // start the physical copying KUrl destUrl = KUrl( QFile::decodeName( destFilename ) ); emit startCopyOrTranscodeJob( sourceUrl, destUrl ); // wait for copying to finish: m_copying.acquire( 1 ); /* fsync so that progress bar gives correct info and user doesnt remove the iPod * prematurely */ QFile destFile( QFile::decodeName( destFilename ) ); if( !destFile.exists() ) { debug() << destFile.fileName() << "does not exist even though we thought we copied it to iPod"; trackProcessed( CopyingFailed, track ); continue; } if( !m_coll ) break; // destructed behind our back if( m_coll.data()->usedCapacity() > totalSafeCapacity ) { debug() << "We exceeded total safe-to-use capacity on iPod (safe-to-use:" << totalSafeCapacity << "B, used:" << m_coll.data()->usedCapacity() << "): removing copied track from iPod"; destFile.remove(); trackProcessed( ExceededingSafeCapacity, track ); continue; } // fsync needs a file opened for writing, and without Apped it truncates files (?) if( !destFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) { warning() << "Cannot open file copied to ipod (for writing):" << destFile.fileName() << ": removing it"; destFile.remove(); trackProcessed( InternalError, track ); continue; } if( destFile.size() ) fsync( destFile.handle() ); // should flush all kernel buffers to disk destFile.close(); // create a new track object by copying meta-data from existing one: IpodMeta::Track *ipodTrack = new IpodMeta::Track( track ); // tell the track it has been copied: bool accepted = ipodTrack->finalizeCopying( mountPoint, destFilename ); g_free( destFilename ); destFilename = 0; if( !accepted ) { debug() << "ipodTrack->finalizeCopying( destFilename ) returned false!"; delete ipodTrack; trackProcessed( InternalError, track ); continue; } if( !m_transcodingConfig.isJustCopy() ) { // we need to reread some metadata in case the file was transcoded Meta::FieldHash fields = Meta::Tag::readTags( destFile.fileName() ); ipodTrack->setBitrate( fields.value( Meta::valBitrate, 0 ).toInt() ); ipodTrack->setLength( fields.value( Meta::valLength, 0 ).toLongLong() ); ipodTrack->setSampleRate( fields.value( Meta::valSamplerate, 0 ).toInt() ); Amarok::FileType type = Amarok::FileType( fields.value( Meta::valFormat, 0 ).toInt() ); ipodTrack->setType( Amarok::FileTypeSupport::toString( type ) ); // we retain ReplayGain, tags etc - these shouldn't change; size is read // in finalizeCopying() } // add the track to collection if( !m_coll ) { delete ipodTrack; break; // we were waiting for copying, m_coll may got destoryed } Meta::TrackPtr newTrack = m_coll.data()->addTrack( ipodTrack ); if( !newTrack ) { destFile.remove(); trackProcessed( InternalError, track ); continue; } trackProcessed( Success, track, newTrack ); } if( m_coll ) itdb_stop_sync( m_coll.data()->m_itdb ); emit endProgressOperation( this ); int sourceSize = m_sources.size(); int successCount = m_sourceTrackStatus.count( Success ); int duplicateCount = m_sourceTrackStatus.count( Duplicate ); QString transferredText; if ( m_transcodingConfig.isJustCopy() ) transferredText = i18ncp( "%2 is collection name", "Transferred one track to %2.", "Transferred %1 tracks to %2.", successCount, collectionPrettyName ); else transferredText = i18ncp( "%2 is collection name", "Transcoded one track to %2.", "Transcoded %1 tracks to %2.", successCount, collectionPrettyName ); if( successCount == sourceSize ) { Amarok::Components::logger()->shortMessage( transferredText ); } else if( m_aborted ) { QString text = i18np( "Transfer aborted. Managed to transfer one track.", "Transfer aborted. Managed to transfer %1 tracks.", successCount ); Amarok::Components::logger()->longMessage( text ); } else if( successCount + duplicateCount == sourceSize ) { QString text = i18ncp( "%2 is the 'Transferred 123 tracks to Some collection.' message", "%2 One track was already there.", "%2 %1 tracks were already there.", duplicateCount, transferredText ); Amarok::Components::logger()->longMessage( text ); } else { // somethig more severe failed, notify user using a dialog emit displaySorryDialog(); } }
bool TagHelper::setTags( const Meta::FieldHash &changes ) { bool modified = false; if( changes.contains( Meta::valTitle ) ) { m_tag->setTitle( Qt4QStringToTString( changes.value( Meta::valTitle ).toString() ) ); modified = true; } if( changes.contains( Meta::valArtist ) ) { m_tag->setArtist( Qt4QStringToTString( changes.value( Meta::valArtist ).toString() ) ); modified = true; } if( changes.contains( Meta::valAlbum ) ) { m_tag->setAlbum( Qt4QStringToTString( changes.value( Meta::valAlbum ).toString() ) ); modified = true; } if( changes.contains( Meta::valTrackNr ) ) { m_tag->setTrack( changes.value( Meta::valTrackNr ).toUInt() ); modified = true; } if( changes.contains( Meta::valYear ) ) { m_tag->setYear( changes.value( Meta::valYear ).toUInt() ); modified = true; } if( changes.contains( Meta::valGenre ) ) { m_tag->setGenre( Qt4QStringToTString( changes.value( Meta::valGenre ).toString() ) ); modified = true; } if( changes.contains( Meta::valComment ) ) { m_tag->setComment( Qt4QStringToTString( changes.value( Meta::valComment ).toString() ) ); modified = true; } return modified; }
Meta::FieldHash ASFTagHelper::tags() const { Meta::FieldHash data = TagHelper::tags(); TagLib::ASF::AttributeListMap map = m_tag->attributeListMap(); for( TagLib::ASF::AttributeListMap::ConstIterator it = map.begin(); it != map.end(); ++it ) { if( !it->second.size() ) continue; qint64 field; TagLib::ASF::Attribute value = it->second[0]; QString strValue = TStringToQString( value.toString() ); if( ( field = fieldName( it->first ) ) ) { if( field == Meta::valBpm || field == Meta::valPlaycount ) data.insert( field, value.toUInt() ); else if( field == Meta::valRating ) data.insert( field, qRound( strValue.toFloat() * 10.0 ) ); else if( field == Meta::valScore ) data.insert( field, strValue.toFloat() * 100.0 ); else if( field == Meta::valDiscNr ) data.insert( field, value.toUInt() ); else if( field == Meta::valCompilation ) data.insert( field, value.toBool() ); else if( field == Meta::valHasCover ) { for( TagLib::ASF::AttributeList::ConstIterator cover = it->second.begin(); cover != it->second.end(); ++cover ) { if( cover->type() != TagLib::ASF::Attribute::BytesType ) continue; TagLib::ASF::Picture pict = cover->toPicture(); if( ( pict.type() == TagLib::ASF::Picture::FrontCover || pict.type() == TagLib::ASF::Picture::Other ) && pict.dataSize() > MIN_COVER_SIZE ) { data.insert( field, true ); break; } } } else data.insert( field, strValue ); } else if( it->first == uidFieldName( UIDAFT ) && isValidUID( strValue, UIDAFT ) ) data.insert( Meta::valUniqueId, strValue ); else if( it->first == uidFieldName( UIDMusicBrainz ) && isValidUID( strValue, UIDMusicBrainz ) ) { if( !data.contains( Meta::valUniqueId ) ) // we prefere AFT uids data.insert( Meta::valUniqueId, strValue.prepend( "mb-" ) ); } } return data; }
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; }
bool ID3v2TagHelper::setTags( const Meta::FieldHash &changes ) { bool modified = TagHelper::setTags( changes ); foreach( const qint64 key, changes.keys() ) { QVariant value = changes.value( key ); TagLib::ByteVector field( fieldName( key ).toCString() ); if( !field.isNull() && !field.isEmpty() ) { if( key == Meta::valHasCover ) continue; else if( key == Meta::valUniqueId ) { QPair< UIDType, QString > uidPair = splitUID( value.toString() ); if( uidPair.first == UIDInvalid ) continue; TagLib::String owner = uidFieldName( uidPair.first ); TagLib::ByteVector uid( uidPair.second.toAscii().data() ); TagLib::ID3v2::FrameList list = m_tag->frameList(); for( TagLib::ID3v2::FrameList::ConstIterator it = list.begin(); it != list.end(); ++it ) { if( ( *it )->frameID() == field ) { TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast< TagLib::ID3v2::UniqueFileIdentifierFrame * >( *it ); if( !frame ) continue; if( frame->owner() == owner ) { m_tag->removeFrame( frame ); modified = true; break; } } } if ( !uid.isEmpty() ) { m_tag->addFrame( new TagLib::ID3v2::UniqueFileIdentifierFrame( owner, uid ) ); modified = true; } continue; } TagLib::String tValue = Qt4QStringToTString( ( key == Meta::valCompilation ) ? QString::number( value.toInt() ) : value.toString() ); if( tValue.isEmpty() ) m_tag->removeFrames( field ); else { TagLib::ID3v2::TextIdentificationFrame *frame = NULL; if( !m_tag->frameListMap()[field].isEmpty() ) frame = dynamic_cast< TagLib::ID3v2::TextIdentificationFrame * >( m_tag->frameListMap()[field].front() ); if( !frame ) { frame = new TagLib::ID3v2::TextIdentificationFrame( field ); m_tag->addFrame( frame ); } // note: TagLib is smart enough to automatically set UTF8 encoding if needed. frame->setText( tValue ); } modified = true; } else if( key == Meta::valScore || key == Meta::valRating || key == Meta::valPlaycount ) { TagLib::String description; TagLib::String tValue; if( key == Meta::valRating ) { description = fmpsFieldName( FMPSRating ); tValue = Qt4QStringToTString( QString::number( value.toFloat() / 10.0 ) ); } else if( key == Meta::valScore ) { description = fmpsFieldName( FMPSScore ); tValue = Qt4QStringToTString( QString::number( value.toFloat() / 100.0 ) ); } else if( key == Meta::valPlaycount ) { description = fmpsFieldName( FMPSPlayCount ); tValue = Qt4QStringToTString( QString::number( value.toInt() ) ); } if( key == Meta::valRating || key == Meta::valPlaycount ) { TagLib::ID3v2::PopularimeterFrame *popFrame = NULL; if( !m_tag->frameListMap()[POPM_Frame].isEmpty() ) popFrame = dynamic_cast< TagLib::ID3v2::PopularimeterFrame * >( m_tag->frameListMap()[POPM_Frame].front() ); if( !popFrame ) { popFrame = new TagLib::ID3v2::PopularimeterFrame( POPM_Frame ); m_tag->addFrame( popFrame ); } if( key == Meta::valRating ) popFrame->setRating( qBound(0, int(qRound(value.toDouble() / 10.0 * 256)), 255) ); else popFrame->setCounter( value.toInt() ); modified = true; } TagLib::ID3v2::FrameList list = m_tag->frameList(); for( TagLib::ID3v2::FrameList::ConstIterator it = list.begin(); it != list.end(); ++it ) { if( ( *it )->frameID() == TXXX_Frame ) { TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast< TagLib::ID3v2::UserTextIdentificationFrame * >( *it ); if( !frame ) continue; if( frame->description() == description ) { m_tag->removeFrame( frame ); modified = true; break; } } } if( value.toBool() ) { TagLib::ID3v2::UserTextIdentificationFrame *frame = new TagLib::ID3v2::UserTextIdentificationFrame( TXXX_Frame ); frame->setDescription( description ); frame->setText( tValue ); m_tag->addFrame( frame ); modified = true; } } } return modified; }