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);
}
Exemple #5
0
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;
}