[multimedia/kid3] /: kid3-cli: Support star ratings with 'get/set ratingstars'
Urs Fleisch
null at kde.org
Sat Jun 4 05:53:11 BST 2022
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;
More information about the kde-doc-english
mailing list