Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
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();
    }
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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;
}