bool TagReader::SaveSongStatisticsToFile( const QString& filename, const pb::tagreader::SongMetadata& song) const { if (filename.isNull()) return false; qLog(Debug) << "Saving song statistics tags to" << filename; std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename)); if (!fileref || fileref->isNull()) // The file probably doesn't exist return false; if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) { TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true); // Save as FMPS SetUserTextFrame("FMPS_PlayCount", QString::number(song.playcount()), tag); SetUserTextFrame("FMPS_Rating_Amarok_Score", QString::number(song.score() / 100.0), tag); // Also save as POPM TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag); frame->setCounter(song.playcount()); } else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) { TagLib::Ogg::XiphComment* vorbis_comments = file->xiphComment(true); SetFMPSStatisticsVorbisComments(vorbis_comments, song); } else if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>( fileref->file()->tag())) { SetFMPSStatisticsVorbisComments(tag, song); } #ifdef TAGLIB_WITH_ASF else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) { TagLib::ASF::Tag* tag = file->tag(); tag->addAttribute("FMPS/Playcount", NumberToASFAttribute(song.playcount())); tag->addAttribute("FMPS/Rating_Amarok_Score", NumberToASFAttribute(song.score() / 100.0)); } #endif else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) { TagLib::MP4::Tag* tag = file->tag(); tag->itemListMap()[kMP4_FMPS_Score_ID] = TagLib::StringList( QStringToTaglibString(QString::number(song.score() / 100.0))); tag->itemListMap()[kMP4_FMPS_Playcount_ID] = TagLib::StringList(TagLib::String::number(song.playcount())); } else { // Nothing to save: stop now return true; } bool ret = fileref->save(); #ifdef Q_OS_LINUX if (ret) { // Linux: inotify doesn't seem to notice the change to the file unless we // change the timestamps as well. (this is what touch does) utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0); } #endif // Q_OS_LINUX return ret; }
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; }