diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 61ef74b6..35ed3989 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -253,11 +253,7 @@ namespace lms::ui { Wt::WTemplate* releaseContainer{ releaseContainers->addNew(Wt::WString::tr("Lms.Explore.Artist.template.release-container")) }; - if (releaseType.primaryType || !releaseType.customTypes.empty()) - releaseContainer->bindString("release-type", releaseHelpers::buildReleaseTypeString(releaseType)); - else - releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.releases")); // fallback when not tagged with MB or custom type - + releaseContainer->bindString("release-type", releaseHelpers::buildReleaseTypeString(releaseType)); releases.container = releaseContainer->bindNew("releases", Wt::WString::tr("Lms.Explore.Releases.template.container")); releases.container->onRequestElements.connect(this, [this, &releases = releases] { addSomeReleases(releases); diff --git a/src/lms/ui/explore/ReleaseHelpers.cpp b/src/lms/ui/explore/ReleaseHelpers.cpp index 042cedd2..7c3a3327 100644 --- a/src/lms/ui/explore/ReleaseHelpers.cpp +++ b/src/lms/ui/explore/ReleaseHelpers.cpp @@ -24,10 +24,13 @@ #include #include "core/EnumSet.hpp" +#include "core/Utils.hpp" + #include "database/objects/Artist.hpp" #include "database/objects/Release.hpp" #include "Utils.hpp" +#include "explore/ReleaseTypes.hpp" namespace lms::ui::releaseListHelpers { @@ -110,85 +113,106 @@ namespace lms::ui::releaseListHelpers namespace lms::ui::releaseHelpers { + namespace + { + static const Wt::WString releaseTypeSeparator{ " · " }; + + Wt::WString buildPicardReleaseTypeString(const PicardReleaseType& releaseType) + { + Wt::WString res; + + switch (releaseType.primaryType) + { + case PicardReleaseType::PrimaryType::Album: + res = Wt::WString::tr("Lms.Explore.Release.type-primary-album"); + break; + case PicardReleaseType::PrimaryType::Broadcast: + res = Wt::WString::tr("Lms.Explore.Release.type-primary-broadcast"); + break; + case PicardReleaseType::PrimaryType::EP: + res = Wt::WString::tr("Lms.Explore.Release.type-primary-ep"); + break; + case PicardReleaseType::PrimaryType::Single: + res = Wt::WString::tr("Lms.Explore.Release.type-primary-single"); + break; + case PicardReleaseType::PrimaryType::Other: + res = Wt::WString::tr("Lms.Explore.Release.type-primary-other"); + break; + } + + for (PicardReleaseType::SecondaryType secondaryType : releaseType.secondaryTypes) + { + if (!res.empty()) + res += releaseTypeSeparator; + + switch (secondaryType) + { + case PicardReleaseType::SecondaryType::Compilation: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-compilation"); + break; + case PicardReleaseType::SecondaryType::Spokenword: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-spokenword"); + break; + case PicardReleaseType::SecondaryType::Soundtrack: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-soundtrack"); + break; + case PicardReleaseType::SecondaryType::Interview: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-interview"); + break; + case PicardReleaseType::SecondaryType::Audiobook: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiobook"); + break; + case PicardReleaseType::SecondaryType::AudioDrama: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiodrama"); + break; + case PicardReleaseType::SecondaryType::Live: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-live"); + break; + case PicardReleaseType::SecondaryType::Remix: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-remix"); + break; + case PicardReleaseType::SecondaryType::DJMix: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-djmix"); + break; + case PicardReleaseType::SecondaryType::Mixtape_Street: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-mixtape-street"); + break; + case PicardReleaseType::SecondaryType::Demo: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-demo"); + break; + case PicardReleaseType::SecondaryType::FieldRecording: + res += Wt::WString::tr("Lms.Explore.Release.type-secondary-field-recording"); + break; + } + } + + return res; + } + + Wt::WString buildCustomReleaseTypeString(const CustomReleaseType& releaseType) + { + Wt::WString res; + + for (const std::string& customType : releaseType.types) + { + if (!res.empty()) + res += releaseTypeSeparator; + + res += customType; + } + + return res; + } + } // namespace + Wt::WString buildReleaseTypeString(const ReleaseType& releaseType) { Wt::WString res; - if (releaseType.primaryType) - { - switch (*releaseType.primaryType) - { - case PrimaryReleaseType::Album: - res = Wt::WString::tr("Lms.Explore.Release.type-primary-album"); - break; - case PrimaryReleaseType::Broadcast: - res = Wt::WString::tr("Lms.Explore.Release.type-primary-broadcast"); - break; - case PrimaryReleaseType::EP: - res = Wt::WString::tr("Lms.Explore.Release.type-primary-ep"); - break; - case PrimaryReleaseType::Single: - res = Wt::WString::tr("Lms.Explore.Release.type-primary-single"); - break; - case PrimaryReleaseType::Other: - res = Wt::WString::tr("Lms.Explore.Release.type-primary-other"); - break; - } - } - - for (SecondaryReleaseType secondaryType : releaseType.secondaryTypes) - { - if (!res.empty()) - res += Wt::WString{ " · " }; - - switch (secondaryType) - { - case SecondaryReleaseType::Compilation: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-compilation"); - break; - case SecondaryReleaseType::Spokenword: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-spokenword"); - break; - case SecondaryReleaseType::Soundtrack: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-soundtrack"); - break; - case SecondaryReleaseType::Interview: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-interview"); - break; - case SecondaryReleaseType::Audiobook: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiobook"); - break; - case SecondaryReleaseType::AudioDrama: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiodrama"); - break; - case SecondaryReleaseType::Live: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-live"); - break; - case SecondaryReleaseType::Remix: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-remix"); - break; - case SecondaryReleaseType::DJMix: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-djmix"); - break; - case SecondaryReleaseType::Mixtape_Street: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-mixtape-street"); - break; - case SecondaryReleaseType::Demo: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-demo"); - break; - case SecondaryReleaseType::FieldRecording: - res += Wt::WString::tr("Lms.Explore.Release.type-secondary-field-recording"); - break; - } - } - - for (const std::string& customType : releaseType.customTypes) - { - if (!res.empty()) - res += Wt::WString{ " · " }; - - res += customType; - } + std::visit(core::utils::overloads{ + [&](const PicardReleaseType& picardReleaseType) { res = buildPicardReleaseTypeString(picardReleaseType); }, + [&](const CustomReleaseType& customReleaseType) { res = buildCustomReleaseTypeString(customReleaseType); } }, + releaseType); return res; } diff --git a/src/lms/ui/explore/ReleaseTypes.cpp b/src/lms/ui/explore/ReleaseTypes.cpp index 6b54076a..12ef66c2 100644 --- a/src/lms/ui/explore/ReleaseTypes.cpp +++ b/src/lms/ui/explore/ReleaseTypes.cpp @@ -17,24 +17,24 @@ * along with LMS. If not, see . */ +#include "ReleaseTypes.hpp" + #include #include #include "core/String.hpp" -#include "ReleaseTypes.hpp" - namespace lms::core::stringUtils { template<> - std::optional readAs(std::string_view str) + std::optional readAs(std::string_view str) { - static const std::unordered_map entries{ - { "album", ui::PrimaryReleaseType::Album }, - { "single", ui::PrimaryReleaseType::Single }, - { "ep", ui::PrimaryReleaseType::EP }, - { "broadcast", ui::PrimaryReleaseType::Broadcast }, - { "other", ui::PrimaryReleaseType::Other }, + static const std::unordered_map entries{ + { "album", ui::PicardReleaseType::PrimaryType::Album }, + { "single", ui::PicardReleaseType::PrimaryType::Single }, + { "ep", ui::PicardReleaseType::PrimaryType::EP }, + { "broadcast", ui::PicardReleaseType::PrimaryType::Broadcast }, + { "other", ui::PicardReleaseType::PrimaryType::Other }, }; const auto it{ entries.find(stringToLower(stringTrim(str))) }; @@ -45,21 +45,21 @@ namespace lms::core::stringUtils } template<> - std::optional readAs(std::string_view str) + std::optional readAs(std::string_view str) { - static const std::unordered_map entries{ - { "compilation", ui::SecondaryReleaseType::Compilation }, - { "soundtrack", ui::SecondaryReleaseType::Soundtrack }, - { "spokenword", ui::SecondaryReleaseType::Spokenword }, - { "interview", ui::SecondaryReleaseType::Interview }, - { "audiobook", ui::SecondaryReleaseType::Audiobook }, - { "audio drama", ui::SecondaryReleaseType::AudioDrama }, - { "live", ui::SecondaryReleaseType::Live }, - { "remix", ui::SecondaryReleaseType::Remix }, - { "dj-mix", ui::SecondaryReleaseType::DJMix }, - { "mixtape/street", ui::SecondaryReleaseType::Mixtape_Street }, - { "demo", ui::SecondaryReleaseType::Demo }, - { "field recording", ui::SecondaryReleaseType::FieldRecording }, + static const std::unordered_map entries{ + { "compilation", ui::PicardReleaseType::SecondaryType::Compilation }, + { "soundtrack", ui::PicardReleaseType::SecondaryType::Soundtrack }, + { "spokenword", ui::PicardReleaseType::SecondaryType::Spokenword }, + { "interview", ui::PicardReleaseType::SecondaryType::Interview }, + { "audiobook", ui::PicardReleaseType::SecondaryType::Audiobook }, + { "audio drama", ui::PicardReleaseType::SecondaryType::AudioDrama }, + { "live", ui::PicardReleaseType::SecondaryType::Live }, + { "remix", ui::PicardReleaseType::SecondaryType::Remix }, + { "dj-mix", ui::PicardReleaseType::SecondaryType::DJMix }, + { "mixtape/street", ui::PicardReleaseType::SecondaryType::Mixtape_Street }, + { "demo", ui::PicardReleaseType::SecondaryType::Demo }, + { "field recording", ui::PicardReleaseType::SecondaryType::FieldRecording }, }; const auto it{ entries.find(stringToLower(stringTrim(str))) }; @@ -72,48 +72,48 @@ namespace lms::core::stringUtils namespace lms::ui { - ReleaseType parseReleaseType(const std::vector& releaseTypeNames) + std::optional parsePicardReleaseType(const std::vector& releaseTypeNames) { - ReleaseType res; + if (releaseTypeNames.empty()) + return std::nullopt; - for (std::string_view releaseTypeName : releaseTypeNames) + const auto primaryType{ core::stringUtils::readAs(releaseTypeNames[0]) }; + if (!primaryType) + return std::nullopt; + + PicardReleaseType res{ .primaryType = *primaryType, .secondaryTypes = {} }; + for (std::size_t i{ 1 }; i < releaseTypeNames.size(); ++i) { - if (auto primaryType{ core::stringUtils::readAs(releaseTypeName) }) - { - if (!res.primaryType) - res.primaryType = primaryType; - else - res.customTypes.push_back(std::string{ releaseTypeName }); - } - else if (auto secondaryType{ core::stringUtils::readAs(releaseTypeName) }) - { - res.secondaryTypes.insert(*secondaryType); - } - else - res.customTypes.push_back(std::string{ releaseTypeName }); + const auto secondaryType{ core::stringUtils::readAs(releaseTypeNames[i]) }; + if (!secondaryType) + return std::nullopt; + + res.secondaryTypes.insert(*secondaryType); } return res; } - bool operator<(std::optional typeA, std::optional typeB) + ReleaseType parseReleaseType(const std::vector& releaseTypeNames) { - if (!typeA && typeB) - return false; - else if (typeA && !typeB) - return true; - else - return static_cast(*typeA) < static_cast(*typeB); + if (const auto picardReleaseType{ parsePicardReleaseType(releaseTypeNames) }) + return *picardReleaseType; + + return CustomReleaseType{ .types = releaseTypeNames }; } - bool operator<(core::EnumSet typesA, core::EnumSet typesB) + bool operator<(core::EnumSet lhs, core::EnumSet rhs) { - return typesA.getBitfield() < typesB.getBitfield(); + return lhs.getBitfield() < rhs.getBitfield(); } - bool ReleaseType::operator<(const ReleaseType& other) const + bool PicardReleaseType::operator<(const PicardReleaseType& other) const { - // TODO : order custom types and compare for each element (size is not to be compared first) - return std::tie(primaryType, secondaryTypes, customTypes) < std::tie(other.primaryType, other.secondaryTypes, other.customTypes); + return std::tie(primaryType, secondaryTypes) < std::tie(other.primaryType, other.secondaryTypes); + } + + bool CustomReleaseType::operator<(const CustomReleaseType& other) const + { + return types < other.types; } } // namespace lms::ui diff --git a/src/lms/ui/explore/ReleaseTypes.hpp b/src/lms/ui/explore/ReleaseTypes.hpp index 882f4317..8a49d51c 100644 --- a/src/lms/ui/explore/ReleaseTypes.hpp +++ b/src/lms/ui/explore/ReleaseTypes.hpp @@ -19,49 +19,55 @@ #pragma once -#include #include +#include #include #include "core/EnumSet.hpp" namespace lms::ui { - // see https://musicbrainz.org/doc/Release_Group/Type - enum class PrimaryReleaseType + struct PicardReleaseType { - Album, - Single, - EP, - Broadcast, - Other + // see https://musicbrainz.org/doc/Release_Group/Type + enum class PrimaryType : std::uint8_t + { + Album, + Single, + EP, + Broadcast, + Other + }; + + enum class SecondaryType : std::uint8_t + { + Compilation, + Soundtrack, + Spokenword, + Interview, + Audiobook, + AudioDrama, + Live, + Remix, + DJMix, + Mixtape_Street, + Demo, + FieldRecording, + }; + + PrimaryType primaryType; + core::EnumSet secondaryTypes; + + bool operator<(const PicardReleaseType&) const; }; - enum class SecondaryReleaseType + struct CustomReleaseType { - Compilation, - Soundtrack, - Spokenword, - Interview, - Audiobook, - AudioDrama, - Live, - Remix, - DJMix, - Mixtape_Street, - Demo, - FieldRecording, - }; - - struct ReleaseType - { - std::optional primaryType; - core::EnumSet secondaryTypes; - std::vector customTypes; - - bool operator<(const ReleaseType& other) const; + std::vector types; + + bool operator<(const CustomReleaseType&) const; }; + using ReleaseType = std::variant; ReleaseType parseReleaseType(const std::vector& releaseTypeNames); - } // namespace lms::ui \ No newline at end of file