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 @@ -37,8 +37,10 @@ #include #include #include +#include #include +#include namespace { @@ -63,7 +65,11 @@ QStringLiteral("audio/x-wavpack"), }; -TagLib::File* openFile(TagLib::IOStream* stream, QString mimeType) +QHash id3v2RatingTranslation = { + {0,0},{1,1},{2,13},{3,54},{4,64},{5,118},{6,128},{7,186},{8,196},{9,242},{10,255} +}; + +TagLib::File* openFile(TagLib::IOStream *stream, QString mimeType) { if ((mimeType == QLatin1String("audio/mpeg")) || (mimeType == QLatin1String("audio/mpeg3")) || (mimeType == QLatin1String("audio/x-mpeg"))) { @@ -98,6 +104,82 @@ using namespace KFileMetaData; +void writeID3v2Tags(TagLib::File *file, QString mimeType, const PropertyMap &properties) +{ + TagLib::ID3v2::Tag *id3Tags; + if ((mimeType == QLatin1String("audio/mpeg")) + || (mimeType == QLatin1String("audio/mpeg3")) + || (mimeType == QLatin1String("audio/x-mpeg"))) + { + auto mpegFile = dynamic_cast(file); + id3Tags = mpegFile->ID3v2Tag(true); + } else { + id3Tags = dynamic_cast(file->tag()); + } + if (!id3Tags) { + return; + } + + if (properties.contains(Property::Rating)) { + int rating = properties.value(Property::Rating).toInt(); + id3Tags->removeFrames("POPM"); + auto ratingFrame = new TagLib::ID3v2::PopularimeterFrame; + ratingFrame->setEmail("org.kde.kfilemetadata"); + ratingFrame->setRating(id3v2RatingTranslation.value(rating)); + id3Tags->addFrame(ratingFrame); + } +} + +void writeVorbisAndApeTags(TagLib::PropertyMap &savedProperties, const PropertyMap &properties) +{ + if (properties.contains(Property::Rating)) { + savedProperties.replace("RATING", TagLib::String::number(properties.value(Property::Rating).toInt() * 10)); + } +} + +void writeAsfTags(TagLib::File *file, const PropertyMap &properties) +{ + auto asfFile = dynamic_cast(file); + if (!asfFile) { + return; + } + auto asfTags = asfFile->tag(); + if (!asfTags){ + return; + } + + if (properties.contains(Property::Rating)) { + //map the rating values of WMP to Baloo rating + //0->0, 1->2, 25->4, 50->6, 75->8, 99->10 + 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::File *file, const PropertyMap &properties) +{ + auto mp4File = dynamic_cast(file); + if (!mp4File) { + return; + } + auto mp4Tags = mp4File->tag(); + if (!mp4Tags) { + return; + } + if (properties.contains(Property::Rating)) { + mp4Tags->setItem("rate", TagLib::StringList(TagLib::String::number(properties.value(Property::Rating).toInt() * 10))); + } +} + TagLibWriter::TagLibWriter(QObject* parent) : WriterPlugin(parent) { @@ -199,8 +281,23 @@ savedProperties.replace("LANGUAGE", QStringToTString(properties.value(Property::Language).toString())); } - file->setProperties(savedProperties); + if ((mimeType == QLatin1String("audio/mpeg")) || (mimeType == QLatin1String("audio/mpeg3")) + || (mimeType == QLatin1String("audio/x-mpeg")) || (mimeType == QLatin1String("audio/x-aiff")) + || (mimeType == QLatin1String("audio/wav")) || (mimeType == QLatin1String("audio/x-wav"))) { + writeID3v2Tags(file, mimeType, properties); + } else if (mimeType == QLatin1String("audio/mp4")) { + writeMp4Tags(file, properties); + } else if ((mimeType == QLatin1String("audio/x-musepack")) || (mimeType == QLatin1String("audio/x-ape")) + || (mimeType == QLatin1String("audio/x-wavpack")) || (mimeType == QLatin1String("audio/flac")) + || (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) + || (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) + || (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex"))) { + writeVorbisAndApeTags(savedProperties, properties); + } else if (mimeType == QLatin1String("audio/x-ms-wma")) { + writeAsfTags(file, properties); + } + file->setProperties(savedProperties); file->save(); delete file; }