UI: added away to filter all the views by music library
This commit is contained in:
@@ -158,6 +158,7 @@
|
|||||||
<message id="Lms.Explore.duration">Duration</message>
|
<message id="Lms.Explore.duration">Duration</message>
|
||||||
<message id="Lms.Explore.filter-added">Filter added</message>
|
<message id="Lms.Explore.filter-added">Filter added</message>
|
||||||
<message id="Lms.Explore.filters">Filters</message>
|
<message id="Lms.Explore.filters">Filters</message>
|
||||||
|
<message id="Lms.Explore.media-library">Music Library</message>
|
||||||
<message id="Lms.Explore.most-played">Most played</message>
|
<message id="Lms.Explore.most-played">Most played</message>
|
||||||
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
||||||
|
|||||||
@@ -158,6 +158,7 @@
|
|||||||
<message id="Lms.Explore.duration">Durée</message>
|
<message id="Lms.Explore.duration">Durée</message>
|
||||||
<message id="Lms.Explore.filter-added">Filtre ajouté</message>
|
<message id="Lms.Explore.filter-added">Filtre ajouté</message>
|
||||||
<message id="Lms.Explore.filters">Filtres</message>
|
<message id="Lms.Explore.filters">Filtres</message>
|
||||||
|
<message id="Lms.Explore.media-library">Bibliothèque musicale</message>
|
||||||
<message id="Lms.Explore.most-played">Plus joués</message>
|
<message id="Lms.Explore.most-played">Plus joués</message>
|
||||||
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
||||||
|
|||||||
@@ -110,6 +110,7 @@
|
|||||||
<message id="Lms.Admin.ScannerController.status-in-progress">Scansione: passo {1}/{2}</message>
|
<message id="Lms.Admin.ScannerController.status-in-progress">Scansione: passo {1}/{2}</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-checking-for-duplicate-files">Controllo duplicati... {1} files</message>
|
<message id="Lms.Admin.ScannerController.step-checking-for-duplicate-files">Controllo duplicati... {1} files</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-checking-for-missing-files">Controllo file... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-checking-for-missing-files">Controllo file... {1}%</message>
|
||||||
|
<message id="Lms.Admin.ScannerController.step-compact">Compattazione del database...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calcolo statistiche... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calcolo statistiche... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-discovering-files">File trovati: {1} files</message>
|
<message id="Lms.Admin.ScannerController.step-discovering-files">File trovati: {1} files</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Recupero metadati da AcousticBrainz: {1}/{2} tracce ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Recupero metadati da AcousticBrainz: {1}/{2} tracce ({3}%)...</message>
|
||||||
@@ -157,6 +158,7 @@
|
|||||||
<message id="Lms.Explore.duration">Durata</message>
|
<message id="Lms.Explore.duration">Durata</message>
|
||||||
<message id="Lms.Explore.filter-added">Filtro aggiunto</message>
|
<message id="Lms.Explore.filter-added">Filtro aggiunto</message>
|
||||||
<message id="Lms.Explore.filters">Filtri</message>
|
<message id="Lms.Explore.filters">Filtri</message>
|
||||||
|
<message id="Lms.Explore.media-library">Libreria musicale</message>
|
||||||
<message id="Lms.Explore.most-played">Più ascoltati</message>
|
<message id="Lms.Explore.most-played">Più ascoltati</message>
|
||||||
<message id="Lms.Explore.musicbrainz-artist">Artista MusicBrainz</message>
|
<message id="Lms.Explore.musicbrainz-artist">Artista MusicBrainz</message>
|
||||||
<message id="Lms.Explore.musicbrainz-release">Album MusicBrainz</message>
|
<message id="Lms.Explore.musicbrainz-release">Album MusicBrainz</message>
|
||||||
|
|||||||
@@ -158,6 +158,7 @@
|
|||||||
|
|
||||||
<message id="Lms.Explore.filter-added">过滤器已添加</message>
|
<message id="Lms.Explore.filter-added">过滤器已添加</message>
|
||||||
<message id="Lms.Explore.filters">过滤器</message>
|
<message id="Lms.Explore.filters">过滤器</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.most-played">播放最多</message>
|
<message id="Lms.Explore.most-played">播放最多</message>
|
||||||
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz 歌手</message>
|
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz 歌手</message>
|
||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz 专辑</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz 专辑</message>
|
||||||
|
|||||||
@@ -43,17 +43,19 @@ namespace lms::db
|
|||||||
query.groupBy("c.id");
|
query.groupBy("c.id");
|
||||||
|
|
||||||
if (params.track.isValid() || params.release.isValid())
|
if (params.track.isValid() || params.release.isValid())
|
||||||
{
|
|
||||||
query.join("track_cluster t_c ON t_c.cluster_id = c.id");
|
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())
|
if (!params.clusterTypeName.empty())
|
||||||
query.join("cluster_type c_t ON c_t.id = c.cluster_type_id");
|
query.join("cluster_type c_t ON c_t.id = c.cluster_type_id");
|
||||||
|
|
||||||
if (params.track.isValid())
|
if (params.track.isValid())
|
||||||
query.where("t.id = ?").bind(params.track);
|
query.where("t_c.track_id = ?").bind(params.track);
|
||||||
|
|
||||||
if (params.release.isValid())
|
if (params.release.isValid())
|
||||||
|
{
|
||||||
|
query.join("track t ON t.id = t_c.track_id");
|
||||||
query.where("t.release_id = ?").bind(params.release);
|
query.where("t.release_id = ?").bind(params.release);
|
||||||
|
}
|
||||||
|
|
||||||
assert(!params.clusterType.isValid() || params.clusterTypeName.empty());
|
assert(!params.clusterType.isValid() || params.clusterTypeName.empty());
|
||||||
if (params.clusterType.isValid())
|
if (params.clusterType.isValid())
|
||||||
@@ -61,6 +63,17 @@ namespace lms::db
|
|||||||
else if (!params.clusterTypeName.empty())
|
else if (!params.clusterTypeName.empty())
|
||||||
query.where("c_t.name = ?").bind(params.clusterTypeName);
|
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;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +221,12 @@ namespace lms::db
|
|||||||
return utils::execRangeQuery<ClusterTypeId>(query, range);
|
return utils::execRangeQuery<ClusterTypeId>(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClusterType::find(Session& session, const std::function<void(const pointer&)>& func)
|
||||||
|
{
|
||||||
|
auto query{ session.getDboSession()->find<ClusterType>() };
|
||||||
|
return utils::forEachQueryResult(query, func);
|
||||||
|
}
|
||||||
|
|
||||||
ClusterType::pointer ClusterType::find(Session& session, std::string_view name)
|
ClusterType::pointer ClusterType::find(Session& session, std::string_view name)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace lms::db
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
static constexpr Version LMS_DATABASE_VERSION{ 57 };
|
static constexpr Version LMS_DATABASE_VERSION{ 58 };
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionInfo::VersionInfo()
|
VersionInfo::VersionInfo()
|
||||||
@@ -455,11 +455,17 @@ SELECT
|
|||||||
void migrateFromV56(Session& session)
|
void migrateFromV56(Session& session)
|
||||||
{
|
{
|
||||||
// Make sure we remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
|
// Make sure we remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
|
||||||
std::vector<std::string> indexeNames{ utils::fetchQueryResults(session.getDboSession()->query<std::string>(R"(SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE '%_idx')")) };
|
std::vector<std::string> indexeNames{ utils::fetchQueryResults(session.getDboSession()->query<std::string>(R"(SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE '%_idx')")) };
|
||||||
for (const auto& indexName : indexeNames)
|
for (const auto& indexName : indexeNames)
|
||||||
session.getDboSession()->execute("DROP INDEX " + indexName);
|
session.getDboSession()->execute("DROP INDEX " + indexName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void migrateFromV57(Session& session)
|
||||||
|
{
|
||||||
|
// useless index
|
||||||
|
session.getDboSession()->execute("DROP INDEX cluster_name_idx");
|
||||||
|
}
|
||||||
|
|
||||||
bool doDbMigration(Session& session)
|
bool doDbMigration(Session& session)
|
||||||
{
|
{
|
||||||
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
||||||
@@ -494,6 +500,7 @@ SELECT
|
|||||||
{54, migrateFromV54},
|
{54, migrateFromV54},
|
||||||
{55, migrateFromV55},
|
{55, migrateFromV55},
|
||||||
{56, migrateFromV56},
|
{56, migrateFromV56},
|
||||||
|
{57, migrateFromV57},
|
||||||
};
|
};
|
||||||
|
|
||||||
bool migrationPerformed{};
|
bool migrationPerformed{};
|
||||||
|
|||||||
@@ -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 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_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 cluster_type_name_idx ON cluster_type(name)");
|
||||||
|
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS listen_backend_idx ON listen(backend)");
|
_session.execute("CREATE INDEX IF NOT EXISTS listen_backend_idx ON listen(backend)");
|
||||||
|
|||||||
@@ -35,6 +35,79 @@
|
|||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <typename ResultType>
|
||||||
|
Wt::Dbo::Query<ResultType> createQuery(Session& session, std::string_view itemToSelect, const TrackList::FindParameters& params)
|
||||||
|
{
|
||||||
|
auto query{ session.getDboSession()->query<ResultType>("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 <typename ResultType>
|
||||||
|
Wt::Dbo::Query<ResultType> createQuery(Session& session, const TrackList::FindParameters& params)
|
||||||
|
{
|
||||||
|
std::string_view itemToSelect;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<ResultType, TrackListId>)
|
||||||
|
itemToSelect = "t_l.id";
|
||||||
|
else if constexpr (std::is_same_v<ResultType, Wt::Dbo::ptr<TrackList>>)
|
||||||
|
itemToSelect = "t_l";
|
||||||
|
else
|
||||||
|
static_assert("Unhandled type");
|
||||||
|
|
||||||
|
return createQuery<ResultType>(session, itemToSelect, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TrackList::TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
|
TrackList::TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
|
||||||
: _name{ name }
|
: _name{ name }
|
||||||
, _type{ type }
|
, _type{ type }
|
||||||
@@ -58,7 +131,6 @@ namespace lms::db
|
|||||||
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(*) FROM tracklist"));
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(*) FROM tracklist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TrackList::pointer TrackList::find(Session& session, std::string_view name, TrackListType type, UserId userId)
|
TrackList::pointer TrackList::find(Session& session, std::string_view name, TrackListType type, UserId userId)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
@@ -73,53 +145,17 @@ namespace lms::db
|
|||||||
RangeResults<TrackListId> TrackList::find(Session& session, const FindParameters& params)
|
RangeResults<TrackListId> TrackList::find(Session& session, const FindParameters& params)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
|
auto query{ createQuery<TrackListId>(session, params) };
|
||||||
auto query{ session.getDboSession()->query<TrackListId>("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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils::execRangeQuery<TrackListId>(query, params.range);
|
return utils::execRangeQuery<TrackListId>(query, params.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TrackList::find(Session& session, const FindParameters& params, const std::function<void(const TrackList::pointer&)>& func)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
auto query{ createQuery<Wt::Dbo::ptr<TrackList>>(session, params) };
|
||||||
|
utils::forEachQueryRangeResult(query, params.range, func);
|
||||||
|
}
|
||||||
|
|
||||||
TrackList::pointer TrackList::find(Session& session, TrackListId id)
|
TrackList::pointer TrackList::find(Session& session, TrackListId id)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -67,7 +68,7 @@ namespace lms::db
|
|||||||
ReleaseId release; // artists involved in this release
|
ReleaseId release; // artists involved in this release
|
||||||
MediaLibraryId mediaLibrary; // artists that belong to this library
|
MediaLibraryId mediaLibrary; // artists that belong to this library
|
||||||
|
|
||||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(std::span<const ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||||
FindParameters& setLinkType(std::optional<TrackArtistLinkType> _linkType) { linkType = _linkType; return *this; }
|
FindParameters& setLinkType(std::optional<TrackArtistLinkType> _linkType) { linkType = _linkType; return *this; }
|
||||||
FindParameters& setSortMethod(ArtistSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
|
FindParameters& setSortMethod(ArtistSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
|
||||||
|
|||||||
@@ -46,12 +46,14 @@ namespace lms::db
|
|||||||
struct FindParameters
|
struct FindParameters
|
||||||
{
|
{
|
||||||
std::optional<Range> range;
|
std::optional<Range> range;
|
||||||
|
ClusterSortMethod sortMethod;
|
||||||
ClusterTypeId clusterType; // if non empty, clusters that belong to this cluster type
|
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
|
std::string clusterTypeName; // if non empty, clusters that belong to this cluster type
|
||||||
TrackId track; // if set, clusters involved in this track
|
TrackId track; // if set, clusters involved in this track
|
||||||
ReleaseId release; // if set, clusters involved in this release
|
ReleaseId release; // if set, clusters involved in this release
|
||||||
|
|
||||||
FindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
FindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
||||||
|
FindParameters& setSortMethod(ClusterSortMethod _method) { sortMethod = _method; return *this; }
|
||||||
FindParameters& setClusterType(ClusterTypeId _clusterType) { clusterType = _clusterType; return *this; }
|
FindParameters& setClusterType(ClusterTypeId _clusterType) { clusterType = _clusterType; return *this; }
|
||||||
FindParameters& setClusterTypeName(std::string_view _name) { clusterTypeName = _name; return *this; }
|
FindParameters& setClusterTypeName(std::string_view _name) { clusterTypeName = _name; return *this; }
|
||||||
FindParameters& setTrack(TrackId _track) { track = _track; return *this; }
|
FindParameters& setTrack(TrackId _track) { track = _track; return *this; }
|
||||||
@@ -119,6 +121,7 @@ namespace lms::db
|
|||||||
// Getters
|
// Getters
|
||||||
static std::size_t getCount(Session& session);
|
static std::size_t getCount(Session& session);
|
||||||
static RangeResults<ClusterTypeId> findIds(Session& session, std::optional<Range> range = std::nullopt);
|
static RangeResults<ClusterTypeId> findIds(Session& session, std::optional<Range> range = std::nullopt);
|
||||||
|
static void find(Session& session, const std::function<void(const pointer&)>& func);
|
||||||
static pointer find(Session& session, std::string_view name);
|
static pointer find(Session& session, std::string_view name);
|
||||||
static pointer find(Session& session, ClusterTypeId id);
|
static pointer find(Session& session, ClusterTypeId id);
|
||||||
static RangeResults<ClusterTypeId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt);
|
static RangeResults<ClusterTypeId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -96,7 +97,7 @@ namespace lms::db
|
|||||||
std::string releaseType; // If set, albums that has this release type
|
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
|
MediaLibraryId mediaLibrary; // If set, releases that has at least a track in this library
|
||||||
|
|
||||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(std::span<const ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||||
FindParameters& setSortMethod(ReleaseSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
|
FindParameters& setSortMethod(ReleaseSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
|
||||||
FindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
FindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -80,7 +81,7 @@ namespace lms::db
|
|||||||
std::optional<int> discNumber; // matching this disc number
|
std::optional<int> discNumber; // matching this disc number
|
||||||
MediaLibraryId mediaLibrary; // If set, tracks in this library
|
MediaLibraryId mediaLibrary; // If set, tracks in this library
|
||||||
|
|
||||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(std::span<const ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||||
FindParameters& setName(std::string_view _name) { name = _name; return *this; }
|
FindParameters& setName(std::string_view _name) { name = _name; return *this; }
|
||||||
FindParameters& setSortMethod(TrackSortMethod _method) { sortMethod = _method; return *this; }
|
FindParameters& setSortMethod(TrackSortMethod _method) { sortMethod = _method; return *this; }
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/ClusterId.hpp"
|
#include "database/ClusterId.hpp"
|
||||||
|
#include "database/MediaLibraryId.hpp"
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/TrackId.hpp"
|
#include "database/TrackId.hpp"
|
||||||
#include "database/TrackListId.hpp"
|
#include "database/TrackListId.hpp"
|
||||||
@@ -57,18 +59,21 @@ namespace lms::db
|
|||||||
std::optional<Range> range;
|
std::optional<Range> range;
|
||||||
std::optional<TrackListType> type;
|
std::optional<TrackListType> type;
|
||||||
UserId user; // only tracklists owned by this user
|
UserId user; // only tracklists owned by this user
|
||||||
|
MediaLibraryId mediaLibrary; // only tracklists that have songs in this media library
|
||||||
TrackListSortMethod sortMethod{ TrackListSortMethod::None };
|
TrackListSortMethod sortMethod{ TrackListSortMethod::None };
|
||||||
|
|
||||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(std::span<const ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
FindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
FindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
||||||
FindParameters& setType(TrackListType _type) { type = _type; return *this; }
|
FindParameters& setType(TrackListType _type) { type = _type; return *this; }
|
||||||
FindParameters& setUser(UserId _user) { user = _user; 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; }
|
FindParameters& setSortMethod(TrackListSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; }
|
||||||
};
|
};
|
||||||
static std::size_t getCount(Session& session);
|
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, std::string_view name, TrackListType type, UserId userId);
|
||||||
static pointer find(Session& session, TrackListId tracklistId);
|
static pointer find(Session& session, TrackListId tracklistId);
|
||||||
static RangeResults<TrackListId> find(Session& session, const FindParameters& params);
|
static RangeResults<TrackListId> find(Session& session, const FindParameters& params);
|
||||||
|
static void find(Session& session, const FindParameters& params, const std::function<void(const TrackList::pointer&)>& func);
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
std::string_view getName() const { return _name; }
|
std::string_view getName() const { return _name; }
|
||||||
|
|||||||
@@ -116,6 +116,12 @@ namespace lms::db
|
|||||||
StarredDateDesc,
|
StarredDateDesc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ClusterSortMethod
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Name,
|
||||||
|
};
|
||||||
|
|
||||||
enum class ReleaseSortMethod
|
enum class ReleaseSortMethod
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -137,11 +137,11 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{cluster1.getId()})) };
|
||||||
ASSERT_EQ(tracks.results.size(), 1);
|
ASSERT_EQ(tracks.results.size(), 1);
|
||||||
EXPECT_EQ(tracks.results.front(), track.getId());
|
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<ClusterId>{ cluster2.getId() }));
|
||||||
EXPECT_EQ(tracks.results.size(), 0);
|
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<ClusterTypeId> 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)
|
TEST_F(DatabaseFixture, ClusterType_singleTrack)
|
||||||
{
|
{
|
||||||
@@ -285,7 +314,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
auto transaction{ session.createReadTransaction() };
|
||||||
ASSERT_EQ(Cluster::findOrphanIds(session).results.size(), 2);
|
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<ClusterId>{ unusedCluster.getId() })).results.size(), 0);
|
||||||
EXPECT_EQ(Release::find(session, Release::FindParameters{}).results.size(), 1);
|
EXPECT_EQ(Release::find(session, Release::FindParameters{}).results.size(), 1);
|
||||||
EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 0);
|
EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 0);
|
||||||
EXPECT_EQ(Cluster::computeReleaseCount(session, unusedCluster.getId()), 0);
|
EXPECT_EQ(Cluster::computeReleaseCount(session, unusedCluster.getId()), 0);
|
||||||
@@ -321,7 +350,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{cluster.getId()})) };
|
||||||
ASSERT_EQ(releases.results.size(), 1);
|
ASSERT_EQ(releases.results.size(), 1);
|
||||||
EXPECT_EQ(releases.results.front(), release.getId());
|
EXPECT_EQ(releases.results.front(), release.getId());
|
||||||
}
|
}
|
||||||
@@ -329,7 +358,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{unusedCluster.getId()})) };
|
||||||
EXPECT_EQ(releases.results.size(), 0);
|
EXPECT_EQ(releases.results.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,12 +404,12 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createWriteTransaction() };
|
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<ClusterId>{cluster1.getId()})) };
|
||||||
ASSERT_EQ(artists.results.size(), 1);
|
ASSERT_EQ(artists.results.size(), 1);
|
||||||
EXPECT_EQ(artists.results.front(), artist.getId());
|
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(std::initializer_list<ClusterId>{ 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<ClusterId>{ cluster3.getId() })).results.size(), 0);
|
||||||
|
|
||||||
cluster2.get().modify()->addTrack(track.get());
|
cluster2.get().modify()->addTrack(track.get());
|
||||||
}
|
}
|
||||||
@@ -388,19 +417,19 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{cluster1.getId()})) };
|
||||||
ASSERT_EQ(artists.results.size(), 1);
|
ASSERT_EQ(artists.results.size(), 1);
|
||||||
EXPECT_EQ(artists.results.front(), artist.getId());
|
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<ClusterId>{ cluster2.getId() }));
|
||||||
ASSERT_EQ(artists.results.size(), 1);
|
ASSERT_EQ(artists.results.size(), 1);
|
||||||
EXPECT_EQ(artists.results.front(), artist.getId());
|
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<ClusterId>{ cluster1.getId() }));
|
||||||
ASSERT_EQ(artists.results.size(), 1);
|
ASSERT_EQ(artists.results.size(), 1);
|
||||||
EXPECT_EQ(artists.results.front(), artist.getId());
|
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<ClusterId>{ cluster3.getId() })).results.size(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +458,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{cluster.getId()})) };
|
||||||
ASSERT_EQ(artists.results.size(), 1);
|
ASSERT_EQ(artists.results.size(), 1);
|
||||||
EXPECT_EQ(artists.results.front(), artist.getId());
|
EXPECT_EQ(artists.results.front(), artist.getId());
|
||||||
}
|
}
|
||||||
@@ -581,7 +610,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{cluster.getId()})) };
|
||||||
ASSERT_EQ(artists.results.size(), 1);
|
ASSERT_EQ(artists.results.size(), 1);
|
||||||
EXPECT_EQ(artists.results.front(), artist.getId());
|
EXPECT_EQ(artists.results.front(), artist.getId());
|
||||||
|
|
||||||
@@ -589,7 +618,7 @@ namespace lms::db::tests
|
|||||||
ASSERT_EQ(releases.results.size(), 1);
|
ASSERT_EQ(releases.results.size(), 1);
|
||||||
EXPECT_EQ(releases.results.front(), release.getId());
|
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<ClusterId>{ cluster.getId() }));
|
||||||
ASSERT_EQ(releases.results.size(), 1);
|
ASSERT_EQ(releases.results.size(), 1);
|
||||||
EXPECT_EQ(releases.results.front(), release.getId());
|
EXPECT_EQ(releases.results.front(), release.getId());
|
||||||
}
|
}
|
||||||
@@ -620,7 +649,7 @@ namespace lms::db::tests
|
|||||||
ASSERT_EQ(releases.results.size(), 1);
|
ASSERT_EQ(releases.results.size(), 1);
|
||||||
EXPECT_EQ(releases.results.front(), release.getId());
|
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<ClusterId>{ cluster1.getId(), cluster2.getId() }));
|
||||||
ASSERT_EQ(releases.results.size(), 1);
|
ASSERT_EQ(releases.results.size(), 1);
|
||||||
EXPECT_EQ(releases.results.front(), release.getId());
|
EXPECT_EQ(releases.results.front(), release.getId());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<TrackListEntry>(track1.get(), trackList1.get());
|
||||||
|
session.create<TrackListEntry>(track2.get(), trackList2.get());
|
||||||
|
track2.get().modify()->setMediaLibrary(library.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
std::vector<TrackListId> 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<TrackListId> 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)
|
TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster)
|
||||||
{
|
{
|
||||||
ScopedUser user{ session, "MyUser" };
|
ScopedUser user{ session, "MyUser" };
|
||||||
@@ -163,7 +203,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{cluster.getId()})) };
|
||||||
EXPECT_EQ(trackLists.results.size(), 0);
|
EXPECT_EQ(trackLists.results.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +217,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
auto transaction{ session.createReadTransaction() };
|
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<ClusterId>{cluster.getId()})) };
|
||||||
ASSERT_EQ(trackLists.results.size(), 1);
|
ASSERT_EQ(trackLists.results.size(), 1);
|
||||||
EXPECT_EQ(trackLists.results.front(), trackList1.getId());
|
EXPECT_EQ(trackLists.results.front(), trackList1.getId());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <boost/asio/io_service.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ namespace lms::feedback
|
|||||||
db::MediaLibraryId library;
|
db::MediaLibraryId library;
|
||||||
|
|
||||||
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
||||||
FindParameters& setClusters(const std::vector<db::ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(std::span<const db::ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
||||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <boost/asio/io_service.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ namespace lms::scrobbling
|
|||||||
db::ArtistId artist; // if set, match this artist
|
db::ArtistId artist; // if set, match this artist
|
||||||
|
|
||||||
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
||||||
FindParameters& setClusters(const std::vector<db::ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(std::span<const db::ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
||||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||||
FindParameters& setArtist(db::ArtistId _artist) { artist = _artist; return *this; }
|
FindParameters& setArtist(db::ArtistId _artist) { artist = _artist; return *this; }
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ namespace lms::api::subsonic
|
|||||||
if (const Cluster::pointer cluster{ clusterType->getCluster(genre) })
|
if (const Cluster::pointer cluster{ clusterType->getCluster(genre) })
|
||||||
{
|
{
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters({ cluster->getId() });
|
params.setClusters(std::initializer_list<ClusterId>{ cluster->getId() });
|
||||||
params.setSortMethod(ReleaseSortMethod::Name);
|
params.setSortMethod(ReleaseSortMethod::Name);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
params.setMediaLibrary(mediaLibraryId);
|
params.setMediaLibrary(mediaLibraryId);
|
||||||
@@ -280,7 +280,7 @@ namespace lms::api::subsonic
|
|||||||
Response::Node& songsByGenreNode{ response.createNode("songsByGenre") };
|
Response::Node& songsByGenreNode{ response.createNode("songsByGenre") };
|
||||||
|
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters({ cluster->getId() });
|
params.setClusters(std::initializer_list<ClusterId>{ cluster->getId() });
|
||||||
params.setRange(Range{ offset, count });
|
params.setRange(Range{ offset, count });
|
||||||
params.setMediaLibrary(mediaLibrary);
|
params.setMediaLibrary(mediaLibrary);
|
||||||
|
|
||||||
|
|||||||
@@ -87,9 +87,7 @@ namespace lms::ui
|
|||||||
return trackListModel->getValue(*row);
|
return trackListModel->getValue(*row);
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static std::shared_ptr<TrackListModel> createTrackListModel()
|
||||||
std::shared_ptr<TrackListModel>
|
|
||||||
createTrackListModel()
|
|
||||||
{
|
{
|
||||||
using namespace db;
|
using namespace db;
|
||||||
|
|
||||||
@@ -102,12 +100,10 @@ namespace lms::ui
|
|||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setSortMethod(TrackListSortMethod::Name);
|
params.setSortMethod(TrackListSortMethod::Name);
|
||||||
|
|
||||||
auto tracklists{ TrackList::find(LmsApp->getDbSession(), params) };
|
TrackList::find(LmsApp->getDbSession(), params, [&](const TrackList::pointer& trackList)
|
||||||
for (const TrackListId trackListId : tracklists.results)
|
{
|
||||||
{
|
model->add(Wt::WString::fromUTF8(std::string{ trackList->getName() }), trackList->getId());
|
||||||
const TrackList::pointer trackList{ TrackList::find(LmsApp->getDbSession(), trackListId) };
|
});
|
||||||
model->add(Wt::WString::fromUTF8(std::string{ trackList->getName() }), trackListId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-34
@@ -80,7 +80,18 @@ namespace lms::ui::utils
|
|||||||
return cover;
|
return cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Wt::WInteractWidget> createCluster(db::ClusterId clusterId, bool canDelete)
|
std::unique_ptr<Wt::WInteractWidget> createFilter(const Wt::WString& name, const Wt::WString& tooltip, std::string_view colorStyleClass, bool canDelete)
|
||||||
|
{
|
||||||
|
auto res{ std::make_unique<Wt::WText>(Wt::WString{ canDelete ? "<i class=\"fa fa-times-circle\"></i> " : "" } + 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<Wt::WInteractWidget> createFilterCluster(db::ClusterId clusterId, bool canDelete)
|
||||||
{
|
{
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
||||||
|
|
||||||
@@ -88,7 +99,7 @@ namespace lms::ui::utils
|
|||||||
if (!cluster)
|
if (!cluster)
|
||||||
return {};
|
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)
|
switch (cluster->getType()->getId().getValue() % 8)
|
||||||
{
|
{
|
||||||
@@ -105,14 +116,33 @@ namespace lms::ui::utils
|
|||||||
return "bg-primary";
|
return "bg-primary";
|
||||||
} };
|
} };
|
||||||
|
|
||||||
const std::string styleClass{ getStyleClass(cluster) };
|
return createFilter(Wt::WString::fromUTF8(std::string{ cluster->getName() }), Wt::WString::fromUTF8(std::string{ cluster->getType()->getName() }), getStyleClass(cluster), canDelete);
|
||||||
auto res{ std::make_unique<Wt::WText>(std::string {} + (canDelete ? "<i class=\"fa fa-times-circle\"></i> " : "") + Wt::WString::fromUTF8(std::string{ cluster->getName() }), Wt::TextFormat::UnsafeXHTML) };
|
}
|
||||||
|
|
||||||
res->setStyleClass("Lms-badge-cluster badge me-1 " + styleClass); // HACK
|
std::unique_ptr<Wt::WContainerWidget> createFilterClustersForTrack(db::Track::pointer track, Filters& filters)
|
||||||
res->setToolTip(std::string{ cluster->getType()->getName() }, Wt::TextFormat::Plain);
|
{
|
||||||
res->setInline(true);
|
using namespace db;
|
||||||
|
|
||||||
return res;
|
std::unique_ptr<Wt::WContainerWidget> clusterContainer{ std::make_unique<Wt::WContainerWidget>() };
|
||||||
|
|
||||||
|
// 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<Wt::WContainerWidget> createArtistAnchorList(const std::vector<db::ArtistId>& artistIds, std::string_view cssAnchorClass)
|
std::unique_ptr<Wt::WContainerWidget> createArtistAnchorList(const std::vector<db::ArtistId>& artistIds, std::string_view cssAnchorClass)
|
||||||
@@ -278,30 +308,4 @@ namespace lms::ui::utils
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Wt::WContainerWidget> createClustersForTrack(db::Track::pointer track, Filters& filters)
|
|
||||||
{
|
|
||||||
using namespace db;
|
|
||||||
|
|
||||||
std::unique_ptr<Wt::WContainerWidget> clusterContainer{ std::make_unique<Wt::WContainerWidget>() };
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <Wt/WContainerWidget.h>
|
#include <Wt/WContainerWidget.h>
|
||||||
#include <Wt/WImage.h>
|
#include <Wt/WImage.h>
|
||||||
#include <Wt/WInteractWidget.h>
|
#include <Wt/WInteractWidget.h>
|
||||||
|
#include <Wt/WString.h>
|
||||||
|
|
||||||
#include "database/ArtistId.hpp"
|
#include "database/ArtistId.hpp"
|
||||||
#include "database/ClusterId.hpp"
|
#include "database/ClusterId.hpp"
|
||||||
@@ -56,7 +57,9 @@ namespace lms::ui::utils
|
|||||||
std::unique_ptr<Wt::WImage> createCover(db::ReleaseId releaseId, CoverResource::Size size);
|
std::unique_ptr<Wt::WImage> createCover(db::ReleaseId releaseId, CoverResource::Size size);
|
||||||
std::unique_ptr<Wt::WImage> createCover(db::TrackId trackId, CoverResource::Size size);
|
std::unique_ptr<Wt::WImage> createCover(db::TrackId trackId, CoverResource::Size size);
|
||||||
|
|
||||||
std::unique_ptr<Wt::WInteractWidget> createCluster(db::ClusterId clusterId, bool canDelete = false);
|
std::unique_ptr<Wt::WInteractWidget> createFilter(const Wt::WString& name, const Wt::WString& tooltip, std::string_view colorStyleClass, bool canDelete = false);
|
||||||
|
std::unique_ptr<Wt::WInteractWidget> createFilterCluster(db::ClusterId clusterId, bool canDelete = false);
|
||||||
|
std::unique_ptr<Wt::WContainerWidget> createFilterClustersForTrack(db::ObjectPtr<db::Track> track, Filters& filters);
|
||||||
|
|
||||||
std::unique_ptr<Wt::WContainerWidget> createArtistAnchorList(const std::vector<db::ArtistId>& artistIds, std::string_view cssAnchorClass = "link-success");
|
std::unique_ptr<Wt::WContainerWidget> createArtistAnchorList(const std::vector<db::ArtistId>& artistIds, std::string_view cssAnchorClass = "link-success");
|
||||||
std::unique_ptr<Wt::WContainerWidget> createArtistDisplayNameWithAnchors(std::string_view displayName, const std::vector<db::ArtistId>& artistIds, std::string_view cssAnchorClass = "link-success");
|
std::unique_ptr<Wt::WContainerWidget> createArtistDisplayNameWithAnchors(std::string_view displayName, const std::vector<db::ArtistId>& artistIds, std::string_view cssAnchorClass = "link-success");
|
||||||
@@ -67,6 +70,4 @@ namespace lms::ui::utils
|
|||||||
Wt::WLink createReleaseLink(db::ObjectPtr<db::Release> release);
|
Wt::WLink createReleaseLink(db::ObjectPtr<db::Release> release);
|
||||||
std::unique_ptr<Wt::WAnchor> createReleaseAnchor(db::ObjectPtr<db::Release> release, bool setText = true);
|
std::unique_ptr<Wt::WAnchor> createReleaseAnchor(db::ObjectPtr<db::Release> release, bool setText = true);
|
||||||
std::unique_ptr<Wt::WAnchor> createTrackListAnchor(db::ObjectPtr<db::TrackList> trackList, bool setText = true);
|
std::unique_ptr<Wt::WAnchor> createTrackListAnchor(db::ObjectPtr<db::TrackList> trackList, bool setText = true);
|
||||||
|
|
||||||
std::unique_ptr<Wt::WContainerWidget> createClustersForTrack(db::ObjectPtr<db::Track> track, Filters& filters);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,60 +23,55 @@
|
|||||||
|
|
||||||
#include <Wt/WStringListModel.h>
|
#include <Wt/WStringListModel.h>
|
||||||
|
|
||||||
namespace lms::ui {
|
namespace lms::ui
|
||||||
|
|
||||||
// Helper class
|
|
||||||
template <typename T>
|
|
||||||
class ValueStringModel : public Wt::WStringListModel
|
|
||||||
{
|
{
|
||||||
public:
|
// Helper class
|
||||||
T getValue(std::size_t row) const
|
template <typename T>
|
||||||
{
|
class ValueStringModel : public Wt::WStringListModel
|
||||||
return Wt::cpp17::any_cast<T>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::User));
|
{
|
||||||
}
|
public:
|
||||||
|
T getValue(std::size_t row) const
|
||||||
|
{
|
||||||
|
return Wt::cpp17::any_cast<T>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::User));
|
||||||
|
}
|
||||||
|
|
||||||
Wt::WString getString(std::size_t row) const
|
Wt::WString getString(std::size_t row) const
|
||||||
{
|
{
|
||||||
return Wt::cpp17::any_cast<Wt::WString>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::Display));
|
return Wt::cpp17::any_cast<Wt::WString>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::Display));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::size_t>
|
std::optional<std::size_t> getRowFromString(const Wt::WString& value)
|
||||||
getRowFromString(const Wt::WString& value)
|
{
|
||||||
{
|
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
||||||
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
{
|
||||||
{
|
if (getString(i) == value)
|
||||||
if (getString(i) == value)
|
return i;
|
||||||
return i;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::size_t>
|
std::optional<std::size_t> getRowFromValue(const T& value)
|
||||||
getRowFromValue(const T& value)
|
{
|
||||||
{
|
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
||||||
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
{
|
||||||
{
|
if (getValue(i) == value)
|
||||||
if (getValue(i) == value)
|
return i;
|
||||||
return i;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void add(const Wt::WString& str, const T& value)
|
||||||
add(const Wt::WString& str, const T& value)
|
{
|
||||||
{
|
insertRows(rowCount(), 1);
|
||||||
insertRows(rowCount(), 1);
|
setData(rowCount() - 1, 0, value, Wt::ItemDataRole::User);
|
||||||
setData(rowCount() - 1, 0, value, Wt::ItemDataRole::User);
|
setData(rowCount() - 1, 0, str, Wt::ItemDataRole::Display);
|
||||||
setData(rowCount() - 1, 0, str, Wt::ItemDataRole::Display);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
clear()
|
|
||||||
{
|
|
||||||
removeRows(0, rowCount());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
removeRows(0, rowCount());
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace lms::ui
|
} // namespace lms::ui
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace lms::ui
|
|||||||
scrobbling::IScrobblingService& scrobblingService{ *core::Service<scrobbling::IScrobblingService>::get() };
|
scrobbling::IScrobblingService& scrobblingService{ *core::Service<scrobbling::IScrobblingService>::get() };
|
||||||
|
|
||||||
const Range range{ getActualRange(requestedRange) };
|
const Range range{ getActualRange(requestedRange) };
|
||||||
|
const Filters& filters{ getFilters() };
|
||||||
|
|
||||||
RangeResults<ArtistId> artists;
|
RangeResults<ArtistId> artists;
|
||||||
|
|
||||||
@@ -52,7 +53,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
feedback::IFeedbackService::ArtistFindParameters params;
|
feedback::IFeedbackService::ArtistFindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::StarredDateDesc);
|
params.setSortMethod(ArtistSortMethod::StarredDateDesc);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
@@ -64,7 +66,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
scrobbling::IScrobblingService::ArtistFindParameters params;
|
scrobbling::IScrobblingService::ArtistFindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
@@ -76,7 +79,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
scrobbling::IScrobblingService::ArtistFindParameters params;
|
scrobbling::IScrobblingService::ArtistFindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
@@ -87,7 +91,8 @@ namespace lms::ui
|
|||||||
case Mode::RecentlyAdded:
|
case Mode::RecentlyAdded:
|
||||||
{
|
{
|
||||||
Artist::FindParameters params;
|
Artist::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::LastWritten);
|
params.setSortMethod(ArtistSortMethod::LastWritten);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
@@ -102,7 +107,8 @@ namespace lms::ui
|
|||||||
case Mode::Search:
|
case Mode::Search:
|
||||||
{
|
{
|
||||||
Artist::FindParameters params;
|
Artist::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setKeywords(getSearchKeywords());
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
@@ -117,7 +123,8 @@ namespace lms::ui
|
|||||||
case Mode::All:
|
case Mode::All:
|
||||||
{
|
{
|
||||||
Artist::FindParameters params;
|
Artist::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::SortName);
|
params.setSortMethod(ArtistSortMethod::SortName);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
@@ -143,7 +150,8 @@ namespace lms::ui
|
|||||||
if (!_randomArtists)
|
if (!_randomArtists)
|
||||||
{
|
{
|
||||||
Artist::FindParameters params;
|
Artist::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::Random);
|
params.setSortMethod(ArtistSortMethod::Random);
|
||||||
params.setRange(Range{ 0, getMaxCount() });
|
params.setRange(Range{ 0, getMaxCount() });
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ namespace lms::ui
|
|||||||
for (const db::Cluster::pointer& cluster : clusters)
|
for (const db::Cluster::pointer& cluster : clusters)
|
||||||
{
|
{
|
||||||
const db::ClusterId clusterId = cluster->getId();
|
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]
|
entry->clicked().connect([this, clusterId]
|
||||||
{
|
{
|
||||||
_filters.add(clusterId);
|
_filters.add(clusterId);
|
||||||
@@ -198,7 +198,8 @@ namespace lms::ui
|
|||||||
_releaseContainers.clear();
|
_releaseContainers.clear();
|
||||||
|
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(_filters.getClusterIds());
|
params.setClusters(_filters.getClusters());
|
||||||
|
params.setMediaLibrary(_filters.getMediaLibrary());
|
||||||
params.setArtist(_artistId, { TrackArtistLinkType::ReleaseArtist }, {});
|
params.setArtist(_artistId, { TrackArtistLinkType::ReleaseArtist }, {});
|
||||||
params.setSortMethod(ReleaseSortMethod::OriginalDateDesc);
|
params.setSortMethod(ReleaseSortMethod::OriginalDateDesc);
|
||||||
|
|
||||||
@@ -257,7 +258,8 @@ namespace lms::ui
|
|||||||
_appearsOnReleaseContainer = {};
|
_appearsOnReleaseContainer = {};
|
||||||
|
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(_filters.getClusterIds());
|
params.setClusters(_filters.getClusters());
|
||||||
|
params.setMediaLibrary(_filters.getMediaLibrary());
|
||||||
params.setArtist(_artistId, types, { TrackArtistLinkType::ReleaseArtist });
|
params.setArtist(_artistId, types, { TrackArtistLinkType::ReleaseArtist });
|
||||||
params.setSortMethod(ReleaseSortMethod::OriginalDateDesc);
|
params.setSortMethod(ReleaseSortMethod::OriginalDateDesc);
|
||||||
|
|
||||||
@@ -343,7 +345,8 @@ namespace lms::ui
|
|||||||
const Range range{ static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize };
|
const Range range{ static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize };
|
||||||
|
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(_filters.getClusterIds());
|
params.setClusters(_filters.getClusters());
|
||||||
|
params.setMediaLibrary(_filters.getMediaLibrary());
|
||||||
params.setArtist(_artistId);
|
params.setArtist(_artistId);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
params.setSortMethod(TrackSortMethod::Name);
|
params.setSortMethod(TrackSortMethod::Name);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace lms::ui
|
|||||||
protected:
|
protected:
|
||||||
Range getActualRange(std::optional<Range> range) const;
|
Range getActualRange(std::optional<Range> range) const;
|
||||||
std::size_t getMaxCount() const;
|
std::size_t getMaxCount() const;
|
||||||
Filters& getFilters() { return _filters; }
|
const Filters& getFilters() { return _filters; }
|
||||||
const std::vector<std::string_view>& getSearchKeywords() const { return _searchKeywords; }
|
const std::vector<std::string_view>& getSearchKeywords() const { return _searchKeywords; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
+148
-85
@@ -19,21 +19,79 @@
|
|||||||
|
|
||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include <Wt/WComboBox.h>
|
#include <Wt/WComboBox.h>
|
||||||
#include <Wt/WDialog.h>
|
#include <Wt/WDialog.h>
|
||||||
#include <Wt/WPushButton.h>
|
#include <Wt/WPushButton.h>
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
|
|
||||||
#include "database/Cluster.hpp"
|
#include "database/Cluster.hpp"
|
||||||
|
#include "database/MediaLibrary.hpp"
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
|
|
||||||
|
#include "common/ValueStringModel.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "ModalManager.hpp"
|
#include "ModalManager.hpp"
|
||||||
|
|
||||||
namespace lms::ui
|
namespace lms::ui
|
||||||
{
|
{
|
||||||
using namespace db;
|
namespace
|
||||||
|
{
|
||||||
|
struct MediaLibraryTag {};
|
||||||
|
|
||||||
|
using TypeVariant = std::variant<db::ClusterTypeId, MediaLibraryTag>;
|
||||||
|
using TypeModel = ValueStringModel<TypeVariant>;
|
||||||
|
|
||||||
|
std::unique_ptr<TypeModel> createTypeModel()
|
||||||
|
{
|
||||||
|
auto typeModel{ std::make_unique<TypeModel>() };
|
||||||
|
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<db::ClusterId, db::MediaLibraryId>;
|
||||||
|
using ValueModel = ValueStringModel<ValueVariant>;
|
||||||
|
|
||||||
|
std::unique_ptr<ValueModel> createValueModel(TypeVariant type)
|
||||||
|
{
|
||||||
|
db::Session& session{ LmsApp->getDbSession() };
|
||||||
|
|
||||||
|
auto valueModel{ std::make_unique<ValueModel>() };
|
||||||
|
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
|
if (std::holds_alternative<MediaLibraryTag>(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<db::ClusterTypeId>(&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()
|
void Filters::showDialog()
|
||||||
{
|
{
|
||||||
@@ -43,32 +101,27 @@ namespace lms::ui
|
|||||||
dialog->addFunction("id", &Wt::WTemplate::Functions::id);
|
dialog->addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
|
|
||||||
Wt::WComboBox* typeCombo{ dialog->bindNew<Wt::WComboBox>("type") };
|
Wt::WComboBox* typeCombo{ dialog->bindNew<Wt::WComboBox>("type") };
|
||||||
|
const std::shared_ptr<TypeModel> typeModel{ createTypeModel() };
|
||||||
|
typeCombo->setModel(typeModel);
|
||||||
|
|
||||||
Wt::WComboBox* valueCombo{ dialog->bindNew<Wt::WComboBox>("value") };
|
Wt::WComboBox* valueCombo{ dialog->bindNew<Wt::WComboBox>("value") };
|
||||||
|
|
||||||
Wt::WPushButton* addBtn{ dialog->bindNew<Wt::WPushButton>("add-btn", Wt::WString::tr("Lms.Explore.add-filter")) };
|
Wt::WPushButton* addBtn{ dialog->bindNew<Wt::WPushButton>("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 auto valueModel{ std::static_pointer_cast<ValueModel>(valueCombo->model()) };
|
||||||
const std::string value{ valueCombo->valueText().toUTF8() };
|
const ValueVariant value{ valueModel->getValue(valueCombo->currentIndex()) };
|
||||||
|
|
||||||
// TODO use a model to store the cluster.id() values
|
|
||||||
ClusterId clusterId{};
|
|
||||||
|
|
||||||
|
if (const db::MediaLibraryId * mediaLibraryId{ std::get_if<db::MediaLibraryId>(&value) })
|
||||||
{
|
{
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
set(*mediaLibraryId);
|
||||||
|
}
|
||||||
ClusterType::pointer clusterType{ ClusterType::find(LmsApp->getDbSession(), type) };
|
else if (const db::ClusterId * clusterId{ std::get_if<db::ClusterId>(&value) })
|
||||||
if (!clusterType)
|
{
|
||||||
return;
|
add(*clusterId);
|
||||||
|
|
||||||
Cluster::pointer cluster{ clusterType->getCluster(value) };
|
|
||||||
if (!cluster)
|
|
||||||
return;
|
|
||||||
|
|
||||||
clusterId = cluster->getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add(clusterId);
|
// TODO
|
||||||
LmsApp->getModalManager().dispose(dialogPtr);
|
LmsApp->getModalManager().dispose(dialogPtr);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -78,79 +131,20 @@ namespace lms::ui
|
|||||||
LmsApp->getModalManager().dispose(dialogPtr);
|
LmsApp->getModalManager().dispose(dialogPtr);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Populate data
|
typeCombo->activated().connect([valueCombo, typeModel](int row)
|
||||||
{
|
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
|
||||||
|
|
||||||
const auto clusterTypesIds{ ClusterType::findUsed(LmsApp->getDbSession()) };
|
|
||||||
for (const ClusterTypeId clusterTypeId : clusterTypesIds.results)
|
|
||||||
{
|
{
|
||||||
const auto clusterType{ ClusterType::find(LmsApp->getDbSession(), clusterTypeId) };
|
const TypeVariant type{ typeModel->getValue(row) };
|
||||||
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 std::shared_ptr<ValueModel> valueModel{ createValueModel(type) };
|
||||||
valueCombo->clear();
|
valueCombo->clear();
|
||||||
|
valueCombo->setModel(valueModel);
|
||||||
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() }));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
typeCombo->activated().emit(0); // force emit to refresh the type combo model
|
||||||
|
|
||||||
LmsApp->getModalManager().show(std::move(dialog));
|
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()
|
Filters::Filters()
|
||||||
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.template.filters") }
|
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.template.filters") }
|
||||||
{
|
{
|
||||||
@@ -162,4 +156,73 @@ namespace lms::ui
|
|||||||
|
|
||||||
_filters = bindNew<Wt::WContainerWidget>("clusters");
|
_filters = bindNew<Wt::WContainerWidget>("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
|
} // namespace lms::ui
|
||||||
|
|||||||
@@ -19,12 +19,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Wt/WContainerWidget.h>
|
#include <Wt/WContainerWidget.h>
|
||||||
#include <Wt/WSignal.h>
|
#include <Wt/WSignal.h>
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
|
|
||||||
#include "database/ClusterId.hpp"
|
#include "database/ClusterId.hpp"
|
||||||
|
#include "database/MediaLibraryId.hpp"
|
||||||
|
|
||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
|
|
||||||
@@ -35,17 +37,24 @@ namespace lms::ui
|
|||||||
public:
|
public:
|
||||||
Filters();
|
Filters();
|
||||||
|
|
||||||
const std::vector<db::ClusterId>& getClusterIds() const { return _clusterIds; }
|
std::span<const db::ClusterId> getClusters() const { return _clusterIds; }
|
||||||
|
db::MediaLibraryId getMediaLibrary() const { return _mediaLibraryId; }
|
||||||
|
|
||||||
void add(db::ClusterId clusterId);
|
void add(db::ClusterId clusterId);
|
||||||
|
|
||||||
Wt::Signal<>& updated() { return _sigUpdated; }
|
Wt::Signal<>& updated() { return _sigUpdated; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void showDialog();
|
void showDialog();
|
||||||
|
void set(db::MediaLibraryId mediaLibraryId);
|
||||||
|
void emitFilterAddedNotification();
|
||||||
|
|
||||||
Wt::WContainerWidget* _filters;
|
Wt::WContainerWidget* _filters{};
|
||||||
Wt::Signal<> _sigUpdated;
|
Wt::Signal<> _sigUpdated;
|
||||||
std::vector<db::ClusterId> _clusterIds;
|
std::vector<db::ClusterId> _clusterIds;
|
||||||
|
|
||||||
|
Wt::WInteractWidget* _mediaLibraryFilter{};
|
||||||
|
db::MediaLibraryId _mediaLibraryId;
|
||||||
};
|
};
|
||||||
} // namespace lms::ui
|
} // namespace lms::ui
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,9 @@
|
|||||||
|
|
||||||
namespace lms::ui
|
namespace lms::ui
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::vector<db::TrackId> getArtistsTracks(db::Session& session, const std::vector<db::ArtistId>& artistsId, const std::vector<db::ClusterId>& clusters, std::size_t maxTrackCount)
|
std::vector<db::TrackId> getArtistsTracks(db::Session& session, const std::vector<db::ArtistId>& artistsId, const Filters& filters, std::size_t maxTrackCount)
|
||||||
{
|
{
|
||||||
assert(maxTrackCount);
|
assert(maxTrackCount);
|
||||||
|
|
||||||
@@ -44,7 +43,8 @@ namespace lms::ui
|
|||||||
db::Track::FindParameters params;
|
db::Track::FindParameters params;
|
||||||
params.setArtist(artistId);
|
params.setArtist(artistId);
|
||||||
params.setSortMethod(db::TrackSortMethod::DateDescAndRelease);
|
params.setSortMethod(db::TrackSortMethod::DateDescAndRelease);
|
||||||
params.setClusters(clusters);
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
|
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
|
||||||
|
|
||||||
const auto tracks{ db::Track::findIds(session, params) };
|
const auto tracks{ db::Track::findIds(session, params) };
|
||||||
@@ -59,7 +59,7 @@ namespace lms::ui
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<db::TrackId> getReleasesTracks(db::Session& session, const std::vector<db::ReleaseId>& releasesId, const std::vector<db::ClusterId>& clusters, std::size_t maxTrackCount)
|
std::vector<db::TrackId> getReleasesTracks(db::Session& session, const std::vector<db::ReleaseId>& releasesId, const Filters& filters, std::size_t maxTrackCount)
|
||||||
{
|
{
|
||||||
using namespace db;
|
using namespace db;
|
||||||
assert(maxTrackCount);
|
assert(maxTrackCount);
|
||||||
@@ -73,7 +73,8 @@ namespace lms::ui
|
|||||||
db::Track::FindParameters params;
|
db::Track::FindParameters params;
|
||||||
params.setRelease(releaseId);
|
params.setRelease(releaseId);
|
||||||
params.setSortMethod(db::TrackSortMethod::Release);
|
params.setSortMethod(db::TrackSortMethod::Release);
|
||||||
params.setClusters(clusters);
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
|
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
|
||||||
|
|
||||||
const auto tracks{ db::Track::findIds(session, params) };
|
const auto tracks{ db::Track::findIds(session, params) };
|
||||||
@@ -88,7 +89,7 @@ namespace lms::ui
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<db::TrackId> getDiscTracks(db::Session& session, const std::vector<PlayQueueController::Disc>& discs, const std::vector<db::ClusterId>& clusters, std::size_t maxTrackCount)
|
std::vector<db::TrackId> getDiscTracks(db::Session& session, const std::vector<PlayQueueController::Disc>& discs, const Filters& filters, std::size_t maxTrackCount)
|
||||||
{
|
{
|
||||||
using namespace db;
|
using namespace db;
|
||||||
assert(maxTrackCount);
|
assert(maxTrackCount);
|
||||||
@@ -103,7 +104,8 @@ namespace lms::ui
|
|||||||
params.setRelease(disc.releaseId);
|
params.setRelease(disc.releaseId);
|
||||||
params.setSortMethod(db::TrackSortMethod::Release);
|
params.setSortMethod(db::TrackSortMethod::Release);
|
||||||
params.setDiscNumber(disc.discNumber);
|
params.setDiscNumber(disc.discNumber);
|
||||||
params.setClusters(clusters);
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
|
params.setRange(db::Range{ 0, maxTrackCount - res.size() });
|
||||||
|
|
||||||
const auto tracks{ db::Track::findIds(session, params) };
|
const auto tracks{ db::Track::findIds(session, params) };
|
||||||
@@ -118,7 +120,7 @@ namespace lms::ui
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<db::TrackId> getTrackListTracks(db::Session& session, db::TrackListId trackListId, const std::vector<db::ClusterId>& clusters, std::size_t maxTrackCount)
|
std::vector<db::TrackId> getTrackListTracks(db::Session& session, db::TrackListId trackListId, const Filters& filters, std::size_t maxTrackCount)
|
||||||
{
|
{
|
||||||
using namespace db;
|
using namespace db;
|
||||||
assert(maxTrackCount);
|
assert(maxTrackCount);
|
||||||
@@ -127,7 +129,8 @@ namespace lms::ui
|
|||||||
|
|
||||||
db::Track::FindParameters params;
|
db::Track::FindParameters params;
|
||||||
params.setTrackList(trackListId);
|
params.setTrackList(trackListId);
|
||||||
params.setClusters(clusters);
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setRange(db::Range{ 0, maxTrackCount });
|
params.setRange(db::Range{ 0, maxTrackCount });
|
||||||
params.setSortMethod(TrackSortMethod::TrackList);
|
params.setSortMethod(TrackSortMethod::TrackList);
|
||||||
|
|
||||||
@@ -143,13 +146,13 @@ namespace lms::ui
|
|||||||
|
|
||||||
void PlayQueueController::processCommand(Command command, const std::vector<db::ArtistId>& artistIds)
|
void PlayQueueController::processCommand(Command command, const std::vector<db::ArtistId>& artistIds)
|
||||||
{
|
{
|
||||||
const std::vector<db::TrackId> tracks{ getArtistsTracks(LmsApp->getDbSession(), artistIds, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
|
const std::vector<db::TrackId> tracks{ getArtistsTracks(LmsApp->getDbSession(), artistIds, _filters, _maxTrackCountToEnqueue) };
|
||||||
processCommand(command, tracks);
|
processCommand(command, tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayQueueController::processCommand(Command command, const std::vector<db::ReleaseId>& releaseIds)
|
void PlayQueueController::processCommand(Command command, const std::vector<db::ReleaseId>& releaseIds)
|
||||||
{
|
{
|
||||||
const std::vector<db::TrackId> tracks{ getReleasesTracks(LmsApp->getDbSession(), releaseIds, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
|
const std::vector<db::TrackId> tracks{ getReleasesTracks(LmsApp->getDbSession(), releaseIds, _filters, _maxTrackCountToEnqueue) };
|
||||||
processCommand(command, tracks);
|
processCommand(command, tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,13 +178,13 @@ namespace lms::ui
|
|||||||
|
|
||||||
void PlayQueueController::processCommand(Command command, db::TrackListId trackListId)
|
void PlayQueueController::processCommand(Command command, db::TrackListId trackListId)
|
||||||
{
|
{
|
||||||
const std::vector<db::TrackId> tracks{ getTrackListTracks(LmsApp->getDbSession(), trackListId, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
|
const std::vector<db::TrackId> tracks{ getTrackListTracks(LmsApp->getDbSession(), trackListId, _filters, _maxTrackCountToEnqueue) };
|
||||||
processCommand(command, tracks);
|
processCommand(command, tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayQueueController::processCommand(Command command, const std::vector<Disc>& discs)
|
void PlayQueueController::processCommand(Command command, const std::vector<Disc>& discs)
|
||||||
{
|
{
|
||||||
const std::vector<db::TrackId> tracks{ getDiscTracks(LmsApp->getDbSession(), discs, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
|
const std::vector<db::TrackId> tracks{ getDiscTracks(LmsApp->getDbSession(), discs, _filters, _maxTrackCountToEnqueue) };
|
||||||
processCommand(command, tracks);
|
processCommand(command, tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +200,7 @@ namespace lms::ui
|
|||||||
releaseId = track->getRelease()->getId();
|
releaseId = track->getRelease()->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<db::TrackId> tracks{ getReleasesTracks(LmsApp->getDbSession(), {releaseId}, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
|
const std::vector<db::TrackId> tracks{ getReleasesTracks(LmsApp->getDbSession(), {releaseId}, _filters, _maxTrackCountToEnqueue) };
|
||||||
auto itTrack{ std::find(std::cbegin(tracks), std::cend(tracks), trackId) };
|
auto itTrack{ std::find(std::cbegin(tracks), std::cend(tracks), trackId) };
|
||||||
if (itTrack == std::cend(tracks))
|
if (itTrack == std::cend(tracks))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
feedback::IFeedbackService::FindParameters params;
|
feedback::IFeedbackService::FindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
releases = feedbackService.findStarredReleases(params);
|
releases = feedbackService.findStarredReleases(params);
|
||||||
break;
|
break;
|
||||||
@@ -61,7 +62,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
scrobbling::IScrobblingService::FindParameters params;
|
scrobbling::IScrobblingService::FindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
releases = scrobblingService.getRecentReleases(params);
|
releases = scrobblingService.getRecentReleases(params);
|
||||||
@@ -72,7 +74,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
scrobbling::IScrobblingService::FindParameters params;
|
scrobbling::IScrobblingService::FindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
releases = scrobblingService.getTopReleases(params);
|
releases = scrobblingService.getTopReleases(params);
|
||||||
@@ -82,7 +85,8 @@ namespace lms::ui
|
|||||||
case Mode::RecentlyAdded:
|
case Mode::RecentlyAdded:
|
||||||
{
|
{
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setSortMethod(ReleaseSortMethod::LastWritten);
|
params.setSortMethod(ReleaseSortMethod::LastWritten);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
@@ -96,7 +100,8 @@ namespace lms::ui
|
|||||||
case Mode::Search:
|
case Mode::Search:
|
||||||
{
|
{
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setKeywords(getSearchKeywords());
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
@@ -110,7 +115,8 @@ namespace lms::ui
|
|||||||
case Mode::All:
|
case Mode::All:
|
||||||
{
|
{
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setSortMethod(ReleaseSortMethod::Name);
|
params.setSortMethod(ReleaseSortMethod::Name);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
@@ -135,7 +141,8 @@ namespace lms::ui
|
|||||||
if (!_randomReleases)
|
if (!_randomReleases)
|
||||||
{
|
{
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setSortMethod(ReleaseSortMethod::Random);
|
params.setSortMethod(ReleaseSortMethod::Random);
|
||||||
params.setRange(Range{ 0, getMaxCount() });
|
params.setRange(Range{ 0, getMaxCount() });
|
||||||
|
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ namespace lms::ui
|
|||||||
for (const db::Cluster::pointer& cluster : clusters)
|
for (const db::Cluster::pointer& cluster : clusters)
|
||||||
{
|
{
|
||||||
const ClusterId clusterId{ cluster->getId() };
|
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]
|
entry->clicked().connect([this, clusterId]
|
||||||
{
|
{
|
||||||
_filters.add(clusterId);
|
_filters.add(clusterId);
|
||||||
@@ -394,7 +394,8 @@ namespace lms::ui
|
|||||||
db::Track::FindParameters params;
|
db::Track::FindParameters params;
|
||||||
params.setRelease(_releaseId);
|
params.setRelease(_releaseId);
|
||||||
params.setSortMethod(db::TrackSortMethod::Release);
|
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)
|
db::Track::find(LmsApp->getDbSession(), params, [&](const db::Track::pointer& track)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ namespace lms::ui
|
|||||||
case Mode::Starred:
|
case Mode::Starred:
|
||||||
{
|
{
|
||||||
feedback::IFeedbackService::FindParameters params;
|
feedback::IFeedbackService::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
tracks = feedbackService.findStarredTracks(params);
|
tracks = feedbackService.findStarredTracks(params);
|
||||||
@@ -64,7 +65,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
scrobbling::IScrobblingService::FindParameters params;
|
scrobbling::IScrobblingService::FindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
tracks = scrobblingService.getRecentTracks(params);
|
tracks = scrobblingService.getRecentTracks(params);
|
||||||
@@ -75,7 +77,8 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
scrobbling::IScrobblingService::FindParameters params;
|
scrobbling::IScrobblingService::FindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
tracks = scrobblingService.getTopTracks(params);
|
tracks = scrobblingService.getTopTracks(params);
|
||||||
@@ -85,7 +88,8 @@ namespace lms::ui
|
|||||||
case Mode::RecentlyAdded:
|
case Mode::RecentlyAdded:
|
||||||
{
|
{
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setSortMethod(TrackSortMethod::LastWritten);
|
params.setSortMethod(TrackSortMethod::LastWritten);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
@@ -99,7 +103,8 @@ namespace lms::ui
|
|||||||
case Mode::Search:
|
case Mode::Search:
|
||||||
{
|
{
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setKeywords(getSearchKeywords());
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
@@ -113,7 +118,8 @@ namespace lms::ui
|
|||||||
case Mode::All:
|
case Mode::All:
|
||||||
{
|
{
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -137,7 +143,8 @@ namespace lms::ui
|
|||||||
if (!_randomTracks)
|
if (!_randomTracks)
|
||||||
{
|
{
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusterIds());
|
params.setClusters(getFilters().getClusters());
|
||||||
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setSortMethod(TrackSortMethod::Random);
|
params.setSortMethod(TrackSortMethod::Random);
|
||||||
params.setRange(Range{ 0, getMaxCount() });
|
params.setRange(Range{ 0, getMaxCount() });
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ namespace lms::ui::TrackListHelpers
|
|||||||
|
|
||||||
trackInfo->bindInt("playcount", core::Service<scrobbling::IScrobblingService>::get()->getCount(LmsApp->getUserId(), track->getId()));
|
trackInfo->bindInt("playcount", core::Service<scrobbling::IScrobblingService>::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)
|
if (clusterContainer->count() > 0)
|
||||||
trackInfo->setCondition("if-has-clusters", true);
|
trackInfo->setCondition("if-has-clusters", true);
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace lms::ui
|
|||||||
for (const db::Cluster::pointer& cluster : clusters)
|
for (const db::Cluster::pointer& cluster : clusters)
|
||||||
{
|
{
|
||||||
const ClusterId clusterId{ cluster->getId() };
|
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]
|
entry->clicked().connect([this, clusterId]
|
||||||
{
|
{
|
||||||
_filters.add(clusterId);
|
_filters.add(clusterId);
|
||||||
@@ -183,7 +183,8 @@ namespace lms::ui
|
|||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
||||||
|
|
||||||
db::Track::FindParameters params;
|
db::Track::FindParameters params;
|
||||||
params.setClusters(_filters.getClusterIds());
|
params.setClusters(_filters.getClusters());
|
||||||
|
params.setMediaLibrary(_filters.getMediaLibrary());
|
||||||
params.setTrackList(_trackListId);
|
params.setTrackList(_trackListId);
|
||||||
params.setSortMethod(db::TrackSortMethod::TrackList);
|
params.setSortMethod(db::TrackSortMethod::TrackList);
|
||||||
params.setRange(db::Range{ static_cast<std::size_t>(_container->getCount()), _batchSize });
|
params.setRange(db::Range{ static_cast<std::size_t>(_container->getCount()), _batchSize });
|
||||||
|
|||||||
@@ -101,7 +101,8 @@ namespace lms::ui
|
|||||||
auto transaction{ session.createReadTransaction() };
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
TrackList::FindParameters params;
|
TrackList::FindParameters params;
|
||||||
params.setClusters(_filters.getClusterIds());
|
params.setClusters(_filters.getClusters());
|
||||||
|
params.setMediaLibrary(_filters.getMediaLibrary());
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setType(TrackListType::Playlist);
|
params.setType(TrackListType::Playlist);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|||||||
Reference in New Issue
Block a user