Skip to content

Add reading of rating tag for MP3, MP4, and FLAC#1089

Closed
LindyBalboa wants to merge 4 commits intomixxxdj:masterfrom
LindyBalboa:ratings
Closed

Add reading of rating tag for MP3, MP4, and FLAC#1089
LindyBalboa wants to merge 4 commits intomixxxdj:masterfrom
LindyBalboa:ratings

Conversation

@LindyBalboa
Copy link
Copy Markdown
Contributor

@LindyBalboa LindyBalboa commented Dec 23, 2016

So this is partly addressed in the open PR #924 but it seems to have stagnated so I have decided to jump on it. Current state is that for mp3 the rating is taken from a POPM tag with email "mixxx@mixxx.org" or the first available if there is none from mixxx. MP4 and FLAC both use a plain 0-100 integer scale.

Values are then parsed into a 0-5 value before being added to the metadata object. For POPM, the same standard as Windows Media Player/Explorer is followed.

To do:
-add support for APE tags I'm not familiar with it and don't use it. I can't seem to find the key for the rating tag. Does anyone know this off hand?
-add writing from database to file. Need some discussion here. As MP4, FLAC, and APE do no use POPM, there isn't the email feature. So I feel it would actually make little sense to use a designated email for mixxx. Some users claim that they need different rating option for different applications/people, but honestly I use a mixed library; format isn't so important to me. Like I said, discussion needed.
-add the option to not overwrite existing ratings in the database
-add the option to turn writing the rating to the file tag on or off

I'm not quite sure where in the code the writing part would be controlled. If someone could point me in the right direction I'm sure I can figure it out, but if someone wants to contribute that, that would be great too.

@LindyBalboa LindyBalboa force-pushed the ratings branch 4 times, most recently from b10f42f to 724b873 Compare December 23, 2016 12:25
@uklotzde
Copy link
Copy Markdown
Contributor

uklotzde commented Dec 24, 2016

Please replace all tabs with 4 spaces:
https://www.mixxx.org/wiki/doku.php/coding_guidelines

Comment thread src/track/trackmetadatataglib.cpp Outdated
return isBpmValid;
}

void parseRating(TrackMetadata *pTrackMetadata, QString sRating, FileType filetype = FileType::UNKNOWN) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The context (i.e. file/tag type) is known at compile time when this function is invoked. Please split it into 3 functions.

@uklotzde
Copy link
Copy Markdown
Contributor

uklotzde commented Dec 24, 2016

Tag writing: Simply add the conversion in the opposite direction from iRating into tag values to the specialized write functions in trackmetadatataglib.cpp.

Writing tags into files is currently disabled:
#728
With this PR all tag values including the rating could be written into files.

