Tried to factorize some db code
This commit is contained in:
@@ -204,114 +204,6 @@ Artist::find(Session& session, const FindParameters& params)
|
||||
return execQuery(query, params.range);
|
||||
}
|
||||
|
||||
RangeResults<ReleaseId>
|
||||
Artist::getReleases(Range range, const std::vector<ClusterId>& clusterIds) const
|
||||
{
|
||||
assert(session());
|
||||
|
||||
WhereClause where;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << "SELECT DISTINCT r.id FROM release r 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 INNER JOIN track t ON t.release_id = r.id";
|
||||
|
||||
if (!clusterIds.empty())
|
||||
{
|
||||
oss << " INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id";
|
||||
|
||||
WhereClause clusterClause;
|
||||
|
||||
for (auto id : clusterIds)
|
||||
clusterClause.Or(WhereClause("c.id = ?")).bind(id.toString());
|
||||
|
||||
where.And(clusterClause);
|
||||
}
|
||||
|
||||
where.And(WhereClause("a.id = ?")).bind(getId().toString());
|
||||
|
||||
oss << " " << where.get();
|
||||
|
||||
if (!clusterIds.empty())
|
||||
oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size();
|
||||
|
||||
oss << " ORDER BY t.date DESC, r.name COLLATE NOCASE";
|
||||
|
||||
auto query {session()->query<ReleaseId>(oss.str())};
|
||||
|
||||
for (const std::string& bindArg : where.getBindArgs())
|
||||
query.bind(bindArg);
|
||||
|
||||
return execQuery(query, range);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
Artist::getReleaseCount() const
|
||||
{
|
||||
assert(session());
|
||||
|
||||
int res = session()->query<int>("SELECT COUNT(DISTINCT r.id) FROM release r 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 INNER JOIN track t ON t.release_id = r.id")
|
||||
.where("a.id = ?").bind(getId());
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Artist::getTracks(std::optional<TrackArtistLinkType> linkType) const
|
||||
{
|
||||
assert(session());
|
||||
|
||||
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT DISTINCT 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(getId())
|
||||
.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number")};
|
||||
|
||||
if (linkType)
|
||||
query.where("t_a_l.type = ?").bind(*linkType);
|
||||
|
||||
auto tracks {query.resultList()};
|
||||
return std::vector<Track::pointer>(tracks.begin(), tracks.end());
|
||||
}
|
||||
|
||||
RangeResults<Track::pointer>
|
||||
Artist::getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType, Range range) const
|
||||
{
|
||||
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(getId())
|
||||
.where("t.release_id is NULL")
|
||||
.orderBy("t.name")};
|
||||
if (linkType)
|
||||
query.where("t_a_l.type = ?").bind(*linkType);
|
||||
|
||||
return execQuery(query, range);
|
||||
}
|
||||
|
||||
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")
|
||||
.where("a.id = ?").bind(getId())
|
||||
.where("t.release_id is NULL")
|
||||
.orderBy("t.name")};
|
||||
|
||||
if (linkType)
|
||||
query.where("t_a_l.type = ?").bind(*linkType);
|
||||
|
||||
return !query.resultList().empty();
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Artist::getRandomTracks(std::optional<std::size_t> count) const
|
||||
{
|
||||
assert(session());
|
||||
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {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(getId())
|
||||
.orderBy("RANDOM()")
|
||||
.limit(count ? static_cast<int>(*count) : -1)};
|
||||
|
||||
return std::vector<Track::pointer>(tracks.begin(), tracks.end());
|
||||
}
|
||||
|
||||
RangeResults<ArtistId>
|
||||
Artist::findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes, Range range) const
|
||||
{
|
||||
|
||||
@@ -549,6 +549,28 @@ CREATE TABLE "listen" (
|
||||
ScanSettings::get(session).modify()->addAudioFileExtension(".wv");
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
migrateFromV33(Session& session)
|
||||
{
|
||||
// remove name from track_artist_link
|
||||
// Drop Auth mode
|
||||
session.getDboSession().execute(R"(
|
||||
CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
|
||||
"id" integer primary key autoincrement,
|
||||
"version" integer not null,
|
||||
"type" integer not null,
|
||||
"track_id" bigint,
|
||||
"artist_id" bigint,
|
||||
constraint "fk_track_artist_link_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_track_artist_link_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred
|
||||
);
|
||||
))");
|
||||
session.getDboSession().execute("INSERT INTO track_artist_link_backup SELECT id, version, type, track_id, artist_id FROM track_artist_link");
|
||||
session.getDboSession().execute("DROP TABLE track_artist_link");
|
||||
session.getDboSession().execute("ALTER TABLE track_artist_link_backup RENAME TO track_artist_link");
|
||||
}
|
||||
|
||||
void
|
||||
doDbMigration(Session& session)
|
||||
{
|
||||
@@ -588,6 +610,7 @@ CREATE TABLE "listen" (
|
||||
{30, migrateFromV30},
|
||||
{31, migrateFromV31},
|
||||
{32, migrateFromV32},
|
||||
{33, migrateFromV33},
|
||||
};
|
||||
|
||||
while (1)
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Database
|
||||
class Session;
|
||||
|
||||
using Version = std::size_t;
|
||||
static constexpr Version LMS_DATABASE_VERSION {33};
|
||||
static constexpr Version LMS_DATABASE_VERSION {34};
|
||||
class VersionInfo
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -43,7 +43,7 @@ createQuery(Session& session, const Track::FindParameters& params)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
auto query {session.getDboSession().query<TrackId>("SELECT t.id from track t")};
|
||||
auto query {session.getDboSession().query<TrackId>("SELECT DISTINCT t.id from track t")};
|
||||
|
||||
for (std::string_view keyword : params.keywords)
|
||||
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + escapeLikeKeyword(keyword) + "%");
|
||||
@@ -79,6 +79,33 @@ createQuery(Session& session, const Track::FindParameters& params)
|
||||
query.where(oss.str());
|
||||
}
|
||||
|
||||
if (params.artist.isValid())
|
||||
{
|
||||
query.join("artist a ON a.id = t_a_l.artist_id")
|
||||
.join("track_artist_link t_a_l ON t_a_l.track_id = t.id")
|
||||
.where("a.id = ?").bind(params.artist);
|
||||
|
||||
if (!params.trackArtistLinkTypes.empty())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
bool first {true};
|
||||
for (TrackArtistLinkType linkType : params.trackArtistLinkTypes)
|
||||
{
|
||||
if (!first)
|
||||
oss << " OR ";
|
||||
oss << "t_a_l.type = ?";
|
||||
query.bind(linkType);
|
||||
|
||||
first = false;
|
||||
}
|
||||
query.where(oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
if (params.nonRelease)
|
||||
query.where("t.release_id IS NULL");
|
||||
|
||||
switch (params.sortMethod)
|
||||
{
|
||||
case TrackSortMethod::None:
|
||||
@@ -93,6 +120,12 @@ createQuery(Session& session, const Track::FindParameters& params)
|
||||
assert(params.starringUser.isValid());
|
||||
query.orderBy("s_t.date_time DESC");
|
||||
break;
|
||||
case TrackSortMethod::Name:
|
||||
query.orderBy("t.name COLLATE NOCASE");
|
||||
break;
|
||||
case TrackSortMethod::DateDescAndRelease:
|
||||
query.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number");
|
||||
break;
|
||||
}
|
||||
|
||||
return query;
|
||||
|
||||
@@ -89,13 +89,6 @@ class Artist : public Object<Artist, ArtistId>
|
||||
const std::string& getSortName() const { return _sortName; }
|
||||
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
|
||||
|
||||
RangeResults<ReleaseId> getReleases(Range range, const std::vector<ClusterId>& clusterIds = {}) const; // if non empty, get the releases that match all these clusters
|
||||
std::size_t getReleaseCount() const;
|
||||
std::vector<ObjectPtr<Track>> getTracks(std::optional<TrackArtistLinkType> linkType = {}) const;
|
||||
bool hasNonReleaseTracks(std::optional<TrackArtistLinkType> linkType = std::nullopt) const;
|
||||
RangeResults<ObjectPtr<Track>> getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType, Range range) const;
|
||||
std::vector<ObjectPtr<Track>> getRandomTracks(std::optional<std::size_t> count) const;
|
||||
|
||||
// No artistLinkTypes means get them all
|
||||
RangeResults<ArtistId> findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes = {}, Range range = {}) const;
|
||||
|
||||
|
||||
@@ -63,7 +63,10 @@ class Track : public Object<Track, TrackId>
|
||||
Range range;
|
||||
Wt::WDateTime writtenAfter;
|
||||
UserId starringUser; // only tracks starred by this user
|
||||
std::optional<Scrobbler> scrobbler; // and for this scrobbler
|
||||
std::optional<Scrobbler> scrobbler; // and for this scrobbler
|
||||
ArtistId artist; // only tracks that involve this user
|
||||
EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
|
||||
bool nonRelease {}; // only tracks that do not belong to a release
|
||||
|
||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||
@@ -71,7 +74,10 @@ class Track : public Object<Track, TrackId>
|
||||
FindParameters& setRange(Range _range) { range = _range; return *this; }
|
||||
FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; }
|
||||
FindParameters& setStarringUser(UserId _user, Scrobbler _scrobbler) { starringUser = _user; scrobbler = _scrobbler; return *this; }
|
||||
FindParameters& setArtist(ArtistId _artist, EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}) { artist = _artist; trackArtistLinkTypes = _trackArtistLinkTypes; return *this; }
|
||||
FindParameters& setNonRelease(bool _nonRelease) { nonRelease = _nonRelease; return *this; }
|
||||
};
|
||||
|
||||
struct PathResult
|
||||
{
|
||||
TrackId trackId;
|
||||
|
||||
@@ -110,6 +110,8 @@ namespace Database
|
||||
Random,
|
||||
LastWritten,
|
||||
StarredDateDesc,
|
||||
Name,
|
||||
DateDescAndRelease,
|
||||
};
|
||||
|
||||
enum class TrackArtistLinkType
|
||||
|
||||
@@ -80,8 +80,6 @@ TEST_F(DatabaseFixture, Artist_singleTrack)
|
||||
ASSERT_EQ(artists.size(), 1);
|
||||
EXPECT_EQ(artists.front()->getId(), artist.getId());
|
||||
|
||||
EXPECT_EQ(artist->getReleaseCount(), 0);
|
||||
|
||||
ASSERT_EQ(track->getArtistLinks().size(), 1);
|
||||
auto artistLink {track->getArtistLinks().front()};
|
||||
EXPECT_EQ(artistLink->getTrack()->getId(), track.getId());
|
||||
@@ -91,17 +89,6 @@ TEST_F(DatabaseFixture, Artist_singleTrack)
|
||||
EXPECT_TRUE(track->getArtists({TrackArtistLinkType::ReleaseArtist}).empty());
|
||||
EXPECT_EQ(track->getArtists({}).size(), 1);
|
||||
}
|
||||
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
|
||||
auto tracks {artist->getTracks()};
|
||||
ASSERT_EQ(tracks.size(), 1);
|
||||
EXPECT_EQ(tracks.front()->getId(), track.getId());
|
||||
|
||||
EXPECT_TRUE(artist->getTracks(TrackArtistLinkType::ReleaseArtist).empty());
|
||||
EXPECT_EQ(artist->getTracks(TrackArtistLinkType::Artist).size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles)
|
||||
@@ -143,10 +130,18 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles)
|
||||
|
||||
EXPECT_EQ(track->getArtistLinks().size(), 3);
|
||||
|
||||
EXPECT_EQ(artist->getTracks().size(), 1);
|
||||
EXPECT_EQ(artist->getTracks({TrackArtistLinkType::ReleaseArtist}).size(), 1);
|
||||
EXPECT_EQ(artist->getTracks({TrackArtistLinkType::Artist}).size(), 1);
|
||||
EXPECT_EQ(artist->getTracks({TrackArtistLinkType::Writer}).size(), 1);
|
||||
auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist.getId()))};
|
||||
EXPECT_EQ(tracks.results.size(), 1);
|
||||
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::ReleaseArtist}));
|
||||
EXPECT_EQ(tracks.results.size(), 1);
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Artist}));
|
||||
EXPECT_EQ(tracks.results.size(), 1);
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Writer}));
|
||||
EXPECT_EQ(tracks.results.size(), 1);
|
||||
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Composer}));
|
||||
EXPECT_EQ(tracks.results.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,13 +182,25 @@ TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists)
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
|
||||
EXPECT_EQ(artist1->getTracks().front(), track.get());
|
||||
EXPECT_EQ(artist2->getTracks().front(), track.get());
|
||||
auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist1->getId()))};
|
||||
ASSERT_EQ(tracks.results.size(), 1);
|
||||
EXPECT_EQ(tracks.results.front(), track->getId());
|
||||
|
||||
EXPECT_TRUE(artist1->getTracks(TrackArtistLinkType::ReleaseArtist).empty());
|
||||
EXPECT_EQ(artist1->getTracks(TrackArtistLinkType::Artist).size(), 1);
|
||||
EXPECT_TRUE(artist2->getTracks(TrackArtistLinkType::ReleaseArtist).empty());
|
||||
EXPECT_EQ(artist2->getTracks(TrackArtistLinkType::Artist).size(), 1);
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist2->getId()));
|
||||
ASSERT_EQ(tracks.results.size(), 1);
|
||||
EXPECT_EQ(tracks.results.front(), track->getId());
|
||||
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist1->getId(), {TrackArtistLinkType::ReleaseArtist}));
|
||||
EXPECT_EQ(tracks.results.size(), 0);
|
||||
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist1->getId(), {TrackArtistLinkType::Artist}));
|
||||
EXPECT_EQ(tracks.results.size(), 1);
|
||||
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist2->getId(), {TrackArtistLinkType::ReleaseArtist}));
|
||||
EXPECT_EQ(tracks.results.size(), 0);
|
||||
|
||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist2->getId(), {TrackArtistLinkType::Artist}));
|
||||
EXPECT_EQ(tracks.results.size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,9 +331,8 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
|
||||
|
||||
{
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
EXPECT_FALSE(artist->hasNonReleaseTracks(std::nullopt));
|
||||
|
||||
const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {})};
|
||||
auto tracks {Track::find(session, Track::FindParameters {}.setNonRelease(true).setArtist(artist->getId()))};
|
||||
EXPECT_EQ(tracks.results.size(), 0);
|
||||
}
|
||||
|
||||
@@ -343,11 +349,9 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
|
||||
{
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {})};
|
||||
EXPECT_TRUE(artist->hasNonReleaseTracks(std::nullopt));
|
||||
EXPECT_FALSE(tracks.moreResults);
|
||||
const auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist->getId()).setNonRelease(true))};
|
||||
ASSERT_EQ(tracks.results.size(), 1);
|
||||
EXPECT_EQ(tracks.results.front()->getId(), track2.getId());
|
||||
EXPECT_EQ(tracks.results.front(), track2.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -574,11 +574,11 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster)
|
||||
ASSERT_EQ(artists.results.size(), 1);
|
||||
EXPECT_EQ(artists.results.front(), artist.getId());
|
||||
|
||||
auto releases {artist->getReleases(Range {})};
|
||||
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release.getId());
|
||||
|
||||
releases = artist->getReleases(Range {}, {cluster.getId()});
|
||||
releases = Release::find(session, Release::FindParameters {}.setArtist(artist.getId()).setClusters({cluster.getId()}));
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release.getId());
|
||||
}
|
||||
@@ -605,11 +605,11 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters)
|
||||
{
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
auto releases {artist->getReleases(Range {})};
|
||||
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release.getId());
|
||||
|
||||
releases = artist->getReleases(Range {}, {cluster1.getId(), cluster2.getId()});
|
||||
releases = Release::find(session, Release::FindParameters {}.setArtist(artist.getId()).setClusters({cluster1.getId(), cluster2.getId()}));
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release.getId());
|
||||
}
|
||||
|
||||
@@ -49,8 +49,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease)
|
||||
{
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
EXPECT_EQ(artist->getReleaseCount(), 1);
|
||||
auto releases {artist->getReleases(Range {})};
|
||||
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release.getId());
|
||||
|
||||
@@ -74,12 +73,10 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist)
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
|
||||
auto releases {artist->getReleases(Range {})};
|
||||
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release.getId());
|
||||
|
||||
EXPECT_EQ(artist->getReleaseCount(), 1);
|
||||
|
||||
auto artists {release->getArtists()};
|
||||
ASSERT_EQ(artists.size(), 1);
|
||||
ASSERT_EQ(artists.front()->getId(), artist.getId());
|
||||
|
||||
@@ -422,7 +422,7 @@ releaseToResponseNode(const Release::pointer& release, Session& dbSession, const
|
||||
|
||||
static
|
||||
Response::Node
|
||||
artistToResponseNode(const User::pointer& user, const Artist::pointer& artist, bool id3)
|
||||
artistToResponseNode(const Artist::pointer& artist, Session& session, const User::pointer& user, bool id3)
|
||||
{
|
||||
Response::Node artistNode;
|
||||
|
||||
@@ -430,7 +430,10 @@ artistToResponseNode(const User::pointer& user, const Artist::pointer& artist, b
|
||||
artistNode.setAttribute("name", artist->getName());
|
||||
|
||||
if (id3)
|
||||
artistNode.setAttribute("albumCount", artist->getReleaseCount());
|
||||
{
|
||||
const auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist->getId()))};
|
||||
artistNode.setAttribute("albumCount", releases.results.size());
|
||||
}
|
||||
|
||||
if (Service<Scrobbling::IScrobblingService>::get()->isStarred(user->getId(), artist->getId()))
|
||||
artistNode.setAttribute("starred", reportedStarredDate);
|
||||
@@ -884,9 +887,9 @@ handleGetArtistRequest(RequestContext& context)
|
||||
throw UserNotAuthorizedError {};
|
||||
|
||||
Response response {Response::createOkResponse(context.serverProtocolVersion)};
|
||||
Response::Node artistNode {artistToResponseNode(user, artist, true /* id3 */)};
|
||||
Response::Node artistNode {artistToResponseNode(artist, context.dbSession, user, true /* id3 */)};
|
||||
|
||||
auto releases {artist->getReleases(Range {})};
|
||||
const auto releases {Release::find(context.dbSession, Release::FindParameters {}.setArtist(artist->getId()))};
|
||||
for (const ReleaseId releaseId : releases.results)
|
||||
{
|
||||
const Release::pointer release {Release::find(context.dbSession, releaseId)};
|
||||
@@ -936,7 +939,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3)
|
||||
{
|
||||
const Artist::pointer similarArtist {Artist::find(context.dbSession, similarArtistId)};
|
||||
if (similarArtist)
|
||||
artistInfoNode.addArrayChild("similarArtist", artistToResponseNode(user, similarArtist, id3));
|
||||
artistInfoNode.addArrayChild("similarArtist", artistToResponseNode(similarArtist, context.dbSession, user, id3));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -988,7 +991,7 @@ handleGetMusicDirectoryRequest(RequestContext& context)
|
||||
for (const ArtistId artistId : artistIds.results)
|
||||
{
|
||||
const Artist::pointer artist {Artist::find(context.dbSession, artistId)};
|
||||
directoryNode.addArrayChild("child", artistToResponseNode(user, artist, false /* no id3 */));
|
||||
directoryNode.addArrayChild("child", artistToResponseNode(artist, context.dbSession, user, false /* no id3 */));
|
||||
}
|
||||
}
|
||||
else if (artistId)
|
||||
@@ -1001,7 +1004,7 @@ handleGetMusicDirectoryRequest(RequestContext& context)
|
||||
|
||||
directoryNode.setAttribute("name", makeNameFilesystemCompatible(artist->getName()));
|
||||
|
||||
auto releases {artist->getReleases(Range {})};
|
||||
const auto releases {Release::find(context.dbSession, Release::FindParameters {}.setArtist(*artistId))};
|
||||
for (const ReleaseId releaseId : releases.results)
|
||||
{
|
||||
const Release::pointer release {Release::find(context.dbSession, releaseId)};
|
||||
@@ -1095,7 +1098,7 @@ handleGetArtistsRequestCommon(RequestContext& context, bool id3)
|
||||
indexNode.setAttribute("name", std::string {sortChar});
|
||||
|
||||
for (const Artist::pointer& artist :artists)
|
||||
indexNode.addArrayChild("artist", artistToResponseNode(user, artist, id3));
|
||||
indexNode.addArrayChild("artist", artistToResponseNode(artist, context.dbSession, user, id3));
|
||||
}
|
||||
|
||||
return response;
|
||||
@@ -1160,26 +1163,34 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
|
||||
throw UserNotAuthorizedError {};
|
||||
|
||||
// "Returns a random collection of songs from the given artist and similar artists"
|
||||
auto tracks {artist->getRandomTracks(count / 2)};
|
||||
const auto trackResults {Track::find(context.dbSession, Track::FindParameters {}
|
||||
.setArtist(artist->getId())
|
||||
.setRange({0, count / 2})
|
||||
.setSortMethod(TrackSortMethod::Random))};
|
||||
|
||||
std::vector<TrackId> tracks {trackResults.results};
|
||||
|
||||
for (const ArtistId similarArtistId : similarArtistIds)
|
||||
{
|
||||
const Artist::pointer similarArtist {Artist::find(context.dbSession, similarArtistId)};
|
||||
if (!similarArtist)
|
||||
continue;
|
||||
|
||||
auto similarArtistTracks {similarArtist->getRandomTracks((count / 2) / 5)};
|
||||
const auto similarArtistTracks {Track::find(context.dbSession, Track::FindParameters {}
|
||||
.setArtist(similarArtistId)
|
||||
.setRange({0, (count / 2) / 5})
|
||||
.setSortMethod(TrackSortMethod::Random))};
|
||||
|
||||
tracks.insert(std::end(tracks),
|
||||
std::make_move_iterator(std::begin(similarArtistTracks)),
|
||||
std::make_move_iterator(std::end(similarArtistTracks)));
|
||||
std::begin(similarArtistTracks.results),
|
||||
std::end(similarArtistTracks.results));
|
||||
}
|
||||
|
||||
Random::shuffleContainer(tracks);
|
||||
|
||||
Response response {Response::createOkResponse(context.serverProtocolVersion)};
|
||||
Response::Node& similarSongsNode {response.createNode(id3 ? "similarSongs2" : "similarSongs")};
|
||||
for (const Track::pointer& track : tracks)
|
||||
for (const TrackId trackId : tracks)
|
||||
{
|
||||
const Track::pointer track {Track::find(context.dbSession, trackId)};
|
||||
similarSongsNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -1216,7 +1227,7 @@ handleGetStarredRequestCommon(RequestContext& context, bool id3)
|
||||
for (const ArtistId artistId : scrobbling.getStarredArtists(context.userId, {} /* clusters */, std::nullopt /* linkType */, ArtistSortMethod::BySortName, Range {}).results)
|
||||
{
|
||||
if (auto artist {Artist::find(context.dbSession, artistId)})
|
||||
starredNode.addArrayChild("artist", artistToResponseNode(user, artist, id3));
|
||||
starredNode.addArrayChild("artist", artistToResponseNode(artist, context.dbSession, user, id3));
|
||||
}
|
||||
|
||||
for (const ReleaseId releaseId : scrobbling.getStarredReleases(context.userId, {} /* clusters */, Range {}).results)
|
||||
@@ -1433,7 +1444,7 @@ handleSearchRequestCommon(RequestContext& context, bool id3)
|
||||
for (const ArtistId artistId : artistIds.results)
|
||||
{
|
||||
const auto artist {Artist::find(context.dbSession, artistId)};
|
||||
searchResult2Node.addArrayChild("artist", artistToResponseNode(user, artist, id3));
|
||||
searchResult2Node.addArrayChild("artist", artistToResponseNode(artist, context.dbSession, user, id3));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,17 +113,12 @@ Artist::refreshView()
|
||||
|
||||
std::size_t sectionCount{};
|
||||
|
||||
LMS_LOG(UI, DEBUG) << "Refresh releases...";
|
||||
if (refreshReleases())
|
||||
sectionCount++;
|
||||
LMS_LOG(UI, DEBUG) << "Refresh appears on releases...";
|
||||
if (refreshAppearsOnReleases())
|
||||
sectionCount++;
|
||||
LMS_LOG(UI, DEBUG) << "Refresh non album tracks...";
|
||||
if (refreshNonReleaseTracks(artist))
|
||||
if (refreshNonReleaseTracks())
|
||||
sectionCount++;
|
||||
|
||||
LMS_LOG(UI, DEBUG) << "Refresh links...";
|
||||
refreshLinks(artist);
|
||||
refreshSimilarArtists(similarArtistIds);
|
||||
|
||||
@@ -235,11 +230,8 @@ Artist::refreshAppearsOnReleases()
|
||||
}
|
||||
|
||||
bool
|
||||
Artist::refreshNonReleaseTracks(const ObjectPtr<Database::Artist>& artist)
|
||||
Artist::refreshNonReleaseTracks()
|
||||
{
|
||||
if (!artist->hasNonReleaseTracks())
|
||||
return false;
|
||||
|
||||
setCondition("if-has-non-release-tracks", true);
|
||||
_trackContainer = bindNew<InfiniteScrollingContainer>("tracks");
|
||||
_trackContainer->onRequestElements.connect(this, [this]
|
||||
@@ -247,7 +239,9 @@ Artist::refreshNonReleaseTracks(const ObjectPtr<Database::Artist>& artist)
|
||||
addSomeNonReleaseTracks();
|
||||
});
|
||||
|
||||
return addSomeNonReleaseTracks();
|
||||
const bool added {addSomeNonReleaseTracks()};
|
||||
setCondition("if-has-non-release-tracks", added);
|
||||
return added;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -317,14 +311,19 @@ Artist::addSomeNonReleaseTracks()
|
||||
bool areTracksAdded{};
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
|
||||
const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)};
|
||||
if (!artist)
|
||||
return areTracksAdded;
|
||||
const Range range {static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize};
|
||||
|
||||
const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize})};
|
||||
Track::FindParameters params;
|
||||
params.setClusters(_filters->getClusterIds());
|
||||
params.setArtist(_artistId);
|
||||
params.setRange(range);
|
||||
params.setSortMethod(TrackSortMethod::Name);
|
||||
params.setNonRelease(true);
|
||||
|
||||
const auto tracks {Track::find(LmsApp->getDbSession(), params)};
|
||||
bool moreResults {tracks.moreResults};
|
||||
|
||||
for (const Track::pointer& track : tracks.results)
|
||||
for (const TrackId trackId : tracks.results)
|
||||
{
|
||||
if (_trackContainer->getCount() == _tracksMaxCount)
|
||||
{
|
||||
@@ -332,6 +331,7 @@ Artist::addSomeNonReleaseTracks()
|
||||
break;
|
||||
}
|
||||
|
||||
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
||||
_trackContainer->add(TrackListHelpers::createEntry(track, tracksAction));
|
||||
|
||||
areTracksAdded = true;
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace UserInterface
|
||||
void refreshView();
|
||||
bool refreshReleases();
|
||||
bool refreshAppearsOnReleases();
|
||||
bool refreshNonReleaseTracks(const Database::ObjectPtr<Database::Artist>& artist);
|
||||
bool refreshNonReleaseTracks();
|
||||
void refreshSimilarArtists(const std::vector<Database::ArtistId>& similarArtistsId);
|
||||
void refreshLinks(const Database::ObjectPtr<Database::Artist>& artist);
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ Explore::search(const Wt::WString& searchText)
|
||||
|
||||
static
|
||||
std::vector<Database::TrackId>
|
||||
getArtistsTracks(Database::Session& session, const std::vector<Database::ArtistId>& artistsId, const std::vector<Database::ClusterId>&)
|
||||
getArtistsTracks(Database::Session& session, const std::vector<Database::ArtistId>& artistsId, const std::vector<Database::ClusterId>& clusters)
|
||||
{
|
||||
std::vector<Database::TrackId> res;
|
||||
|
||||
@@ -138,15 +138,10 @@ getArtistsTracks(Database::Session& session, const std::vector<Database::ArtistI
|
||||
|
||||
for (const Database::ArtistId artistId : artistsId)
|
||||
{
|
||||
const Database::Artist::pointer artist {Database::Artist::find(session, artistId)};
|
||||
if (!artist)
|
||||
continue;
|
||||
const auto tracks {Database::Track::find(LmsApp->getDbSession(), Database::Track::FindParameters {}.setArtist(artistId).setClusters(clusters).setSortMethod(Database::TrackSortMethod::DateDescAndRelease))};
|
||||
|
||||
// TODO handle clusters here
|
||||
const std::vector<Database::Track::pointer> tracks {artist->getTracks()};
|
||||
|
||||
res.reserve(res.size() + tracks.size());
|
||||
std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const Database::Track::pointer& track) { return track->getId(); });
|
||||
res.reserve(res.size() + tracks.results.size());
|
||||
res.insert(std::end(res), std::cbegin(tracks.results), std::cend(tracks.results));
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
@@ -145,6 +145,9 @@ static
|
||||
std::unique_ptr<Zip::Zipper>
|
||||
createZipper(const std::vector<Database::Track::pointer>& tracks)
|
||||
{
|
||||
if (tracks.empty())
|
||||
return {};
|
||||
|
||||
std::map<std::string, std::filesystem::path> files;
|
||||
|
||||
for (const Database::Track::pointer& track : tracks)
|
||||
@@ -185,14 +188,14 @@ DownloadArtistResource::createZipper()
|
||||
{
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
|
||||
const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)};
|
||||
if (!artist)
|
||||
{
|
||||
LOG(DEBUG) << "Cannot find artist";
|
||||
return {};
|
||||
}
|
||||
const auto trackResults {Database::Track::find(LmsApp->getDbSession(), Database::Track::FindParameters {}.setArtist(_artistId).setSortMethod(Database::TrackSortMethod::DateDescAndRelease))};
|
||||
std::vector<Database::Track::pointer> tracks;
|
||||
tracks.reserve(trackResults.results.size());
|
||||
|
||||
return UserInterface::createZipper(artist->getTracks());
|
||||
for (const Database::TrackId trackId : trackResults.results)
|
||||
tracks.push_back(Database::Track::find(LmsApp->getDbSession(), trackId));
|
||||
|
||||
return UserInterface::createZipper(tracks);
|
||||
}
|
||||
|
||||
DownloadReleaseResource::DownloadReleaseResource(Database::ReleaseId releaseId)
|
||||
|
||||
Reference in New Issue
Block a user