Added support for release type in metadata parser
This commit is contained in:
@@ -225,6 +225,13 @@ getRelease(const TagMap& tags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
release->primaryType = getPropertyValueFirstMatchAs<MetaData::Release::PrimaryType>(tags, {"MUSICBRAINZ_ALBUMTYPE", "RELEASETYPE", "MUSICBRAINZ ALBUM TYPE", "MUSICBRAINZ/ALBUM TYPE"});
|
||||||
|
if (release->primaryType)
|
||||||
|
{
|
||||||
|
const auto secondaryTypes {getPropertyValuesFirstMatchAs<MetaData::Release::SecondaryType>(tags, {"MUSICBRAINZ_ALBUMTYPE", "RELEASETYPE", "MUSICBRAINZ ALBUM TYPE", "MUSICBRAINZ/ALBUM TYPE"})};
|
||||||
|
release->secondaryTypes.assign(std::cbegin(secondaryTypes), std::cend(secondaryTypes));
|
||||||
|
}
|
||||||
|
|
||||||
return release;
|
return release;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +452,7 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
|
|||||||
|
|
||||||
for (const auto& [name, attributeList] : tag->attributeListMap())
|
for (const auto& [name, attributeList] : tag->attributeListMap())
|
||||||
{
|
{
|
||||||
std::string strName {name.to8Bit(true)};
|
std::string strName {StringUtils::stringToUpper(name.to8Bit(true))};
|
||||||
if (strName.find("WM/") == 0 || tags.find(strName) != std::cend(tags))
|
if (strName.find("WM/") == 0 || tags.find(strName) != std::cend(tags))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -461,7 +468,7 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
|
|||||||
if (debug)
|
if (debug)
|
||||||
std::cout << "ASF property: '" << name << "'" << std::endl;
|
std::cout << "ASF property: '" << name << "'" << std::endl;
|
||||||
|
|
||||||
tags[strName] = std::move(attributes);
|
tags.emplace(strName, std::move(attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,3 +119,48 @@ namespace MetaData::Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace StringUtils
|
||||||
|
{
|
||||||
|
static bool iequals(std::string_view a, std::string_view b)
|
||||||
|
{
|
||||||
|
return std::equal(std::cbegin(a), std::cend(a),
|
||||||
|
std::cbegin(b), std::cend(b),
|
||||||
|
[](char a, char b) { return tolower(a) == tolower(b);}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::optional<MetaData::Release::PrimaryType> readAs(std::string_view str)
|
||||||
|
{
|
||||||
|
str = stringTrim(str);
|
||||||
|
|
||||||
|
if (iequals(str, "album"))
|
||||||
|
return MetaData::Release::PrimaryType::Album;
|
||||||
|
else if (iequals(str, "single"))
|
||||||
|
return MetaData::Release::PrimaryType::Single;
|
||||||
|
else if (iequals(str, "EP"))
|
||||||
|
return MetaData::Release::PrimaryType::EP;
|
||||||
|
else if (iequals(str, "broadcast"))
|
||||||
|
return MetaData::Release::PrimaryType::Broadcast;
|
||||||
|
else if (iequals(str, "other"))
|
||||||
|
return MetaData::Release::PrimaryType::Other;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::optional<MetaData::Release::SecondaryType> readAs(std::string_view str)
|
||||||
|
{
|
||||||
|
str = stringTrim(str);
|
||||||
|
|
||||||
|
if (iequals(str, "compilation"))
|
||||||
|
return MetaData::Release::SecondaryType::Compilation;
|
||||||
|
else if (iequals(str, "soundtrack"))
|
||||||
|
return MetaData::Release::SecondaryType::Soundtrack;
|
||||||
|
else if (iequals(str, "live"))
|
||||||
|
return MetaData::Release::SecondaryType::Live;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ namespace MetaData::Utils
|
|||||||
|
|
||||||
// format is "artist name (role)"
|
// format is "artist name (role)"
|
||||||
PerformerArtist extractPerformerAndRole(std::string_view entry);
|
PerformerArtist extractPerformerAndRole(std::string_view entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace StringUtils
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
std::optional<MetaData::Release::PrimaryType> readAs(std::string_view str);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::optional<MetaData::Release::SecondaryType> readAs(std::string_view str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <Wt/WDate.h>
|
#include <Wt/WDate.h>
|
||||||
|
#include "utils/EnumSet.hpp"
|
||||||
#include "utils/UUID.hpp"
|
#include "utils/UUID.hpp"
|
||||||
|
|
||||||
namespace MetaData
|
namespace MetaData
|
||||||
@@ -51,10 +52,37 @@ namespace MetaData
|
|||||||
|
|
||||||
struct Release
|
struct Release
|
||||||
{
|
{
|
||||||
|
// see https://musicbrainz.org/doc/Release_Group/Type
|
||||||
|
enum class PrimaryType
|
||||||
|
{
|
||||||
|
Album,
|
||||||
|
Single,
|
||||||
|
EP,
|
||||||
|
Broadcast,
|
||||||
|
Other
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SecondaryType
|
||||||
|
{
|
||||||
|
Compilation,
|
||||||
|
Soundtrack,
|
||||||
|
Spokenword,
|
||||||
|
Interview,
|
||||||
|
Audiobook,
|
||||||
|
AudioDrama,
|
||||||
|
Live,
|
||||||
|
Remix,
|
||||||
|
DJMix,
|
||||||
|
Mixtape_Street,
|
||||||
|
Demo,
|
||||||
|
};
|
||||||
|
|
||||||
std::optional<UUID> mbid;
|
std::optional<UUID> mbid;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<Artist> artists;
|
std::vector<Artist> artists;
|
||||||
std::optional<std::size_t> mediumCount;
|
std::optional<std::size_t> mediumCount;
|
||||||
|
std::optional<PrimaryType> primaryType;
|
||||||
|
EnumSet<SecondaryType> secondaryTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Medium
|
struct Medium
|
||||||
|
|||||||
@@ -115,3 +115,60 @@ TEST(MetaData, extractPerformerAndRole)
|
|||||||
EXPECT_EQ(performer.role, testCase.expectedRole) << " str was '" << testCase.str << "'";
|
EXPECT_EQ(performer.role, testCase.expectedRole) << " str was '" << testCase.str << "'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(MetaData, primaryReleaseTypes)
|
||||||
|
{
|
||||||
|
using namespace MetaData;
|
||||||
|
|
||||||
|
struct TestCase
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
std::optional<Release::PrimaryType> result;
|
||||||
|
} testCases []
|
||||||
|
{
|
||||||
|
{ "", std::nullopt },
|
||||||
|
{ "album", Release::PrimaryType::Album },
|
||||||
|
{ "Album", Release::PrimaryType::Album },
|
||||||
|
{ " Album", Release::PrimaryType::Album },
|
||||||
|
{ "Album ", Release::PrimaryType::Album },
|
||||||
|
{ "ep", Release::PrimaryType::EP },
|
||||||
|
{ " ep ", Release::PrimaryType::EP },
|
||||||
|
{ "broadcast", Release::PrimaryType::Broadcast },
|
||||||
|
{ "single", Release::PrimaryType::Single },
|
||||||
|
{ "other", Release::PrimaryType::Other },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const TestCase& testCase : testCases)
|
||||||
|
{
|
||||||
|
std::optional<Release::PrimaryType> parsed {StringUtils::readAs<Release::PrimaryType>(testCase.str)};
|
||||||
|
|
||||||
|
EXPECT_EQ(parsed, testCase.result) << " str was '" << testCase.str << "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MetaData, secondaryReleaseTypes)
|
||||||
|
{
|
||||||
|
using namespace MetaData;
|
||||||
|
|
||||||
|
struct TestCase
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
std::optional<Release::SecondaryType> result;
|
||||||
|
} testCases []
|
||||||
|
{
|
||||||
|
{ "", std::nullopt },
|
||||||
|
{ "compilation", Release::SecondaryType::Compilation },
|
||||||
|
{ " compilation ", Release::SecondaryType::Compilation },
|
||||||
|
{ "soundtrack", Release::SecondaryType::Soundtrack },
|
||||||
|
{ "live", Release::SecondaryType::Live },
|
||||||
|
{ "demo", Release::SecondaryType::Demo },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const TestCase& testCase : testCases)
|
||||||
|
{
|
||||||
|
std::optional<Release::SecondaryType> parsed {StringUtils::readAs<Release::SecondaryType>(testCase.str)};
|
||||||
|
|
||||||
|
EXPECT_EQ(parsed, testCase.result) << " str was '" << testCase.str << "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ class EnumSet
|
|||||||
template <typename It>
|
template <typename It>
|
||||||
constexpr EnumSet(It begin, It end)
|
constexpr EnumSet(It begin, It end)
|
||||||
{
|
{
|
||||||
|
assign(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename It>
|
||||||
|
constexpr void assign(It begin, It end)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
for (It it {begin}; it != end; ++it)
|
for (It it {begin}; it != end; ++it)
|
||||||
insert(*it);
|
insert(*it);
|
||||||
}
|
}
|
||||||
@@ -71,6 +78,11 @@ class EnumSet
|
|||||||
return _bitfield & (underlying_type{ 1 } << static_cast<underlying_type>(value));
|
return _bitfield & (underlying_type{ 1 } << static_cast<underlying_type>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void clear()
|
||||||
|
{
|
||||||
|
_bitfield = 0;
|
||||||
|
}
|
||||||
|
|
||||||
class iterator
|
class iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -44,6 +44,46 @@ operator<<(std::ostream& os, const MetaData::Artist& artist)
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os, MetaData::Release::PrimaryType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MetaData::Release::PrimaryType::Album: os << "Album"; break;
|
||||||
|
case MetaData::Release::PrimaryType::Single: os << "Single"; break;
|
||||||
|
case MetaData::Release::PrimaryType::EP: os << "EP"; break;
|
||||||
|
case MetaData::Release::PrimaryType::Broadcast: os << "Broadcast"; break;
|
||||||
|
case MetaData::Release::PrimaryType::Other: os << "Other"; break;
|
||||||
|
default:
|
||||||
|
os << "??";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os, MetaData::Release::SecondaryType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MetaData::Release::SecondaryType::Compilation: os << "Compilation"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Soundtrack: os << "Soundtrack"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Spokenword: os << "Spokenword"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Interview: os << "Interview"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Audiobook: os << "Audiobook"; break;
|
||||||
|
case MetaData::Release::SecondaryType::AudioDrama: os << "Audio drama"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Live: os << "Live"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Remix: os << "Remix"; break;
|
||||||
|
case MetaData::Release::SecondaryType::DJMix: os << "DJ-mix"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Mixtape_Street: os << "Mixtape/Street"; break;
|
||||||
|
case MetaData::Release::SecondaryType::Demo: os << "Mixtape/Demo"; break;
|
||||||
|
default:
|
||||||
|
os << "??";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
std::ostream&
|
std::ostream&
|
||||||
operator<<(std::ostream& os, const MetaData::Release& release)
|
operator<<(std::ostream& os, const MetaData::Release& release)
|
||||||
@@ -59,6 +99,13 @@ operator<<(std::ostream& os, const MetaData::Release& release)
|
|||||||
for (const MetaData::Artist& artist : release.artists)
|
for (const MetaData::Artist& artist : release.artists)
|
||||||
std::cout << "\tRelease artist: " << artist << std::endl;
|
std::cout << "\tRelease artist: " << artist << std::endl;
|
||||||
|
|
||||||
|
if (release.primaryType)
|
||||||
|
{
|
||||||
|
std::cout << "\tPrimary type: " << *release.primaryType << std::endl;
|
||||||
|
for (MetaData::Release::SecondaryType type : release.secondaryTypes)
|
||||||
|
std::cout << "\tSecondary type:" << type << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user