Added a dedicated table for release artists + handle display names in UI/Subsonic API, ref #731

This commit is contained in:
emeric
2025-12-24 09:00:59 +01:00
parent 68cc7116a7
commit f70692b680
78 changed files with 1916 additions and 935 deletions
+81 -34
View File
@@ -31,6 +31,7 @@
#include "database/objects/Directory.hpp"
#include "database/objects/MediaLibrary.hpp"
#include "database/objects/Medium.hpp"
#include "database/objects/ReleaseArtistLink.hpp"
#include "database/objects/Track.hpp"
#include "database/objects/TrackArtistLink.hpp"
#include "database/objects/TrackEmbeddedImage.hpp"
@@ -72,7 +73,7 @@ namespace lms::db
|| params.sortMethod == ReleaseSortMethod::OriginalDateDesc
|| params.writtenAfter.isValid()
|| params.dateRange
|| params.artist.isValid()
|| params.trackArtist.isValid()
|| params.filters.clusters.size() == 1
|| params.filters.mediaLibrary.isValid()
|| params.filters.codec.has_value()
@@ -141,13 +142,22 @@ namespace lms::db
.bind(SyncState::PendingRemove);
}
if (params.artist.isValid()
if (params.artist.isValid())
{
assert(!params.trackArtist.isValid());
query.join("release_artist_link r_a_l ON r_a_l.release_id = r.id");
query.where("r_a_l.artist_id = ?").bind(params.artist);
}
if (params.trackArtist.isValid()
|| params.sortMethod == ReleaseSortMethod::ArtistNameThenName)
{
assert(!params.artist.isValid());
query.join("track_artist_link t_a_l ON t_a_l.track_id = t.id");
if (params.artist.isValid())
query.where("t_a_l.artist_id = ?").bind(params.artist);
if (params.trackArtist.isValid())
query.where("t_a_l.artist_id = ?").bind(params.trackArtist);
if (params.sortMethod == ReleaseSortMethod::ArtistNameThenName)
query.join("artist a ON a.id = t_a_l.artist_id");
@@ -168,30 +178,6 @@ namespace lms::db
}
query.where(oss.str());
}
if (!params.excludedTrackArtistLinkTypes.empty())
{
std::ostringstream oss;
oss << "r.id NOT IN (SELECT DISTINCT r.id FROM release r"
" 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 (t_a_l.artist_id = ? AND (";
query.bind(params.artist);
bool first{ true };
for (const TrackArtistLinkType linkType : params.excludedTrackArtistLinkTypes)
{
if (!first)
oss << " OR ";
oss << "t_a_l.type = ?";
query.bind(linkType);
first = false;
}
oss << ")))";
query.where(oss.str());
}
}
if (params.filters.clusters.size() == 1)
@@ -272,7 +258,7 @@ namespace lms::db
}
template<typename ResultType>
Wt::Dbo::Query<ResultType> createArtistQuery(Wt::Dbo::Session& session, std::string_view itemToSelect, ReleaseId releaseId, TrackArtistLinkType linkType)
Wt::Dbo::Query<ResultType> createTrackArtistQuery(Wt::Dbo::Session& session, std::string_view itemToSelect, ReleaseId releaseId, TrackArtistLinkType linkType)
{
auto query{ session.query<ResultType>("SELECT " + std::string{ itemToSelect } + " from artist a")
.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id")
@@ -700,19 +686,50 @@ namespace lms::db
return res;
}
std::vector<Artist::pointer> Release::getArtists(TrackArtistLinkType linkType) const
std::vector<ObjectPtr<Artist>> Release::getArtists() const
{
assert(session());
const auto query{ createArtistQuery<Wt::Dbo::ptr<Artist>>(*session(), "a", getId(), linkType) };
auto query{ session()->query<Wt::Dbo::ptr<Artist>>("SELECT a from artist a") };
query.join("release_artist_link r_a_l ON r_a_l.artist_id = a.id");
query.where("r_a_l.release_id = ?").bind(getId());
query.groupBy("a.id");
return utils::fetchQueryResults<Artist::pointer>(query);
}
std::vector<ArtistId> Release::getArtistIds(TrackArtistLinkType linkType) const
bool Release::hasArtist(ArtistId artistId) const
{
assert(session());
const auto query{ createArtistQuery<ArtistId>(*session(), "a.id", getId(), linkType) };
auto query{ session()->query<int>("SELECT COUNT(1) FROM release_artist_link r_a_l") };
query.where("r_a_l.release_id = ?").bind(getId());
query.where("r_a_l.artist_id = ?").bind(artistId);
return utils::fetchQuerySingleResult(query) > 0;
}
std::vector<Artist::pointer> Release::getTrackArtists(TrackArtistLinkType type) const
{
assert(session());
const auto query{ createTrackArtistQuery<Wt::Dbo::ptr<Artist>>(*session(), "a", getId(), type) };
return utils::fetchQueryResults<Artist::pointer>(query);
}
void Release::visitTrackArtists(TrackArtistLinkType type, std::function<void(const ObjectPtr<Artist>&)> visitor) const
{
assert(session());
const auto query{ createTrackArtistQuery<Wt::Dbo::ptr<Artist>>(*session(), "a", getId(), type) };
return utils::forEachQueryResult(query, visitor);
}
std::vector<ArtistId> Release::getTrackArtistIds(TrackArtistLinkType linkType) const
{
assert(session());
const auto query{ createTrackArtistQuery<ArtistId>(*session(), "a.id", getId(), linkType) };
return utils::fetchQueryResults(query);
}
@@ -765,6 +782,36 @@ namespace lms::db
return utils::fetchQueryResults<Medium::pointer>(query);
}
std::vector<ObjectPtr<ReleaseArtistLink>> Release::getArtistLinks() const
{
return utils::fetchQueryResults<ReleaseArtistLink::pointer>(_releaseArtistLinks.find());
}
void Release::visitArtistLinks(const std::function<void(const ReleaseArtistLink::pointer& artistLink)>& visitor) const
{
return utils::forEachQueryResult(_releaseArtistLinks.find(), visitor);
}
void Release::visitTrackArtistLinks(TrackArtistLinkType linkType, const std::function<void(const ObjectPtr<TrackArtistLink>& artistLink)>& visitor) const
{
auto query{ session()->query<Wt::Dbo::ptr<TrackArtistLink>>("SELECT t_a_l from track_artist_link t_a_l") };
query.join("track t ON t.id = t_a_l.track_id");
query.where("t.release_id = ?").bind(getId());
query.where("t_a_l.type = ?").bind(linkType);
utils::forEachQueryResult(query, visitor);
}
void Release::clearArtistLinks()
{
_releaseArtistLinks.clear();
}
void Release::addArtistLink(const ObjectPtr<ReleaseArtistLink>& artistLink)
{
_releaseArtistLinks.insert(getDboPtr(artistLink));
}
void Release::clearLabels()
{
_labels.clear();
@@ -803,7 +850,7 @@ namespace lms::db
bool Release::hasVariousArtists() const
{
// TODO optimize
return getArtists().size() > 1;
return getTrackArtists().size() > 1;
}
std::size_t Release::getTrackCount() const