When an artist is referred to using several names, pick first the one set in the artist info file, and fallback on the most recent release, ref #731

This commit is contained in:
emeric
2025-11-14 21:32:09 +01:00
parent adf80aa601
commit df344448c3
12 changed files with 256 additions and 53 deletions
+25
View File
@@ -334,6 +334,31 @@ AND NOT EXISTS (
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT 1 FROM artist").where("id = ?").bind(id)) == 1;
}
RangeResults<Artist::pointer> Artist::findWithMBIDNameVariants(Session& session, ArtistId& lastRetrievedArtist, std::optional<Range> range)
{
session.checkReadTransaction();
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Artist>>(R"(
SELECT a FROM artist a
WHERE a.id IN (
SELECT t_a_l.artist_id
FROM track_artist_link t_a_l
WHERE t_a_l.artist_mbid_matched = 1
GROUP BY t_a_l.artist_id
HAVING COUNT(DISTINCT t_a_l.artist_name) > 1
)
AND a.id > ?
)")
.bind(lastRetrievedArtist) };
auto results{ utils::execRangeQuery<Artist::pointer>(query, range) };
if (!results.results.empty())
lastRetrievedArtist = results.results.back()->getId();
return results;
}
void Artist::updatePreferredArtwork(Session& session, ArtistId artistId, ArtworkId artworkId)
{
session.checkWriteTransaction();
+4
View File
@@ -162,6 +162,7 @@ namespace lms::db
query.where("t.track_number = ?").bind(*params.trackNumber);
if (params.sortMethod == TrackSortMethod::DateDescAndRelease
|| params.sortMethod == TrackSortMethod::OriginalDateDescAndRelease
|| params.sortMethod == TrackSortMethod::Release)
{
query.join("medium m ON t.medium_id = m.id");
@@ -223,6 +224,9 @@ namespace lms::db
case TrackSortMethod::DateDescAndRelease:
query.orderBy("t.date DESC,t.release_id,m.position,t.track_number");
break;
case TrackSortMethod::OriginalDateDescAndRelease:
query.orderBy("COALESCE(t.original_date, t.date) DESC,t.release_id,m.position,t.track_number");
break;
case TrackSortMethod::Release:
query.orderBy("m.position,t.track_number");
break;
@@ -50,10 +50,22 @@ namespace lms::db
if (params.artist.isValid())
query.where("t_a_l.artist_id = ?").bind(params.artist);
if (params.release.isValid())
if (params.release.isValid()
|| params.sortMethod == TrackArtistLinkSortMethod::OriginalDateDesc)
{
query.join("track t ON t.id = t_a_l.track_id");
query.where("t.release_id = ?").bind(params.release);
if (params.release.isValid())
query.where("t.release_id = ?").bind(params.release);
}
switch (params.sortMethod)
{
case TrackArtistLinkSortMethod::None:
break;
case TrackArtistLinkSortMethod::OriginalDateDesc:
query.orderBy("COALESCE(t.original_date, t.date) DESC");
break;
}
return query;
@@ -112,11 +124,8 @@ namespace lms::db
void TrackArtistLink::find(Session& session, const FindParameters& parameters, const std::function<void(const TrackArtistLink::pointer&)>& func)
{
const auto query{ createQuery(session, parameters) };
utils::forEachQueryResult(query, [&](const TrackArtistLink::pointer& link) {
func(link);
});
auto query{ createQuery(session, parameters) };
utils::forEachQueryRangeResult(query, parameters.range, func);
}
core::EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session, ArtistId artistId)
@@ -193,6 +193,12 @@ namespace lms::db
Name,
};
enum class TrackArtistLinkSortMethod
{
None,
OriginalDateDesc,
};
enum class TrackEmbeddedImageSortMethod
{
None,
@@ -220,6 +226,7 @@ namespace lms::db
AbsoluteFilePath,
Name,
DateDescAndRelease,
OriginalDateDescAndRelease,
Release, // order by disc/track number
TrackList, // order by asc order in tracklist
TrackNumber,
@@ -136,6 +136,7 @@ namespace lms::db
static RangeResults<ArtistId> findIds(Session& session, const FindParameters& params);
static RangeResults<ArtistId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt); // No track related
static bool exists(Session& session, ArtistId id);
static RangeResults<pointer> findWithMBIDNameVariants(Session& session, ArtistId& lastRetrievedArtist, std::optional<Range> range = std::nullopt);
// Updates
static void updatePreferredArtwork(Session& session, ArtistId artistId, ArtworkId artworkId);
@@ -51,6 +51,7 @@ namespace lms::db
ArtistId artist; // if set, links involved with this artist
ReleaseId release; // if set, artists involved in this release
TrackId track; // if set, artists involved in this track
TrackArtistLinkSortMethod sortMethod{ TrackArtistLinkSortMethod::None };
FindParameters& setRange(std::optional<Range> _range)
{
@@ -77,6 +78,11 @@ namespace lms::db
track = _track;
return *this;
}
FindParameters& setSortMethod(TrackArtistLinkSortMethod _method)
{
sortMethod = _method;
return *this;
}
};
TrackArtistLink() = default;
+39
View File
@@ -934,4 +934,43 @@ namespace lms::db::tests
EXPECT_EQ(artist->getPreferredArtwork(), Artwork::pointer{});
}
}
TEST_F(DatabaseFixture, Artist_findWithMBIDNameVariants)
{
ScopedArtist artistA{ session, "ArtistA" };
ScopedArtist artistB{ session, "ArtistB" };
ScopedTrack trackA1{ session };
ScopedTrack trackA2{ session };
ScopedTrack trackB1{ session };
{
auto transaction{ session.createWriteTransaction() };
{
auto link{ TrackArtistLink::create(session, trackA1.get(), artistA.get(), TrackArtistLinkType::Artist, true) };
link.modify()->setArtistName("ArtistA");
}
{
auto link{ TrackArtistLink::create(session, trackA2.get(), artistA.get(), TrackArtistLinkType::Artist, true) };
link.modify()->setArtistName("AlternateArtistA");
}
{
auto link{ TrackArtistLink::create(session, trackB1.get(), artistB.get(), TrackArtistLinkType::Artist, true) };
link.modify()->setArtistName("ArtistB");
}
}
{
auto transaction{ session.createReadTransaction() };
ArtistId lastRetrievedArtist;
const auto results{ Artist::findWithMBIDNameVariants(session, lastRetrievedArtist) };
ASSERT_EQ(results.results.size(), 1);
EXPECT_EQ(results.results[0]->getId(), artistA.getId());
EXPECT_EQ(lastRetrievedArtist, artistA.getId());
}
}
} // namespace lms::db::tests
+40 -1
View File
@@ -18,8 +18,8 @@
*/
#include "Common.hpp"
#include "database/Types.hpp"
#include "database/Types.hpp"
#include "database/objects/TrackArtistLink.hpp"
namespace lms::db::tests
@@ -183,4 +183,43 @@ namespace lms::db::tests
}
}
}
TEST_F(DatabaseFixture, TrackArtistLink_findWithOriginalDateDesc)
{
ScopedArtist artist{ session, "MyArtist" };
ScopedTrack track1{ session };
ScopedTrack track2{ session };
{
auto transaction{ session.createWriteTransaction() };
{
auto link1{ session.create<TrackArtistLink>(track1.get(), artist.get(), TrackArtistLinkType::Artist, false) };
link1.modify()->setArtistName("MyArtistOldName");
track1.get().modify()->setOriginalDate(core::PartialDateTime{ 1990, 1 });
}
{
auto link2{ session.create<TrackArtistLink>(track2.get(), artist.get(), TrackArtistLinkType::Artist, false) };
link2.modify()->setArtistName("MyArtistNewName");
track2.get().modify()->setOriginalDate(core::PartialDateTime{ 1995, 1 });
}
}
{
auto transaction{ session.createReadTransaction() };
TrackArtistLink::FindParameters params;
params.setSortMethod(TrackArtistLinkSortMethod::OriginalDateDesc);
params.setArtist(artist->getId());
std::vector<TrackArtistLink::pointer> links;
TrackArtistLink::find(session, params, [&](const TrackArtistLink::pointer& link) {
links.push_back(link);
});
ASSERT_EQ(links.size(), 2);
EXPECT_EQ(links[0]->getArtistName(), "MyArtistNewName");
EXPECT_EQ(links[1]->getArtistName(), "MyArtistOldName");
}
}
} // namespace lms::db::tests