Git commit 70dbaaa264a65dc85fe4e7ff3551cbd0acb144dd by Urs Fleisch. Committed on 04/06/2022 at 04:51. Pushed by ufleisch into branch 'master'.
kid3-cli: Support star ratings with 'get/set ratingstars' BUG: 454437 M +18 -0 doc/en/index.docbook M +103 -1 src/core/model/kid3application.cpp M +4 -1 src/core/tags/frame.cpp https://invent.kde.org/multimedia/kid3/commit/70dbaaa264a65dc85fe4e7ff3551cbd0acb144dd diff --git a/doc/en/index.docbook b/doc/en/index.docbook index b969a814..069861ab 100644 --- a/doc/en/index.docbook +++ b/doc/en/index.docbook @@ -2892,6 +2892,11 @@ for example <userinput>get artist.selected</userinput> will return <computeroutput>1</computeroutput> if the artist frame is selected, else <computeroutput>0</computeroutput>. </para> +<para> +The pseudo frame name "ratingstars" can be used to get the value of the +"rating" frame as the format specific value corresponding to the number of +stars (0 to 5). When using "rating", the internal value is returned. +</para> </sect2> <sect2 id="cli-set"> @@ -2949,6 +2954,19 @@ field name "selected" can be used. Normally, all frames are selected, to deselect all, use <userinput>set '*.selected' 0</userinput>, then for example <userinput>set artist.selected 1</userinput> to select the artist frame. </para> +<para> +The pseudo frame name "ratingstars" can be used to set the value of the +"rating" frame to the format specific value corresponding to the number of +stars (0 to 5). The frame name "rating" can be used to set the internal +value. +</para> +<para> +Setting "ratingstars" on multiple files having different tag formats will not +work because the frame with the value mapped from the star count is created +for the first file and then used for all files. So instead of +<userinput>kid3-cli -c "set ratingstars 2" *</userinput> you should rather use +<userinput>for f in *; do kid3-cli -c "set ratingstars 2" $f; done</userinput>. +</para> </sect2> <sect2 id="cli-revert"> diff --git a/src/core/model/kid3application.cpp b/src/core/model/kid3application.cpp index 4c980c7e..b326ff75 100644 --- a/src/core/model/kid3application.cpp +++ b/src/core/model/kid3application.cpp @@ -178,6 +178,69 @@ void extractFileFieldIndex( } } +/** + * Get the internal rating frame name with optional field. + * @param frame frame containing rating + * @param taggedFile optional taggedFile to be used if @a frame does not have + * a useful internal name + * @param tagNr used together with @a taggedFile to guess rating name + * @return internal name, "POPM.Email-Value" if POPM with Email value. + */ +QString ratingTypeName(const Frame& frame, + const TaggedFile* taggedFile = nullptr, + Frame::TagNumber tagNr = Frame::Tag_2) +{ + QString name = frame.getInternalName(); + if (name.startsWith(QLatin1String("POPM"))) { + name.truncate(4); + QVariant emailVar = frame.getFieldValue(Frame::ID_Email); + QString emailValue; + if (emailVar.isValid() && + !(emailValue = emailVar.toString()).isEmpty()) { + name += QLatin1Char('.'); + name += emailValue; + } + } else if (taggedFile && + name != QLatin1String("RATING") && + name != QLatin1String("rate") && + name != QLatin1String("IRTD") && + name != QLatin1String("WM/SharedUserRating")) { + QString tagFormat = taggedFile->getTagFormat(tagNr); + if (tagFormat.isEmpty()) { + QString ext = taggedFile->getFileExtension().toLower(); + if (ext == QLatin1String(".mp3") || ext == QLatin1String(".mp2") || + ext == QLatin1String(".aac") || ext == QLatin1String(".tta") || + ext == QLatin1String(".dsf") || ext == QLatin1String(".dff")) { + tagFormat = QLatin1String("ID3v2.3.0"); + } else if (ext == QLatin1String(".ogg") || + ext == QLatin1String(".flac") || + ext == QLatin1String(".opus")) { + tagFormat = QLatin1String("Vorbis"); + } else if (ext == QLatin1String(".m4a")) { + tagFormat = QLatin1String("MP4"); + } else if (ext == QLatin1String(".wav") || + ext == QLatin1String(".aiff")) { + tagFormat = tagNr == Frame::Tag_3 ? QLatin1String("RIFF INFO") + : QLatin1String("ID3v2.3.0"); + } else if (ext == QLatin1String(".wma")) { + tagFormat = QLatin1String("ASF"); + } + } + if (tagFormat.startsWith(QLatin1String("ID3v2"))) { + name = QLatin1String("POPM"); + } else if (tagFormat == QLatin1String("Vorbis")) { + name = QLatin1String("RATING"); + } else if (tagFormat == QLatin1String("MP4")) { + name = QLatin1String("rate"); + } else if (tagFormat == QLatin1String("RIFF INFO")) { + name = QLatin1String("IRTD"); + } else if (tagFormat == QLatin1String("ASF")) { + name = QLatin1String("WM/SharedUserRating"); + } + } + return name; +} + } /** Fallback for path to search for plugins */ @@ -3470,6 +3533,11 @@ QString Kid3Application::getFrame(Frame::TagVersion tagMask, explicitType = Frame::ExtendedType(Frame::FT_Other, frameName); } extractFileFieldIndex(frameName, dataFileName, fieldName, index); + bool isRatingStars = false; + if (frameName.toLower() == QLatin1String("ratingstars")) { + frameName.truncate(6); // Reduce to "rating" + isRatingStars = true; + } Frame::TagNumber tagNr = Frame::tagNumberFromMask(tagMask); if (tagNr >= Frame::Tag_NumValues) return QString(); @@ -3535,6 +3603,14 @@ QString Kid3Application::getFrame(Frame::TagVersion tagMask, return Frame::getField(*it, fieldName).toString(); } } + if (isRatingStars) { + bool ok; + int rating = it->getValue().toInt(&ok); + if (ok) { + return QString::number(TagConfig::instance().starCountFromRating( + rating, ratingTypeName(*it))); + } + } return it->getValue(); } else { return QString(); @@ -3613,6 +3689,11 @@ bool Kid3Application::setFrame(Frame::TagVersion tagMask, explicitType = Frame::ExtendedType(Frame::FT_Other, frameName); } extractFileFieldIndex(frameName, dataFileName, fieldName, index); + bool isRatingStars = false; + if (frameName.toLower() == QLatin1String("ratingstars")) { + frameName.truncate(6); // Reduce to "rating" + isRatingStars = true; + } FrameCollection frames(ft->frames()); auto it = explicitType.getType() == Frame::FT_UnknownFrame ? frames.findByName(frameName, index) @@ -3676,7 +3757,18 @@ bool Kid3Application::setFrame(Frame::TagVersion tagMask, } else { auto& frame = const_cast<Frame&>(*it); if (fieldName.isEmpty()) { - frame.setValueIfChanged(value); + QString val(value); + if (isRatingStars) { + bool ok; + int starCount = value.toInt(&ok); + if (ok && starCount >= 0 && starCount <= 5) { + val = QString::number(TagConfig::instance().starCountToRating( + starCount, ratingTypeName(*it))); + } else { + return false; + } + } + frame.setValueIfChanged(val); } else { if (fieldName == QLatin1String("selected")) { const int frameIndex = frame.getIndex(); @@ -3768,6 +3860,16 @@ bool Kid3Application::setFrame(Frame::TagVersion tagMask, } } } + if (isRatingStars) { + bool ok; + int starCount = value.toInt(&ok); + if (ok && starCount >= 0 && starCount <= 5) { + frame.setValue(QString::number(TagConfig::instance().starCountToRating( + starCount, ratingTypeName(frame, getSelectedFile(), tagNr)))); + } else { + return false; + } + } addFrame(tagNr, &frame); return true; } diff --git a/src/core/tags/frame.cpp b/src/core/tags/frame.cpp index 0dbd2a3d..9287e014 100644 --- a/src/core/tags/frame.cpp +++ b/src/core/tags/frame.cpp @@ -1466,7 +1466,10 @@ FrameCollection::const_iterator FrameCollection::searchByName( #if QT_VERSION >= 0x060000 if (ucName == ucFrameName.left(len)) #else - if (ucName == ucFrameName.leftRef(len)) + // Do not return ASF "Rating Information" when searching for "Rating". + if (ucName == ucFrameName.leftRef(len) && + !(ucName == QLatin1String("RATING") && + ucFrameName == QLatin1String("RATING INFORMATION"))) #endif { return it;
