Added recently added releases feature

This commit is contained in:
emeric
2018-06-06 13:14:00 +02:00
parent c1e7e9a602
commit 1e68ccce2a
12 changed files with 190 additions and 62 deletions
+7 -1
View File
@@ -7,6 +7,12 @@
<h2>${tr:Lms.Explore.most-played-artists}</h2>
</div>
${most-played-artists}
<div class="page-header">
<h2>${tr:Lms.Explore.recently-added-artists}</h2>
</div>
${recently-added-artists}
<div class="page-header">
<h2>${tr:Lms.Explore.artists}</h2>
</div>
@@ -20,7 +26,7 @@
${show-more}
</message>
<message id="Lms.Explore.Artists.template.most-played-entry">
<message id="Lms.Explore.Artists.template.compact-entry">
${name}
</message>
+2
View File
@@ -58,6 +58,8 @@
<message id="Lms.Explore.artists">Artists</message>
<message id="Lms.Explore.most-played-artists">Most played artists</message>
<message id="Lms.Explore.most-played-releases">Most played releases</message>
<message id="Lms.Explore.recently-added-artists">Recently added artists</message>
<message id="Lms.Explore.recently-added-releases">Recently added releases</message>
<message id="Lms.Explore.releases">Albums</message>
<message id="Lms.Explore.search-placeholder">Search...</message>
<message id="Lms.Explore.show-more">Show more</message>
+7 -1
View File
@@ -7,6 +7,12 @@
<h2>${tr:Lms.Explore.most-played-releases}</h2>
</div>
${most-played-releases}
<div class="page-header">
<h2>${tr:Lms.Explore.recently-added-releases}</h2>
</div>
${recently-added-releases}
<div class="page-header">
<h2>${tr:Lms.Explore.releases}</h2>
</div>
@@ -28,7 +34,7 @@
<i class="fa fa-plus"></i>
</message>
<message id="Lms.Explore.Releases.template.most-played-entry">
<message id="Lms.Explore.Releases.template.compact-entry">
${cover}
${release-name}
${<if-has-artist>}${artist-name}${</if-has-artist>}
+14
View File
@@ -18,6 +18,8 @@
*/
#include "Artist.hpp"
#include <Wt/Dbo/WtSqlTraits.h>
#include "utils/Logger.hpp"
#include "Cluster.hpp"
@@ -140,6 +142,18 @@ Artist::getByFilter(Wt::Dbo::Session& session,
return res;
}
std::vector<Artist::pointer>
Artist::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int limit)
{
Wt::Dbo::collection<Artist::pointer> res = session.query<Artist::pointer>("SELECT a from artist a INNER JOIN track t ON a.id = t.artist_id")
.where("t.file_added > ?").bind(after)
.groupBy("a.id")
.orderBy("t.file_added DESC")
.limit(limit);
return std::vector<pointer>(res.begin(), res.end());
}
std::vector<Wt::Dbo::ptr<Release>>
Artist::getReleases(const std::set<IdType>& clusterIds) const
{
+2
View File
@@ -22,6 +22,7 @@
#include <string>
#include <vector>
#include <Wt/WDateTime.h>
#include <Wt/Dbo/Dbo.h>
#include "Types.hpp"
@@ -56,6 +57,7 @@ class Artist : public Wt::Dbo::Dbo<Artist>
static std::vector<pointer> getAll(Wt::Dbo::Session& session, int offset = -1, int size = -1);
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session); // No track related
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int size = 1);
// Accessors
std::string getName(void) const { return _name; }
+12
View File
@@ -74,6 +74,18 @@ Release::getAllOrphans(Wt::Dbo::Session& session)
return std::vector<pointer>(res.begin(), res.end());
}
std::vector<Release::pointer>
Release::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int limit)
{
Wt::Dbo::collection<Release::pointer> res = session.query<Release::pointer>("SELECT r from release r INNER JOIN track t ON r.id = t.release_id")
.where("t.file_added > ?").bind(after)
.groupBy("r.id")
.orderBy("t.file_added DESC")
.limit(limit);
return std::vector<pointer>(res.begin(), res.end());
}
static
Wt::Dbo::Query<Release::pointer>
getQuery(Wt::Dbo::Session& session,
+2 -1
View File
@@ -21,7 +21,7 @@
#include <boost/optional.hpp>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/WtSqlTraits.h>
#include "Types.hpp"
@@ -49,6 +49,7 @@ class Release : public Wt::Dbo::Dbo<Release>
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session); // no track related
static std::vector<pointer> getAll(Wt::Dbo::Session& session, int offset, int size);
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int size = 1);
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
+3 -1
View File
@@ -263,6 +263,9 @@ LmsApplication::handleAuthEvent(void)
LMS_LOG(UI, INFO) << "User '" << LmsApp->getCurrentAuthUser().identity(Wt::Auth::Identity::LoginName) << "' logged in from '" << Wt::WApplication::instance()->environment().clientAddress() << "', user agent = " << Wt::WApplication::instance()->environment().userAgent() << ", session = " << Wt::WApplication::instance()->sessionId();
// Since we plug into the Media Scanner events, we need to enable updates
enableUpdates(true);
{
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
_isAdmin = LmsApp->getCurrentUser()->isAdmin();
@@ -387,7 +390,6 @@ LmsApplication::handleAuthEvent(void)
// Events from MediaScanner
if (_isAdmin)
{
enableUpdates(true);
std::string sessionId = this->sessionId();
_scanner.scanComplete().connect(std::bind([=] (Scanner::MediaScanner::Stats stats)
{
+53 -14
View File
@@ -20,8 +20,10 @@
#include "ArtistsView.hpp"
#include <Wt/WAnchor.h>
#include <Wt/WTemplate.h>
#include <Wt/WLineEdit.h>
#include <Wt/WLocalDateTime.h>
#include <Wt/WServer.h>
#include <Wt/WTemplate.h>
#include "database/Artist.hpp"
#include "database/Setting.hpp"
@@ -35,6 +37,22 @@
using namespace Database;
namespace {
using namespace UserInterface;
void addCompactEntries(Wt::WContainerWidget *container, const std::vector<Artist::pointer>& artists)
{
for (auto artist : artists)
{
Wt::WTemplate* entry = container->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.compact-entry"));
entry->bindWidget("name", LmsApplication::createArtistAnchor(artist));
}
}
}
namespace UserInterface {
Artists::Artists(Filters* filters)
@@ -47,8 +65,9 @@ Artists::Artists(Filters* filters)
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Artists::refresh);
_artistsContainer = bindNew<Wt::WContainerWidget>("artists");
_mostPlayedArtistsContainer = bindNew<Wt::WContainerWidget>("most-played-artists");
_container = bindNew<Wt::WContainerWidget>("artists");
_mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played-artists");
_recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added-artists");
_showMore = bindNew<Wt::WTemplate>("show-more", Wt::WString::tr("Lms.Explore.template.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
@@ -60,30 +79,50 @@ Artists::Artists(Filters* filters)
refresh();
refreshMostPlayed();
refreshRecentlyAdded();
filters->updated().connect(this, &Artists::refresh);
std::string sessionId = LmsApp->sessionId();
LmsApp->getMediaScanner().scanComplete().connect([=] (Scanner::MediaScanner::Stats stats)
{
if (stats.nbChanges() > 0)
{
Wt::WServer::instance()->post(sessionId, [=]
{
refreshRecentlyAdded();
LmsApp->triggerUpdate();
});
}
});
}
void
Artists::refreshRecentlyAdded()
{
auto after = Wt::WLocalDateTime::currentServerDateTime().toUTC().addMonths(-1);
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto artists = Artist::getLastAdded(LmsApp->getDboSession(), after, 5);
_recentlyAddedContainer->clear();
addCompactEntries(_recentlyAddedContainer, artists);
}
void
Artists::refreshMostPlayed()
{
_mostPlayedArtistsContainer->clear();
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto artists = TrackStats::getMostPlayedArtists(LmsApp->getDboSession(), LmsApp->getCurrentUser(), 5);
for (auto artist : artists)
{
Wt::WTemplate* entry = _mostPlayedArtistsContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.most-played-entry"));
entry->bindWidget("name", LmsApplication::createArtistAnchor(artist));
}
_mostPlayedContainer->clear();
addCompactEntries(_mostPlayedContainer, artists);
}
void
Artists::refresh()
{
_artistsContainer->clear();
_container->clear();
addSome();
}
@@ -100,11 +139,11 @@ Artists::addSome()
auto artists = Artist::getByFilter(LmsApp->getDboSession(),
clusterIds,
searchKeywords,
_artistsContainer->count(), 20, moreResults);
_container->count(), 20, moreResults);
for (auto artist : artists)
{
Wt::WTemplate* entry = _artistsContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.entry"));
Wt::WTemplate* entry = _container->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.entry"));
entry->bindInt("nb-release", artist->getReleases(clusterIds).size());
entry->bindWidget("name", LmsApplication::createArtistAnchor(artist));
+4 -2
View File
@@ -39,6 +39,7 @@ class Artists : public Wt::WTemplate
Wt::Signal<Database::IdType> artistPlay;
private:
void refreshRecentlyAdded();
void refreshMostPlayed();
void refresh();
void addSome();
@@ -46,8 +47,9 @@ class Artists : public Wt::WTemplate
Filters* _filters;
Wt::WTemplate* _showMore;
Wt::WLineEdit* _search;
Wt::WContainerWidget* _artistsContainer;
Wt::WContainerWidget* _mostPlayedArtistsContainer;
Wt::WContainerWidget* _container;
Wt::WContainerWidget* _mostPlayedContainer;
Wt::WContainerWidget* _recentlyAddedContainer;
};
} // namespace UserInterface
+80 -40
View File
@@ -19,9 +19,10 @@
#include "ReleasesView.hpp"
#include <Wt/WApplication.h>
#include <Wt/WServer.h>
#include <Wt/WAnchor.h>
#include <Wt/WImage.h>
#include <Wt/WLocalDateTime.h>
#include <Wt/WText.h>
#include <Wt/WTemplate.h>
@@ -38,46 +39,15 @@
using namespace Database;
namespace UserInterface {
namespace {
Releases::Releases(Filters* filters)
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.Releases.template")),
_filters(filters)
using namespace UserInterface;
void addCompactEntries(Wt::WContainerWidget* container, const std::vector<Release::pointer>& releases)
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = bindNew<Wt::WLineEdit>("search");
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Releases::refresh);
_releasesContainer = bindNew<Wt::WContainerWidget>("releases");
_mostPlayedReleasesContainer = bindNew<Wt::WContainerWidget>("most-played-releases");
_showMore = bindNew<Wt::WTemplate>("show-more", Wt::WString::tr("Lms.Explore.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
_showMore->clicked().connect(std::bind([=]
{
addSome();
}));
refreshMostPlayed();
refresh();
filters->updated().connect(this, &Releases::refresh);
}
void
Releases::refreshMostPlayed()
{
_mostPlayedReleasesContainer->clear();
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto releases = TrackStats::getMostPlayedReleases(LmsApp->getDboSession(), LmsApp->getCurrentUser(), 5);
for (auto release : releases)
{
Wt::WTemplate* entry = _mostPlayedReleasesContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Releases.template.most-played-entry"));
Wt::WTemplate* entry = container->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Releases.template.compact-entry"));
entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(release));
@@ -100,10 +70,80 @@ Releases::refreshMostPlayed()
}
}
} // namespace
namespace UserInterface {
Releases::Releases(Filters* filters)
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.Releases.template")),
_filters(filters)
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = bindNew<Wt::WLineEdit>("search");
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Releases::refresh);
_container = bindNew<Wt::WContainerWidget>("releases");
_mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played-releases");
_recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added-releases");
_showMore = bindNew<Wt::WTemplate>("show-more", Wt::WString::tr("Lms.Explore.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
_showMore->clicked().connect(std::bind([=]
{
addSome();
}));
refreshRecentlyAdded();
refreshMostPlayed();
refresh();
filters->updated().connect(this, &Releases::refresh);
std::string sessionId = LmsApp->sessionId();
LmsApp->getMediaScanner().scanComplete().connect([=] (Scanner::MediaScanner::Stats stats)
{
if (stats.nbChanges() > 0)
{
Wt::WServer::instance()->post(sessionId, [=]
{
refreshRecentlyAdded();
LmsApp->triggerUpdate();
});
}
});
}
void
Releases::refreshRecentlyAdded()
{
auto after = Wt::WLocalDateTime::currentServerDateTime().toUTC().addMonths(-1);
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto releases = Release::getLastAdded(LmsApp->getDboSession(), after, 5);
_recentlyAddedContainer->clear();
addCompactEntries(_recentlyAddedContainer, releases);
}
void
Releases::refreshMostPlayed()
{
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto releases = TrackStats::getMostPlayedReleases(LmsApp->getDboSession(), LmsApp->getCurrentUser(), 5);
_mostPlayedContainer->clear();
addCompactEntries(_mostPlayedContainer, releases);
}
void
Releases::refresh()
{
_releasesContainer->clear();
_container->clear();
addSome();
}
@@ -117,13 +157,13 @@ Releases::addSome()
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
bool moreResults;
auto releases = Release::getByFilter(LmsApp->getDboSession(), clusterIds, searchKeywords, _releasesContainer->count(), 20, moreResults);
auto releases = Release::getByFilter(LmsApp->getDboSession(), clusterIds, searchKeywords, _container->count(), 20, moreResults);
for (auto release : releases)
{
auto releaseId = release.id();
Wt::WTemplate* entry = _releasesContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Releases.template.entry"));
Wt::WTemplate* entry = _container->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Releases.template.entry"));
entry->addFunction("tr", Wt::WTemplate::Functions::tr);
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
+4 -2
View File
@@ -39,6 +39,7 @@ class Releases : public Wt::WTemplate
Wt::Signal<Database::IdType> releasePlay;
private:
void refreshRecentlyAdded();
void refreshMostPlayed();
void refresh();
void addSome();
@@ -46,8 +47,9 @@ class Releases : public Wt::WTemplate
Filters* _filters;
Wt::WTemplate* _showMore;
Wt::WLineEdit* _search;
Wt::WContainerWidget* _releasesContainer;
Wt::WContainerWidget* _mostPlayedReleasesContainer;
Wt::WContainerWidget* _container;
Wt::WContainerWidget* _mostPlayedContainer;
Wt::WContainerWidget* _recentlyAddedContainer;
};
} // namespace UserInterface