From 8bdf3b42d021b76309f92c34f5f3ffa7e574d569 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 11 Oct 2024 19:04:59 +0200 Subject: [PATCH] Changed the artists view to use artist images --- approot/artist.xml | 2 +- approot/artists.xml | 11 +- approot/images/unknown-artist.svg | 9 + src/libs/services/CMakeLists.txt | 2 +- src/libs/services/artwork/CMakeLists.txt | 29 +++ .../impl/ArtworkService.cpp} | 45 ++-- .../impl/ArtworkService.hpp} | 27 +- .../{cover => artwork}/impl/ImageCache.cpp | 0 .../{cover => artwork}/impl/ImageCache.hpp | 0 .../services/artwork/IArtworkService.hpp} | 16 +- src/libs/services/cover/CMakeLists.txt | 29 --- src/libs/subsonic/CMakeLists.txt | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 4 +- src/libs/subsonic/impl/SubsonicResource.hpp | 2 +- .../impl/entrypoints/MediaRetrieval.cpp | 10 +- src/lms/CMakeLists.txt | 4 +- src/lms/main.cpp | 6 +- src/lms/ui/LmsApplication.cpp | 6 +- src/lms/ui/LmsApplication.hpp | 6 +- src/lms/ui/MediaPlayer.cpp | 10 +- src/lms/ui/PlayQueue.cpp | 18 +- src/lms/ui/Utils.cpp | 64 +---- src/lms/ui/Utils.hpp | 7 +- src/lms/ui/explore/ArtistListHelpers.cpp | 11 +- src/lms/ui/explore/ArtistView.cpp | 2 +- src/lms/ui/explore/ReleaseHelpers.cpp | 6 +- src/lms/ui/explore/ReleaseView.cpp | 2 +- src/lms/ui/explore/TrackListHelpers.cpp | 14 +- src/lms/ui/resource/ArtworkResource.cpp | 241 ++++++++++++++++++ ...{CoverResource.hpp => ArtworkResource.hpp} | 20 +- src/lms/ui/resource/CoverResource.cpp | 125 --------- src/tools/cover/CMakeLists.txt | 2 +- src/tools/cover/LmsCover.cpp | 18 +- 33 files changed, 439 insertions(+), 311 deletions(-) create mode 100644 approot/images/unknown-artist.svg create mode 100644 src/libs/services/artwork/CMakeLists.txt rename src/libs/services/{cover/impl/CoverService.cpp => artwork/impl/ArtworkService.cpp} (74%) rename src/libs/services/{cover/impl/CoverService.hpp => artwork/impl/ArtworkService.hpp} (56%) rename src/libs/services/{cover => artwork}/impl/ImageCache.cpp (100%) rename src/libs/services/{cover => artwork}/impl/ImageCache.hpp (100%) rename src/libs/services/{cover/include/services/cover/ICoverService.hpp => artwork/include/services/artwork/IArtworkService.hpp} (62%) delete mode 100644 src/libs/services/cover/CMakeLists.txt create mode 100644 src/lms/ui/resource/ArtworkResource.cpp rename src/lms/ui/resource/{CoverResource.hpp => ArtworkResource.hpp} (61%) delete mode 100644 src/lms/ui/resource/CoverResource.cpp diff --git a/approot/artist.xml b/approot/artist.xml index ddd340d9..6119ca39 100644 --- a/approot/artist.xml +++ b/approot/artist.xml @@ -29,7 +29,7 @@ ${} ${}

${tr:Lms.Explore.Artist.similar-artists}

