From 0ce423fbf8c77812495ed81597b3257f1c27711b Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 5 Dec 2024 19:57:56 +0100 Subject: [PATCH 01/20] exclude .cache (for clangd) --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b8617e34..e1698464 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ CMakeCache.txt CMakeFiles/ build/ -.vscode/ \ No newline at end of file +.cache/ +.vscode/ From d5ba6d83dc5e66f180d392ea165f120b1cff7479 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 5 Dec 2024 19:58:18 +0100 Subject: [PATCH 02/20] Relaxed some clang tidy checks --- .clang-tidy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index aca1b6d3..e901f557 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -12,5 +12,9 @@ CheckOptions: - key: cppcoreguidelines-avoid-do-while.IgnoreMacros value: '1' - key: performance-unnecessary-value-param.AllowedTypes - value: "shared_ptr" + value: "shared_ptr;ObjectPtr" + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted + value: '1' + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: '1' ... From 76b9c1fe104fddaa6797990aea9eab5aaeab4fc0 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 5 Dec 2024 20:01:55 +0100 Subject: [PATCH 03/20] Exposed metadata for each embedded picture --- src/libs/av/impl/AudioFile.cpp | 17 +++++++++++------ src/libs/av/impl/AudioFile.hpp | 11 ++++------- src/libs/av/include/av/IAudioFile.hpp | 8 +++----- .../services/artwork/impl/ArtworkService.cpp | 6 ++---- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/libs/av/impl/AudioFile.cpp b/src/libs/av/impl/AudioFile.cpp index 31ad7267..1067fb7c 100644 --- a/src/libs/av/impl/AudioFile.cpp +++ b/src/libs/av/impl/AudioFile.cpp @@ -34,6 +34,8 @@ extern "C" #include "core/ILogger.hpp" #include "core/String.hpp" +#include "av/Types.hpp" + namespace lms::av { namespace @@ -43,9 +45,9 @@ namespace lms::av std::array buf = { 0 }; if (::av_strerror(error, buf.data(), buf.size()) == 0) - return &buf[0]; - else - return "Unknown error"; + return buf.data(); + + return "Unknown error"; } class AudioFileException : public Exception @@ -158,7 +160,7 @@ namespace lms::av { ContainerInfo info; info.bitrate = _context->bit_rate; - info.duration = std::chrono::milliseconds{ _context->duration == AV_NOPTS_VALUE ? 0 : _context->duration / AV_TIME_BASE * 1000 }; + info.duration = std::chrono::milliseconds{ _context->duration == AV_NOPTS_VALUE ? 0 : _context->duration / AV_TIME_BASE * 1'000 }; info.name = _context->iformat->name; return info; @@ -237,7 +239,7 @@ namespace lms::av return false; } - void AudioFile::visitAttachedPictures(std::function func) const + void AudioFile::visitAttachedPictures(std::function func) const { static const std::unordered_map codecMimeMap{ { AV_CODEC_ID_BMP, "image/x-bmp" }, @@ -262,6 +264,9 @@ namespace lms::av continue; } + MetadataMap metadata; + getMetaDataFromDictionnary(avstream->metadata, metadata); + Picture picture; auto itMime = codecMimeMap.find(avstream->codecpar->codec_id); @@ -280,7 +285,7 @@ namespace lms::av picture.data = reinterpret_cast(pkt.data); picture.dataSize = pkt.size; - func(picture); + func(picture, metadata); } } diff --git a/src/libs/av/impl/AudioFile.hpp b/src/libs/av/impl/AudioFile.hpp index b1b7f1b6..41cc3ab0 100644 --- a/src/libs/av/impl/AudioFile.hpp +++ b/src/libs/av/impl/AudioFile.hpp @@ -25,12 +25,13 @@ struct AVFormatContext; namespace lms::av { - class AudioFile final : public IAudioFile { public: AudioFile(const std::filesystem::path& p); - ~AudioFile(); + ~AudioFile() override; + AudioFile(const AudioFile&) = delete; + AudioFile& operator=(const AudioFile&) = delete; const std::filesystem::path& getPath() const override; ContainerInfo getContainerInfo() const override; @@ -39,16 +40,12 @@ namespace lms::av std::optional getBestStreamInfo() const override; std::optional getBestStreamIndex() const override; bool hasAttachedPictures() const override; - void visitAttachedPictures(std::function func) const override; + void visitAttachedPictures(std::function func) const override; private: - AudioFile(const AudioFile&) = delete; - AudioFile& operator=(const AudioFile&) = delete; - std::optional getStreamInfo(std::size_t streamIndex) const; const std::filesystem::path _p; AVFormatContext* _context{}; }; - } // namespace lms::av diff --git a/src/libs/av/include/av/IAudioFile.hpp b/src/libs/av/include/av/IAudioFile.hpp index d6618da6..244fef58 100644 --- a/src/libs/av/include/av/IAudioFile.hpp +++ b/src/libs/av/include/av/IAudioFile.hpp @@ -28,8 +28,6 @@ #include #include -#include "Types.hpp" - namespace lms::av { // List should be sync with the codecs shipped in the lms's docker version @@ -62,14 +60,14 @@ namespace lms::av struct Picture { std::string mimeType; - const std::byte* data{}; + const std::byte* data{}; // valid as long as IAudioFile exists std::size_t dataSize{}; }; struct ContainerInfo { std::size_t bitrate{}; - std::string name{}; + std::string name; std::chrono::milliseconds duration{}; }; @@ -99,7 +97,7 @@ namespace lms::av virtual std::optional getBestStreamInfo() const = 0; // none if failure/unknown virtual std::optional getBestStreamIndex() const = 0; // none if failure/unknown virtual bool hasAttachedPictures() const = 0; - virtual void visitAttachedPictures(std::function func) const = 0; + virtual void visitAttachedPictures(std::function func) const = 0; }; std::unique_ptr parseAudioFile(const std::filesystem::path& p); diff --git a/src/libs/services/artwork/impl/ArtworkService.cpp b/src/libs/services/artwork/impl/ArtworkService.cpp index 0f3ae1f1..839e4556 100644 --- a/src/libs/services/artwork/impl/ArtworkService.cpp +++ b/src/libs/services/artwork/impl/ArtworkService.cpp @@ -20,11 +20,9 @@ #include "ArtworkService.hpp" #include "av/IAudioFile.hpp" +#include "av/Types.hpp" #include "core/IConfig.hpp" #include "core/ILogger.hpp" -#include "core/Path.hpp" -#include "core/Random.hpp" -#include "core/String.hpp" #include "core/Utils.hpp" #include "database/Artist.hpp" #include "database/Db.hpp" @@ -71,7 +69,7 @@ namespace lms::cover { std::unique_ptr image; - input.visitAttachedPictures([&](const av::Picture& picture) { + input.visitAttachedPictures([&](const av::Picture& picture, const av::IAudioFile::MetadataMap& /* metadata */) { if (image) return; From 45175a721cd5abe51ef00b4f1aacf9db7abcc391 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 5 Dec 2024 20:09:14 +0100 Subject: [PATCH 04/20] Various minor cleanup --- src/libs/av/impl/Transcoder.cpp | 7 ++-- src/libs/av/impl/Transcoder.hpp | 3 -- .../av/impl/TranscodingResourceHandler.cpp | 24 ++++++------- .../av/impl/TranscodingResourceHandler.hpp | 7 ++-- .../av/include/av/TranscodingParameters.hpp | 4 +-- src/libs/core/bench/TraceLoggerBench.cpp | 4 +-- src/libs/core/impl/ArchiveZipper.cpp | 16 ++++----- src/libs/core/impl/ArchiveZipper.hpp | 3 +- src/libs/core/impl/ChildProcess.hpp | 5 ++- src/libs/core/impl/ChildProcessManager.hpp | 3 +- src/libs/core/impl/Config.cpp | 1 - src/libs/core/impl/Config.hpp | 4 +-- src/libs/core/impl/TraceLogger.cpp | 4 ++- src/libs/core/test/String.cpp | 2 +- src/libs/database/impl/Artist.cpp | 12 +++---- src/libs/database/impl/Db.cpp | 1 - src/libs/database/impl/Image.cpp | 4 +-- src/libs/database/include/database/Artist.hpp | 18 +++++----- src/libs/image/impl/SvgImage.cpp | 1 + src/libs/image/impl/SvgImage.hpp | 8 ++--- src/libs/image/impl/stb/JPEGImage.cpp | 6 ++-- src/libs/image/impl/stb/RawImage.cpp | 4 +-- src/libs/image/impl/stb/RawImage.hpp | 8 +++-- src/libs/image/include/image/IRawImage.hpp | 2 ++ src/libs/metadata/impl/AvFormatTagReader.cpp | 6 +--- src/libs/metadata/impl/AvFormatTagReader.hpp | 5 ++- .../services/scanner/impl/ScannerService.hpp | 3 +- src/libs/subsonic/impl/ProtocolVersion.hpp | 3 ++ src/libs/subsonic/impl/SubsonicId.cpp | 17 ---------- src/libs/subsonic/impl/SubsonicId.hpp | 8 ----- src/libs/subsonic/impl/SubsonicResource.cpp | 34 +++++++++---------- src/libs/subsonic/impl/SubsonicResource.hpp | 3 -- src/libs/subsonic/impl/SubsonicResponse.cpp | 1 - .../impl/SubsonicResponseAllocator.hpp | 4 +-- src/lms/ui/admin/UserView.cpp | 6 ++-- 35 files changed, 104 insertions(+), 137 deletions(-) diff --git a/src/libs/av/impl/Transcoder.cpp b/src/libs/av/impl/Transcoder.cpp index 760376ef..d9c9821f 100644 --- a/src/libs/av/impl/Transcoder.cpp +++ b/src/libs/av/impl/Transcoder.cpp @@ -25,9 +25,10 @@ #include "core/IChildProcessManager.hpp" #include "core/IConfig.hpp" #include "core/ILogger.hpp" -#include "core/Path.hpp" #include "core/Service.hpp" +#include "av/Types.hpp" + namespace lms::av::transcoding { @@ -81,7 +82,7 @@ namespace lms::av::transcoding { if (!std::filesystem::exists(_inputParameters.trackPath)) throw Exception{ "File '" + _inputParameters.trackPath.string() + "' does not exist!" }; - else if (!std::filesystem::is_regular_file(_inputParameters.trackPath)) + if (!std::filesystem::is_regular_file(_inputParameters.trackPath)) throw Exception{ "File '" + _inputParameters.trackPath.string() + "' is not regular!" }; } catch (const std::filesystem::filesystem_error& e) @@ -108,7 +109,7 @@ namespace lms::av::transcoding args.emplace_back("-ss"); std::ostringstream oss; - oss << std::fixed << std::showpoint << std::setprecision(3) << (_outputParameters.offset.count() / float{ 1000 }); + oss << std::fixed << std::showpoint << std::setprecision(3) << (_outputParameters.offset.count() / float{ 1'000 }); args.emplace_back(oss.str()); } diff --git a/src/libs/av/impl/Transcoder.hpp b/src/libs/av/impl/Transcoder.hpp index 4e24c4f3..3b226dff 100644 --- a/src/libs/av/impl/Transcoder.hpp +++ b/src/libs/av/impl/Transcoder.hpp @@ -19,11 +19,9 @@ #pragma once -#include #include #include "av/TranscodingParameters.hpp" -#include "av/Types.hpp" namespace lms::core { @@ -37,7 +35,6 @@ namespace lms::av::transcoding public: Transcoder(const InputParameters& inputParameters, const OutputParameters& outputParameters); ~Transcoder(); - Transcoder(const Transcoder&) = delete; Transcoder& operator=(const Transcoder&) = delete; Transcoder(Transcoder&&) = delete; diff --git a/src/libs/av/impl/TranscodingResourceHandler.cpp b/src/libs/av/impl/TranscodingResourceHandler.cpp index 09932835..611c87c8 100644 --- a/src/libs/av/impl/TranscodingResourceHandler.cpp +++ b/src/libs/av/impl/TranscodingResourceHandler.cpp @@ -59,7 +59,7 @@ namespace lms::av::transcoding { LMS_LOG(TRANSCODING, DEBUG, "Writing " << _bytesReadyCount << " bytes back to client"); - response.out().write(reinterpret_cast(&_buffer[0]), _bytesReadyCount); + response.out().write(reinterpret_cast(_buffer.data()), _bytesReadyCount); _totalServedByteCount += _bytesReadyCount; _bytesReadyCount = 0; } @@ -78,24 +78,22 @@ namespace lms::av::transcoding return continuation; } - else + + // pad with 0 if necessary as duration may not be accurate + if (_estimatedContentLength && *_estimatedContentLength > _totalServedByteCount) { - // pad with 0 if necessary as duration may not be accurate - if (_estimatedContentLength && *_estimatedContentLength > _totalServedByteCount) - { - const std::size_t padSize{ *_estimatedContentLength - _totalServedByteCount }; + const std::size_t padSize{ *_estimatedContentLength - _totalServedByteCount }; - LMS_LOG(TRANSCODING, DEBUG, "Adding " << padSize << " padding bytes"); + LMS_LOG(TRANSCODING, DEBUG, "Adding " << padSize << " padding bytes"); - for (std::size_t i{}; i < padSize; ++i) - response.out().put(0); + for (std::size_t i{}; i < padSize; ++i) + response.out().put(0); - _totalServedByteCount += padSize; - } - - LMS_LOG(TRANSCODING, DEBUG, "Transcoding finished. Total served byte count = " << _totalServedByteCount); + _totalServedByteCount += padSize; } + LMS_LOG(TRANSCODING, DEBUG, "Transcoding finished. Total served byte count = " << _totalServedByteCount); + return {}; } } // namespace lms::av::transcoding diff --git a/src/libs/av/impl/TranscodingResourceHandler.hpp b/src/libs/av/impl/TranscodingResourceHandler.hpp index 037ebe16..6e5aa516 100644 --- a/src/libs/av/impl/TranscodingResourceHandler.hpp +++ b/src/libs/av/impl/TranscodingResourceHandler.hpp @@ -20,7 +20,6 @@ #pragma once #include -#include #include #include "av/TranscodingParameters.hpp" @@ -34,9 +33,13 @@ namespace lms::av::transcoding { public: TranscodingResourceHandler(const InputParameters& inputParameters, const OutputParameters& outputParameters, bool estimateContentLength); + ~TranscodingResourceHandler() override = default; + + TranscodingResourceHandler(const TranscodingResourceHandler&) = delete; + TranscodingResourceHandler& operator=(const TranscodingResourceHandler&) = delete; private: - Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& reponse) override; + Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; void abort() override{}; static constexpr std::size_t _chunkSize{ 262'144 }; diff --git a/src/libs/av/include/av/TranscodingParameters.hpp b/src/libs/av/include/av/TranscodingParameters.hpp index cc2a9a79..a2f9516b 100644 --- a/src/libs/av/include/av/TranscodingParameters.hpp +++ b/src/libs/av/include/av/TranscodingParameters.hpp @@ -23,8 +23,6 @@ #include #include -#include "Types.hpp" - namespace lms::av::transcoding { struct InputParameters @@ -47,7 +45,7 @@ namespace lms::av::transcoding struct OutputParameters { OutputFormat format; - std::size_t bitrate{ 128000 }; + std::size_t bitrate{ 128'000 }; std::optional stream; // Id of the stream to be transcoded (auto detect by default) std::chrono::milliseconds offset{ 0 }; bool stripMetadata{ true }; diff --git a/src/libs/core/bench/TraceLoggerBench.cpp b/src/libs/core/bench/TraceLoggerBench.cpp index 1134fb42..ac3eb591 100644 --- a/src/libs/core/bench/TraceLoggerBench.cpp +++ b/src/libs/core/bench/TraceLoggerBench.cpp @@ -29,8 +29,8 @@ namespace lms::core { // The trace logger is meant to built/destroyed once - Service logger{ std::make_unique(std::cout, logging::StreamLogger::allSeverities) }; - Service traceLogger{ tracing::createTraceLogger(tracing::Level::Overview) }; + const Service logger{ std::make_unique(std::cout, logging::StreamLogger::allSeverities) }; + const Service traceLogger{ tracing::createTraceLogger(tracing::Level::Overview) }; static void BM_TraceLogger_Overview(benchmark::State& state) { diff --git a/src/libs/core/impl/ArchiveZipper.cpp b/src/libs/core/impl/ArchiveZipper.cpp index 5f3fcecd..b677ac2e 100644 --- a/src/libs/core/impl/ArchiveZipper.cpp +++ b/src/libs/core/impl/ArchiveZipper.cpp @@ -217,14 +217,14 @@ namespace lms::zip if (!std::filesystem::is_regular_file(entry.filePath)) throw FileException{ entry.filePath, "not a regular file" }; - ArchiveEntryPtr archiveEntry{ archive_entry_new() }; + ArchiveEntryPtr archiveEntry{ ::archive_entry_new() }; if (!archiveEntry) throw Exception{ "Cannot create archive entry control struct" }; - archive_entry_set_pathname(archiveEntry.get(), entry.fileName.c_str()); - archive_entry_set_size(archiveEntry.get(), std::filesystem::file_size(entry.filePath)); - archive_entry_set_mode(archiveEntry.get(), permsToMode(std::filesystem::status(entry.filePath).permissions())); - archive_entry_set_filetype(archiveEntry.get(), AE_IFREG); + ::archive_entry_set_pathname(archiveEntry.get(), entry.fileName.c_str()); + ::archive_entry_set_size(archiveEntry.get(), std::filesystem::file_size(entry.filePath)); + ::archive_entry_set_mode(archiveEntry.get(), permsToMode(std::filesystem::status(entry.filePath).permissions())); + ::archive_entry_set_filetype(archiveEntry.get(), AE_IFREG); return archiveEntry; } @@ -256,7 +256,7 @@ namespace lms::zip if (!ifs.seekg(_currentEntryOffset, std::ios::beg)) throw FileException{ _currentEntry->filePath, "seek failed", errno }; - if (!ifs.read(reinterpret_cast(&_readBuffer[0]), bytesToRead)) + if (!ifs.read(reinterpret_cast(_readBuffer.data()), bytesToRead)) throw FileException{ _currentEntry->filePath, "read failed", errno }; const std::uint64_t actualBytesRead{ static_cast(ifs.gcount()) }; @@ -266,7 +266,7 @@ namespace lms::zip std::uint64_t remainingBytesToWrite{ actualBytesRead }; while (remainingBytesToWrite > 0) { - const auto writtenBytes{ archive_write_data(_archive.get(), &_readBuffer[actualBytesRead - remainingBytesToWrite], remainingBytesToWrite) }; + const auto writtenBytes{ ::archive_write_data(_archive.get(), &_readBuffer[actualBytesRead - remainingBytesToWrite], remainingBytesToWrite) }; if (writtenBytes < 0) throw ArchiveException{ _archive.get() }; @@ -283,7 +283,7 @@ namespace lms::zip { if (!_currentOutputStream) { - archive_set_error(_archive.get(), EIO, "IO error: operation cancelled"); + ::archive_set_error(_archive.get(), EIO, "IO error: operation cancelled"); return -1; } diff --git a/src/libs/core/impl/ArchiveZipper.hpp b/src/libs/core/impl/ArchiveZipper.hpp index 5d5b97e5..0166dbe4 100644 --- a/src/libs/core/impl/ArchiveZipper.hpp +++ b/src/libs/core/impl/ArchiveZipper.hpp @@ -35,7 +35,8 @@ namespace lms::zip class ArchiveZipper : public IZipper { public: - ArchiveZipper(const EntryContainer& files); + ArchiveZipper(const EntryContainer& entries); + ~ArchiveZipper() = default; ArchiveZipper(const ArchiveZipper&) = delete; ArchiveZipper& operator=(const ArchiveZipper&) = delete; diff --git a/src/libs/core/impl/ChildProcess.hpp b/src/libs/core/impl/ChildProcess.hpp index a1628e14..e1053c68 100644 --- a/src/libs/core/impl/ChildProcess.hpp +++ b/src/libs/core/impl/ChildProcess.hpp @@ -36,8 +36,11 @@ namespace lms::core class ChildProcess : public IChildProcess { public: - ~ChildProcess(); ChildProcess(boost::asio::io_context& ioContext, const std::filesystem::path& path, const Args& args); + ~ChildProcess() override; + + ChildProcess(const ChildProcess&) = delete; + ChildProcess& operator=(const ChildProcess&) = delete; private: void asyncRead(std::byte* data, std::size_t bufferSize, ReadCallback callback) override; diff --git a/src/libs/core/impl/ChildProcessManager.hpp b/src/libs/core/impl/ChildProcessManager.hpp index d7e5bc29..4a78ff8a 100644 --- a/src/libs/core/impl/ChildProcessManager.hpp +++ b/src/libs/core/impl/ChildProcessManager.hpp @@ -20,7 +20,6 @@ #pragma once #include -#include #include @@ -32,7 +31,7 @@ namespace lms::core { public: ChildProcessManager(boost::asio::io_context& ioContext); - ~ChildProcessManager() = default; + ~ChildProcessManager() override = default; ChildProcessManager(const ChildProcessManager&) = delete; ChildProcessManager(ChildProcessManager&&) = delete; diff --git a/src/libs/core/impl/Config.cpp b/src/libs/core/impl/Config.cpp index c4523171..eef30ad4 100644 --- a/src/libs/core/impl/Config.cpp +++ b/src/libs/core/impl/Config.cpp @@ -20,7 +20,6 @@ #include "Config.hpp" #include "core/Exception.hpp" -#include "core/ILogger.hpp" namespace lms::core { diff --git a/src/libs/core/impl/Config.hpp b/src/libs/core/impl/Config.hpp index b1d323ff..03bd4737 100644 --- a/src/libs/core/impl/Config.hpp +++ b/src/libs/core/impl/Config.hpp @@ -29,13 +29,14 @@ namespace lms::core { public: Config(const std::filesystem::path& p); - ~Config() = default; + ~Config() override = default; Config(const Config&) = delete; Config& operator=(const Config&) = delete; Config(Config&&) = delete; Config& operator=(Config&&) = delete; + private: // Default values are returned in case of setting not found std::string_view getString(std::string_view setting, std::string_view def = "") override; void visitStrings(std::string_view setting, std::function _func, std::initializer_list defs) override; @@ -44,7 +45,6 @@ namespace lms::core long getLong(std::string_view setting, long def = 0) override; bool getBool(std::string_view setting, bool def = false) override; - private: libconfig::Config _config; }; } // namespace lms::core \ No newline at end of file diff --git a/src/libs/core/impl/TraceLogger.cpp b/src/libs/core/impl/TraceLogger.cpp index 88b0f269..af521eb2 100644 --- a/src/libs/core/impl/TraceLogger.cpp +++ b/src/libs/core/impl/TraceLogger.cpp @@ -44,6 +44,8 @@ namespace lms::core::tracing private: CurrentThreadUnregisterer(const CurrentThreadUnregisterer&) = delete; CurrentThreadUnregisterer& operator=(const CurrentThreadUnregisterer&) = delete; + CurrentThreadUnregisterer(CurrentThreadUnregisterer&&) = delete; + CurrentThreadUnregisterer& operator=(CurrentThreadUnregisterer&&) = delete; TraceLogger* _logger; }; @@ -295,7 +297,7 @@ namespace lms::core::tracing oss << threadId; std::istringstream iss{ oss.str() }; - std::uint64_t id; + std::uint64_t id{}; iss >> id; return static_cast(id); diff --git a/src/libs/core/test/String.cpp b/src/libs/core/test/String.cpp index 2e9789ef..3d66bb99 100644 --- a/src/libs/core/test/String.cpp +++ b/src/libs/core/test/String.cpp @@ -167,7 +167,7 @@ namespace lms::core::stringUtils::tests std::string expectedOutput; }; - TestCase tests[]{ + const TestCase tests[]{ { { "" }, ';', '\\', "" }, { { ";" }, ';', '\\', "\\;" }, { { ";;" }, ';', '\\', "\\;\\;" }, diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/database/impl/Artist.cpp index 9ecddfe7..1b8a2cca 100644 --- a/src/libs/database/impl/Artist.cpp +++ b/src/libs/database/impl/Artist.cpp @@ -29,7 +29,6 @@ #include "database/Track.hpp" #include "database/User.hpp" -#include "EnumSetTraits.hpp" #include "IdTypeTraits.hpp" #include "SqlQuery.hpp" #include "Utils.hpp" @@ -182,16 +181,16 @@ namespace lms::db } } // namespace - Artist::Artist(const std::string& name, const std::optional& MBID) - : _MBID{ MBID ? MBID->getAsString() : "" } + Artist::Artist(const std::string& name, const std::optional& mbid) + : _mbid{ mbid ? mbid->getAsString() : "" } { setName(name); _sortName = _name; } - Artist::pointer Artist::create(Session& session, const std::string& name, const std::optional& MBID) + Artist::pointer Artist::create(Session& session, const std::string& name, const std::optional& mbid) { - return session.getDboSession()->add(std::unique_ptr{ new Artist{ name, MBID } }); + return session.getDboSession()->add(std::unique_ptr{ new Artist{ name, mbid } }); } std::size_t Artist::getCount(Session& session) @@ -322,7 +321,7 @@ namespace lms::db return utils::execRangeQuery(query, range); } - std::vector> Artist::getClusterGroups(std::vector clusterTypeIds, std::size_t size) const + std::vector> Artist::getClusterGroups(std::span clusterTypeIds, std::size_t size) const { assert(session()); @@ -354,6 +353,7 @@ namespace lms::db }); std::vector> res; + res.reserve(clustersByType.size()); for (const auto& [clusterTypeId, clusters] : clustersByType) res.push_back(clusters); diff --git a/src/libs/database/impl/Db.cpp b/src/libs/database/impl/Db.cpp index e4d95a71..b033a143 100644 --- a/src/libs/database/impl/Db.cpp +++ b/src/libs/database/impl/Db.cpp @@ -24,7 +24,6 @@ #include "core/IConfig.hpp" #include "core/ILogger.hpp" -#include "core/ITraceLogger.hpp" #include "core/Service.hpp" #include "database/Session.hpp" #include "database/User.hpp" diff --git a/src/libs/database/impl/Image.cpp b/src/libs/database/impl/Image.cpp index e2a0d02b..0466f9a0 100644 --- a/src/libs/database/impl/Image.cpp +++ b/src/libs/database/impl/Image.cpp @@ -71,11 +71,11 @@ namespace lms::db return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT i from image i").where("i.id = ?").bind(id)); } - Image::pointer Image::find(Session& session, const std::filesystem::path& path) + Image::pointer Image::find(Session& session, const std::filesystem::path& file) { session.checkReadTransaction(); - return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT i from image i").where("i.absolute_file_path = ?").bind(path)); + return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT i from image i").where("i.absolute_file_path = ?").bind(file)); } void Image::find(Session& session, ImageId& lastRetrievedImage, std::size_t count, const std::function& func) diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/database/include/database/Artist.hpp index 8ed22ee0..84d95d88 100644 --- a/src/libs/database/include/database/Artist.hpp +++ b/src/libs/database/include/database/Artist.hpp @@ -130,16 +130,16 @@ namespace lms::db static pointer find(Session& session, ArtistId id); static std::vector find(Session& session, std::string_view name); // exact match on name field static void find(Session& session, ArtistId& lastRetrievedArtist, std::size_t count, const std::function& func, MediaLibraryId library = {}); - static RangeResults find(Session& session, const FindParameters& parameters); - static void find(Session& session, const FindParameters& parameters, std::function func); - static RangeResults findIds(Session& session, const FindParameters& parameters); + static RangeResults find(Session& session, const FindParameters& params); + static void find(Session& session, const FindParameters& params, std::function func); + static RangeResults findIds(Session& session, const FindParameters& params); static RangeResults findOrphanIds(Session& session, std::optional range = std::nullopt); // No track related static bool exists(Session& session, ArtistId id); // Accessors const std::string& getName() const { return _name; } const std::string& getSortName() const { return _sortName; } - std::optional getMBID() const { return core::UUID::fromString(_MBID); } + std::optional getMBID() const { return core::UUID::fromString(_mbid); } ObjectPtr getImage() const; // No artistLinkTypes means get them all @@ -148,10 +148,10 @@ namespace lms::db // Get the cluster of the tracks made by this artist // Each clusters are grouped by cluster type, sorted by the number of occurence // size is the max number of cluster per cluster type - std::vector>> getClusterGroups(std::vector clusterTypeIds, std::size_t size) const; + std::vector>> getClusterGroups(std::span clusterTypeIds, std::size_t size) const; void setName(std::string_view name); - void setMBID(const std::optional& mbid) { _MBID = mbid ? mbid->getAsString() : ""; } + void setMBID(const std::optional& mbid) { _mbid = mbid ? mbid->getAsString() : ""; } void setSortName(std::string_view sortName); void setImage(ObjectPtr image); @@ -160,7 +160,7 @@ namespace lms::db { Wt::Dbo::field(a, _name, "name"); Wt::Dbo::field(a, _sortName, "sort_name"); - Wt::Dbo::field(a, _MBID, "mbid"); + Wt::Dbo::field(a, _mbid, "mbid"); Wt::Dbo::belongsTo(a, _image, "image", Wt::Dbo::OnDeleteSetNull); Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "artist"); @@ -173,11 +173,11 @@ namespace lms::db friend class Session; // Create Artist(const std::string& name, const std::optional& MBID = {}); - static pointer create(Session& session, const std::string& name, const std::optional& UUID = {}); + static pointer create(Session& session, const std::string& name, const std::optional& mbid = std::nullopt); std::string _name; std::string _sortName; - std::string _MBID; // Musicbrainz Identifier + std::string _mbid; // Musicbrainz Identifier Wt::Dbo::ptr _image; Wt::Dbo::collection> _trackArtistLinks; // Tracks involving this artist diff --git a/src/libs/image/impl/SvgImage.cpp b/src/libs/image/impl/SvgImage.cpp index 85eb62df..4534e4a0 100644 --- a/src/libs/image/impl/SvgImage.cpp +++ b/src/libs/image/impl/SvgImage.cpp @@ -19,6 +19,7 @@ #include "SvgImage.hpp" +#include #include #include "core/ITraceLogger.hpp" diff --git a/src/libs/image/impl/SvgImage.hpp b/src/libs/image/impl/SvgImage.hpp index 5ae53aa3..4befe5cd 100644 --- a/src/libs/image/impl/SvgImage.hpp +++ b/src/libs/image/impl/SvgImage.hpp @@ -19,8 +19,6 @@ #pragma once -#include -#include #include #include "image/IEncodedImage.hpp" @@ -33,9 +31,9 @@ namespace lms::image 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"; } + const std::byte* getData() const override { return &_data.front(); } + std::size_t getDataSize() const override { return _data.size(); } + std::string_view getMimeType() const override { return "image/svg+xml"; } private: const std::vector _data; diff --git a/src/libs/image/impl/stb/JPEGImage.cpp b/src/libs/image/impl/stb/JPEGImage.cpp index f64f831a..bf3cf93a 100644 --- a/src/libs/image/impl/stb/JPEGImage.cpp +++ b/src/libs/image/impl/stb/JPEGImage.cpp @@ -47,8 +47,7 @@ namespace lms::image::STB } } - const std::byte* - JPEGImage::getData() const + const std::byte* JPEGImage::getData() const { if (_data.empty()) return nullptr; @@ -56,8 +55,7 @@ namespace lms::image::STB return &_data.front(); } - std::size_t - JPEGImage::getDataSize() const + std::size_t JPEGImage::getDataSize() const { return _data.size(); } diff --git a/src/libs/image/impl/stb/RawImage.cpp b/src/libs/image/impl/stb/RawImage.cpp index 44a02101..3187fd08 100644 --- a/src/libs/image/impl/stb/RawImage.cpp +++ b/src/libs/image/impl/stb/RawImage.cpp @@ -45,7 +45,7 @@ namespace lms::image::STB { RawImage::RawImage(const std::byte* encodedData, std::size_t encodedDataSize) { - int n; + int n{}; _data = UniquePtrFree{ ::stbi_load_from_memory(reinterpret_cast(encodedData), encodedDataSize, &_width, &_height, &n, 3), std::free }; if (!_data) throw Exception{ "Cannot load image from memory: " + std::string{ ::stbi_failure_reason() } }; @@ -78,7 +78,7 @@ namespace lms::image::STB width = (size_t)((float)height / _height * _width); } - UniquePtrFree resizedData{ reinterpret_cast(malloc(width * height * 3)), std::free }; + UniquePtrFree resizedData{ static_cast(malloc(width * height * 3)), std::free }; if (!resizedData) throw Exception{ "Cannot allocate memory for resized image!" }; diff --git a/src/libs/image/impl/stb/RawImage.hpp b/src/libs/image/impl/stb/RawImage.hpp index ceaccd8c..044217fd 100644 --- a/src/libs/image/impl/stb/RawImage.hpp +++ b/src/libs/image/impl/stb/RawImage.hpp @@ -33,6 +33,10 @@ namespace lms::image::STB RawImage(const std::byte* encodedData, std::size_t encodedDataSize); RawImage(const std::filesystem::path& path); + ~RawImage() override = default; + RawImage(const RawImage&) = delete; + RawImage& operator=(const RawImage&) = delete; + ImageSize getWidth() const override; ImageSize getHeight() const override; @@ -42,8 +46,8 @@ namespace lms::image::STB const std::byte* getData() const; private: - int _width; - int _height; + int _width{}; + int _height{}; using UniquePtrFree = std::unique_ptr; UniquePtrFree _data{ nullptr, std::free }; }; diff --git a/src/libs/image/include/image/IRawImage.hpp b/src/libs/image/include/image/IRawImage.hpp index 75ed8c88..3347228f 100644 --- a/src/libs/image/include/image/IRawImage.hpp +++ b/src/libs/image/include/image/IRawImage.hpp @@ -19,6 +19,8 @@ #pragma once +#include + #include "image/IEncodedImage.hpp" namespace lms::image diff --git a/src/libs/metadata/impl/AvFormatTagReader.cpp b/src/libs/metadata/impl/AvFormatTagReader.cpp index 081144ce..7891890a 100644 --- a/src/libs/metadata/impl/AvFormatTagReader.cpp +++ b/src/libs/metadata/impl/AvFormatTagReader.cpp @@ -19,16 +19,12 @@ #include "AvFormatTagReader.hpp" -#include -#include - #include "av/IAudioFile.hpp" +#include "av/Types.hpp" #include "core/ILogger.hpp" #include "core/String.hpp" #include "metadata/Exception.hpp" -#include "Utils.hpp" - namespace lms::metadata { namespace diff --git a/src/libs/metadata/impl/AvFormatTagReader.hpp b/src/libs/metadata/impl/AvFormatTagReader.hpp index cdf5db4f..c18eed1c 100644 --- a/src/libs/metadata/impl/AvFormatTagReader.hpp +++ b/src/libs/metadata/impl/AvFormatTagReader.hpp @@ -22,7 +22,6 @@ #include #include "av/IAudioFile.hpp" -#include "metadata/IParser.hpp" #include "ITagReader.hpp" @@ -32,11 +31,11 @@ namespace lms::metadata { public: AvFormatTagReader(const std::filesystem::path& path, bool debug); - - private: + ~AvFormatTagReader() override = default; AvFormatTagReader(const AvFormatTagReader&) = delete; AvFormatTagReader& operator=(const AvFormatTagReader&) = delete; + private: void visitTagValues(TagType tag, TagValueVisitor visitor) const override; void visitTagValues(std::string_view tag, TagValueVisitor visitor) const override; void visitPerformerTags(PerformerVisitor visitor) const override; diff --git a/src/libs/services/scanner/impl/ScannerService.hpp b/src/libs/services/scanner/impl/ScannerService.hpp index 2a1bbd82..3f4a57aa 100644 --- a/src/libs/services/scanner/impl/ScannerService.hpp +++ b/src/libs/services/scanner/impl/ScannerService.hpp @@ -43,7 +43,7 @@ namespace lms::scanner { public: ScannerService(db::Db& db); - ~ScannerService(); + ~ScannerService() override; private: ScannerService(const ScannerService&) = delete; @@ -55,7 +55,6 @@ namespace lms::scanner Status getStatus() const override; Events& getEvents() override { return _events; } - private: void start(); void stop(); diff --git a/src/libs/subsonic/impl/ProtocolVersion.hpp b/src/libs/subsonic/impl/ProtocolVersion.hpp index 28d63cfa..69aed228 100644 --- a/src/libs/subsonic/impl/ProtocolVersion.hpp +++ b/src/libs/subsonic/impl/ProtocolVersion.hpp @@ -19,6 +19,9 @@ #pragma once +#include +#include + #include "core/String.hpp" namespace lms::api::subsonic diff --git a/src/libs/subsonic/impl/SubsonicId.cpp b/src/libs/subsonic/impl/SubsonicId.cpp index b1fda5af..13e75909 100644 --- a/src/libs/subsonic/impl/SubsonicId.cpp +++ b/src/libs/subsonic/impl/SubsonicId.cpp @@ -19,9 +19,6 @@ #include "SubsonicId.hpp" -#include "SubsonicResponse.hpp" - -#include "core/ILogger.hpp" #include "core/String.hpp" namespace lms::api::subsonic @@ -47,11 +44,6 @@ namespace lms::api::subsonic return "al-" + id.toString(); } - std::string idToString(RootId) - { - return "root"; - } - std::string idToString(db::TrackId id) { return "tr-" + id.toString(); @@ -122,15 +114,6 @@ namespace lms::core::stringUtils return std::nullopt; } - template<> - std::optional readAs(std::string_view str) - { - if (str == "root") - return api::subsonic::RootId{}; - - return std::nullopt; - } - template<> std::optional readAs(std::string_view str) { diff --git a/src/libs/subsonic/impl/SubsonicId.hpp b/src/libs/subsonic/impl/SubsonicId.hpp index 331708a5..7b285b08 100644 --- a/src/libs/subsonic/impl/SubsonicId.hpp +++ b/src/libs/subsonic/impl/SubsonicId.hpp @@ -29,25 +29,17 @@ namespace lms::api::subsonic { - struct RootId - { - }; - std::string idToString(db::ArtistId id); std::string idToString(db::DirectoryId id); std::string idToString(db::MediaLibraryId id); std::string idToString(db::ReleaseId id); std::string idToString(db::TrackId id); std::string idToString(db::TrackListId id); - std::string idToString(RootId); } // namespace lms::api::subsonic // Used to parse parameters namespace lms::core::stringUtils { - template<> - std::optional readAs(std::string_view str); - template<> std::optional readAs(std::string_view str); diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 925b3764..7ea0b82f 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -29,7 +29,6 @@ #include "core/LiteralString.hpp" #include "core/Service.hpp" #include "core/String.hpp" -#include "core/Utils.hpp" #include "database/Db.hpp" #include "database/Session.hpp" #include "database/User.hpp" @@ -39,7 +38,6 @@ #include "ParameterParsing.hpp" #include "ProtocolVersion.hpp" #include "RequestContext.hpp" -#include "SubsonicId.hpp" #include "SubsonicResponse.hpp" #include "endpoints/AlbumSongLists.hpp" #include "endpoints/Bookmarks.hpp" @@ -105,8 +103,8 @@ namespace lms::api::subsonic auto censorValue = [](const std::string& type, const std::string& value) -> std::string { if (type == "p" || type == "password") return "*REDACTED*"; - else - return value; + + return value; }; std::string res; @@ -138,7 +136,7 @@ namespace lms::api::subsonic throw UserNotAuthorizedError{}; } - Response handleNotImplemented(RequestContext&) + Response handleNotImplemented(RequestContext& /*context*/) { throw NotImplementedGenericError{}; } @@ -292,6 +290,18 @@ namespace lms::api::subsonic throw UserNotAuthorizedError{}; } + + ClientInfo getClientInfo(const Wt::Http::Request& request) + { + const auto& parameters{ request.getParameterMap() }; + ClientInfo res; + + // Mandatory parameters + res.name = getMandatoryParameterAs(parameters, "c"); + res.version = getMandatoryParameterAs(parameters, "v"); + + return res; + } } // namespace SubsonicResource::SubsonicResource(db::Db& db) @@ -403,25 +413,13 @@ namespace lms::api::subsonic throw ClientMustUpgradeError{}; if (client.minor > server.minor) throw ServerMustUpgradeError{}; - else if (client.minor == server.minor) + if (client.minor == server.minor) { if (client.patch > server.patch) throw ServerMustUpgradeError{}; } } - ClientInfo SubsonicResource::getClientInfo(const Wt::Http::Request& request) - { - const auto& parameters{ request.getParameterMap() }; - ClientInfo res; - - // Mandatory parameters - res.name = getMandatoryParameterAs(parameters, "c"); - res.version = getMandatoryParameterAs(parameters, "v"); - - return res; - } - RequestContext SubsonicResource::buildRequestContext(const Wt::Http::Request& request) { const Wt::Http::ParameterMap& parameters{ request.getParameterMap() }; diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index f6bd7417..7c00f847 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -25,10 +25,8 @@ #include #include -#include "database/Types.hpp" #include "database/UserId.hpp" -#include "ClientInfo.hpp" #include "RequestContext.hpp" namespace lms::db @@ -48,7 +46,6 @@ namespace lms::api::subsonic ProtocolVersion getServerProtocolVersion(const std::string& clientName) const; static void checkProtocolVersion(ProtocolVersion client, ProtocolVersion server); - ClientInfo getClientInfo(const Wt::Http::Request& request); RequestContext buildRequestContext(const Wt::Http::Request& request); db::UserId authenticateUser(const Wt::Http::Request& request); diff --git a/src/libs/subsonic/impl/SubsonicResponse.cpp b/src/libs/subsonic/impl/SubsonicResponse.cpp index 4110cc4e..5bf5acd3 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.cpp +++ b/src/libs/subsonic/impl/SubsonicResponse.cpp @@ -25,7 +25,6 @@ #include -#include "core/Exception.hpp" #include "core/String.hpp" #include "ProtocolVersion.hpp" diff --git a/src/libs/subsonic/impl/SubsonicResponseAllocator.hpp b/src/libs/subsonic/impl/SubsonicResponseAllocator.hpp index b0335a79..5ea437b3 100644 --- a/src/libs/subsonic/impl/SubsonicResponseAllocator.hpp +++ b/src/libs/subsonic/impl/SubsonicResponseAllocator.hpp @@ -38,7 +38,7 @@ namespace lms::api::subsonic constexpr Allocator() noexcept = default; template - constexpr Allocator(const Allocator&) noexcept + constexpr Allocator(const Allocator& /*allocator*/) noexcept { } @@ -54,7 +54,7 @@ namespace lms::api::subsonic } // Deallocate memory pointed to by p - void deallocate(pointer p, std::size_t) noexcept + void deallocate(pointer p, std::size_t /*n*/) noexcept { MemoryResource::getInstance().deallocate(reinterpret_cast(p)); } diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp index 123ef5ea..a8a1ddb7 100644 --- a/src/lms/ui/admin/UserView.cpp +++ b/src/lms/ui/admin/UserView.cpp @@ -27,9 +27,7 @@ #include -#include "core/Exception.hpp" #include "core/IConfig.hpp" -#include "core/ILogger.hpp" #include "core/Service.hpp" #include "core/String.hpp" #include "database/Session.hpp" @@ -125,7 +123,7 @@ namespace lms::ui const User::pointer user{ User::find(LmsApp->getDbSession(), *_userId) }; if (!user) throw UserNotFoundException{}; - else if (user == LmsApp->getUser()) + if (user == LmsApp->getUser()) throw UserNotAllowedException{}; } @@ -155,7 +153,7 @@ namespace lms::ui return valueText(LoginField).toUTF8(); } - bool validateField(Field field) + bool validateField(Field field) override { Wt::WString error; From 5ce99411e198766a222c2551a1d3b32975525f9b Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 5 Dec 2024 21:05:12 +0100 Subject: [PATCH 05/20] Various minor header cleanup --- src/lms/ui/Auth.cpp | 1 - src/lms/ui/LmsApplication.cpp | 2 -- src/lms/ui/LmsApplication.hpp | 1 - src/lms/ui/LmsApplicationException.hpp | 3 ++- src/lms/ui/MediaPlayer.cpp | 2 ++ src/lms/ui/MediaPlayer.hpp | 2 +- src/lms/ui/ModalManager.cpp | 1 + src/lms/ui/PlayQueue.cpp | 1 - src/lms/ui/SettingsView.cpp | 1 - src/lms/ui/common/PasswordValidator.cpp | 8 +++++++- src/lms/ui/explore/ArtistListHelpers.cpp | 3 ++- src/lms/ui/explore/ArtistView.cpp | 1 - src/lms/ui/explore/ArtistView.hpp | 2 -- src/lms/ui/explore/ArtistsView.cpp | 2 +- src/lms/ui/explore/ArtistsView.hpp | 1 - src/lms/ui/explore/DatabaseCollectorBase.hpp | 1 - src/lms/ui/explore/Filters.hpp | 2 -- src/lms/ui/explore/PlayQueueController.cpp | 1 - src/lms/ui/explore/ReleaseCollector.hpp | 1 - src/lms/ui/explore/ReleaseHelpers.hpp | 2 -- src/lms/ui/explore/ReleaseTypes.cpp | 3 ++- src/lms/ui/explore/ReleaseTypes.hpp | 1 - src/lms/ui/explore/ReleaseView.cpp | 1 - src/lms/ui/explore/ReleasesView.hpp | 2 -- src/lms/ui/explore/TrackCollector.cpp | 2 -- src/lms/ui/explore/TrackCollector.hpp | 1 - src/lms/ui/explore/TrackListHelpers.cpp | 1 - src/lms/ui/explore/TrackListView.cpp | 1 - src/lms/ui/explore/TrackListView.hpp | 1 - src/lms/ui/explore/TrackListsView.hpp | 1 - src/lms/ui/explore/TracksView.cpp | 1 - src/lms/ui/explore/TracksView.hpp | 2 -- src/lms/ui/resource/ArtworkResource.cpp | 1 - src/lms/ui/resource/AudioFileResource.cpp | 3 --- src/lms/ui/resource/AudioFileResource.hpp | 2 +- src/lms/ui/resource/DownloadResource.cpp | 1 - src/lms/ui/resource/DownloadResource.hpp | 3 +-- src/tools/cover/LmsCover.cpp | 1 - src/tools/db-generator/LmsDbGenerator.cpp | 1 - src/tools/metadata/LmsMetadata.cpp | 1 - src/tools/recommendation/LmsRecommendation.cpp | 1 - 41 files changed, 20 insertions(+), 49 deletions(-) diff --git a/src/lms/ui/Auth.cpp b/src/lms/ui/Auth.cpp index eb021da3..850f66d4 100644 --- a/src/lms/ui/Auth.cpp +++ b/src/lms/ui/Auth.cpp @@ -28,7 +28,6 @@ #include #include -#include "core/ILogger.hpp" #include "core/Service.hpp" #include "database/Session.hpp" #include "database/User.hpp" diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index b5aa9e9d..b5932496 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -29,7 +29,6 @@ #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" #include "core/Service.hpp" -#include "core/String.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" @@ -63,7 +62,6 @@ #include "explore/Explore.hpp" #include "explore/Filters.hpp" #include "resource/ArtworkResource.hpp" -#include "resource/AudioFileResource.hpp" #include "resource/AudioTranscodingResource.hpp" namespace lms::ui diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 5e59a677..8f943561 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -32,7 +32,6 @@ #include "Auth.hpp" #include "Notification.hpp" -#include "admin/ScannerController.hpp" namespace lms::db { diff --git a/src/lms/ui/LmsApplicationException.hpp b/src/lms/ui/LmsApplicationException.hpp index 1b986aee..8027ecd2 100644 --- a/src/lms/ui/LmsApplicationException.hpp +++ b/src/lms/ui/LmsApplicationException.hpp @@ -19,8 +19,9 @@ #pragma once +#include + #include "core/Exception.hpp" -#include "database/Types.hpp" namespace lms::ui { diff --git a/src/lms/ui/MediaPlayer.cpp b/src/lms/ui/MediaPlayer.cpp index 313f7bb5..a165aa15 100644 --- a/src/lms/ui/MediaPlayer.cpp +++ b/src/lms/ui/MediaPlayer.cpp @@ -227,6 +227,8 @@ namespace lms::ui } } + MediaPlayer::~MediaPlayer() = default; + void MediaPlayer::loadTrack(db::TrackId trackId, bool play, float replayGain) { LMS_LOG(UI, DEBUG, "Playing track ID = " << trackId.toString()); diff --git a/src/lms/ui/MediaPlayer.hpp b/src/lms/ui/MediaPlayer.hpp index 5d627c45..0bdc41e7 100644 --- a/src/lms/ui/MediaPlayer.hpp +++ b/src/lms/ui/MediaPlayer.hpp @@ -89,7 +89,7 @@ namespace lms::ui }; MediaPlayer(); - ~MediaPlayer() = default; + ~MediaPlayer() override; MediaPlayer(const MediaPlayer&) = delete; MediaPlayer& operator=(const MediaPlayer&) = delete; diff --git a/src/lms/ui/ModalManager.cpp b/src/lms/ui/ModalManager.cpp index d17e5a41..e2a5290f 100644 --- a/src/lms/ui/ModalManager.cpp +++ b/src/lms/ui/ModalManager.cpp @@ -18,6 +18,7 @@ */ #include "ModalManager.hpp" + #include "core/ILogger.hpp" namespace lms::ui diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index e8e86dd5..44365446 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -32,7 +32,6 @@ #include "core/ILogger.hpp" #include "core/Random.hpp" #include "core/Service.hpp" -#include "core/String.hpp" #include "database/Artist.hpp" #include "database/Release.hpp" #include "database/Session.hpp" diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 73d70f58..befccef5 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -29,7 +29,6 @@ #include #include "core/IConfig.hpp" -#include "core/ILogger.hpp" #include "core/Service.hpp" #include "database/Session.hpp" #include "database/User.hpp" diff --git a/src/lms/ui/common/PasswordValidator.cpp b/src/lms/ui/common/PasswordValidator.cpp index 0c22c793..690a8e1d 100644 --- a/src/lms/ui/common/PasswordValidator.cpp +++ b/src/lms/ui/common/PasswordValidator.cpp @@ -21,7 +21,6 @@ #include -#include "core/Service.hpp" #include "services/auth/IPasswordService.hpp" #include "LmsApplication.hpp" @@ -39,6 +38,10 @@ namespace lms::ui { } + ~PasswordStrengthValidator() override = default; + PasswordStrengthValidator(const PasswordStrengthValidator&) = delete; + PasswordStrengthValidator& operator=(const PasswordStrengthValidator&) = delete; + private: Wt::WValidator::Result validate(const Wt::WString& input) const override; std::string javaScriptValidate() const override { return {}; } @@ -80,6 +83,9 @@ namespace lms::ui : _passwordService{ passwordService } { } + ~PasswordCheckValidator() = default; + PasswordCheckValidator(const PasswordCheckValidator&) = delete; + PasswordCheckValidator& operator=(const PasswordCheckValidator&) = delete; private: Wt::WValidator::Result validate(const Wt::WString& input) const override; diff --git a/src/lms/ui/explore/ArtistListHelpers.cpp b/src/lms/ui/explore/ArtistListHelpers.cpp index 00ae8580..db8be097 100644 --- a/src/lms/ui/explore/ArtistListHelpers.cpp +++ b/src/lms/ui/explore/ArtistListHelpers.cpp @@ -18,8 +18,9 @@ */ #include "ArtistListHelpers.hpp" +#include + #include "database/Artist.hpp" -#include "database/Session.hpp" #include "LmsApplication.hpp" #include "Utils.hpp" diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 8a363f04..09d5cd8a 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -21,7 +21,6 @@ #include -#include "core/ILogger.hpp" #include "core/String.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index 7220e625..cdf54f75 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -20,9 +20,7 @@ #pragma once #include -#include -#include "core/EnumSet.hpp" #include "database/ArtistId.hpp" #include "database/Object.hpp" #include "database/ReleaseId.hpp" diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index fab15863..20b4757f 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -19,9 +19,9 @@ #include "ArtistsView.hpp" +#include #include -#include "core/ILogger.hpp" #include "database/Artist.hpp" #include "database/Session.hpp" #include "database/TrackArtistLink.hpp" diff --git a/src/lms/ui/explore/ArtistsView.hpp b/src/lms/ui/explore/ArtistsView.hpp index e00546f8..4ef4c38f 100644 --- a/src/lms/ui/explore/ArtistsView.hpp +++ b/src/lms/ui/explore/ArtistsView.hpp @@ -20,7 +20,6 @@ #pragma once #include -#include #include #include diff --git a/src/lms/ui/explore/DatabaseCollectorBase.hpp b/src/lms/ui/explore/DatabaseCollectorBase.hpp index fe8f03eb..956abbe8 100644 --- a/src/lms/ui/explore/DatabaseCollectorBase.hpp +++ b/src/lms/ui/explore/DatabaseCollectorBase.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "database/Types.hpp" diff --git a/src/lms/ui/explore/Filters.hpp b/src/lms/ui/explore/Filters.hpp index d1a01175..bf380433 100644 --- a/src/lms/ui/explore/Filters.hpp +++ b/src/lms/ui/explore/Filters.hpp @@ -29,8 +29,6 @@ #include "database/ClusterId.hpp" #include "database/MediaLibraryId.hpp" -#include "Filters.hpp" - namespace lms::ui { class Filters : public Wt::WTemplate diff --git a/src/lms/ui/explore/PlayQueueController.cpp b/src/lms/ui/explore/PlayQueueController.cpp index 3af83380..518bb86c 100644 --- a/src/lms/ui/explore/PlayQueueController.cpp +++ b/src/lms/ui/explore/PlayQueueController.cpp @@ -19,7 +19,6 @@ #include "explore/PlayQueueController.hpp" -#include "database/ClusterId.hpp" #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" diff --git a/src/lms/ui/explore/ReleaseCollector.hpp b/src/lms/ui/explore/ReleaseCollector.hpp index a4a54940..4b002e95 100644 --- a/src/lms/ui/explore/ReleaseCollector.hpp +++ b/src/lms/ui/explore/ReleaseCollector.hpp @@ -23,7 +23,6 @@ #include "DatabaseCollectorBase.hpp" -#include "database/Object.hpp" #include "database/ReleaseId.hpp" #include "database/Types.hpp" diff --git a/src/lms/ui/explore/ReleaseHelpers.hpp b/src/lms/ui/explore/ReleaseHelpers.hpp index 547efa42..791a9a2e 100644 --- a/src/lms/ui/explore/ReleaseHelpers.hpp +++ b/src/lms/ui/explore/ReleaseHelpers.hpp @@ -26,9 +26,7 @@ #include #include -#include "core/EnumSet.hpp" #include "database/Object.hpp" -#include "database/Types.hpp" #include "ReleaseTypes.hpp" diff --git a/src/lms/ui/explore/ReleaseTypes.cpp b/src/lms/ui/explore/ReleaseTypes.cpp index b844daac..6b54076a 100644 --- a/src/lms/ui/explore/ReleaseTypes.cpp +++ b/src/lms/ui/explore/ReleaseTypes.cpp @@ -20,9 +20,10 @@ #include #include -#include "ReleaseTypes.hpp" #include "core/String.hpp" +#include "ReleaseTypes.hpp" + namespace lms::core::stringUtils { template<> diff --git a/src/lms/ui/explore/ReleaseTypes.hpp b/src/lms/ui/explore/ReleaseTypes.hpp index 92db1ce5..882f4317 100644 --- a/src/lms/ui/explore/ReleaseTypes.hpp +++ b/src/lms/ui/explore/ReleaseTypes.hpp @@ -21,7 +21,6 @@ #include #include -#include #include #include "core/EnumSet.hpp" diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index ae69b29c..76466ca4 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -26,7 +26,6 @@ #include #include "av/IAudioFile.hpp" -#include "core/ILogger.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Release.hpp" diff --git a/src/lms/ui/explore/ReleasesView.hpp b/src/lms/ui/explore/ReleasesView.hpp index e81dcc48..f321b2bb 100644 --- a/src/lms/ui/explore/ReleasesView.hpp +++ b/src/lms/ui/explore/ReleasesView.hpp @@ -19,8 +19,6 @@ #pragma once -#include "database/Types.hpp" - #include "ReleaseCollector.hpp" #include "common/Template.hpp" diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 8c882e50..4bb2a338 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -19,8 +19,6 @@ #include "TrackCollector.hpp" -#include - #include "core/Service.hpp" #include "database/Session.hpp" #include "database/Track.hpp" diff --git a/src/lms/ui/explore/TrackCollector.hpp b/src/lms/ui/explore/TrackCollector.hpp index 88ce422f..034d4cb6 100644 --- a/src/lms/ui/explore/TrackCollector.hpp +++ b/src/lms/ui/explore/TrackCollector.hpp @@ -21,7 +21,6 @@ #include -#include "database/Object.hpp" #include "database/TrackId.hpp" #include "DatabaseCollectorBase.hpp" diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index f8bb10bb..40b62b9f 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -26,7 +26,6 @@ #include #include "av/IAudioFile.hpp" -#include "core/ILogger.hpp" #include "core/Service.hpp" #include "database/Artist.hpp" #include "database/Release.hpp" diff --git a/src/lms/ui/explore/TrackListView.cpp b/src/lms/ui/explore/TrackListView.cpp index cdd77b26..37336966 100644 --- a/src/lms/ui/explore/TrackListView.cpp +++ b/src/lms/ui/explore/TrackListView.cpp @@ -21,7 +21,6 @@ #include -#include "core/ILogger.hpp" #include "core/String.hpp" #include "database/Cluster.hpp" #include "database/ScanSettings.hpp" diff --git a/src/lms/ui/explore/TrackListView.hpp b/src/lms/ui/explore/TrackListView.hpp index eb765a03..2e381f3d 100644 --- a/src/lms/ui/explore/TrackListView.hpp +++ b/src/lms/ui/explore/TrackListView.hpp @@ -20,7 +20,6 @@ #pragma once #include "database/TrackListId.hpp" -#include "database/Types.hpp" #include "common/Template.hpp" diff --git a/src/lms/ui/explore/TrackListsView.hpp b/src/lms/ui/explore/TrackListsView.hpp index e169203c..8f04e4b9 100644 --- a/src/lms/ui/explore/TrackListsView.hpp +++ b/src/lms/ui/explore/TrackListsView.hpp @@ -25,7 +25,6 @@ #include "database/Object.hpp" #include "database/TrackListId.hpp" -#include "database/Types.hpp" #include "common/Template.hpp" diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index 63b1b306..b83aef68 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -22,7 +22,6 @@ #include #include -#include "core/ILogger.hpp" #include "database/Session.hpp" #include "database/Track.hpp" diff --git a/src/lms/ui/explore/TracksView.hpp b/src/lms/ui/explore/TracksView.hpp index ca1062b3..2d7db3e9 100644 --- a/src/lms/ui/explore/TracksView.hpp +++ b/src/lms/ui/explore/TracksView.hpp @@ -19,8 +19,6 @@ #pragma once -#include "database/Types.hpp" - #include "TrackCollector.hpp" #include "common/Template.hpp" diff --git a/src/lms/ui/resource/ArtworkResource.cpp b/src/lms/ui/resource/ArtworkResource.cpp index 78c9f5d1..131f085c 100644 --- a/src/lms/ui/resource/ArtworkResource.cpp +++ b/src/lms/ui/resource/ArtworkResource.cpp @@ -22,7 +22,6 @@ #include #include -#include "core/Exception.hpp" #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" #include "core/Service.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index c1b0d64f..cefc5ea7 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -19,11 +19,8 @@ #include "AudioFileResource.hpp" -#include - #include -#include "av/IAudioFile.hpp" #include "av/RawResourceHandlerCreator.hpp" #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" diff --git a/src/lms/ui/resource/AudioFileResource.hpp b/src/lms/ui/resource/AudioFileResource.hpp index 3ed9e0a9..da00ae7d 100644 --- a/src/lms/ui/resource/AudioFileResource.hpp +++ b/src/lms/ui/resource/AudioFileResource.hpp @@ -28,7 +28,7 @@ namespace lms::ui class AudioFileResource : public Wt::WResource { public: - ~AudioFileResource(); + ~AudioFileResource() override; std::string getUrl(db::TrackId trackId) const; diff --git a/src/lms/ui/resource/DownloadResource.cpp b/src/lms/ui/resource/DownloadResource.cpp index 88acc471..812c70a3 100644 --- a/src/lms/ui/resource/DownloadResource.cpp +++ b/src/lms/ui/resource/DownloadResource.cpp @@ -22,7 +22,6 @@ #include #include -#include "core/Exception.hpp" #include "core/ILogger.hpp" #include "database/Artist.hpp" #include "database/Release.hpp" diff --git a/src/lms/ui/resource/DownloadResource.hpp b/src/lms/ui/resource/DownloadResource.hpp index 36128422..b525b560 100644 --- a/src/lms/ui/resource/DownloadResource.hpp +++ b/src/lms/ui/resource/DownloadResource.hpp @@ -23,7 +23,6 @@ #include #include "core/IZipper.hpp" -#include "core/ZipperResourceHandlerCreator.hpp" #include "database/ArtistId.hpp" #include "database/ReleaseId.hpp" #include "database/TrackId.hpp" @@ -36,7 +35,7 @@ namespace lms::ui public: static constexpr std::size_t bufferSize{ 32768 }; - ~DownloadResource(); + ~DownloadResource() override; private: void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; diff --git a/src/tools/cover/LmsCover.cpp b/src/tools/cover/LmsCover.cpp index db177b01..c5e08921 100644 --- a/src/tools/cover/LmsCover.cpp +++ b/src/tools/cover/LmsCover.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include diff --git a/src/tools/db-generator/LmsDbGenerator.cpp b/src/tools/db-generator/LmsDbGenerator.cpp index b4361d24..13effd41 100644 --- a/src/tools/db-generator/LmsDbGenerator.cpp +++ b/src/tools/db-generator/LmsDbGenerator.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/src/tools/metadata/LmsMetadata.cpp b/src/tools/metadata/LmsMetadata.cpp index 96dbb7e2..0ed739af 100644 --- a/src/tools/metadata/LmsMetadata.cpp +++ b/src/tools/metadata/LmsMetadata.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index 0dd76576..cf44404b 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include From 06fd444d2673e72bd3f2640a1a7e9fa22289f8b4 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 6 Dec 2024 14:08:31 +0100 Subject: [PATCH 06/20] Various minor cleanup --- .../services/artwork/impl/ArtworkService.hpp | 7 +++--- .../services/artwork/IArtworkService.hpp | 2 +- .../services/auth/impl/AuthServiceBase.hpp | 3 +++ .../services/auth/impl/AuthTokenService.cpp | 9 ++++--- .../services/auth/impl/AuthTokenService.hpp | 1 + src/libs/services/auth/impl/EnvService.cpp | 3 +-- .../services/auth/impl/LoginThrottler.hpp | 5 +++- .../auth/impl/PasswordServiceBase.cpp | 16 +++++-------- .../auth/impl/PasswordServiceBase.hpp | 1 + .../impl/internal/InternalPasswordService.hpp | 2 +- .../auth/impl/pam/PAMPasswordService.cpp | 3 +-- .../auth/impl/pam/PAMPasswordService.hpp | 2 -- .../services/auth/IAuthTokenService.hpp | 3 +-- .../include/services/auth/IEnvService.hpp | 3 +-- .../services/auth/IPasswordService.hpp | 2 -- .../feedback/impl/FeedbackService.hpp | 10 ++++---- .../feedback/impl/IFeedbackBackend.hpp | 12 +++++----- .../impl/internal/InternalBackend.cpp | 24 +++++++++---------- .../impl/internal/InternalBackend.hpp | 15 +++++++----- .../impl/listenbrainz/FeedbackTypes.cpp | 2 ++ .../impl/listenbrainz/FeedbackTypes.hpp | 2 +- .../impl/listenbrainz/FeedbacksParser.cpp | 6 ++--- .../listenbrainz/FeedbacksSynchronizer.cpp | 8 +++---- .../listenbrainz/FeedbacksSynchronizer.hpp | 8 ++++--- .../impl/listenbrainz/ListenBrainzBackend.cpp | 1 - .../services/feedback/IFeedbackService.hpp | 2 +- .../impl/RecommendationService.cpp | 3 --- .../impl/RecommendationService.hpp | 5 ++-- .../impl/clusters/ClustersEngine.cpp | 4 ++-- .../impl/clusters/ClustersEngine.hpp | 5 ++-- .../ConsecutiveArtists.cpp | 1 - .../ConsecutiveArtists.hpp | 5 ++-- .../ConsecutiveReleases.cpp | 5 ++-- .../ConsecutiveReleases.hpp | 3 +++ .../playlist-constraints/DuplicateTracks.cpp | 2 +- .../impl/playlist-constraints/IConstraint.hpp | 2 -- .../IPlaylistGeneratorService.hpp | 3 +-- .../services/scanner/impl/FileScanQueue.cpp | 3 --- .../services/scanner/impl/FileScanQueue.hpp | 1 - .../impl/ScanStepAssociateArtistImages.cpp | 5 ++-- .../impl/ScanStepAssociateArtistImages.hpp | 3 +++ .../impl/ScanStepAssociateReleaseImages.cpp | 2 -- .../impl/ScanStepAssociateReleaseImages.hpp | 3 +++ .../services/scanner/impl/ScanStepBase.hpp | 2 ++ .../impl/ScanStepComputeClusterStats.cpp | 1 - .../impl/ScanStepRemoveOrphanedDbEntries.cpp | 1 - .../impl/ScanStepRemoveOrphanedDbEntries.hpp | 2 -- .../scanner/impl/ScanStepScanFiles.cpp | 21 ++++------------ .../scanner/impl/ScanStepScanFiles.hpp | 6 ++--- .../impl/ScanStepUpdateLibraryFields.cpp | 2 -- .../impl/ScanStepUpdateLibraryFields.hpp | 2 -- .../services/scanner/impl/ScannerService.cpp | 2 -- .../services/scanner/impl/ScannerService.hpp | 7 ++---- .../recommendation/LmsRecommendation.cpp | 4 ++-- 54 files changed, 113 insertions(+), 144 deletions(-) diff --git a/src/libs/services/artwork/impl/ArtworkService.hpp b/src/libs/services/artwork/impl/ArtworkService.hpp index 58e0c201..ff8fa2f1 100644 --- a/src/libs/services/artwork/impl/ArtworkService.hpp +++ b/src/libs/services/artwork/impl/ArtworkService.hpp @@ -22,7 +22,6 @@ #include #include -#include "database/Types.hpp" #include "image/IEncodedImage.hpp" #include "services/artwork/IArtworkService.hpp" @@ -43,12 +42,12 @@ namespace lms::cover class ArtworkService : public IArtworkService { public: - ArtworkService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath, const std::filesystem::path& defaultArtistImageSvgPath); - - private: + ArtworkService(db::Db& db, const std::filesystem::path& defaultReleaseCoverSvgPath, const std::filesystem::path& defaultArtistImageSvgPath); + ~ArtworkService() override = default; ArtworkService(const ArtworkService&) = delete; ArtworkService& operator=(const ArtworkService&) = delete; + private: 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; diff --git a/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp index 3d499303..6bb119f0 100644 --- a/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp +++ b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp @@ -56,6 +56,6 @@ namespace lms::cover virtual void setJpegQuality(unsigned quality) = 0; // from 1 to 100 }; - std::unique_ptr createArtworkService(db::Db& db, const std::filesystem::path& defaultSvgCoverPath, const std::filesystem::path& defaultArtistImageSvgPath); + std::unique_ptr createArtworkService(db::Db& db, const std::filesystem::path& defaultReleaseCoverSvgPath, const std::filesystem::path& defaultArtistImageSvgPath); } // namespace lms::cover diff --git a/src/libs/services/auth/impl/AuthServiceBase.hpp b/src/libs/services/auth/impl/AuthServiceBase.hpp index bba5f54e..ec066fb1 100644 --- a/src/libs/services/auth/impl/AuthServiceBase.hpp +++ b/src/libs/services/auth/impl/AuthServiceBase.hpp @@ -35,6 +35,9 @@ namespace lms::auth { protected: AuthServiceBase(db::Db& db); + ~AuthServiceBase() = default; + AuthServiceBase(const AuthServiceBase&) = delete; + AuthServiceBase& operator=(const AuthServiceBase&) = delete; db::UserId getOrCreateUser(std::string_view loginName); void onUserAuthenticated(db::UserId userId); diff --git a/src/libs/services/auth/impl/AuthTokenService.cpp b/src/libs/services/auth/impl/AuthTokenService.cpp index 1fceeb1a..da31cbe2 100644 --- a/src/libs/services/auth/impl/AuthTokenService.cpp +++ b/src/libs/services/auth/impl/AuthTokenService.cpp @@ -22,7 +22,6 @@ #include #include -#include "core/Exception.hpp" #include "core/ILogger.hpp" #include "database/AuthToken.hpp" #include "database/Session.hpp" @@ -126,7 +125,7 @@ namespace lms::auth std::shared_lock lock{ _mutex }; if (_loginThrottler.isClientThrottled(clientAddress)) - return AuthTokenProcessResult{ AuthTokenProcessResult::State::Throttled }; + return AuthTokenProcessResult{ .state = AuthTokenProcessResult::State::Throttled, .authTokenInfo = std::nullopt }; } auto res{ processAuthToken(domain, tokenValue) }; @@ -134,17 +133,17 @@ namespace lms::auth std::unique_lock lock{ _mutex }; if (_loginThrottler.isClientThrottled(clientAddress)) - return AuthTokenProcessResult{ AuthTokenProcessResult::State::Throttled }; + return AuthTokenProcessResult{ .state = AuthTokenProcessResult::State::Throttled, .authTokenInfo = std::nullopt }; if (!res) { _loginThrottler.onBadClientAttempt(clientAddress); - return AuthTokenProcessResult{ AuthTokenProcessResult::State::Denied }; + return AuthTokenProcessResult{ .state = AuthTokenProcessResult::State::Denied, .authTokenInfo = std::nullopt }; } _loginThrottler.onGoodClientAttempt(clientAddress); onUserAuthenticated(res->userId); - return AuthTokenProcessResult{ AuthTokenProcessResult::State::Granted, res }; + return AuthTokenProcessResult{ .state = AuthTokenProcessResult::State::Granted, .authTokenInfo = res }; } } diff --git a/src/libs/services/auth/impl/AuthTokenService.hpp b/src/libs/services/auth/impl/AuthTokenService.hpp index 0f4e8e04..c7f3e5c0 100644 --- a/src/libs/services/auth/impl/AuthTokenService.hpp +++ b/src/libs/services/auth/impl/AuthTokenService.hpp @@ -39,6 +39,7 @@ namespace lms::auth public: AuthTokenService(db::Db& db, std::size_t maxThrottlerEntryCount); + ~AuthTokenService() override = default; AuthTokenService(const AuthTokenService&) = delete; AuthTokenService& operator=(const AuthTokenService&) = delete; AuthTokenService(AuthTokenService&&) = delete; diff --git a/src/libs/services/auth/impl/EnvService.cpp b/src/libs/services/auth/impl/EnvService.cpp index 113303c7..486616ad 100644 --- a/src/libs/services/auth/impl/EnvService.cpp +++ b/src/libs/services/auth/impl/EnvService.cpp @@ -25,8 +25,7 @@ namespace lms::auth { - std::unique_ptr - createEnvService(std::string_view backendName, db::Db& db) + std::unique_ptr createEnvService(std::string_view backendName, db::Db& db) { if (backendName == "http-headers") return std::make_unique(db); diff --git a/src/libs/services/auth/impl/LoginThrottler.hpp b/src/libs/services/auth/impl/LoginThrottler.hpp index 42a20fb8..bfa0b360 100644 --- a/src/libs/services/auth/impl/LoginThrottler.hpp +++ b/src/libs/services/auth/impl/LoginThrottler.hpp @@ -24,7 +24,6 @@ #include -#include "core/Exception.hpp" #include "core/NetAddress.hpp" namespace lms::auth @@ -35,6 +34,10 @@ namespace lms::auth LoginThrottler(std::size_t maxEntries) : _maxEntries{ maxEntries } {} + ~LoginThrottler() = default; + LoginThrottler(const LoginThrottler&) = delete; + LoginThrottler& operator=(const LoginThrottler&) = delete; + // user must lock these calls to avoid races bool isClientThrottled(const boost::asio::ip::address& address) const; void onBadClientAttempt(const boost::asio::ip::address& address); diff --git a/src/libs/services/auth/impl/PasswordServiceBase.cpp b/src/libs/services/auth/impl/PasswordServiceBase.cpp index 4a7a8b91..6972a954 100644 --- a/src/libs/services/auth/impl/PasswordServiceBase.cpp +++ b/src/libs/services/auth/impl/PasswordServiceBase.cpp @@ -27,10 +27,8 @@ #include "pam/PAMPasswordService.hpp" #endif // LMS_SUPPORT_PAM -#include "core/Exception.hpp" #include "core/ILogger.hpp" #include "database/Session.hpp" -#include "database/User.hpp" #include "services/auth/Types.hpp" namespace lms::auth @@ -63,7 +61,7 @@ namespace lms::auth std::shared_lock lock{ _mutex }; if (_loginThrottler.isClientThrottled(clientAddress)) - return { CheckResult::State::Throttled }; + return CheckResult{ .state = CheckResult::State::Throttled, .userId = {} }; } const bool match{ checkUserPassword(loginName, password) }; @@ -71,7 +69,7 @@ namespace lms::auth std::unique_lock lock{ _mutex }; if (_loginThrottler.isClientThrottled(clientAddress)) - return { CheckResult::State::Throttled }; + return CheckResult{ .state = CheckResult::State::Throttled, .userId = {} }; if (match) { @@ -79,13 +77,11 @@ namespace lms::auth const db::UserId userId{ getOrCreateUser(loginName) }; onUserAuthenticated(userId); - return { CheckResult::State::Granted, userId }; - } - else - { - _loginThrottler.onBadClientAttempt(clientAddress); - return { CheckResult::State::Denied }; + return CheckResult{ .state = CheckResult::State::Granted, .userId = userId }; } + + _loginThrottler.onBadClientAttempt(clientAddress); + return CheckResult{ .state = CheckResult::State::Denied, .userId = {} }; } } } // namespace lms::auth diff --git a/src/libs/services/auth/impl/PasswordServiceBase.hpp b/src/libs/services/auth/impl/PasswordServiceBase.hpp index ac75290f..8743e707 100644 --- a/src/libs/services/auth/impl/PasswordServiceBase.hpp +++ b/src/libs/services/auth/impl/PasswordServiceBase.hpp @@ -38,6 +38,7 @@ namespace lms::auth public: PasswordServiceBase(db::Db& db, std::size_t maxThrottlerEntries); + ~PasswordServiceBase() override = default; PasswordServiceBase(const PasswordServiceBase&) = delete; PasswordServiceBase& operator=(const PasswordServiceBase&) = delete; PasswordServiceBase(PasswordServiceBase&&) = delete; diff --git a/src/libs/services/auth/impl/internal/InternalPasswordService.hpp b/src/libs/services/auth/impl/internal/InternalPasswordService.hpp index 3f37f2a2..105fd746 100644 --- a/src/libs/services/auth/impl/internal/InternalPasswordService.hpp +++ b/src/libs/services/auth/impl/internal/InternalPasswordService.hpp @@ -38,7 +38,7 @@ namespace lms::auth bool checkUserPassword(std::string_view loginName, std::string_view password) override; bool canSetPasswords() const override; - PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view loginName, const PasswordValidationContext& context) const override; + PasswordAcceptabilityResult checkPasswordAcceptability(std::string_view password, const PasswordValidationContext& context) const override; void setPassword(db::UserId userId, std::string_view newPassword) override; db::User::PasswordHash hashPassword(std::string_view password) const; diff --git a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp index 12cfe99c..b4f6e24a 100644 --- a/src/libs/services/auth/impl/pam/PAMPasswordService.cpp +++ b/src/libs/services/auth/impl/pam/PAMPasswordService.cpp @@ -27,7 +27,6 @@ #include #include "core/ILogger.hpp" -#include "database/Session.hpp" #include "services/auth/Types.hpp" namespace lms::auth @@ -192,7 +191,7 @@ namespace lms::auth throw NotImplementedException{}; } - void PAMPasswordService::setPassword(db::UserId, std::string_view) + void PAMPasswordService::setPassword(db::UserId /*userId*/, std::string_view /*newPassword*/) { throw NotImplementedException{}; } diff --git a/src/libs/services/auth/impl/pam/PAMPasswordService.hpp b/src/libs/services/auth/impl/pam/PAMPasswordService.hpp index 38bb6d53..4429c952 100644 --- a/src/libs/services/auth/impl/pam/PAMPasswordService.hpp +++ b/src/libs/services/auth/impl/pam/PAMPasswordService.hpp @@ -19,8 +19,6 @@ #pragma once -#include - #include "PasswordServiceBase.hpp" namespace lms::auth diff --git a/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp b/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp index e3fea311..12763ac5 100644 --- a/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp +++ b/src/libs/services/auth/include/services/auth/IAuthTokenService.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -62,7 +61,7 @@ namespace lms::auth }; State state{ State::Denied }; - std::optional authTokenInfo{}; + std::optional authTokenInfo; }; struct DomainParameters diff --git a/src/libs/services/auth/include/services/auth/IEnvService.hpp b/src/libs/services/auth/include/services/auth/IEnvService.hpp index 64f1a41c..23ab73f4 100644 --- a/src/libs/services/auth/include/services/auth/IEnvService.hpp +++ b/src/libs/services/auth/include/services/auth/IEnvService.hpp @@ -19,8 +19,7 @@ #pragma once -#include -#include +#include #include "database/UserId.hpp" diff --git a/src/libs/services/auth/include/services/auth/IPasswordService.hpp b/src/libs/services/auth/include/services/auth/IPasswordService.hpp index 065fcd50..a1a90d68 100644 --- a/src/libs/services/auth/include/services/auth/IPasswordService.hpp +++ b/src/libs/services/auth/include/services/auth/IPasswordService.hpp @@ -19,7 +19,6 @@ #pragma once -#include #include #include @@ -52,7 +51,6 @@ namespace lms::auth }; State state{ State::Denied }; db::UserId userId{}; - std::optional expiry{}; }; virtual CheckResult checkUserPassword(const boost::asio::ip::address& clientAddress, std::string_view loginName, diff --git a/src/libs/services/feedback/impl/FeedbackService.hpp b/src/libs/services/feedback/impl/FeedbackService.hpp index 10cce872..6c5add69 100644 --- a/src/libs/services/feedback/impl/FeedbackService.hpp +++ b/src/libs/services/feedback/impl/FeedbackService.hpp @@ -37,12 +37,11 @@ namespace lms::feedback { public: FeedbackService(boost::asio::io_context& ioContext, db::Db& db); - ~FeedbackService(); - - private: + ~FeedbackService() override; FeedbackService(const FeedbackService&) = delete; FeedbackService& operator=(const FeedbackService&) = delete; + private: void star(db::UserId userId, db::ArtistId artistId) override; void unstar(db::UserId userId, db::ArtistId artistId) override; bool isStarred(db::UserId userId, db::ArtistId artistId) override; @@ -54,8 +53,8 @@ namespace lms::feedback void star(db::UserId userId, db::ReleaseId releaseId) override; void unstar(db::UserId userId, db::ReleaseId releaseId) override; - bool isStarred(db::UserId userId, db::ReleaseId releasedId) override; - Wt::WDateTime getStarredDateTime(db::UserId userId, db::ReleaseId releasedId) override; + bool isStarred(db::UserId userId, db::ReleaseId releaseId) override; + Wt::WDateTime getStarredDateTime(db::UserId userId, db::ReleaseId releaseId) override; ReleaseContainer findStarredReleases(const FindParameters& params) override; void setRating(db::UserId userId, db::ReleaseId releaseId, std::optional rating) override; @@ -70,7 +69,6 @@ namespace lms::feedback void setRating(db::UserId userId, db::TrackId trackId, std::optional rating) override; std::optional getRating(db::UserId userId, db::TrackId trackId) override; - private: std::optional getUserFeedbackBackend(db::UserId userId); template diff --git a/src/libs/services/feedback/impl/IFeedbackBackend.hpp b/src/libs/services/feedback/impl/IFeedbackBackend.hpp index 1d6a6b79..9fbe227e 100644 --- a/src/libs/services/feedback/impl/IFeedbackBackend.hpp +++ b/src/libs/services/feedback/impl/IFeedbackBackend.hpp @@ -30,12 +30,12 @@ namespace lms::feedback public: virtual ~IFeedbackBackend() = default; - virtual void onStarred(db::StarredArtistId) = 0; - virtual void onUnstarred(db::StarredArtistId) = 0; - virtual void onStarred(db::StarredReleaseId) = 0; - virtual void onUnstarred(db::StarredReleaseId) = 0; - virtual void onStarred(db::StarredTrackId) = 0; - virtual void onUnstarred(db::StarredTrackId) = 0; + virtual void onStarred(db::StarredArtistId artistId) = 0; + virtual void onUnstarred(db::StarredArtistId artistId) = 0; + virtual void onStarred(db::StarredReleaseId releaseId) = 0; + virtual void onUnstarred(db::StarredReleaseId releaseId) = 0; + virtual void onStarred(db::StarredTrackId trackId) = 0; + virtual void onUnstarred(db::StarredTrackId trackId) = 0; }; std::unique_ptr createFeedbackBackend(std::string_view backendName); diff --git a/src/libs/services/feedback/impl/internal/InternalBackend.cpp b/src/libs/services/feedback/impl/internal/InternalBackend.cpp index 1fa51022..7d3f1b9a 100644 --- a/src/libs/services/feedback/impl/internal/InternalBackend.cpp +++ b/src/libs/services/feedback/impl/internal/InternalBackend.cpp @@ -53,33 +53,33 @@ namespace lms::feedback { } - void InternalBackend::onStarred(db::StarredArtistId starredArtistId) + void InternalBackend::onStarred(db::StarredArtistId artistId) { - details::onStarred(_db.getTLSSession(), starredArtistId); + details::onStarred(_db.getTLSSession(), artistId); } - void InternalBackend::onUnstarred(db::StarredArtistId starredArtistId) + void InternalBackend::onUnstarred(db::StarredArtistId artistId) { - details::onUnstarred(_db.getTLSSession(), starredArtistId); + details::onUnstarred(_db.getTLSSession(), artistId); } - void InternalBackend::onStarred(db::StarredReleaseId starredReleaseId) + void InternalBackend::onStarred(db::StarredReleaseId releaseId) { - details::onStarred(_db.getTLSSession(), starredReleaseId); + details::onStarred(_db.getTLSSession(), releaseId); } - void InternalBackend::onUnstarred(db::StarredReleaseId starredReleaseId) + void InternalBackend::onUnstarred(db::StarredReleaseId releaseId) { - details::onUnstarred(_db.getTLSSession(), starredReleaseId); + details::onUnstarred(_db.getTLSSession(), releaseId); } - void InternalBackend::onStarred(db::StarredTrackId starredTrackId) + void InternalBackend::onStarred(db::StarredTrackId trackId) { - details::onStarred(_db.getTLSSession(), starredTrackId); + details::onStarred(_db.getTLSSession(), trackId); } - void InternalBackend::onUnstarred(db::StarredTrackId starredTrackId) + void InternalBackend::onUnstarred(db::StarredTrackId trackId) { - details::onUnstarred(_db.getTLSSession(), starredTrackId); + details::onUnstarred(_db.getTLSSession(), trackId); } } // namespace lms::feedback diff --git a/src/libs/services/feedback/impl/internal/InternalBackend.hpp b/src/libs/services/feedback/impl/internal/InternalBackend.hpp index a7447d17..0d41be4e 100644 --- a/src/libs/services/feedback/impl/internal/InternalBackend.hpp +++ b/src/libs/services/feedback/impl/internal/InternalBackend.hpp @@ -32,14 +32,17 @@ namespace lms::feedback { public: InternalBackend(db::Db& db); + ~InternalBackend() override = default; + InternalBackend(const InternalBackend&) = delete; + InternalBackend& operator=(const InternalBackend&) = delete; private: - void onStarred(db::StarredArtistId) override; - void onUnstarred(db::StarredArtistId) override; - void onStarred(db::StarredReleaseId) override; - void onUnstarred(db::StarredReleaseId) override; - void onStarred(db::StarredTrackId) override; - void onUnstarred(db::StarredTrackId) override; + void onStarred(db::StarredArtistId artistId) override; + void onUnstarred(db::StarredArtistId artistId) override; + void onStarred(db::StarredReleaseId releaseId) override; + void onUnstarred(db::StarredReleaseId releaseId) override; + void onStarred(db::StarredTrackId trackId) override; + void onUnstarred(db::StarredTrackId trackId) override; db::Db& _db; }; diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.cpp index 328cf2a6..5d231141 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.cpp @@ -19,6 +19,8 @@ #include "FeedbackTypes.hpp" +#include + namespace lms::feedback::listenBrainz { std::ostream& operator<<(std::ostream& os, const Feedback& feedback) diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.hpp b/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.hpp index 354b1aa1..3c670340 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.hpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbackTypes.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp index fe6738b6..9c2650ac 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksParser.cpp @@ -38,9 +38,9 @@ namespace lms::feedback::listenBrainz throw Exception{ "MBID not found!" }; return Feedback{ - Wt::WDateTime::fromTime_t(static_cast(feedbackObj.get("created"))), - *recordingMBID, - static_cast(static_cast(feedbackObj.get("score"))) + .created = Wt::WDateTime::fromTime_t(static_cast(feedbackObj.get("created"))), + .recordingMBID = *recordingMBID, + .score = static_cast(static_cast(feedbackObj.get("score"))) }; } } // namespace diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp index 2961858f..f463ce55 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp @@ -251,10 +251,9 @@ namespace lms::feedback::listenBrainz LOG(DEBUG, "getFeedbacks aborted"); return; } - else if (ec) - { + + if (ec) throw Exception{ "GetFeedbacks timer failure: " + std::string{ ec.message() } }; - } startSync(); })); @@ -430,7 +429,8 @@ namespace lms::feedback::listenBrainz LOG(DEBUG, "Too many matches for feedback '" << feedback << "': duplicate recording MBIDs found"); return; } - else if (tracks.empty()) + + if (tracks.empty()) { LOG(DEBUG, "Cannot match feedback '" << feedback << "': no track found for this recording MBID"); return; diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.hpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.hpp index 38c01030..0371de51 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.hpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.hpp @@ -27,7 +27,6 @@ #include #include "database/StarredTrackId.hpp" -#include "database/Types.hpp" #include "database/UserId.hpp" #include "FeedbackTypes.hpp" @@ -50,12 +49,14 @@ namespace lms::feedback::listenBrainz { public: FeedbacksSynchronizer(boost::asio::io_context& ioContext, db::Db& db, core::http::IClient& client); + ~FeedbacksSynchronizer() = default; + FeedbacksSynchronizer(const FeedbacksSynchronizer&) = delete; + FeedbacksSynchronizer& operator=(const FeedbacksSynchronizer&) = delete; void enqueFeedback(FeedbackType type, db::StarredTrackId starredTrackId); private: void onFeedbackSent(FeedbackType type, db::StarredTrackId starredTrackId); - void enquePendingFeedbacks(); struct UserContext @@ -63,12 +64,13 @@ namespace lms::feedback::listenBrainz UserContext(db::UserId id) : userId{ id } {} + ~UserContext() = default; UserContext(const UserContext&) = delete; UserContext& operator=(const UserContext&) = delete; const db::UserId userId; bool syncing{}; - std::optional feedbackCount{}; + std::optional feedbackCount; // resetted at each sync std::string listenBrainzUserName; // need to be resolved first diff --git a/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp b/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp index 3789c332..bb7347ad 100644 --- a/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/ListenBrainzBackend.cpp @@ -20,7 +20,6 @@ #include "ListenBrainzBackend.hpp" #include "core/IConfig.hpp" -#include "core/ILogger.hpp" #include "core/Service.hpp" #include "core/http/IClient.hpp" #include "database/Db.hpp" diff --git a/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp b/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp index eb067057..11a8e462 100644 --- a/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp +++ b/src/libs/services/feedback/include/services/feedback/IFeedbackService.hpp @@ -133,6 +133,6 @@ namespace lms::feedback virtual std::optional getRating(db::UserId userId, db::TrackId trackId) = 0; }; - std::unique_ptr createFeedbackService(boost::asio::io_service& ioService, db::Db& db); + std::unique_ptr createFeedbackService(boost::asio::io_service& ioContext, db::Db& db); } // namespace lms::feedback diff --git a/src/libs/services/recommendation/impl/RecommendationService.cpp b/src/libs/services/recommendation/impl/RecommendationService.cpp index 333bfdd2..01a96bde 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.cpp +++ b/src/libs/services/recommendation/impl/RecommendationService.cpp @@ -19,11 +19,8 @@ #include "RecommendationService.hpp" -#include #include -#include "core/Exception.hpp" -#include "core/ILogger.hpp" #include "database/Db.hpp" #include "database/ScanSettings.hpp" #include "database/Session.hpp" diff --git a/src/libs/services/recommendation/impl/RecommendationService.hpp b/src/libs/services/recommendation/impl/RecommendationService.hpp index 721acc1a..76a252e2 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.hpp +++ b/src/libs/services/recommendation/impl/RecommendationService.hpp @@ -42,8 +42,7 @@ namespace lms::recommendation { public: RecommendationService(db::Db& db); - ~RecommendationService() = default; - + ~RecommendationService() override= default; RecommendationService(const RecommendationService&) = delete; RecommendationService& operator=(const RecommendationService&) = delete; @@ -51,7 +50,7 @@ namespace lms::recommendation void load() override; TrackContainer findSimilarTracks(db::TrackListId tracklistId, std::size_t maxCount) const override; - TrackContainer findSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + TrackContainer findSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const override; ReleaseContainer getSimilarReleases(db::ReleaseId releaseId, std::size_t maxCount) const override; ArtistContainer getSimilarArtists(db::ArtistId artistId, core::EnumSet linkTypes, std::size_t maxCount) const override; diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp index b1e43fad..662d9da6 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp @@ -45,7 +45,7 @@ namespace lms::recommendation Session& dbSession{ _db.getTLSSession() }; auto transaction{ dbSession.createReadTransaction() }; - const auto similarTrackIds{ Track::findSimilarTrackIds(dbSession, trackIds, Range{ 0, maxCount }) }; + auto similarTrackIds{ Track::findSimilarTrackIds(dbSession, trackIds, Range{ 0, maxCount }) }; return std::move(similarTrackIds.results); } @@ -105,7 +105,7 @@ namespace lms::recommendation if (!artist) return {}; - const auto similarArtistIds{ artist->findSimilarArtistIds(artistLinkTypes, Range{ 0, maxCount }) }; + auto similarArtistIds{ artist->findSimilarArtistIds(artistLinkTypes, Range{ 0, maxCount }) }; return std::move(similarArtistIds.results); } diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp index 4d019ccd..7cf50846 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.hpp @@ -30,17 +30,18 @@ namespace lms::recommendation ClusterEngine(db::Db& db) : _db{ db } {} + ~ClusterEngine() override = default; ClusterEngine(const ClusterEngine&) = delete; ClusterEngine(ClusterEngine&&) = delete; ClusterEngine& operator=(const ClusterEngine&) = delete; ClusterEngine& operator=(ClusterEngine&&) = delete; private: - void load(bool, const ProgressCallback&) override {} + void load(bool /*forceReload*/, const ProgressCallback& /*progressCallback*/) override {} void requestCancelLoad() override {} TrackContainer findSimilarTracksFromTrackList(db::TrackListId tracklistId, std::size_t maxCount) const override; - TrackContainer findSimilarTracks(const std::vector& tracksId, std::size_t maxCount) const override; + TrackContainer findSimilarTracks(const std::vector& trackIds, std::size_t maxCount) const override; ReleaseContainer getSimilarReleases(db::ReleaseId releaseId, std::size_t maxCount) const override; ArtistContainer getSimilarArtists(db::ArtistId artistId, core::EnumSet linkTypes, std::size_t maxCount) const override; diff --git a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp index 0174a972..731cddd8 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.cpp @@ -21,7 +21,6 @@ #include -#include "core/ILogger.hpp" #include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" diff --git a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.hpp b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.hpp index 2ae75144..15ce02f4 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.hpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveArtists.hpp @@ -21,8 +21,6 @@ #include "IConstraint.hpp" -#include "database/ReleaseId.hpp" - namespace lms::db { class Db; @@ -34,6 +32,9 @@ namespace lms::recommendation::PlaylistGeneratorConstraint { public: ConsecutiveArtists(db::Db& db); + ~ConsecutiveArtists() override = default; + ConsecutiveArtists(const ConsecutiveArtists&) = delete; + ConsecutiveArtists& operator=(const ConsecutiveArtists&) = delete; private: float computeScore(const TrackContainer& trackIds, std::size_t trackIndex) override; diff --git a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp index ab14bbc4..9e072f07 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.cpp @@ -19,7 +19,6 @@ #include "ConsecutiveReleases.hpp" -#include "core/ILogger.hpp" #include "database/Db.hpp" #include "database/Release.hpp" #include "database/Session.hpp" @@ -46,10 +45,10 @@ namespace lms::recommendation::PlaylistGeneratorConstraint for (std::size_t i{ 1 }; i < rangeSize; ++i) { if ((trackIndex >= i) && getReleaseId(trackIds[trackIndex - i]) == releaseId) - score += (1.f / static_cast(i)); + score += (1.F / static_cast(i)); if ((trackIndex + i < trackIds.size()) && getReleaseId(trackIds[trackIndex + i]) == releaseId) - score += (1.f / static_cast(i)); + score += (1.F / static_cast(i)); } return score; diff --git a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.hpp b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.hpp index fc997a72..11480ae7 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.hpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/ConsecutiveReleases.hpp @@ -34,6 +34,9 @@ namespace lms::recommendation::PlaylistGeneratorConstraint { public: ConsecutiveReleases(db::Db& db); + ~ConsecutiveReleases() override = default; + ConsecutiveReleases(const ConsecutiveReleases&) = delete; + ConsecutiveReleases& operator=(const ConsecutiveReleases&) = delete; private: float computeScore(const std::vector& trackIds, std::size_t trackIndex) override; diff --git a/src/libs/services/recommendation/impl/playlist-constraints/DuplicateTracks.cpp b/src/libs/services/recommendation/impl/playlist-constraints/DuplicateTracks.cpp index 8c61f970..50f48980 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/DuplicateTracks.cpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/DuplicateTracks.cpp @@ -26,6 +26,6 @@ namespace lms::recommendation::PlaylistGeneratorConstraint float DuplicateTracks::computeScore(const std::vector& trackIds, std::size_t trackIndex) { const auto count{ std::count(std::cbegin(trackIds), std::cend(trackIds), trackIds[trackIndex]) }; - return count == 1 ? 0 : 1000; + return count == 1 ? 0 : 1'000; } } // namespace lms::recommendation::PlaylistGeneratorConstraint diff --git a/src/libs/services/recommendation/impl/playlist-constraints/IConstraint.hpp b/src/libs/services/recommendation/impl/playlist-constraints/IConstraint.hpp index cf5119fb..1d01c299 100644 --- a/src/libs/services/recommendation/impl/playlist-constraints/IConstraint.hpp +++ b/src/libs/services/recommendation/impl/playlist-constraints/IConstraint.hpp @@ -19,8 +19,6 @@ #pragma once -#include - #include "services/recommendation/Types.hpp" namespace lms::recommendation::PlaylistGeneratorConstraint diff --git a/src/libs/services/recommendation/include/services/recommendation/IPlaylistGeneratorService.hpp b/src/libs/services/recommendation/include/services/recommendation/IPlaylistGeneratorService.hpp index 0676f13f..58bda161 100644 --- a/src/libs/services/recommendation/include/services/recommendation/IPlaylistGeneratorService.hpp +++ b/src/libs/services/recommendation/include/services/recommendation/IPlaylistGeneratorService.hpp @@ -22,7 +22,6 @@ #include #include "database/TrackListId.hpp" -#include "database/Types.hpp" #include "services/recommendation/Types.hpp" namespace lms::db @@ -42,5 +41,5 @@ namespace lms::recommendation virtual TrackContainer extendPlaylist(db::TrackListId tracklistId, std::size_t maxCount) const = 0; }; - std::unique_ptr createPlaylistGeneratorService(db::Db& db, IRecommendationService& recommandationService); + std::unique_ptr createPlaylistGeneratorService(db::Db& db, IRecommendationService& recommendationService); } // namespace lms::recommendation diff --git a/src/libs/services/scanner/impl/FileScanQueue.cpp b/src/libs/services/scanner/impl/FileScanQueue.cpp index 0fcdf95f..40f6c0e0 100644 --- a/src/libs/services/scanner/impl/FileScanQueue.cpp +++ b/src/libs/services/scanner/impl/FileScanQueue.cpp @@ -21,11 +21,8 @@ #include -#include "core/Exception.hpp" -#include "core/IConfig.hpp" #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" -#include "core/Path.hpp" #include "image/Exception.hpp" #include "image/Image.hpp" #include "metadata/Exception.hpp" diff --git a/src/libs/services/scanner/impl/FileScanQueue.hpp b/src/libs/services/scanner/impl/FileScanQueue.hpp index 5503c034..e97f08f0 100644 --- a/src/libs/services/scanner/impl/FileScanQueue.hpp +++ b/src/libs/services/scanner/impl/FileScanQueue.hpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.cpp b/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.cpp index 33729986..18d34bd0 100644 --- a/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.cpp +++ b/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "core/IConfig.hpp" #include "core/ILogger.hpp" @@ -33,8 +34,6 @@ #include "database/Image.hpp" #include "database/Session.hpp" #include "database/Track.hpp" -#include "image/Exception.hpp" -#include "image/Image.hpp" namespace lms::scanner { @@ -55,7 +54,7 @@ namespace lms::scanner db::Session& session; db::ArtistId lastRetrievedArtistId; std::size_t processedArtistCount{}; - const std::vector& artistFileNames; + std::span artistFileNames; }; db::Image::pointer findImageInDirectory(SearchImageContext& searchContext, const std::filesystem::path& directoryPath) diff --git a/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.hpp b/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.hpp index 4d65399d..b041f65a 100644 --- a/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.hpp +++ b/src/libs/services/scanner/impl/ScanStepAssociateArtistImages.hpp @@ -30,6 +30,9 @@ namespace lms::scanner { public: ScanStepAssociateArtistImages(InitParams& initParams); + ~ScanStepAssociateArtistImages() override = default; + ScanStepAssociateArtistImages(const ScanStepAssociateArtistImages&) = delete; + ScanStepAssociateArtistImages& operator=(const ScanStepAssociateArtistImages&) = delete; private: ScanStep getStep() const override { return ScanStep::AssociateArtistImages; } diff --git a/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.cpp b/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.cpp index 891eea6f..cefb76ec 100644 --- a/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.cpp +++ b/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.cpp @@ -33,8 +33,6 @@ #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" -#include "image/Exception.hpp" -#include "image/Image.hpp" namespace lms::scanner { diff --git a/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.hpp b/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.hpp index 0b4c9ee8..abf8a62f 100644 --- a/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.hpp +++ b/src/libs/services/scanner/impl/ScanStepAssociateReleaseImages.hpp @@ -30,6 +30,9 @@ namespace lms::scanner { public: ScanStepAssociateReleaseImages(InitParams& initParams); + ~ScanStepAssociateReleaseImages() override = default; + ScanStepAssociateReleaseImages(const ScanStepAssociateReleaseImages&) = delete; + ScanStepAssociateReleaseImages& operator=(const ScanStepAssociateReleaseImages&) = delete; private: ScanStep getStep() const override { return ScanStep::AssociateReleaseImages; } diff --git a/src/libs/services/scanner/impl/ScanStepBase.hpp b/src/libs/services/scanner/impl/ScanStepBase.hpp index a530dc1d..eea8339d 100644 --- a/src/libs/services/scanner/impl/ScanStepBase.hpp +++ b/src/libs/services/scanner/impl/ScanStepBase.hpp @@ -55,6 +55,8 @@ namespace lms::scanner } protected: + ~ScanStepBase() override = default; + const ScannerSettings& _settings; ProgressCallback _progressCallback; bool& _abortScan; diff --git a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp index bc2f99b1..f776c6eb 100644 --- a/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp +++ b/src/libs/services/scanner/impl/ScanStepComputeClusterStats.cpp @@ -19,7 +19,6 @@ #include "ScanStepComputeClusterStats.hpp" #include "core/ILogger.hpp" -#include "core/Path.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" #include "database/Session.hpp" diff --git a/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.cpp b/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.cpp index 9cc84ab9..8cd5dfdc 100644 --- a/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.cpp +++ b/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.cpp @@ -20,7 +20,6 @@ #include "ScanStepRemoveOrphanedDbEntries.hpp" #include "core/ILogger.hpp" -#include "core/Path.hpp" #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Db.hpp" diff --git a/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.hpp b/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.hpp index 90346c98..f53a6b87 100644 --- a/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.hpp +++ b/src/libs/services/scanner/impl/ScanStepRemoveOrphanedDbEntries.hpp @@ -19,8 +19,6 @@ #pragma once -#include - #include "ScanStepBase.hpp" namespace lms::scanner diff --git a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp index 1126959a..f930370d 100644 --- a/src/libs/services/scanner/impl/ScanStepScanFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepScanFiles.cpp @@ -36,7 +36,6 @@ #include "database/TrackArtistLink.hpp" #include "database/TrackFeatures.hpp" #include "database/TrackLyrics.hpp" -#include "metadata/Exception.hpp" #include "metadata/IParser.hpp" namespace lms::scanner @@ -391,9 +390,9 @@ namespace lms::scanner if (readStyle == "fast") return metadata::ParserReadStyle::Fast; - else if (readStyle == "average") + if (readStyle == "average") return metadata::ParserReadStyle::Average; - else if (readStyle == "accurate") + if (readStyle == "accurate") return metadata::ParserReadStyle::Accurate; throw core::LmsException{ "Invalid value for 'scanner-parser-read-style'" }; @@ -888,16 +887,9 @@ namespace lms::scanner return; } - bool added; + const bool added{ !image }; if (!image) - { image = dbSession.create(file); - added = true; - } - else - { - added = false; - } image.modify()->setLastWriteTime(fileInfo->lastWriteTime); image.modify()->setFileSize(fileInfo->fileSize); @@ -945,16 +937,11 @@ namespace lms::scanner return; } - bool added; + const bool added{ !trackLyrics }; if (!trackLyrics) { trackLyrics = dbSession.create(); trackLyrics.modify()->setAbsoluteFilePath(file); - added = true; - } - else - { - added = false; } trackLyrics.modify()->setLastWriteTime(fileInfo->lastWriteTime); diff --git a/src/libs/services/scanner/impl/ScanStepScanFiles.hpp b/src/libs/services/scanner/impl/ScanStepScanFiles.hpp index 01041114..372c3301 100644 --- a/src/libs/services/scanner/impl/ScanStepScanFiles.hpp +++ b/src/libs/services/scanner/impl/ScanStepScanFiles.hpp @@ -47,9 +47,9 @@ namespace lms::scanner bool checkLyricsFileNeedScan(ScanContext& context, const std::filesystem::path& file); void processFileScanResults(ScanContext& context, std::span scanResults, const ScannerSettings::MediaLibraryInfo& libraryInfo); - void processAudioFileScanData(ScanContext& context, const std::filesystem::path& path, const metadata::Track* trackMetadata, const ScannerSettings::MediaLibraryInfo& libraryInfo); - void processImageFileScanData(ScanContext& context, const std::filesystem::path& path, const ImageInfo* imageInfo, const ScannerSettings::MediaLibraryInfo& libraryInfo); - void processLyricsFileScanData(ScanContext& context, const std::filesystem::path& path, const metadata::Lyrics* lyrics, const ScannerSettings::MediaLibraryInfo& libraryInfo); + void processAudioFileScanData(ScanContext& context, const std::filesystem::path& file, const metadata::Track* trackMetadata, const ScannerSettings::MediaLibraryInfo& libraryInfo); + void processImageFileScanData(ScanContext& context, const std::filesystem::path& file, const ImageInfo* imageInfo, const ScannerSettings::MediaLibraryInfo& libraryInfo); + void processLyricsFileScanData(ScanContext& context, const std::filesystem::path& file, const metadata::Lyrics* lyrics, const ScannerSettings::MediaLibraryInfo& libraryInfo); std::unique_ptr _metadataParser; const std::vector _extraTagsToParse; diff --git a/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.cpp b/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.cpp index 3520e7f4..d9a0ec34 100644 --- a/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.cpp +++ b/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.cpp @@ -19,8 +19,6 @@ #include "ScanStepUpdateLibraryFields.hpp" -#include "core/ILogger.hpp" -#include "core/Path.hpp" #include "database/Db.hpp" #include "database/Directory.hpp" #include "database/MediaLibrary.hpp" diff --git a/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.hpp b/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.hpp index 6bdfc9b2..dc671abe 100644 --- a/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.hpp +++ b/src/libs/services/scanner/impl/ScanStepUpdateLibraryFields.hpp @@ -19,8 +19,6 @@ #pragma once -#include "database/DirectoryId.hpp" - #include "ScanStepBase.hpp" namespace lms::scanner diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index afb82f32..f691aede 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -21,11 +21,9 @@ #include -#include "core/Exception.hpp" #include "core/IConfig.hpp" #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" -#include "core/Path.hpp" #include "database/MediaLibrary.hpp" #include "database/ScanSettings.hpp" #include "database/TrackFeatures.hpp" diff --git a/src/libs/services/scanner/impl/ScannerService.hpp b/src/libs/services/scanner/impl/ScannerService.hpp index 3f4a57aa..58a59aed 100644 --- a/src/libs/services/scanner/impl/ScannerService.hpp +++ b/src/libs/services/scanner/impl/ScannerService.hpp @@ -31,10 +31,8 @@ #include "IScanStep.hpp" #include "ScannerSettings.hpp" -#include "core/Path.hpp" #include "database/Db.hpp" #include "database/Session.hpp" -#include "database/Types.hpp" #include "services/scanner/IScannerService.hpp" namespace lms::scanner @@ -44,11 +42,10 @@ namespace lms::scanner public: ScannerService(db::Db& db); ~ScannerService() override; - - private: ScannerService(const ScannerService&) = delete; ScannerService& operator=(const ScannerService&) = delete; + private: void requestReload() override; void requestImmediateScan(const ScanOptions& scanOptions) override; @@ -83,7 +80,7 @@ namespace lms::scanner Wt::WIOService _ioService; boost::asio::system_timer _scheduleTimer{ _ioService }; Events _events; - std::chrono::system_clock::time_point _lastScanInProgressEmit{}; + std::chrono::system_clock::time_point _lastScanInProgressEmit; db::Db& _db; mutable std::shared_mutex _statusMutex; diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index cf44404b..c2e45a5e 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -58,9 +58,9 @@ namespace lms res += track->getName(); if (track->getRelease()) res += " [" + std::string{ track->getRelease()->getName() } + "]"; - for (auto artist : track->getArtists({ TrackArtistLinkType::Artist })) + for (const auto& artist : track->getArtists({ TrackArtistLinkType::Artist })) res += " - " + artist->getName(); - for (auto cluster : track->getClusters()) + for (const auto& cluster : track->getClusters()) res += " {" + std::string{ cluster->getType()->getName() } + "-" + std::string{ cluster->getName() } + "}"; return res; From 1dc7e143ef99bc41da261b0d54833f1e85bb1f1b Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 6 Dec 2024 14:09:01 +0100 Subject: [PATCH 07/20] Format code --- .../feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp | 2 +- src/libs/services/recommendation/impl/RecommendationService.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp index f463ce55..973225e1 100644 --- a/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp +++ b/src/libs/services/feedback/impl/listenbrainz/FeedbacksSynchronizer.cpp @@ -429,7 +429,7 @@ namespace lms::feedback::listenBrainz LOG(DEBUG, "Too many matches for feedback '" << feedback << "': duplicate recording MBIDs found"); return; } - + if (tracks.empty()) { LOG(DEBUG, "Cannot match feedback '" << feedback << "': no track found for this recording MBID"); diff --git a/src/libs/services/recommendation/impl/RecommendationService.hpp b/src/libs/services/recommendation/impl/RecommendationService.hpp index 76a252e2..d5cde868 100644 --- a/src/libs/services/recommendation/impl/RecommendationService.hpp +++ b/src/libs/services/recommendation/impl/RecommendationService.hpp @@ -42,7 +42,7 @@ namespace lms::recommendation { public: RecommendationService(db::Db& db); - ~RecommendationService() override= default; + ~RecommendationService() override = default; RecommendationService(const RecommendationService&) = delete; RecommendationService& operator=(const RecommendationService&) = delete; From 350e213529cb9c27c9fc2e712e235d5e48366f60 Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 7 Dec 2024 11:48:15 +0100 Subject: [PATCH 08/20] Various minor cleanup --- src/libs/services/scrobbling/impl/IScrobblingBackend.hpp | 1 - .../include/services/scrobbling/IScrobblingService.hpp | 2 +- src/libs/subsonic/impl/SubsonicResource.cpp | 2 +- src/libs/subsonic/impl/endpoints/Browsing.cpp | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/services/scrobbling/impl/IScrobblingBackend.hpp b/src/libs/services/scrobbling/impl/IScrobblingBackend.hpp index 60c6f128..66070dc8 100644 --- a/src/libs/services/scrobbling/impl/IScrobblingBackend.hpp +++ b/src/libs/services/scrobbling/impl/IScrobblingBackend.hpp @@ -20,7 +20,6 @@ #pragma once #include -#include #include #include "services/scrobbling/Listen.hpp" diff --git a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp index a0a26008..5c00444c 100644 --- a/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp +++ b/src/libs/services/scrobbling/include/services/scrobbling/IScrobblingService.hpp @@ -134,5 +134,5 @@ namespace lms::scrobbling virtual TrackContainer getTopTracks(const FindParameters& params) = 0; }; - std::unique_ptr createScrobblingService(boost::asio::io_service& ioService, db::Db& db); + std::unique_ptr createScrobblingService(boost::asio::io_context& ioContext, db::Db& db); } // namespace lms::scrobbling diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 7ea0b82f..c21a54b0 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -65,7 +65,7 @@ namespace lms::api::subsonic core::Service::get()->visitStrings("api-subsonic-old-server-protocol-clients", [&](std::string_view client) { - res.emplace(std::string{ client }, ProtocolVersion{ 1, 12, 0 }); + res.emplace(std::string{ client }, ProtocolVersion{ .major = 1, .minor = 12, .patch = 0 }); }, { "DSub" }); diff --git a/src/libs/subsonic/impl/endpoints/Browsing.cpp b/src/libs/subsonic/impl/endpoints/Browsing.cpp index 453262ff..79fe87ed 100644 --- a/src/libs/subsonic/impl/endpoints/Browsing.cpp +++ b/src/libs/subsonic/impl/endpoints/Browsing.cpp @@ -164,7 +164,7 @@ namespace lms::api::subsonic return tracks; } - std::vector findSimilarSongs(RequestContext&, TrackId trackId, std::size_t count) + std::vector findSimilarSongs(RequestContext& /*context*/, TrackId trackId, std::size_t count) { return core::Service::get()->findSimilarTracks({ trackId }, count); } From def4d8c3e390fe36fdcf14dd7c0ae302d68f035a Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 7 Dec 2024 18:27:01 +0100 Subject: [PATCH 09/20] Subsonic API: getCoverArt: serve raw image if size is not provided --- src/libs/av/impl/AudioFile.cpp | 3 +- src/libs/image/CMakeLists.txt | 4 +- src/libs/image/impl/EncodedImage.cpp | 103 ++++++++++++++++++ .../impl/{SvgImage.hpp => EncodedImage.hpp} | 17 ++- src/libs/image/impl/SvgImage.cpp | 55 ---------- src/libs/image/impl/graphicsmagick/Image.cpp | 54 ++++++--- .../image/impl/graphicsmagick/JPEGImage.cpp | 57 ---------- .../image/impl/graphicsmagick/JPEGImage.hpp | 41 ------- .../image/impl/graphicsmagick/RawImage.cpp | 15 +-- .../image/impl/graphicsmagick/RawImage.hpp | 8 +- src/libs/image/impl/stb/Image.cpp | 53 ++++++--- src/libs/image/impl/stb/JPEGImage.cpp | 62 ----------- src/libs/image/impl/stb/RawImage.cpp | 19 +--- src/libs/image/impl/stb/RawImage.hpp | 5 +- .../image/include/image/IEncodedImage.hpp | 4 +- src/libs/image/include/image/IRawImage.hpp | 5 +- src/libs/image/include/image/Image.hpp | 9 +- .../JPEGImage.hpp => include/image/Types.hpp} | 26 +---- .../services/artwork/impl/ArtworkService.cpp | 85 +++++++++------ .../services/artwork/impl/ArtworkService.hpp | 16 ++- src/libs/services/artwork/impl/ImageCache.cpp | 14 ++- src/libs/services/artwork/impl/ImageCache.hpp | 6 +- .../services/artwork/IArtworkService.hpp | 7 +- .../impl/endpoints/MediaRetrieval.cpp | 7 +- src/lms/ui/resource/ArtworkResource.cpp | 2 +- 25 files changed, 305 insertions(+), 372 deletions(-) create mode 100644 src/libs/image/impl/EncodedImage.cpp rename src/libs/image/impl/{SvgImage.hpp => EncodedImage.hpp} (58%) delete mode 100644 src/libs/image/impl/SvgImage.cpp delete mode 100644 src/libs/image/impl/graphicsmagick/JPEGImage.cpp delete mode 100644 src/libs/image/impl/graphicsmagick/JPEGImage.hpp delete mode 100644 src/libs/image/impl/stb/JPEGImage.cpp rename src/libs/image/{impl/stb/JPEGImage.hpp => include/image/Types.hpp} (54%) diff --git a/src/libs/av/impl/AudioFile.cpp b/src/libs/av/impl/AudioFile.cpp index 1067fb7c..bd0b6504 100644 --- a/src/libs/av/impl/AudioFile.cpp +++ b/src/libs/av/impl/AudioFile.cpp @@ -242,11 +242,10 @@ namespace lms::av void AudioFile::visitAttachedPictures(std::function func) const { static const std::unordered_map codecMimeMap{ - { AV_CODEC_ID_BMP, "image/x-bmp" }, + { AV_CODEC_ID_BMP, "image/bmp" }, { AV_CODEC_ID_GIF, "image/gif" }, { AV_CODEC_ID_MJPEG, "image/jpeg" }, { AV_CODEC_ID_PNG, "image/png" }, - { AV_CODEC_ID_PNG, "image/x-png" }, { AV_CODEC_ID_PPM, "image/x-portable-pixmap" }, }; diff --git a/src/libs/image/CMakeLists.txt b/src/libs/image/CMakeLists.txt index 5c041943..15555911 100644 --- a/src/libs/image/CMakeLists.txt +++ b/src/libs/image/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(lmsimage SHARED - impl/SvgImage.cpp + impl/EncodedImage.cpp ) target_include_directories(lmsimage INTERFACE @@ -26,7 +26,6 @@ if (${LMS_IMAGE_BACKEND} STREQUAL "stb") target_sources(lmsimage PRIVATE impl/stb/Image.cpp - impl/stb/JPEGImage.cpp impl/stb/RawImage.cpp ) target_compile_options(lmsimage PRIVATE "-DSTB_IMAGE_RESIZE_VERSION=${STB_IMAGE_RESIZE_VERSION}") @@ -38,7 +37,6 @@ elseif (${LMS_IMAGE_BACKEND} STREQUAL "graphicsmagick") target_sources(lmsimage PRIVATE impl/graphicsmagick/Image.cpp - impl/graphicsmagick/JPEGImage.cpp impl/graphicsmagick/RawImage.cpp ) target_link_libraries(lmsimage PRIVATE PkgConfig::GraphicsMagick++) diff --git a/src/libs/image/impl/EncodedImage.cpp b/src/libs/image/impl/EncodedImage.cpp new file mode 100644 index 00000000..bef1cbca --- /dev/null +++ b/src/libs/image/impl/EncodedImage.cpp @@ -0,0 +1,103 @@ +/* + * 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 "EncodedImage.hpp" + +#include +#include +#include + +#include "core/ITraceLogger.hpp" +#include "core/String.hpp" +#include "image/Exception.hpp" + +namespace lms::image +{ + namespace + { + std::string_view extensionToMimeType(const std::filesystem::path& extension) + { + static const std::unordered_map mimeTypesByExtension{ + { ".bmp", "image/bmp" }, + { ".gif", "image/gif" }, + { ".jpeg", "image/jpeg" }, + { ".jpg", "image/jpeg" }, + { ".png", "image/png" }, + { ".ppm", "image/x-portable-pixmap" }, + { ".svg", "image/svg+xml" }, + }; + + const auto it{ mimeTypesByExtension.find(core::stringUtils::stringToLower(extension.c_str())) }; + if (it == std::cend(mimeTypesByExtension)) + throw Exception{ "Unhandled image extension '" + extension.string() + "'" }; + return it->second; + } + + std::vector fileToBuffer(const std::filesystem::path& p) + { + LMS_SCOPED_TRACE_DETAILED("Image", "ReadFile"); + + 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 data; + } + + } // namespace + + std::unique_ptr readImage(const std::filesystem::path& path) + { + return std::make_unique(path); + } + + std::unique_ptr readImage(std::span encodedData, std::string_view mimeType) + { + return std::make_unique(encodedData, mimeType); + } + + EncodedImage::EncodedImage(std::vector&& data, std::string_view mimeType) + : _data{ std::move(data) } + , _mimeType(mimeType) + { + } + + EncodedImage::EncodedImage(std::span data, std::string_view mimeType) + : _data(std::cbegin(data), std::cend(data)) + , _mimeType(mimeType) + { + } + + EncodedImage::EncodedImage(const std::filesystem::path& p) + : EncodedImage::EncodedImage{ fileToBuffer(p), extensionToMimeType(p.extension()) } + { + } +} // namespace lms::image \ No newline at end of file diff --git a/src/libs/image/impl/SvgImage.hpp b/src/libs/image/impl/EncodedImage.hpp similarity index 58% rename from src/libs/image/impl/SvgImage.hpp rename to src/libs/image/impl/EncodedImage.hpp index 4befe5cd..fcf87691 100644 --- a/src/libs/image/impl/SvgImage.hpp +++ b/src/libs/image/impl/EncodedImage.hpp @@ -19,23 +19,28 @@ #pragma once +#include #include #include "image/IEncodedImage.hpp" namespace lms::image { - class SvgImage : public IEncodedImage + class EncodedImage : public IEncodedImage { public: - SvgImage(std::vector&& data) - : _data{ std::move(data) } {} + EncodedImage(const std::filesystem::path& path); + EncodedImage(std::vector&& data, std::string_view mimeType); + EncodedImage(std::span data, std::string_view mimeType); + ~EncodedImage() override = default; + EncodedImage(const EncodedImage&) = delete; + EncodedImage& operator=(const EncodedImage&) = delete; - const std::byte* getData() const override { return &_data.front(); } - std::size_t getDataSize() const override { return _data.size(); } - std::string_view getMimeType() const override { return "image/svg+xml"; } + std::span getData() const override { return _data; } + std::string_view getMimeType() const override { return _mimeType; } private: const std::vector _data; + const std::string _mimeType; }; } // namespace lms::image \ No newline at end of file diff --git a/src/libs/image/impl/SvgImage.cpp b/src/libs/image/impl/SvgImage.cpp deleted file mode 100644 index 4534e4a0..00000000 --- a/src/libs/image/impl/SvgImage.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 - -#include "core/ITraceLogger.hpp" -#include "image/Exception.hpp" - -namespace lms::image -{ - std::unique_ptr readSvgFile(const std::filesystem::path& p) - { - LMS_SCOPED_TRACE_DETAILED("Image", "ReadSVG"); - - 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)); - } -} // namespace lms::image \ No newline at end of file diff --git a/src/libs/image/impl/graphicsmagick/Image.cpp b/src/libs/image/impl/graphicsmagick/Image.cpp index 44a59786..16f54fbe 100644 --- a/src/libs/image/impl/graphicsmagick/Image.cpp +++ b/src/libs/image/impl/graphicsmagick/Image.cpp @@ -19,26 +19,17 @@ #include "image/Image.hpp" -#include +#include -#include "RawImage.hpp" #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" +#include "image/Exception.hpp" + +#include "EncodedImage.hpp" +#include "RawImage.hpp" namespace lms::image { - std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize) - { - LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); - return std::make_unique(encodedData, encodedDataSize); - } - - std::unique_ptr decodeImage(const std::filesystem::path& path) - { - LMS_SCOPED_TRACE_DETAILED("Image", "DecodeFile"); - return std::make_unique(path); - } - void init(const std::filesystem::path& path) { Magick::InitializeMagick(path.string().c_str()); @@ -61,4 +52,39 @@ namespace lms::image static const std::array fileExtensions{ ".jpg", ".jpeg", ".png", ".bmp" }; return fileExtensions; } + + std::unique_ptr decodeImage(std::span encodedData) + { + LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); + return std::make_unique(encodedData); + } + + std::unique_ptr decodeImage(const std::filesystem::path& path) + { + LMS_SCOPED_TRACE_DETAILED("Image", "DecodeFile"); + return std::make_unique(path); + } + + std::unique_ptr encodeToJPEG(const IRawImage& rawImage, unsigned quality) + { + LMS_SCOPED_TRACE_DETAILED("Image", "WriteJPEG"); + + try + { + Magick::Image image{ static_cast(rawImage).getMagickImage() }; + image.magick("JPEG"); + image.quality(quality); + + Magick::Blob blob; + image.write(&blob); + + return std::make_unique(std::span{ static_cast(blob.data()), blob.length() }, "image/jpeg"); + } + catch (Magick::Exception& e) + { + LMS_LOG(COVER, ERROR, "Caught Magick exception: " << e.what()); + throw Exception{ std::string{ "Magick read error: " } + e.what() }; + } + } + } // namespace lms::image diff --git a/src/libs/image/impl/graphicsmagick/JPEGImage.cpp b/src/libs/image/impl/graphicsmagick/JPEGImage.cpp deleted file mode 100644 index 94738b20..00000000 --- a/src/libs/image/impl/graphicsmagick/JPEGImage.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2020 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 "JPEGImage.hpp" - -#include "core/ILogger.hpp" -#include "core/ITraceLogger.hpp" -#include "image/Exception.hpp" - -#include "RawImage.hpp" - -namespace lms::image::GraphicsMagick -{ - JPEGImage::JPEGImage(const RawImage& rawImage, unsigned quality) - { - LMS_SCOPED_TRACE_DETAILED("Image", "WriteJPEG"); - - try - { - Magick::Image image{ rawImage.getMagickImage() }; - image.magick("JPEG"); - image.quality(quality); - image.write(&_blob); - } - catch (Magick::Exception& e) - { - LMS_LOG(COVER, ERROR, "Caught Magick exception: " << e.what()); - throw Exception{ std::string{ "Magick read error: " } + e.what() }; - } - } - - const std::byte* JPEGImage::getData() const - { - return reinterpret_cast(_blob.data()); - } - - std::size_t JPEGImage::getDataSize() const - { - return _blob.length(); - } -} // namespace lms::image::GraphicsMagick diff --git a/src/libs/image/impl/graphicsmagick/JPEGImage.hpp b/src/libs/image/impl/graphicsmagick/JPEGImage.hpp deleted file mode 100644 index 4d9386c0..00000000 --- a/src/libs/image/impl/graphicsmagick/JPEGImage.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2020 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 "image/IEncodedImage.hpp" - -namespace lms::image::GraphicsMagick -{ - class RawImage; - class JPEGImage : public IEncodedImage - { - public: - JPEGImage(const RawImage& rawImage, unsigned quality); - - private: - const std::byte* getData() const override; - std::size_t getDataSize() const override; - std::string_view getMimeType() const override { return "image/jpeg"; } - - Magick::Blob _blob; - }; -} // namespace lms::image::GraphicsMagick diff --git a/src/libs/image/impl/graphicsmagick/RawImage.cpp b/src/libs/image/impl/graphicsmagick/RawImage.cpp index f3550478..3e7d017a 100644 --- a/src/libs/image/impl/graphicsmagick/RawImage.cpp +++ b/src/libs/image/impl/graphicsmagick/RawImage.cpp @@ -19,24 +19,19 @@ #include "RawImage.hpp" -#include -#include - #include #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" #include "image/Exception.hpp" -#include "JPEGImage.hpp" - namespace lms::image::GraphicsMagick { - RawImage::RawImage(const std::byte* encodedData, std::size_t encodedDataSize) + RawImage::RawImage(std::span encodedData) { try { - Magick::Blob blob{ encodedData, encodedDataSize }; + Magick::Blob blob{ encodedData.data(), encodedData.size() }; _image.read(blob); } catch (Magick::WarningCoder& e) @@ -102,14 +97,8 @@ namespace lms::image::GraphicsMagick } } - std::unique_ptr RawImage::encodeToJPEG(unsigned quality) const - { - return std::make_unique(*this, quality); - } - Magick::Image RawImage::getMagickImage() const { return _image; } - } // namespace lms::image::GraphicsMagick diff --git a/src/libs/image/impl/graphicsmagick/RawImage.hpp b/src/libs/image/impl/graphicsmagick/RawImage.hpp index 13fc7b59..525f70ac 100644 --- a/src/libs/image/impl/graphicsmagick/RawImage.hpp +++ b/src/libs/image/impl/graphicsmagick/RawImage.hpp @@ -21,10 +21,10 @@ #include #include +#include #include -#include "image/IEncodedImage.hpp" #include "image/IRawImage.hpp" namespace lms::image::GraphicsMagick @@ -32,19 +32,17 @@ namespace lms::image::GraphicsMagick class RawImage : public IRawImage { public: - RawImage(const std::byte* encodedData, std::size_t encodedDataSize); + RawImage(std::span encodedData); RawImage(const std::filesystem::path& path); ImageSize getWidth() const override; ImageSize getHeight() const override; void resize(ImageSize width) override; - std::unique_ptr encodeToJPEG(unsigned quality) const override; - private: - friend class JPEGImage; Magick::Image getMagickImage() const; + private: Magick::Image _image; }; } // namespace lms::image::GraphicsMagick diff --git a/src/libs/image/impl/stb/Image.cpp b/src/libs/image/impl/stb/Image.cpp index 06b953aa..28e443d2 100644 --- a/src/libs/image/impl/stb/Image.cpp +++ b/src/libs/image/impl/stb/Image.cpp @@ -21,24 +21,18 @@ #include -#include "RawImage.hpp" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + #include "core/ITraceLogger.hpp" +#include "EncodedImage.hpp" +#include "RawImage.hpp" +#include "image/Exception.hpp" + namespace lms::image { - std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize) - { - LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); - return std::make_unique(encodedData, encodedDataSize); - } - - std::unique_ptr decodeImage(const std::filesystem::path& path) - { - LMS_SCOPED_TRACE_DETAILED("Image", "DecodeFile"); - return std::make_unique(path); - } - - void init(const std::filesystem::path&) + void init(const std::filesystem::path& /*unused*/) { } @@ -47,4 +41,35 @@ namespace lms::image static const std::array fileExtensions{ ".jpg", ".jpeg", ".png", ".bmp" }; return fileExtensions; } + + std::unique_ptr decodeImage(std::span encodedData) + { + LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); + return std::make_unique(encodedData); + } + + std::unique_ptr decodeImage(const std::filesystem::path& path) + { + LMS_SCOPED_TRACE_DETAILED("Image", "DecodeFile"); + return std::make_unique(path); + } + + std::unique_ptr encodeToJPEG(const IRawImage& rawImage, unsigned quality) + { + LMS_SCOPED_TRACE_DETAILED("Image", "WriteJPEG"); + + std::vector encodedData; + + auto writeCb{ [](void* ctx, void* writeData, int writeSize) { + auto& output{ *reinterpret_cast*>(ctx) }; + const std::size_t currentOutputSize{ output.size() }; + output.resize(currentOutputSize + writeSize); + std::copy(reinterpret_cast(writeData), reinterpret_cast(writeData) + writeSize, output.data() + currentOutputSize); + } }; + + if (::stbi_write_jpg_to_func(writeCb, &encodedData, rawImage.getWidth(), rawImage.getHeight(), 3, static_cast(rawImage).getData(), quality) == 0) + throw Exception{ "Failed to export in jpeg format!" }; + + return std::make_unique(std::move(encodedData), "image/jpeg"); + } } // namespace lms::image \ No newline at end of file diff --git a/src/libs/image/impl/stb/JPEGImage.cpp b/src/libs/image/impl/stb/JPEGImage.cpp deleted file mode 100644 index bf3cf93a..00000000 --- a/src/libs/image/impl/stb/JPEGImage.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2020 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 "JPEGImage.hpp" - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include - -#include "core/ITraceLogger.hpp" -#include "image/Exception.hpp" - -#include "RawImage.hpp" - -namespace lms::image::STB -{ - JPEGImage::JPEGImage(const RawImage& rawImage, unsigned quality) - { - LMS_SCOPED_TRACE_DETAILED("Image", "WriteJPEG"); - - auto writeCb{ [](void* ctx, void* writeData, int writeSize) { - auto& output{ *reinterpret_cast*>(ctx) }; - const std::size_t currentOutputSize{ output.size() }; - output.resize(currentOutputSize + writeSize); - std::copy(reinterpret_cast(writeData), reinterpret_cast(writeData) + writeSize, output.data() + currentOutputSize); - } }; - - if (stbi_write_jpg_to_func(writeCb, &_data, rawImage.getWidth(), rawImage.getHeight(), 3, rawImage.getData(), quality) == 0) - { - _data.clear(); - throw Exception{ "Failed to export in jpeg format!" }; - } - } - - const std::byte* JPEGImage::getData() const - { - if (_data.empty()) - return nullptr; - - return &_data.front(); - } - - std::size_t JPEGImage::getDataSize() const - { - return _data.size(); - } -} // namespace lms::image::STB diff --git a/src/libs/image/impl/stb/RawImage.cpp b/src/libs/image/impl/stb/RawImage.cpp index 3187fd08..99021bf1 100644 --- a/src/libs/image/impl/stb/RawImage.cpp +++ b/src/libs/image/impl/stb/RawImage.cpp @@ -39,21 +39,19 @@ #include "core/ITraceLogger.hpp" #include "image/Exception.hpp" -#include "JPEGImage.hpp" - namespace lms::image::STB { - RawImage::RawImage(const std::byte* encodedData, std::size_t encodedDataSize) + RawImage::RawImage(std::span encodedData) { int n{}; - _data = UniquePtrFree{ ::stbi_load_from_memory(reinterpret_cast(encodedData), encodedDataSize, &_width, &_height, &n, 3), std::free }; + _data = UniquePtrFree{ ::stbi_load_from_memory(reinterpret_cast(encodedData.data()), encodedData.size(), &_width, &_height, &n, 3), std::free }; if (!_data) throw Exception{ "Cannot load image from memory: " + std::string{ ::stbi_failure_reason() } }; } RawImage::RawImage(const std::filesystem::path& p) { - int n; + int n{}; _data = UniquePtrFree{ stbi_load(p.string().c_str(), &_width, &_height, &n, 3), std::free }; if (!_data) throw Exception{ "Cannot load image from file: " + std::string{ ::stbi_failure_reason() } }; @@ -63,7 +61,7 @@ namespace lms::image::STB { LMS_SCOPED_TRACE_DETAILED("Image", "Resize"); - size_t height; + size_t height{}; if (_width == _height) { height = width; @@ -104,11 +102,6 @@ namespace lms::image::STB _width = width; } - std::unique_ptr RawImage::encodeToJPEG(unsigned quality) const - { - return std::make_unique(*this, quality); - } - ImageSize RawImage::getWidth() const { return _width; @@ -121,9 +114,7 @@ namespace lms::image::STB const std::byte* RawImage::getData() const { - if (!_data) - return nullptr; - + assert(_data); return reinterpret_cast(_data.get()); } } // namespace lms::image::STB \ No newline at end of file diff --git a/src/libs/image/impl/stb/RawImage.hpp b/src/libs/image/impl/stb/RawImage.hpp index 044217fd..b002decb 100644 --- a/src/libs/image/impl/stb/RawImage.hpp +++ b/src/libs/image/impl/stb/RawImage.hpp @@ -21,8 +21,8 @@ #include #include +#include -#include "image/IEncodedImage.hpp" #include "image/IRawImage.hpp" namespace lms::image::STB @@ -30,7 +30,7 @@ namespace lms::image::STB class RawImage : public IRawImage { public: - RawImage(const std::byte* encodedData, std::size_t encodedDataSize); + RawImage(std::span encodedData); RawImage(const std::filesystem::path& path); ~RawImage() override = default; @@ -41,7 +41,6 @@ namespace lms::image::STB ImageSize getHeight() const override; void resize(ImageSize width) override; - std::unique_ptr encodeToJPEG(unsigned quality) const override; const std::byte* getData() const; diff --git a/src/libs/image/include/image/IEncodedImage.hpp b/src/libs/image/include/image/IEncodedImage.hpp index 9bed88c0..f14120c9 100644 --- a/src/libs/image/include/image/IEncodedImage.hpp +++ b/src/libs/image/include/image/IEncodedImage.hpp @@ -20,6 +20,7 @@ #pragma once #include +#include #include namespace lms::image @@ -31,8 +32,7 @@ namespace lms::image public: virtual ~IEncodedImage() = default; - virtual const std::byte* getData() const = 0; - virtual std::size_t getDataSize() const = 0; + virtual std::span getData() const = 0; virtual std::string_view getMimeType() const = 0; }; } // namespace lms::image \ 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 3347228f..ad512905 100644 --- a/src/libs/image/include/image/IRawImage.hpp +++ b/src/libs/image/include/image/IRawImage.hpp @@ -19,9 +19,7 @@ #pragma once -#include - -#include "image/IEncodedImage.hpp" +#include "image/Types.hpp" namespace lms::image { @@ -34,6 +32,5 @@ namespace lms::image virtual ImageSize getHeight() const = 0; virtual void resize(ImageSize width) = 0; - virtual std::unique_ptr encodeToJPEG(unsigned quality) const = 0; }; } // namespace lms::image diff --git a/src/libs/image/include/image/Image.hpp b/src/libs/image/include/image/Image.hpp index 7bf6592f..39fc67f8 100644 --- a/src/libs/image/include/image/Image.hpp +++ b/src/libs/image/include/image/Image.hpp @@ -30,7 +30,12 @@ namespace lms::image { void init(const std::filesystem::path& path); std::span getSupportedFileExtensions(); - std::unique_ptr decodeImage(const std::byte* encodedData, std::size_t encodedDataSize); + + std::unique_ptr decodeImage(std::span encodedData); std::unique_ptr decodeImage(const std::filesystem::path& path); - std::unique_ptr readSvgFile(const std::filesystem::path& path); + + std::unique_ptr readImage(std::span encodedData, std::string_view mimeType); + std::unique_ptr readImage(const std::filesystem::path& path); + + std::unique_ptr encodeToJPEG(const IRawImage& rawImage, unsigned quality); } // namespace lms::image \ No newline at end of file diff --git a/src/libs/image/impl/stb/JPEGImage.hpp b/src/libs/image/include/image/Types.hpp similarity index 54% rename from src/libs/image/impl/stb/JPEGImage.hpp rename to src/libs/image/include/image/Types.hpp index 3b4ad706..ccd56849 100644 --- a/src/libs/image/impl/stb/JPEGImage.hpp +++ b/src/libs/image/include/image/Types.hpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Emeric Poupon + * Copyright (C) 2024 Emeric Poupon * * This file is part of LMS. * @@ -17,25 +17,9 @@ * along with LMS. If not, see . */ -#pragma once +#include -#include - -#include "image/IEncodedImage.hpp" - -namespace lms::image::STB +namespace lms::image { - class RawImage; - class JPEGImage : public IEncodedImage - { - public: - JPEGImage(const RawImage& rawImage, unsigned quality); - - private: - const std::byte* getData() const override; - std::size_t getDataSize() const override; - std::string_view getMimeType() const override { return "image/jpeg"; } - - std::vector _data; - }; -} // namespace lms::image::STB + using ImageSize = std::size_t; +} \ No newline at end of file diff --git a/src/libs/services/artwork/impl/ArtworkService.cpp b/src/libs/services/artwork/impl/ArtworkService.cpp index 839e4556..e9f7f01e 100644 --- a/src/libs/services/artwork/impl/ArtworkService.cpp +++ b/src/libs/services/artwork/impl/ArtworkService.cpp @@ -19,6 +19,8 @@ #include "ArtworkService.hpp" +#include + #include "av/IAudioFile.hpp" #include "av/Types.hpp" #include "core/IConfig.hpp" @@ -31,6 +33,7 @@ #include "database/Session.hpp" #include "database/Track.hpp" #include "image/Exception.hpp" +#include "image/IEncodedImage.hpp" #include "image/Image.hpp" namespace lms::cover @@ -48,8 +51,6 @@ namespace lms::cover return std::make_unique(db, defaultReleaseCoverSvgPath, defaultArtistImageSvgPath); } - using namespace image; - ArtworkService::ArtworkService(db::Db& db, const std::filesystem::path& defaultReleaseCoverSvgPath, const std::filesystem::path& defaultArtistImageSvgPath) @@ -61,13 +62,13 @@ namespace lms::cover LMS_LOG(COVER, INFO, "Default release cover path = '" << defaultReleaseCoverSvgPath.string() << "'"); LMS_LOG(COVER, INFO, "Max cache size = " << _cache.getMaxCacheSize()); - _defaultReleaseCover = image::readSvgFile(defaultReleaseCoverSvgPath); // may throw - _defaultArtistImage = image::readSvgFile(defaultArtistImageSvgPath); // may throw + _defaultReleaseCover = image::readImage(defaultReleaseCoverSvgPath); // may throw + _defaultArtistImage = image::readImage(defaultArtistImageSvgPath); // may throw } - std::unique_ptr ArtworkService::getFromAvMediaFile(const av::IAudioFile& input, ImageSize width) const + std::unique_ptr ArtworkService::getFromAvMediaFile(const av::IAudioFile& input, std::optional width) const { - std::unique_ptr image; + std::unique_ptr image; input.visitAttachedPictures([&](const av::Picture& picture, const av::IAudioFile::MetadataMap& /* metadata */) { if (image) @@ -75,9 +76,16 @@ namespace lms::cover try { - std::unique_ptr rawImage{ decodeImage(picture.data, picture.dataSize) }; - rawImage->resize(width); - image = rawImage->encodeToJPEG(_jpegQuality); + if (!width) + { + image = image::readImage(std::span{ picture.data, picture.dataSize }, picture.mimeType); + } + else + { + auto rawImage{ image::decodeImage(std::span{ picture.data, picture.dataSize }) }; + rawImage->resize(*width); + image = image::encodeToJPEG(*rawImage, _jpegQuality); + } } catch (const image::Exception& e) { @@ -88,15 +96,22 @@ namespace lms::cover return image; } - std::unique_ptr ArtworkService::getFromImageFile(const std::filesystem::path& p, ImageSize width) const + std::unique_ptr ArtworkService::getFromImageFile(const std::filesystem::path& p, std::optional width) const { - std::unique_ptr image; + std::unique_ptr image; try { - std::unique_ptr rawImage{ decodeImage(p) }; - rawImage->resize(width); - image = rawImage->encodeToJPEG(_jpegQuality); + if (!width) + { + image = image::readImage(p); + } + else + { + auto rawImage{ image::decodeImage(p) }; + rawImage->resize(*width); + image = image::encodeToJPEG(*rawImage, _jpegQuality); + } } catch (const image::Exception& e) { @@ -106,17 +121,17 @@ namespace lms::cover return image; } - std::shared_ptr ArtworkService::getDefaultReleaseCover() + std::shared_ptr ArtworkService::getDefaultReleaseCover() { return _defaultReleaseCover; } - std::shared_ptr ArtworkService::getDefaultArtistImage() + std::shared_ptr ArtworkService::getDefaultArtistImage() { return _defaultArtistImage; } - bool ArtworkService::checkImageFile(const std::filesystem::path& filePath) const + bool ArtworkService::checkImageFile(const std::filesystem::path& filePath) { std::error_code ec; @@ -132,9 +147,9 @@ namespace lms::cover return true; } - std::unique_ptr ArtworkService::getTrackImage(const std::filesystem::path& p, ImageSize width) const + std::unique_ptr ArtworkService::getTrackImage(const std::filesystem::path& p, std::optional width) const { - std::unique_ptr image; + std::unique_ptr image; try { @@ -148,77 +163,81 @@ namespace lms::cover return image; } - std::shared_ptr ArtworkService::getTrackImage(db::TrackId trackId, ImageSize width) + std::shared_ptr ArtworkService::getTrackImage(db::TrackId trackId, std::optional width) { const ImageCache::EntryDesc cacheEntryDesc{ trackId, width }; - std::shared_ptr cover{ _cache.getImage(cacheEntryDesc) }; + std::shared_ptr cover{ _cache.getImage(cacheEntryDesc) }; if (cover) return cover; + std::filesystem::path trackFile; { db::Session& session{ _db.getTLSSession() }; auto transaction{ session.createReadTransaction() }; const db::Track::pointer track{ db::Track::find(session, trackId) }; if (track && track->hasCover()) - cover = getTrackImage(track->getAbsoluteFilePath(), width); + trackFile = track->getAbsoluteFilePath(); } + cover = getTrackImage(trackFile, width); if (cover) _cache.addImage(cacheEntryDesc, cover); return cover; } - std::shared_ptr ArtworkService::getReleaseCover(db::ReleaseId releaseId, ImageSize width) + std::shared_ptr ArtworkService::getReleaseCover(db::ReleaseId releaseId, std::optional width) { - using namespace db; const ImageCache::EntryDesc cacheEntryDesc{ releaseId, width }; - std::shared_ptr image{ _cache.getImage(cacheEntryDesc) }; + std::shared_ptr image{ _cache.getImage(cacheEntryDesc) }; if (image) return image; + std::filesystem::path imagePath; { - Session& session{ _db.getTLSSession() }; + db::Session& session{ _db.getTLSSession() }; auto transaction{ session.createReadTransaction() }; const db::Release::pointer release{ db::Release::find(session, releaseId) }; if (release) { if (const db::Image::pointer dbImage{ release->getImage() }) - image = getFromImageFile(dbImage->getAbsoluteFilePath(), width); + imagePath = dbImage->getAbsoluteFilePath(); } } + image = getFromImageFile(imagePath, width); if (image) _cache.addImage(cacheEntryDesc, image); return image; } - std::shared_ptr ArtworkService::getArtistImage(db::ArtistId artistId, ImageSize width) + std::shared_ptr ArtworkService::getArtistImage(db::ArtistId artistId, std::optional width) { - using namespace db; const ImageCache::EntryDesc cacheEntryDesc{ artistId, width }; - std::shared_ptr artistImage{ _cache.getImage(cacheEntryDesc) }; + std::shared_ptr artistImage{ _cache.getImage(cacheEntryDesc) }; if (artistImage) return artistImage; + std::filesystem::path imagePath; { - Session& session{ _db.getTLSSession() }; + db::Session& session{ _db.getTLSSession() }; auto transaction{ session.createReadTransaction() }; - if (const Artist::pointer artist{ Artist::find(session, artistId) }) + if (const db::Artist::pointer artist{ db::Artist::find(session, artistId) }) { if (const db::Image::pointer image{ artist->getImage() }) - artistImage = getFromImageFile(image->getAbsoluteFilePath(), width); + imagePath = image->getAbsoluteFilePath(); } } + artistImage = getFromImageFile(imagePath, width); if (artistImage) _cache.addImage(cacheEntryDesc, artistImage); diff --git a/src/libs/services/artwork/impl/ArtworkService.hpp b/src/libs/services/artwork/impl/ArtworkService.hpp index ff8fa2f1..f08452a3 100644 --- a/src/libs/services/artwork/impl/ArtworkService.hpp +++ b/src/libs/services/artwork/impl/ArtworkService.hpp @@ -48,22 +48,20 @@ namespace lms::cover ArtworkService& operator=(const ArtworkService&) = delete; private: - 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 getTrackImage(db::TrackId trackId, std::optional width) override; + std::shared_ptr getReleaseCover(db::ReleaseId releaseId, std::optional width) override; + std::shared_ptr getArtistImage(db::ArtistId artistId, std::optional width) override; std::shared_ptr getDefaultReleaseCover() override; std::shared_ptr getDefaultArtistImage() override; void flushCache() override; void setJpegQuality(unsigned quality) override; - 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 getFromAvMediaFile(const av::IAudioFile& input, std::optional width) const; + std::unique_ptr getFromImageFile(const std::filesystem::path& p, std::optional width) const; + std::unique_ptr getTrackImage(const std::filesystem::path& path, std::optional width) const; - std::unique_ptr getTrackImage(const std::filesystem::path& path, image::ImageSize width) const; - - bool checkImageFile(const std::filesystem::path& filePath) const; + static bool checkImageFile(const std::filesystem::path& filePath); db::Db& _db; diff --git a/src/libs/services/artwork/impl/ImageCache.cpp b/src/libs/services/artwork/impl/ImageCache.cpp index 13ffc31e..586cf4b4 100644 --- a/src/libs/services/artwork/impl/ImageCache.cpp +++ b/src/libs/services/artwork/impl/ImageCache.cpp @@ -33,21 +33,29 @@ namespace lms::cover void ImageCache::addImage(const EntryDesc& entryDesc, std::shared_ptr image) { + // cache only resized files + if (!entryDesc.size) + return; + const std::unique_lock lock{ _mutex }; - while (_cacheSize + image->getDataSize() > _maxCacheSize && !_cache.empty()) + while (_cacheSize + image->getData().size() > _maxCacheSize && !_cache.empty()) { auto itRandom{ core::random::pickRandom(_cache) }; - _cacheSize -= itRandom->second->getDataSize(); + _cacheSize -= itRandom->second->getData().size(); _cache.erase(itRandom); } - _cacheSize += image->getDataSize(); + _cacheSize += image->getData().size(); _cache[entryDesc] = image; } std::shared_ptr ImageCache::getImage(const EntryDesc& entryDesc) const { + // cache only resized files + if (!entryDesc.size) + return {}; + const std::shared_lock lock{ _mutex }; const auto it{ _cache.find(entryDesc) }; diff --git a/src/libs/services/artwork/impl/ImageCache.hpp b/src/libs/services/artwork/impl/ImageCache.hpp index 41445296..d5866930 100644 --- a/src/libs/services/artwork/impl/ImageCache.hpp +++ b/src/libs/services/artwork/impl/ImageCache.hpp @@ -20,6 +20,7 @@ #pragma once #include +#include #include #include #include @@ -40,7 +41,7 @@ namespace lms::cover { using VariantType = std::variant; VariantType id; - std::size_t size; + std::optional size; bool operator==(const EntryDesc& other) const = default; }; @@ -60,7 +61,8 @@ namespace lms::cover { std::size_t operator()(const EntryDesc& entry) const { - return std::hash{}(entry.id) ^ std::hash{}(entry.size); + assert(entry.size); // should not cache unresized images + return std::hash{}(entry.id) ^ std::hash{}(*entry.size); } }; diff --git a/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp index 6bb119f0..b0592015 100644 --- a/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp +++ b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp @@ -21,6 +21,7 @@ #include #include +#include #include "database/ArtistId.hpp" #include "database/ReleaseId.hpp" @@ -39,13 +40,13 @@ namespace lms::cover public: virtual ~IArtworkService() = default; - virtual std::shared_ptr getArtistImage(db::ArtistId artistId, image::ImageSize width) = 0; + virtual std::shared_ptr getArtistImage(db::ArtistId artistId, std::optional width) = 0; // no logic to fallback to release here - virtual std::shared_ptr getTrackImage(db::TrackId trackId, image::ImageSize width) = 0; + virtual std::shared_ptr getTrackImage(db::TrackId trackId, std::optional width) = 0; // no logic to fallback to track here - virtual std::shared_ptr getReleaseCover(db::ReleaseId releaseId, image::ImageSize width) = 0; + virtual std::shared_ptr getReleaseCover(db::ReleaseId releaseId, std::optional width) = 0; // Svg images dont have image "size" virtual std::shared_ptr getDefaultReleaseCover() = 0; diff --git a/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp index a6b41938..78e09410 100644 --- a/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp @@ -333,8 +333,9 @@ namespace lms::api::subsonic if (!trackId && !releaseId && !artistId) throw BadParameterGenericError{ "id" }; - std::size_t size{ getParameterAs(context.parameters, "size").value_or(1024) }; - size = core::utils::clamp(size, std::size_t{ 32 }, std::size_t{ 2048 }); + std::optional size{ getParameterAs(context.parameters, "size") }; + if (size) + *size = core::utils::clamp(*size, std::size_t{ 32 }, std::size_t{ 2048 }); std::shared_ptr cover; if (trackId) @@ -353,7 +354,7 @@ namespace lms::api::subsonic return; } - response.out().write(reinterpret_cast(cover->getData()), cover->getDataSize()); + response.out().write(reinterpret_cast(cover->getData().data()), cover->getData().size()); response.setMimeType(std::string{ cover->getMimeType() }); } diff --git a/src/lms/ui/resource/ArtworkResource.cpp b/src/lms/ui/resource/ArtworkResource.cpp index 131f085c..0bf8147b 100644 --- a/src/lms/ui/resource/ArtworkResource.cpp +++ b/src/lms/ui/resource/ArtworkResource.cpp @@ -232,7 +232,7 @@ namespace lms::ui if (image) { response.setMimeType(std::string{ image->getMimeType() }); - response.out().write(reinterpret_cast(image->getData()), image->getDataSize()); + response.out().write(reinterpret_cast(image->getData().data()), image->getData().size()); } else response.setStatus(404); From 38efdfaf870c2aee960956895250b74b76ddf5ed Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 8 Dec 2024 11:53:31 +0100 Subject: [PATCH 10/20] Prefer first embedded image that contains the 'front' keyword --- src/libs/av/impl/AudioFile.cpp | 4 +- src/libs/av/include/av/IAudioFile.hpp | 4 +- src/libs/core/impl/String.cpp | 12 +++++ src/libs/core/include/core/String.hpp | 1 + src/libs/core/test/String.cpp | 12 +++++ .../services/artwork/impl/ArtworkService.cpp | 45 +++++++++++++++---- 6 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/libs/av/impl/AudioFile.cpp b/src/libs/av/impl/AudioFile.cpp index bd0b6504..593ebbe4 100644 --- a/src/libs/av/impl/AudioFile.cpp +++ b/src/libs/av/impl/AudioFile.cpp @@ -281,9 +281,7 @@ namespace lms::av const AVPacket& pkt{ avstream->attached_pic }; - picture.data = reinterpret_cast(pkt.data); - picture.dataSize = pkt.size; - + picture.data = std::span{ reinterpret_cast(pkt.data), static_cast(pkt.size) }; func(picture, metadata); } } diff --git a/src/libs/av/include/av/IAudioFile.hpp b/src/libs/av/include/av/IAudioFile.hpp index 244fef58..4a57b0c2 100644 --- a/src/libs/av/include/av/IAudioFile.hpp +++ b/src/libs/av/include/av/IAudioFile.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -60,8 +61,7 @@ namespace lms::av struct Picture { std::string mimeType; - const std::byte* data{}; // valid as long as IAudioFile exists - std::size_t dataSize{}; + std::span data; // valid as long as IAudioFile exists }; struct ContainerInfo diff --git a/src/libs/core/impl/String.cpp b/src/libs/core/impl/String.cpp index 9a736474..5677b34c 100644 --- a/src/libs/core/impl/String.cpp +++ b/src/libs/core/impl/String.cpp @@ -321,6 +321,18 @@ namespace lms::core::stringUtils return true; } + std::string_view::size_type stringCaseInsensitiveContains(std::string_view str, std::string_view strtoFind) + { + if (str.empty() && strtoFind.empty()) + return true; // same as std + + const auto it{ std::search( + std::cbegin(str), std::cend(str), + std::cbegin(strtoFind), std::cend(strtoFind), + [](char chA, char chB) { return std::tolower(chA) == std::tolower(chB); }) }; + return (it != std::cend(str)); + } + void capitalize(std::string& str) { for (auto it{ std::begin(str) }; it != std::end(str); ++it) diff --git a/src/libs/core/include/core/String.hpp b/src/libs/core/include/core/String.hpp index 867e73e6..652ac009 100644 --- a/src/libs/core/include/core/String.hpp +++ b/src/libs/core/include/core/String.hpp @@ -62,6 +62,7 @@ namespace lms::core::stringUtils [[nodiscard]] std::string bufferToString(std::span data); [[nodiscard]] bool stringCaseInsensitiveEqual(std::string_view strA, std::string_view strB); + [[nodiscard]] std::string_view::size_type stringCaseInsensitiveContains(std::string_view str, std::string_view strtoFind); void capitalize(std::string& str); diff --git a/src/libs/core/test/String.cpp b/src/libs/core/test/String.cpp index 3d66bb99..7bae9e3e 100644 --- a/src/libs/core/test/String.cpp +++ b/src/libs/core/test/String.cpp @@ -322,4 +322,16 @@ namespace lms::core::stringUtils::tests EXPECT_FALSE(stringEndsWith("FooBar", "1FooBar")); EXPECT_FALSE(stringEndsWith("FooBar", "R")); } + + TEST(StringUtils, stringCaseInsensitiveContains) + { + EXPECT_TRUE(stringCaseInsensitiveContains("FooBar", "Bar")); + EXPECT_TRUE(stringCaseInsensitiveContains("FooBar", "bar")); + EXPECT_TRUE(stringCaseInsensitiveContains("FooBar", "Foo")); + EXPECT_TRUE(stringCaseInsensitiveContains("FooBar", "foo")); + EXPECT_FALSE(stringCaseInsensitiveContains("something", "foo")); + EXPECT_TRUE(stringCaseInsensitiveContains("FooBar", "")); + EXPECT_TRUE(stringCaseInsensitiveContains("", "")); + EXPECT_FALSE(stringCaseInsensitiveContains("", "Foo")); + } } // namespace lms::core::stringUtils::tests \ No newline at end of file diff --git a/src/libs/services/artwork/impl/ArtworkService.cpp b/src/libs/services/artwork/impl/ArtworkService.cpp index e9f7f01e..2612be66 100644 --- a/src/libs/services/artwork/impl/ArtworkService.cpp +++ b/src/libs/services/artwork/impl/ArtworkService.cpp @@ -19,12 +19,15 @@ #include "ArtworkService.hpp" +#include +#include #include #include "av/IAudioFile.hpp" #include "av/Types.hpp" #include "core/IConfig.hpp" #include "core/ILogger.hpp" +#include "core/String.hpp" #include "core/Utils.hpp" #include "database/Artist.hpp" #include "database/Db.hpp" @@ -68,21 +71,47 @@ namespace lms::cover std::unique_ptr ArtworkService::getFromAvMediaFile(const av::IAudioFile& input, std::optional width) const { + struct CandidatePicture + { + av::Picture picture; + bool isFront{}; + std::size_t index; + + // > means is better candidate + bool operator>(const CandidatePicture& other) const + { + if (!isFront && other.isFront) + return false; + if (isFront && !other.isFront) + return true; + + return index < other.index; + } + }; + + auto metadataHasFrontKeyword{ [](const av::IAudioFile::MetadataMap& metadata) { + return std::any_of(std::cbegin(metadata), std::cend(metadata), [](const auto& keyValue) { return core::stringUtils::stringCaseInsensitiveContains(keyValue.second, "front"); }); + } }; + + std::vector candidatePictures; + std::size_t pictureIndex{}; + input.visitAttachedPictures([&](const av::Picture& picture, const av::IAudioFile::MetadataMap& metadata) { + candidatePictures.emplace_back(picture, metadataHasFrontKeyword(metadata), pictureIndex++); + }); + std::stable_sort(std::begin(candidatePictures), std::end(candidatePictures), std::greater<>()); + std::unique_ptr image; - - input.visitAttachedPictures([&](const av::Picture& picture, const av::IAudioFile::MetadataMap& /* metadata */) { - if (image) - return; - + for (const CandidatePicture& candidatePicture : candidatePictures) + { try { if (!width) { - image = image::readImage(std::span{ picture.data, picture.dataSize }, picture.mimeType); + image = image::readImage(candidatePicture.picture.data, candidatePicture.picture.mimeType); } else { - auto rawImage{ image::decodeImage(std::span{ picture.data, picture.dataSize }) }; + auto rawImage{ image::decodeImage(candidatePicture.picture.data) }; rawImage->resize(*width); image = image::encodeToJPEG(*rawImage, _jpegQuality); } @@ -91,7 +120,7 @@ namespace lms::cover { LMS_LOG(COVER, ERROR, "Cannot read embedded cover: " << e.what()); } - }); + } return image; } From 4a8754a7fa1e2741bf11e5d54b0aee9119efb582 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 8 Dec 2024 15:48:52 +0100 Subject: [PATCH 11/20] Subsonic API: added timestamps into coverart ids, Made use of image ids to save a lookup, ref #558 --- conf/lms.conf | 3 - .../services/artwork/impl/ArtworkService.cpp | 81 +++++----------- .../services/artwork/impl/ArtworkService.hpp | 3 +- src/libs/services/artwork/impl/ImageCache.hpp | 5 +- .../services/artwork/IArtworkService.hpp | 8 +- src/libs/subsonic/CMakeLists.txt | 1 + src/libs/subsonic/impl/CoverArtId.cpp | 97 +++++++++++++++++++ src/libs/subsonic/impl/CoverArtId.hpp | 49 ++++++++++ src/libs/subsonic/impl/RequestContext.hpp | 1 - src/libs/subsonic/impl/SubsonicResource.cpp | 16 --- src/libs/subsonic/impl/SubsonicResource.hpp | 1 - .../impl/endpoints/MediaRetrieval.cpp | 30 ++---- src/libs/subsonic/impl/responses/Album.cpp | 9 +- src/libs/subsonic/impl/responses/Artist.cpp | 8 +- src/libs/subsonic/impl/responses/Playlist.cpp | 4 +- src/libs/subsonic/impl/responses/Song.cpp | 16 ++- src/lms/ui/resource/ArtworkResource.cpp | 94 +++++++----------- src/lms/ui/resource/ArtworkResource.hpp | 6 +- 18 files changed, 253 insertions(+), 179 deletions(-) create mode 100644 src/libs/subsonic/impl/CoverArtId.cpp create mode 100644 src/libs/subsonic/impl/CoverArtId.hpp diff --git a/conf/lms.conf b/conf/lms.conf index 56822c3f..d64477a7 100644 --- a/conf/lms.conf +++ b/conf/lms.conf @@ -79,9 +79,6 @@ api-subsonic-support-user-password-auth = true; # Main usage is to make auto detections for the 'p' (password) parameter work api-subsonic-old-server-protocol-clients = ("DSub"); -# List of clients for whom a default cover is served (as they do not have their own) -api-subsonic-default-cover-clients = ("DSub", "substreamer"); - # List of clients for whom open subsonic extensions and extra fields are disabled api-open-subsonic-disabled-clients = ("DSub"); diff --git a/src/libs/services/artwork/impl/ArtworkService.cpp b/src/libs/services/artwork/impl/ArtworkService.cpp index 2612be66..2aff44c1 100644 --- a/src/libs/services/artwork/impl/ArtworkService.cpp +++ b/src/libs/services/artwork/impl/ArtworkService.cpp @@ -192,6 +192,31 @@ namespace lms::cover return image; } + std::shared_ptr ArtworkService::getImage(db::ImageId imageId, std::optional width) + { + const ImageCache::EntryDesc cacheEntryDesc{ imageId, width }; + + std::shared_ptr cover{ _cache.getImage(cacheEntryDesc) }; + if (cover) + return cover; + + std::filesystem::path imageFile; + { + db::Session& session{ _db.getTLSSession() }; + auto transaction{ session.createReadTransaction() }; + + const db::Image::pointer image{ db::Image::find(session, imageId) }; + if (image) + imageFile = image->getAbsoluteFilePath(); + } + + cover = getFromImageFile(imageFile, width); + if (cover) + _cache.addImage(cacheEntryDesc, cover); + + return cover; + } + std::shared_ptr ArtworkService::getTrackImage(db::TrackId trackId, std::optional width) { const ImageCache::EntryDesc cacheEntryDesc{ trackId, width }; @@ -217,62 +242,6 @@ namespace lms::cover return cover; } - std::shared_ptr ArtworkService::getReleaseCover(db::ReleaseId releaseId, std::optional width) - { - const ImageCache::EntryDesc cacheEntryDesc{ releaseId, width }; - - std::shared_ptr image{ _cache.getImage(cacheEntryDesc) }; - if (image) - return image; - - std::filesystem::path imagePath; - { - db::Session& session{ _db.getTLSSession() }; - auto transaction{ session.createReadTransaction() }; - - const db::Release::pointer release{ db::Release::find(session, releaseId) }; - if (release) - { - if (const db::Image::pointer dbImage{ release->getImage() }) - imagePath = dbImage->getAbsoluteFilePath(); - } - } - - image = getFromImageFile(imagePath, width); - if (image) - _cache.addImage(cacheEntryDesc, image); - - return image; - } - - std::shared_ptr ArtworkService::getArtistImage(db::ArtistId artistId, std::optional width) - { - const ImageCache::EntryDesc cacheEntryDesc{ artistId, width }; - - std::shared_ptr artistImage{ _cache.getImage(cacheEntryDesc) }; - if (artistImage) - return artistImage; - - std::filesystem::path imagePath; - { - db::Session& session{ _db.getTLSSession() }; - - auto transaction{ session.createReadTransaction() }; - - if (const db::Artist::pointer artist{ db::Artist::find(session, artistId) }) - { - if (const db::Image::pointer image{ artist->getImage() }) - imagePath = image->getAbsoluteFilePath(); - } - } - - artistImage = getFromImageFile(imagePath, width); - if (artistImage) - _cache.addImage(cacheEntryDesc, artistImage); - - return artistImage; - } - void ArtworkService::flushCache() { _cache.flush(); diff --git a/src/libs/services/artwork/impl/ArtworkService.hpp b/src/libs/services/artwork/impl/ArtworkService.hpp index f08452a3..1dc1f5cd 100644 --- a/src/libs/services/artwork/impl/ArtworkService.hpp +++ b/src/libs/services/artwork/impl/ArtworkService.hpp @@ -48,9 +48,8 @@ namespace lms::cover ArtworkService& operator=(const ArtworkService&) = delete; private: + std::shared_ptr getImage(db::ImageId imageId, std::optional width) override; std::shared_ptr getTrackImage(db::TrackId trackId, std::optional width) override; - std::shared_ptr getReleaseCover(db::ReleaseId releaseId, std::optional width) override; - std::shared_ptr getArtistImage(db::ArtistId artistId, std::optional width) override; std::shared_ptr getDefaultReleaseCover() override; std::shared_ptr getDefaultArtistImage() override; diff --git a/src/libs/services/artwork/impl/ImageCache.hpp b/src/libs/services/artwork/impl/ImageCache.hpp index d5866930..6b60d577 100644 --- a/src/libs/services/artwork/impl/ImageCache.hpp +++ b/src/libs/services/artwork/impl/ImageCache.hpp @@ -25,8 +25,7 @@ #include #include -#include "database/ArtistId.hpp" -#include "database/ReleaseId.hpp" +#include "database/ImageId.hpp" #include "database/TrackId.hpp" #include "image/IEncodedImage.hpp" @@ -39,7 +38,7 @@ namespace lms::cover struct EntryDesc { - using VariantType = std::variant; + using VariantType = std::variant; VariantType id; std::optional size; diff --git a/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp index b0592015..66f986e6 100644 --- a/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp +++ b/src/libs/services/artwork/include/services/artwork/IArtworkService.hpp @@ -23,8 +23,7 @@ #include #include -#include "database/ArtistId.hpp" -#include "database/ReleaseId.hpp" +#include "database/ImageId.hpp" #include "database/TrackId.hpp" #include "image/IEncodedImage.hpp" @@ -40,14 +39,11 @@ namespace lms::cover public: virtual ~IArtworkService() = default; - virtual std::shared_ptr getArtistImage(db::ArtistId artistId, std::optional width) = 0; + virtual std::shared_ptr getImage(db::ImageId imageId, std::optional width) = 0; // no logic to fallback to release here virtual std::shared_ptr getTrackImage(db::TrackId trackId, std::optional width) = 0; - // no logic to fallback to track here - virtual std::shared_ptr getReleaseCover(db::ReleaseId releaseId, std::optional width) = 0; - // Svg images dont have image "size" virtual std::shared_ptr getDefaultReleaseCover() = 0; virtual std::shared_ptr getDefaultArtistImage() = 0; diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index 76f2630c..875fce08 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -25,6 +25,7 @@ add_library(lmssubsonic SHARED impl/responses/ReplayGain.cpp impl/responses/Song.cpp impl/responses/User.cpp + impl/CoverArtId.cpp impl/ResponseFormat.cpp impl/ProtocolVersion.cpp impl/ParameterParsing.cpp diff --git a/src/libs/subsonic/impl/CoverArtId.cpp b/src/libs/subsonic/impl/CoverArtId.cpp new file mode 100644 index 00000000..7b64fd3b --- /dev/null +++ b/src/libs/subsonic/impl/CoverArtId.cpp @@ -0,0 +1,97 @@ +/* + * 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 . + */ + +#include "CoverArtId.hpp" + +#include "SubsonicId.hpp" +#include "core/String.hpp" + +namespace lms::api::subsonic +{ + namespace + { + constexpr char timestampSeparatorChar{ ':' }; + } + + std::string idToString(db::ImageId id) + { + return "im-" + id.toString(); + } + + std::string idToString(CoverArtId coverId) + { + // produce "id:timestamp" + std::string res{ std::visit([](auto&& id) { + return idToString(id); + }, + coverId.id) }; + + res += timestampSeparatorChar; + res += std::to_string(coverId.timestamp); + + return res; + } +} // namespace lms::api::subsonic + +// Used to parse parameters +namespace lms::core::stringUtils +{ + template<> + std::optional readAs(std::string_view str) + { + std::vector values{ core::stringUtils::splitString(str, '-') }; + if (values.size() != 2) + return std::nullopt; + + if (values[0] != "im") + return std::nullopt; + + if (const auto value{ core::stringUtils::readAs(values[1]) }) + return db::ImageId{ *value }; + + return std::nullopt; + } + + template<> + std::optional readAs(std::string_view str) + { + // expect "id:timestamp" + auto timeStampSeparator{ str.find_last_of(api::subsonic::timestampSeparatorChar) }; + if (timeStampSeparator == std::string_view::npos) + return std::nullopt; + + std::string_view strId{ str.substr(0, timeStampSeparator) }; + std::string_view strTimestamp{ str.substr(timeStampSeparator + 1) }; + + api::subsonic::CoverArtId cover; + if (const auto imagetId{ readAs(strId) }) + cover.id = *imagetId; + else if (const auto trackId{ readAs(strId) }) + cover.id = *trackId; + else + return std::nullopt; + + if (const auto timestamp{ readAs(strTimestamp) }) + cover.timestamp = *timestamp; + else + return std::nullopt; + + return cover; + } +} // namespace lms::core::stringUtils diff --git a/src/libs/subsonic/impl/CoverArtId.hpp b/src/libs/subsonic/impl/CoverArtId.hpp new file mode 100644 index 00000000..6753e9bc --- /dev/null +++ b/src/libs/subsonic/impl/CoverArtId.hpp @@ -0,0 +1,49 @@ +/* + * 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 "core/String.hpp" +#include "database/ImageId.hpp" +#include "database/TrackId.hpp" + +namespace lms::api::subsonic +{ + struct CoverArtId + { + std::variant id; + std::time_t timestamp; + }; + + std::string idToString(CoverArtId coverId); + std::string idToString(db::ImageId imageId); +} // namespace lms::api::subsonic + +// Used to parse parameters +namespace lms::core::stringUtils +{ + template<> + std::optional readAs(std::string_view str); + + template<> + std::optional readAs(std::string_view str); +} // namespace lms::core::stringUtils diff --git a/src/libs/subsonic/impl/RequestContext.hpp b/src/libs/subsonic/impl/RequestContext.hpp index f3ddda2f..1b80bf70 100644 --- a/src/libs/subsonic/impl/RequestContext.hpp +++ b/src/libs/subsonic/impl/RequestContext.hpp @@ -47,6 +47,5 @@ namespace lms::api::subsonic ProtocolVersion serverProtocolVersion; ResponseFormat responseFormat; bool enableOpenSubsonic{ true }; - bool enableDefaultCover{}; }; } // namespace lms::api::subsonic diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index c21a54b0..6a38fae0 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -85,19 +85,6 @@ namespace lms::api::subsonic return res; } - std::unordered_set readDefaultCoverClients() - { - std::unordered_set res; - - core::Service::get()->visitStrings("api-subsonic-default-cover-clients", - [&](std::string_view client) { - res.emplace(std::string{ client }); - }, - { "DSub", "substreamer" }); - - return res; - } - std::string parameterMapToDebugString(const Wt::Http::ParameterMap& parameterMap) { auto censorValue = [](const std::string& type, const std::string& value) -> std::string { @@ -307,7 +294,6 @@ namespace lms::api::subsonic SubsonicResource::SubsonicResource(db::Db& db) : _serverProtocolVersionsByClient{ readConfigProtocolVersions() } , _openSubsonicDisabledClients{ readOpenSubsonicDisabledClients() } - , _defaultReleaseCoverClients{ readDefaultCoverClients() } , _supportUserPasswordAuthentication{ core::Service::get()->getBool("api-subsonic-support-user-password-auth", true) } , _db{ db } { @@ -425,7 +411,6 @@ namespace lms::api::subsonic const Wt::Http::ParameterMap& parameters{ request.getParameterMap() }; const ClientInfo clientInfo{ getClientInfo(request) }; bool enableOpenSubsonic{ !_openSubsonicDisabledClients.contains(clientInfo.name) }; - bool enableDefaultCover{ _defaultReleaseCoverClients.contains(clientInfo.name) }; const ResponseFormat format{ getParameterAs(request.getParameterMap(), "f").value_or("xml") == "json" ? ResponseFormat::json : ResponseFormat::xml }; return RequestContext{ @@ -437,7 +422,6 @@ namespace lms::api::subsonic .serverProtocolVersion = getServerProtocolVersion(clientInfo.name), .responseFormat = format, .enableOpenSubsonic = enableOpenSubsonic, - .enableDefaultCover = enableDefaultCover }; } diff --git a/src/libs/subsonic/impl/SubsonicResource.hpp b/src/libs/subsonic/impl/SubsonicResource.hpp index 7c00f847..af8ace3d 100644 --- a/src/libs/subsonic/impl/SubsonicResource.hpp +++ b/src/libs/subsonic/impl/SubsonicResource.hpp @@ -51,7 +51,6 @@ namespace lms::api::subsonic const std::unordered_map _serverProtocolVersionsByClient; const std::unordered_set _openSubsonicDisabledClients; - const std::unordered_set _defaultReleaseCoverClients; const bool _supportUserPasswordAuthentication; db::Db& _db; diff --git a/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp index 78e09410..c5a738f6 100644 --- a/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp @@ -24,7 +24,6 @@ #include "av/TranscodingParameters.hpp" #include "av/TranscodingResourceHandlerCreator.hpp" #include "av/Types.hpp" -#include "core/FileResourceHandlerCreator.hpp" #include "core/ILogger.hpp" #include "core/IResourceHandler.hpp" #include "core/String.hpp" @@ -35,6 +34,7 @@ #include "database/User.hpp" #include "services/artwork/IArtworkService.hpp" +#include "CoverArtId.hpp" #include "ParameterParsing.hpp" #include "RequestContext.hpp" #include "SubsonicId.hpp" @@ -326,36 +326,26 @@ namespace lms::api::subsonic void handleGetCoverArt(RequestContext& context, const Wt::Http::Request& /*request*/, Wt::Http::Response& response) { // Mandatory params - const auto trackId{ getParameterAs(context.parameters, "id") }; - const auto releaseId{ getParameterAs(context.parameters, "id") }; - const auto artistId{ getParameterAs(context.parameters, "id") }; - - if (!trackId && !releaseId && !artistId) - throw BadParameterGenericError{ "id" }; + const CoverArtId coverArtId{ getMandatoryParameterAs(context.parameters, "id") }; std::optional size{ getParameterAs(context.parameters, "size") }; if (size) *size = core::utils::clamp(*size, std::size_t{ 32 }, std::size_t{ 2048 }); - std::shared_ptr cover; - if (trackId) - cover = core::Service::get()->getTrackImage(*trackId, size); - else if (releaseId) - cover = core::Service::get()->getReleaseCover(*releaseId, size); - else if (artistId) - cover = core::Service::get()->getArtistImage(*artistId, size); + std::shared_ptr image; + if (const db::TrackId * trackId{ std::get_if(&coverArtId.id) }) + image = core::Service::get()->getTrackImage(*trackId, size); + else if (const db::ImageId * imageId{ std::get_if(&coverArtId.id) }) + image = core::Service::get()->getImage(*imageId, size); - if (!cover && context.enableDefaultCover && !artistId) - cover = core::Service::get()->getDefaultReleaseCover(); - - if (!cover) + if (!image) { response.setStatus(404); return; } - response.out().write(reinterpret_cast(cover->getData().data()), cover->getData().size()); - response.setMimeType(std::string{ cover->getMimeType() }); + response.out().write(reinterpret_cast(image->getData().data()), image->getData().size()); + response.setMimeType(std::string{ image->getMimeType() }); } } // namespace lms::api::subsonic diff --git a/src/libs/subsonic/impl/responses/Album.cpp b/src/libs/subsonic/impl/responses/Album.cpp index 9d7666eb..5a33db2e 100644 --- a/src/libs/subsonic/impl/responses/Album.cpp +++ b/src/libs/subsonic/impl/responses/Album.cpp @@ -32,6 +32,7 @@ #include "services/feedback/IFeedbackService.hpp" #include "services/scrobbling/IScrobblingService.hpp" +#include "CoverArtId.hpp" #include "RequestContext.hpp" #include "SubsonicId.hpp" #include "responses/Artist.hpp" @@ -84,9 +85,10 @@ namespace lms::api::subsonic } albumNode.setAttribute("created", core::stringUtils::toISO8601String(release->getLastWritten())); - if (release->getImage()) + if (const auto image{ release->getImage() }) { - albumNode.setAttribute("coverArt", idToString(release->getId())); + const CoverArtId coverArtId{ image->getId(), image->getLastWriteTime().toTime_t() }; + albumNode.setAttribute("coverArt", idToString(coverArtId)); } else { @@ -96,7 +98,8 @@ namespace lms::api::subsonic params.setRange(db::Range{ 0, 1 }); db::Track::find(context.dbSession, params, [&](const db::Track::pointer& track) { - albumNode.setAttribute("coverArt", idToString(track->getId())); + const CoverArtId coverArtId{ track->getId(), track->getLastWriteTime().toTime_t() }; + albumNode.setAttribute("coverArt", idToString(coverArtId)); }); } if (const auto year{ release->getYear() }) diff --git a/src/libs/subsonic/impl/responses/Artist.cpp b/src/libs/subsonic/impl/responses/Artist.cpp index 116d58e7..632e3548 100644 --- a/src/libs/subsonic/impl/responses/Artist.cpp +++ b/src/libs/subsonic/impl/responses/Artist.cpp @@ -29,6 +29,7 @@ #include "database/User.hpp" #include "services/feedback/IFeedbackService.hpp" +#include "CoverArtId.hpp" #include "RequestContext.hpp" #include "SubsonicId.hpp" @@ -94,8 +95,11 @@ namespace lms::api::subsonic artistNode.setAttribute("id", idToString(artist->getId())); artistNode.setAttribute("name", artist->getName()); - if (artist->getImage()) - artistNode.setAttribute("coverArt", idToString(artist->getId())); + if (const auto image{ artist->getImage() }) + { + const CoverArtId coverArtId{ image->getId(), image->getLastWriteTime().toTime_t() }; + artistNode.setAttribute("coverArt", idToString(coverArtId)); + } const std::size_t count{ Release::getCount(context.dbSession, Release::FindParameters{}.setArtist(artist->getId())) }; artistNode.setAttribute("albumCount", count); diff --git a/src/libs/subsonic/impl/responses/Playlist.cpp b/src/libs/subsonic/impl/responses/Playlist.cpp index 4aa0842b..7b12758f 100644 --- a/src/libs/subsonic/impl/responses/Playlist.cpp +++ b/src/libs/subsonic/impl/responses/Playlist.cpp @@ -23,6 +23,7 @@ #include "database/TrackList.hpp" #include "database/User.hpp" +#include "CoverArtId.hpp" #include "SubsonicId.hpp" namespace lms::api::subsonic @@ -50,7 +51,8 @@ namespace lms::api::subsonic params.setSortMethod(TrackSortMethod::TrackList); db::Track::find(session, params, [&](const db::Track::pointer& track) { - playlistNode.setAttribute("coverArt", idToString(track->getId())); + const CoverArtId coverArtId{ track->getId(), track->getLastWriteTime().toTime_t() }; + playlistNode.setAttribute("coverArt", idToString(coverArtId)); }); return playlistNode; diff --git a/src/libs/subsonic/impl/responses/Song.cpp b/src/libs/subsonic/impl/responses/Song.cpp index 93a60863..bc74729a 100644 --- a/src/libs/subsonic/impl/responses/Song.cpp +++ b/src/libs/subsonic/impl/responses/Song.cpp @@ -36,6 +36,7 @@ #include "services/feedback/IFeedbackService.hpp" #include "services/scrobbling/IScrobblingService.hpp" +#include "CoverArtId.hpp" #include "RequestContext.hpp" #include "SubsonicId.hpp" #include "responses/Artist.hpp" @@ -109,9 +110,18 @@ namespace lms::api::subsonic const Release::pointer release{ track->getRelease() }; if (track->hasCover()) - trackResponse.setAttribute("coverArt", idToString(track->getId())); - else if (release && release->getImage()) - trackResponse.setAttribute("coverArt", idToString(release->getId())); + { + const CoverArtId coverArtId{ track->getId(), track->getLastWriteTime().toTime_t() }; + trackResponse.setAttribute("coverArt", idToString(coverArtId)); + } + else if (release) + { + if (const db::Image::pointer image{ release->getImage() }) + { + const CoverArtId coverArtId{ image->getId(), image->getLastWriteTime().toTime_t() }; + trackResponse.setAttribute("coverArt", idToString(coverArtId)); + } + } const std::vector& artists{ track->getArtists({ TrackArtistLinkType::Artist }) }; if (!artists.empty()) diff --git a/src/lms/ui/resource/ArtworkResource.cpp b/src/lms/ui/resource/ArtworkResource.cpp index 0bf8147b..70d4ea6d 100644 --- a/src/lms/ui/resource/ArtworkResource.cpp +++ b/src/lms/ui/resource/ArtworkResource.cpp @@ -60,8 +60,11 @@ namespace lms::ui 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 (artist) + { + if (const db::Image::pointer image{ artist->getImage() }) + url = getImageUrl(image->getId(), size, "artist"); + } } if (url.empty()) @@ -80,9 +83,9 @@ namespace lms::ui const db::Release::pointer release{ db::Release::find(LmsApp->getDbSession(), releaseId) }; if (release) { - if (release->getImage()) + if (const db::Image::pointer image{ release->getImage() }) { - url = getReleaseIdCoverUrl(release->getId(), size); + url = getImageUrl(image->getId(), size, "release"); } else { @@ -92,7 +95,7 @@ namespace lms::ui params.setRange(db::Range{ 0, 1 }); db::Track::find(LmsApp->getDbSession(), params, [&](const db::Track::pointer& track) { - url = getTrackIdImageUrl(track->getId(), size); + url = getImageUrl(track->getId(), size, "release"); }); } } @@ -115,9 +118,14 @@ namespace lms::ui 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); + { + url = getImageUrl(trackId, size, "release"); + } + else if (const db::Release::pointer release{ track->getRelease() }) + { + if (const db::Image::pointer image{ release->getImage() }) + url = getImageUrl(image->getId(), size, "release"); + } } } @@ -127,29 +135,24 @@ namespace lms::ui return url; } - std::string ArtworkResource::getArtistIdImageUrl(db::ArtistId artistId, Size size) const + std::string ArtworkResource::getImageUrl(db::ImageId imageId, Size size, std::string_view type) const { - return url() + "&artistid=" + artistId.toString() + "&size=" + std::to_string(static_cast(size)); + return url() + "&imageid=" + imageId.toString() + "&size=" + std::to_string(static_cast(size)) + "&type=" + std::string{ type }; } - std::string ArtworkResource::getReleaseIdCoverUrl(db::ReleaseId releaseId, Size size) const + std::string ArtworkResource::getImageUrl(db::TrackId trackId, Size size, std::string_view type) 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)); + return url() + "&trackid=" + trackId.toString() + "&size=" + std::to_string(static_cast(size)) + "&type=" + std::string{ type }; } std::string ArtworkResource::getDefaultArtistImageUrl() const { - return url() + "&type=defaultartistimage"; + return url() + "&type=artist"; } std::string ArtworkResource::getDefaultReleaseCoverUrl() const { - return url() + "&type=defaultreleasecover"; + return url() + "&type=release"; } void ArtworkResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) @@ -157,58 +160,32 @@ namespace lms::ui LMS_SCOPED_TRACE_OVERVIEW("UI", "HandleCoverRequest"); // Retrieve parameters - const std::string* artistIdStr = request.getParameter("artistid"); + const std::string* imageIdStr = request.getParameter("imageid"); 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 ((imageIdStr || trackIdStr)) { - if (!sizeStr) - { - LOG(DEBUG, "no size provided!"); - return; - } - - const auto size{ core::stringUtils::readAs(*sizeStr) }; - if (!size || *size > maxSize) + const auto size{ sizeStr ? core::stringUtils::readAs(*sizeStr) : std::nullopt }; + if (size && *size > maxSize) { LOG(DEBUG, "invalid size provided!"); return; } - if (artistIdStr) + if (imageIdStr) { - LOG(DEBUG, "Requested cover for track " << *artistIdStr << ", size = " << *size); - - const std::optional artistId{ core::stringUtils::readAs(*artistIdStr) }; - if (!artistId) + const std::optional imageId{ core::stringUtils::readAs(*imageIdStr) }; + if (!imageId) 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(); + image = core::Service::get()->getImage(*imageId, size); } else if (trackIdStr) { - LOG(DEBUG, "Requested cover for track " << *trackIdStr << ", size = " << *size); - const std::optional trackId{ core::stringUtils::readAs(*trackIdStr) }; if (!trackId) { @@ -216,16 +193,15 @@ namespace lms::ui return; } - image = core::Service::get()->getTrackImage(*trackId, *size); - if (!image) - image = core::Service::get()->getDefaultReleaseCover(); + image = core::Service::get()->getTrackImage(*trackId, size); } } - else if (typeStr) + + if (!image) { - if (*typeStr == "defaultreleasecover") + if (*typeStr == "release") image = core::Service::get()->getDefaultReleaseCover(); - else if (*typeStr == "defaultartistimage") + else if (*typeStr == "artist") image = core::Service::get()->getDefaultArtistImage(); } diff --git a/src/lms/ui/resource/ArtworkResource.hpp b/src/lms/ui/resource/ArtworkResource.hpp index c3b40083..e3ba4a1c 100644 --- a/src/lms/ui/resource/ArtworkResource.hpp +++ b/src/lms/ui/resource/ArtworkResource.hpp @@ -22,6 +22,7 @@ #include #include "database/ArtistId.hpp" +#include "database/ImageId.hpp" #include "database/ReleaseId.hpp" #include "database/TrackId.hpp" @@ -46,9 +47,8 @@ namespace lms::ui 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 getImageUrl(db::ImageId imageId, Size size, std::string_view type) const; + std::string getImageUrl(db::TrackId trackId, Size size, std::string_view type) const; std::string getDefaultArtistImageUrl() const; std::string getDefaultReleaseCoverUrl() const; From ec20c9bfa666a06eb71ebb365265a0d978278762 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 8 Dec 2024 18:02:00 +0100 Subject: [PATCH 12/20] Removed useless includes --- src/libs/services/artwork/impl/ArtworkService.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libs/services/artwork/impl/ArtworkService.cpp b/src/libs/services/artwork/impl/ArtworkService.cpp index 2aff44c1..fccd74b0 100644 --- a/src/libs/services/artwork/impl/ArtworkService.cpp +++ b/src/libs/services/artwork/impl/ArtworkService.cpp @@ -21,7 +21,6 @@ #include #include -#include #include "av/IAudioFile.hpp" #include "av/Types.hpp" @@ -29,10 +28,8 @@ #include "core/ILogger.hpp" #include "core/String.hpp" #include "core/Utils.hpp" -#include "database/Artist.hpp" #include "database/Db.hpp" #include "database/Image.hpp" -#include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" #include "image/Exception.hpp" From 3661eaccb6e377352e07074eccfc65d71541a016 Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 18 Dec 2024 14:15:25 +0100 Subject: [PATCH 13/20] Added playlist import (and sync)from m3u files, ref #391 --- .clang-tidy | 2 +- approot/messages.xml | 4 +- approot/messages_fr.xml | 4 +- approot/messages_it.xml | 4 +- approot/messages_pl.xml | 4 +- approot/messages_zh.xml | 2 + src/libs/core/impl/IOContextRunner.cpp | 10 +- .../core/include/core/IOContextRunner.hpp | 13 +- src/libs/database/CMakeLists.txt | 1 + src/libs/database/impl/Directory.cpp | 2 + src/libs/database/impl/Migration.cpp | 114 +- src/libs/database/impl/PlayListFile.cpp | 142 +++ src/libs/database/impl/ScanSettings.cpp | 11 - src/libs/database/impl/Session.cpp | 159 +-- src/libs/database/impl/Track.cpp | 10 +- src/libs/database/impl/TrackList.cpp | 45 +- src/libs/database/include/database/Object.hpp | 8 - .../include/database/PlayListFile.hpp | 108 ++ .../include/database/ScanSettings.hpp | 3 - .../database/include/database/Session.hpp | 12 +- src/libs/database/include/database/Track.hpp | 18 +- .../database/include/database/TrackList.hpp | 63 +- src/libs/database/include/database/Types.hpp | 4 +- src/libs/database/test/CMakeLists.txt | 1 + src/libs/database/test/Cluster.cpp | 8 +- src/libs/database/test/Migration.cpp | 2 + src/libs/database/test/PlayListFile.cpp | 114 ++ src/libs/database/test/TrackList.cpp | 55 +- src/libs/metadata/CMakeLists.txt | 1 + src/libs/metadata/impl/Parser.cpp | 26 + src/libs/metadata/impl/Parser.hpp | 1 + src/libs/metadata/impl/PlayList.cpp | 95 ++ .../metadata/include/metadata/IParser.hpp | 1 + src/libs/metadata/include/metadata/Lyrics.hpp | 1 - .../metadata/include/metadata/PlayList.hpp | 38 + src/libs/metadata/test/CMakeLists.txt | 1 + src/libs/metadata/test/PlayList.cpp | 53 + src/libs/services/scanner/CMakeLists.txt | 34 +- .../services/scanner/impl/FileScanQueue.cpp | 174 ---- .../services/scanner/impl/FileScanQueue.hpp | 86 -- .../scanner/impl/MediaLibraryInfo.hpp | 35 + .../services/scanner/impl/ScanContext.hpp | 33 + .../scanner/impl/ScanStepScanFiles.cpp | 973 ------------------ .../services/scanner/impl/ScannerService.cpp | 102 +- .../services/scanner/impl/ScannerService.hpp | 5 +- .../services/scanner/impl/ScannerSettings.hpp | 16 +- .../impl/scanners/AudioFileScanner.cpp | 668 ++++++++++++ .../impl/scanners/AudioFileScanner.hpp | 62 ++ .../impl/scanners/IFileScanOperation.hpp | 37 + .../scanner/impl/scanners/IFileScanner.hpp | 48 + .../impl/scanners/ImageFileScanner.cpp | 171 +++ .../impl/scanners/ImageFileScanner.hpp | 51 + .../impl/scanners/LyricsFileScanner.cpp | 177 ++++ .../impl/scanners/LyricsFileScanner.hpp | 51 + .../impl/scanners/PlayListFileScanner.cpp | 176 ++++ .../impl/scanners/PlayListFileScanner.hpp | 51 + .../services/scanner/impl/scanners/Utils.cpp | 100 ++ .../services/scanner/impl/scanners/Utils.hpp | 52 + .../scanner/impl/steps/FileScanQueue.cpp | 109 ++ .../scanner/impl/steps/FileScanQueue.hpp | 62 ++ .../scanner/impl/{ => steps}/IScanStep.hpp | 11 +- .../ScanStepAssociateArtistImages.cpp | 0 .../ScanStepAssociateArtistImages.hpp | 0 .../ScanStepAssociateExternalLyrics.cpp | 4 +- .../ScanStepAssociateExternalLyrics.hpp | 0 .../steps/ScanStepAssociatePlayListTracks.cpp | 202 ++++ .../steps/ScanStepAssociatePlayListTracks.hpp | 36 + .../ScanStepAssociateReleaseImages.cpp | 0 .../ScanStepAssociateReleaseImages.hpp | 0 .../scanner/impl/{ => steps}/ScanStepBase.hpp | 15 +- .../ScanStepCheckForDuplicatedFiles.cpp | 0 .../ScanStepCheckForDuplicatedFiles.hpp | 0 .../ScanStepCheckForRemovedFiles.cpp | 22 +- .../ScanStepCheckForRemovedFiles.hpp | 0 .../impl/{ => steps}/ScanStepCompact.cpp | 0 .../impl/{ => steps}/ScanStepCompact.hpp | 0 .../ScanStepComputeClusterStats.cpp | 0 .../ScanStepComputeClusterStats.hpp | 0 .../{ => steps}/ScanStepDiscoverFiles.cpp | 21 +- .../{ => steps}/ScanStepDiscoverFiles.hpp | 0 .../impl/{ => steps}/ScanStepOptimize.cpp | 2 +- .../impl/{ => steps}/ScanStepOptimize.hpp | 0 .../ScanStepRemoveOrphanedDbEntries.cpp | 2 +- .../ScanStepRemoveOrphanedDbEntries.hpp | 2 +- .../scanner/impl/steps/ScanStepScanFiles.cpp | 146 +++ .../impl/{ => steps}/ScanStepScanFiles.hpp | 23 +- .../ScanStepUpdateLibraryFields.cpp | 7 +- .../ScanStepUpdateLibraryFields.hpp | 4 +- .../include/services/scanner/ScannerStats.hpp | 14 +- .../impl/TLSMonotonicMemoryResource.hpp | 57 +- .../subsonic/impl/endpoints/Playlists.cpp | 49 +- src/libs/subsonic/impl/responses/Playlist.cpp | 17 +- src/lms/ui/PlayQueue.cpp | 17 +- src/lms/ui/admin/ScannerController.cpp | 7 + src/lms/ui/explore/TrackListsView.cpp | 2 +- 95 files changed, 3439 insertions(+), 1634 deletions(-) create mode 100644 src/libs/database/impl/PlayListFile.cpp create mode 100644 src/libs/database/include/database/PlayListFile.hpp create mode 100644 src/libs/database/test/PlayListFile.cpp create mode 100644 src/libs/metadata/impl/PlayList.cpp create mode 100644 src/libs/metadata/include/metadata/PlayList.hpp create mode 100644 src/libs/metadata/test/PlayList.cpp delete mode 100644 src/libs/services/scanner/impl/FileScanQueue.cpp delete mode 100644 src/libs/services/scanner/impl/FileScanQueue.hpp create mode 100644 src/libs/services/scanner/impl/MediaLibraryInfo.hpp create mode 100644 src/libs/services/scanner/impl/ScanContext.hpp delete mode 100644 src/libs/services/scanner/impl/ScanStepScanFiles.cpp create mode 100644 src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp create mode 100644 src/libs/services/scanner/impl/scanners/AudioFileScanner.hpp create mode 100644 src/libs/services/scanner/impl/scanners/IFileScanOperation.hpp create mode 100644 src/libs/services/scanner/impl/scanners/IFileScanner.hpp create mode 100644 src/libs/services/scanner/impl/scanners/ImageFileScanner.cpp create mode 100644 src/libs/services/scanner/impl/scanners/ImageFileScanner.hpp create mode 100644 src/libs/services/scanner/impl/scanners/LyricsFileScanner.cpp create mode 100644 src/libs/services/scanner/impl/scanners/LyricsFileScanner.hpp create mode 100644 src/libs/services/scanner/impl/scanners/PlayListFileScanner.cpp create mode 100644 src/libs/services/scanner/impl/scanners/PlayListFileScanner.hpp create mode 100644 src/libs/services/scanner/impl/scanners/Utils.cpp create mode 100644 src/libs/services/scanner/impl/scanners/Utils.hpp create mode 100644 src/libs/services/scanner/impl/steps/FileScanQueue.cpp create mode 100644 src/libs/services/scanner/impl/steps/FileScanQueue.hpp rename src/libs/services/scanner/impl/{ => steps}/IScanStep.hpp (80%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepAssociateArtistImages.cpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepAssociateArtistImages.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepAssociateExternalLyrics.cpp (99%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepAssociateExternalLyrics.hpp (100%) create mode 100644 src/libs/services/scanner/impl/steps/ScanStepAssociatePlayListTracks.cpp create mode 100644 src/libs/services/scanner/impl/steps/ScanStepAssociatePlayListTracks.hpp rename src/libs/services/scanner/impl/{ => steps}/ScanStepAssociateReleaseImages.cpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepAssociateReleaseImages.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepBase.hpp (79%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepCheckForDuplicatedFiles.cpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepCheckForDuplicatedFiles.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepCheckForRemovedFiles.cpp (84%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepCheckForRemovedFiles.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepCompact.cpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepCompact.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepComputeClusterStats.cpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepComputeClusterStats.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepDiscoverFiles.cpp (77%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepDiscoverFiles.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepOptimize.cpp (98%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepOptimize.hpp (100%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepRemoveOrphanedDbEntries.cpp (98%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepRemoveOrphanedDbEntries.hpp (94%) create mode 100644 src/libs/services/scanner/impl/steps/ScanStepScanFiles.cpp rename src/libs/services/scanner/impl/{ => steps}/ScanStepScanFiles.hpp (50%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepUpdateLibraryFields.cpp (93%) rename src/libs/services/scanner/impl/{ => steps}/ScanStepUpdateLibraryFields.hpp (91%) diff --git a/.clang-tidy b/.clang-tidy index e901f557..fd3c5114 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -12,7 +12,7 @@ CheckOptions: - key: cppcoreguidelines-avoid-do-while.IgnoreMacros value: '1' - key: performance-unnecessary-value-param.AllowedTypes - value: "shared_ptr;ObjectPtr" + value: "shared_ptr;ObjectPtr;.*::pointer" - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted value: '1' - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor diff --git a/approot/messages.xml b/approot/messages.xml index ffee2275..9b6fe164 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -95,9 +95,10 @@ Cannot get track duration Cannot parse audio file +Cannot read file Cannot parse image file Cannot parse lyrics file -Cannot read file +Cannot parse playlist file Compact the database. Caution: this may take a while and will block the whole application during the compact step! {1} duplicate files: {1} errors: @@ -119,6 +120,7 @@ Scanning: step {1}/{2} Associating artist images: {1}%... Associating external lyrics: {1}%... +Associating playlist tracks: {1}%... Associating release images: {1}%... Checking for duplicate files... {1} files Checking for removed files... {1}% diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml index 4a7e576c..af414c08 100644 --- a/approot/messages_fr.xml +++ b/approot/messages_fr.xml @@ -95,9 +95,10 @@ Impossible de récupérer la durée de la piste Impossible d'analyser le fichier audio +Impossible de lire le fichier Impossible d'analyser le fichier image Impossible d'analyser le fichier de paroles -Impossible de lire le fichier +Impossible d'analyser le fichier de liste de lecture Compacter la base de données. Attention : cette opération peut prendre du temps et va vérouiller l'application pendant toute l'étape de compactage! {1} fichiers dupliqués : {1} erreurs : @@ -119,6 +120,7 @@ En cours de scan : étape {1}/{2} Association des images des artistes: {1}%... Association des paroles externes: {1}%... +Association des pistes des listes de lectures: {1}%... Association des images des albums: {1}%... Vérification des fichiers dupliqués... {1} fichiers Vérification des fichiers supprimés... {1}% diff --git a/approot/messages_it.xml b/approot/messages_it.xml index 478c0dc4..d4b8e40e 100644 --- a/approot/messages_it.xml +++ b/approot/messages_it.xml @@ -95,9 +95,10 @@ Non sono stato in grado di determinare la durata della traccia Impossibile analizzare il file audio +Non in grado di leggere il file Impossibile analizzare il file immagine Impossibile analizzare il file dei testi -Non in grado di leggere il file +Impossibile analizzare il file della playlist Compatta il database. Attenzione: ciò potrebbe richiedere del tempo e bloccherà l'intera applicazione durante il passaggio di compattazione! {1} file duplicati: {1} errori: @@ -119,6 +120,7 @@ Scansione: passo {1}/{2} Associando immagini degli artisti: {1}%... Associazione dei testi esterni: {1}%... +Associando brani della playlist: {1}%... Associando immagini degli album: {1}%... Controllo duplicati... {1} files Controllo file... {1}% diff --git a/approot/messages_pl.xml b/approot/messages_pl.xml index b0945b64..97841bc2 100644 --- a/approot/messages_pl.xml +++ b/approot/messages_pl.xml @@ -96,9 +96,10 @@ Nie udało się ustalić długości ścieżki Nie można przeanalizować pliku audio +Nie udało się odczytać pliku Nie można przeanalizować pliku obrazu Nie można przetworzyć pliku z tekstem -Nie udało się odczytać pliku +Nie można przeanalizować pliku playlisty Sprasuj bazę danych. Uwaga: może to trochę zająć, a cała aplikacja będzie w tym czasie zablokowana! {1} zduplikowany plik: @@ -128,6 +129,7 @@ Skanowanie: krok {1}/{2} Kojarzenie obrazów artystów: {1}%... Kojarzenie zewnętrznych tekstów: {1}%... +Kojarzenie utworów z playlisty: {1}%... Kojarzenie obrazów albumów: {1}%... Sprawdzanie duplikatów... {1} plik diff --git a/approot/messages_zh.xml b/approot/messages_zh.xml index 1a127517..f28c6fa9 100644 --- a/approot/messages_zh.xml +++ b/approot/messages_zh.xml @@ -99,6 +99,7 @@ 无法读取文件 + {1} 个重复文件: {1} 个错误: @@ -121,6 +122,7 @@ + 检查文件中... {1}% diff --git a/src/libs/core/impl/IOContextRunner.cpp b/src/libs/core/impl/IOContextRunner.cpp index 2094f691..465cf0fe 100644 --- a/src/libs/core/impl/IOContextRunner.cpp +++ b/src/libs/core/impl/IOContextRunner.cpp @@ -26,9 +26,9 @@ namespace lms::core { - IOContextRunner::IOContextRunner(boost::asio::io_service& ioService, std::size_t threadCount, std::string_view name) - : _ioService{ ioService } - , _work{ ioService } + IOContextRunner::IOContextRunner(boost::asio::io_context& ioContext, std::size_t threadCount, std::string_view name) + : _ioContext{ ioContext } + , _work{ ioContext } { LMS_LOG(UTILS, INFO, "Starting IO context with " << threadCount << " threads..."); @@ -50,7 +50,7 @@ namespace lms::core try { - _ioService.run(); + _ioContext.run(); } catch (const std::exception& e) { @@ -65,7 +65,7 @@ namespace lms::core { LMS_LOG(UTILS, DEBUG, "Stopping IO context..."); _work.reset(); - _ioService.stop(); + _ioContext.stop(); LMS_LOG(UTILS, DEBUG, "IO context stopped!"); } diff --git a/src/libs/core/include/core/IOContextRunner.hpp b/src/libs/core/include/core/IOContextRunner.hpp index a3fb30e0..56b1f881 100644 --- a/src/libs/core/include/core/IOContextRunner.hpp +++ b/src/libs/core/include/core/IOContextRunner.hpp @@ -22,25 +22,24 @@ #include #include -#include +#include namespace lms::core { class IOContextRunner { public: - IOContextRunner(boost::asio::io_service& ioService, std::size_t threadCount, std::string_view name); + IOContextRunner(boost::asio::io_context& ioContext, std::size_t threadCount, std::string_view name); ~IOContextRunner(); + IOContextRunner(const IOContextRunner&) = delete; + IOContextRunner& operator=(const IOContextRunner&) = delete; void stop(); std::size_t getThreadCount() const; private: - IOContextRunner(const IOContextRunner&) = delete; - IOContextRunner& operator=(const IOContextRunner&) = delete; - - boost::asio::io_service& _ioService; - std::optional _work; + boost::asio::io_context& _ioContext; + std::optional _work; std::vector _threads; }; } // namespace lms::core \ No newline at end of file diff --git a/src/libs/database/CMakeLists.txt b/src/libs/database/CMakeLists.txt index af4a6a1b..2fe78ac7 100644 --- a/src/libs/database/CMakeLists.txt +++ b/src/libs/database/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(lmsdatabase SHARED impl/Listen.cpp impl/MediaLibrary.cpp impl/Migration.cpp + impl/PlayListFile.cpp impl/PlayQueue.cpp impl/TrackArtistLink.cpp impl/TrackFeatures.cpp diff --git a/src/libs/database/impl/Directory.cpp b/src/libs/database/impl/Directory.cpp index 7bed3d4a..3824bb78 100644 --- a/src/libs/database/impl/Directory.cpp +++ b/src/libs/database/impl/Directory.cpp @@ -170,10 +170,12 @@ namespace lms::db query.leftJoin("track t ON d.id = t.directory_id"); query.leftJoin("image i ON d.id = i.directory_id"); query.leftJoin("track_lyrics l_lrc ON d.id = l_lrc.directory_id"); + query.leftJoin("playlist_file pl_f ON d.id = pl_f.directory_id"); query.where("d_child.id IS NULL"); query.where("t.directory_id IS NULL"); query.where("i.directory_id IS NULL"); query.where("l_lrc.directory_id IS NULL"); + query.where("pl_f.directory_id IS NULL"); return utils::execRangeQuery(query, range); } diff --git a/src/libs/database/impl/Migration.cpp b/src/libs/database/impl/Migration.cpp index 44378136..4a83942d 100644 --- a/src/libs/database/impl/Migration.cpp +++ b/src/libs/database/impl/Migration.cpp @@ -35,7 +35,7 @@ namespace lms::db { namespace { - static constexpr Version LMS_DATABASE_VERSION{ 76 }; + static constexpr Version LMS_DATABASE_VERSION{ 77 }; } VersionInfo::VersionInfo() @@ -127,7 +127,7 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" ( void migrateFromV36(Session& session) { // Increased precision for track durations (now in milliseconds instead of secodns) - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -136,7 +136,7 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" ( // Support Performer tags (via subtypes) utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_artist_link ADD subtype TEXT"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -176,7 +176,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( utils::executeCommand(*session.getDboSession(), "DROP TABLE track"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_backup RENAME TO track"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -186,7 +186,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD primary_type INTEGER"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD secondary_types INTEGER"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -196,7 +196,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD artist_display_name TEXT NOT NULL DEFAULT ''"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD artist_display_name TEXT NOT NULL DEFAULT ''"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -241,7 +241,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( utils::executeCommand(*session.getDboSession(), "ALTER TABLE cluster ADD track_count INTEGER"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE cluster ADD release_count INTEGER"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -250,7 +250,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( // add bitrate utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD bitrate INTEGER NOT NULL DEFAULT 0"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -274,7 +274,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN extra_tags_to_scan TEXT"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -299,14 +299,14 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_release_type_release_type" on "release_release_type" ("release_type_id"))"); utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_release_type_release" on "release_release_type" ("release_id"))"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } void migrateFromV48(Session& session) { // Regression for the extra tags not being parsed - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -316,7 +316,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD year INTEGER"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD original_year INTEGER"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -423,7 +423,7 @@ SELECT // Add sort name for releases utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD sort_name TEXT NOT NULL DEFAULT ''"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -432,7 +432,7 @@ SELECT // Add release group mbid utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD group_mbid TEXT NOT NULL DEFAULT ''"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -443,7 +443,7 @@ SELECT utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD file_size BIGINT NOT NULL DEFAULT(0)"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD relative_file_path TEXT NOT NULL DEFAULT ''"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -454,7 +454,7 @@ SELECT utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD channel_count INTEGER NOT NULL DEFAULT(0)"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD sample_rate INTEGER NOT NULL DEFAULT(0)"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -494,7 +494,7 @@ SELECT constraint "fk_image_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred ))"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -625,7 +625,7 @@ SELECT utils::executeCommand(*session.getDboSession(), "DROP TABLE image"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE image_backup RENAME TO image"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -660,7 +660,7 @@ SELECT utils::executeCommand(*session.getDboSession(), "DROP TABLE directory"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE directory_backup RENAME TO directory"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -669,7 +669,7 @@ SELECT // Add a new column comment utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD comment TEXT NOT NULL DEFAULT ''"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -732,7 +732,7 @@ SELECT utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_label_label" on "release_label" ("label_id"))"); utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_label_release" on "release_label" ("release_id"))"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -740,7 +740,7 @@ SELECT { utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD is_compilation BOOLEAN NOT NULL DEFAULT(false)"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -805,7 +805,7 @@ SELECT for (const auto& indexName : indexeNames) utils::executeCommand(*session.getDboSession(), "DROP INDEX " + indexName); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -868,7 +868,7 @@ SELECT utils::executeCommand(*session.getDboSession(), "DROP TABLE artist"); utils::executeCommand(*session.getDboSession(), "ALTER TABLE artist_backup RENAME TO artist"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -903,7 +903,7 @@ SELECT constraint "fk_track_lyrics_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred ))"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -937,7 +937,7 @@ SELECT // Add catalog number utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD barcode TEXT NOT NULL DEFAULT ''"); - // Just increment the scan version of the settings to make the next scheduled scan rescan everything + // Just increment the scan version of the settings to make the next scan rescan everything utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); } @@ -968,6 +968,67 @@ SELECT utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD bcrypt_round_count INTEGER NOT NULL DEFAULT(7)"); } + void migrateFromV76(Session& session) + { + // Rename public -> visibility (false is Private(0) and true is Public(1)) + utils::executeCommand(*session.getDboSession(), "ALTER TABLE tracklist RENAME COLUMN public TO visibility"); + + // Supported extensions are now runtime + utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings DROP COLUMN audio_file_extensions"); + + // Add PlayListFile + utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "playlist_file" ( + "id" integer primary key autoincrement, + "version" integer not null, + "absolute_file_path" text not null, + "file_stem" text not null, + "file_size" bigint not null, + "file_last_write" text, + "name" text not null, + "entries" text not null, + "media_library_id" bigint, + "directory_id" bigint, + constraint "fk_playlist_file_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred, + constraint "fk_playlist_file_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred +))"); + + // Add a link into tracklist to ease cleanup + utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "tracklist_backup" ( + "id" integer primary key autoincrement, + "version" integer not null, + "name" text not null, + "type" integer not null, + "visibility" integer not null, + "creation_date_time" text, + "last_modified_date_time" text, + "user_id" bigint, + "playlist_file_id" bigint, + constraint "fk_tracklist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred, + constraint "fk_tracklist_playlist_file" foreign key ("playlist_file_id") references "playlist_file" ("id") on delete cascade deferrable initially deferred))"); + + utils::executeCommand(*session.getDboSession(), R"(INSERT INTO tracklist_backup +SELECT + id, + version, + name, + type, + visibility, + creation_date_time, + last_modified_date_time, + user_id, + NULL AS playlist_file_id +FROM tracklist)"); + + utils::executeCommand(*session.getDboSession(), R"(DROP TABLE tracklist)"); + utils::executeCommand(*session.getDboSession(), R"(ALTER TABLE tracklist_backup RENAME TO tracklist)"); + + // Add a file name in tracks + utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD COLUMN file_name TEXT NOT NULL DEFAULT ''"); + + // Just increment the scan version of the settings to make the next scan rescan everything + utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1"); + } + bool doDbMigration(Session& session) { constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" }; @@ -1020,6 +1081,7 @@ SELECT { 73, migrateFromV73 }, { 74, migrateFromV74 }, { 75, migrateFromV75 }, + { 76, migrateFromV76 }, }; bool migrationPerformed{}; diff --git a/src/libs/database/impl/PlayListFile.cpp b/src/libs/database/impl/PlayListFile.cpp new file mode 100644 index 00000000..0bbc4db1 --- /dev/null +++ b/src/libs/database/impl/PlayListFile.cpp @@ -0,0 +1,142 @@ +/* + * 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 . + */ + +#include "database/PlayListFile.hpp" + +#include + +#include "core/ILogger.hpp" +#include "database/Directory.hpp" +#include "database/MediaLibrary.hpp" +#include "database/Session.hpp" +#include "database/TrackList.hpp" +#include "database/User.hpp" + +#include "IdTypeTraits.hpp" +#include "PathTraits.hpp" +#include "StringViewTraits.hpp" +#include "Utils.hpp" + +namespace lms::db +{ + PlayListFile::PlayListFile(const std::filesystem::path& file) + { + setAbsoluteFilePath(file); + } + + PlayListFile::pointer PlayListFile::create(Session& session, const std::filesystem::path& file) + { + return session.getDboSession()->add(std::unique_ptr{ new PlayListFile{ file } }); + } + + std::size_t PlayListFile::getCount(Session& session) + { + session.checkReadTransaction(); + + return utils::fetchQuerySingleResult(session.getDboSession()->query("SELECT COUNT(*) FROM playlist_file")); + } + + PlayListFile::pointer PlayListFile::find(Session& session, const std::filesystem::path& p) + { + session.checkReadTransaction(); + + return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT pl_f from playlist_file pl_f").where("pl_f.absolute_file_path = ?").bind(p.string())); + } + + void PlayListFile::find(Session& session, PlayListFileId& lastRetrievedId, std::size_t count, const std::function& func) + { + session.checkReadTransaction(); + + auto query{ session.getDboSession()->query>("SELECT pl_f from playlist_file pl_f").orderBy("pl_f.id").where("pl_f.id > ?").bind(lastRetrievedId).limit(static_cast(count)) }; + + utils::forEachQueryResult(query, [&](const PlayListFile::pointer& playList) { + func(playList); + lastRetrievedId = playList->getId(); + }); + } + + PlayListFile::pointer PlayListFile::find(Session& session, PlayListFileId id) + { + session.checkReadTransaction(); + + return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT pl_f from playlist_file pl_f").where("pl_f.id = ?").bind(id)); + } + + std::vector PlayListFile::getFiles() const + { + std::vector files; + { + Wt::Json::Object root; + Wt::Json::parse(_entries, root); + + assert(root.type("files") == Wt::Json::Type::Array); + const Wt::Json::Array& filesArray = root.get("files"); + for (const Wt::Json::Value& file : filesArray) + files.push_back(static_cast(file.toString())); + } + + return files; + } + + TrackList::pointer PlayListFile::getTrackList() const + { + return _trackList.lock(); + } + + Directory::pointer PlayListFile::getDirectory() const + { + return _directory; + } + + void PlayListFile::setAbsoluteFilePath(const std::filesystem::path& filePath) + { + assert(filePath.is_absolute()); + _absoluteFilePath = filePath; + _fileStem = filePath.stem(); + } + + void PlayListFile::setDirectory(ObjectPtr directory) + { + _directory = getDboPtr(directory); + } + + void PlayListFile::setTrackList(ObjectPtr trackList) + { + _trackList = getDboPtr(trackList); + } + + void PlayListFile::setName(std::string_view name) + { + _name = std::string{ name, 0, _maxNameLength }; + if (name.size() > _maxNameLength) + LMS_LOG(DB, WARNING, "PlaylistFile name too long, truncated to '" << _name << "'"); + } + + void PlayListFile::setFiles(std::span files) + { + Wt::Json::Object root; + + Wt::Json::Array fileArray; + for (const auto& file : files) + fileArray.push_back(Wt::Json::Value{ file.string() }); + + root["files"] = std::move(fileArray); + _entries = Wt::Json::serialize(root); + } +} // namespace lms::db diff --git a/src/libs/database/impl/ScanSettings.cpp b/src/libs/database/impl/ScanSettings.cpp index c031fb81..4168e402 100644 --- a/src/libs/database/impl/ScanSettings.cpp +++ b/src/libs/database/impl/ScanSettings.cpp @@ -46,17 +46,6 @@ namespace lms::db return utils::fetchQuerySingleResult(session.getDboSession()->find()); } - std::vector ScanSettings::getAudioFileExtensions() const - { - const auto extensions{ core::stringUtils::splitString(_audioFileExtensions, ' ') }; - - std::vector res(std::cbegin(extensions), std::cend(extensions)); - std::sort(std::begin(res), std::end(res)); - res.erase(std::unique(std::begin(res), std::end(res)), std::end(res)); - - return res; - } - std::vector ScanSettings::getExtraTagsToScan() const { std::vector tags{ core::stringUtils::splitString(_extraTagsToScan, ';') }; diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp index d03389e2..2cb88d37 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -30,6 +30,7 @@ #include "database/Image.hpp" #include "database/Listen.hpp" #include "database/MediaLibrary.hpp" +#include "database/PlayListFile.hpp" #include "database/PlayQueue.hpp" #include "database/RatedArtist.hpp" #include "database/RatedRelease.hpp" @@ -105,6 +106,7 @@ namespace lms::db _session.mapClass