Associating an artwork to each medium, ref #699

This commit is contained in:
emeric
2025-07-30 20:12:01 +02:00
parent 0ceaf4cfaa
commit 6bf08b94ae
12 changed files with 485 additions and 22 deletions
+6 -2
View File
@@ -43,7 +43,8 @@ namespace lms::db
query.where("d.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
if (params.artist.isValid()
|| params.release.isValid())
|| params.release.isValid()
|| params.medium.isValid())
{
query.join("track t ON t.directory_id = d.id");
query.groupBy("d.id");
@@ -55,6 +56,9 @@ namespace lms::db
if (params.parentDirectory.isValid())
query.where("d.parent_directory_id = ?").bind(params.parentDirectory);
if (params.medium.isValid())
query.where("t.medium_id = ?").bind(params.medium);
if (params.release.isValid())
query.where("t.release_id = ?").bind(params.release);
@@ -91,7 +95,7 @@ namespace lms::db
case DirectorySortMethod::None:
break;
case DirectorySortMethod::Name:
query.orderBy("name COLLATE NOCASE");
query.orderBy("d.name COLLATE NOCASE");
break;
}
+42
View File
@@ -83,4 +83,46 @@ namespace lms::db
return utils::fetchQuerySingleResult(query);
}
void Medium::find(Session& session, const IdRange<MediumId>& idRange, const std::function<void(const Medium::pointer&)>& func)
{
assert(idRange.isValid());
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Medium>>("SELECT m from medium m").orderBy("m.id").where("m.id BETWEEN ? AND ?").bind(idRange.first).bind(idRange.last) };
utils::forEachQueryResult(query, [&](const Medium::pointer& medium) {
func(medium);
});
}
IdRange<MediumId> Medium::findNextIdRange(Session& session, MediumId lastRetrievedId, std::size_t count)
{
session.checkReadTransaction();
auto query{ session.getDboSession()->query<std::tuple<MediumId, MediumId>>("SELECT MIN(sub.id) AS first_id, MAX(sub.id) AS last_id FROM (SELECT m.id FROM medium m WHERE m.id > ? ORDER BY m.id LIMIT ?) sub") };
query.bind(lastRetrievedId);
query.bind(static_cast<int>(count));
auto res{ utils::fetchQuerySingleResult(query) };
return IdRange<MediumId>{ .first = std::get<0>(res), .last = std::get<1>(res) };
}
RangeResults<MediumId> Medium::findOrphanIds(Session& session, std::optional<Range> range)
{
session.checkReadTransaction();
// select the mediums that have no track
auto query{ session.getDboSession()->query<MediumId>("select m.id from medium m LEFT OUTER JOIN track t ON m.id = t.medium_id WHERE t.id IS NULL") };
return utils::execRangeQuery<MediumId>(query, range);
}
void Medium::updatePreferredArtwork(Session& session, MediumId mediumId, ArtworkId artworkId)
{
session.checkWriteTransaction();
if (artworkId.isValid())
utils::executeCommand(*session.getDboSession(), "UPDATE medium SET preferred_artwork_id = ? WHERE id = ?", artworkId, mediumId);
else
utils::executeCommand(*session.getDboSession(), "UPDATE medium SET preferred_artwork_id = NULL WHERE id = ?", mediumId);
}
} // namespace lms::db
@@ -34,6 +34,7 @@
#include "database/objects/ArtistId.hpp"
#include "database/objects/DirectoryId.hpp"
#include "database/objects/MediaLibraryId.hpp"
#include "database/objects/MediumId.hpp"
#include "database/objects/ReleaseId.hpp"
namespace lms::db
@@ -50,8 +51,9 @@ namespace lms::db
{
std::optional<Range> range;
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords
ArtistId artist; // only directory that involve this artist
ReleaseId release; // only releases that involve this artist
ArtistId artist; // only directoies that involve this artist
MediumId medium; // only directories that involve this medium
ReleaseId release; // only directories that involve this release
core::EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
DirectoryId parentDirectory; // If set, directories that have this parent
bool withNoTrack{}; // If set, directories that do not contain any track
@@ -74,6 +76,11 @@ namespace lms::db
trackArtistLinkTypes = _trackArtistLinkTypes;
return *this;
}
FindParameters& setMedium(MediumId _medium)
{
medium = _medium;
return *this;
}
FindParameters& setRelease(ReleaseId _release)
{
release = _release;
@@ -27,6 +27,7 @@
#include <Wt/Dbo/Field.h>
#include <Wt/Dbo/collection.h>
#include "database/IdRange.hpp"
#include "database/Object.hpp"
#include "database/Types.hpp"
#include "database/objects/ArtworkId.hpp"
@@ -74,7 +75,12 @@ namespace lms::db
static std::size_t getCount(Session& session);
static pointer find(Session& session, MediumId id);
static pointer find(Session& session, ReleaseId id, std::optional<std::size_t> position);
static void find(Session& session, const FindParameters& params, std::function<void(const pointer&)> func);
static void find(Session& session, const IdRange<MediumId>& idRange, const std::function<void(const Medium::pointer&)>& func);
static IdRange<MediumId> findNextIdRange(Session& session, MediumId lastRetrievedId, std::size_t count);
static RangeResults<MediumId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt);
// Updates
static void updatePreferredArtwork(Session& session, MediumId mediumId, ArtworkId artworkId);
// getters
std::string_view getName() const { return _name; }
+55
View File
@@ -20,10 +20,12 @@
#include "Common.hpp"
#include "database/objects/Directory.hpp"
#include "database/objects/Medium.hpp"
namespace lms::db::tests
{
using ScopedDirectory = ScopedEntity<db::Directory>;
using ScopedMedium = ScopedEntity<db::Medium>;
TEST_F(DatabaseFixture, Directory)
{
@@ -285,4 +287,57 @@ namespace lms::db::tests
EXPECT_EQ(res.size(), 0);
}
}
TEST_F(DatabaseFixture, Directory_findByMedium)
{
ScopedDirectory dir1{ session, "/root" };
ScopedDirectory dir2{ session, "/root" };
ScopedTrack track1{ session };
ScopedTrack track2{ session };
ScopedRelease release1{ session, "Release1" };
ScopedMedium medium1{ session, release1.lockAndGet() };
ScopedRelease release2{ session, "Release2" };
ScopedMedium medium2{ session, release1.lockAndGet() };
{
auto transaction{ session.createReadTransaction() };
Directory::FindParameters params;
params.setMedium(medium1.getId());
bool visited{};
Directory::find(session, params, [&](const Directory::pointer&) {
visited = true;
});
EXPECT_FALSE(visited);
}
{
auto transaction{ session.createWriteTransaction() };
track1.get().modify()->setMedium(medium1.get());
track1.get().modify()->setRelease(release1.get());
track1.get().modify()->setDirectory(dir1.get());
track2.get().modify()->setMedium(medium2.get());
track2.get().modify()->setRelease(release2.get());
track2.get().modify()->setDirectory(dir2.get());
}
{
auto transaction{ session.createReadTransaction() };
Directory::FindParameters params;
params.setMedium(medium1.getId());
std::vector<DirectoryId> visitedDirectories;
Directory::find(session, params, [&](const Directory::pointer& dir) {
visitedDirectories.push_back(dir->getId());
});
ASSERT_EQ(visitedDirectories.size(), 1);
EXPECT_EQ(visitedDirectories[0], dir1.getId());
}
}
} // namespace lms::db::tests