[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