diff --git a/autotests/samplefiles/mp3_rating/testMM.mp3 b/autotests/samplefiles/mp3_rating/testMM.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@("path"); + QTest::addColumn("expectedRating"); + + QTest::addRow("WMP") + << QFINDTESTDATA("samplefiles/mp3_rating/testWMP.mp3") + << 0 ; + QTest::addRow("WMP1") + << QFINDTESTDATA("samplefiles/mp3_rating/testWMP1.mp3") + << 2 ; + QTest::addRow("WMP2") + << QFINDTESTDATA("samplefiles/mp3_rating/testWMP2.mp3") + << 4 ; + QTest::addRow("WMP3") + << QFINDTESTDATA("samplefiles/mp3_rating/testWMP3.mp3") + << 6 ; + QTest::addRow("WMP4") + << QFINDTESTDATA("samplefiles/mp3_rating/testWMP4.mp3") + << 8 ; + QTest::addRow("WMP5") + << QFINDTESTDATA("samplefiles/mp3_rating/testWMP5.mp3") + << 10 ; + QTest::addRow("MM") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM.mp3") + << 0 ; + QTest::addRow("MM1") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM1.mp3") + << 1 ; + QTest::addRow("MM2") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM2.mp3") + << 2 ; + QTest::addRow("MM3") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM3.mp3") + << 3 ; + QTest::addRow("MM4") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM4.mp3") + << 4 ; + QTest::addRow("MM5") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM5.mp3") + << 5 ; + QTest::addRow("MM6") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM6.mp3") + << 6 ; + QTest::addRow("MM7") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM7.mp3") + << 7 ; + QTest::addRow("MM8") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM8.mp3") + << 8 ; + QTest::addRow("MM9") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM9.mp3") + << 9 ; + QTest::addRow("MM10") + << QFINDTESTDATA("samplefiles/mp3_rating/testMM10.mp3") + << 10 ; +} + +void TagLibExtractorTest::testMP3Rating() +{ + QFETCH(QString, path); + QFETCH(int, expectedRating); + + TagLibExtractor plugin{this}; + SimpleExtractionResult resultMp3(path, "audio/mpeg"); + plugin.extract(&resultMp3); + + QCOMPARE(resultMp3.properties().value(Property::Rating).toInt(), expectedRating); } void TagLibExtractorTest::testNoMetadata_data() diff --git a/src/extractors/taglibextractor.h b/src/extractors/taglibextractor.h --- a/src/extractors/taglibextractor.h +++ b/src/extractors/taglibextractor.h @@ -62,6 +62,7 @@ TagLib::StringList genres; QVariant discNumber; QVariant opus; + QVariant rating; }; void extractMP3(TagLib::FileStream& stream, ExtractedData& data); void extractMP4(TagLib::FileStream& stream, ExtractedData& data); diff --git a/src/extractors/taglibextractor.cpp b/src/extractors/taglibextractor.cpp --- a/src/extractors/taglibextractor.cpp +++ b/src/extractors/taglibextractor.cpp @@ -37,7 +37,7 @@ #include #include #include - +#include #include #include @@ -208,6 +208,30 @@ data.compilation += (*it)->toString(); } } + + // Rating. + /* There is no standard regarding ratings. Most of the implementations match + a 5 stars rating to a range of 0-255 for MP3. + Match it to baloo rating with a range of 0 - 10 */ + lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["POPM"]; + if (!lstID3v2.isEmpty()) { + for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { + TagLib::ID3v2::PopularimeterFrame *ratingFrame = static_cast(*it); + int rating = ratingFrame->rating(); + if (rating == 0) { + data.rating = 0; + } else if (rating == 1) { + TagLib::String ratingProvider = ratingFrame->email(); + if (ratingProvider == "no@email" || ratingProvider == "org.kde.kfilemetadata") { + data.rating = 1; + } else { + data.rating = 2; + } + } else if (rating >= 1 && rating <= 255) { + data.rating = static_cast(0.032 * rating + 2); + } + } + } //TODO handle TIPL tag } @@ -250,6 +274,15 @@ if (itComposers != allTags.end()) { data.composers = itComposers->second.toStringList().toString(", "); } + + /* There is no standard regarding ratings. Mimic MediaMonkey's behavior + with a range of 0 to 100 (stored in steps of 10) and make it compatible + with baloo rating with a range from 0 to 10 */ + TagLib::MP4::ItemListMap::Iterator itRating = allTags.find("rate"); + if (itRating != allTags.end()) { + data.rating = itRating->second.toStringList().toString().toInt() / 10; + } + } void TagLibExtractor::extractMusePack(TagLib::FileStream& stream, ExtractedData& data) @@ -407,6 +440,15 @@ if (itMPC != lstMusepack.end()) { data.opus = (*itMPC).second.toString().toInt(); } + + // Rating. + itMPC = lstMusepack.find("RATING"); + if (itMPC != lstMusepack.end()) { + /* There is no standard regarding ratings. There is one implementation + most seem to follow with a range of 0 to 100 (stored in steps of 10). + Make it compatible with baloo rating with a range from 0 to 10 */ + data.rating = (*itMPC).second.toString().toInt() / 10; + } } void TagLibExtractor::extractOgg(TagLib::FileStream& stream, const QString& mimeType, ExtractedData& data) @@ -584,6 +626,15 @@ if (itOgg != lstOgg.end()) { data.opus = (*itOgg).second.toString("").toInt(); } + + // Rating. + itOgg = lstOgg.find("RATING"); + if (itOgg != lstOgg.end()) { + //there is no standard regarding ratings. There is one implementation + //most seem to follow with a range of 0 to 100 (stored in steps of 10). + //make it compatible with baloo rating with a range from 0 to 10 + data.rating = (*itOgg).second.toString("").toInt() / 10; + } } } @@ -773,6 +824,10 @@ result->add(Property::DiscNumber, data.discNumber); } + if (data.rating.isValid()) { + result->add(Property::Rating, data.rating); + } + TagLib::AudioProperties* audioProp = file.audioProperties(); if (audioProp) { if (audioProp->length()) { diff --git a/src/properties.h b/src/properties.h --- a/src/properties.h +++ b/src/properties.h @@ -316,6 +316,11 @@ */ License, + /** + * For ratings stored in Metadata tags + */ + Rating, + PropertyCount, LastProperty = PropertyCount-1, diff --git a/src/propertyinfo.cpp b/src/propertyinfo.cpp --- a/src/propertyinfo.cpp +++ b/src/propertyinfo.cpp @@ -413,6 +413,12 @@ d->valueType = QVariant::Int; break; + case Property::Rating: + d->name = QStringLiteral("embeddedRating"); + d->displayName = i18nc("@label", "Rating"); + d->valueType = QVariant::Int; + break; + case Property::Width: d->name = QStringLiteral("width"); d->displayName = i18nc("@label", "Width"); @@ -580,6 +586,7 @@ { QStringLiteral("arranger"), Property::Arranger }, { QStringLiteral("conductor"), Property::Conductor }, { QStringLiteral("opus"), Property::Opus }, + { QStringLiteral("embeddedrating"), Property::Rating }, { QStringLiteral("author"), Property::Author }, { QStringLiteral("title"), Property::Title }, { QStringLiteral("subject"), Property::Subject },