diff --git a/src/libs/database/impl/Artwork.cpp b/src/libs/database/impl/Artwork.cpp index e9264f06..4dd62d4d 100644 --- a/src/libs/database/impl/Artwork.cpp +++ b/src/libs/database/impl/Artwork.cpp @@ -27,6 +27,7 @@ #include "Utils.hpp" #include "traits/IdTypeTraits.hpp" +#include "traits/PathTraits.hpp" namespace lms::db { @@ -87,4 +88,17 @@ namespace lms::db return utils::fetchQuerySingleResult(query); } + + std::filesystem::path Artwork::getAbsoluteFilePath() const + { + auto query{ session()->query("SELECT COALESCE(image.absolute_file_path, track.absolute_file_path) AS absolute_file_path FROM artwork") }; + query.leftJoin("image ON artwork.image_id = image.id"); + query.leftJoin("track_embedded_image ON artwork.track_embedded_image_id = track_embedded_image.id"); + query.leftJoin("track_embedded_image_link ON track_embedded_image.id = track_embedded_image_link.track_embedded_image_id"); + query.leftJoin("track ON track.id = track_embedded_image_link.track_id"); + query.limit(1); // there may be several tracks matching this artwork... + query.where("artwork.id = ?").bind(getId()); + + return utils::fetchQuerySingleResult(query); + } } // namespace lms::db diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp index 76012da3..be8d334e 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -234,6 +234,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_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_type_track_embedded_image_id_idx ON track_embedded_image_link(track_id, type, track_embedded_image_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)"); diff --git a/src/libs/database/include/database/Artwork.hpp b/src/libs/database/include/database/Artwork.hpp index 581dcc00..4873e45b 100644 --- a/src/libs/database/include/database/Artwork.hpp +++ b/src/libs/database/include/database/Artwork.hpp @@ -19,6 +19,8 @@ #pragma once +#include + #include #include @@ -48,6 +50,7 @@ namespace lms::db TrackEmbeddedImageId getTrackEmbeddedImageId() const { return _trackEmbeddedImage.id(); } ImageId getImageId() const { return _image.id(); } Wt::WDateTime getLastWrittenTime() const; + std::filesystem::path getAbsoluteFilePath() const; template void persist(Action& a) diff --git a/src/libs/database/include/database/TrackEmbeddedImageLink.hpp b/src/libs/database/include/database/TrackEmbeddedImageLink.hpp index 77dd0d2e..69335195 100644 --- a/src/libs/database/include/database/TrackEmbeddedImageLink.hpp +++ b/src/libs/database/include/database/TrackEmbeddedImageLink.hpp @@ -40,8 +40,6 @@ namespace lms::db public: TrackEmbeddedImageLink() = default; - using HashType = std::uint64_t; - // find static std::size_t getCount(Session& session); static pointer find(Session& session, TrackEmbeddedImageLinkId id); diff --git a/src/libs/database/test/Artwork.cpp b/src/libs/database/test/Artwork.cpp index 832aba0d..e0a2745e 100644 --- a/src/libs/database/test/Artwork.cpp +++ b/src/libs/database/test/Artwork.cpp @@ -48,11 +48,13 @@ namespace lms::db::tests { auto transaction{ session.createWriteTransaction() }; image.get().modify()->setLastWriteTime(dateTime); + image.get().modify()->setAbsoluteFilePath("/tmp/foo"); } { auto transaction{ session.createReadTransaction() }; EXPECT_EQ(artwork.get()->getLastWrittenTime(), dateTime); + EXPECT_EQ(artwork.get()->getAbsoluteFilePath(), "/tmp/foo"); } } @@ -73,11 +75,13 @@ namespace lms::db::tests auto transaction{ session.createWriteTransaction() }; session.create(track.get(), image.get()); track.get().modify()->setLastWriteTime(dateTime); + track.get().modify()->setAbsoluteFilePath("/tmp/foo"); } { auto transaction{ session.createReadTransaction() }; EXPECT_EQ(artwork.get()->getLastWrittenTime(), dateTime); + EXPECT_EQ(artwork.get()->getAbsoluteFilePath(), "/tmp/foo"); } } } // namespace lms::db::tests \ No newline at end of file diff --git a/src/libs/services/scanner/impl/steps/ArtworkUtils.cpp b/src/libs/services/scanner/impl/steps/ArtworkUtils.cpp index 153aca0d..e74c6095 100644 --- a/src/libs/services/scanner/impl/steps/ArtworkUtils.cpp +++ b/src/libs/services/scanner/impl/steps/ArtworkUtils.cpp @@ -27,34 +27,6 @@ namespace lms::scanner::utils { - namespace - { - std::filesystem::path toPath(db::Session& session, const db::TrackEmbeddedImageId trackEmbeddedImageId) - { - session.checkReadTransaction(); - - std::filesystem::path res; - - db::Track::FindParameters params; - params.setEmbeddedImage(trackEmbeddedImageId); - params.setRange(db::Range{ .offset = 0, .size = 1 }); - db::Track::find(session, params, [&](const db::Track::pointer& track) { - res = track->getAbsoluteFilePath(); - }); - - return res; - } - - std::filesystem::path toPath(db::Session& session, db::ImageId imageId) - { - session.checkReadTransaction(); - - db::Image::pointer image{ db::Image::find(session, imageId) }; - return image ? image->getAbsoluteFilePath() : std::filesystem::path{}; - } - - } // namespace - db::ObjectPtr getOrCreateArtworkFromTrackEmbeddedImage(db::Session& session, db::TrackEmbeddedImageId trackEmbeddedImageId) { assert(trackEmbeddedImageId.isValid()); @@ -84,21 +56,4 @@ namespace lms::scanner::utils } return artwork; } - - std::filesystem::path toPath(db::Session& session, db::ArtworkId artworkId) - { - session.checkReadTransaction(); - - db::Artwork::pointer artwork{ db::Artwork::find(session, artworkId) }; - if (!artwork) - return std::filesystem::path{}; - - if (artwork->getTrackEmbeddedImageId().isValid()) - return toPath(session, artwork->getTrackEmbeddedImageId()); - if (artwork->getImageId().isValid()) - return toPath(session, artwork->getImageId()); - - return std::filesystem::path{}; - } - } // namespace lms::scanner::utils \ No newline at end of file diff --git a/src/libs/services/scanner/impl/steps/ArtworkUtils.hpp b/src/libs/services/scanner/impl/steps/ArtworkUtils.hpp index 8d18b009..4dc0722b 100644 --- a/src/libs/services/scanner/impl/steps/ArtworkUtils.hpp +++ b/src/libs/services/scanner/impl/steps/ArtworkUtils.hpp @@ -36,6 +36,4 @@ namespace lms::scanner::utils { db::ObjectPtr getOrCreateArtworkFromTrackEmbeddedImage(db::Session& session, db::TrackEmbeddedImageId trackEmbeddedImageId); db::ObjectPtr getOrCreateArtworkFromImage(db::Session& session, db::ImageId imageId); - - std::filesystem::path toPath(db::Session& session, db::ArtworkId artworkId); } // namespace lms::scanner::utils \ No newline at end of file diff --git a/src/libs/services/scanner/impl/steps/ScanStepAssociateArtistImages.cpp b/src/libs/services/scanner/impl/steps/ScanStepAssociateArtistImages.cpp index 5eff245d..d3e4146f 100644 --- a/src/libs/services/scanner/impl/steps/ScanStepAssociateArtistImages.cpp +++ b/src/libs/services/scanner/impl/steps/ScanStepAssociateArtistImages.cpp @@ -271,7 +271,7 @@ namespace lms::scanner artist.modify()->setPreferredArtwork(artwork); if (artwork) - LMS_LOG(DBUPDATER, DEBUG, "Updated preferred artwork for artist '" << artist->getName() << "' with image in " << utils::toPath(session, artwork->getId())); + LMS_LOG(DBUPDATER, DEBUG, "Updated preferred artwork for artist '" << artist->getName() << "' with image in " << artwork->getAbsoluteFilePath()); else LMS_LOG(DBUPDATER, DEBUG, "Removed preferred artwork from artist '" << artist->getName() << "'"); } diff --git a/src/libs/services/scanner/impl/steps/ScanStepAssociateReleaseImages.cpp b/src/libs/services/scanner/impl/steps/ScanStepAssociateReleaseImages.cpp index b6ce83d2..adba6876 100644 --- a/src/libs/services/scanner/impl/steps/ScanStepAssociateReleaseImages.cpp +++ b/src/libs/services/scanner/impl/steps/ScanStepAssociateReleaseImages.cpp @@ -222,7 +222,7 @@ namespace lms::scanner release.modify()->setPreferredArtwork(artwork); if (artwork) - LMS_LOG(DBUPDATER, DEBUG, "Updated preferred artwork in release '" << release->getName() << "' with image in " << utils::toPath(session, artwork->getId())); + LMS_LOG(DBUPDATER, DEBUG, "Updated preferred artwork in release '" << release->getName() << "' with image in " << artwork->getAbsoluteFilePath()); else LMS_LOG(DBUPDATER, DEBUG, "Removed preferred artwork from release '" << release->getName() << "'"); } diff --git a/src/libs/services/scanner/impl/steps/ScanStepAssociateTrackImages.cpp b/src/libs/services/scanner/impl/steps/ScanStepAssociateTrackImages.cpp index 6d32e21f..035c505d 100644 --- a/src/libs/services/scanner/impl/steps/ScanStepAssociateTrackImages.cpp +++ b/src/libs/services/scanner/impl/steps/ScanStepAssociateTrackImages.cpp @@ -49,10 +49,16 @@ namespace lms::scanner return !artwork; if (const db::TrackEmbeddedImageId* trackEmbeddedImageId = std::get_if(&preferredArtwork)) + { + assert(trackEmbeddedImageId->isValid()); return artwork && *trackEmbeddedImageId == artwork->getTrackEmbeddedImageId(); + } if (const db::ArtworkId* artworkId = std::get_if(&preferredArtwork)) + { + assert(artworkId->isValid()); return artwork && *artworkId == artwork->getId(); + } return false; } @@ -77,36 +83,9 @@ namespace lms::scanner std::size_t processedTrackCount{}; }; - TrackArtwork computePreferredTrackArtwork(SearchTrackArtworkContext& searchContext, const db::Track::pointer& track) + TrackArtwork computePreferredTrackArtwork(SearchTrackArtworkContext& searchContext, const db::Track::pointer& track, TrackArtwork preferredMediaArtwork) { - // Try to get a media image - TrackArtwork res; - { - db::TrackEmbeddedImage::FindParameters params; - params.setTrack(track->getId()); - params.setImageTypes({ db::ImageType::Media }); - params.setSortMethod(db::TrackEmbeddedImageSortMethod::SizeDesc); - params.setRange(db::Range{ .offset = 0, .size = 1 }); - db::TrackEmbeddedImage::find(searchContext.session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); }); - } - - if (isValid(res)) - return res; - - // Fallback on another track of the same disc - 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::TrackNumberThenSizeDesc); - params.setRange(db::Range{ .offset = 0, .size = 1 }); - db::TrackEmbeddedImage::find(searchContext.session, params, [&](const db::TrackEmbeddedImage::pointer& image) { res = image->getId(); }); - } + TrackArtwork res{ preferredMediaArtwork }; if (isValid(res)) return res; @@ -125,8 +104,15 @@ namespace lms::scanner return res; // Fallback on the artwork already resolved for the release + const db::ReleaseId releaseId{ track->getReleaseId() }; + if (!releaseId.isValid()) + return res; + if (const db::Release::pointer release{ db::Release::find(searchContext.session, releaseId) }) - res = release->getPreferredArtworkId(); + { + if (const db::ArtworkId releaseArtworkId{ release->getPreferredArtworkId() }; releaseArtworkId.isValid()) + res = releaseArtworkId; + } return res; } @@ -147,11 +133,15 @@ namespace lms::scanner return res; // fallback on another track of the same disc - if (const db::ReleaseId releaseId{ track->getReleaseId() }; releaseId.isValid()) + const db::ReleaseId releaseId{ track->getReleaseId() }; + if (!releaseId.isValid()) + return res; + + if (const auto discNumber{ track->getDiscNumber() }) { db::TrackEmbeddedImage::FindParameters params; params.setRelease(releaseId); - params.setDiscNumber(track->getDiscNumber()); + params.setDiscNumber(discNumber); params.setImageTypes({ db::ImageType::Media }); params.setSortMethod(db::TrackEmbeddedImageSortMethod::TrackNumberThenSizeDesc); params.setRange(db::Range{ .offset = 0, .size = 1 }); @@ -171,8 +161,8 @@ namespace lms::scanner auto transaction{ searchContext.session.createReadTransaction() }; db::Track::find(searchContext.session, searchContext.lastRetrievedTrackId, readBatchSize, [&](const db::Track::pointer& track) { - const TrackArtwork preferredArtwork{ computePreferredTrackArtwork(searchContext, track) }; const TrackArtwork preferredMediaArtwork{ computePreferredTrackMediaArtwork(searchContext, track) }; + const TrackArtwork preferredArtwork{ computePreferredTrackArtwork(searchContext, track, preferredMediaArtwork) }; const db::Artwork::pointer currentPreferredArtwork{ track->getPreferredArtwork() }; const db::Artwork::pointer currentPreferredMediaArtwork{ track->getPreferredMediaArtwork() }; @@ -180,7 +170,7 @@ namespace lms::scanner if (!isSameArtwork(preferredArtwork, currentPreferredArtwork) || !isSameArtwork(preferredMediaArtwork, currentPreferredMediaArtwork)) { - TrackArtworksAssociations.push_back(TrackArtworksAssociation{ track, preferredArtwork, preferredMediaArtwork }); + TrackArtworksAssociations.push_back(TrackArtworksAssociation{ .track = track, .preferredArtwork = preferredArtwork, .preferredMediaArtwork = preferredMediaArtwork }); } searchContext.processedTrackCount++; @@ -201,7 +191,7 @@ namespace lms::scanner // Using track.modify() is quite CPU intensive as the track class has too many fields db::Track::updatePreferredArtwork(session, track->getId(), artwork ? artwork->getId() : db::ArtworkId{}); if (artwork) - LMS_LOG(DBUPDATER, DEBUG, "Updated preferred artwork in track " << track->getAbsoluteFilePath() << " with image in " << utils::toPath(session, artwork->getId())); + LMS_LOG(DBUPDATER, DEBUG, "Updated preferred artwork in track " << track->getAbsoluteFilePath() << " with image in " << artwork->getAbsoluteFilePath()); else LMS_LOG(DBUPDATER, DEBUG, "Removed preferred artwork from track " << track->getAbsoluteFilePath()); } @@ -217,9 +207,9 @@ namespace lms::scanner // Using track.modify() is quite CPU intensive as the track class has too many fields db::Track::updatePreferredMediaArtwork(session, track->getId(), artwork ? artwork->getId() : db::ArtworkId{}); if (artwork) - LMS_LOG(DBUPDATER, DEBUG, "Updated preferred media artwork in track '" << track->getAbsoluteFilePath() << "' with image in " << utils::toPath(session, artwork->getId())); + LMS_LOG(DBUPDATER, DEBUG, "Updated preferred media artwork in track " << track->getAbsoluteFilePath() << " with image in " << artwork->getAbsoluteFilePath()); else - LMS_LOG(DBUPDATER, DEBUG, "Removed preferred media artwork from track '" << track->getAbsoluteFilePath() << "'"); + LMS_LOG(DBUPDATER, DEBUG, "Removed preferred media artwork from track " << track->getAbsoluteFilePath()); } void updateTrackPreferredArtworks(db::Session& session, const TrackArtworksAssociation& TrackArtworksAssociation)