Added non album tracks in the artist view. closes #165
This commit is contained in:
@@ -15,12 +15,8 @@
|
|||||||
${</if-has-release>}
|
${</if-has-release>}
|
||||||
${<if-has-non-release-track>}
|
${<if-has-non-release-track>}
|
||||||
<div class="Lms-explore-artist-tracks-container">
|
<div class="Lms-explore-artist-tracks-container">
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-8">
|
|
||||||
${tracks}
|
${tracks}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
${</if-has-non-release-track>}
|
${</if-has-non-release-track>}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
${<if-has-similar-artists>}
|
${<if-has-similar-artists>}
|
||||||
|
|||||||
@@ -465,12 +465,39 @@ Artist::getTracks(std::optional<TrackArtistLinkType> linkType) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Track>>
|
std::vector<Wt::Dbo::ptr<Track>>
|
||||||
Artist::getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType) const
|
Artist::getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType, std::optional<Range> range, bool& moreResults) const
|
||||||
{
|
{
|
||||||
assert(self());
|
assert(self());
|
||||||
assert(IdIsValid(self()->id()));
|
assert(IdIsValid(self()->id()));
|
||||||
assert(session());
|
assert(session());
|
||||||
|
|
||||||
|
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id")
|
||||||
|
.where("a.id = ?").bind(self()->id())
|
||||||
|
.where("t.release_id is NULL")
|
||||||
|
.orderBy("t.name")
|
||||||
|
.limit(range ? static_cast<int>(range->limit) + 1 : -1)
|
||||||
|
.offset(range ? static_cast<int>(range->offset) : -1)};
|
||||||
|
|
||||||
|
if (linkType)
|
||||||
|
query.where("t_a_l.type = ?").bind(*linkType);
|
||||||
|
|
||||||
|
Wt::Dbo::collection<Track::pointer> tracks {query.resultList()};
|
||||||
|
|
||||||
|
auto res {std::vector<Track::pointer>(tracks.begin(), tracks.end())};
|
||||||
|
if (range && res.size() == static_cast<std::size_t>(range->limit) + 1)
|
||||||
|
{
|
||||||
|
moreResults = true;
|
||||||
|
res.pop_back();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
moreResults = false;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Artist::hasNonReleaseTracks(std::optional<TrackArtistLinkType> linkType) const
|
||||||
|
{
|
||||||
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id")
|
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id")
|
||||||
.where("a.id = ?").bind(self()->id())
|
.where("a.id = ?").bind(self()->id())
|
||||||
.where("t.release_id is NULL")
|
.where("t.release_id is NULL")
|
||||||
@@ -479,12 +506,10 @@ Artist::getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType) const
|
|||||||
if (linkType)
|
if (linkType)
|
||||||
query.where("t_a_l.type = ?").bind(*linkType);
|
query.where("t_a_l.type = ?").bind(*linkType);
|
||||||
|
|
||||||
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {query.resultList()};
|
Wt::Dbo::collection<Track::pointer> tracks {query.resultList()};
|
||||||
|
return !tracks.empty();
|
||||||
return std::vector<Wt::Dbo::ptr<Track>>(tracks.begin(), tracks.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Track>>
|
std::vector<Wt::Dbo::ptr<Track>>
|
||||||
Artist::getRandomTracks(std::optional<std::size_t> count) const
|
Artist::getRandomTracks(std::optional<std::size_t> count) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ class Artist : public Wt::Dbo::Dbo<Artist>
|
|||||||
std::vector<Wt::Dbo::ptr<Release>> getReleases(const std::set<IdType>& clusterIds = {}) const; // if non empty, get the releases that match all these clusters
|
std::vector<Wt::Dbo::ptr<Release>> getReleases(const std::set<IdType>& clusterIds = {}) const; // if non empty, get the releases that match all these clusters
|
||||||
std::size_t getReleaseCount() const;
|
std::size_t getReleaseCount() const;
|
||||||
std::vector<Wt::Dbo::ptr<Track>> getTracks(std::optional<TrackArtistLinkType> linkType = {}) const;
|
std::vector<Wt::Dbo::ptr<Track>> getTracks(std::optional<TrackArtistLinkType> linkType = {}) const;
|
||||||
std::vector<Wt::Dbo::ptr<Track>> getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType = {}) const;
|
bool hasNonReleaseTracks(std::optional<TrackArtistLinkType> linkType = std::nullopt) const;
|
||||||
|
std::vector<Wt::Dbo::ptr<Track>> getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType, std::optional<Range> range, bool& moreResults) const;
|
||||||
std::vector<Wt::Dbo::ptr<Track>> getRandomTracks(std::optional<std::size_t> count) const;
|
std::vector<Wt::Dbo::ptr<Track>> getRandomTracks(std::optional<std::size_t> count) const;
|
||||||
|
|
||||||
// No artistLinkTypes means get them all
|
// No artistLinkTypes means get them all
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
|
|
||||||
|
#include "common/InfiniteScrollingContainer.hpp"
|
||||||
#include "resource/DownloadResource.hpp"
|
#include "resource/DownloadResource.hpp"
|
||||||
#include "ArtistListHelpers.hpp"
|
#include "ArtistListHelpers.hpp"
|
||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
@@ -93,6 +94,8 @@ Artist::refreshView()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
_artistId = {};
|
||||||
|
_trackContainer = nullptr;
|
||||||
|
|
||||||
const auto artistId {extractArtistIdFromInternalPath()};
|
const auto artistId {extractArtistIdFromInternalPath()};
|
||||||
if (!artistId)
|
if (!artistId)
|
||||||
@@ -109,6 +112,8 @@ Artist::refreshView()
|
|||||||
if (!artist)
|
if (!artist)
|
||||||
throw ArtistNotFoundException {};
|
throw ArtistNotFoundException {};
|
||||||
|
|
||||||
|
_artistId = *artistId;
|
||||||
|
|
||||||
refreshReleases(artist);
|
refreshReleases(artist);
|
||||||
refreshNonReleaseTracks(artist);
|
refreshNonReleaseTracks(artist);
|
||||||
refreshLinks(artist);
|
refreshLinks(artist);
|
||||||
@@ -140,7 +145,7 @@ Artist::refreshView()
|
|||||||
|
|
||||||
playBtn->clicked().connect([=]
|
playBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
artistsAction.emit(PlayQueueAction::Play, {*artistId});
|
artistsAction.emit(PlayQueueAction::Play, {_artistId});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,12 +159,12 @@ Artist::refreshView()
|
|||||||
popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled"))
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled"))
|
||||||
->triggered().connect(this, [=]
|
->triggered().connect(this, [=]
|
||||||
{
|
{
|
||||||
artistsAction.emit(PlayQueueAction::PlayShuffled, {*artistId});
|
artistsAction.emit(PlayQueueAction::PlayShuffled, {_artistId});
|
||||||
});
|
});
|
||||||
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
->triggered().connect(this, [=]
|
->triggered().connect(this, [=]
|
||||||
{
|
{
|
||||||
artistsAction.emit(PlayQueueAction::PlayLast, {*artistId});
|
artistsAction.emit(PlayQueueAction::PlayLast, {_artistId});
|
||||||
});
|
});
|
||||||
|
|
||||||
bool isStarred {};
|
bool isStarred {};
|
||||||
@@ -210,17 +215,17 @@ Artist::refreshReleases(const Wt::Dbo::ptr<Database::Artist>& artist)
|
|||||||
void
|
void
|
||||||
Artist::refreshNonReleaseTracks(const Wt::Dbo::ptr<Database::Artist>& artist)
|
Artist::refreshNonReleaseTracks(const Wt::Dbo::ptr<Database::Artist>& artist)
|
||||||
{
|
{
|
||||||
const auto tracks {artist->getNonReleaseTracks()};
|
if (!artist->hasNonReleaseTracks())
|
||||||
if (tracks.empty())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setCondition("if-has-non-release-track", true);
|
setCondition("if-has-non-release-track", true);
|
||||||
|
_trackContainer = bindNew<InfiniteScrollingContainer>("tracks", Wt::WString::tr("Lms.Explore.Tracks.template.container"));
|
||||||
Wt::WContainerWidget* tracksContainer = bindNew<Wt::WContainerWidget>("tracks");
|
_trackContainer->onRequestElements.connect(this, [this]
|
||||||
for (const Track::pointer& track : tracks)
|
|
||||||
{
|
{
|
||||||
tracksContainer->addWidget(TrackListHelpers::createEntry(track, tracksAction));
|
addSomeNonReleaseTracks();
|
||||||
}
|
});
|
||||||
|
|
||||||
|
addSomeNonReleaseTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -257,5 +262,34 @@ Artist::refreshLinks(const Database::Artist::pointer& artist)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Artist::addSomeNonReleaseTracks()
|
||||||
|
{
|
||||||
|
bool moreResults {};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
|
const Database::Artist::pointer artist {Database::Artist::getById(LmsApp->getDbSession(), _artistId)};
|
||||||
|
if (!artist)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto tracks {artist->getNonReleaseTracks(std::nullopt, Database::Range {static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize}, moreResults)};
|
||||||
|
|
||||||
|
for (const auto& track : tracks)
|
||||||
|
{
|
||||||
|
if (_trackContainer->getCount() == _tracksMaxCount)
|
||||||
|
{
|
||||||
|
moreResults = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_trackContainer->add(TrackListHelpers::createEntry(track, tracksAction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_trackContainer->setHasMore(moreResults);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -34,12 +34,14 @@ namespace Database
|
|||||||
class Release;
|
class Release;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface
|
||||||
|
|
||||||
class Filters;
|
|
||||||
|
|
||||||
class Artist : public Wt::WTemplate
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Filters;
|
||||||
|
class InfiniteScrollingContainer;
|
||||||
|
|
||||||
|
class Artist : public Wt::WTemplate
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Artist(Filters* filters);
|
Artist(Filters* filters);
|
||||||
|
|
||||||
@@ -53,10 +55,13 @@ class Artist : public Wt::WTemplate
|
|||||||
void refreshSimilarArtists(const std::unordered_set<Database::IdType>& similarArtistsId);
|
void refreshSimilarArtists(const std::unordered_set<Database::IdType>& similarArtistsId);
|
||||||
void refreshLinks(const Wt::Dbo::ptr<Database::Artist>& artist);
|
void refreshLinks(const Wt::Dbo::ptr<Database::Artist>& artist);
|
||||||
|
|
||||||
std::unique_ptr<Wt::WTemplate> createRelease(const Wt::Dbo::ptr<Database::Artist>& artist, const Wt::Dbo::ptr<Database::Release>& release);
|
void addSomeNonReleaseTracks();
|
||||||
|
static constexpr std::size_t _tracksBatchSize {6};
|
||||||
|
static constexpr std::size_t _tracksMaxCount {160};
|
||||||
|
|
||||||
Filters* _filters {};
|
Filters* _filters {};
|
||||||
};
|
InfiniteScrollingContainer* _trackContainer {};
|
||||||
|
Database::IdType _artistId {};
|
||||||
|
};
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -318,6 +318,16 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks)
|
|||||||
ScopedTrack track1 {session, "MyTrack1"};
|
ScopedTrack track1 {session, "MyTrack1"};
|
||||||
ScopedTrack track2 {session, "MyTrack2"};
|
ScopedTrack track2 {session, "MyTrack2"};
|
||||||
ScopedRelease release{session, "MyRelease"};
|
ScopedRelease release{session, "MyRelease"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
EXPECT_EQ(artist->hasNonReleaseTracks(std::nullopt), false);
|
||||||
|
|
||||||
|
bool moreResults;
|
||||||
|
const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )};
|
||||||
|
EXPECT_EQ(tracks.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto transaction {session.createUniqueTransaction()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
@@ -331,7 +341,10 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks)
|
|||||||
{
|
{
|
||||||
auto transaction {session.createSharedTransaction()};
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
const auto tracks {artist->getNonReleaseTracks()};
|
bool moreResults;
|
||||||
|
const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )};
|
||||||
|
EXPECT_EQ(artist->hasNonReleaseTracks(std::nullopt), true);
|
||||||
|
EXPECT_EQ(moreResults, false);
|
||||||
ASSERT_EQ(tracks.size(), 1);
|
ASSERT_EQ(tracks.size(), 1);
|
||||||
EXPECT_EQ(tracks.front().id(), track2.getId());
|
EXPECT_EQ(tracks.front().id(), track2.getId());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user