diff --git a/autotests/taglibwritertest.h b/autotests/taglibwritertest.h --- a/autotests/taglibwritertest.h +++ b/autotests/taglibwritertest.h @@ -34,6 +34,8 @@ void testCommonData_data(); void testExtendedData(); void testExtendedData_data(); + void testRating(); + void testRating_data(); }; #endif // TAGLIBWRITERTEST_H diff --git a/autotests/taglibwritertest.cpp b/autotests/taglibwritertest.cpp --- a/autotests/taglibwritertest.cpp +++ b/autotests/taglibwritertest.cpp @@ -343,4 +343,205 @@ ; } +void TagLibWriterTest::testRating() +{ + QFETCH(QString, fileType); + QFETCH(QString, mimeType); + QFETCH(int, rating); + + QString temporaryFileName = QStringLiteral("writertest.") + fileType; + + QFile::copy(testFilePath("test.") + fileType, testFilePath(temporaryFileName)); + TagLibWriter writerPlugin{this}; + QCOMPARE(writerPlugin.writeMimetypes().contains(mimeType),true); + + WriteData data(testFilePath(temporaryFileName), mimeType); + + data.add(Property::Rating, rating); + writerPlugin.write(data); + + KFileMetaData::ExtractorCollection extractors; + QList extractorList = extractors.fetchExtractors(mimeType); + if (extractorList.isEmpty()) + QFAIL("This mime type is not supported by the extractor. Likely a newer KDE Frameworks version is required."); + KFileMetaData::Extractor* ex = extractorList.first(); + KFileMetaData::SimpleExtractionResult result(testFilePath(temporaryFileName), mimeType, + KFileMetaData::ExtractionResult::ExtractMetaData); + + ex->extract(&result); + QCOMPARE(result.properties().value(Property::Rating).toInt(), rating); + + QFile::remove(testFilePath(temporaryFileName)); +} + +void TagLibWriterTest::testRating_data() +{ + + QTest::addColumn("fileType"); + QTest::addColumn("mimeType"); + QTest::addColumn("rating"); + + QTest::addRow("aiff") + << QStringLiteral("aif") + << QStringLiteral("audio/x-aiff") + << 3 + ; + + QTest::addRow("ape") + << QStringLiteral("ape") + << QStringLiteral("audio/x-ape") + << 1 + ; + + QTest::addRow("flac") + << QStringLiteral("flac") + << QStringLiteral("audio/flac") + << 3 + ; + + QTest::addRow("m4a") + << QStringLiteral("m4a") + << QStringLiteral("audio/mp4") + << 5 + ; + + QTest::addRow("mp3_0") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 0 + ; + + QTest::addRow("mp3_1") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 1 + ; + + QTest::addRow("mp3_2") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 2 + ; + + QTest::addRow("mp3_3") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 3 + ; + + QTest::addRow("mp3_4") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 4 + ; + + QTest::addRow("mp3_5") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 5 + ; + + QTest::addRow("mp3_6") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 6 + ; + + QTest::addRow("mp3_7") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 7 + ; + + QTest::addRow("mp3_8") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 8 + ; + + QTest::addRow("mp3_9") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 9 + ; + + QTest::addRow("mp3_10") + << QStringLiteral("mp3") + << QStringLiteral("audio/mpeg3") + << 10 + ; + + QTest::addRow("mpc") + << QStringLiteral("mpc") + << QStringLiteral("audio/x-musepack") + << 7 + ; + + QTest::addRow("opus") + << QStringLiteral("opus") + << QStringLiteral("audio/opus") + << 6 + ; + + QTest::addRow("speex") + << QStringLiteral("spx") + << QStringLiteral("audio/speex") + << 8 + ; + + QTest::addRow("wav") + << QStringLiteral("wav") + << QStringLiteral("audio/wav") + << 4 + ; + + QTest::addRow("wavpack") + << QStringLiteral("wv") + << QStringLiteral("audio/x-wavpack") + << 9 + ; + + QTest::addRow("wma_0") + << QStringLiteral("wma") + << QStringLiteral("audio/x-ms-wma") + << 0 + ; + + QTest::addRow("wma_2") + << QStringLiteral("wma") + << QStringLiteral("audio/x-ms-wma") + << 2 + ; + + QTest::addRow("wma_4") + << QStringLiteral("wma") + << QStringLiteral("audio/x-ms-wma") + << 4 + ; + + QTest::addRow("wma_5") + << QStringLiteral("wma") + << QStringLiteral("audio/x-ms-wma") + << 5 + ; + + QTest::addRow("wma_6") + << QStringLiteral("wma") + << QStringLiteral("audio/x-ms-wma") + << 6 + ; + + QTest::addRow("wma_8") + << QStringLiteral("wma") + << QStringLiteral("audio/x-ms-wma") + << 8 + ; + + QTest::addRow("wma_10") + << QStringLiteral("wma") + << QStringLiteral("audio/x-ms-wma") + << 10 + ; +} + QTEST_GUILESS_MAIN(TagLibWriterTest) diff --git a/src/writers/taglibwriter.cpp b/src/writers/taglibwriter.cpp --- a/src/writers/taglibwriter.cpp +++ b/src/writers/taglibwriter.cpp @@ -27,16 +27,20 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include @@ -63,10 +67,69 @@ QStringLiteral("audio/x-wavpack"), }; -} +int id3v2RatingTranslation[11] = { + 0, 1, 13, 54, 64, 118, 128, 186, 196, 242, 255 +}; + using namespace KFileMetaData; +void writeID3v2Tags(TagLib::ID3v2::Tag *id3Tags, const PropertyMap &newProperties) +{ + if (newProperties.contains(Property::Rating)) { + int rating = newProperties.value(Property::Rating).toInt(); + if (rating >= 0 && rating <= 10) { + id3Tags->removeFrames("POPM"); + auto ratingFrame = new TagLib::ID3v2::PopularimeterFrame; + ratingFrame->setEmail("org.kde.kfilemetadata"); + ratingFrame->setRating(id3v2RatingTranslation[rating]); + id3Tags->addFrame(ratingFrame); + } + } +} + +void writeApeTags(TagLib::PropertyMap &oldProperties, const PropertyMap &newProperties) +{ + if (newProperties.contains(Property::Rating)) { + oldProperties.replace("RATING", TagLib::String::number(newProperties.value(Property::Rating).toInt() * 10)); + } +} + +void writeVorbisTags(TagLib::PropertyMap &oldProperties, const PropertyMap &newProperties) +{ + if (newProperties.contains(Property::Rating)) { + oldProperties.replace("RATING", TagLib::String::number(newProperties.value(Property::Rating).toInt() * 10)); + } +} + +void writeAsfTags(TagLib::ASF::Tag *asfTags, const PropertyMap &properties) +{ + if (properties.contains(Property::Rating)) { + //map the rating values of WMP to Baloo rating + //0->0, 1->2, 4->25, 6->50, 8->75, 10->99 + int rating = properties.value(Property::Rating).toInt(); + if (rating == 0) { + rating = 0; + } else if (rating <= 2) { + rating = 1; + } else if (rating == 10){ + rating = 99; + } else { + rating = 12.5 * rating - 25; + } + asfTags->setAttribute("WM/SharedUserRating", TagLib::String::number(rating)); + } +} + +void writeMp4Tags(TagLib::MP4::Tag *mp4Tags, const PropertyMap &newProperties) +{ + if (newProperties.contains(Property::Rating)) { + mp4Tags->setItem("rate", TagLib::StringList(TagLib::String::number(newProperties.value(Property::Rating).toInt() * 10))); + } +} + +} // anonymous namespace + void writeGenericProperties(TagLib::PropertyMap &oldProperties, const PropertyMap &newProperties) { if (newProperties.contains(Property::Title)) { @@ -171,85 +234,107 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); file.setProperties(savedProperties); + if (file.hasID3v2Tag()) { + writeID3v2Tags(file.ID3v2Tag(), properties); + } file.save(); } } else if (mimeType == QLatin1String("audio/x-aiff")) { TagLib::RIFF::AIFF::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); file.setProperties(savedProperties); + auto id3Tags = dynamic_cast(file.tag()); + if (id3Tags) { + writeID3v2Tags(id3Tags, properties); + } file.save(); } } else if ((mimeType == QLatin1String("audio/wav")) || (mimeType == QLatin1String("audio/x-wav"))) { TagLib::RIFF::WAV::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); file.setProperties(savedProperties); + auto id3Tags = dynamic_cast(file.tag()); + if (id3Tags) { + writeID3v2Tags(id3Tags, properties); + } file.save(); } } else if (mimeType == QLatin1String("audio/x-musepack")) { TagLib::MPC::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + writeApeTags(savedProperties, properties); file.setProperties(savedProperties); file.save(); } } else if (mimeType == QLatin1String("audio/x-ape")) { TagLib::APE::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + writeApeTags(savedProperties, properties); file.setProperties(savedProperties); file.save(); } } else if (mimeType == QLatin1String("audio/x-wavpack")) { TagLib::WavPack::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + writeApeTags(savedProperties, properties); file.setProperties(savedProperties); file.save(); } } else if (mimeType == QLatin1String("audio/mp4")) { TagLib::MP4::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + auto mp4Tags = dynamic_cast(file.tag()); + if (mp4Tags) { + writeMp4Tags(mp4Tags, properties); + } file.setProperties(savedProperties); file.save(); } } else if (mimeType == QLatin1String("audio/flac")) { TagLib::FLAC::File file(&stream, TagLib::ID3v2::FrameFactory::instance(), false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + writeVorbisTags(savedProperties, properties); file.setProperties(savedProperties); file.save(); } } else if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { TagLib::Ogg::Vorbis::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + writeVorbisTags(savedProperties, properties); file.setProperties(savedProperties); file.save(); } } else if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { TagLib::Ogg::Opus::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + writeVorbisTags(savedProperties, properties); file.setProperties(savedProperties); file.save(); } } else if (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex+ogg")) { TagLib::Ogg::Speex::File file(&stream, false); if (file.isValid()) { auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); + writeVorbisTags(savedProperties, properties); file.setProperties(savedProperties); file.save(); } @@ -259,6 +344,10 @@ auto savedProperties = file.properties(); writeGenericProperties(savedProperties, properties); file.setProperties(savedProperties); + auto asfTags = dynamic_cast(file.tag()); + if (asfTags){ + writeAsfTags(asfTags, properties); + } file.save(); } }