Comment thread src/track/trackmetadatataglib.cpp Outdated
rating = QString::number(dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(popmRatingFrameList.front())->rating());
for (TagLib::ID3v2::FrameList::ConstIterator it(popmRatingFrameList.begin());
it != popmRatingFrameList.end(); it++){
auto popmFrame = dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(*it);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result of a dynamic_cast should always be checked! See various examples in this file

Comment thread src/track/trackmetadatataglib.cpp Outdated
QString rating;
const TagLib::ID3v2::FrameList popmRatingFrameList(tag.frameListMap()["POPM"]);
if (!popmRatingFrameList.isEmpty()) {
rating = QString::number(dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(popmRatingFrameList.front())->rating());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code duplicates part of the loop body. Initialize the rating with -1 and inside the loop body check if either of these conditions is met: (rating == -1) || (email == ...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point for the duplicaton was to take the first rating found and then check all of them if there is a mixxx specific frame. Since this is only truly a problem for MP3, an alternative would be to forget having the mixxx specific email and just reading writing the 1st frame.

That is really a decision that should be made by people more familiar with the trajectory of the project.

Comment thread src/track/trackmetadatataglib.cpp Outdated
it != popmRatingFrameList.end(); it++){
auto popmFrame = dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(*it);
if (toQString(popmFrame->email()) == "mixxx@mixxx.org") {
rating = QString::number(popmFrame->rating());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should exit the loop after you found the first rating with a matching email.

Comment thread src/track/trackmetadata.h Outdated
m_bpm.resetValue();
}

// rating
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "// rating" does not add any value.

Comment thread src/track/trackmetadatataglib.cpp Outdated
for (TagLib::ID3v2::FrameList::ConstIterator it(popmRatingFrameList.begin());
it != popmRatingFrameList.end(); it++){
auto popmFrame = dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(*it);
if (toQString(popmFrame->email()) == "mixxx@mixxx.org") {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The e-mail address should be stored in a named constant. Simply define one in the anonymous namespace in the .cpp file.

Comment thread src/track/trackmetadatataglib.cpp Outdated
return isBpmValid;
}

void parseRating(TrackMetadata *pTrackMetadata, QString sRating, FileType filetype = FileType::UNKNOWN) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can omit the pTrackMetadata argument if you simply return the parsed rating and let the caller decide what to do with this result. Currently this function does two different things: parsing + setting. But the name is only "parse".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always prefer to write functions with a result and no side effects over functions with no result but with side effects.

Comment thread src/track/trackmetadatataglib.cpp Outdated

QString rating;
if (readMP4Atom(tag, "rate", &rating)) {
parseRating(pTrackMetadata, rating, FileType::MP4);
Copy link
Copy Markdown
Contributor

@uklotzde uklotzde Dec 24, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As already mentioned, the FileType is always known at compile time and does not need to be passed as an additional argument.

@LindyBalboa LindyBalboa force-pushed the ratings branch 2 times, most recently from 09a159d to 1ce1dad Compare March 19, 2017 17:02
@LindyBalboa
Copy link
Copy Markdown
Contributor Author

I think this should have a default off operation and only be turned on by choice. It has the potential to overwrite the current database-only ratings. Once writing metadata is implemented, it should also have the option "write all nonzero ratings to file before reading ratings from files".

Thoughts?

@uklotzde
Copy link
Copy Markdown
Contributor

uklotzde commented Mar 19, 2017 via email

@illuusio
Copy link
Copy Markdown
Contributor

illuusio commented Apr 6, 2017

What is this current situation? Should this work and is there test case for this implemented so I can run this on test pack?

@LindyBalboa
Copy link
Copy Markdown
Contributor Author

This does work. There is no testing implemented. It is basically bare bones minimum working example as of yet. I've really been tied up with school lately and haven't had time to sit down with this. However, I am trying to keep it up to date with master.

I am completely open to suggestions on how to continue with this.

@illuusio
Copy link
Copy Markdown
Contributor

illuusio commented Apr 7, 2017

Little testing advise would be nice. How to add these in MP3, M4A or OGG so I can test this and what would I expect as return.

@LindyBalboa
Copy link
Copy Markdown
Contributor Author

I'm not sure what you are asking? This is built directly in to the standard "read tags from file" operations. Nothing else is required at the moment beyond scanning the library.

Currently Mixxx does not write any tags to file, it only stores them in the database. As @uklotzde wrote, #728 is concerned with mix writing ratings. So this will only pick up ratings generated by other programs. I'm not sure if iTunes is writing ratings to file yet, or if it is all still stored in their XML file. I personally have used MediaMonkey while on Windows and have written my ratings using that up until now.

So basically, rate some files in another program that we know works. Amarok or Banshee maybe for linux? WMP on Windows I think. Then scan them into Mixxx's library and see if they result is as expected. Half star ratings from programs like MediaMonkey should be rounded up to the next full star.

@illuusio
Copy link
Copy Markdown
Contributor

illuusio commented Apr 7, 2017

Sorry not being clear about my thoughts! I have to investigate which app I can use to test this. I haven't yet read code very hard but on mp3 it would read ID3v2 POPM tag and on Ogg Vorbis it would be RATING comment and m4a RATE tag. Just wondering which tags to use for testing this out.

@uklotzde
Copy link
Copy Markdown
Contributor

uklotzde commented Apr 7, 2017 via email

@LindyBalboa
Copy link
Copy Markdown
Contributor Author

@uklotzde I can definitely add some tests there. A few questions.

  1. The folder is id3, but this is more general. Should I just place the files in there as is, or should we rename the folder to be more general like test-tag-data?

  2. For ratings there are a wide range of cases-- basically the starting and ending points of ranges which become encapsulated in a singular star rating. That would suggest upwards of #ofFormats*#ofStars*2. In this case that comes out to 30. That seems like a lot of test files to include. Any suggestion on limiting the number of cases, but still providing a reasonable amount of testing?

@illuusio
Copy link
Copy Markdown
Contributor

First there should be test files with and without stars and maybe different files have different staring or something like that. Added thirty test files sounds awful lot to me.

@illuusio
Copy link
Copy Markdown
Contributor

illuusio commented Apr 26, 2017

Ok. i'll give this a shot and check how this is working. Still should have some test cases.

parseRating has been split into separate functions for MP3, FLAC, and
MP4 filetypes since  they are already known.
parse*TYPE*Rating now only returns the value, without side effects.
Now check when casting a generic TagLib frame to a popularimeter frame.
For MP4 and Flac, the convenience functions to read tag items
assumes all fields are strings. Previously conversion was done
in the parsing functions, it has been switched to write after
reading, before the parsers are called.
@ghost
Copy link
Copy Markdown

ghost commented Sep 11, 2017

Just to let you guys know: Your work is very appreciated (i do 👍 ) and there are enough scenarios out there, in which the rating is nescessary/helpful.

In my case, I have a bigger collection of files (5000 or so) from http://remix.kwed.org with complex sounds, that I have to hear in another audio player/on the go. So i need ratings to filter out, what needs a second listening later and i can mark some hymns (5 stars), which are always possible for a dance crowd. 3 stars is good, but only for private listening and rarely for dj sets. And so son.

I deeply hope, your contribution will make in the mixxx code!

Thx and greetings!

@Be-ing
Copy link
Copy Markdown
Contributor

Be-ing commented Dec 11, 2017

@LindyBalboa merge conflicts have developed since #728 was merged.

@Be-ing Be-ing added this to the 2.2.0 milestone Dec 27, 2017
@Be-ing
Copy link
Copy Markdown
Contributor

Be-ing commented Apr 18, 2018

This pull request has not been updated in over a year and merge conflicts have developed, so I am closing it. @LindyBalboa if you fix the merge conflicts and would like to continue working on this, please reopen this PR.

@joaonunatings
Copy link
Copy Markdown

joaonunatings commented Jul 4, 2024

Is this still being developed @LindyBalboa? I can take over this PR if possible

EDIT: also tagging @Be-ing & @uklotzde

@ronso0
Copy link
Copy Markdown
Member

ronso0 commented Jul 4, 2024

The last update before your post was that Be closed this because there has not been any progress for over a year.
Since then Be and uklotz left the dev team.

If you want to take over feel free to do so. Though, in order to avoid repetition (and maybe frustration), I recommend you read the entire conversation here, and probably also in #9477 and #924.

@joaonunatings
Copy link
Copy Markdown

@ronso0 thank you for the quick reply!

I already went through those links (that's how I got to this PR), so I'll probably just create a new issue as a tabula rasa for this problem and mention implementation details, issue/PR mentions and additional info.

@CaqKa
Copy link
Copy Markdown

CaqKa commented Sep 23, 2025

Hi Mixxx team,

First of all, thank you for building Mixxx — it’s by far the best option I’ve found on Linux for DJing with BPM-aware libraries, and I really appreciate the work you all put into it.

I’m a swing dance DJ and until recently I was using iTunes 12, mainly because of its Auto DJ function. My library has about 1000 MP3s, each tagged with gain, start/end markers (to skip silence or clapping), ratings, BPM, and comments. In iTunes this was very easy to manage: search worked across comments, BPM, ratings, etc., and the setup was portable — all I needed was the library file and the correct iTunes version.

After moving to Linux I tried Clementine, Rhythmbox, ncmpcpp and others, but none of them could combine BPM support with good searching and rating management. Mixxx finally gave me BPM + search in one app, which is great.

That said, I’ve run into a couple of inconveniences where I’d really appreciate some advice or clarification:

  1. Ratings
  • I’d love to keep my old iTunes ratings, but I haven’t found a way to import them into Mixxx.
  • From what I understand, Mixxx currently doesn’t write ratings into MP3 tags (ID3 POPM), so ratings stay in the Mixxx database only.
  • Is there any plan or discussion about supporting rating import/export in the future? I even wonder if there’s a way for interested users like me to support this (for example via a bounty) since I’m not a developer myself (yet).
  1. Library paths in Flatpak
  • On Linux Mint I’m using the Flatpak version of Mixxx and pointed it to the same music directory I used on Ubuntu.
  • Sometimes, however, Mixxx seems to show different paths for my tracks (e.g. under /run/user/1000/.../mp3). I’m not sure if this is normal Flatpak behavior or if I’ve misconfigured something. Could you perhaps clarify what the best practice is for pointing Mixxx (Flatpak) to a music directory?
  1. Music library portability guidance
  • With iTunes, portability was easy: I just copied the library directory.
  • With Mixxx, I’d like to achieve something similar. Could you point me to a tutorial or documentation on which files need to be backed up (e.g. Mixxx database, configuration, metadata in the music files) in order to make my collection portable across machines?
  • I think such guidance would help not only me, but also other DJs migrating from iTunes or similar setups.

If there’s already documentation on these topics that I’ve missed, I’d be very grateful for a link.

Thanks again for all your work — Mixxx has already made my Linux DJ life much easier, and I’m excited to keep learning how to get the most out of it.

@ronso0
Copy link
Copy Markdown
Member

ronso0 commented Sep 24, 2025

Hej @CaqKa
These are too many questions for a ratings PR. In the future, try to file separate issues / feature requests so they can be tracked easily and the conversations are focused.

Let's break it down:

  1. Ratings, plan or discussion about supporting rating import/export in the future
    Please check the related feature requests to get an idea what's up / possible / desired / to do
    https://github.com/mixxxdj/mixxx/issues?q=is%3Aissue%20state%3Aopen%20rating+import
    If you're able, you could also write a helper tool (c++, python) that does a one-time import: reads ratings from individual tracks or a library (iTunes xml) and import them into the rating column in the library table of mixxxdb.sqlite. Track file locations are stored in the track_locations table.
  2. Library paths in Flatpak
    For issue with the Flatpak build I suggest you file a report at Mixxx' Flathub repo
    https://github.com/flathub/org.mixxx.Mixxx/issues
  3. Music library portability guidance
    For migrating settings and database we have a guide in the manual
    https://manual.mixxx.org/latest/chapters/advanced_topics#migrate-your-mixxx-library-and-settings-to-a-new-computer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants