Added support for explcit tags (rtng, ITUNESADVISORY)

This commit is contained in:
emeric
2025-01-17 18:03:16 +01:00
parent dd93a3f6c7
commit c7db5fc61a
15 changed files with 181 additions and 13 deletions
@@ -32,6 +32,7 @@ namespace lms::metadata
// Mapping to internal avformat names and/or common alternative custom names
static const std::unordered_map<TagType, std::vector<std::string>> tagMapping{
{ TagType::AcoustID, { "ACOUSTID_ID", "ACOUSTID ID" } },
{ TagType::Advisory, { "ITUNESADVISORY" } },
{ TagType::Album, { "ALBUM", "TALB", "WM/ALBUMTITLE" } },
{ TagType::AlbumArtist, { "ALBUMARTIST", "ALBUM_ARTIST" } },
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT", "TSO2" } },
+2 -1
View File
@@ -26,12 +26,13 @@
namespace lms::metadata
{
// using picard internal names
// prefer using picard internal names
// see https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html
enum class TagType
{
AcoustID,
AcoustIDFingerprint,
Advisory, // non standard
Album,
AlbumArtist,
AlbumArtists, // non standard
+20
View File
@@ -246,6 +246,25 @@ namespace lms::metadata
return artistDisplayName;
}
std::optional<Track::Advisory> getAdvisory(const ITagReader& tagReader)
{
if (const auto value{ getTagValueAs<int>(tagReader, TagType::Advisory) })
{
switch (*value)
{
case 1:
case 4:
return Track::Advisory::Explicit;
case 2:
return Track::Advisory::Clean;
case 0:
return Track::Advisory::Unknown;
}
}
return std::nullopt;
}
} // namespace
std::unique_ptr<IParser> createParser(ParserBackend parserBackend, ParserReadStyle parserReadStyle)
@@ -370,6 +389,7 @@ namespace lms::metadata
track.originalYear = utils::parseYear(*dateStr);
}
track.advisory = getAdvisory(tagReader);
track.lyrics = getLyrics(tagReader); // no custom delimiter on lyrics
track.comments = getTagValuesAs<std::string>(tagReader, TagType::Comment, {} /* no custom delimiter on comments */);
track.copyright = getTagValueAs<std::string>(tagReader, TagType::Copyright).value_or("");
+13 -4
View File
@@ -59,6 +59,7 @@ namespace lms::metadata
// Mapping to internal taglib names and/or common alternative custom names
const std::unordered_map<TagType, std::vector<std::string>> tagMapping{
{ TagType::AcoustID, { "ACOUSTID_ID", "ACOUSTID ID" } },
{ TagType::Advisory, { "ITUNESADVISORY" } },
{ TagType::Album, { "ALBUM" } },
{ TagType::AlbumArtist, { "ALBUMARTIST" } },
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT" } },
@@ -319,10 +320,18 @@ namespace lms::metadata
// MP4
else if (TagLib::MP4::File * mp4File{ dynamic_cast<TagLib::MP4::File*>(_file.file()) })
{
TagLib::MP4::Item coverItem{ mp4File->tag()->item("covr") };
TagLib::MP4::CoverArtList coverArtList{ coverItem.toCoverArtList() };
if (!coverArtList.isEmpty())
_hasEmbeddedCover = true;
if (const TagLib::MP4::Item coverItem{ mp4File->tag()->item("covr") }; coverItem.isValid())
{
if (coverItem.type() == TagLib::MP4::Item::Type::CoverArtList)
_hasEmbeddedCover = true;
}
// Taglib does not expose rtng in properties
if (const TagLib::MP4::Item rtngItem{ mp4File->tag()->item("rtng") }; rtngItem.isValid())
{
if (rtngItem.type() == TagLib::MP4::Item::Type::Byte)
_propertyMap["ITUNESADVISORY"] = TagLib::String{ std::to_string(rtngItem.toByte()) };
}
if (!_propertyMap.contains("ORIGINALDATE"))
{
@@ -102,6 +102,12 @@ namespace lms::metadata
struct Track
{
enum class Advisory
{
Unknown,
Explicit,
Clean,
};
AudioProperties audioProperties;
std::optional<core::UUID> mbid;
std::optional<core::UUID> recordingMBID;
@@ -117,6 +123,7 @@ namespace lms::metadata
Wt::WDate date;
std::optional<int> originalYear{};
Wt::WDate originalDate;
std::optional<Advisory> advisory;
bool hasCover{};
std::optional<core::UUID> acoustID;
std::string copyright;
+30
View File
@@ -33,6 +33,7 @@ namespace lms::metadata
TestTagReader testTags{
{
{ TagType::AcoustID, { "e987a441-e134-4960-8019-274eddacc418" } },
{ TagType::Advisory, { "2" } },
{ TagType::Album, { "MyAlbum" } },
{ TagType::AlbumSortOrder, { "MyAlbumSortName" } },
{ TagType::Artist, { "MyArtist1 & MyArtist2" } },
@@ -99,6 +100,8 @@ namespace lms::metadata
}
EXPECT_EQ(track->acoustID, core::UUID::fromString("e987a441-e134-4960-8019-274eddacc418"));
ASSERT_TRUE(track->advisory.has_value());
EXPECT_EQ(track->advisory.value(), Track::Advisory::Clean);
EXPECT_EQ(track->artistDisplayName, "MyArtist1 & MyArtist2");
ASSERT_EQ(track->artists.size(), 2);
EXPECT_EQ(track->artists[0].name, "MyArtist1");
@@ -581,4 +584,31 @@ namespace lms::metadata
EXPECT_EQ(track->artists[1].mbid, std::nullopt);
EXPECT_EQ(track->artistDisplayName, "Artist1, Artist2"); // reconstruct the artist display name
}
TEST(Parser, advisory)
{
auto doTest = [](std::string_view value, std::optional<Track::Advisory> expectedValue) {
const TestTagReader testTags{
{
{ TagType::Advisory, { value } },
}
};
Parser parser;
std::unique_ptr<Track> track{ Parser{}.parse(testTags) };
ASSERT_EQ(track->advisory.has_value(), expectedValue.has_value()) << "Value = '" << value << "'";
if (track->advisory.has_value())
{
EXPECT_EQ(track->advisory.value(), expectedValue);
}
};
doTest("0", Track::Advisory::Unknown);
doTest("1", Track::Advisory::Explicit);
doTest("4", Track::Advisory::Explicit);
doTest("2", Track::Advisory::Clean);
doTest("", std::nullopt);
doTest("3", std::nullopt);
}
} // namespace lms::metadata