diff --git a/approot/tracks.xml b/approot/tracks.xml
index 41f92006..b8dcf259 100644
--- a/approot/tracks.xml
+++ b/approot/tracks.xml
@@ -36,6 +36,7 @@
${}${release-name}${}
${}${artist-name}${}
+ ${clusters}
${play-btn}${add-btn}
diff --git a/src/Makefile.am b/src/Makefile.am
index b049316e..8d0239b2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,6 +33,7 @@ lms_SOURCES = \
$(srcdir)/ui/explore/ArtistView.cpp \
$(srcdir)/ui/explore/ArtistsView.cpp \
$(srcdir)/ui/explore/Explore.cpp \
+ $(srcdir)/ui/explore/ExploreUtils.cpp \
$(srcdir)/ui/explore/Filters.cpp \
$(srcdir)/ui/explore/ReleasesView.cpp \
$(srcdir)/ui/explore/ReleaseView.cpp \
diff --git a/src/database/Track.cpp b/src/database/Track.cpp
index 54307fb3..6b553258 100644
--- a/src/database/Track.cpp
+++ b/src/database/Track.cpp
@@ -210,6 +210,50 @@ Track::getOriginalYear() const
return (_originalYear > 0) ? boost::make_optional(_originalYear) : boost::none;
}
+std::vector>
+Track::getClusterGroups(std::vector clusterTypes, std::size_t size) const
+{
+ assert(self());
+ assert(self()->id() != Wt::Dbo::dbo_traits::invalidId() );
+ assert(session());
+
+ WhereClause where;
+
+ std::ostringstream oss;
+
+ oss << "SELECT c from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id";
+
+ where.And(WhereClause("t.id = ?")).bind(std::to_string(self()->id()));
+ {
+ WhereClause clusterClause;
+ for (auto clusterType : clusterTypes)
+ clusterClause.Or(WhereClause("c_type.id = ?")).bind(std::to_string(clusterType.id()));
+ where.And(clusterClause);
+ }
+ oss << " " << where.get();
+ oss << " GROUP BY c.id ORDER BY COUNT(c.id) DESC";
+
+ Wt::Dbo::Query query = session()->query( oss.str() );
+
+ for (const std::string& bindArg : where.getBindArgs())
+ query.bind(bindArg);
+
+ Wt::Dbo::collection queryRes = query;
+
+ std::map> clusters;
+ for (auto cluster : queryRes)
+ {
+ if (clusters[cluster->getType().id()].size() < size)
+ clusters[cluster->getType().id()].push_back(cluster);
+ }
+
+ std::vector> res;
+ for (auto cluster_list : clusters)
+ res.push_back(cluster_list.second);
+
+ return res;
+}
+
Cluster::Cluster()
{
}
diff --git a/src/database/Track.hpp b/src/database/Track.hpp
index d0c90081..9fc4f3a8 100644
--- a/src/database/Track.hpp
+++ b/src/database/Track.hpp
@@ -120,8 +120,7 @@ class ClusterType : public Wt::Dbo::Dbo
Wt::Dbo::collection< Wt::Dbo::ptr > _clusters;
};
-
-class Track
+class Track : public Wt::Dbo::Dbo
{
public:
@@ -197,6 +196,8 @@ class Track
Wt::Dbo::ptr getRelease(void) const { return _release; }
std::vector< Cluster::pointer > getClusters(void) const;
+ std::vector>> getClusterGroups(std::vector> clusterTypes, std::size_t size) const;
+
template
void persist(Action& a)
{
diff --git a/src/ui/explore/ArtistView.cpp b/src/ui/explore/ArtistView.cpp
index 230776df..91dcc3ab 100644
--- a/src/ui/explore/ArtistView.cpp
+++ b/src/ui/explore/ArtistView.cpp
@@ -34,13 +34,14 @@
#include "LmsApplication.hpp"
#include "Filters.hpp"
+#include "ExploreUtils.hpp"
using namespace Database;
namespace {
const std::string artistClusterTypesSetting = "artist_cluster_types";
-const std::vector defaultArtistClusterTypes =
+const std::set defaultArtistClusterTypes =
{
"GENRE",
"ALBUMGROUPING",
@@ -48,21 +49,6 @@ const std::vector defaultArtistClusterTypes =
"COMMENT:SONGS-DB_OCCASION",
};
-std::vector getArtistClusterTypes(Wt::Dbo::Session& session)
-{
- Wt::Dbo::Transaction transaction(session);
-
- std::vector res;
- for (auto clusterTypeName : splitString(Setting::getString(session, artistClusterTypesSetting), " "))
- {
- auto clusterType = ClusterType::getByName(session, clusterTypeName);
- if (clusterType)
- res.push_back(clusterType);
- }
-
- return res;
-}
-
} // namespace
namespace UserInterface {
@@ -71,7 +57,7 @@ Artist::Artist(Filters* filters)
: _filters(filters)
{
if (!Setting::exists(LmsApp->getDboSession(), artistClusterTypesSetting))
- Setting::setString(LmsApp->getDboSession(), artistClusterTypesSetting, joinStrings(defaultArtistClusterTypes, " "));
+ setClusterTypesToSetting(artistClusterTypesSetting, defaultArtistClusterTypes);
wApp->internalPathChanged().connect(std::bind([=]
{
@@ -112,7 +98,7 @@ Artist::refresh()
Wt::WContainerWidget* clusterContainers = t->bindNew("clusters");
{
- auto clusterTypes = getArtistClusterTypes(LmsApp->getDboSession());
+ auto clusterTypes = getClusterTypesFromSetting(artistClusterTypesSetting);
auto clusterGroups = artist->getClusterGroups(clusterTypes, 3);
for (auto clusters : clusterGroups)
diff --git a/src/ui/explore/ArtistsView.cpp b/src/ui/explore/ArtistsView.cpp
index 249d8c93..2c953754 100644
--- a/src/ui/explore/ArtistsView.cpp
+++ b/src/ui/explore/ArtistsView.cpp
@@ -31,34 +31,20 @@
#include "LmsApplication.hpp"
#include "Filters.hpp"
+#include "ExploreUtils.hpp"
using namespace Database;
namespace {
const std::string artistsClusterTypesSetting = "artists_cluster_types";
-const std::vector defaultArtistsClusterTypes =
+const std::set defaultArtistsClusterTypes =
{
"ALBUMGROUPING",
"GENRE",
"ALBUMMOOD",
};
-std::vector getArtistsClusterTypes(Wt::Dbo::Session& session)
-{
- Wt::Dbo::Transaction transaction(session);
-
- std::vector res;
- for (auto clusterTypeName : splitString(Setting::getString(session, artistsClusterTypesSetting), " "))
- {
- auto clusterType = ClusterType::getByName(session, clusterTypeName);
- if (clusterType)
- res.push_back(clusterType);
- }
-
- return res;
-}
-
}
namespace UserInterface {
@@ -68,7 +54,7 @@ Artists::Artists(Filters* filters)
_filters(filters)
{
if (!Setting::exists(LmsApp->getDboSession(), artistsClusterTypesSetting))
- Setting::setString(LmsApp->getDboSession(), artistsClusterTypesSetting, joinStrings(defaultArtistsClusterTypes, " "));
+ setClusterTypesToSetting(artistsClusterTypesSetting, defaultArtistsClusterTypes);
addFunction("tr", &Wt::WTemplate::Functions::tr);
@@ -123,7 +109,7 @@ Artists::addSome()
Wt::WContainerWidget* clusterContainers = entry->bindNew("clusters");
{
- auto clusterTypes = getArtistsClusterTypes(LmsApp->getDboSession());
+ auto clusterTypes = getClusterTypesFromSetting(artistsClusterTypesSetting);
auto clusterGroups = artist->getClusterGroups(clusterTypes, 1);
for (auto clusters : clusterGroups)
diff --git a/src/ui/explore/ExploreUtils.cpp b/src/ui/explore/ExploreUtils.cpp
new file mode 100644
index 00000000..6ecaeccc
--- /dev/null
+++ b/src/ui/explore/ExploreUtils.cpp
@@ -0,0 +1,51 @@
+
+/*
+ * Copyright (C) 2018 Emeric Poupon
+ *
+ * This file is part of LMS.
+ *
+ * LMS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LMS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LMS. If not, see .
+ */
+
+#include "ExploreUtils.hpp"
+
+#include "utils/Utils.hpp"
+
+#include "database/Setting.hpp"
+
+#include "LmsApplication.hpp"
+
+namespace UserInterface {
+
+std::vector getClusterTypesFromSetting(std::string setting)
+{
+ std::vector res;
+ for (auto clusterTypeName : splitString(Database::Setting::getString(LmsApp->getDboSession(), setting), " "))
+ {
+ auto clusterType = Database::ClusterType::getByName(LmsApp->getDboSession(), clusterTypeName);
+ if (clusterType)
+ res.push_back(clusterType);
+ }
+
+ return res;
+}
+
+void setClusterTypesToSetting(std::string setting, const std::set& clusterTypes)
+{
+ std::vector vec(clusterTypes.begin(), clusterTypes.end());
+ Database::Setting::setString(LmsApp->getDboSession(), setting, joinStrings(vec, " "));
+}
+
+} // namespace UserInterface
+
diff --git a/src/ui/explore/ExploreUtils.hpp b/src/ui/explore/ExploreUtils.hpp
new file mode 100644
index 00000000..a46a2be9
--- /dev/null
+++ b/src/ui/explore/ExploreUtils.hpp
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright (C) 2018 Emeric Poupon
+ *
+ * This file is part of LMS.
+ *
+ * LMS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LMS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LMS. If not, see .
+ */
+
+#include "database/Track.hpp"
+
+namespace UserInterface {
+
+std::vector getClusterTypesFromSetting(std::string setting);
+
+void setClusterTypesToSetting(std::string setting, const std::set& clusterTypes);
+
+} // namespace UserInterface
diff --git a/src/ui/explore/ReleaseView.cpp b/src/ui/explore/ReleaseView.cpp
index f03ccaf2..079d9ecb 100644
--- a/src/ui/explore/ReleaseView.cpp
+++ b/src/ui/explore/ReleaseView.cpp
@@ -35,13 +35,14 @@
#include "LmsApplication.hpp"
#include "Filters.hpp"
+#include "ExploreUtils.hpp"
using namespace Database;
namespace {
const std::string releaseClusterTypesSetting = "release_cluster_types";
-const std::vector defaultReleaseClusterTypes =
+const std::set defaultReleaseClusterTypes =
{
"GENRE",
"ALBUMGROUPING",
@@ -49,20 +50,6 @@ const std::vector defaultReleaseClusterTypes =
"COMMENT:SONGS-DB_OCCASION",
};
-std::vector getReleaseClusterTypes(Wt::Dbo::Session& session)
-{
- Wt::Dbo::Transaction transaction(session);
-
- std::vector res;
- for (auto clusterTypeName : splitString(Setting::getString(session, releaseClusterTypesSetting), " "))
- {
- auto clusterType = ClusterType::getByName(session, clusterTypeName);
- if (clusterType)
- res.push_back(clusterType);
- }
-
- return res;
-}
} // namespace
namespace UserInterface {
@@ -71,7 +58,7 @@ Release::Release(Filters* filters)
: _filters(filters)
{
if (!Setting::exists(LmsApp->getDboSession(), releaseClusterTypesSetting))
- Setting::setString(LmsApp->getDboSession(), releaseClusterTypesSetting, joinStrings(defaultReleaseClusterTypes, " "));
+ setClusterTypesToSetting(releaseClusterTypesSetting, defaultReleaseClusterTypes);
wApp->internalPathChanged().connect(std::bind([=]
{
@@ -143,7 +130,7 @@ Release::refresh()
Wt::WContainerWidget* clusterContainers = t->bindNew("clusters");
{
- auto clusterTypes = getReleaseClusterTypes(LmsApp->getDboSession());
+ auto clusterTypes = getClusterTypesFromSetting(releaseClusterTypesSetting);
auto clusterGroups = release->getClusterGroups(clusterTypes, 3);
for (auto clusters : clusterGroups)
diff --git a/src/ui/explore/ReleasesView.cpp b/src/ui/explore/ReleasesView.cpp
index 4812e2e3..81bddc25 100644
--- a/src/ui/explore/ReleasesView.cpp
+++ b/src/ui/explore/ReleasesView.cpp
@@ -35,33 +35,19 @@
#include "LmsApplication.hpp"
#include "Filters.hpp"
+#include "ExploreUtils.hpp"
using namespace Database;
namespace {
const std::string releasesClusterTypesSetting = "releases_cluster_types";
-const std::vector defaultReleasesClusterTypes =
+const std::set defaultReleasesClusterTypes =
{
"GENRE",
"ALBUMGROUPING",
};
-std::vector getReleasesClusterTypes(Wt::Dbo::Session& session)
-{
- Wt::Dbo::Transaction transaction(session);
-
- std::vector res;
- for (auto clusterTypeName : splitString(Setting::getString(session, releasesClusterTypesSetting), " "))
- {
- auto clusterType = ClusterType::getByName(session, clusterTypeName);
- if (clusterType)
- res.push_back(clusterType);
- }
-
- return res;
-}
-
} // namespace
namespace UserInterface {
@@ -71,7 +57,7 @@ Releases::Releases(Filters* filters)
_filters(filters)
{
if (!Setting::exists(LmsApp->getDboSession(), releasesClusterTypesSetting))
- Setting::setString(LmsApp->getDboSession(), releasesClusterTypesSetting, joinStrings(defaultReleasesClusterTypes, " "));
+ setClusterTypesToSetting(releasesClusterTypesSetting, defaultReleasesClusterTypes);
addFunction("tr", &Wt::WTemplate::Functions::tr);
@@ -140,7 +126,7 @@ Releases::addSome()
Wt::WContainerWidget* clusterContainers = entry->bindNew("clusters");
{
- auto clusterTypes = getReleasesClusterTypes(LmsApp->getDboSession());
+ auto clusterTypes = getClusterTypesFromSetting(releasesClusterTypesSetting);
auto clusterGroups = release->getClusterGroups(clusterTypes, 1);
for (auto clusters : clusterGroups)
diff --git a/src/ui/explore/TracksView.cpp b/src/ui/explore/TracksView.cpp
index 53862e1c..5b7796e1 100644
--- a/src/ui/explore/TracksView.cpp
+++ b/src/ui/explore/TracksView.cpp
@@ -27,6 +27,7 @@
#include "database/DbArtist.hpp"
#include "database/Track.hpp"
#include "database/Release.hpp"
+#include "database/Setting.hpp"
#include "utils/Logger.hpp"
#include "utils/Utils.hpp"
@@ -35,15 +36,32 @@
#include "LmsApplication.hpp"
#include "Filters.hpp"
-
-namespace UserInterface {
+#include "ExploreUtils.hpp"
using namespace Database;
+namespace {
+
+const std::string trackClusterTypesSetting = "track_cluster_types";
+const std::set defaultTrackClusterTypes =
+{
+ "GENRE",
+ "ALBUMGROUPING",
+ "ALBUMMOOD",
+ "COMMENT:SONGS-DB_OCCASION",
+};
+
+} // namespace
+
+namespace UserInterface {
+
Tracks::Tracks(Filters* filters)
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.Tracks.template")),
_filters(filters)
{
+ if (!Setting::exists(LmsApp->getDboSession(), trackClusterTypesSetting))
+ setClusterTypesToSetting(trackClusterTypesSetting, defaultTrackClusterTypes);
+
addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = bindNew("search");
@@ -136,6 +154,25 @@ Tracks::addSome()
// Some images may not be square
cover->setWidth(64);
+ Wt::WContainerWidget* clusterContainers = entry->bindNew("clusters");
+ {
+ auto clusterTypes = getClusterTypesFromSetting(trackClusterTypesSetting);
+ auto clusterGroups = release->getClusterGroups(clusterTypes, 1);
+
+ for (auto clusters : clusterGroups)
+ {
+ for (auto cluster : clusters)
+ {
+ auto clusterId = cluster.id();
+ auto entry = clusterContainers->addWidget(LmsApp->createCluster(cluster));
+ entry->clicked().connect([=]
+ {
+ _filters->add(clusterId);
+ });
+ }
+ }
+ }
+
Wt::WText* playBtn = entry->bindNew("play-btn", Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{