Added recently added releases feature
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user