Fixed bad fallback when hitting only a secondary release type as used by picard, fixes #766
This commit is contained in:
@@ -253,11 +253,7 @@ namespace lms::ui
|
||||
{
|
||||
Wt::WTemplate* releaseContainer{ releaseContainers->addNew<Wt::WTemplate>(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<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
|
||||
releases.container->onRequestElements.connect(this, [this, &releases = releases] {
|
||||
addSomeReleases(releases);
|
||||
|
||||
@@ -24,10 +24,13 @@
|
||||
#include <Wt/WText.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -17,24 +17,24 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ReleaseTypes.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "core/String.hpp"
|
||||
|
||||
#include "ReleaseTypes.hpp"
|
||||
|
||||
namespace lms::core::stringUtils
|
||||
{
|
||||
template<>
|
||||
std::optional<ui::PrimaryReleaseType> readAs(std::string_view str)
|
||||
std::optional<ui::PicardReleaseType::PrimaryType> readAs(std::string_view str)
|
||||
{
|
||||
static const std::unordered_map<std::string, ui::PrimaryReleaseType> 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<std::string, ui::PicardReleaseType::PrimaryType> 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<ui::SecondaryReleaseType> readAs(std::string_view str)
|
||||
std::optional<ui::PicardReleaseType::SecondaryType> readAs(std::string_view str)
|
||||
{
|
||||
static const std::unordered_map<std::string, ui::SecondaryReleaseType> 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<std::string, ui::PicardReleaseType::SecondaryType> 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<std::string>& releaseTypeNames)
|
||||
std::optional<PicardReleaseType> parsePicardReleaseType(const std::vector<std::string>& releaseTypeNames)
|
||||
{
|
||||
ReleaseType res;
|
||||
if (releaseTypeNames.empty())
|
||||
return std::nullopt;
|
||||
|
||||
for (std::string_view releaseTypeName : releaseTypeNames)
|
||||
const auto primaryType{ core::stringUtils::readAs<PicardReleaseType::PrimaryType>(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<PrimaryReleaseType>(releaseTypeName) })
|
||||
{
|
||||
if (!res.primaryType)
|
||||
res.primaryType = primaryType;
|
||||
else
|
||||
res.customTypes.push_back(std::string{ releaseTypeName });
|
||||
}
|
||||
else if (auto secondaryType{ core::stringUtils::readAs<SecondaryReleaseType>(releaseTypeName) })
|
||||
{
|
||||
res.secondaryTypes.insert(*secondaryType);
|
||||
}
|
||||
else
|
||||
res.customTypes.push_back(std::string{ releaseTypeName });
|
||||
const auto secondaryType{ core::stringUtils::readAs<PicardReleaseType::SecondaryType>(releaseTypeNames[i]) };
|
||||
if (!secondaryType)
|
||||
return std::nullopt;
|
||||
|
||||
res.secondaryTypes.insert(*secondaryType);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool operator<(std::optional<PrimaryReleaseType> typeA, std::optional<PrimaryReleaseType> typeB)
|
||||
ReleaseType parseReleaseType(const std::vector<std::string>& releaseTypeNames)
|
||||
{
|
||||
if (!typeA && typeB)
|
||||
return false;
|
||||
else if (typeA && !typeB)
|
||||
return true;
|
||||
else
|
||||
return static_cast<int>(*typeA) < static_cast<int>(*typeB);
|
||||
if (const auto picardReleaseType{ parsePicardReleaseType(releaseTypeNames) })
|
||||
return *picardReleaseType;
|
||||
|
||||
return CustomReleaseType{ .types = releaseTypeNames };
|
||||
}
|
||||
|
||||
bool operator<(core::EnumSet<SecondaryReleaseType> typesA, core::EnumSet<SecondaryReleaseType> typesB)
|
||||
bool operator<(core::EnumSet<PicardReleaseType::SecondaryType> lhs, core::EnumSet<PicardReleaseType::SecondaryType> 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
|
||||
|
||||
@@ -19,49 +19,55 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#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<SecondaryType> 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<PrimaryReleaseType> primaryType;
|
||||
core::EnumSet<SecondaryReleaseType> secondaryTypes;
|
||||
std::vector<std::string> customTypes;
|
||||
|
||||
bool operator<(const ReleaseType& other) const;
|
||||
std::vector<std::string> types;
|
||||
|
||||
bool operator<(const CustomReleaseType&) const;
|
||||
};
|
||||
|
||||
using ReleaseType = std::variant<PicardReleaseType, CustomReleaseType>;
|
||||
ReleaseType parseReleaseType(const std::vector<std::string>& releaseTypeNames);
|
||||
|
||||
} // namespace lms::ui
|
||||
Reference in New Issue
Block a user