diff --git a/approot/messages.xml b/approot/messages.xml
index 058d29e0..9ba2dc91 100644
--- a/approot/messages.xml
+++ b/approot/messages.xml
@@ -158,6 +158,7 @@
Duration
Filter added
Filters
+Music Library
Most played
MusicBrainz Artist
MusicBrainz Release
diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml
index 77da8540..54b33a6f 100644
--- a/approot/messages_fr.xml
+++ b/approot/messages_fr.xml
@@ -158,6 +158,7 @@
Durée
Filtre ajouté
Filtres
+Bibliothèque musicale
Plus joués
MusicBrainz Artist
MusicBrainz Release
diff --git a/approot/messages_it.xml b/approot/messages_it.xml
index 8a2da532..409b048c 100644
--- a/approot/messages_it.xml
+++ b/approot/messages_it.xml
@@ -110,6 +110,7 @@
Scansione: passo {1}/{2}
Controllo duplicati... {1} files
Controllo file... {1}%
+Compattazione del database...
Calcolo statistiche... {1}%
File trovati: {1} files
Recupero metadati da AcousticBrainz: {1}/{2} tracce ({3}%)...
@@ -157,6 +158,7 @@
Durata
Filtro aggiunto
Filtri
+Libreria musicale
Più ascoltati
Artista MusicBrainz
Album MusicBrainz
diff --git a/approot/messages_zh.xml b/approot/messages_zh.xml
index 05774126..745cfe82 100644
--- a/approot/messages_zh.xml
+++ b/approot/messages_zh.xml
@@ -158,6 +158,7 @@
过滤器已添加
过滤器
+
播放最多
MusicBrainz 歌手
MusicBrainz 专辑
diff --git a/src/libs/database/impl/Cluster.cpp b/src/libs/database/impl/Cluster.cpp
index 7c9a3d3f..ee1dab9b 100644
--- a/src/libs/database/impl/Cluster.cpp
+++ b/src/libs/database/impl/Cluster.cpp
@@ -43,17 +43,19 @@ namespace lms::db
query.groupBy("c.id");
if (params.track.isValid() || params.release.isValid())
- {
query.join("track_cluster t_c ON t_c.cluster_id = c.id");
- query.join("track t ON t.id = t_c.track_id");
- }
+
if (!params.clusterTypeName.empty())
query.join("cluster_type c_t ON c_t.id = c.cluster_type_id");
if (params.track.isValid())
- query.where("t.id = ?").bind(params.track);
+ query.where("t_c.track_id = ?").bind(params.track);
+
if (params.release.isValid())
+ {
+ query.join("track t ON t.id = t_c.track_id");
query.where("t.release_id = ?").bind(params.release);
+ }
assert(!params.clusterType.isValid() || params.clusterTypeName.empty());
if (params.clusterType.isValid())
@@ -61,6 +63,17 @@ namespace lms::db
else if (!params.clusterTypeName.empty())
query.where("c_t.name = ?").bind(params.clusterTypeName);
+ switch (params.sortMethod)
+ {
+ case ClusterSortMethod::None:
+ break;
+ case ClusterSortMethod::Name:
+ query.orderBy("c.name COLLATE NOCASE");
+ break;
+ }
+
+ query.groupBy("c.id");
+
return query;
}
@@ -208,6 +221,12 @@ namespace lms::db
return utils::execRangeQuery(query, range);
}
+ void ClusterType::find(Session& session, const std::function& func)
+ {
+ auto query{ session.getDboSession()->find() };
+ return utils::forEachQueryResult(query, func);
+ }
+
ClusterType::pointer ClusterType::find(Session& session, std::string_view name)
{
session.checkReadTransaction();
diff --git a/src/libs/database/impl/Migration.cpp b/src/libs/database/impl/Migration.cpp
index 426de394..5db01f67 100644
--- a/src/libs/database/impl/Migration.cpp
+++ b/src/libs/database/impl/Migration.cpp
@@ -34,7 +34,7 @@ namespace lms::db
{
namespace
{
- static constexpr Version LMS_DATABASE_VERSION{ 57 };
+ static constexpr Version LMS_DATABASE_VERSION{ 58 };
}
VersionInfo::VersionInfo()
@@ -455,11 +455,17 @@ SELECT
void migrateFromV56(Session& session)
{
// Make sure we remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
- std::vector indexeNames{ utils::fetchQueryResults(session.getDboSession()->query(R"(SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE '%_idx')")) };
+ std::vector indexeNames{ utils::fetchQueryResults(session.getDboSession()->query(R"(SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE '%_idx')")) };
for (const auto& indexName : indexeNames)
session.getDboSession()->execute("DROP INDEX " + indexName);
}
+ void migrateFromV57(Session& session)
+ {
+ // useless index
+ session.getDboSession()->execute("DROP INDEX cluster_name_idx");
+ }
+
bool doDbMigration(Session& session)
{
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
@@ -494,6 +500,7 @@ SELECT
{54, migrateFromV54},
{55, migrateFromV55},
{56, migrateFromV56},
+ {57, migrateFromV57},
};
bool migrationPerformed{};
diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp
index 342d8cb4..8201588c 100644
--- a/src/libs/database/impl/Session.cpp
+++ b/src/libs/database/impl/Session.cpp
@@ -172,7 +172,6 @@ namespace lms::db
_session.execute("CREATE INDEX IF NOT EXISTS auth_token_value_idx ON auth_token(value)");
_session.execute("CREATE INDEX IF NOT EXISTS cluster_cluster_type_idx ON cluster(cluster_type_id)");
- _session.execute("CREATE INDEX IF NOT EXISTS cluster_name_idx ON cluster(name)");
_session.execute("CREATE INDEX IF NOT EXISTS cluster_type_name_idx ON cluster_type(name)");
_session.execute("CREATE INDEX IF NOT EXISTS listen_backend_idx ON listen(backend)");
diff --git a/src/libs/database/impl/TrackList.cpp b/src/libs/database/impl/TrackList.cpp
index d97e74a8..aa6d85db 100644
--- a/src/libs/database/impl/TrackList.cpp
+++ b/src/libs/database/impl/TrackList.cpp
@@ -35,6 +35,79 @@
namespace lms::db
{
+ namespace
+ {
+ template
+ Wt::Dbo::Query createQuery(Session& session, std::string_view itemToSelect, const TrackList::FindParameters& params)
+ {
+ auto query{ session.getDboSession()->query("SELECT DISTINCT " + std::string{ itemToSelect } + " FROM tracklist t_l") };
+
+ if (!params.clusters.empty() || params.mediaLibrary.isValid())
+ query.join("tracklist_entry t_l_e ON t_l_e.tracklist_id = t_l.id");
+
+ if (params.mediaLibrary.isValid())
+ query.join("track t ON t.id = t_l_e.track_id");
+
+ if (params.mediaLibrary.isValid())
+ query.where("t.media_library_id = ?").bind(params.mediaLibrary);
+
+ if (params.user.isValid())
+ query.where("t_l.user_id = ?").bind(params.user);
+
+ if (params.type)
+ query.where("t_l.type = ?").bind(*params.type);
+
+ if (!params.clusters.empty())
+ {
+ std::ostringstream oss;
+ oss << "t_l_e.track_id IN (SELECT DISTINCT t.id FROM track t"
+ " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"
+ " INNER JOIN cluster c ON c.id = t_c.cluster_id";
+
+ WhereClause clusterClause;
+ for (const ClusterId clusterId : params.clusters)
+ {
+ clusterClause.Or(WhereClause("c.id = ?"));
+ query.bind(clusterId);
+ }
+
+ oss << " " << clusterClause.get();
+ oss << " GROUP BY t.id HAVING COUNT(*) = " << params.clusters.size() << ")";
+
+ query.where(oss.str());
+ }
+
+ switch (params.sortMethod)
+ {
+ case TrackListSortMethod::None:
+ break;
+ case TrackListSortMethod::Name:
+ query.orderBy("t_l.name COLLATE NOCASE");
+ break;
+ case TrackListSortMethod::LastModifiedDesc:
+ query.orderBy("t_l.last_modified_date_time DESC");
+ break;
+ }
+
+ return query;
+ }
+
+ template
+ Wt::Dbo::Query createQuery(Session& session, const TrackList::FindParameters& params)
+ {
+ std::string_view itemToSelect;
+
+ if constexpr (std::is_same_v)
+ itemToSelect = "t_l.id";
+ else if constexpr (std::is_same_v>)
+ itemToSelect = "t_l";
+ else
+ static_assert("Unhandled type");
+
+ return createQuery(session, itemToSelect, params);
+ }
+ }
+
TrackList::TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr user)
: _name{ name }
, _type{ type }
@@ -58,7 +131,6 @@ namespace lms::db
return utils::fetchQuerySingleResult(session.getDboSession()->query("SELECT COUNT(*) FROM tracklist"));
}
-
TrackList::pointer TrackList::find(Session& session, std::string_view name, TrackListType type, UserId userId)
{
session.checkReadTransaction();
@@ -73,53 +145,17 @@ namespace lms::db
RangeResults TrackList::find(Session& session, const FindParameters& params)
{
session.checkReadTransaction();
-
- auto query{ session.getDboSession()->query("SELECT DISTINCT t_l.id FROM tracklist t_l") };
-
- if (params.user.isValid())
- query.where("t_l.user_id = ?").bind(params.user);
-
- if (params.type)
- query.where("t_l.type = ?").bind(*params.type);
-
- if (!params.clusters.empty())
- {
- query.join("tracklist_entry t_l_e ON t_l_e.tracklist_id = t_l.id");
- query.join("track t ON t.id = t_l_e.track_id");
-
- std::ostringstream oss;
- oss << "t.id IN (SELECT DISTINCT t.id FROM track t"
- " INNER JOIN track_cluster t_c ON t_c.track_id = t.id"
- " INNER JOIN cluster c ON c.id = t_c.cluster_id";
-
- WhereClause clusterClause;
- for (const ClusterId clusterId : params.clusters)
- {
- clusterClause.Or(WhereClause("c.id = ?"));
- query.bind(clusterId);
- }
-
- oss << " " << clusterClause.get();
- oss << " GROUP BY t.id HAVING COUNT(*) = " << params.clusters.size() << ")";
-
- query.where(oss.str());
- }
-
- switch (params.sortMethod)
- {
- case TrackListSortMethod::None:
- break;
- case TrackListSortMethod::Name:
- query.orderBy("t_l.name COLLATE NOCASE");
- break;
- case TrackListSortMethod::LastModifiedDesc:
- query.orderBy("t_l.last_modified_date_time DESC");
- break;
- }
-
+ auto query{ createQuery(session, params) };
return utils::execRangeQuery(query, params.range);
}
+ void TrackList::find(Session& session, const FindParameters& params, const std::function& func)
+ {
+ session.checkReadTransaction();
+ auto query{ createQuery>(session, params) };
+ utils::forEachQueryRangeResult(query, params.range, func);
+ }
+
TrackList::pointer TrackList::find(Session& session, TrackListId id)
{
session.checkReadTransaction();
diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/database/include/database/Artist.hpp
index 27994799..12f81161 100644
--- a/src/libs/database/include/database/Artist.hpp
+++ b/src/libs/database/include/database/Artist.hpp
@@ -20,6 +20,7 @@
#pragma once
#include
+#include
#include
#include
#include
@@ -67,7 +68,7 @@ namespace lms::db
ReleaseId release; // artists involved in this release
MediaLibraryId mediaLibrary; // artists that belong to this library
- FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; }
+ FindParameters& setClusters(std::span _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; }
FindParameters& setLinkType(std::optional _linkType) { linkType = _linkType; return *this; }
FindParameters& setSortMethod(ArtistSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
diff --git a/src/libs/database/include/database/Cluster.hpp b/src/libs/database/include/database/Cluster.hpp
index 4408d2c2..fbdc1a72 100644
--- a/src/libs/database/include/database/Cluster.hpp
+++ b/src/libs/database/include/database/Cluster.hpp
@@ -46,12 +46,14 @@ namespace lms::db
struct FindParameters
{
std::optional range;
+ ClusterSortMethod sortMethod;
ClusterTypeId clusterType; // if non empty, clusters that belong to this cluster type
std::string clusterTypeName; // if non empty, clusters that belong to this cluster type
TrackId track; // if set, clusters involved in this track
ReleaseId release; // if set, clusters involved in this release
FindParameters& setRange(std::optional _range) { range = _range; return *this; }
+ FindParameters& setSortMethod(ClusterSortMethod _method) { sortMethod = _method; return *this; }
FindParameters& setClusterType(ClusterTypeId _clusterType) { clusterType = _clusterType; return *this; }
FindParameters& setClusterTypeName(std::string_view _name) { clusterTypeName = _name; return *this; }
FindParameters& setTrack(TrackId _track) { track = _track; return *this; }
@@ -119,6 +121,7 @@ namespace lms::db
// Getters
static std::size_t getCount(Session& session);
static RangeResults findIds(Session& session, std::optional range = std::nullopt);
+ static void find(Session& session, const std::function& func);
static pointer find(Session& session, std::string_view name);
static pointer find(Session& session, ClusterTypeId id);
static RangeResults findOrphanIds(Session& session, std::optional range = std::nullopt);
diff --git a/src/libs/database/include/database/Release.hpp b/src/libs/database/include/database/Release.hpp
index 2933d481..6bb91457 100644
--- a/src/libs/database/include/database/Release.hpp
+++ b/src/libs/database/include/database/Release.hpp
@@ -21,6 +21,7 @@
#include
#include
+#include
#include
#include
#include
@@ -96,7 +97,7 @@ namespace lms::db
std::string releaseType; // If set, albums that has this release type
MediaLibraryId mediaLibrary; // If set, releases that has at least a track in this library
- FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; }
+ FindParameters& setClusters(std::span _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; }
FindParameters& setSortMethod(ReleaseSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
FindParameters& setRange(std::optional _range) { range = _range; return *this; }
diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/database/include/database/Track.hpp
index 84cde313..9d197411 100644
--- a/src/libs/database/include/database/Track.hpp
+++ b/src/libs/database/include/database/Track.hpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -80,7 +81,7 @@ namespace lms::db
std::optional discNumber; // matching this disc number
MediaLibraryId mediaLibrary; // If set, tracks in this library
- FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; }
+ FindParameters& setClusters(std::span _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; }
FindParameters& setName(std::string_view _name) { name = _name; return *this; }
FindParameters& setSortMethod(TrackSortMethod _method) { sortMethod = _method; return *this; }
diff --git a/src/libs/database/include/database/TrackList.hpp b/src/libs/database/include/database/TrackList.hpp
index bdeff6bb..91dc92e1 100644
--- a/src/libs/database/include/database/TrackList.hpp
+++ b/src/libs/database/include/database/TrackList.hpp
@@ -20,6 +20,7 @@
#pragma once
#include
+#include
#include
#include
#include
@@ -28,6 +29,7 @@
#include
#include "database/ClusterId.hpp"
+#include "database/MediaLibraryId.hpp"
#include "database/Object.hpp"
#include "database/TrackId.hpp"
#include "database/TrackListId.hpp"
@@ -57,18 +59,21 @@ namespace lms::db
std::optional range;
std::optional type;
UserId user; // only tracklists owned by this user
+ MediaLibraryId mediaLibrary; // only tracklists that have songs in this media library
TrackListSortMethod sortMethod{ TrackListSortMethod::None };
- FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; }
+ FindParameters& setClusters(std::span _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
FindParameters& setRange(std::optional _range) { range = _range; return *this; }
FindParameters& setType(TrackListType _type) { type = _type; return *this; }
FindParameters& setUser(UserId _user) { user = _user; return *this; }
+ FindParameters& setMediaLibrary(MediaLibraryId _mediaLibrary) { mediaLibrary = _mediaLibrary; return *this; }
FindParameters& setSortMethod(TrackListSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
};
static std::size_t getCount(Session& session);
static pointer find(Session& session, std::string_view name, TrackListType type, UserId userId);
static pointer find(Session& session, TrackListId tracklistId);
static RangeResults find(Session& session, const FindParameters& params);
+ static void find(Session& session, const FindParameters& params, const std::function& func);
// Accessors
std::string_view getName() const { return _name; }
diff --git a/src/libs/database/include/database/Types.hpp b/src/libs/database/include/database/Types.hpp
index 506355b4..17b6bd27 100644
--- a/src/libs/database/include/database/Types.hpp
+++ b/src/libs/database/include/database/Types.hpp
@@ -116,6 +116,12 @@ namespace lms::db
StarredDateDesc,
};
+ enum class ClusterSortMethod
+ {
+ None,
+ Name,
+ };
+
enum class ReleaseSortMethod
{
None,
diff --git a/src/libs/database/test/Cluster.cpp b/src/libs/database/test/Cluster.cpp
index 027cc14b..edb28da2 100644
--- a/src/libs/database/test/Cluster.cpp
+++ b/src/libs/database/test/Cluster.cpp
@@ -137,11 +137,11 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- auto tracks{ Track::findIds(session, Track::FindParameters {}.setClusters({cluster1.getId()})) };
+ auto tracks{ Track::findIds(session, Track::FindParameters {}.setClusters(std::initializer_list{cluster1.getId()})) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track.getId());
- tracks = Track::findIds(session, Track::FindParameters{}.setClusters({ cluster2.getId() }));
+ tracks = Track::findIds(session, Track::FindParameters{}.setClusters(std::initializer_list{ cluster2.getId() }));
EXPECT_EQ(tracks.results.size(), 0);
}
@@ -237,6 +237,35 @@ namespace lms::db::tests
}
}
+ TEST_F(DatabaseFixture, ClusterType)
+ {
+ {
+ auto transaction{ session.createReadTransaction() };
+
+ bool visited{};
+ ClusterType::find(session, [&](const ClusterType::pointer&)
+ {
+ visited = true;
+ });
+ EXPECT_FALSE(visited);
+ }
+
+ ScopedClusterType clusterType1{ session, "MyClusterType1" };
+ ScopedClusterType clusterType2{ session, "MyClusterType2" };
+
+ {
+ auto transaction{ session.createReadTransaction() };
+
+ std::vector visitedClusterTypes;
+ ClusterType::find(session, [&](const ClusterType::pointer& clusterType)
+ {
+ visitedClusterTypes.push_back(clusterType->getId());
+ });
+ ASSERT_EQ(visitedClusterTypes.size(), 2);
+ EXPECT_EQ(visitedClusterTypes[0], clusterType1->getId());
+ EXPECT_EQ(visitedClusterTypes[1], clusterType2->getId());
+ }
+ }
TEST_F(DatabaseFixture, ClusterType_singleTrack)
{
@@ -285,7 +314,7 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
ASSERT_EQ(Cluster::findOrphanIds(session).results.size(), 2);
- EXPECT_EQ(Release::find(session, Release::FindParameters{}.setClusters({ unusedCluster.getId() })).results.size(), 0);
+ EXPECT_EQ(Release::find(session, Release::FindParameters{}.setClusters(std::initializer_list{ unusedCluster.getId() })).results.size(), 0);
EXPECT_EQ(Release::find(session, Release::FindParameters{}).results.size(), 1);
EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 0);
EXPECT_EQ(Cluster::computeReleaseCount(session, unusedCluster.getId()), 0);
@@ -321,7 +350,7 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters({cluster.getId()})) };
+ const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters(std::initializer_list{cluster.getId()})) };
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
}
@@ -329,7 +358,7 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters({unusedCluster.getId()})) };
+ const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters(std::initializer_list{unusedCluster.getId()})) };
EXPECT_EQ(releases.results.size(), 0);
}
@@ -375,12 +404,12 @@ namespace lms::db::tests
{
auto transaction{ session.createWriteTransaction() };
- auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) };
+ auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters(std::initializer_list{cluster1.getId()})) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
- EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster2.getId() })).results.size(), 0);
- EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.size(), 0);
+ EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setClusters(std::initializer_list{ cluster2.getId() })).results.size(), 0);
+ EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setClusters(std::initializer_list{ cluster3.getId() })).results.size(), 0);
cluster2.get().modify()->addTrack(track.get());
}
@@ -388,19 +417,19 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) };
+ auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters(std::initializer_list{cluster1.getId()})) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
- artists = Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster2.getId() }));
+ artists = Artist::findIds(session, Artist::FindParameters{}.setClusters(std::initializer_list{ cluster2.getId() }));
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
- artists = Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster1.getId() }));
+ artists = Artist::findIds(session, Artist::FindParameters{}.setClusters(std::initializer_list{ cluster1.getId() }));
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
- EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.size(), 0);
+ EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setClusters(std::initializer_list{ cluster3.getId() })).results.size(), 0);
}
}
@@ -429,7 +458,7 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) };
+ auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters(std::initializer_list{cluster.getId()})) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
@@ -581,7 +610,7 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) };
+ auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters(std::initializer_list{cluster.getId()})) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
@@ -589,7 +618,7 @@ namespace lms::db::tests
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
- releases = Release::findIds(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters({ cluster.getId() }));
+ releases = Release::findIds(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters(std::initializer_list{ cluster.getId() }));
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
}
@@ -620,7 +649,7 @@ namespace lms::db::tests
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
- releases = Release::findIds(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters({ cluster1.getId(), cluster2.getId() }));
+ releases = Release::findIds(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters(std::initializer_list{ cluster1.getId(), cluster2.getId() }));
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
}
diff --git a/src/libs/database/test/TrackList.cpp b/src/libs/database/test/TrackList.cpp
index 5473e13e..091e4cc3 100644
--- a/src/libs/database/test/TrackList.cpp
+++ b/src/libs/database/test/TrackList.cpp
@@ -151,6 +151,46 @@ namespace lms::db::tests
}
}
+ TEST_F(DatabaseFixture, TrackList_MediaLibrary)
+ {
+ ScopedUser user{ session, "MyUser" };
+ ScopedTrackList trackList1{ session, "MytrackList1", TrackListType::Playlist, false, user.lockAndGet() };
+ ScopedTrackList trackList2{ session, "MytrackList2", TrackListType::Playlist, false, user.lockAndGet() };
+ ScopedTrack track1{ session };
+ ScopedTrack track2{ session };
+ ScopedMediaLibrary library{ session };
+
+ {
+ auto transaction{ session.createWriteTransaction() };
+ session.create(track1.get(), trackList1.get());
+ session.create(track2.get(), trackList2.get());
+ track2.get().modify()->setMediaLibrary(library.get());
+ }
+
+ {
+ auto transaction{ session.createReadTransaction() };
+ std::vector visitedTrackLists;
+ TrackList::find(session, TrackList::FindParameters{}, [&](const TrackList::pointer& trackList)
+ {
+ visitedTrackLists.push_back(trackList->getId());
+ });
+ ASSERT_EQ(visitedTrackLists.size(), 2);
+ EXPECT_EQ(visitedTrackLists[0], trackList1->getId());
+ EXPECT_EQ(visitedTrackLists[1], trackList2->getId());
+ }
+
+ {
+ auto transaction{ session.createReadTransaction() };
+ std::vector visitedTrackLists;
+ TrackList::find(session, TrackList::FindParameters{}.setMediaLibrary(library->getId()), [&](const TrackList::pointer& trackList)
+ {
+ visitedTrackLists.push_back(trackList->getId());
+ });
+ ASSERT_EQ(visitedTrackLists.size(), 1);
+ EXPECT_EQ(visitedTrackLists[0], trackList2->getId());
+ }
+ }
+
TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster)
{
ScopedUser user{ session, "MyUser" };
@@ -163,7 +203,7 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) };
+ auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters(std::initializer_list{cluster.getId()})) };
EXPECT_EQ(trackLists.results.size(), 0);
}
@@ -177,7 +217,7 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
- auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) };
+ auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters(std::initializer_list{cluster.getId()})) };
ASSERT_EQ(trackLists.results.size(), 1);
EXPECT_EQ(trackLists.results.front(), trackList1.getId());
}
diff --git a/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp b/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp
index 04a4ae8a..0f39cdb2 100644
--- a/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp
+++ b/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp
@@ -21,6 +21,7 @@
#include
#include
+#include
#include
#include
@@ -57,7 +58,7 @@ namespace lms::feedback
db::MediaLibraryId library;
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
- FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; }
+ FindParameters& setClusters(std::span _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
FindParameters& setRange(std::optional _range) { range = _range; return *this; }
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
};
diff --git a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp
index f61162cb..2b73770e 100644
--- a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp
+++ b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
@@ -65,7 +66,7 @@ namespace lms::scrobbling
db::ArtistId artist; // if set, match this artist
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
- FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; }
+ FindParameters& setClusters(std::span _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
FindParameters& setRange(std::optional _range) { range = _range; return *this; }
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
FindParameters& setArtist(db::ArtistId _artist) { artist = _artist; return *this; }
diff --git a/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp b/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp
index 33495a61..7cfc60ab 100644
--- a/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp
+++ b/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp
@@ -88,7 +88,7 @@ namespace lms::api::subsonic
if (const Cluster::pointer cluster{ clusterType->getCluster(genre) })
{
Release::FindParameters params;
- params.setClusters({ cluster->getId() });
+ params.setClusters(std::initializer_list{ cluster->getId() });
params.setSortMethod(ReleaseSortMethod::Name);
params.setRange(range);
params.setMediaLibrary(mediaLibraryId);
@@ -280,7 +280,7 @@ namespace lms::api::subsonic
Response::Node& songsByGenreNode{ response.createNode("songsByGenre") };
Track::FindParameters params;
- params.setClusters({ cluster->getId() });
+ params.setClusters(std::initializer_list{ cluster->getId() });
params.setRange(Range{ offset, count });
params.setMediaLibrary(mediaLibrary);
diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp
index d00c670f..d12f7889 100644
--- a/src/lms/ui/PlayQueue.cpp
+++ b/src/lms/ui/PlayQueue.cpp
@@ -87,9 +87,7 @@ namespace lms::ui
return trackListModel->getValue(*row);
}
- static
- std::shared_ptr
- createTrackListModel()
+ static std::shared_ptr createTrackListModel()
{
using namespace db;
@@ -102,12 +100,10 @@ namespace lms::ui
params.setUser(LmsApp->getUserId());
params.setSortMethod(TrackListSortMethod::Name);
- auto tracklists{ TrackList::find(LmsApp->getDbSession(), params) };
- for (const TrackListId trackListId : tracklists.results)
- {
- const TrackList::pointer trackList{ TrackList::find(LmsApp->getDbSession(), trackListId) };
- model->add(Wt::WString::fromUTF8(std::string{ trackList->getName() }), trackListId);
- }
+ TrackList::find(LmsApp->getDbSession(), params, [&](const TrackList::pointer& trackList)
+ {
+ model->add(Wt::WString::fromUTF8(std::string{ trackList->getName() }), trackList->getId());
+ });
return model;
}
diff --git a/src/lms/ui/Utils.cpp b/src/lms/ui/Utils.cpp
index 86d0dfaf..cc36f3fa 100644
--- a/src/lms/ui/Utils.cpp
+++ b/src/lms/ui/Utils.cpp
@@ -80,7 +80,18 @@ namespace lms::ui::utils
return cover;
}
- std::unique_ptr createCluster(db::ClusterId clusterId, bool canDelete)
+ std::unique_ptr createFilter(const Wt::WString& name, const Wt::WString& tooltip, std::string_view colorStyleClass, bool canDelete)
+ {
+ auto res{ std::make_unique(Wt::WString{ canDelete ? " " : "" } + name, Wt::TextFormat::UnsafeXHTML) };
+
+ res->setStyleClass("Lms-badge-cluster badge me-1 " + std::string{ colorStyleClass }); // HACK
+ res->setToolTip(tooltip, Wt::TextFormat::Plain);
+ res->setInline(true);
+
+ return res;
+ }
+
+ std::unique_ptr createFilterCluster(db::ClusterId clusterId, bool canDelete)
{
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
@@ -88,7 +99,7 @@ namespace lms::ui::utils
if (!cluster)
return {};
- auto getStyleClass{ [](const db::Cluster::pointer& cluster) -> const char*
+ auto getStyleClass{ [](const db::Cluster::pointer& cluster) -> std::string_view
{
switch (cluster->getType()->getId().getValue() % 8)
{
@@ -105,14 +116,33 @@ namespace lms::ui::utils
return "bg-primary";
} };
- const std::string styleClass{ getStyleClass(cluster) };
- auto res{ std::make_unique(std::string {} + (canDelete ? " " : "") + Wt::WString::fromUTF8(std::string{ cluster->getName() }), Wt::TextFormat::UnsafeXHTML) };
+ return createFilter(Wt::WString::fromUTF8(std::string{ cluster->getName() }), Wt::WString::fromUTF8(std::string{ cluster->getType()->getName() }), getStyleClass(cluster), canDelete);
+ }
- res->setStyleClass("Lms-badge-cluster badge me-1 " + styleClass); // HACK
- res->setToolTip(std::string{ cluster->getType()->getName() }, Wt::TextFormat::Plain);
- res->setInline(true);
+ std::unique_ptr createFilterClustersForTrack(db::Track::pointer track, Filters& filters)
+ {
+ using namespace db;
- return res;
+ std::unique_ptr clusterContainer{ std::make_unique() };
+
+ // TODO: optimize this
+ const auto clusterTypes{ ClusterType::findIds(LmsApp->getDbSession()).results };
+ const auto clusterGroups{ track->getClusterGroups(clusterTypes, 3) };
+
+ for (const auto& clusters : clusterGroups)
+ {
+ for (const Cluster::pointer& cluster : clusters)
+ {
+ const ClusterId clusterId{ cluster->getId() };
+ Wt::WInteractWidget* entry{ clusterContainer->addWidget(createFilterCluster(clusterId)) };
+ entry->clicked().connect([&filters, clusterId]
+ {
+ filters.add(clusterId);
+ });
+ }
+ }
+
+ return clusterContainer;
}
std::unique_ptr createArtistAnchorList(const std::vector& artistIds, std::string_view cssAnchorClass)
@@ -278,30 +308,4 @@ namespace lms::ui::utils
return res;
}
-
- std::unique_ptr createClustersForTrack(db::Track::pointer track, Filters& filters)
- {
- using namespace db;
-
- std::unique_ptr clusterContainer{ std::make_unique() };
-
- // TODO: optimize this
- const auto clusterTypes{ ClusterType::findIds(LmsApp->getDbSession()).results };
- const auto clusterGroups{ track->getClusterGroups(clusterTypes, 3) };
-
- for (const auto& clusters : clusterGroups)
- {
- for (const Cluster::pointer& cluster : clusters)
- {
- const ClusterId clusterId{ cluster->getId() };
- Wt::WInteractWidget* entry{ clusterContainer->addWidget(createCluster(clusterId)) };
- entry->clicked().connect([&filters, clusterId]
- {
- filters.add(clusterId);
- });
- }
- }
-
- return clusterContainer;
- }
}
diff --git a/src/lms/ui/Utils.hpp b/src/lms/ui/Utils.hpp
index 3b79acf0..f1db77c1 100644
--- a/src/lms/ui/Utils.hpp
+++ b/src/lms/ui/Utils.hpp
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include "database/ArtistId.hpp"
#include "database/ClusterId.hpp"
@@ -56,7 +57,9 @@ namespace lms::ui::utils
std::unique_ptr createCover(db::ReleaseId releaseId, CoverResource::Size size);
std::unique_ptr createCover(db::TrackId trackId, CoverResource::Size size);
- std::unique_ptr createCluster(db::ClusterId clusterId, bool canDelete = false);
+ std::unique_ptr createFilter(const Wt::WString& name, const Wt::WString& tooltip, std::string_view colorStyleClass, bool canDelete = false);
+ std::unique_ptr createFilterCluster(db::ClusterId clusterId, bool canDelete = false);
+ std::unique_ptr createFilterClustersForTrack(db::ObjectPtr track, Filters& filters);
std::unique_ptr createArtistAnchorList(const std::vector& artistIds, std::string_view cssAnchorClass = "link-success");
std::unique_ptr createArtistDisplayNameWithAnchors(std::string_view displayName, const std::vector& artistIds, std::string_view cssAnchorClass = "link-success");
@@ -67,6 +70,4 @@ namespace lms::ui::utils
Wt::WLink createReleaseLink(db::ObjectPtr release);
std::unique_ptr createReleaseAnchor(db::ObjectPtr release, bool setText = true);
std::unique_ptr createTrackListAnchor(db::ObjectPtr trackList, bool setText = true);
-
- std::unique_ptr createClustersForTrack(db::ObjectPtr track, Filters& filters);
}
diff --git a/src/lms/ui/common/ValueStringModel.hpp b/src/lms/ui/common/ValueStringModel.hpp
index c0fe922a..39682bd9 100644
--- a/src/lms/ui/common/ValueStringModel.hpp
+++ b/src/lms/ui/common/ValueStringModel.hpp
@@ -23,60 +23,55 @@
#include
-namespace lms::ui {
-
-// Helper class
-template
-class ValueStringModel : public Wt::WStringListModel
+namespace lms::ui
{
- public:
- T getValue(std::size_t row) const
- {
- return Wt::cpp17::any_cast(data(index(static_cast(row), 0), Wt::ItemDataRole::User));
- }
+ // Helper class
+ template
+ class ValueStringModel : public Wt::WStringListModel
+ {
+ public:
+ T getValue(std::size_t row) const
+ {
+ return Wt::cpp17::any_cast(data(index(static_cast(row), 0), Wt::ItemDataRole::User));
+ }
- Wt::WString getString(std::size_t row) const
- {
- return Wt::cpp17::any_cast(data(index(static_cast(row), 0), Wt::ItemDataRole::Display));
- }
+ Wt::WString getString(std::size_t row) const
+ {
+ return Wt::cpp17::any_cast(data(index(static_cast(row), 0), Wt::ItemDataRole::Display));
+ }
- std::optional
- getRowFromString(const Wt::WString& value)
- {
- for (std::size_t i{}; i < static_cast(rowCount()); ++i)
- {
- if (getString(i) == value)
- return i;
- }
+ std::optional getRowFromString(const Wt::WString& value)
+ {
+ for (std::size_t i{}; i < static_cast(rowCount()); ++i)
+ {
+ if (getString(i) == value)
+ return i;
+ }
- return std::nullopt;
- }
+ return std::nullopt;
+ }
- std::optional
- getRowFromValue(const T& value)
- {
- for (std::size_t i{}; i < static_cast(rowCount()); ++i)
- {
- if (getValue(i) == value)
- return i;
- }
+ std::optional getRowFromValue(const T& value)
+ {
+ for (std::size_t i{}; i < static_cast(rowCount()); ++i)
+ {
+ if (getValue(i) == value)
+ return i;
+ }
- return std::nullopt;
- }
+ return std::nullopt;
+ }
- void
- add(const Wt::WString& str, const T& value)
- {
- insertRows(rowCount(), 1);
- setData(rowCount() - 1, 0, value, Wt::ItemDataRole::User);
- setData(rowCount() - 1, 0, str, Wt::ItemDataRole::Display);
- }
-
- void
- clear()
- {
- removeRows(0, rowCount());
- }
-};
+ void add(const Wt::WString& str, const T& value)
+ {
+ insertRows(rowCount(), 1);
+ setData(rowCount() - 1, 0, value, Wt::ItemDataRole::User);
+ setData(rowCount() - 1, 0, str, Wt::ItemDataRole::Display);
+ }
+ void clear()
+ {
+ removeRows(0, rowCount());
+ }
+ };
} // namespace lms::ui
diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp
index d927c86b..097b63a3 100644
--- a/src/lms/ui/explore/ArtistCollector.cpp
+++ b/src/lms/ui/explore/ArtistCollector.cpp
@@ -39,6 +39,7 @@ namespace lms::ui
scrobbling::IScrobblingService& scrobblingService{ *core::Service::get() };
const Range range{ getActualRange(requestedRange) };
+ const Filters& filters{ getFilters() };
RangeResults artists;
@@ -52,7 +53,8 @@ namespace lms::ui
{
feedback::IFeedbackService::ArtistFindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setLinkType(_linkType);
params.setSortMethod(ArtistSortMethod::StarredDateDesc);
params.setRange(range);
@@ -64,7 +66,8 @@ namespace lms::ui
{
scrobbling::IScrobblingService::ArtistFindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setLinkType(_linkType);
params.setRange(range);
@@ -76,7 +79,8 @@ namespace lms::ui
{
scrobbling::IScrobblingService::ArtistFindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setLinkType(_linkType);
params.setRange(range);
@@ -87,7 +91,8 @@ namespace lms::ui
case Mode::RecentlyAdded:
{
Artist::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setLinkType(_linkType);
params.setSortMethod(ArtistSortMethod::LastWritten);
params.setRange(range);
@@ -102,7 +107,8 @@ namespace lms::ui
case Mode::Search:
{
Artist::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setKeywords(getSearchKeywords());
params.setLinkType(_linkType);
params.setRange(range);
@@ -117,7 +123,8 @@ namespace lms::ui
case Mode::All:
{
Artist::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setLinkType(_linkType);
params.setSortMethod(ArtistSortMethod::SortName);
params.setRange(range);
@@ -143,7 +150,8 @@ namespace lms::ui
if (!_randomArtists)
{
Artist::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setLinkType(_linkType);
params.setSortMethod(ArtistSortMethod::Random);
params.setRange(Range{ 0, getMaxCount() });
diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp
index f8e31405..6c6b9d23 100644
--- a/src/lms/ui/explore/ArtistView.cpp
+++ b/src/lms/ui/explore/ArtistView.cpp
@@ -138,7 +138,7 @@ namespace lms::ui
for (const db::Cluster::pointer& cluster : clusters)
{
const db::ClusterId clusterId = cluster->getId();
- Wt::WInteractWidget* entry{ clusterContainers->addWidget(utils::createCluster(clusterId)) };
+ Wt::WInteractWidget* entry{ clusterContainers->addWidget(utils::createFilterCluster(clusterId)) };
entry->clicked().connect([this, clusterId]
{
_filters.add(clusterId);
@@ -198,7 +198,8 @@ namespace lms::ui
_releaseContainers.clear();
Release::FindParameters params;
- params.setClusters(_filters.getClusterIds());
+ params.setClusters(_filters.getClusters());
+ params.setMediaLibrary(_filters.getMediaLibrary());
params.setArtist(_artistId, { TrackArtistLinkType::ReleaseArtist }, {});
params.setSortMethod(ReleaseSortMethod::OriginalDateDesc);
@@ -257,7 +258,8 @@ namespace lms::ui
_appearsOnReleaseContainer = {};
Release::FindParameters params;
- params.setClusters(_filters.getClusterIds());
+ params.setClusters(_filters.getClusters());
+ params.setMediaLibrary(_filters.getMediaLibrary());
params.setArtist(_artistId, types, { TrackArtistLinkType::ReleaseArtist });
params.setSortMethod(ReleaseSortMethod::OriginalDateDesc);
@@ -343,7 +345,8 @@ namespace lms::ui
const Range range{ static_cast(_trackContainer->getCount()), _tracksBatchSize };
Track::FindParameters params;
- params.setClusters(_filters.getClusterIds());
+ params.setClusters(_filters.getClusters());
+ params.setMediaLibrary(_filters.getMediaLibrary());
params.setArtist(_artistId);
params.setRange(range);
params.setSortMethod(TrackSortMethod::Name);
diff --git a/src/lms/ui/explore/DatabaseCollectorBase.hpp b/src/lms/ui/explore/DatabaseCollectorBase.hpp
index 93f3bb21..dddd039e 100644
--- a/src/lms/ui/explore/DatabaseCollectorBase.hpp
+++ b/src/lms/ui/explore/DatabaseCollectorBase.hpp
@@ -58,7 +58,7 @@ namespace lms::ui
protected:
Range getActualRange(std::optional range) const;
std::size_t getMaxCount() const;
- Filters& getFilters() { return _filters; }
+ const Filters& getFilters() { return _filters; }
const std::vector& getSearchKeywords() const { return _searchKeywords; }
private:
diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp
index f7ee3ff9..1062b715 100644
--- a/src/lms/ui/explore/Filters.cpp
+++ b/src/lms/ui/explore/Filters.cpp
@@ -19,21 +19,79 @@
#include "Filters.hpp"
+#include
+
#include
#include
#include
#include
#include "database/Cluster.hpp"
+#include "database/MediaLibrary.hpp"
#include "database/Session.hpp"
+#include "common/ValueStringModel.hpp"
#include "LmsApplication.hpp"
#include "Utils.hpp"
#include "ModalManager.hpp"
-namespace lms::ui
+namespace lms::ui
{
- using namespace db;
+ namespace
+ {
+ struct MediaLibraryTag {};
+
+ using TypeVariant = std::variant;
+ using TypeModel = ValueStringModel;
+
+ std::unique_ptr createTypeModel()
+ {
+ auto typeModel{ std::make_unique() };
+ typeModel->add(Wt::WString::tr("Lms.Explore.media-library"), MediaLibraryTag{});
+
+ auto transaction{ LmsApp->getDbSession().createReadTransaction() };
+
+ db::ClusterType::find(LmsApp->getDbSession(), [&](const db::ClusterType::pointer& clusterType)
+ {
+ typeModel->add(Wt::WString::fromUTF8(std::string{ clusterType->getName() }), clusterType->getId());
+ });
+
+ return typeModel;
+ }
+
+ using ValueVariant = std::variant;
+ using ValueModel = ValueStringModel;
+
+ std::unique_ptr createValueModel(TypeVariant type)
+ {
+ db::Session& session{ LmsApp->getDbSession() };
+
+ auto valueModel{ std::make_unique() };
+
+ auto transaction{ session.createReadTransaction() };
+
+ if (std::holds_alternative(type))
+ {
+ db::MediaLibrary::find(session, [&](const db::MediaLibrary::pointer& library)
+ {
+ valueModel->add(Wt::WString::fromUTF8(std::string{ library->getName() }), library->getId());
+ });
+ }
+ else if (const db::ClusterTypeId * clusterTypeId{ std::get_if(&type) })
+ {
+ db::Cluster::FindParameters params;
+ params.setClusterType(*clusterTypeId);
+ params.setSortMethod(db::ClusterSortMethod::Name);
+
+ db::Cluster::find(session, params, [&](const db::Cluster::pointer& cluster)
+ {
+ valueModel->add(Wt::WString::fromUTF8(std::string{ cluster->getName() }), cluster->getId());
+ });
+ }
+
+ return valueModel;
+ }
+ }
void Filters::showDialog()
{
@@ -43,32 +101,27 @@ namespace lms::ui
dialog->addFunction("id", &Wt::WTemplate::Functions::id);
Wt::WComboBox* typeCombo{ dialog->bindNew("type") };
+ const std::shared_ptr typeModel{ createTypeModel() };
+ typeCombo->setModel(typeModel);
+
Wt::WComboBox* valueCombo{ dialog->bindNew("value") };
Wt::WPushButton* addBtn{ dialog->bindNew("add-btn", Wt::WString::tr("Lms.Explore.add-filter")) };
- addBtn->clicked().connect([this, typeCombo, valueCombo, dialogPtr]
+ addBtn->clicked().connect([this, valueCombo, dialogPtr]
{
- const std::string type{ typeCombo->valueText().toUTF8() };
- const std::string value{ valueCombo->valueText().toUTF8() };
-
- // TODO use a model to store the cluster.id() values
- ClusterId clusterId{};
+ const auto valueModel{ std::static_pointer_cast(valueCombo->model()) };
+ const ValueVariant value{ valueModel->getValue(valueCombo->currentIndex()) };
+ if (const db::MediaLibraryId * mediaLibraryId{ std::get_if(&value) })
{
- auto transaction{ LmsApp->getDbSession().createReadTransaction() };
-
- ClusterType::pointer clusterType{ ClusterType::find(LmsApp->getDbSession(), type) };
- if (!clusterType)
- return;
-
- Cluster::pointer cluster{ clusterType->getCluster(value) };
- if (!cluster)
- return;
-
- clusterId = cluster->getId();
+ set(*mediaLibraryId);
+ }
+ else if (const db::ClusterId * clusterId{ std::get_if(&value) })
+ {
+ add(*clusterId);
}
- add(clusterId);
+ // TODO
LmsApp->getModalManager().dispose(dialogPtr);
});
@@ -78,79 +131,20 @@ namespace lms::ui
LmsApp->getModalManager().dispose(dialogPtr);
});
- // Populate data
- {
- auto transaction{ LmsApp->getDbSession().createReadTransaction() };
-
- const auto clusterTypesIds{ ClusterType::findUsed(LmsApp->getDbSession()) };
- for (const ClusterTypeId clusterTypeId : clusterTypesIds.results)
+ typeCombo->activated().connect([valueCombo, typeModel](int row)
{
- const auto clusterType{ ClusterType::find(LmsApp->getDbSession(), clusterTypeId) };
- typeCombo->addItem(Wt::WString::fromUTF8(std::string{ clusterType->getName() }));
- }
-
- if (!clusterTypesIds.results.empty())
- {
- const auto clusterType{ ClusterType::find(LmsApp->getDbSession(), clusterTypesIds.results.front()) };
-
- for (const Cluster::pointer& cluster : clusterType->getClusters())
- {
- if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), cluster->getId()) == _clusterIds.end())
- valueCombo->addItem(Wt::WString::fromUTF8(std::string{ cluster->getName() }));
- }
- }
- }
-
- typeCombo->changed().connect([this, typeCombo, valueCombo]
- {
- const std::string name{ typeCombo->valueText().toUTF8() };
+ const TypeVariant type{ typeModel->getValue(row) };
+ const std::shared_ptr valueModel{ createValueModel(type) };
valueCombo->clear();
-
- auto transaction{ LmsApp->getDbSession().createReadTransaction() };
-
- auto clusterType{ ClusterType::find(LmsApp->getDbSession(), name) };
- for (const Cluster::pointer& cluster : clusterType->getClusters())
- {
- if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), cluster->getId()) == _clusterIds.end())
- valueCombo->addItem(Wt::WString::fromUTF8(std::string{ cluster->getName() }));
- }
+ valueCombo->setModel(valueModel);
});
+ typeCombo->activated().emit(0); // force emit to refresh the type combo model
+
LmsApp->getModalManager().show(std::move(dialog));
}
- void Filters::add(ClusterId clusterId)
- {
- if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), clusterId) != std::cend(_clusterIds))
- return;
-
- Wt::WInteractWidget* filter{};
-
- {
- auto cluster{ utils::createCluster(clusterId, true) };
- if (!cluster)
- return;
-
- filter = _filters->addWidget(std::move(cluster));
- }
-
- _clusterIds.push_back(clusterId);
-
- filter->clicked().connect([this, filter, clusterId]
- {
- _filters->removeWidget(filter);
- _clusterIds.erase(std::remove_if(std::begin(_clusterIds), std::end(_clusterIds), [clusterId](ClusterId id) { return id == clusterId; }), std::end(_clusterIds));
- _sigUpdated.emit();
- });
-
- LmsApp->notifyMsg(Notification::Type::Info,
- Wt::WString::tr("Lms.Explore.filters"),
- Wt::WString::tr("Lms.Explore.filter-added"), std::chrono::seconds{ 2 });
-
- _sigUpdated.emit();
- }
-
Filters::Filters()
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.template.filters") }
{
@@ -162,4 +156,73 @@ namespace lms::ui
_filters = bindNew("clusters");
}
+
+ void Filters::add(db::ClusterId clusterId)
+ {
+ if (std::find(std::cbegin(_clusterIds), std::cend(_clusterIds), clusterId) != std::cend(_clusterIds))
+ return;
+
+ Wt::WInteractWidget* filter{};
+
+ {
+ auto cluster{ utils::createFilterCluster(clusterId, true) };
+ if (!cluster)
+ return;
+
+ filter = _filters->addWidget(std::move(cluster));
+ }
+
+ _clusterIds.push_back(clusterId);
+
+ filter->clicked().connect([this, filter, clusterId]
+ {
+ _filters->removeWidget(filter);
+ _clusterIds.erase(std::remove_if(std::begin(_clusterIds), std::end(_clusterIds), [clusterId](db::ClusterId id) { return id == clusterId; }), std::end(_clusterIds));
+ _sigUpdated.emit();
+ });
+
+ emitFilterAddedNotification();
+ }
+
+ void Filters::set(db::MediaLibraryId mediaLibraryId)
+ {
+ if (_mediaLibraryFilter)
+ {
+ _filters->removeWidget(_mediaLibraryFilter);
+ _mediaLibraryFilter = nullptr;
+ _mediaLibraryId = db::MediaLibraryId{};
+ }
+
+ std::string libraryName;
+ {
+ auto transaction{ LmsApp->getDbSession().createReadTransaction() };
+
+ const auto library{ db::MediaLibrary::find(LmsApp->getDbSession(), mediaLibraryId) };
+ if (!library)
+ return;
+
+ libraryName = library->getName();
+ }
+
+ _mediaLibraryId = mediaLibraryId;
+ _mediaLibraryFilter = _filters->addWidget(utils::createFilter(Wt::WString::fromUTF8(libraryName), Wt::WString::tr("Lms.Explore.media-library"), "bg-primary", true));
+ _mediaLibraryFilter->clicked().connect(_mediaLibraryFilter, [this]
+ {
+ _filters->removeWidget(_mediaLibraryFilter);
+ _mediaLibraryId = db::MediaLibraryId{};
+ _mediaLibraryFilter = nullptr;
+ _sigUpdated.emit();
+ });
+
+ emitFilterAddedNotification();
+ }
+
+ void Filters::emitFilterAddedNotification()
+ {
+ LmsApp->notifyMsg(Notification::Type::Info,
+ Wt::WString::tr("Lms.Explore.filters"),
+ Wt::WString::tr("Lms.Explore.filter-added"), std::chrono::seconds{ 2 });
+
+ _sigUpdated.emit();
+ }
} // namespace lms::ui
diff --git a/src/lms/ui/explore/Filters.hpp b/src/lms/ui/explore/Filters.hpp
index 8db27180..f13a1d5d 100644
--- a/src/lms/ui/explore/Filters.hpp
+++ b/src/lms/ui/explore/Filters.hpp
@@ -19,12 +19,14 @@
#pragma once
+#include
#include
#include
#include
#include
#include "database/ClusterId.hpp"
+#include "database/MediaLibraryId.hpp"
#include "Filters.hpp"
@@ -35,17 +37,24 @@ namespace lms::ui
public:
Filters();
- const std::vector& getClusterIds() const { return _clusterIds; }
+ std::span getClusters() const { return _clusterIds; }
+ db::MediaLibraryId getMediaLibrary() const { return _mediaLibraryId; }
+
void add(db::ClusterId clusterId);
Wt::Signal<>& updated() { return _sigUpdated; }
private:
void showDialog();
+ void set(db::MediaLibraryId mediaLibraryId);
+ void emitFilterAddedNotification();
- Wt::WContainerWidget* _filters;
+ Wt::WContainerWidget* _filters{};
Wt::Signal<> _sigUpdated;
std::vector _clusterIds;
+
+ Wt::WInteractWidget* _mediaLibraryFilter{};
+ db::MediaLibraryId _mediaLibraryId;
};
} // namespace lms::ui
diff --git a/src/lms/ui/explore/PlayQueueController.cpp b/src/lms/ui/explore/PlayQueueController.cpp
index 4a24d412..71606e46 100644
--- a/src/lms/ui/explore/PlayQueueController.cpp
+++ b/src/lms/ui/explore/PlayQueueController.cpp
@@ -28,10 +28,9 @@
namespace lms::ui
{
-
namespace
{
- std::vector getArtistsTracks(db::Session& session, const std::vector& artistsId, const std::vector& clusters, std::size_t maxTrackCount)
+ std::vector getArtistsTracks(db::Session& session, const std::vector& artistsId, const Filters& filters, std::size_t maxTrackCount)
{
assert(maxTrackCount);
@@ -44,7 +43,8 @@ namespace lms::ui
db::Track::FindParameters params;
params.setArtist(artistId);
params.setSortMethod(db::TrackSortMethod::DateDescAndRelease);
- params.setClusters(clusters);
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
const auto tracks{ db::Track::findIds(session, params) };
@@ -59,7 +59,7 @@ namespace lms::ui
return res;
}
- std::vector getReleasesTracks(db::Session& session, const std::vector& releasesId, const std::vector& clusters, std::size_t maxTrackCount)
+ std::vector getReleasesTracks(db::Session& session, const std::vector& releasesId, const Filters& filters, std::size_t maxTrackCount)
{
using namespace db;
assert(maxTrackCount);
@@ -73,7 +73,8 @@ namespace lms::ui
db::Track::FindParameters params;
params.setRelease(releaseId);
params.setSortMethod(db::TrackSortMethod::Release);
- params.setClusters(clusters);
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
const auto tracks{ db::Track::findIds(session, params) };
@@ -88,7 +89,7 @@ namespace lms::ui
return res;
}
- std::vector getDiscTracks(db::Session& session, const std::vector& discs, const std::vector& clusters, std::size_t maxTrackCount)
+ std::vector getDiscTracks(db::Session& session, const std::vector& discs, const Filters& filters, std::size_t maxTrackCount)
{
using namespace db;
assert(maxTrackCount);
@@ -103,7 +104,8 @@ namespace lms::ui
params.setRelease(disc.releaseId);
params.setSortMethod(db::TrackSortMethod::Release);
params.setDiscNumber(disc.discNumber);
- params.setClusters(clusters);
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
const auto tracks{ db::Track::findIds(session, params) };
@@ -118,7 +120,7 @@ namespace lms::ui
return res;
}
- std::vector getTrackListTracks(db::Session& session, db::TrackListId trackListId, const std::vector& clusters, std::size_t maxTrackCount)
+ std::vector getTrackListTracks(db::Session& session, db::TrackListId trackListId, const Filters& filters, std::size_t maxTrackCount)
{
using namespace db;
assert(maxTrackCount);
@@ -127,7 +129,8 @@ namespace lms::ui
db::Track::FindParameters params;
params.setTrackList(trackListId);
- params.setClusters(clusters);
+ params.setClusters(filters.getClusters());
+ params.setMediaLibrary(filters.getMediaLibrary());
params.setRange(db::Range{ 0, maxTrackCount });
params.setSortMethod(TrackSortMethod::TrackList);
@@ -143,13 +146,13 @@ namespace lms::ui
void PlayQueueController::processCommand(Command command, const std::vector& artistIds)
{
- const std::vector tracks{ getArtistsTracks(LmsApp->getDbSession(), artistIds, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
+ const std::vector tracks{ getArtistsTracks(LmsApp->getDbSession(), artistIds, _filters, _maxTrackCountToEnqueue) };
processCommand(command, tracks);
}
void PlayQueueController::processCommand(Command command, const std::vector& releaseIds)
{
- const std::vector tracks{ getReleasesTracks(LmsApp->getDbSession(), releaseIds, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
+ const std::vector tracks{ getReleasesTracks(LmsApp->getDbSession(), releaseIds, _filters, _maxTrackCountToEnqueue) };
processCommand(command, tracks);
}
@@ -175,13 +178,13 @@ namespace lms::ui
void PlayQueueController::processCommand(Command command, db::TrackListId trackListId)
{
- const std::vector tracks{ getTrackListTracks(LmsApp->getDbSession(), trackListId, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
+ const std::vector tracks{ getTrackListTracks(LmsApp->getDbSession(), trackListId, _filters, _maxTrackCountToEnqueue) };
processCommand(command, tracks);
}
void PlayQueueController::processCommand(Command command, const std::vector& discs)
{
- const std::vector tracks{ getDiscTracks(LmsApp->getDbSession(), discs, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
+ const std::vector tracks{ getDiscTracks(LmsApp->getDbSession(), discs, _filters, _maxTrackCountToEnqueue) };
processCommand(command, tracks);
}
@@ -197,7 +200,7 @@ namespace lms::ui
releaseId = track->getRelease()->getId();
}
- const std::vector tracks{ getReleasesTracks(LmsApp->getDbSession(), {releaseId}, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
+ const std::vector tracks{ getReleasesTracks(LmsApp->getDbSession(), {releaseId}, _filters, _maxTrackCountToEnqueue) };
auto itTrack{ std::find(std::cbegin(tracks), std::cend(tracks), trackId) };
if (itTrack == std::cend(tracks))
return;
diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp
index 2623f94c..98f37025 100644
--- a/src/lms/ui/explore/ReleaseCollector.cpp
+++ b/src/lms/ui/explore/ReleaseCollector.cpp
@@ -51,7 +51,8 @@ namespace lms::ui
{
feedback::IFeedbackService::FindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setRange(range);
releases = feedbackService.findStarredReleases(params);
break;
@@ -61,7 +62,8 @@ namespace lms::ui
{
scrobbling::IScrobblingService::FindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setRange(range);
releases = scrobblingService.getRecentReleases(params);
@@ -72,7 +74,8 @@ namespace lms::ui
{
scrobbling::IScrobblingService::FindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setRange(range);
releases = scrobblingService.getTopReleases(params);
@@ -82,7 +85,8 @@ namespace lms::ui
case Mode::RecentlyAdded:
{
Release::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setSortMethod(ReleaseSortMethod::LastWritten);
params.setRange(range);
@@ -96,7 +100,8 @@ namespace lms::ui
case Mode::Search:
{
Release::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setKeywords(getSearchKeywords());
params.setRange(range);
@@ -110,7 +115,8 @@ namespace lms::ui
case Mode::All:
{
Release::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setSortMethod(ReleaseSortMethod::Name);
params.setRange(range);
@@ -135,7 +141,8 @@ namespace lms::ui
if (!_randomReleases)
{
Release::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setSortMethod(ReleaseSortMethod::Random);
params.setRange(Range{ 0, getMaxCount() });
diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp
index 7a5a1e12..587b6d09 100644
--- a/src/lms/ui/explore/ReleaseView.cpp
+++ b/src/lms/ui/explore/ReleaseView.cpp
@@ -262,7 +262,7 @@ namespace lms::ui
for (const db::Cluster::pointer& cluster : clusters)
{
const ClusterId clusterId{ cluster->getId() };
- Wt::WInteractWidget* entry{ clusterContainers->addWidget(utils::createCluster(clusterId)) };
+ Wt::WInteractWidget* entry{ clusterContainers->addWidget(utils::createFilterCluster(clusterId)) };
entry->clicked().connect([this, clusterId]
{
_filters.add(clusterId);
@@ -394,7 +394,8 @@ namespace lms::ui
db::Track::FindParameters params;
params.setRelease(_releaseId);
params.setSortMethod(db::TrackSortMethod::Release);
- params.setClusters(_filters.getClusterIds());
+ params.setClusters(_filters.getClusters());
+ params.setMediaLibrary(_filters.getMediaLibrary());
db::Track::find(LmsApp->getDbSession(), params, [&](const db::Track::pointer& track)
{
diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp
index 4af3853c..b54d5a5a 100644
--- a/src/lms/ui/explore/TrackCollector.cpp
+++ b/src/lms/ui/explore/TrackCollector.cpp
@@ -53,7 +53,8 @@ namespace lms::ui
case Mode::Starred:
{
feedback::IFeedbackService::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setRange(range);
params.setUser(LmsApp->getUserId());
tracks = feedbackService.findStarredTracks(params);
@@ -64,7 +65,8 @@ namespace lms::ui
{
scrobbling::IScrobblingService::FindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setRange(range);
tracks = scrobblingService.getRecentTracks(params);
@@ -75,7 +77,8 @@ namespace lms::ui
{
scrobbling::IScrobblingService::FindParameters params;
params.setUser(LmsApp->getUserId());
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setRange(range);
tracks = scrobblingService.getTopTracks(params);
@@ -85,7 +88,8 @@ namespace lms::ui
case Mode::RecentlyAdded:
{
Track::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setSortMethod(TrackSortMethod::LastWritten);
params.setRange(range);
@@ -99,7 +103,8 @@ namespace lms::ui
case Mode::Search:
{
Track::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setKeywords(getSearchKeywords());
params.setRange(range);
@@ -113,7 +118,8 @@ namespace lms::ui
case Mode::All:
{
Track::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setRange(range);
{
@@ -137,7 +143,8 @@ namespace lms::ui
if (!_randomTracks)
{
Track::FindParameters params;
- params.setClusters(getFilters().getClusterIds());
+ params.setClusters(getFilters().getClusters());
+ params.setMediaLibrary(getFilters().getMediaLibrary());
params.setSortMethod(TrackSortMethod::Random);
params.setRange(Range{ 0, getMaxCount() });
diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp
index b2496405..24fe180f 100644
--- a/src/lms/ui/explore/TrackListHelpers.cpp
+++ b/src/lms/ui/explore/TrackListHelpers.cpp
@@ -138,7 +138,7 @@ namespace lms::ui::TrackListHelpers
trackInfo->bindInt("playcount", core::Service::get()->getCount(LmsApp->getUserId(), track->getId()));
- Wt::WContainerWidget* clusterContainer{ trackInfo->bindWidget("clusters", utils::createClustersForTrack(track, filters)) };
+ Wt::WContainerWidget* clusterContainer{ trackInfo->bindWidget("clusters", utils::createFilterClustersForTrack(track, filters)) };
if (clusterContainer->count() > 0)
trackInfo->setCondition("if-has-clusters", true);
diff --git a/src/lms/ui/explore/TrackListView.cpp b/src/lms/ui/explore/TrackListView.cpp
index 4fd379d3..57220e95 100644
--- a/src/lms/ui/explore/TrackListView.cpp
+++ b/src/lms/ui/explore/TrackListView.cpp
@@ -107,7 +107,7 @@ namespace lms::ui
for (const db::Cluster::pointer& cluster : clusters)
{
const ClusterId clusterId{ cluster->getId() };
- Wt::WInteractWidget* entry{ clusterContainers->addWidget(utils::createCluster(clusterId)) };
+ Wt::WInteractWidget* entry{ clusterContainers->addWidget(utils::createFilterCluster(clusterId)) };
entry->clicked().connect([this, clusterId]
{
_filters.add(clusterId);
@@ -183,7 +183,8 @@ namespace lms::ui
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
db::Track::FindParameters params;
- params.setClusters(_filters.getClusterIds());
+ params.setClusters(_filters.getClusters());
+ params.setMediaLibrary(_filters.getMediaLibrary());
params.setTrackList(_trackListId);
params.setSortMethod(db::TrackSortMethod::TrackList);
params.setRange(db::Range{ static_cast(_container->getCount()), _batchSize });
diff --git a/src/lms/ui/explore/TrackListsView.cpp b/src/lms/ui/explore/TrackListsView.cpp
index fb023f35..4d903964 100644
--- a/src/lms/ui/explore/TrackListsView.cpp
+++ b/src/lms/ui/explore/TrackListsView.cpp
@@ -101,7 +101,8 @@ namespace lms::ui
auto transaction{ session.createReadTransaction() };
TrackList::FindParameters params;
- params.setClusters(_filters.getClusterIds());
+ params.setClusters(_filters.getClusters());
+ params.setMediaLibrary(_filters.getMediaLibrary());
params.setUser(LmsApp->getUserId());
params.setType(TrackListType::Playlist);
params.setRange(range);