diff --git a/approot/artist.xml b/approot/artist.xml index 9fe64bf9..ada0f662 100644 --- a/approot/artist.xml +++ b/approot/artist.xml @@ -23,12 +23,10 @@ ${} - ${releases class="row gy-4 mb-5"} + ${releases} ${} ${} -
- ${tracks} -
+ ${tracks} ${
} ${}
diff --git a/approot/release.xml b/approot/release.xml index dbe29c90..b26ab715 100644 --- a/approot/release.xml +++ b/approot/release.xml @@ -42,7 +42,7 @@
${}

${tr:Lms.Explore.Release.similar-releases}

- ${similar-releases class="row gy-4"} + ${similar-releases class="row row-cols-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 gx-2 gy-4"} ${
} diff --git a/approot/releases.xml b/approot/releases.xml index ccec58eb..8e93de3b 100644 --- a/approot/releases.xml +++ b/approot/releases.xml @@ -39,17 +39,21 @@ -
- ${cover class="shadow-sm"} +
+
+
+ ${cover class="shadow-sm"} +
+ ${release-name class="d-block text-truncate text-nowrap text-decoration-none link-success"} + ${}${artist-name class="d-block text-truncate text-nowrap text-decoration-none link-secondary"}${} + ${}
${tr:Lms.Explore.various-artists}
${
} + ${}
${year}
${
} +
- ${release-name class="d-block text-truncate text-nowrap text-decoration-none link-success"} - ${}${artist-name class="d-block text-truncate text-nowrap text-decoration-none link-secondary"}${} - ${}
${tr:Lms.Explore.various-artists}
${
} - ${}
${year}
${
} - ${elements class="row gy-4"} + ${elements class="row row-cols-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 gx-2 gy-4"} ${loading-indicator} diff --git a/approot/settings.xml b/approot/settings.xml index c17ef13a..83585400 100644 --- a/approot/settings.xml +++ b/approot/settings.xml @@ -9,7 +9,7 @@
${tr:Lms.Settings.audio-settings-are-local}
-
+
@@ -18,7 +18,7 @@ ${transcode-mode-info}
-
+
@@ -27,7 +27,7 @@ ${transcode-format-info}
-
+
@@ -39,7 +39,7 @@ ${transcode-bitrate-info}
-
+
@@ -48,7 +48,7 @@ ${replaygain-mode-info}
-
+
@@ -60,7 +60,7 @@ ${replaygain-preamp-info}
-
+
@@ -117,7 +117,7 @@
${} ${tr:Lms.Settings.scrobbling} -
+
@@ -126,7 +126,7 @@ ${scrobbler-info class="help-block"}
-
+
@@ -148,7 +148,7 @@
${} -
+
@@ -157,7 +157,7 @@ ${password-info class="help-block"}
-
+
diff --git a/src/libs/services/database/impl/Artist.cpp b/src/libs/services/database/impl/Artist.cpp index 6a7428a0..575cb675 100644 --- a/src/libs/services/database/impl/Artist.cpp +++ b/src/libs/services/database/impl/Artist.cpp @@ -204,8 +204,8 @@ Artist::find(Session& session, const FindParameters& params) return execQuery(query, params.range); } -std::vector -Artist::getReleases(const std::vector& clusterIds) const +RangeResults +Artist::getReleases(Range range, const std::vector& clusterIds) const { assert(session()); @@ -213,7 +213,7 @@ Artist::getReleases(const std::vector& clusterIds) const std::ostringstream oss; - oss << "SELECT DISTINCT r 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"; + 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()) { @@ -236,13 +236,12 @@ Artist::getReleases(const std::vector& clusterIds) const oss << " ORDER BY t.date DESC, r.name COLLATE NOCASE"; - auto query {session()->query>(oss.str())}; + auto query {session()->query(oss.str())}; for (const std::string& bindArg : where.getBindArgs()) query.bind(bindArg); - auto res {query.resultList()}; - return std::vector(res.begin(), res.end()); + return execQuery(query, range); } std::size_t diff --git a/src/libs/services/database/include/services/database/Artist.hpp b/src/libs/services/database/include/services/database/Artist.hpp index ac94ab3d..8fd4b1a9 100644 --- a/src/libs/services/database/include/services/database/Artist.hpp +++ b/src/libs/services/database/include/services/database/Artist.hpp @@ -30,6 +30,7 @@ #include "services/database/ArtistId.hpp" #include "services/database/ClusterId.hpp" #include "services/database/Object.hpp" +#include "services/database/ReleaseId.hpp" #include "services/database/Types.hpp" #include "services/database/UserId.hpp" #include "utils/EnumSet.hpp" @@ -88,7 +89,7 @@ class Artist : public Object const std::string& getSortName() const { return _sortName; } std::optional getMBID() const { return UUID::fromString(_MBID); } - std::vector> getReleases(const std::vector& clusterIds = {}) const; // if non empty, get the releases that match all these clusters + RangeResults getReleases(Range range, const std::vector& clusterIds = {}) const; // if non empty, get the releases that match all these clusters std::size_t getReleaseCount() const; std::vector> getTracks(std::optional linkType = {}) const; bool hasNonReleaseTracks(std::optional linkType = std::nullopt) const; diff --git a/src/libs/services/database/test/Cluster.cpp b/src/libs/services/database/test/Cluster.cpp index df6c677d..76ff7be0 100644 --- a/src/libs/services/database/test/Cluster.cpp +++ b/src/libs/services/database/test/Cluster.cpp @@ -574,13 +574,13 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); - auto releases {artist->getReleases()}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + auto releases {artist->getReleases(Range {})}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); - releases = artist->getReleases({cluster.getId()}); - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + releases = artist->getReleases(Range {}, {cluster.getId()}); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); } } @@ -605,13 +605,13 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters) { auto transaction {session.createSharedTransaction()}; - auto releases {artist->getReleases()}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + auto releases {artist->getReleases(Range {})}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); - releases = artist->getReleases({cluster1.getId(), cluster2.getId()}); - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + releases = artist->getReleases(Range {}, {cluster1.getId(), cluster2.getId()}); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); } } diff --git a/src/libs/services/database/test/DatabaseTest.cpp b/src/libs/services/database/test/DatabaseTest.cpp index 918d519a..adf53728 100644 --- a/src/libs/services/database/test/DatabaseTest.cpp +++ b/src/libs/services/database/test/DatabaseTest.cpp @@ -50,8 +50,9 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease) auto transaction {session.createSharedTransaction()}; EXPECT_EQ(artist->getReleaseCount(), 1); - ASSERT_EQ(artist->getReleases().size(), 1); - EXPECT_EQ(artist->getReleases().front()->getId(), release.getId()); + auto releases {artist->getReleases(Range {})}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); EXPECT_EQ(release->getTracks().size(), nbTracks); } @@ -73,9 +74,9 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist) { auto transaction {session.createUniqueTransaction()}; - auto releases {artist->getReleases()}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release.getId()); + auto releases {artist->getReleases(Range {})}; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); EXPECT_EQ(artist->getReleaseCount(), 1); diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 0f4f627c..df5228e5 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -885,9 +885,12 @@ handleGetArtistRequest(RequestContext& context) Response response {Response::createOkResponse(context.serverProtocolVersion)}; Response::Node artistNode {artistToResponseNode(user, artist, true /* id3 */)}; - auto releases {artist->getReleases()}; - for (const Release::pointer& release : releases) + auto releases {artist->getReleases(Range {})}; + for (const ReleaseId releaseId : releases.results) + { + const Release::pointer release {Release::find(context.dbSession, releaseId)}; artistNode.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, true /* id3 */)); + } response.addNode("artist", std::move(artistNode)); @@ -997,9 +1000,12 @@ handleGetMusicDirectoryRequest(RequestContext& context) directoryNode.setAttribute("name", makeNameFilesystemCompatible(artist->getName())); - auto releases {artist->getReleases()}; - for (const Release::pointer& release : releases) + auto releases {artist->getReleases(Range {})}; + for (const ReleaseId releaseId : releases.results) + { + const Release::pointer release {Release::find(context.dbSession, releaseId)}; directoryNode.addArrayChild("child", releaseToResponseNode(release, context.dbSession, user, false /* no id3 */)); + } } else if (releaseId) { diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 0b541df6..9aef8471 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -330,13 +329,6 @@ LmsApplication::createCluster(Database::Cluster::pointer cluster, bool canDelete return res; } -Wt::WPopupMenu* -LmsApplication::createPopupMenu() -{ - _popupMenu = std::make_unique(); - return _popupMenu.get(); -} - void LmsApplication::handleException(LmsApplicationException& e) { diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 41198fd0..eae65285 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -38,10 +38,6 @@ namespace Database class Session; class User; } -namespace Wt -{ - class WPopupMenu; -} namespace UserInterface { @@ -88,7 +84,6 @@ class LmsApplication : public Wt::WApplication static Wt::WLink createReleaseLink(Database::ObjectPtr release); static std::unique_ptr createReleaseAnchor(Database::ObjectPtr release, bool addText = true); static std::unique_ptr createCluster(Database::ObjectPtr cluster, bool canDelete = false); - Wt::WPopupMenu* createPopupMenu(); MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; } PlayQueue& getPlayQueue() const { return *_playQueue; } @@ -125,7 +120,6 @@ class LmsApplication : public Wt::WApplication std::shared_ptr _coverResource; MediaPlayer* _mediaPlayer {}; PlayQueue* _playQueue {}; - std::unique_ptr _popupMenu; NotificationContainer* _notificationContainer {}; ModalManager* _modalManager {}; }; diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index d421f811..ab1f9c65 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -489,7 +489,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) Wt::WText* moreBtn {entry->bindNew("more-btn", Wt::WString::tr("Lms.PlayQueue.template.more-btn"), Wt::TextFormat::XHTML)}; moreBtn->clicked().connect([=] { - Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; +/* Wt::WPopupMenu* popup {LmsApp->createPopupMenu()}; const bool isStarred {Service::get()->isStarred(LmsApp->getUserId(), trackId)}; popup->addItem(Wt::WString::tr(isStarred ? "Lms.Explore.unstar" : "Lms.Explore.star")) @@ -504,6 +504,7 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry) ->setLink(Wt::WLink {std::make_unique(trackId)}); popup->popup(moreBtn); + */ }); } diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 0d817647..e98d0254 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -180,17 +180,16 @@ Artist::refreshView() void Artist::refreshReleases(const ObjectPtr& artist) { - const auto releases {artist->getReleases(_filters->getClusterIds())}; - if (releases.empty()) + if (artist->getReleaseCount() == 0) return; setCondition("if-has-release", true); - - Wt::WContainerWidget* releasesContainer = bindNew("releases"); - for (const auto& release : releases) + _releaseContainer = bindNew("releases", Wt::WString::tr("Lms.Explore.Releases.template.container")); + _releaseContainer->onRequestElements.connect(this, [this] { - releasesContainer->addWidget(ReleaseListHelpers::createEntryForArtist(release, artist)); - } + addSomeReleases(); + }); + addSomeReleases(); } void @@ -239,6 +238,27 @@ Artist::refreshLinks(const Database::Artist::pointer& artist) } } +void +Artist::addSomeReleases() +{ + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + + const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)}; + if (!artist) + return; + + const Range range {static_cast(_releaseContainer->getCount()), _releasesBatchSize}; + const auto releases {artist->getReleases(range, _filters->getClusterIds())}; + + for (const ReleaseId releaseId : releases.results) + { + const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseId)}; + _releaseContainer->add(ReleaseListHelpers::createEntryForArtist(release, artist)); + } + + _releaseContainer->setHasMore(releases.moreResults); +} + void Artist::addSomeNonReleaseTracks() { diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index e6e2933e..9b7dabb5 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -51,11 +51,14 @@ namespace UserInterface void refreshSimilarArtists(const std::vector& similarArtistsId); void refreshLinks(const Database::ObjectPtr& artist); + void addSomeReleases(); void addSomeNonReleaseTracks(); + static constexpr std::size_t _releasesBatchSize {6}; static constexpr std::size_t _tracksBatchSize {6}; static constexpr std::size_t _tracksMaxCount {160}; Filters* _filters {}; + InfiniteScrollingContainer* _releaseContainer {}; InfiniteScrollingContainer* _trackContainer {}; Database::ArtistId _artistId {}; }; diff --git a/src/lms/ui/explore/ReleaseListHelpers.cpp b/src/lms/ui/explore/ReleaseListHelpers.cpp index 296c4b42..cc5d2f41 100644 --- a/src/lms/ui/explore/ReleaseListHelpers.cpp +++ b/src/lms/ui/explore/ReleaseListHelpers.cpp @@ -83,8 +83,6 @@ namespace UserInterface::ReleaseListHelpers } } - // hack - entry->setStyleClass("p-2 Lms-bg-dark-hover rounded col-6 col-md-4 col-lg-3 col-xl-2"); return entry; }