Added more fallbacks for track artworks
This commit is contained in:
@@ -229,6 +229,7 @@ namespace lms::db
|
|||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_id_idx ON track_embedded_image_link(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_id_idx ON track_embedded_image_link(id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_id_idx ON track_embedded_image_link(track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_id_idx ON track_embedded_image_link(track_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_embedded_image_id_track_id_idx ON track_embedded_image_link(track_embedded_image_id, track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_embedded_image_id_track_id_idx ON track_embedded_image_link(track_embedded_image_id, track_id)");
|
||||||
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_type_track_embedded_image_id_track_id_idx ON track_embedded_image_link(type,track_embedded_image_id,track_id)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_directory_stem_idx ON image(directory_id, stem COLLATE NOCASE)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_directory_stem_idx ON image(directory_id, stem COLLATE NOCASE)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_id_idx ON image(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_id_idx ON image(id)");
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ namespace lms::db
|
|||||||
|
|
||||||
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<TrackEmbeddedImage>>("SELECT t_e_i FROM track_embedded_image t_e_i") };
|
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<TrackEmbeddedImage>>("SELECT t_e_i FROM track_embedded_image t_e_i") };
|
||||||
|
|
||||||
if (params.track.isValid()
|
if (params.artist.isValid()
|
||||||
|
|| params.discNumber.has_value()
|
||||||
|
|| params.track.isValid()
|
||||||
|| params.release.isValid()
|
|| params.release.isValid()
|
||||||
|| params.trackList.isValid()
|
|| params.trackList.isValid()
|
||||||
|| !params.imageTypes.empty()
|
|| !params.imageTypes.empty()
|
||||||
@@ -47,13 +49,36 @@ namespace lms::db
|
|||||||
{
|
{
|
||||||
query.join("track_embedded_image_link t_e_i_l ON t_e_i_l.track_embedded_image_id = t_e_i.id");
|
query.join("track_embedded_image_link t_e_i_l ON t_e_i_l.track_embedded_image_id = t_e_i.id");
|
||||||
|
|
||||||
|
if (params.artist.isValid())
|
||||||
|
{
|
||||||
|
query.join("track_artist_link t_a_l ON t_a_l.track_id = t_e_i_l.track_id");
|
||||||
|
query.where("t_a_l.artist_id = ?").bind(params.artist);
|
||||||
|
|
||||||
|
if (!params.trackArtistLinkTypes.empty())
|
||||||
|
{
|
||||||
|
std::string clause{ "t_a_l.type IN (" };
|
||||||
|
for (const auto& type : params.trackArtistLinkTypes)
|
||||||
|
{
|
||||||
|
if (clause.back() != '(')
|
||||||
|
clause += ",";
|
||||||
|
clause += "?";
|
||||||
|
query.bind(type);
|
||||||
|
}
|
||||||
|
clause += ")";
|
||||||
|
query.where(clause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (params.track.isValid())
|
if (params.track.isValid())
|
||||||
query.where("t_e_i_l.track_id = ?").bind(params.track);
|
query.where("t_e_i_l.track_id = ?").bind(params.track);
|
||||||
|
|
||||||
if (params.release.isValid())
|
if (params.release.isValid() || params.discNumber.has_value())
|
||||||
{
|
{
|
||||||
query.join("track t ON t_e_i_l.track_id = t.id");
|
query.join("track t ON t_e_i_l.track_id = t.id");
|
||||||
query.where("t.release_id = ?").bind(params.release);
|
if (params.release.isValid())
|
||||||
|
query.where("t.release_id = ?").bind(params.release);
|
||||||
|
if (params.discNumber.has_value())
|
||||||
|
query.where("t.disc_number = ?").bind(params.discNumber.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.trackList.isValid())
|
if (params.trackList.isValid())
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
|
|
||||||
#include "core/EnumSet.hpp"
|
#include "core/EnumSet.hpp"
|
||||||
|
#include "database/ArtistId.hpp"
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/ReleaseId.hpp"
|
#include "database/ReleaseId.hpp"
|
||||||
#include "database/TrackEmbeddedImageId.hpp"
|
#include "database/TrackEmbeddedImageId.hpp"
|
||||||
@@ -47,8 +48,11 @@ namespace lms::db
|
|||||||
struct FindParameters
|
struct FindParameters
|
||||||
{
|
{
|
||||||
std::optional<Range> range;
|
std::optional<Range> range;
|
||||||
|
ArtistId artist;
|
||||||
|
core::EnumSet<TrackArtistLinkType> trackArtistLinkTypes;
|
||||||
TrackId track;
|
TrackId track;
|
||||||
ReleaseId release;
|
ReleaseId release;
|
||||||
|
std::optional<int> discNumber;
|
||||||
TrackListId trackList;
|
TrackListId trackList;
|
||||||
core::EnumSet<ImageType> imageTypes;
|
core::EnumSet<ImageType> imageTypes;
|
||||||
TrackEmbeddedImageSortMethod sortMethod{ TrackEmbeddedImageSortMethod::None };
|
TrackEmbeddedImageSortMethod sortMethod{ TrackEmbeddedImageSortMethod::None };
|
||||||
@@ -58,6 +62,13 @@ namespace lms::db
|
|||||||
range = _range;
|
range = _range;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
FindParameters& setArtist(ArtistId _artist, core::EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {})
|
||||||
|
{
|
||||||
|
artist = _artist;
|
||||||
|
trackArtistLinkTypes = _trackArtistLinkTypes;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
FindParameters& setTrack(TrackId _track)
|
FindParameters& setTrack(TrackId _track)
|
||||||
{
|
{
|
||||||
track = _track;
|
track = _track;
|
||||||
@@ -68,6 +79,11 @@ namespace lms::db
|
|||||||
release = _release;
|
release = _release;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
FindParameters& setDiscNumber(std::optional<int> _discNumber)
|
||||||
|
{
|
||||||
|
discNumber = _discNumber;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
FindParameters& setTrackList(TrackListId _trackList)
|
FindParameters& setTrackList(TrackListId _trackList)
|
||||||
{
|
{
|
||||||
trackList = _trackList;
|
trackList = _trackList;
|
||||||
|
|||||||
@@ -271,6 +271,77 @@ namespace lms::db::tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, TrackEmbeddedImage_findByParams_artist)
|
||||||
|
{
|
||||||
|
ScopedTrackEmbeddedImage image{ session };
|
||||||
|
ScopedTrack track{ session };
|
||||||
|
ScopedArtist artist{ session, "MyArtist" };
|
||||||
|
ScopedTrackEmbeddedImageLink link{ session, track.lockAndGet(), image.lockAndGet() };
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createWriteTransaction() };
|
||||||
|
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
|
TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setArtist(artist.getId());
|
||||||
|
|
||||||
|
bool visited{};
|
||||||
|
TrackEmbeddedImage::find(session, params, [&](const TrackEmbeddedImage::pointer&) { visited = true; });
|
||||||
|
ASSERT_TRUE(visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
|
TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setArtist(artist.getId(), { TrackArtistLinkType::ReleaseArtist });
|
||||||
|
|
||||||
|
bool visited{};
|
||||||
|
TrackEmbeddedImage::find(session, params, [&](const TrackEmbeddedImage::pointer&) { visited = true; });
|
||||||
|
ASSERT_FALSE(visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, TrackEmbeddedImage_findByParams_discNumber)
|
||||||
|
{
|
||||||
|
ScopedTrackEmbeddedImage image{ session };
|
||||||
|
ScopedTrack track{ session };
|
||||||
|
ScopedRelease release{ session, "MyRelease" };
|
||||||
|
ScopedTrackEmbeddedImageLink link{ session, track.lockAndGet(), image.lockAndGet() };
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createWriteTransaction() };
|
||||||
|
track.get().modify()->setRelease(release.get());
|
||||||
|
track.get().modify()->setDiscNumber(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
|
TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setDiscNumber(1);
|
||||||
|
|
||||||
|
bool visited{};
|
||||||
|
TrackEmbeddedImage::find(session, params, [&](const TrackEmbeddedImage::pointer&) { visited = true; });
|
||||||
|
ASSERT_TRUE(visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
|
TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setDiscNumber(2);
|
||||||
|
|
||||||
|
bool visited{};
|
||||||
|
TrackEmbeddedImage::find(session, params, [&](const TrackEmbeddedImage::pointer&) { visited = true; });
|
||||||
|
ASSERT_FALSE(visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, Track_findByEmbeddedImage)
|
TEST_F(DatabaseFixture, Track_findByEmbeddedImage)
|
||||||
{
|
{
|
||||||
ScopedTrackEmbeddedImage image{ session };
|
ScopedTrackEmbeddedImage image{ session };
|
||||||
|
|||||||
@@ -38,6 +38,14 @@
|
|||||||
|
|
||||||
namespace lms::cover
|
namespace lms::cover
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool isImageFound(const ArtworkService::ImageFindResult& res)
|
||||||
|
{
|
||||||
|
return !std::holds_alternative<std::monostate>(res);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<IArtworkService> createArtworkService(db::Db& db, const std::filesystem::path& defaultReleaseCoverSvgPath, const std::filesystem::path& defaultArtistImageSvgPath)
|
std::unique_ptr<IArtworkService> createArtworkService(db::Db& db, const std::filesystem::path& defaultReleaseCoverSvgPath, const std::filesystem::path& defaultArtistImageSvgPath)
|
||||||
{
|
{
|
||||||
return std::make_unique<ArtworkService>(db, defaultReleaseCoverSvgPath, defaultArtistImageSvgPath);
|
return std::make_unique<ArtworkService>(db, defaultReleaseCoverSvgPath, defaultArtistImageSvgPath);
|
||||||
@@ -147,6 +155,7 @@ namespace lms::cover
|
|||||||
if (const db::ImageId imageId{ artist->getImageId() }; imageId.isValid())
|
if (const db::ImageId imageId{ artist->getImageId() }; imageId.isValid())
|
||||||
res = imageId;
|
res = imageId;
|
||||||
|
|
||||||
|
// TODO fallback on embedded Band/LeadArtist/Artist?
|
||||||
// TODO fallback on first release?
|
// TODO fallback on first release?
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,30 +164,76 @@ namespace lms::cover
|
|||||||
|
|
||||||
ArtworkService::ImageFindResult ArtworkService::findTrackImage(db::TrackId trackId)
|
ArtworkService::ImageFindResult ArtworkService::findTrackImage(db::TrackId trackId)
|
||||||
{
|
{
|
||||||
db::Session& session{ _db.getTLSSession() };
|
|
||||||
auto transaction{ session.createReadTransaction() };
|
|
||||||
ImageFindResult res;
|
ImageFindResult res;
|
||||||
|
|
||||||
db::TrackEmbeddedImage::FindParameters params;
|
db::Session& session{ _db.getTLSSession() };
|
||||||
params.setTrack(trackId);
|
auto transaction{ session.createReadTransaction() };
|
||||||
params.setSortMethod(db::TrackEmbeddedImageSortMethod::MediaTypeThenFrontTypeThenSizeDescDesc);
|
|
||||||
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
|
||||||
|
|
||||||
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) {
|
|
||||||
res = image->getId();
|
|
||||||
});
|
|
||||||
|
|
||||||
// No embedded image found, fallback on release image
|
|
||||||
if (res.index() == 0)
|
|
||||||
{
|
{
|
||||||
if (const db::Track::pointer track{ db::Track::find(session, trackId) })
|
db::TrackEmbeddedImage::FindParameters params;
|
||||||
{
|
params.setTrack(trackId);
|
||||||
if (const db::Release::pointer release{ track->getRelease() })
|
params.setImageTypes({ db::ImageType::Media });
|
||||||
{
|
params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc);
|
||||||
if (const db::ImageId imageId{ release->getImageId() }; imageId.isValid())
|
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
||||||
res = imageId;
|
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (isImageFound(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// fallback on another track of the same disc
|
||||||
|
const db::Track::pointer track{ db::Track::find(session, trackId) };
|
||||||
|
if (!track)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
const db::ReleaseId releaseId{ track->getReleaseId() };
|
||||||
|
if (!releaseId.isValid())
|
||||||
|
return res;
|
||||||
|
|
||||||
|
{
|
||||||
|
db::TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setRelease(releaseId);
|
||||||
|
params.setDiscNumber(track->getDiscNumber());
|
||||||
|
params.setImageTypes({ db::ImageType::Media });
|
||||||
|
params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc);
|
||||||
|
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
||||||
|
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isImageFound(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// Fallback on front cover
|
||||||
|
{
|
||||||
|
db::TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setTrack(trackId);
|
||||||
|
params.setImageTypes({ db::ImageType::FrontCover });
|
||||||
|
params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc);
|
||||||
|
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
||||||
|
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isImageFound(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// Fallback on whatever front cover found on the release
|
||||||
|
{
|
||||||
|
db::TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setRelease(releaseId);
|
||||||
|
params.setImageTypes({ db::ImageType::FrontCover });
|
||||||
|
params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc);
|
||||||
|
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
||||||
|
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isImageFound(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// Fallback on external cover of the release
|
||||||
|
if (const db::Release::pointer release{ db::Release::find(session, releaseId) })
|
||||||
|
{
|
||||||
|
if (const db::ImageId imageId{ release->getImageId() }; imageId.isValid())
|
||||||
|
res = imageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@@ -190,15 +245,33 @@ namespace lms::cover
|
|||||||
auto transaction{ session.createReadTransaction() };
|
auto transaction{ session.createReadTransaction() };
|
||||||
ImageFindResult res;
|
ImageFindResult res;
|
||||||
|
|
||||||
db::TrackEmbeddedImage::FindParameters params;
|
{
|
||||||
params.setTrack(trackId);
|
db::TrackEmbeddedImage::FindParameters params;
|
||||||
params.setImageTypes({ db::ImageType::Media });
|
params.setTrack(trackId);
|
||||||
params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc);
|
params.setImageTypes({ db::ImageType::Media });
|
||||||
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc);
|
||||||
|
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
||||||
|
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); });
|
||||||
|
}
|
||||||
|
|
||||||
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) {
|
if (isImageFound(res))
|
||||||
res = image->getId();
|
return res;
|
||||||
});
|
|
||||||
|
// fallback on another track of the same disc
|
||||||
|
if (const db::Track::pointer track{ db::Track::find(session, trackId) })
|
||||||
|
{
|
||||||
|
const db::ReleaseId releaseId{ track->getReleaseId() };
|
||||||
|
if (releaseId.isValid())
|
||||||
|
{
|
||||||
|
db::TrackEmbeddedImage::FindParameters params;
|
||||||
|
params.setRelease(releaseId);
|
||||||
|
params.setDiscNumber(track->getDiscNumber());
|
||||||
|
params.setImageTypes({ db::ImageType::Media });
|
||||||
|
params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc);
|
||||||
|
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
||||||
|
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -209,9 +282,7 @@ namespace lms::cover
|
|||||||
auto transaction{ session.createReadTransaction() };
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
ImageFindResult res;
|
ImageFindResult res;
|
||||||
|
if (const db::Release::pointer release{ db::Release::find(session, releaseId) })
|
||||||
const db::Release::pointer release{ db::Release::find(session, releaseId) };
|
|
||||||
if (release)
|
|
||||||
{
|
{
|
||||||
if (const db::ImageId imageId{ release->getImageId() }; imageId.isValid())
|
if (const db::ImageId imageId{ release->getImageId() }; imageId.isValid())
|
||||||
{
|
{
|
||||||
@@ -221,12 +292,11 @@ namespace lms::cover
|
|||||||
{
|
{
|
||||||
db::TrackEmbeddedImage::FindParameters params;
|
db::TrackEmbeddedImage::FindParameters params;
|
||||||
params.setRelease(releaseId);
|
params.setRelease(releaseId);
|
||||||
|
params.setImageTypes({ db::ImageType::FrontCover, db::ImageType::Media, db::ImageType::Unknown /* give unknown a chance */ });
|
||||||
params.setSortMethod(db::TrackEmbeddedImageSortMethod::FrontTypeThenSizeDesc);
|
params.setSortMethod(db::TrackEmbeddedImageSortMethod::FrontTypeThenSizeDesc);
|
||||||
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
params.setRange(db::Range{ .offset = 0, .size = 1 });
|
||||||
|
|
||||||
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) {
|
db::TrackEmbeddedImage::find(session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); });
|
||||||
res = image->getId();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,5 +382,4 @@ namespace lms::cover
|
|||||||
|
|
||||||
LMS_LOG(COVER, INFO, "JPEG export quality = " << _jpegQuality);
|
LMS_LOG(COVER, INFO, "JPEG export quality = " << _jpegQuality);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace lms::cover
|
} // namespace lms::cover
|
||||||
|
|||||||
Reference in New Issue
Block a user