From ac69582f4b038b7bf41ee4cae0da462a01028831 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 24 Mar 2024 15:15:36 +0100 Subject: [PATCH] Changed the default cover image for releases --- approot/images/unknown-cover.jpg | Bin 25322 -> 0 bytes approot/images/unknown-cover.svg | 11 ++++ src/libs/image/CMakeLists.txt | 1 + src/libs/image/impl/SvgImage.cpp | 50 ++++++++++++++++++ src/libs/image/impl/SvgImage.hpp | 42 +++++++++++++++ src/libs/image/include/image/Exception.hpp | 12 ++--- .../image/include/image/IEncodedImage.hpp | 21 ++++---- src/libs/image/include/image/IRawImage.hpp | 21 +++----- src/libs/image/include/image/Image.hpp | 34 ++++++++++++ src/libs/services/cover/impl/CoverService.cpp | 49 +++-------------- src/libs/services/cover/impl/CoverService.hpp | 9 ++-- .../include/services/cover/ICoverService.hpp | 4 +- .../impl/entrypoints/MediaRetrieval.cpp | 2 +- src/lms/main.cpp | 4 +- src/lms/ui/resource/CoverResource.cpp | 4 +- src/tools/cover/LmsCover.cpp | 14 ++--- 16 files changed, 187 insertions(+), 91 deletions(-) delete mode 100755 approot/images/unknown-cover.jpg create mode 100755 approot/images/unknown-cover.svg create mode 100644 src/libs/image/impl/SvgImage.cpp create mode 100644 src/libs/image/impl/SvgImage.hpp create mode 100644 src/libs/image/include/image/Image.hpp diff --git a/approot/images/unknown-cover.jpg b/approot/images/unknown-cover.jpg deleted file mode 100755 index 3adc6dc1dc2f55f423d4e703afb7eb1d88758f14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25322 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 diff --git a/approot/images/unknown-cover.svg b/approot/images/unknown-cover.svg new file mode 100755 index 00000000..52ff34f6 --- /dev/null +++ b/approot/images/unknown-cover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/libs/image/CMakeLists.txt b/src/libs/image/CMakeLists.txt index 32391b0f..f6228210 100644 --- a/src/libs/image/CMakeLists.txt +++ b/src/libs/image/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(lmsimage SHARED + impl/SvgImage.cpp ) target_include_directories(lmsimage INTERFACE diff --git a/src/libs/image/impl/SvgImage.cpp b/src/libs/image/impl/SvgImage.cpp new file mode 100644 index 00000000..433c58ad --- /dev/null +++ b/src/libs/image/impl/SvgImage.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 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 "SvgImage.hpp" + +#include +#include "image/Exception.hpp" + +namespace lms::image +{ + std::unique_ptr readSvgFile(const std::filesystem::path& p) + { + if (p.extension() != ".svg") + throw Exception{ "Unexpected file extension: '" + p.extension().string() + "', expected .svg" }; + + std::ifstream ifs{ p.string(), std::ios::binary }; + if (!ifs.is_open()) + throw Exception{ "Cannot open file '" + p.string() + "' for reading purpose" }; + + std::vector data; + // read file content + ifs.seekg(0, std::ios::end); + std::streamsize size = ifs.tellg(); + if (size < 0) + throw Exception{ "Cannot determine file size for '" + p.string() + "'" }; + + ifs.seekg(0, std::ios::beg); + data.resize(size); + if (!ifs.read(reinterpret_cast(data.data()), size)) + throw Exception{ "Cannot read file content for '" + p.string() + "'" }; + + return std::make_unique(std::move(data)); + } +} \ No newline at end of file diff --git a/src/libs/image/impl/SvgImage.hpp b/src/libs/image/impl/SvgImage.hpp new file mode 100644 index 00000000..ce54cb30 --- /dev/null +++ b/src/libs/image/impl/SvgImage.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 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 . + */ + +#pragma once + +#include +#include +#include + +#include "image/IEncodedImage.hpp" + +namespace lms::image +{ + class SvgImage : public IEncodedImage + { + public: + SvgImage(std::vector&& data) : _data{ std::move(data) } {} + + const std::byte* getData() const { return &_data.front(); } + std::size_t getDataSize() const { return _data.size(); } + std::string_view getMimeType() const { return "image/svg+xml"; } + + private: + const std::vector _data; + }; +} \ No newline at end of file diff --git a/src/libs/image/include/image/Exception.hpp b/src/libs/image/include/image/Exception.hpp index 1991be18..7e0d83ba 100644 --- a/src/libs/image/include/image/Exception.hpp +++ b/src/libs/image/include/image/Exception.hpp @@ -23,11 +23,9 @@ namespace lms::image { - class Exception : public core::LmsException - { - public: - using LmsException::LmsException; - }; - + class Exception : public core::LmsException + { + public: + using LmsException::LmsException; + }; } // namespace lms::cover - diff --git a/src/libs/image/include/image/IEncodedImage.hpp b/src/libs/image/include/image/IEncodedImage.hpp index 0613f214..89685143 100644 --- a/src/libs/image/include/image/IEncodedImage.hpp +++ b/src/libs/image/include/image/IEncodedImage.hpp @@ -24,16 +24,15 @@ namespace lms::image { - using ImageSize = std::size_t; + using ImageSize = std::size_t; - class IEncodedImage - { - public: - virtual ~IEncodedImage() = default; - - virtual const std::byte* getData() const = 0; - virtual std::size_t getDataSize() const = 0; - virtual std::string_view getMimeType() const = 0; - }; -} + class IEncodedImage + { + public: + virtual ~IEncodedImage() = default; + virtual const std::byte* getData() const = 0; + virtual std::size_t getDataSize() const = 0; + virtual std::string_view getMimeType() const = 0; + }; +} \ No newline at end of file diff --git a/src/libs/image/include/image/IRawImage.hpp b/src/libs/image/include/image/IRawImage.hpp index e0949b2c..178da958 100644 --- a/src/libs/image/include/image/IRawImage.hpp +++ b/src/libs/image/include/image/IRawImage.hpp @@ -19,23 +19,16 @@ #pragma once -#include -#include - #include "image/IEncodedImage.hpp" namespace lms::image { - class IRawImage - { - public: - virtual ~IRawImage() = default; - virtual void resize(ImageSize width) = 0; - virtual std::unique_ptr encodeToJPEG(unsigned quality) const = 0; - }; - - void init(const std::filesystem::path& path); - std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize); - std::unique_ptr decodeImage(const std::filesystem::path& path); + class IRawImage + { + public: + virtual ~IRawImage() = default; + virtual void resize(ImageSize width) = 0; + virtual std::unique_ptr encodeToJPEG(unsigned quality) const = 0; + }; } diff --git a/src/libs/image/include/image/Image.hpp b/src/libs/image/include/image/Image.hpp new file mode 100644 index 00000000..a0ee4182 --- /dev/null +++ b/src/libs/image/include/image/Image.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 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 . + */ + +#pragma once + +#include +#include + +#include "image/IEncodedImage.hpp" +#include "image/IRawImage.hpp" + +namespace lms::image +{ + void init(const std::filesystem::path& path); + std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize); + std::unique_ptr decodeImage(const std::filesystem::path& path); + std::unique_ptr readSvgFile(const std::filesystem::path& path); +} \ No newline at end of file diff --git a/src/libs/services/cover/impl/CoverService.cpp b/src/libs/services/cover/impl/CoverService.cpp index 49540e48..605e5a92 100644 --- a/src/libs/services/cover/impl/CoverService.cpp +++ b/src/libs/services/cover/impl/CoverService.cpp @@ -30,7 +30,7 @@ #include "database/Track.hpp" #include "image/Exception.hpp" -#include "image/IRawImage.hpp" +#include "image/Image.hpp" #include "core/IConfig.hpp" #include "core/ILogger.hpp" #include "core/Path.hpp" @@ -107,18 +107,16 @@ namespace lms::cover } } - std::unique_ptr createCoverService(db::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath) + std::unique_ptr createCoverService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath) { - return std::make_unique(db, execPath, defaultCoverPath); + return std::make_unique(db, defaultSvgCoverPath); } using namespace image; CoverService::CoverService(db::Db& db, - const std::filesystem::path& execPath, - const std::filesystem::path& defaultCoverPath) + const std::filesystem::path& defaultSvgCoverPath) : _db{ db } - , _defaultCoverPath{ defaultCoverPath } , _maxCacheSize{ core::Service::get()->getULong("cover-max-cache-size", 30) * 1000 * 1000 } , _maxFileSize{ core::Service::get()->getULong("cover-max-file-size", 10) * 1000 * 1000 } , _preferredFileNames{ constructPreferredFileNames() } @@ -126,25 +124,12 @@ namespace lms::cover { setJpegQuality(core::Service::get()->getULong("cover-jpeg-quality", 75)); - LMS_LOG(COVER, INFO, "Default cover path = '" << _defaultCoverPath.string() << "'"); + LMS_LOG(COVER, INFO, "Default cover path = '" << defaultSvgCoverPath.string() << "'"); LMS_LOG(COVER, INFO, "Max cache size = " << _maxCacheSize); LMS_LOG(COVER, INFO, "Max file size = " << _maxFileSize); LMS_LOG(COVER, INFO, "Preferred file names: " << core::stringUtils::joinStrings(_preferredFileNames, ",")); -#if LMS_SUPPORT_IMAGE_GM - GraphicsMagick::init(execPath); -#else - (void)execPath; -#endif - - try - { - getDefault(512); - } - catch (const image::Exception& e) - { - throw core::LmsException("Cannot read default cover file '" + _defaultCoverPath.string() + "': " + e.what()); - } + _defaultCover = image::readSvgFile(defaultSvgCoverPath); // may throw } std::unique_ptr CoverService::getFromAvMediaFile(const av::IAudioFile& input, ImageSize width) const @@ -189,27 +174,9 @@ namespace lms::cover return image; } - std::shared_ptr CoverService::getDefault(ImageSize width) + std::shared_ptr CoverService::getDefaultSvgCover() { - { - std::shared_lock lock{ _cacheMutex }; - - if (auto it{ _defaultCoverCache.find(width) }; it != std::cend(_defaultCoverCache)) - return it->second; - } - - { - std::unique_lock lock{ _cacheMutex }; - - if (auto it{ _defaultCoverCache.find(width) }; it != std::cend(_defaultCoverCache)) - return it->second; - - std::shared_ptr image{ getFromCoverFile(_defaultCoverPath, width) }; - _defaultCoverCache[width] = image; - LMS_LOG(COVER, DEBUG, "Default cache entries = " << _defaultCoverCache.size()); - - return image; - } + return _defaultCover; } std::unique_ptr CoverService::getFromDirectory(const std::filesystem::path& directory, ImageSize width, const std::vector& preferredFileNames, bool allowPickRandom) const diff --git a/src/libs/services/cover/impl/CoverService.hpp b/src/libs/services/cover/impl/CoverService.hpp index 30f24bad..40fffe4c 100644 --- a/src/libs/services/cover/impl/CoverService.hpp +++ b/src/libs/services/cover/impl/CoverService.hpp @@ -84,16 +84,16 @@ namespace lms::cover class CoverService : public ICoverService { public: - CoverService(db::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath); + CoverService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath); + private: CoverService(const CoverService&) = delete; CoverService& operator=(const CoverService&) = delete; - private: 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 getDefault(image::ImageSize width) override; + std::shared_ptr getDefaultSvgCover() override; void flushCache() override; void setJpegQuality(unsigned quality) override; @@ -112,7 +112,7 @@ namespace lms::cover std::shared_mutex _cacheMutex; std::unordered_map> _cache; - std::unordered_map> _defaultCoverCache; + std::shared_ptr _defaultCover; std::atomic _cacheMisses{}; std::atomic _cacheHits{}; std::size_t _cacheSize{}; @@ -120,7 +120,6 @@ namespace lms::cover void saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr image); std::shared_ptr loadFromCache(const CacheEntryDesc& entryDesc); - const std::filesystem::path _defaultCoverPath; const std::size_t _maxCacheSize; static inline const std::vector _fileExtensions{ ".jpg", ".jpeg", ".png", ".bmp" }; // TODO parametrize const std::size_t _maxFileSize; diff --git a/src/libs/services/cover/include/services/cover/ICoverService.hpp b/src/libs/services/cover/include/services/cover/ICoverService.hpp index 4825cc2d..6c740d23 100644 --- a/src/libs/services/cover/include/services/cover/ICoverService.hpp +++ b/src/libs/services/cover/include/services/cover/ICoverService.hpp @@ -43,14 +43,14 @@ namespace lms::cover virtual std::shared_ptr getFromRelease(db::ReleaseId releaseId, image::ImageSize width) = 0; virtual std::shared_ptr getFromArtist(db::ArtistId artistId, image::ImageSize width) = 0; - virtual std::shared_ptr getDefault(image::ImageSize width) = 0; + virtual std::shared_ptr getDefaultSvgCover() = 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& execPath, const std::filesystem::path& defaultCoverPath); + std::unique_ptr createCoverService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath); } // namespace lms::coverArt diff --git a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp index 2c729447..7d4ef6b2 100644 --- a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp @@ -272,7 +272,7 @@ namespace lms::api::subsonic cover = core::Service::get()->getFromArtist(*artistId, size); if (!cover && context.enableDefaultCover && !artistId) - cover = core::Service::get()->getDefault(size); + cover = core::Service::get()->getDefaultSvgCover(); if (!cover) { diff --git a/src/lms/main.cpp b/src/lms/main.cpp index ed32dba8..4380005f 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -25,7 +25,7 @@ #include #include -#include "image/IRawImage.hpp" +#include "image/Image.hpp" #include "services/auth/IAuthTokenService.hpp" #include "services/auth/IPasswordService.hpp" #include "services/auth/IEnvService.hpp" @@ -310,7 +310,7 @@ namespace lms throw core::LmsException{ "Bad value '" + authenticationBackend + "' for 'authentication-backend'" }; image::init(argv[0]); - core::Service coverService{ cover::createCoverService(database, argv[0], server.appRoot() + "/images/unknown-cover.jpg") }; + core::Service coverService{ cover::createCoverService(database, server.appRoot() + "/images/unknown-cover.svg") }; core::Service recommendationService{ recommendation::createRecommendationService(database) }; core::Service playlistGeneratorService{ recommendation::createPlaylistGeneratorService(database, *recommendationService.get()) }; core::Service scannerService{ scanner::createScannerService(database) }; diff --git a/src/lms/ui/resource/CoverResource.cpp b/src/lms/ui/resource/CoverResource.cpp index d0e7d0bb..6768a560 100644 --- a/src/lms/ui/resource/CoverResource.cpp +++ b/src/lms/ui/resource/CoverResource.cpp @@ -98,7 +98,7 @@ namespace lms::ui cover = core::Service::get()->getFromTrack(*trackId, *size); if (!cover) - cover = core::Service::get()->getDefault(*size); + cover = core::Service::get()->getDefaultSvgCover(); } else if (releaseIdStr) { @@ -110,7 +110,7 @@ namespace lms::ui cover = core::Service::get()->getFromRelease(*releaseId, *size); if (!cover) - cover = core::Service::get()->getDefault(*size); + cover = core::Service::get()->getDefaultSvgCover(); } else { diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index 2867cacf..b7b22d1a 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -24,15 +24,16 @@ #include -#include "database/Db.hpp" -#include "database/Release.hpp" -#include "database/Session.hpp" -#include "database/Track.hpp" -#include "services/cover/ICoverService.hpp" #include "core/IConfig.hpp" #include "core/ILogger.hpp" #include "core/Service.hpp" #include "core/StreamLogger.hpp" +#include "database/Db.hpp" +#include "database/Release.hpp" +#include "database/Session.hpp" +#include "database/Track.hpp" +#include "image/Image.hpp" +#include "services/cover/ICoverService.hpp" namespace lms { @@ -83,9 +84,10 @@ int main(int argc, char* argv[]) return EXIT_SUCCESS; } + 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, argv[0], vm["default-cover"].as()) }; + core::Service coverArtService{ cover::createCoverService(db, vm["default-cover"].as()) }; coverArtService->setJpegQuality(config->getULong("cover-jpeg-quality", vm["quality"].as()));