diff --git a/ext/libstrawberry-tagreader/tagreadermessages.proto b/ext/libstrawberry-tagreader/tagreadermessages.proto index e68fc20d9..0560dd9b3 100644 --- a/ext/libstrawberry-tagreader/tagreadermessages.proto +++ b/ext/libstrawberry-tagreader/tagreadermessages.proto @@ -67,8 +67,11 @@ message SongMetadata { optional int64 lastplayed = 29; optional int64 lastseen = 30; - optional bool suspicious_tags = 31; - optional string art_automatic = 32; + optional string art_automatic = 31; + + optional float rating = 32; + + optional bool suspicious_tags = 40; } diff --git a/ext/libstrawberry-tagreader/tagreadertaglib.cpp b/ext/libstrawberry-tagreader/tagreadertaglib.cpp index 650448c15..b244a6cb8 100644 --- a/ext/libstrawberry-tagreader/tagreadertaglib.cpp +++ b/ext/libstrawberry-tagreader/tagreadertaglib.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -129,10 +130,11 @@ TagLib::String QStringToTaglibString(const QString &s) { } // namespace namespace { -// Tags containing the year the album was originally released (in contrast to other tags that contain the release year of the current edition) -const char *kMP4_OriginalYear_ID = "----:com.apple.iTunes:ORIGINAL YEAR"; -const char *kASF_OriginalDate_ID = "WM/OriginalReleaseTime"; -const char *kASF_OriginalYear_ID = "WM/OriginalReleaseYear"; +const char *kMP4_OriginalYear_ID = "----:com.apple.iTunes:ORIGINAL YEAR"; +const char *kMP4_FMPS_Playcount_ID = "----:com.apple.iTunes:FMPS_Playcount"; +const char *kMP4_FMPS_Rating_ID = "----:com.apple.iTunes:FMPS_Rating"; +const char *kASF_OriginalDate_ID = "WM/OriginalReleaseTime"; +const char *kASF_OriginalYear_ID = "WM/OriginalReleaseYear"; } // namespace @@ -316,6 +318,18 @@ void TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta } } + if (!map["POPM"].isEmpty()) { + const TagLib::ID3v2::PopularimeterFrame* frame = dynamic_cast(map["POPM"].front()); + if (frame) { + if (song->playcount() <= 0 && frame->counter() > 0) { + song->set_playcount(frame->counter()); + } + if (song->rating() <= 0 && frame->rating() > 0) { + song->set_rating(ConvertPOPMRating(frame->rating())); + } + } + } + } } @@ -357,6 +371,26 @@ void TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta song->set_originalyear(TStringToQString(mp4_tag->item(kMP4_OriginalYear_ID).toStringList().toString('\n')).left(4).toInt()); } + { + TagLib::MP4::Item item = mp4_tag->item(kMP4_FMPS_Playcount_ID); + if (item.isValid()) { + const int playcount = TStringToQString(item.toStringList().toString('\n')).toFloat(); + if (song->playcount() <= 0 && playcount > 0) { + song->set_playcount(playcount); + } + } + } + + { + TagLib::MP4::Item item = mp4_tag->item(kMP4_FMPS_Rating_ID); + if (item.isValid()) { + const float rating = TStringToQString(item.toStringList().toString('\n')).toFloat(); + if (song->rating() <= 0 && rating > 0) { + song->set_rating(rating); + } + } + } + Decode(mp4_tag->comment(), song->mutable_comment()); } } @@ -367,22 +401,44 @@ void TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta if (file_asf->tag()) { Decode(file_asf->tag()->comment(), song->mutable_comment()); + + const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap(); + + if (attributes_map.contains(kASF_OriginalDate_ID)) { + const TagLib::ASF::AttributeList &attributes = attributes_map[kASF_OriginalDate_ID]; + if (!attributes.isEmpty()) { + song->set_originalyear(TStringToQString(attributes.front().toString()).left(4).toInt()); + } + } + else if (attributes_map.contains(kASF_OriginalYear_ID)) { + const TagLib::ASF::AttributeList &attributes = attributes_map[kASF_OriginalYear_ID]; + if (!attributes.isEmpty()) { + song->set_originalyear(TStringToQString(attributes.front().toString()).left(4).toInt()); + } + } + + if (attributes_map.contains("FMPS/Playcount")) { + const TagLib::ASF::AttributeList &attributes = attributes_map["FMPS/Playcount"]; + if (!attributes.isEmpty()) { + int playcount = TStringToQString(attributes.front().toString()).toInt(); + if (song->playcount() <= 0 && playcount > 0) { + song->set_playcount(playcount); + } + } + } + + if (attributes_map.contains("FMPS/Rating")) { + const TagLib::ASF::AttributeList& attributes = attributes_map["FMPS/Rating"]; + if (!attributes.isEmpty()) { + float rating = TStringToQString(attributes.front().toString()).toFloat(); + if (song->rating() <= 0 && rating > 0) { + song->set_rating(rating); + } + } + } + } - const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap(); - - if (attributes_map.contains(kASF_OriginalDate_ID)) { - const TagLib::ASF::AttributeList &attributes = attributes_map[kASF_OriginalDate_ID]; - if (!attributes.isEmpty()) { - song->set_originalyear(TStringToQString(attributes.front().toString()).left(4).toInt()); - } - } - else if (attributes_map.contains(kASF_OriginalYear_ID)) { - const TagLib::ASF::AttributeList &attributes = attributes_map[kASF_OriginalYear_ID]; - if (!attributes.isEmpty()) { - song->set_originalyear(TStringToQString(attributes.front().toString()).left(4).toInt()); - } - } } else if (TagLib::MPC::File *file_mpc = dynamic_cast(fileref->file())) { @@ -465,6 +521,7 @@ void TagReaderTagLib::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString if (!map["METADATA_BLOCK_PICTURE"].isEmpty()) song->set_art_automatic(kEmbeddedCover); if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat()); + if (!map["FMPS_RATING"].isEmpty() && song->rating() <= 0) song->set_rating(TStringToQString(map["FMPS_RATING"].front()).trimmed().toFloat()); if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), song->mutable_lyrics()); else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), song->mutable_lyrics()); @@ -507,12 +564,19 @@ void TagReaderTagLib::ParseAPETag(const TagLib::APE::ItemListMap &map, QString * } if (map.contains("FMPS_PLAYCOUNT")) { - int playcount = TStringToQString(map["FMPS_PLAYCOUNT"].toString()).toFloat(); + const int playcount = TStringToQString(map["FMPS_PLAYCOUNT"].toString()).toFloat(); if (song->playcount() <= 0 && playcount > 0) { song->set_playcount(playcount); } } + if (map.contains("FMPS_RATING")) { + const float rating = TStringToQString(map["FMPS_RATING"].toString()).toFloat(); + if (song->rating() <= 0 && rating > 0) { + song->set_rating(rating); + } + } + } void TagReaderTagLib::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const spb::tagreader::SongMetadata &song) const { @@ -897,3 +961,45 @@ bool TagReaderTagLib::SaveEmbeddedArt(const QString &filename, const QByteArray return ref.file()->save(); } + +TagLib::ID3v2::PopularimeterFrame *TagReaderTagLib::GetPOPMFrameFromTag(TagLib::ID3v2::Tag *tag) { + + TagLib::ID3v2::PopularimeterFrame *frame = nullptr; + + const TagLib::ID3v2::FrameListMap &map = tag->frameListMap(); + if (!map["POPM"].isEmpty()) { + frame = dynamic_cast(map["POPM"].front()); + } + + if (!frame) { + frame = new TagLib::ID3v2::PopularimeterFrame(); + tag->addFrame(frame); + } + + return frame; + +} + +float TagReaderTagLib::ConvertPOPMRating(const int POPM_rating) { + + if (POPM_rating < 0x01) return 0.0; + else if (POPM_rating < 0x40) return 0.20; + else if (POPM_rating < 0x80) return 0.40; + else if (POPM_rating < 0xC0) return 0.60; + else if (POPM_rating < 0xFC) return 0.80; + + return 1.0; + +} + +int TagReaderTagLib::ConvertToPOPMRating(const float rating) { + + if (rating < 0.20) return 0x00; + else if (rating < 0.40) return 0x01; + else if (rating < 0.60) return 0x40; + else if (rating < 0.80) return 0x80; + else if (rating < 1.0) return 0xC0; + + return 0xFF; + +} diff --git a/ext/libstrawberry-tagreader/tagreadertaglib.h b/ext/libstrawberry-tagreader/tagreadertaglib.h index 2707043a3..812e85438 100644 --- a/ext/libstrawberry-tagreader/tagreadertaglib.h +++ b/ext/libstrawberry-tagreader/tagreadertaglib.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "tagreaderbase.h" #include "tagreadermessages.pb.h" @@ -73,6 +74,10 @@ class TagReaderTagLib : public TagReaderBase { QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const; + static float ConvertPOPMRating(const int POPM_rating); + static int ConvertToPOPMRating(const float rating); + static TagLib::ID3v2::PopularimeterFrame *GetPOPMFrameFromTag(TagLib::ID3v2::Tag* tag); + private: FileRefFactory *factory_;