Added release type in UI (artist view)

This commit is contained in:
emeric
2023-04-20 15:29:21 +02:00
parent 1d41a26ead
commit ca09a14780
11 changed files with 185 additions and 107 deletions
@@ -139,6 +139,11 @@ createQuery(Session& session, const Release::FindParameters& params)
query.where(oss.str());
}
if (params.primaryType)
query.where("primary_type = ?").bind(*params.primaryType);
if (!params.secondaryTypes.empty())
query.where("secondary_type = ?").bind(params.secondaryTypes);
switch (params.sortMethod)
{
case ReleaseSortMethod::None:
@@ -61,6 +61,8 @@ class Release : public Object<Release, ReleaseId>
ArtistId artist; // only releases that involved this user
EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
EnumSet<TrackArtistLinkType> excludedTrackArtistLinkTypes; // but not for these link types
std::optional<ReleaseTypePrimary> primaryType; // if, set, matching this primary type
EnumSet<ReleaseTypeSecondary> secondaryTypes; // Matching all this (if any)
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
+1 -1
View File
@@ -34,7 +34,7 @@ add_executable(lms
ui/explore/Filters.cpp
ui/explore/PlayQueueController.cpp
ui/explore/ReleaseCollector.cpp
ui/explore/ReleaseListHelpers.cpp
ui/explore/ReleaseHelpers.cpp
ui/explore/ReleasesView.cpp
ui/explore/ReleaseView.cpp
ui/explore/SearchView.cpp
+95 -50
View File
@@ -40,7 +40,7 @@
#include "LmsApplication.hpp"
#include "LmsApplicationException.hpp"
#include "PlayQueueController.hpp"
#include "ReleaseListHelpers.hpp"
#include "ReleaseHelpers.hpp"
#include "TrackListHelpers.hpp"
#include "Utils.hpp"
@@ -48,6 +48,19 @@ using namespace Database;
namespace UserInterface {
bool
Artist::ReleaseType::operator<(const ReleaseType& other) const
{
if (!primaryType && other.primaryType)
return false;
else if (primaryType && !other.primaryType)
return true;
else if (*primaryType == *other.primaryType)
return secondaryTypes.getBitfield() < other.secondaryTypes.getBitfield();
else
return static_cast<int>(*primaryType) < static_cast<int>(*other.primaryType);
}
Artist::Artist(Filters& filters, PlayQueueController& controller)
: Template {Wt::WString::tr("Lms.Explore.Artist.template")}
, _filters {filters}
@@ -114,15 +127,12 @@ Artist::refreshView()
LmsApp->setTitle(artist->getName());
_artistId = *artistId;
bool setReleaseTitle {};
refreshReleases();
setReleaseTitle |= refreshAppearsOnReleases();
setReleaseTitle |= refreshNonReleaseTracks();
refreshAppearsOnReleases();
refreshNonReleaseTracks();
refreshLinks(artist);
refreshSimilarArtists(similarArtistIds);
setCondition("if-release-title", setReleaseTitle);
Wt::WContainerWidget* clusterContainers {bindNew<Wt::WContainerWidget>("clusters")};
{
@@ -184,25 +194,57 @@ Artist::refreshView()
}
}
bool
void
Artist::refreshReleases()
{
_releaseContainer = bindNew<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
_releaseContainer->onRequestElements.connect(this, [this]
_releaseContainers.clear();
Release::FindParameters params;
params.setClusters(_filters.getClusterIds());
params.setArtist(_artistId, {TrackArtistLinkType::ReleaseArtist}, {});
params.setSortMethod(ReleaseSortMethod::DateDesc);
const auto releases {Release::find(LmsApp->getDbSession(), params)};
if (!releases.results.empty())
{
addSomeReleases(*_releaseContainer, {TrackArtistLinkType::ReleaseArtist}, {});
});
// first pass: gather all ids and sort by type
for (const ReleaseId releaseId : releases.results)
{
const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseId)};
const bool added {addSomeReleases(*_releaseContainer, {TrackArtistLinkType::ReleaseArtist}, {})};
setCondition("if-has-releases", added);
ReleaseType releaseType {release->getPrimaryType(), release->getSecondaryTypes()};
_releaseContainers[releaseType].releases.push_back(releaseId);
}
return added;
// second pass: construct widgets
Wt::WContainerWidget* releaseContainers {bindNew<Wt::WContainerWidget>("release-containers")};
for (auto& [releaseType, releases] : _releaseContainers)
{
Wt::WTemplate* releaseContainer {releaseContainers->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artist.template.release-container"))};
if (releaseType.primaryType)
releaseContainer->bindString("release-type", ReleaseHelpers::buildReleaseTypeString(*releaseType.primaryType, releaseType.secondaryTypes));
else
releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.releases")); // fallback when not tagged with MB
releases.container = releaseContainer->bindNew<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
releases.container->onRequestElements.connect(this, [this, &releases]
{
addSomeReleases(releases);
});
releases.container->setHasMore(true);
}
}
else
{
bindEmpty("release-containers");
}
}
bool
void
Artist::refreshAppearsOnReleases()
{
const EnumSet<TrackArtistLinkType> types
constexpr EnumSet<TrackArtistLinkType> types
{
TrackArtistLinkType::Artist,
TrackArtistLinkType::Arranger,
@@ -216,18 +258,33 @@ Artist::refreshAppearsOnReleases()
TrackArtistLinkType::Writer,
};
_appearsOnReleaseContainer = bindNew<InfiniteScrollingContainer>("appears-on-releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
_appearsOnReleaseContainer->onRequestElements.connect(this, [=]
{
addSomeReleases(*_appearsOnReleaseContainer, types, {TrackArtistLinkType::ReleaseArtist});
});
_appearsOnReleaseContainer = {};
const bool added {addSomeReleases(*_appearsOnReleaseContainer, types, {TrackArtistLinkType::ReleaseArtist})};
setCondition("if-has-appears-on-releases", added);
return added;
Release::FindParameters params;
params.setClusters(_filters.getClusterIds());
params.setArtist(_artistId, types, {TrackArtistLinkType::ReleaseArtist});
params.setSortMethod(ReleaseSortMethod::DateDesc);
const auto releases {Release::find(LmsApp->getDbSession(), params)};
if (!releases.results.empty())
{
Wt::WTemplate* releaseContainer {bindNew<Wt::WTemplate>("appears-on-releases", Wt::WString::tr("Lms.Explore.Artist.template.release-container"))};
releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.Artist.appears-on"));
_appearsOnReleaseContainer.releases = releases.results;
_appearsOnReleaseContainer.container = releaseContainer->bindNew<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
_appearsOnReleaseContainer.container->onRequestElements.connect(this, [this, &releases]
{
addSomeReleases(_appearsOnReleaseContainer);
});
_appearsOnReleaseContainer.container->setHasMore(true);
}
else
{
bindEmpty("appears-on-releases");
}
}
bool
void
Artist::refreshNonReleaseTracks()
{
setCondition("if-has-non-release-tracks", true);
@@ -239,7 +296,6 @@ Artist::refreshNonReleaseTracks()
const bool added {addSomeNonReleaseTracks()};
setCondition("if-has-non-release-tracks", added);
return added;
}
void
@@ -272,35 +328,24 @@ Artist::refreshLinks(const Database::Artist::pointer& artist)
}
}
bool
Artist::addSomeReleases(InfiniteScrollingContainer& releaseContainer, EnumSet<Database::TrackArtistLinkType> linkTypes, EnumSet<Database::TrackArtistLinkType> excludedLinkTypes)
void
Artist::addSomeReleases(ReleaseContainer& releaseContainer)
{
bool areArtistsAdded{};
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)};
if (!artist)
return areArtistsAdded;
const Range range {static_cast<std::size_t>(releaseContainer.getCount()), _releasesBatchSize};
Release::FindParameters params;
params.setClusters(_filters.getClusterIds());
params.setArtist(_artistId, linkTypes, excludedLinkTypes);
params.setRange(range);
params.setSortMethod(ReleaseSortMethod::DateDesc);
const auto releases {Release::find(LmsApp->getDbSession(), params)};
for (const ReleaseId releaseId : releases.results)
if (const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)})
{
const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseId)};
releaseContainer.add(ReleaseListHelpers::createEntryForArtist(release, artist));
areArtistsAdded = true;
for (std::size_t i {static_cast<std::size_t>(releaseContainer.container->getCount())}; i < releaseContainer.releases.size(); ++i)
{
const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseContainer.releases[i])};
releaseContainer.container->add(ReleaseListHelpers::createEntryForArtist(release, artist));
}
releaseContainer.container->setHasMore(releaseContainer.container->getCount() < releaseContainer.releases.size());
}
else
{
releaseContainer.container->setHasMore(false);
}
releaseContainer.setHasMore(releases.moreResults);
return areArtistsAdded;
}
bool
+23 -6
View File
@@ -19,8 +19,10 @@
#pragma once
#include <map>
#include "services/database/ArtistId.hpp"
#include "services/database/Object.hpp"
#include "services/database/ReleaseId.hpp"
#include "services/database/Types.hpp"
#include "utils/EnumSet.hpp"
#include "common/Template.hpp"
@@ -44,13 +46,14 @@ namespace UserInterface
private:
void refreshView();
bool refreshReleases();
bool refreshAppearsOnReleases();
bool refreshNonReleaseTracks();
void refreshReleases();
void refreshAppearsOnReleases();
void refreshNonReleaseTracks();
void refreshSimilarArtists(const std::vector<Database::ArtistId>& similarArtistsId);
void refreshLinks(const Database::ObjectPtr<Database::Artist>& artist);
bool addSomeReleases(InfiniteScrollingContainer& container, EnumSet<Database::TrackArtistLinkType> linkTypes, EnumSet<Database::TrackArtistLinkType> excludedLinkTypes);
struct ReleaseContainer;
void addSomeReleases(ReleaseContainer& releaseContainer);
bool addSomeNonReleaseTracks();
static constexpr std::size_t _releasesBatchSize {6};
static constexpr std::size_t _tracksBatchSize {6};
@@ -58,8 +61,22 @@ namespace UserInterface
Filters& _filters;
PlayQueueController& _playQueueController;
InfiniteScrollingContainer* _releaseContainer {};
InfiniteScrollingContainer* _appearsOnReleaseContainer {};
struct ReleaseType
{
std::optional<Database::ReleaseTypePrimary> primaryType;
EnumSet<Database::ReleaseTypeSecondary> secondaryTypes;
bool operator<(const ReleaseType& other) const;
};
struct ReleaseContainer
{
InfiniteScrollingContainer* container {};
std::vector<Database::ReleaseId> releases;
};
std::map<ReleaseType, ReleaseContainer> _releaseContainers;
ReleaseContainer _appearsOnReleaseContainer {};
InfiniteScrollingContainer* _trackContainer {};
Database::ArtistId _artistId {};
};
@@ -17,7 +17,7 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ReleaseListHelpers.hpp"
#include "ReleaseHelpers.hpp"
#include <Wt/WAnchor.h>
#include <Wt/WImage.h>
@@ -104,3 +104,42 @@ namespace UserInterface::ReleaseListHelpers
}
} // namespace UserInterface
namespace UserInterface::ReleaseHelpers
{
Wt::WString
buildReleaseTypeString(ReleaseTypePrimary primaryType, EnumSet<ReleaseTypeSecondary> secondaryTypes)
{
Wt::WString res;
switch (primaryType)
{
case ReleaseTypePrimary::Album: res = Wt::WString::tr("Lms.Explore.Release.type-primary-album"); break;
case ReleaseTypePrimary::Broadcast: res = Wt::WString::tr("Lms.Explore.Release.type-primary-broadcast"); break;
case ReleaseTypePrimary::EP: res = Wt::WString::tr("Lms.Explore.Release.type-primary-ep"); break;
case ReleaseTypePrimary::Single: res = Wt::WString::tr("Lms.Explore.Release.type-primary-single"); break;
case ReleaseTypePrimary::Other: res = Wt::WString::tr("Lms.Explore.Release.type-primary-other"); break;
}
for (ReleaseTypeSecondary secondaryType : secondaryTypes)
{
res += Wt::WString {" · "};
switch (secondaryType)
{
case ReleaseTypeSecondary::Compilation: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-compilation"); break;
case ReleaseTypeSecondary::Spokenword: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-spokenword"); break;
case ReleaseTypeSecondary::Soundtrack: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-soundtrack"); break;
case ReleaseTypeSecondary::Interview: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-interview"); break;
case ReleaseTypeSecondary::Audiobook: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiobook"); break;
case ReleaseTypeSecondary::AudioDrama: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiodrama"); break;
case ReleaseTypeSecondary::Live: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-live"); break;
case ReleaseTypeSecondary::Remix: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-remix"); break;
case ReleaseTypeSecondary::DJMix: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-djmix"); break;
case ReleaseTypeSecondary::Mixtape_Street: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-mixtape-street"); break;
case ReleaseTypeSecondary::Demo: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-demo"); break;
}
}
return res;
}
} // namespace UserInterface::ReleaseHelpers}
@@ -23,6 +23,8 @@
#include <Wt/WTemplate.h>
#include "services/database/Object.hpp"
#include "services/database/Types.hpp"
#include "utils/EnumSet.hpp"
namespace Database
{
@@ -36,3 +38,7 @@ namespace UserInterface::ReleaseListHelpers
std::unique_ptr<Wt::WTemplate> createEntryForArtist(const Database::ObjectPtr<Database::Release>& release, const Database::ObjectPtr<Database::Artist>& artist);
} // namespace UserInterface
namespace UserInterface::ReleaseHelpers
{
Wt::WString buildReleaseTypeString(Database::ReleaseTypePrimary primaryType, EnumSet<Database::ReleaseTypeSecondary> secondaryTypes);
}
+2 -39
View File
@@ -40,7 +40,7 @@
#include "resource/DownloadResource.hpp"
#include "explore/Filters.hpp"
#include "explore/PlayQueueController.hpp"
#include "explore/ReleaseListHelpers.hpp"
#include "explore/ReleaseHelpers.hpp"
#include "explore/TrackListHelpers.hpp"
#include "LmsApplication.hpp"
#include "LmsApplicationException.hpp"
@@ -52,43 +52,6 @@ using namespace Database;
namespace UserInterface {
Wt::WString
buildReleaseTypeString(ReleaseTypePrimary primaryType, EnumSet<ReleaseTypeSecondary> secondaryTypes)
{
Wt::WString res;
switch (primaryType)
{
case ReleaseTypePrimary::Album: res = Wt::WString::tr("Lms.Explore.Release.type-primary-album"); break;
case ReleaseTypePrimary::Broadcast: res = Wt::WString::tr("Lms.Explore.Release.type-primary-broadcast"); break;
case ReleaseTypePrimary::EP: res = Wt::WString::tr("Lms.Explore.Release.type-primary-ep"); break;
case ReleaseTypePrimary::Single: res = Wt::WString::tr("Lms.Explore.Release.type-primary-single"); break;
case ReleaseTypePrimary::Other: res = Wt::WString::tr("Lms.Explore.Release.type-primary-other"); break;
}
for (ReleaseTypeSecondary secondaryType : secondaryTypes)
{
res += Wt::WString {" · "};
switch (secondaryType)
{
case ReleaseTypeSecondary::Compilation: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-compilation"); break;
case ReleaseTypeSecondary::Spokenword: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-spokenword"); break;
case ReleaseTypeSecondary::Soundtrack: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-soundtrack"); break;
case ReleaseTypeSecondary::Interview: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-interview"); break;
case ReleaseTypeSecondary::Audiobook: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiobook"); break;
case ReleaseTypeSecondary::AudioDrama: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-audiodrama"); break;
case ReleaseTypeSecondary::Live: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-live"); break;
case ReleaseTypeSecondary::Remix: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-remix"); break;
case ReleaseTypeSecondary::DJMix: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-djmix"); break;
case ReleaseTypeSecondary::Mixtape_Street: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-mixtape-street"); break;
case ReleaseTypeSecondary::Demo: res += Wt::WString::tr("Lms.Explore.Release.type-secondary-demo"); break;
}
}
return res;
}
void
showReleaseInfoModal(Database::ReleaseId releaseId)
{
@@ -105,7 +68,7 @@ showReleaseInfoModal(Database::ReleaseId releaseId)
if (auto primaryReleaseType {release->getPrimaryType()})
{
releaseInfo->setCondition("if-has-release-type", true);
releaseInfo->bindString("release-type", buildReleaseTypeString(*primaryReleaseType, release->getSecondaryTypes()));
releaseInfo->bindString("release-type", ReleaseHelpers::buildReleaseTypeString(*primaryReleaseType, release->getSecondaryTypes()));
}
std::map<Wt::WString, std::set<ArtistId>> artistMap;
+1 -1
View File
@@ -28,7 +28,7 @@
#include "common/Template.hpp"
#include "explore/Filters.hpp"
#include "explore/PlayQueueController.hpp"
#include "explore/ReleaseListHelpers.hpp"
#include "explore/ReleaseHelpers.hpp"
#include "LmsApplication.hpp"
using namespace Database;
+1 -1
View File
@@ -28,7 +28,7 @@
#include "ArtistListHelpers.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"
#include "ReleaseListHelpers.hpp"
#include "ReleaseHelpers.hpp"
#include "TrackListHelpers.hpp"
using namespace Database;