void TagReader::SetVorbisComments( TagLib::Ogg::XiphComment* vorbis_comments, const pb::tagreader::SongMetadata& song) const { vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true); vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true); vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true); vorbis_comments->addField( "BPM", QStringToTaglibString(song.bpm() <= 0 - 1 ? QString() : QString::number(song.bpm())), true); vorbis_comments->addField( "DISCNUMBER", QStringToTaglibString( song.disc() <= 0 - 1 ? QString() : QString::number(song.disc())), true); vorbis_comments->addField( "COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true); // Try to be coherent, the two forms are used but the first one is preferred vorbis_comments->addField("ALBUMARTIST", StdStringToTaglibString(song.albumartist()), true); vorbis_comments->removeField("ALBUM ARTIST"); vorbis_comments->addField("LYRICS", StdStringToTaglibString(song.lyrics()), true); vorbis_comments->removeField("UNSYNCEDLYRICS"); }
void TagReader::SetFMPSStatisticsVorbisComments( TagLib::Ogg::XiphComment* vorbis_comments, const pb::tagreader::SongMetadata& song) const { vorbis_comments->addField( "FMPS_PLAYCOUNT", QStringToTaglibString(QString::number(song.playcount()))); vorbis_comments->addField( "FMPS_RATING_AMAROK_SCORE", QStringToTaglibString(QString::number(song.score() / 100.0))); }
void TagReader::SetFMPSRatingVorbisComments( TagLib::Ogg::XiphComment* vorbis_comments, const pb::tagreader::SongMetadata& song) const { vorbis_comments->addField( "FMPS_RATING", QStringToTaglibString(QString::number(song.rating()))); }
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments, const pb::tagreader::SongMetadata& song) const { vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true); vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true); vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true); vorbis_comments->addField( "BPM", QStringToTaglibString( song.bpm() <= 0 - 1 ? QString() : QString::number(song.bpm())), true); vorbis_comments->addField( "DISCNUMBER", QStringToTaglibString( song.disc() <= 0 - 1 ? QString() : QString::number(song.disc())), true); vorbis_comments->addField( "COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true); }
void Song::InitFromProtobuf(const pb::tagreader::SongMetadata& pb) { d->init_from_file_ = true; d->valid_ = pb.valid(); d->title_ = QStringFromStdString(pb.title()); d->album_ = QStringFromStdString(pb.album()); d->artist_ = QStringFromStdString(pb.artist()); d->albumartist_ = QStringFromStdString(pb.albumartist()); d->composer_ = QStringFromStdString(pb.composer()); d->performer_ = QStringFromStdString(pb.performer()); d->grouping_ = QStringFromStdString(pb.grouping()); d->lyrics_ = QStringFromStdString(pb.lyrics()); d->track_ = pb.track(); d->disc_ = pb.disc(); d->bpm_ = pb.bpm(); d->year_ = pb.year(); d->originalyear_ = pb.originalyear(); d->genre_ = QStringFromStdString(pb.genre()); d->comment_ = QStringFromStdString(pb.comment()); d->compilation_ = pb.compilation(); d->skipcount_ = pb.skipcount(); d->lastplayed_ = pb.lastplayed(); d->score_ = pb.score(); set_length_nanosec(pb.length_nanosec()); d->bitrate_ = pb.bitrate(); d->samplerate_ = pb.samplerate(); set_url(QUrl::fromEncoded(QByteArray(pb.url().data(), pb.url().size()))); d->basefilename_ = QStringFromStdString(pb.basefilename()); d->mtime_ = pb.mtime(); d->ctime_ = pb.ctime(); d->filesize_ = pb.filesize(); d->suspicious_tags_ = pb.suspicious_tags(); d->filetype_ = static_cast<FileType>(pb.type()); d->etag_ = QStringFromStdString(pb.etag()); if (pb.has_art_automatic()) { d->art_automatic_ = QStringFromStdString(pb.art_automatic()); } if (pb.has_rating()) { d->rating_ = pb.rating(); } if (pb.has_playcount()) { d->playcount_ = pb.playcount(); } InitArtManual(); }
bool TagReader::SaveSongRatingToFile( const QString& filename, const pb::tagreader::SongMetadata& song) const { if (filename.isNull()) return false; qLog(Debug) << "Saving song rating tags to" << filename; if (song.rating() < 0) { // The FMPS spec says unrated == "tag not present". For us, no rating // results in rating being -1, so don't write anything in that case. // Actually, we should also remove tag set in this case, but in // Clementine it is not possible to unset rating i.e. make a song "unrated". qLog(Debug) << "Unrated: do nothing"; return true; } 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_Rating", QString::number(song.rating()), tag); // Also save as POPM TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag); frame->setRating(ConvertToPOPMRating(song.rating())); } else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) { TagLib::Ogg::XiphComment* vorbis_comments = file->xiphComment(true); SetFMPSRatingVorbisComments(vorbis_comments, song); } else if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>( fileref->file()->tag())) { SetFMPSRatingVorbisComments(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/Rating", NumberToASFAttribute(song.rating())); } #endif else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) { TagLib::MP4::Tag* tag = file->tag(); tag->itemListMap()[kMP4_FMPS_Rating_ID] = TagLib::StringList( QStringToTaglibString(QString::number(song.rating()))); } 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 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 TagReader::SaveFile(const QString& filename, const pb::tagreader::SongMetadata& song) const { if (filename.isNull()) return false; qLog(Debug) << "Saving tags to" << filename; std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename)); if (!fileref || fileref->isNull()) // The file probably doesn't exist return false; fileref->tag()->setTitle(StdStringToTaglibString(song.title())); fileref->tag()->setArtist(StdStringToTaglibString(song.artist())); // TPE1 fileref->tag()->setAlbum(StdStringToTaglibString(song.album())); fileref->tag()->setGenre(StdStringToTaglibString(song.genre())); fileref->tag()->setComment(StdStringToTaglibString(song.comment())); fileref->tag()->setYear(song.year()); fileref->tag()->setTrack(song.track()); if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) { TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true); SetTextFrame( "TPOS", song.disc() <= 0 - 1 ? QString() : QString::number(song.disc()), tag); SetTextFrame("TBPM", song.bpm() <= 0 - 1 ? QString() : QString::number(song.bpm()), tag); SetTextFrame("TCOM", song.composer(), tag); SetTextFrame("TIT1", song.grouping(), tag); // Skip TPE1 (which is the artist) here because we already set it SetTextFrame("TPE2", song.albumartist(), tag); SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag); } else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) { TagLib::Ogg::XiphComment* tag = file->xiphComment(); SetVorbisComments(tag, song); } else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) { TagLib::MP4::Tag* tag = file->tag(); tag->itemListMap()["disk"] = TagLib::MP4::Item(song.disc() <= 0 - 1 ? 0 : song.disc(), 0); tag->itemListMap()["tmpo"] = TagLib::StringList( song.bpm() <= 0 - 1 ? "0" : TagLib::String::number(song.bpm())); tag->itemListMap()["\251wrt"] = TagLib::StringList(song.composer()); tag->itemListMap()["\251grp"] = TagLib::StringList(song.grouping()); tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist()); tag->itemListMap()["cpil"] = TagLib::StringList(song.compilation() ? "1" : "0"); } // Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same // way; // apart, so we keep specific behavior for some formats by adding another // "else if" block above. if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) { SetVorbisComments(tag, song); } 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; }