diff --git a/approot/explore.xml b/approot/explore.xml
index 36eef544..38d64d00 100644
--- a/approot/explore.xml
+++ b/approot/explore.xml
@@ -41,4 +41,16 @@
+
+
+
+ ${type}
+
+
+ ${artist-container data-bs-dismiss="modal"}
+
+
+
+
+
diff --git a/approot/messages.xml b/approot/messages.xml
index 3a640a49..f805b9fa 100644
--- a/approot/messages.xml
+++ b/approot/messages.xml
@@ -136,6 +136,7 @@
Recently added
Recently modified
Recently played
+Release information
Albums
Search
Star
@@ -162,6 +163,10 @@
Composer
Composers
+
+ Conductor
+ Conductors
+
Lyricist
Lyricists
diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml
index b5c778bc..1c32c26b 100644
--- a/approot/messages_fr.xml
+++ b/approot/messages_fr.xml
@@ -136,6 +136,7 @@
Ajoutés récemment
Modifiés récemment
Joués récemment
+Informations sur l'album
Albums
Rechercher
Ajouter aux favoris
@@ -162,6 +163,10 @@
Compositeur
Compositeurs
+
+ Chef d'orchestre
+ Chefs d'orchestre
+
Parolier
Paroliers
diff --git a/approot/messages_it.xml b/approot/messages_it.xml
index 300f3305..0f000814 100644
--- a/approot/messages_it.xml
+++ b/approot/messages_it.xml
@@ -136,6 +136,7 @@
Aggiunti di recente
Riprodotti di recente
+
Album
Ricerca
Aggiungi ai preferiti
@@ -178,6 +179,10 @@
+
+
+
+
diff --git a/approot/messages_zh.xml b/approot/messages_zh.xml
index db2f6714..9472f65a 100644
--- a/approot/messages_zh.xml
+++ b/approot/messages_zh.xml
@@ -136,6 +136,7 @@
最近添加
最近更改
最近播放
+
专辑
搜索
收藏
@@ -178,6 +179,10 @@
+
+
+
+
diff --git a/approot/release.xml b/approot/release.xml
index deb0cca8..16545709 100644
--- a/approot/release.xml
+++ b/approot/release.xml
@@ -28,6 +28,7 @@
${tr:Lms.Explore.musicbrainz-release}
${}
${download class="dropdown-item"}
+ ${release-info class="dropdown-item"}
@@ -84,4 +85,45 @@
+
+
+
+
+
+
+
+ ${artist-table class="d-grid gap-2"}
+ ${
}
+
+
+ ${tr:Lms.Explore.codec}
+
+
+ ${codec}
+
+
+ ${}
+ ${
}
+
+
+ ${tr:Lms.Explore.bitrate}
+
+
+ ${bitrate}
+
+
+ ${}
+
+
+
+
+
+
+
+
diff --git a/approot/tracks.xml b/approot/tracks.xml
index 1f13f5e8..731fb8a2 100644
--- a/approot/tracks.xml
+++ b/approot/tracks.xml
@@ -80,39 +80,39 @@
${
}
-
+
${tr:Lms.clusters}
-
${}
${artist-table class="d-grid gap-2"}
-
+
${tr:Lms.Explore.duration}
-
${
}
-
+
${tr:Lms.Explore.codec}
-
${}
${
}
-
+
${tr:Lms.Explore.bitrate}
-
@@ -127,15 +127,4 @@
-
-
-
- ${type}
-
-
- ${artist-container data-bs-dismiss="modal"}
-
-
-
-
diff --git a/src/libs/services/database/impl/Artist.cpp b/src/libs/services/database/impl/Artist.cpp
index 5ed14990..b3588fb5 100644
--- a/src/libs/services/database/impl/Artist.cpp
+++ b/src/libs/services/database/impl/Artist.cpp
@@ -97,7 +97,8 @@ createQuery(Session& session, const Artist::FindParameters& params)
if (params.sortMethod == ArtistSortMethod::LastWritten
|| params.writtenAfter.isValid()
|| params.linkType
- || params.track.isValid())
+ || params.track.isValid()
+ || params.release.isValid())
{
query.join("track t ON t.id = t_a_l.track_id");
query.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id");
@@ -161,9 +162,10 @@ createQuery(Session& session, const Artist::FindParameters& params)
}
if (params.track.isValid())
- {
query.where("t.id = ?").bind(params.track);
- }
+
+ if (params.release.isValid())
+ query.where("t.release_id = ?").bind(params.release);
switch (params.sortMethod)
{
diff --git a/src/libs/services/database/include/services/database/Artist.hpp b/src/libs/services/database/include/services/database/Artist.hpp
index 160aa618..dd032a60 100644
--- a/src/libs/services/database/include/services/database/Artist.hpp
+++ b/src/libs/services/database/include/services/database/Artist.hpp
@@ -63,6 +63,7 @@ class Artist : public Object
UserId starringUser; // only artists starred by this user
std::optional scrobbler; // and for this scrobbler
TrackId track; // artists involved in this track
+ ReleaseId release; // artists involved in this release
FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; }
FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; }
@@ -72,6 +73,7 @@ class Artist : public Object
FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; }
FindParameters& setStarringUser(UserId _user, Scrobbler _scrobbler) { starringUser = _user; scrobbler = _scrobbler; return *this; }
FindParameters& setTrack(TrackId _track) { track = _track; return *this; }
+ FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; }
};
Artist() = default;
diff --git a/src/libs/services/database/test/Artist.cpp b/src/libs/services/database/test/Artist.cpp
index 654228dd..f04c2655 100644
--- a/src/libs/services/database/test/Artist.cpp
+++ b/src/libs/services/database/test/Artist.cpp
@@ -372,9 +372,46 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
{
auto transaction {session.createSharedTransaction()};
- const auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist->getId()).setNonRelease(true))};
+ const auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist.getId()).setNonRelease(true))};
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track2.getId());
}
+
+}
+
+TEST_F(DatabaseFixture, Artist_findByRelease)
+{
+ ScopedArtist artist {session, "artist"};
+ ScopedTrack track {session, "MyTrack"};
+ ScopedRelease release{session, "MyRelease"};
+
+ {
+ auto transaction {session.createSharedTransaction()};
+ const auto artists {Artist::find(session, Artist::FindParameters {}.setRelease(release.getId()))};
+ EXPECT_EQ(artists.results.size(), 0);
+ }
+
+ {
+ auto transaction {session.createUniqueTransaction()};
+ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
+ }
+
+ {
+ auto transaction {session.createSharedTransaction()};
+ const auto artists {Artist::find(session, Artist::FindParameters {}.setRelease(release.getId()))};
+ EXPECT_EQ(artists.results.size(), 0);
+ }
+
+ {
+ auto transaction {session.createUniqueTransaction()};
+ track.get().modify()->setRelease(release.get());
+ }
+
+ {
+ auto transaction {session.createSharedTransaction()};
+ const auto artists {Artist::find(session, Artist::FindParameters {}.setRelease(release.getId()))};
+ ASSERT_EQ(artists.results.size(), 1);
+ EXPECT_EQ(artists.results.front(), artist.getId());
+ }
}
diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp
index 021f57cf..760c727b 100644
--- a/src/lms/ui/explore/ArtistsView.cpp
+++ b/src/lms/ui/explore/ArtistsView.cpp
@@ -148,6 +148,7 @@ Artists::refreshArtistLinkTypes()
addTypeIfUsed(TrackArtistLinkType::Artist, "Lms.Explore.Artists.linktype-artist");
addTypeIfUsed(TrackArtistLinkType::ReleaseArtist, "Lms.Explore.Artists.linktype-releaseartist");
addTypeIfUsed(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
+ addTypeIfUsed(TrackArtistLinkType::Conductor, "Lms.Explore.Artists.linktype-conductor");
addTypeIfUsed(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
addTypeIfUsed(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
addTypeIfUsed(TrackArtistLinkType::Producer, "Lms.Explore.Artists.linktype-producer");
diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp
index 884f2c4c..257db1f7 100644
--- a/src/lms/ui/explore/ReleaseView.cpp
+++ b/src/lms/ui/explore/ReleaseView.cpp
@@ -23,6 +23,8 @@
#include
#include
+#include "av/IAudioFile.hpp"
+#include "services/database/Artist.hpp"
#include "services/database/Cluster.hpp"
#include "services/database/Release.hpp"
#include "services/database/ScanSettings.hpp"
@@ -41,12 +43,84 @@
#include "LmsApplication.hpp"
#include "LmsApplicationException.hpp"
#include "MediaPlayer.hpp"
+#include "ModalManager.hpp"
#include "Utils.hpp"
using namespace Database;
namespace UserInterface {
+void
+showReleaseInfoModal(Database::ReleaseId releaseId)
+{
+ auto transaction {LmsApp->getDbSession().createSharedTransaction()};
+
+ const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseId)};
+ if (!release)
+ return;
+
+ auto releaseInfo {std::make_unique(Wt::WString::tr("Lms.Explore.Release.template.release-info"))};
+ Wt::WWidget* releaseInfoPtr {releaseInfo.get()};
+ releaseInfo->addFunction("tr", &Wt::WTemplate::Functions::tr);
+
+ Wt::WContainerWidget* artistTable {releaseInfo->bindNew("artist-table")};
+
+ auto addArtists = [&](TrackArtistLinkType linkType, const char* type)
+ {
+ Artist::FindParameters params;
+ params.setRelease(releaseId);
+ params.setLinkType(linkType);
+ const auto artistIds {Artist::find(LmsApp->getDbSession(), params)};
+ if (artistIds.results.empty())
+ return;
+
+ std::unique_ptr artistContainer {Utils::createArtistContainer(artistIds.results)};
+ auto artistsEntry {std::make_unique(Wt::WString::tr("Lms.Explore.template.info.artists"))};
+ artistsEntry->bindString("type", Wt::WString::trn(type, artistContainer->count()));
+ artistsEntry->bindWidget("artist-container", std::move(artistContainer));
+ artistTable->addWidget(std::move(artistsEntry));
+ };
+
+ addArtists(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
+ addArtists(TrackArtistLinkType::Conductor, "Lms.Explore.Artists.linktype-conductor");
+ addArtists(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
+ addArtists(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
+ addArtists(TrackArtistLinkType::Remixer, "Lms.Explore.Artists.linktype-remixer");
+ addArtists(TrackArtistLinkType::Producer, "Lms.Explore.Artists.linktype-producer");
+
+ // TODO: save in DB and mean all this
+ for (TrackId trackId : Track::find(LmsApp->getDbSession(), Track::FindParameters {}.setRelease(releaseId).setRange(Range {0, 1})).results)
+ {
+ const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
+ if (!track)
+ continue;
+
+ if (const auto audioFile {Av::parseAudioFile(track->getPath())})
+ {
+ const std::optional audioStream {audioFile->getBestStreamInfo()};
+ if (audioStream)
+ {
+ releaseInfo->setCondition("if-has-codec", true);
+ releaseInfo->bindString("codec", audioStream->codec);
+ if (audioStream->bitrate)
+ {
+ releaseInfo->setCondition("if-has-bitrate", true);
+ releaseInfo->bindString("bitrate", std::to_string(audioStream->bitrate / 1000) + " kbps");
+ break;
+ }
+ }
+ }
+ }
+
+ Wt::WPushButton* okBtn {releaseInfo->bindNew("ok-btn", Wt::WString::tr("Lms.ok"))};
+ okBtn->clicked().connect([=]
+ {
+ LmsApp->getModalManager().dispose(releaseInfoPtr);
+ });
+
+ LmsApp->getModalManager().show(std::move(releaseInfo));
+}
+
Release::Release(Filters& filters, PlayQueueController& playQueueController)
: Template {Wt::WString::tr("Lms.Explore.Release.template")}
, _filters {filters}
@@ -177,6 +251,12 @@ Release::refreshView()
bindNew("download", Wt::WString::tr("Lms.Explore.download"))
->setLink(Wt::WLink {std::make_unique(*releaseId)});
+ bindNew("release-info", Wt::WString::tr("Lms.Explore.release-info"))
+ ->clicked().connect([=]
+ {
+ showReleaseInfoModal(*releaseId);
+ });
+
{
auto isStarred {[=] { return Service::get()->isStarred(LmsApp->getUserId(), *releaseId); }};
diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp
index 26fc40e7..26813267 100644
--- a/src/lms/ui/explore/TrackListHelpers.cpp
+++ b/src/lms/ui/explore/TrackListHelpers.cpp
@@ -69,13 +69,14 @@ namespace UserInterface::TrackListHelpers
return;
std::unique_ptr artistContainer {Utils::createArtistContainer(artistIds.results)};
- auto artistsEntry {std::make_unique(Wt::WString::tr("Lms.Explore.Tracks.template.track-info.artists"))};
+ auto artistsEntry {std::make_unique(Wt::WString::tr("Lms.Explore.template.info.artists"))};
artistsEntry->bindString("type", Wt::WString::trn(type, artistContainer->count()));
artistsEntry->bindWidget("artist-container", std::move(artistContainer));
artistTable->addWidget(std::move(artistsEntry));
};
addArtists(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
+ addArtists(TrackArtistLinkType::Conductor, "Lms.Explore.Artists.linktype-conductor");
addArtists(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
addArtists(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
addArtists(TrackArtistLinkType::Remixer, "Lms.Explore.Artists.linktype-remixer");