More permissive grouping of multi disc albums (each disc can now be split on sub directories) when musicbrainz ids are not set, fixes #481

This commit is contained in:
emeric
2024-09-08 11:56:27 +02:00
parent 4a70d9c24f
commit 95a5b58f48
7 changed files with 100 additions and 80 deletions
+14 -8
View File
@@ -41,6 +41,9 @@ namespace lms::db
template<typename ResultType>
Wt::Dbo::Query<ResultType> createQuery(Session& session, std::string_view itemToSelect, const Release::FindParameters& params)
{
assert(params.keywords.empty() || params.name.empty());
assert(!params.directory.isValid() || !params.parentDirectory.isValid());
auto query{ session.getDboSession()->query<ResultType>("SELECT " + std::string{ itemToSelect } + " from release r") };
if (params.sortMethod == ReleaseSortMethod::ArtistNameThenName
@@ -54,11 +57,18 @@ namespace lms::db
|| params.artist.isValid()
|| params.clusters.size() == 1
|| params.mediaLibrary.isValid()
|| params.directory.isValid())
|| params.directory.isValid()
|| params.parentDirectory.isValid())
{
query.join("track t ON t.release_id = r.id");
}
if (params.parentDirectory.isValid())
{
query.join("directory d ON t.directory_id = d.id");
query.where("d.parent_directory_id = ?").bind(params.parentDirectory);
}
if (params.mediaLibrary.isValid())
query.where("t.media_library_id = ?").bind(params.mediaLibrary);
@@ -82,6 +92,9 @@ namespace lms::db
query.where("COALESCE(CAST(SUBSTR(t.date, 1, 4) AS INTEGER), t.year) <= ?").bind(params.dateRange->end);
}
if (!params.name.empty())
query.where("r.name = ?").bind(params.name);
for (std::string_view keyword : params.keywords)
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
@@ -287,13 +300,6 @@ namespace lms::db
return session.getDboSession()->add(std::unique_ptr<Release>{ new Release{ name, MBID } });
}
std::vector<Release::pointer> Release::find(Session& session, const std::string& name, const std::filesystem::path& releaseDirectory)
{
session.checkReadTransaction();
return utils::fetchQueryResults<Release::pointer>(session.getDboSession()->query<Wt::Dbo::ptr<Release>>("SELECT DISTINCT r from release r").join("track t ON t.release_id = r.id").where("r.name = ?").bind(std::string(name, 0, _maxNameLength)).where("t.absolute_file_path LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind(utils::escapeLikeKeyword(releaseDirectory.string()) + "%"));
}
Release::pointer Release::find(Session& session, const core::UUID& mbid)
{
session.checkReadTransaction();
+11 -9
View File
@@ -189,6 +189,7 @@ namespace lms::db
_session.execute("CREATE INDEX IF NOT EXISTS cluster_type_name_idx ON cluster_type(name)");
_session.execute("CREATE INDEX IF NOT EXISTS directory_id_idx ON directory(id)");
_session.execute("CREATE INDEX IF NOT EXISTS directory_parent_directory_idx ON directory(parent_directory_id)");
_session.execute("CREATE INDEX IF NOT EXISTS directory_path_idx ON directory(absolute_path)");
_session.execute("CREATE INDEX IF NOT EXISTS directory_media_library_idx ON directory(media_library_id)");
@@ -207,34 +208,35 @@ namespace lms::db
_session.execute("CREATE INDEX IF NOT EXISTS listen_track_user_backend_idx ON listen(track_id,user_id,backend)");
_session.execute("CREATE INDEX IF NOT EXISTS listen_user_track_backend_date_time_idx ON listen(user_id,track_id,backend,date_time)");
_session.execute("CREATE INDEX IF NOT EXISTS media_library_id_idx ON media_library(id)");
_session.execute("CREATE INDEX IF NOT EXISTS rated_artist_user_artist_idx ON rated_artist(user_id,artist_id)");
_session.execute("CREATE INDEX IF NOT EXISTS rated_release_user_release_idx ON rated_release(user_id,release_id)");
_session.execute("CREATE INDEX IF NOT EXISTS rated_track_user_track_idx ON rated_track(user_id,track_id)");
_session.execute("CREATE INDEX IF NOT EXISTS release_id_idx ON release(id)");
_session.execute("CREATE INDEX IF NOT EXISTS release_mbid_idx ON release(mbid)");
_session.execute("CREATE INDEX IF NOT EXISTS release_name_idx ON release(name)");
_session.execute("CREATE INDEX IF NOT EXISTS release_name_nocase_idx ON release(name COLLATE NOCASE)");
_session.execute("CREATE INDEX IF NOT EXISTS release_mbid_idx ON release(mbid)");
_session.execute("CREATE INDEX IF NOT EXISTS release_type_name_idx ON release_type(name)");
_session.execute("CREATE INDEX IF NOT EXISTS track_id_idx ON track(id)");
_session.execute("CREATE INDEX IF NOT EXISTS track_absolute_path_idx ON track(absolute_file_path)");
_session.execute("CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)");
_session.execute("CREATE INDEX IF NOT EXISTS track_directory_release_idx ON track(directory_id, release_id);");
_session.execute("CREATE INDEX IF NOT EXISTS track_file_last_write_idx ON track(file_last_write)");
_session.execute("CREATE INDEX IF NOT EXISTS track_media_library_idx ON track(media_library_id)");
_session.execute("CREATE INDEX IF NOT EXISTS track_media_library_release_idx ON track(media_library_id, release_id)");
_session.execute("CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)");
_session.execute("CREATE INDEX IF NOT EXISTS track_name_idx ON track(name)");
_session.execute("CREATE INDEX IF NOT EXISTS track_name_nocase_idx ON track(name COLLATE NOCASE)");
_session.execute("CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)");
_session.execute("CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)");
_session.execute("CREATE INDEX IF NOT EXISTS track_original_year_idx ON track(original_year)");
_session.execute("CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)");
_session.execute("CREATE INDEX IF NOT EXISTS track_release_idx ON track(release_id)");
_session.execute("CREATE INDEX IF NOT EXISTS track_release_file_last_write_idx ON track(release_id, file_last_write)");
_session.execute("CREATE INDEX IF NOT EXISTS track_release_year_idx ON track(release_id, year)");
_session.execute("CREATE INDEX IF NOT EXISTS track_file_last_write_idx ON track(file_last_write)");
_session.execute("CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)");
_session.execute("CREATE INDEX IF NOT EXISTS track_year_idx ON track(year)");
_session.execute("CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)");
_session.execute("CREATE INDEX IF NOT EXISTS track_original_year_idx ON track(original_year)");
_session.execute("CREATE INDEX IF NOT EXISTS track_media_library_idx ON track(media_library_id)");
_session.execute("CREATE INDEX IF NOT EXISTS track_media_library_release_idx ON track(media_library_id, release_id)");
_session.execute("CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)");
_session.execute("CREATE INDEX IF NOT EXISTS tracklist_user_idx ON tracklist(user_id)");
@@ -109,6 +109,7 @@ namespace lms::db
const std::filesystem::path& getAbsolutePath() const { return _absolutePath; }
std::string_view getName() const { return _name; }
ObjectPtr<Directory> getParentDirectory() const { return _parent; }
DirectoryId getParentDirectoryId() const { return _parent.id(); }
ObjectPtr<MediaLibrary> getMediaLibrary() const { return _mediaLibrary; }
// setters
@@ -22,6 +22,7 @@
#include <Wt/Dbo/ptr.h>
#include <cassert>
#include <functional>
#include <string>
namespace lms::db
{
@@ -32,7 +33,7 @@ namespace lms::db
IdType() = default;
IdType(ValueType id)
: _id{ id } { assert(isValid()); }
: _id{ id } { }
bool isValid() const { return _id != Wt::Dbo::dbo_default_traits::invalidId(); }
std::string toString() const
+14 -3
View File
@@ -114,7 +114,8 @@ namespace lms::db
struct FindParameters
{
std::vector<ClusterId> clusters; // if non empty, releases that belong to these clusters
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords (cannot be set with keywords)
std::string name; // must match this name (cannot be set with keywords)
ReleaseSortMethod sortMethod{ ReleaseSortMethod::None };
std::optional<Range> range;
Wt::WDateTime writtenAfter;
@@ -126,7 +127,8 @@ namespace lms::db
core::EnumSet<TrackArtistLinkType> excludedTrackArtistLinkTypes; // but not for these link types
std::string releaseType; // If set, albums that has this release type
MediaLibraryId mediaLibrary; // If set, releases that has at least a track in this library
DirectoryId directory; // if set, tracks in this directory
DirectoryId directory; // if set, releases in this directory (cannot be set with parent directory)
DirectoryId parentDirectory; // if set, releases in this parent directory (cannot be set with directory)
FindParameters& setClusters(std::span<const ClusterId> _clusters)
{
@@ -138,6 +140,11 @@ namespace lms::db
keywords = _keywords;
return *this;
}
FindParameters& setName(std::string_view _name)
{
name = _name;
return *this;
}
FindParameters& setSortMethod(ReleaseSortMethod _sortMethod)
{
sortMethod = _sortMethod;
@@ -186,6 +193,11 @@ namespace lms::db
directory = _directory;
return *this;
}
FindParameters& setParentDirectory(DirectoryId _parentDirectory)
{
parentDirectory = _parentDirectory;
return *this;
}
};
Release() = default;
@@ -194,7 +206,6 @@ namespace lms::db
static std::size_t getCount(Session& session);
static bool exists(Session& session, ReleaseId id);
static pointer find(Session& session, const core::UUID& MBID);
static std::vector<pointer> find(Session& session, const std::string& name, const std::filesystem::path& releaseDirectory);
static pointer find(Session& session, ReleaseId id);
static void find(Session& session, ReleaseId& lastRetrievedRelease, std::size_t count, const std::function<void(const Release::pointer&)>& func, MediaLibraryId library = {});
static RangeResults<pointer> find(Session& session, const FindParameters& parameters);
-33
View File
@@ -252,39 +252,6 @@ namespace lms::db::tests
}
}
TEST_F(DatabaseFixture, Release_findByNameAndPath)
{
ScopedRelease release1{ session, "MyRelease" };
ScopedRelease release2{ session, "MyRelease" };
ScopedTrack track1{ session };
ScopedTrack track2{ session };
{
auto transaction{ session.createWriteTransaction() };
track1.get().modify()->setRelease(release1.get());
track1.get().modify()->setAbsoluteFilePath("/tmp/foo/foo.mp3");
track2.get().modify()->setRelease(release2.get());
track2.get().modify()->setAbsoluteFilePath("/tmp/bar/bar.mp3");
}
{
auto transaction{ session.createReadTransaction() };
{
const auto releases{ Release::find(session, "MyRelease", "/tmp/foo") };
ASSERT_EQ(releases.size(), 1);
EXPECT_EQ(releases.front()->getId(), release1.getId());
}
{
const auto releases{ Release::find(session, "MyRelease", "/tmp/bar") };
ASSERT_EQ(releases.size(), 1);
EXPECT_EQ(releases.front()->getId(), release2.getId());
}
}
}
TEST_F(DatabaseFixture, MulitpleReleaseSearchByName)
{
ScopedRelease release1{ session, "MyRelease" };