- ${similar-artists class="d-grid gap-3"} + ${similar-artists class="row row-cols-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 gx-2 gy-4"} ${
} diff --git a/approot/artists.xml b/approot/artists.xml index 12938860..a9f78e6d 100644 --- a/approot/artists.xml +++ b/approot/artists.xml @@ -14,11 +14,18 @@ - ${name class="text-decoration-none link-secondary"} +
+
+
+ ${image class="shadow-sm"} +
+ ${name class="d-block text-center text-truncate text-decoration-none link-secondary"} +
+
- ${elements class="d-grid gap-3"} + ${elements class="row row-cols-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 gx-2 gy-4"} ${loading-indicator} diff --git a/approot/images/unknown-artist.svg b/approot/images/unknown-artist.svg new file mode 100644 index 00000000..107866e9 --- /dev/null +++ b/approot/images/unknown-artist.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/libs/services/CMakeLists.txt b/src/libs/services/CMakeLists.txt index b8bfa254..9cb1f7ce 100644 --- a/src/libs/services/CMakeLists.txt +++ b/src/libs/services/CMakeLists.txt @@ -1,5 +1,5 @@ +add_subdirectory(artwork) add_subdirectory(auth) -add_subdirectory(cover) add_subdirectory(feedback) add_subdirectory(recommendation) add_subdirectory(scanner) diff --git a/src/libs/services/artwork/CMakeLists.txt b/src/libs/services/artwork/CMakeLists.txt new file mode 100644 index 00000000..1d18565a --- /dev/null +++ b/src/libs/services/artwork/CMakeLists.txt @@ -0,0 +1,29 @@ + +add_library(lmsartwork SHARED + impl/ImageCache.cpp + impl/ArtworkService.cpp + ) + +target_include_directories(lmsartwork INTERFACE + include + ) + +target_include_directories(lmsartwork PRIVATE + include + impl + ) + +target_link_libraries(lmsartwork PRIVATE + lmsav + lmsimage + ) + +target_link_libraries(lmsartwork PUBLIC + lmsdatabase + lmsimage + lmscore + std::filesystem + ) + +install(TARGETS lmsartwork DESTINATION ${CMAKE_INSTALL_LIBDIR}) + diff --git a/src/libs/services/cover/impl/CoverService.cpp b/src/libs/services/artwork/impl/ArtworkService.cpp similarity index 74% rename from src/libs/services/cover/impl/CoverService.cpp rename to src/libs/services/artwork/impl/ArtworkService.cpp index b42bbcae..0f3ae1f1 100644 --- a/src/libs/services/cover/impl/CoverService.cpp +++ b/src/libs/services/artwork/impl/ArtworkService.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "CoverService.hpp" +#include "ArtworkService.hpp" #include "av/IAudioFile.hpp" #include "core/IConfig.hpp" @@ -45,27 +45,29 @@ namespace lms::cover } } // namespace - std::unique_ptr createCoverService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath) + std::unique_ptr createArtworkService(db::Db& db, const std::filesystem::path& defaultReleaseCoverSvgPath, const std::filesystem::path& defaultArtistImageSvgPath) { - return std::make_unique(db, defaultSvgCoverPath); + return std::make_unique(db, defaultReleaseCoverSvgPath, defaultArtistImageSvgPath); } using namespace image; - CoverService::CoverService(db::Db& db, - const std::filesystem::path& defaultSvgCoverPath) + ArtworkService::ArtworkService(db::Db& db, + const std::filesystem::path& defaultReleaseCoverSvgPath, + const std::filesystem::path& defaultArtistImageSvgPath) : _db{ db } , _cache{ core::Service::get()->getULong("cover-max-cache-size", 30) * 1000 * 1000 } { setJpegQuality(core::Service::get()->getULong("cover-jpeg-quality", 75)); - LMS_LOG(COVER, INFO, "Default cover path = '" << defaultSvgCoverPath.string() << "'"); + LMS_LOG(COVER, INFO, "Default release cover path = '" << defaultReleaseCoverSvgPath.string() << "'"); LMS_LOG(COVER, INFO, "Max cache size = " << _cache.getMaxCacheSize()); - _defaultCover = image::readSvgFile(defaultSvgCoverPath); // may throw + _defaultReleaseCover = image::readSvgFile(defaultReleaseCoverSvgPath); // may throw + _defaultArtistImage = image::readSvgFile(defaultArtistImageSvgPath); // may throw } - std::unique_ptr CoverService::getFromAvMediaFile(const av::IAudioFile& input, ImageSize width) const + std::unique_ptr ArtworkService::getFromAvMediaFile(const av::IAudioFile& input, ImageSize width) const { std::unique_ptr image; @@ -88,7 +90,7 @@ namespace lms::cover return image; } - std::unique_ptr CoverService::getFromImageFile(const std::filesystem::path& p, ImageSize width) const + std::unique_ptr ArtworkService::getFromImageFile(const std::filesystem::path& p, ImageSize width) const { std::unique_ptr image; @@ -106,12 +108,17 @@ namespace lms::cover return image; } - std::shared_ptr CoverService::getDefaultSvgCover() + std::shared_ptr ArtworkService::getDefaultReleaseCover() { - return _defaultCover; + return _defaultReleaseCover; } - bool CoverService::checkImageFile(const std::filesystem::path& filePath) const + std::shared_ptr ArtworkService::getDefaultArtistImage() + { + return _defaultArtistImage; + } + + bool ArtworkService::checkImageFile(const std::filesystem::path& filePath) const { std::error_code ec; @@ -127,7 +134,7 @@ namespace lms::cover return true; } - std::unique_ptr CoverService::getFromTrack(const std::filesystem::path& p, ImageSize width) const + std::unique_ptr ArtworkService::getTrackImage(const std::filesystem::path& p, ImageSize width) const { std::unique_ptr image; @@ -143,7 +150,7 @@ namespace lms::cover return image; } - std::shared_ptr CoverService::getFromTrack(db::TrackId trackId, ImageSize width) + std::shared_ptr ArtworkService::getTrackImage(db::TrackId trackId, ImageSize width) { const ImageCache::EntryDesc cacheEntryDesc{ trackId, width }; @@ -157,7 +164,7 @@ namespace lms::cover const db::Track::pointer track{ db::Track::find(session, trackId) }; if (track && track->hasCover()) - cover = getFromTrack(track->getAbsoluteFilePath(), width); + cover = getTrackImage(track->getAbsoluteFilePath(), width); } if (cover) @@ -166,7 +173,7 @@ namespace lms::cover return cover; } - std::shared_ptr CoverService::getFromRelease(db::ReleaseId releaseId, ImageSize width) + std::shared_ptr ArtworkService::getReleaseCover(db::ReleaseId releaseId, ImageSize width) { using namespace db; const ImageCache::EntryDesc cacheEntryDesc{ releaseId, width }; @@ -193,7 +200,7 @@ namespace lms::cover return image; } - std::shared_ptr CoverService::getFromArtist(db::ArtistId artistId, ImageSize width) + std::shared_ptr ArtworkService::getArtistImage(db::ArtistId artistId, ImageSize width) { using namespace db; const ImageCache::EntryDesc cacheEntryDesc{ artistId, width }; @@ -220,12 +227,12 @@ namespace lms::cover return artistImage; } - void CoverService::flushCache() + void ArtworkService::flushCache() { _cache.flush(); } - void CoverService::setJpegQuality(unsigned quality) + void ArtworkService::setJpegQuality(unsigned quality) { _jpegQuality = core::utils::clamp(quality, 1, 100); diff --git a/src/libs/services/cover/impl/CoverService.hpp b/src/libs/services/artwork/impl/ArtworkService.hpp similarity index 56% rename from src/libs/services/cover/impl/CoverService.hpp rename to src/libs/services/artwork/impl/ArtworkService.hpp index 1587a073..58e0c201 100644 --- a/src/libs/services/cover/impl/CoverService.hpp +++ b/src/libs/services/artwork/impl/ArtworkService.hpp @@ -24,7 +24,7 @@ #include "database/Types.hpp" #include "image/IEncodedImage.hpp" -#include "services/cover/ICoverService.hpp" +#include "services/artwork/IArtworkService.hpp" #include "ImageCache.hpp" @@ -40,34 +40,37 @@ namespace lms::av namespace lms::cover { - class CoverService : public ICoverService + class ArtworkService : public IArtworkService { public: - CoverService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath); + ArtworkService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath, const std::filesystem::path& defaultArtistImageSvgPath); private: - CoverService(const CoverService&) = delete; - CoverService& operator=(const CoverService&) = delete; + ArtworkService(const ArtworkService&) = delete; + ArtworkService& operator=(const ArtworkService&) = delete; + + std::shared_ptr getTrackImage(db::TrackId trackId, image::ImageSize width) override; + std::shared_ptr getReleaseCover(db::ReleaseId releaseId, image::ImageSize width) override; + std::shared_ptr getArtistImage(db::ArtistId artistId, image::ImageSize width) override; + std::shared_ptr getDefaultReleaseCover() override; + std::shared_ptr getDefaultArtistImage() override; - std::shared_ptr getFromTrack(db::TrackId trackId, image::ImageSize width) override; - std::shared_ptr getFromRelease(db::ReleaseId releaseId, image::ImageSize width) override; - std::shared_ptr getFromArtist(db::ArtistId artistId, image::ImageSize width) override; - std::shared_ptr getDefaultSvgCover() override; void flushCache() override; void setJpegQuality(unsigned quality) override; - std::shared_ptr getFromTrack(db::Session& dbSession, db::TrackId trackId, image::ImageSize width, bool allowReleaseFallback); + std::shared_ptr getTrackImage(db::Session& dbSession, db::TrackId trackId, image::ImageSize width, bool allowReleaseFallback); std::unique_ptr getFromAvMediaFile(const av::IAudioFile& input, image::ImageSize width) const; std::unique_ptr getFromImageFile(const std::filesystem::path& p, image::ImageSize width) const; - std::unique_ptr getFromTrack(const std::filesystem::path& path, image::ImageSize width) const; + std::unique_ptr getTrackImage(const std::filesystem::path& path, image::ImageSize width) const; bool checkImageFile(const std::filesystem::path& filePath) const; db::Db& _db; ImageCache _cache; - std::shared_ptr _defaultCover; + std::shared_ptr _defaultReleaseCover; + std::shared_ptr _defaultArtistImage; static inline const std::vector _fileExtensions{ ".jpg", ".jpeg", ".png", ".bmp" }; // TODO parametrize unsigned _jpegQuality; diff --git a/src/libs/services/cover/impl/ImageCache.cpp b/src/libs/services/artwork/impl/ImageCache.cpp similarity index 100% rename from src/libs/services/cover/impl/ImageCache.cpp rename to src/libs/services/artwork/impl/ImageCache.cpp diff --git a/src/libs/services/cover/impl/ImageCache.hpp b/src/libs/services/artwork/impl/ImageCache.hpp similarity index 100% rename from src/libs/services/cover/impl/ImageCache.hpp rename to src/libs/services/artwork/impl/ImageCache.hpp diff --git a/src/libs/services/cover/include/services/cover/ICoverService.hpp b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp similarity index 62% rename from src/libs/services/cover/include/services/cover/ICoverService.hpp rename to src/libs/services/artwork/include/services/artwork/IArtworkService.hpp index bf0f6c57..3d499303 100644 --- a/src/libs/services/cover/include/services/cover/ICoverService.hpp +++ b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp @@ -34,26 +34,28 @@ namespace lms::db namespace lms::cover { - class ICoverService + class IArtworkService { public: - virtual ~ICoverService() = default; + virtual ~IArtworkService() = default; - virtual std::shared_ptr getFromArtist(db::ArtistId artistId, image::ImageSize width) = 0; + virtual std::shared_ptr getArtistImage(db::ArtistId artistId, image::ImageSize width) = 0; // no logic to fallback to release here - virtual std::shared_ptr getFromTrack(db::TrackId trackId, image::ImageSize width) = 0; + virtual std::shared_ptr getTrackImage(db::TrackId trackId, image::ImageSize width) = 0; // no logic to fallback to track here - virtual std::shared_ptr getFromRelease(db::ReleaseId releaseId, image::ImageSize width) = 0; + virtual std::shared_ptr getReleaseCover(db::ReleaseId releaseId, image::ImageSize width) = 0; - virtual std::shared_ptr getDefaultSvgCover() = 0; + // Svg images dont have image "size" + virtual std::shared_ptr getDefaultReleaseCover() = 0; + virtual std::shared_ptr getDefaultArtistImage() = 0; virtual void flushCache() = 0; virtual void setJpegQuality(unsigned quality) = 0; // from 1 to 100 }; - std::unique_ptr createCoverService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath); + std::unique_ptr createArtworkService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath, const std::filesystem::path& defaultArtistImageSvgPath); } // namespace lms::cover diff --git a/src/libs/services/cover/CMakeLists.txt b/src/libs/services/cover/CMakeLists.txt deleted file mode 100644 index 2f7c054f..00000000 --- a/src/libs/services/cover/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ - -add_library(lmsservice-cover SHARED - impl/ImageCache.cpp - impl/CoverService.cpp - ) - -target_include_directories(lmsservice-cover INTERFACE - include - ) - -target_include_directories(lmsservice-cover PRIVATE - include - impl - ) - -target_link_libraries(lmsservice-cover PRIVATE - lmsav - lmsimage - ) - -target_link_libraries(lmsservice-cover PUBLIC - lmsdatabase - lmsimage - lmscore - std::filesystem - ) - -install(TARGETS lmsservice-cover DESTINATION ${CMAKE_INSTALL_LIBDIR}) - diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index 9e5500c1..c35720ee 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -48,7 +48,7 @@ target_link_libraries(lmssubsonic PRIVATE lmsrecommendation lmsscanner lmsscrobbling - lmsservice-cover + lmsartwork lmscore std::filesystem ) diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index c2d6f608..7e67c05e 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -282,7 +282,7 @@ namespace lms::api::subsonic SubsonicResource::SubsonicResource(db::Db& db) : _serverProtocolVersionsByClient{ readConfigProtocolVersions() } , _openSubsonicDisabledClients{ readOpenSubsonicDisabledClients() } - , _defaultCoverClients{ readDefaultCoverClients() } + , _defaultReleaseCoverClients{ readDefaultCoverClients() } , _db{ db } { } @@ -410,7 +410,7 @@ namespace lms::api::subsonic const ClientInfo clientInfo{ getClientInfo(request) }; const db::UserId userId{ authenticateUser(request, clientInfo) }; bool enableOpenSubsonic{ !_openSubsonicDisabledClients.contains(clientInfo.name) }; - bool enableDefaultCover{ _defaultCoverClients.contains(clientInfo.name) }; + bool enableDefaultCover{ _defaultReleaseCoverClients.contains(clientInfo.name) }; const ResponseFormat format{ getParameterAs(request.getParameterMap(), "f").value_or("xml") == "json" ? ResponseFormat::json : ResponseFormat::xml }; db::User::pointer user; diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index 6303d580..9a00a448 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -54,7 +54,7 @@ namespace lms::api::subsonic const std::unordered_map _serverProtocolVersionsByClient; const std::unordered_set _openSubsonicDisabledClients; - const std::unordered_set _defaultCoverClients; + const std::unordered_set _defaultReleaseCoverClients; db::Db& _db; }; diff --git a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp index 781d0f3a..d3c8c315 100644 --- a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp @@ -32,7 +32,7 @@ #include "database/Session.hpp" #include "database/Track.hpp" #include "database/User.hpp" -#include "services/cover/ICoverService.hpp" +#include "services/artwork/IArtworkService.hpp" #include "ParameterParsing.hpp" #include "SubsonicId.hpp" @@ -267,14 +267,14 @@ namespace lms::api::subsonic std::shared_ptr cover; if (trackId) - cover = core::Service::get()->getFromTrack(*trackId, size); + cover = core::Service::get()->getTrackImage(*trackId, size); else if (releaseId) - cover = core::Service::get()->getFromRelease(*releaseId, size); + cover = core::Service::get()->getReleaseCover(*releaseId, size); else if (artistId) - cover = core::Service::get()->getFromArtist(*artistId, size); + cover = core::Service::get()->getArtistImage(*artistId, size); if (!cover && context.enableDefaultCover && !artistId) - cover = core::Service::get()->getDefaultSvgCover(); + cover = core::Service::get()->getDefaultReleaseCover(); if (!cover) { diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt index 55fc992e..19f1790b 100644 --- a/src/lms/CMakeLists.txt +++ b/src/lms/CMakeLists.txt @@ -51,7 +51,7 @@ add_executable(lms ui/explore/TracksView.cpp ui/resource/AudioFileResource.cpp ui/resource/AudioTranscodingResource.cpp - ui/resource/CoverResource.cpp + ui/resource/ArtworkResource.cpp ui/resource/DownloadResource.cpp ) @@ -70,7 +70,7 @@ target_link_libraries(lms PRIVATE lmsrecommendation lmsscanner lmsscrobbling - lmsservice-cover + lmsartwork lmssubsonic lmscore ) diff --git a/src/lms/main.cpp b/src/lms/main.cpp index bd6f3467..6c349966 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -35,10 +35,10 @@ #include "database/Db.hpp" #include "database/Session.hpp" #include "image/Image.hpp" +#include "services/artwork/IArtworkService.hpp" #include "services/auth/IAuthTokenService.hpp" #include "services/auth/IEnvService.hpp" #include "services/auth/IPasswordService.hpp" -#include "services/cover/ICoverService.hpp" #include "services/feedback/IFeedbackService.hpp" #include "services/recommendation/IPlaylistGeneratorService.hpp" #include "services/recommendation/IRecommendationService.hpp" @@ -340,7 +340,7 @@ namespace lms throw core::LmsException{ "Bad value '" + authenticationBackend + "' for 'authentication-backend'" }; image::init(argv[0]); - core::Service coverService{ cover::createCoverService(database, server.appRoot() + "/images/unknown-cover.svg") }; + core::Service artworkService{ cover::createArtworkService(database, server.appRoot() + "/images/unknown-cover.svg", server.appRoot() + "/images/unknown-artist.svg") }; core::Service recommendationService{ recommendation::createRecommendationService(database) }; core::Service playlistGeneratorService{ recommendation::createPlaylistGeneratorService(database, *recommendationService.get()) }; core::Service scannerService{ scanner::createScannerService(database) }; @@ -348,7 +348,7 @@ namespace lms scannerService->getEvents().scanComplete.connect([&] { // Flush cover cache even if no changes: // covers may be external files that changed and we don't keep track of them for now (but we should) - coverService->flushCache(); + artworkService->flushCache(); database.getTLSSession().refreshTracingLoggerStats(); }); diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 767f466a..1e5ca064 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -37,9 +37,9 @@ #include "database/Session.hpp" #include "database/TrackList.hpp" #include "database/User.hpp" +#include "services/artwork/IArtworkService.hpp" #include "services/auth/IEnvService.hpp" #include "services/auth/IPasswordService.hpp" -#include "services/cover/ICoverService.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "Auth.hpp" @@ -61,9 +61,9 @@ #include "common/Template.hpp" #include "explore/Explore.hpp" #include "explore/Filters.hpp" +#include "resource/ArtworkResource.hpp" #include "resource/AudioFileResource.hpp" #include "resource/AudioTranscodingResource.hpp" -#include "resource/CoverResource.hpp" namespace lms::ui { @@ -400,7 +400,7 @@ namespace lms::ui { LMS_SCOPED_TRACE_OVERVIEW("UI", "ApplicationCreateHome"); - _coverResource = std::make_shared(); + _artworkResource = std::make_shared(); declareJavaScriptFunction("onLoadCover", "function(id) { id.className += \" Lms-cover-loaded\"}"); declareJavaScriptFunction("updateActiveNav", diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 4a63195a..537add0e 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -42,7 +42,7 @@ namespace lms::db namespace lms::ui { - class CoverResource; + class ArtworkResource; class LmsApplicationException; class MediaPlayer; class PlayQueue; @@ -60,7 +60,7 @@ namespace lms::ui static LmsApplication* instance(); // Session application data - std::shared_ptr getCoverResource() { return _coverResource; } + std::shared_ptr getArtworkResource() { return _artworkResource; } db::Db& getDb(); db::Session& getDbSession(); // always thread safe @@ -115,7 +115,7 @@ namespace lms::ui bool strongAuth{}; }; std::optional _user; - std::shared_ptr _coverResource; + std::shared_ptr _artworkResource; MediaPlayer* _mediaPlayer{}; PlayQueue* _playQueue{}; NotificationContainer* _notificationContainer{}; diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index 0897f824..313f7bb5 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -37,9 +37,9 @@ #include "LmsApplication.hpp" #include "Utils.hpp" +#include "resource/ArtworkResource.hpp" #include "resource/AudioFileResource.hpp" #include "resource/AudioTranscodingResource.hpp" -#include "resource/CoverResource.hpp" namespace lms::ui { @@ -255,13 +255,13 @@ namespace lms::ui << " artist: \"" << (!artists.empty() ? core::stringUtils::jsEscape(track->getArtistDisplayName()) : "") << "\"," << " release: \"" << (track->getRelease() ? core::stringUtils::jsEscape(track->getRelease()->getName()) : "") << "\"," << " artwork: [" - << " { src: \"" << LmsApp->getCoverResource()->getTrackUrl(trackId, CoverResource::Size::Small) << "\", sizes: \"128x128\", type: \"image/jpeg\" }," - << " { src: \"" << LmsApp->getCoverResource()->getTrackUrl(trackId, CoverResource::Size::Large) << "\", sizes: \"512x512\", type: \"image/jpeg\" }," + << " { src: \"" << LmsApp->getArtworkResource()->getTrackImageUrl(trackId, ArtworkResource::Size::Small) << "\", sizes: \"128x128\", type: \"image/jpeg\" }," + << " { src: \"" << LmsApp->getArtworkResource()->getTrackImageUrl(trackId, ArtworkResource::Size::Large) << "\", sizes: \"512x512\", type: \"image/jpeg\" }," << " ]" << "};"; // Update 'sizes' above to match this: - static_assert(static_cast>(CoverResource::Size::Small) == 128); - static_assert(static_cast>(CoverResource::Size::Large) == 512); + static_assert(static_cast>(ArtworkResource::Size::Small) == 128); + static_assert(static_cast>(ArtworkResource::Size::Large) == 512); oss << "LMS.mediaplayer.loadTrack(params, " << (play ? "true" : "false") << ")"; // true to autoplay _title->setTextFormat(Wt::TextFormat::Plain); diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 0973f693..e8e86dd5 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -485,22 +485,20 @@ namespace lms::ui entry->bindWidget("artists-md", utils::createArtistAnchorList(artists)); } - const auto release{ track->getRelease() }; - if (release) + auto image{ utils::createTrackImage(track->getId(), ArtworkResource::Size::Small) }; + image->addStyleClass("Lms-cover-track"); + if (const auto release{ track->getRelease() }) { entry->setCondition("if-has-release", true); entry->bindWidget("release", utils::createReleaseAnchor(release)); - { - Wt::WAnchor* anchor{ entry->bindWidget("cover", utils::createReleaseAnchor(release, false)) }; - auto cover{ utils::createCover(release->getId(), CoverResource::Size::Small) }; - cover->addStyleClass("Lms-cover-track Lms-cover-anchor"); // HACK - anchor->setImage(std::move(cover)); - } + + Wt::WAnchor* anchor{ entry->bindWidget("cover", utils::createReleaseAnchor(release, false)) }; + image->addStyleClass("Lms-cover-anchor"); // HACK + anchor->setImage(std::move(image)); } else { - auto cover{ entry->bindWidget("cover", utils::createCover(track->getId(), CoverResource::Size::Small)) }; - cover->addStyleClass("Lms-cover-track"); + entry->bindWidget("cover", std::move(image)); } entry->bindString("duration", utils::durationToString(track->getDuration()), Wt::TextFormat::Plain); diff --git a/src/lms/ui/Utils.cpp b/src/lms/ui/Utils.cpp index 8bf98c5e..f0e39ec9 100644 --- a/src/lms/ui/Utils.cpp +++ b/src/lms/ui/Utils.cpp @@ -63,66 +63,28 @@ namespace lms::ui::utils return oss.str(); } - std::unique_ptr createCover(db::ReleaseId releaseId, CoverResource::Size size) + std::unique_ptr createArtistImage(db::ArtistId artistId, ArtworkResource::Size size) + { + auto image{ std::make_unique() }; + image->setImageLink(LmsApp->getArtworkResource()->getArtistImageUrl(artistId, size)); + image->setStyleClass("Lms-cover img-fluid"); // HACK + image->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); // HACK + return image; + } + + std::unique_ptr createReleaseCover(db::ReleaseId releaseId, ArtworkResource::Size size) { auto cover{ std::make_unique() }; - std::string internalUrl; - - { - auto transaction{ LmsApp->getDbSession().createReadTransaction() }; - - const db::Release::pointer release{ db::Release::find(LmsApp->getDbSession(), releaseId) }; - if (release) - { - if (release->getImage()) - { - internalUrl = LmsApp->getCoverResource()->getReleaseUrl(release->getId(), size); - } - else - { - db::Track::FindParameters params; - params.setRelease(releaseId); - params.setHasEmbeddedImage(true); - params.setRange(db::Range{ 0, 1 }); - - db::Track::find(LmsApp->getDbSession(), params, [&](const db::Track::pointer& track) { - internalUrl = LmsApp->getCoverResource()->getTrackUrl(track->getId(), size); - }); - } - } - } - - if (internalUrl.empty()) - internalUrl = LmsApp->getCoverResource()->getDefaultUrl(size); - - cover->setImageLink(internalUrl); + cover->setImageLink(LmsApp->getArtworkResource()->getReleaseCoverUrl(releaseId, size)); cover->setStyleClass("Lms-cover img-fluid"); // HACK cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); // HACK return cover; } - std::unique_ptr createCover(db::TrackId trackId, CoverResource::Size size) + std::unique_ptr createTrackImage(db::TrackId trackId, ArtworkResource::Size size) { auto cover{ std::make_unique() }; - std::string internalUrl; - - { - auto transaction{ LmsApp->getDbSession().createReadTransaction() }; - - const db::Track::pointer track{ db::Track::find(LmsApp->getDbSession(), trackId) }; - if (track) - { - if (track->hasCover()) - internalUrl = LmsApp->getCoverResource()->getTrackUrl(trackId, size); - else if (const db::Release::pointer release{ track->getRelease() }; release && release->getImage()) - internalUrl = LmsApp->getCoverResource()->getReleaseUrl(release->getId(), size); - } - } - - if (internalUrl.empty()) - internalUrl = LmsApp->getCoverResource()->getDefaultUrl(size); - - cover->setImageLink(internalUrl); + cover->setImageLink(LmsApp->getArtworkResource()->getTrackImageUrl(trackId, size)); cover->setStyleClass("Lms-cover img-fluid"); // HACK cover->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); // HACK diff --git a/src/lms/ui/Utils.hpp b/src/lms/ui/Utils.hpp index aab8de54..0365870b 100644 --- a/src/lms/ui/Utils.hpp +++ b/src/lms/ui/Utils.hpp @@ -34,7 +34,7 @@ #include "database/ReleaseId.hpp" #include "database/TrackId.hpp" -#include "resource/CoverResource.hpp" +#include "resource/ArtworkResource.hpp" namespace lms::db { @@ -54,8 +54,9 @@ namespace lms::ui::utils { std::string durationToString(std::chrono::milliseconds msDuration); - std::unique_ptr createCover(db::ReleaseId releaseId, CoverResource::Size size); - std::unique_ptr createCover(db::TrackId trackId, CoverResource::Size size); + std::unique_ptr createReleaseCover(db::ReleaseId releaseId, ArtworkResource::Size size); + std::unique_ptr createTrackImage(db::TrackId trackId, ArtworkResource::Size size); + std::unique_ptr createArtistImage(db::ArtistId artistId, ArtworkResource::Size size); std::unique_ptr createFilter(const Wt::WString& name, const Wt::WString& tooltip, std::string_view colorStyleClass, bool canDelete = false); std::unique_ptr createFilterCluster(db::ClusterId clusterId, bool canDelete = false); diff --git a/src/lms/ui/explore/ArtistListHelpers.cpp b/src/lms/ui/explore/ArtistListHelpers.cpp index c8dbb038..00ae8580 100644 --- a/src/lms/ui/explore/ArtistListHelpers.cpp +++ b/src/lms/ui/explore/ArtistListHelpers.cpp @@ -28,9 +28,14 @@ namespace lms::ui::ArtistListHelpers { std::unique_ptr createEntry(const db::ObjectPtr& artist) { - auto res{ std::make_unique(Wt::WString::tr("Lms.Explore.Artists.template.entry")) }; - res->bindWidget("name", utils::createArtistAnchor(artist)); + auto entry{ std::make_unique(Wt::WString::tr("Lms.Explore.Artists.template.entry")) }; + entry->bindWidget("name", utils::createArtistAnchor(artist)); - return res; + Wt::WAnchor* anchor{ entry->bindWidget("image", utils::createArtistAnchor(artist, false)) }; + auto image{ utils::createArtistImage(artist->getId(), ArtworkResource::Size::Large) }; + image->addStyleClass("Lms-cover-release Lms-cover-anchor"); + anchor->setImage(std::move(image)); + + return entry; } } // namespace lms::ui::ArtistListHelpers \ No newline at end of file diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index aa2b2fed..8a363f04 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -108,7 +108,7 @@ namespace lms::ui if (!artistId) throw ArtistNotFoundException{}; - const auto similarArtistIds{ core::Service::get()->getSimilarArtists(*artistId, { TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist }, 5) }; + const auto similarArtistIds{ core::Service::get()->getSimilarArtists(*artistId, { TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist }, 6) }; auto transaction{ LmsApp->getDbSession().createReadTransaction() }; diff --git a/src/lms/ui/explore/ReleaseHelpers.cpp b/src/lms/ui/explore/ReleaseHelpers.cpp index 675fea52..20465ae8 100644 --- a/src/lms/ui/explore/ReleaseHelpers.cpp +++ b/src/lms/ui/explore/ReleaseHelpers.cpp @@ -43,9 +43,9 @@ namespace lms::ui::releaseListHelpers { Wt::WAnchor* anchor{ entry->bindWidget("cover", utils::createReleaseAnchor(release, false)) }; - auto cover{ utils::createCover(release->getId(), CoverResource::Size::Large) }; - cover->addStyleClass("Lms-cover-release Lms-cover-anchor"); - anchor->setImage(std::move(cover)); + auto image{ utils::createReleaseCover(release->getId(), ArtworkResource::Size::Large) }; + image->addStyleClass("Lms-cover-release Lms-cover-anchor"); // hack + anchor->setImage(std::move(image)); } auto artistAnchors{ utils::createArtistsAnchorsForRelease(release, artist ? artist->getId() : ArtistId{}, "link-secondary") }; diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index 839c9bb2..0c969d19 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -245,7 +245,7 @@ namespace lms::ui refreshReleaseArtists(release); - bindWidget("cover", utils::createCover(release->getId(), CoverResource::Size::Large)); + bindWidget("cover", utils::createReleaseCover(release->getId(), ArtworkResource::Size::Large)); Wt::WContainerWidget* clusterContainers{ bindNew("clusters") }; { diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index cd118bc3..6ed07c76 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -43,7 +43,7 @@ #include "Utils.hpp" #include "common/Template.hpp" #include "explore/PlayQueueController.hpp" -#include "resource/CoverResource.hpp" +#include "resource/ArtworkResource.hpp" #include "resource/DownloadResource.hpp" namespace lms::ui::TrackListHelpers @@ -173,20 +173,20 @@ namespace lms::ui::TrackListHelpers entry->bindWidget("artists-md", utils::createArtistDisplayNameWithAnchors(track->getArtistDisplayName(), artists)); } + auto image{ utils::createTrackImage(trackId, ArtworkResource::Size::Small) }; + image->addStyleClass("Lms-cover-track"); if (track->getRelease()) { entry->setCondition("if-has-release", true); entry->bindWidget("release", utils::createReleaseAnchor(track->getRelease())); + Wt::WAnchor* anchor{ entry->bindWidget("cover", utils::createReleaseAnchor(release, false)) }; - auto cover{ utils::createCover(release->getId(), CoverResource::Size::Small) }; - cover->addStyleClass("Lms-cover-track Lms-cover-anchor"); // HACK - anchor->setImage(std::move((cover))); + image->addStyleClass("Lms-cover-anchor"); // HACK + anchor->setImage(std::move((image))); } else { - auto cover{ utils::createCover(trackId, CoverResource::Size::Small) }; - cover->addStyleClass("Lms-cover-track"); // HACK - entry->bindWidget("cover", std::move(cover)); + entry->bindWidget("cover", std::move(image)); } entry->bindString("duration", utils::durationToString(track->getDuration()), Wt::TextFormat::Plain); diff --git a/src/lms/ui/resource/ArtworkResource.cpp b/src/lms/ui/resource/ArtworkResource.cpp new file mode 100644 index 00000000..78c9f5d1 --- /dev/null +++ b/src/lms/ui/resource/ArtworkResource.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include "ArtworkResource.hpp" + +#include +#include + +#include "core/Exception.hpp" +#include "core/ILogger.hpp" +#include "core/ITraceLogger.hpp" +#include "core/Service.hpp" +#include "core/String.hpp" +#include "database/Artist.hpp" +#include "database/Image.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "services/artwork/IArtworkService.hpp" + +#include "LmsApplication.hpp" + +#define LOG(severity, message) LMS_LOG(UI, severity, "Image resource: " << message) + +namespace lms::ui +{ + ArtworkResource::ArtworkResource() + { + LmsApp->getScannerEvents().scanComplete.connect(this, [this](const scanner::ScanStats& stats) { + if (stats.nbChanges()) + setChanged(); + }); + } + + ArtworkResource::~ArtworkResource() + { + beingDeleted(); + } + + std::string ArtworkResource::getArtistImageUrl(db::ArtistId artistId, Size size) const + { + std::string url; + + { + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; + + const db::Artist::pointer artist{ db::Artist::find(LmsApp->getDbSession(), artistId) }; + if (artist && artist->getImage()) + url = getArtistIdImageUrl(artistId, size); + } + + if (url.empty()) + url = getDefaultArtistImageUrl(); + + return url; + } + + std::string ArtworkResource::getReleaseCoverUrl(db::ReleaseId releaseId, Size size) const + { + std::string url; + + { + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; + + const db::Release::pointer release{ db::Release::find(LmsApp->getDbSession(), releaseId) }; + if (release) + { + if (release->getImage()) + { + url = getReleaseIdCoverUrl(release->getId(), size); + } + else + { + db::Track::FindParameters params; + params.setRelease(releaseId); + params.setHasEmbeddedImage(true); + params.setRange(db::Range{ 0, 1 }); + + db::Track::find(LmsApp->getDbSession(), params, [&](const db::Track::pointer& track) { + url = getTrackIdImageUrl(track->getId(), size); + }); + } + } + } + + if (url.empty()) + url = getDefaultReleaseCoverUrl(); + + return url; + } + + std::string ArtworkResource::getTrackImageUrl(db::TrackId trackId, Size size) const + { + std::string url; + + { + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; + + const db::Track::pointer track{ db::Track::find(LmsApp->getDbSession(), trackId) }; + if (track) + { + if (track->hasCover()) + url = getTrackIdImageUrl(trackId, size); + else if (const db::Release::pointer release{ track->getRelease() }; release && release->getImage()) + url = getReleaseIdCoverUrl(release->getId(), size); + } + } + + if (url.empty()) + url = getDefaultReleaseCoverUrl(); + + return url; + } + + std::string ArtworkResource::getArtistIdImageUrl(db::ArtistId artistId, Size size) const + { + return url() + "&artistid=" + artistId.toString() + "&size=" + std::to_string(static_cast(size)); + } + + std::string ArtworkResource::getReleaseIdCoverUrl(db::ReleaseId releaseId, Size size) const + { + return url() + "&releaseid=" + releaseId.toString() + "&size=" + std::to_string(static_cast(size)); + } + + std::string ArtworkResource::getTrackIdImageUrl(db::TrackId trackId, Size size) const + { + return url() + "&trackid=" + trackId.toString() + "&size=" + std::to_string(static_cast(size)); + } + + std::string ArtworkResource::getDefaultArtistImageUrl() const + { + return url() + "&type=defaultartistimage"; + } + + std::string ArtworkResource::getDefaultReleaseCoverUrl() const + { + return url() + "&type=defaultreleasecover"; + } + + void ArtworkResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) + { + LMS_SCOPED_TRACE_OVERVIEW("UI", "HandleCoverRequest"); + + // Retrieve parameters + const std::string* artistIdStr = request.getParameter("artistid"); + const std::string* trackIdStr = request.getParameter("trackid"); + const std::string* releaseIdStr = request.getParameter("releaseid"); + const std::string* sizeStr = request.getParameter("size"); + const std::string* typeStr = request.getParameter("type"); + + std::shared_ptr image; + + // Mandatory parameter size + if ((artistIdStr || trackIdStr || releaseIdStr)) + { + if (!sizeStr) + { + LOG(DEBUG, "no size provided!"); + return; + } + + const auto size{ core::stringUtils::readAs(*sizeStr) }; + if (!size || *size > maxSize) + { + LOG(DEBUG, "invalid size provided!"); + return; + } + + if (artistIdStr) + { + LOG(DEBUG, "Requested cover for track " << *artistIdStr << ", size = " << *size); + + const std::optional artistId{ core::stringUtils::readAs(*artistIdStr) }; + if (!artistId) + return; + + image = core::Service::get()->getArtistImage(*artistId, *size); + if (!image) + image = core::Service::get()->getDefaultArtistImage(); + } + else if (releaseIdStr) + { + LOG(DEBUG, "Requested cover for release " << *releaseIdStr << ", size = " << *size); + + const std::optional releaseId{ core::stringUtils::readAs(*releaseIdStr) }; + if (!releaseId) + return; + + image = core::Service::get()->getReleaseCover(*releaseId, *size); + if (!image) + image = core::Service::get()->getDefaultReleaseCover(); + } + else if (trackIdStr) + { + LOG(DEBUG, "Requested cover for track " << *trackIdStr << ", size = " << *size); + + const std::optional trackId{ core::stringUtils::readAs(*trackIdStr) }; + if (!trackId) + { + LOG(DEBUG, "track not found"); + return; + } + + image = core::Service::get()->getTrackImage(*trackId, *size); + if (!image) + image = core::Service::get()->getDefaultReleaseCover(); + } + } + else if (typeStr) + { + if (*typeStr == "defaultreleasecover") + image = core::Service::get()->getDefaultReleaseCover(); + else if (*typeStr == "defaultartistimage") + image = core::Service::get()->getDefaultArtistImage(); + } + + if (image) + { + response.setMimeType(std::string{ image->getMimeType() }); + response.out().write(reinterpret_cast(image->getData()), image->getDataSize()); + } + else + response.setStatus(404); + } +} // namespace lms::ui diff --git a/src/lms/ui/resource/CoverResource.hpp b/src/lms/ui/resource/ArtworkResource.hpp similarity index 61% rename from src/lms/ui/resource/CoverResource.hpp rename to src/lms/ui/resource/ArtworkResource.hpp index b75772d5..c3b40083 100644 --- a/src/lms/ui/resource/CoverResource.hpp +++ b/src/lms/ui/resource/ArtworkResource.hpp @@ -21,18 +21,19 @@ #include +#include "database/ArtistId.hpp" #include "database/ReleaseId.hpp" #include "database/TrackId.hpp" namespace lms::ui { - class CoverResource : public Wt::WResource + class ArtworkResource : public Wt::WResource { public: static const std::size_t maxSize{ 512 }; - CoverResource(); - ~CoverResource(); + ArtworkResource(); + ~ArtworkResource(); enum class Size : std::size_t { @@ -40,11 +41,18 @@ namespace lms::ui Large = 512, }; - std::string getReleaseUrl(db::ReleaseId releaseId, Size size) const; - std::string getTrackUrl(db::TrackId trackId, Size size) const; - std::string getDefaultUrl(Size size) const; + std::string getArtistImageUrl(db::ArtistId artistId, Size size) const; + std::string getReleaseCoverUrl(db::ReleaseId releaseId, Size size) const; + std::string getTrackImageUrl(db::TrackId trackId, Size size) const; private: + std::string getArtistIdImageUrl(db::ArtistId artistId, Size size) const; + std::string getReleaseIdCoverUrl(db::ReleaseId releaseId, Size size) const; + std::string getTrackIdImageUrl(db::TrackId trackId, Size size) const; + + std::string getDefaultArtistImageUrl() const; + std::string getDefaultReleaseCoverUrl() const; + void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; }; } // namespace lms::ui diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp deleted file mode 100644 index 5c8d14ee..00000000 --- a/src/lms/ui/resource/CoverResource.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2014 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#include "CoverResource.hpp" - -#include -#include - -#include "core/Exception.hpp" -#include "core/ILogger.hpp" -#include "core/ITraceLogger.hpp" -#include "core/Service.hpp" -#include "core/String.hpp" -#include "database/Track.hpp" -#include "services/cover/ICoverService.hpp" - -#include "LmsApplication.hpp" - -#define LOG(severity, message) LMS_LOG(UI, severity, "Image resource: " << message) - -namespace lms::ui -{ - CoverResource::CoverResource() - { - LmsApp->getScannerEvents().scanComplete.connect(this, [this](const scanner::ScanStats& stats) { - if (stats.nbChanges()) - setChanged(); - }); - } - - CoverResource::~CoverResource() - { - beingDeleted(); - } - - std::string CoverResource::getReleaseUrl(db::ReleaseId releaseId, Size size) const - { - return url() + "&releaseid=" + releaseId.toString() + "&size=" + std::to_string(static_cast(size)); - } - - std::string CoverResource::getTrackUrl(db::TrackId trackId, Size size) const - { - return url() + "&trackid=" + trackId.toString() + "&size=" + std::to_string(static_cast(size)); - } - - std::string CoverResource::getDefaultUrl(Size size) const - { - return url() + "&size=" + std::to_string(static_cast(size)); - } - - void CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) - { - LMS_SCOPED_TRACE_OVERVIEW("UI", "HandleCoverRequest"); - - // Retrieve parameters - const std::string* trackIdStr = request.getParameter("trackid"); - const std::string* releaseIdStr = request.getParameter("releaseid"); - const std::string* sizeStr = request.getParameter("size"); - - // Mandatory parameter size - if (!sizeStr) - { - LOG(DEBUG, "no size provided!"); - return; - } - - const auto size{ core::stringUtils::readAs(*sizeStr) }; - if (!size || *size > maxSize) - { - LOG(DEBUG, "invalid size provided!"); - return; - } - - std::shared_ptr cover; - - if (trackIdStr) - { - LOG(DEBUG, "Requested cover for track " << *trackIdStr << ", size = " << *size); - - const std::optional trackId{ core::stringUtils::readAs(*trackIdStr) }; - if (!trackId) - { - LOG(DEBUG, "track not found"); - return; - } - - cover = core::Service::get()->getFromTrack(*trackId, *size); - } - else if (releaseIdStr) - { - LOG(DEBUG, "Requested cover for release " << *releaseIdStr << ", size = " << *size); - - const std::optional releaseId{ core::stringUtils::readAs(*releaseIdStr) }; - if (!releaseId) - return; - - cover = core::Service::get()->getFromRelease(*releaseId, *size); - } - else - LOG(DEBUG, "Requested default cover"); - - if (!cover) - cover = core::Service::get()->getDefaultSvgCover(); - - response.setMimeType(std::string{ cover->getMimeType() }); - - response.out().write(reinterpret_cast(cover->getData()), cover->getDataSize()); - } -} // namespace lms::ui diff --git a/src/tools/cover/CMakeLists.txt b/src/tools/cover/CMakeLists.txt index b7131b97..3f80c8a0 100644 --- a/src/tools/cover/CMakeLists.txt +++ b/src/tools/cover/CMakeLists.txt @@ -4,7 +4,7 @@ add_executable(lms-cover ) target_link_libraries(lms-cover PRIVATE - lmsservice-cover + lmsartwork Boost::program_options ) diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index dee31376..db177b01 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -34,7 +34,7 @@ #include "database/Session.hpp" #include "database/Track.hpp" #include "image/Image.hpp" -#include "services/cover/ICoverService.hpp" +#include "services/artwork/IArtworkService.hpp" namespace lms { @@ -51,7 +51,7 @@ namespace lms for (const db::TrackId trackId : trackIds.results) { std::cout << "Getting cover for track id " << trackId.toString() << std::endl; - core::Service::get()->getFromTrack(trackId, width); + core::Service::get()->getTrackImage(trackId, width); } } } // namespace lms @@ -67,7 +67,17 @@ int main(int argc, char* argv[]) core::Service logger{ std::make_unique(std::cout) }; po::options_description desc{ "Allowed options" }; - desc.add_options()("help,h", "print usage message")("conf,c", po::value()->default_value(core::sysconfDirectory / "lms.conf"), "LMS config file")("default-cover,d", po::value(), "Default cover path")("tracks,t", "dump covers for tracks")("size,s", po::value()->default_value(512), "Requested cover size")("quality,q", po::value()->default_value(75), "JPEG quality (1-100)"); + + // clang-format off + desc.add_options() + ("help,h", "print usage message") + ("conf,c", po::value()->default_value(core::sysconfDirectory / "lms.conf"), "LMS config file") + ("default-release-cover,d", po::value(), "Default release cover path") + ("default-artist-image,d", po::value(), "Default artist image") + ("tracks,t", "dump covers for tracks") + ("size,s", po::value()->default_value(512), "Requested cover size") + ("quality,q", po::value()->default_value(75), "JPEG quality (1-100)"); + // clang-format on po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -81,7 +91,7 @@ int main(int argc, char* argv[]) image::init(argv[0]); core::Service config{ core::createConfig(vm["conf"].as()) }; db::Db db{ config->getPath("working-dir") / "lms.db" }; - core::Service coverArtService{ cover::createCoverService(db, vm["default-cover"].as()) }; + core::Service coverArtService{ cover::createArtworkService(db, vm["default-release-cover,"].as(), vm["default-artist-image"].as()) }; coverArtService->setJpegQuality(config->getULong("cover-jpeg-quality", vm["quality"].as()));