Merge branch 'develop' for release 3.9.0
This commit is contained in:
+4
-4
@@ -9,17 +9,17 @@ matrix:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-8
|
||||
- g++-9
|
||||
env:
|
||||
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
|
||||
- MATRIX_EVAL="CC=gcc-9 && CXX=g++-9"
|
||||
compiler:
|
||||
- gcc
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
- sudo apt-get install build-essential cmake libboost-all-dev libconfig++-dev libavcodec-dev libavutil-dev libavformat-dev ffmpeg libmagick++-dev libpstreams-dev libconfig++-dev libpstreams-dev libtag1-dev
|
||||
- sudo apt-get install build-essential cmake libboost-all-dev libconfig++-dev libavcodec-dev libavutil-dev libavformat-dev ffmpeg libgraphicsmagick++1-dev libpstreams-dev libconfig++-dev libpstreams-dev libtag1-dev
|
||||
- git clone https://github.com/emweb/wt.git wt
|
||||
- pushd wt;
|
||||
- git checkout 4.1.0
|
||||
- git checkout 4.2.1
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr && sudo make install
|
||||
- popd
|
||||
script: cmake -DCMAKE_BUILD_TYPE=Release . && make && make test
|
||||
|
||||
+6
-2
@@ -2,12 +2,16 @@ cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(lms)
|
||||
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
include(CTest)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(IMAGEMAGICKXX REQUIRED ImageMagick++)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Filesystem REQUIRED)
|
||||
pkg_check_modules(GRAPHICSMAGICKXX REQUIRED GraphicsMagick++)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ __Notes__:
|
||||
* a C++17 compiler is needed
|
||||
* ffmpeg version 4 minimum is required
|
||||
```sh
|
||||
apt-get install g++ cmake libboost-system-dev libavutil-dev libavformat-dev libmagick++-dev libconfig++-dev libpstreams-dev ffmpeg libtag1-dev
|
||||
apt-get install g++ cmake libboost-system-dev libavutil-dev libavformat-dev libgraphicsmagick++1-dev libconfig++-dev libpstreams-dev ffmpeg libtag1-dev
|
||||
```
|
||||
|
||||
You also need _Wt4_, which is not packaged yet on _Debian_. See [installation instructions](https://www.webtoolkit.eu/wt/doc/reference/html/InstallationUnix.html).</br>
|
||||
@@ -157,7 +157,7 @@ systemctl restart lms
|
||||
__Note__: don't forget to give the _lms_ user read access to the music directory you want to scan.
|
||||
|
||||
### Configuration
|
||||
_LMS_ uses a configuration file, installed by default in `/etc/lms.conf`. It is recommended to edit this file and change relevant settings (listen address, listen port, working directory, Subsonic API activation, ...).
|
||||
_LMS_ uses a configuration file, installed by default in `/etc/lms.conf`. It is recommended to edit this file and change relevant settings (listen address, listen port, working directory, Subsonic API activation, deployment path, ...).
|
||||
|
||||
All other settings are set using the web interface (user management, scan settings, transcode settings, ...).
|
||||
|
||||
@@ -208,6 +208,6 @@ To connect to _LMS_, just open your favorite browser and go to http://localhost:
|
||||
* Wt (http://www.webtoolkit.eu/)
|
||||
* bootstrap3 (http://getbootstrap.com/)
|
||||
* ffmpeg project (https://ffmpeg.org/)
|
||||
* Magick++ (http://www.imagemagick.org/Magick++/)
|
||||
* GraphicsMagick++ (http://www.graphicsmagick.org/)
|
||||
* MetaBrainz (https://metabrainz.org/)
|
||||
* Bootstrap Notify: https://github.com/mouse0270/bootstrap-notify
|
||||
|
||||
Executable
+233
@@ -0,0 +1,233 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
|
||||
FindFilesystem
|
||||
##############
|
||||
|
||||
This module supports the C++17 standard library's filesystem utilities. Use the
|
||||
:imp-target:`std::filesystem` imported target to
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
The ``COMPONENTS`` argument to this module supports the following values:
|
||||
|
||||
.. find-component:: Experimental
|
||||
:name: fs.Experimental
|
||||
|
||||
Allows the module to find the "experimental" Filesystem TS version of the
|
||||
Filesystem library. This is the library that should be used with the
|
||||
``std::experimental::filesystem`` namespace.
|
||||
|
||||
.. find-component:: Final
|
||||
:name: fs.Final
|
||||
|
||||
Finds the final C++17 standard version of the filesystem library.
|
||||
|
||||
If no components are provided, behaves as if the
|
||||
:find-component:`fs.Final` component was specified.
|
||||
|
||||
If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are
|
||||
provided, first looks for ``Final``, and falls back to ``Experimental`` in case
|
||||
of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all
|
||||
:ref:`variables <fs.variables>` will refer to the ``Final`` version.
|
||||
|
||||
|
||||
Imported Targets
|
||||
****************
|
||||
|
||||
.. imp-target:: std::filesystem
|
||||
|
||||
The ``std::filesystem`` imported target is defined when any requested
|
||||
version of the C++ filesystem library has been found, whether it is
|
||||
*Experimental* or *Final*.
|
||||
|
||||
If no version of the filesystem library is available, this target will not
|
||||
be defined.
|
||||
|
||||
.. note::
|
||||
This target has ``cxx_std_17`` as an ``INTERFACE``
|
||||
:ref:`compile language standard feature <req-lang-standards>`. Linking
|
||||
to this target will automatically enable C++17 if no later standard
|
||||
version is already required on the linking target.
|
||||
|
||||
|
||||
.. _fs.variables:
|
||||
|
||||
Variables
|
||||
*********
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL
|
||||
|
||||
Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++
|
||||
filesystem library was found, otherwise ``FALSE``.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_HAVE_FS
|
||||
|
||||
Set to ``TRUE`` when a filesystem header was found.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_HEADER
|
||||
|
||||
Set to either ``filesystem`` or ``experimental/filesystem`` depending on
|
||||
whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was
|
||||
found.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_NAMESPACE
|
||||
|
||||
Set to either ``std::filesystem`` or ``std::experimental::filesystem``
|
||||
depending on whether :find-component:`fs.Final` or
|
||||
:find-component:`fs.Experimental` was found.
|
||||
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
Using `find_package(Filesystem)` with no component arguments:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(Filesystem REQUIRED)
|
||||
|
||||
add_executable(my-program main.cpp)
|
||||
target_link_libraries(my-program PRIVATE std::filesystem)
|
||||
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
|
||||
if(TARGET std::filesystem)
|
||||
# This module has already been processed. Don't do it again.
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(CMakePushCheckState)
|
||||
include(CheckIncludeFileCXX)
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
cmake_push_check_state()
|
||||
|
||||
set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})
|
||||
|
||||
# All of our tests required C++17 or later
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Normalize and check the component list we were given
|
||||
set(want_components ${Filesystem_FIND_COMPONENTS})
|
||||
if(Filesystem_FIND_COMPONENTS STREQUAL "")
|
||||
set(want_components Final)
|
||||
endif()
|
||||
|
||||
# Warn on any unrecognized components
|
||||
set(extra_components ${want_components})
|
||||
list(REMOVE_ITEM extra_components Final Experimental)
|
||||
foreach(component IN LISTS extra_components)
|
||||
message(WARNING "Extraneous find_package component for Filesystem: ${component}")
|
||||
endforeach()
|
||||
|
||||
# Detect which of Experimental and Final we should look for
|
||||
set(find_experimental TRUE)
|
||||
set(find_final TRUE)
|
||||
if(NOT "Final" IN_LIST want_components)
|
||||
set(find_final FALSE)
|
||||
endif()
|
||||
if(NOT "Experimental" IN_LIST want_components)
|
||||
set(find_experimental FALSE)
|
||||
endif()
|
||||
|
||||
if(find_final)
|
||||
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
|
||||
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
if(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
# We found the non-experimental header. Don't bother looking for the
|
||||
# experimental one.
|
||||
set(find_experimental FALSE)
|
||||
endif()
|
||||
else()
|
||||
set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)
|
||||
endif()
|
||||
|
||||
if(find_experimental)
|
||||
check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
else()
|
||||
set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)
|
||||
endif()
|
||||
|
||||
if(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
set(_have_fs TRUE)
|
||||
set(_fs_header filesystem)
|
||||
set(_fs_namespace std::filesystem)
|
||||
elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
set(_have_fs TRUE)
|
||||
set(_fs_header experimental/filesystem)
|
||||
set(_fs_namespace std::experimental::filesystem)
|
||||
else()
|
||||
set(_have_fs FALSE)
|
||||
endif()
|
||||
|
||||
set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers")
|
||||
set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs")
|
||||
set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs")
|
||||
|
||||
set(_found FALSE)
|
||||
|
||||
if(CXX_FILESYSTEM_HAVE_FS)
|
||||
# We have some filesystem library available. Do link checks
|
||||
string(CONFIGURE [[
|
||||
#include <@CXX_FILESYSTEM_HEADER@>
|
||||
|
||||
int main() {
|
||||
auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();
|
||||
return static_cast<int>(cwd.string().size());
|
||||
}
|
||||
]] code @ONLY)
|
||||
|
||||
# Try to compile a simple filesystem program without any linker flags
|
||||
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
|
||||
set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
|
||||
|
||||
if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES})
|
||||
# Try to compile a simple filesystem program with the libstdc++ flag
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs)
|
||||
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
|
||||
if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
# Try to compile a simple filesystem program with the libc++ flag
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs)
|
||||
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
|
||||
set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})
|
||||
if(NOT CXX_FILESYSTEM_CPPFS_NEEDED)
|
||||
# Try to compile a simple filesystem program without any linker flags
|
||||
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if(can_link)
|
||||
add_library(std::filesystem INTERFACE IMPORTED)
|
||||
target_compile_features(std::filesystem INTERFACE cxx_std_17)
|
||||
set(_found TRUE)
|
||||
|
||||
if(CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
# Nothing to add...
|
||||
elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
target_link_libraries(std::filesystem INTERFACE -lstdc++fs)
|
||||
elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
|
||||
target_link_libraries(std::filesystem INTERFACE -lc++fs)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cmake_pop_check_state()
|
||||
|
||||
set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE)
|
||||
|
||||
if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
|
||||
message(FATAL_ERROR "Cannot Compile simple program using std::filesystem")
|
||||
endif()
|
||||
@@ -18,6 +18,9 @@ listen-port = 5082;
|
||||
listen-addr = "0.0.0.0";
|
||||
behind-reverse-proxy = false;
|
||||
|
||||
# Location for deployment
|
||||
deploy-path = "/";
|
||||
|
||||
# If enabled, these files have to exist and have correct permissions
|
||||
tls-enable = false;
|
||||
tls-cert = "/var/lms/cert.pem";
|
||||
|
||||
+37
-41
@@ -5,7 +5,7 @@ WORKDIR /tmp/workdir
|
||||
ARG MAKEFLAGS="-j2"
|
||||
ARG FFMPEG_VERSION=4.1.4
|
||||
ARG WT_VERSION=4.2.0
|
||||
ARG IMAGEMAGICK6_VERSION=6.9.10-71
|
||||
ARG GRAPHICSMAGICK_VERSION=1.3.35
|
||||
ARG PSTREAMS_VERSION=1.0.1
|
||||
ARG LMS_VERSION=v3.6.3
|
||||
|
||||
@@ -24,6 +24,7 @@ ARG BUILD_PACKAGES=" \
|
||||
make \
|
||||
libjpeg-turbo-dev \
|
||||
openjpeg-dev \
|
||||
jasper-dev \
|
||||
libpng-dev \
|
||||
tiff-dev \
|
||||
nasm \
|
||||
@@ -95,50 +96,42 @@ RUN \
|
||||
make && \
|
||||
make install
|
||||
|
||||
# ImageMagick
|
||||
# GraphicsMagick++
|
||||
RUN \
|
||||
DIR=/tmp/imagemagick && mkdir -p ${DIR} && cd ${DIR} && \
|
||||
curl -sLO https://github.com/ImageMagick/ImageMagick6/archive/${IMAGEMAGICK6_VERSION}.tar.gz && \
|
||||
tar -x --strip-components=1 -f ${IMAGEMAGICK6_VERSION}.tar.gz
|
||||
DIR=/tmp/graphicsmagick && mkdir -p ${DIR} && cd ${DIR} && \
|
||||
curl -sL ftp://ftp.graphicsmagick.org/pub/GraphicsMagick/1.3/GraphicsMagick-${GRAPHICSMAGICK_VERSION}.tar.gz -o ${GRAPHICSMAGICK_VERSION}.tar.gz && \
|
||||
tar -x --strip-components=1 -f ${GRAPHICSMAGICK_VERSION}.tar.gz
|
||||
|
||||
RUN \
|
||||
DIR=/tmp/imagemagick && mkdir -p ${DIR} && cd ${DIR} && \
|
||||
DIR=/tmp/graphicsmagick && mkdir -p ${DIR} && cd ${DIR} && \
|
||||
./configure \
|
||||
--prefix=${PREFIX} \
|
||||
--enable-shared=yes \
|
||||
--disable-static \
|
||||
--disable-docs \
|
||||
--with-magick-plus-plus \
|
||||
--without-zstd \
|
||||
--without-dps \
|
||||
--without-autotrace \
|
||||
--without-dps \
|
||||
--without-fftw \
|
||||
--without-flif \
|
||||
--without-fpx \
|
||||
--without-djvu \
|
||||
--without-fontconfig \
|
||||
--without-freetype \
|
||||
--without-raqm \
|
||||
--without-gslib \
|
||||
--without-gvc \
|
||||
--without-heic \
|
||||
--without-jbig \
|
||||
--with-jpeg \
|
||||
--without-jxl \
|
||||
--without-lcms \
|
||||
--with-openjp2 \
|
||||
--without-lqr \
|
||||
--with-lzma \
|
||||
--without-openexr \
|
||||
--without-pango \
|
||||
--with-png \
|
||||
--without-raw \
|
||||
--without-rsvg \
|
||||
--with-tiff \
|
||||
--without-webp \
|
||||
--without-wmf \
|
||||
--without-xml && \
|
||||
--enable-static=no \
|
||||
--with-quantum-depth=8 \
|
||||
--with-modules=no \
|
||||
--with-frozenpaths=no \
|
||||
--with-magick-plus-plus=yes \
|
||||
--with-perl=no \
|
||||
--with-bzlib=no \
|
||||
--with-dps=no \
|
||||
--with-fpx=no \
|
||||
--with-jbig=no \
|
||||
--with-webp=no \
|
||||
--with-jp2=yes \
|
||||
--with-jpeg=yes \
|
||||
--with-lcms2=no \
|
||||
--with-lzma=no \
|
||||
--with-png=yes \
|
||||
--with-tiff=yes \
|
||||
--with-trio=no \
|
||||
--with-ttf=no \
|
||||
--with-tcmalloc=no \
|
||||
--with-wmf=no \
|
||||
--with-xml=no \
|
||||
--with-zlib=yes \
|
||||
--with-zstd=no \
|
||||
--with-x=no && \
|
||||
make && \
|
||||
make install && \
|
||||
make distclean
|
||||
@@ -158,8 +151,9 @@ RUN \
|
||||
|
||||
RUN \
|
||||
DIR=/tmp/lms && mkdir -p ${DIR} && cd ${DIR} && \
|
||||
PKG_CONFIG_PATH=/tmp/install/lib/pkgconfig CXXFLAGS="-I${PREFIX}/include -L${PREFIX}/lib -Wl,--rpath-link=${PREFIX}/lib" cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} && \
|
||||
PKG_CONFIG_PATH=/tmp/install/lib/pkgconfig CXXFLAGS="-I${PREFIX}/include" LDFLAGS="-L${PREFIX}/lib -Wl,--rpath-link=${PREFIX}/lib" cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} && \
|
||||
make && \
|
||||
LD_LIBRARY_PATH=${PREFIX}/lib make test && \
|
||||
make install && \
|
||||
mkdir -p ${PREFIX}/etc/ && \
|
||||
cp conf/lms.conf ${PREFIX}/etc
|
||||
@@ -178,7 +172,8 @@ RUN \
|
||||
done && \
|
||||
cp -r ${PREFIX}/lib /tmp/fakeroot/lib && \
|
||||
cp -r ${PREFIX}/share /tmp/fakeroot/share && \
|
||||
LD_LIBRARY_PATH=/tmp/fakeroot/lib /tmp/fakeroot/bin/ffmpeg -buildconf
|
||||
rm -rf /tmp/fakeroot/share/doc && \
|
||||
rm -rf /tmp/fakeroot/share/man
|
||||
|
||||
## Release Stage
|
||||
FROM alpine:3.10 AS release
|
||||
@@ -188,6 +183,7 @@ ARG RUNTIME_PACKAGES=" \
|
||||
openssl \
|
||||
libjpeg-turbo \
|
||||
openjpeg \
|
||||
jasper-libs \
|
||||
libpng \
|
||||
tiff \
|
||||
libogg \
|
||||
|
||||
@@ -19,6 +19,7 @@ target_link_libraries(lmsav PUBLIC
|
||||
lmsutils
|
||||
avformat
|
||||
avutil
|
||||
std::filesystem
|
||||
wt
|
||||
)
|
||||
|
||||
|
||||
@@ -10,17 +10,21 @@ target_include_directories(lmscover INTERFACE
|
||||
|
||||
target_include_directories(lmscover PRIVATE
|
||||
include
|
||||
${IMAGEMAGICKXX_INCLUDE_DIRS}
|
||||
${GRAPHICSMAGICKXX_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_options(lmscover PRIVATE
|
||||
${IMAGEMAGICKXX_CFLAGS_OTHER}
|
||||
${GRAPHICSMAGICKXX_CFLAGS_OTHER}
|
||||
)
|
||||
|
||||
target_link_libraries(lmscover PRIVATE
|
||||
lmsav
|
||||
${GRAPHICSMAGICKXX_LIBRARIES}
|
||||
)
|
||||
|
||||
target_link_libraries(lmscover PUBLIC
|
||||
lmsdatabase
|
||||
${IMAGEMAGICKXX_LIBRARIES}
|
||||
std::filesystem
|
||||
)
|
||||
|
||||
install(TARGETS lmscover DESTINATION lib)
|
||||
|
||||
@@ -49,15 +49,13 @@ Grabber::Grabber(const std::filesystem::path& execPath)
|
||||
init(execPath);
|
||||
}
|
||||
|
||||
Grabber::~Grabber()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
void
|
||||
Grabber::setDefaultCover(const std::filesystem::path& p)
|
||||
{
|
||||
if (!_defaultCover.load(p))
|
||||
std::unique_lock lock {_mutex};
|
||||
|
||||
_defaultCover = std::make_unique<Image>();
|
||||
if (!_defaultCover->load(p))
|
||||
throw LmsException("Cannot read default cover file '" + p.string() + "'");
|
||||
}
|
||||
|
||||
@@ -65,12 +63,12 @@ Image
|
||||
Grabber::getDefaultCover(std::size_t size)
|
||||
{
|
||||
LMS_LOG(COVER, DEBUG) << "Getting a default cover using size = " << size;
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
std::unique_lock lock {_mutex};
|
||||
|
||||
auto it = _defaultCovers.find(size);
|
||||
if (it == _defaultCovers.end())
|
||||
{
|
||||
Image cover = _defaultCover;
|
||||
Image cover = *_defaultCover;
|
||||
|
||||
LMS_LOG(COVER, DEBUG) << "default cover size = " << cover.getSize().width << " x " << cover.getSize().height;
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ namespace CoverArt
|
||||
{
|
||||
public:
|
||||
Grabber(const std::filesystem::path& execPath);
|
||||
~Grabber();
|
||||
|
||||
Grabber(const Grabber&) = delete;
|
||||
Grabber& operator=(const Grabber&) = delete;
|
||||
@@ -63,9 +62,8 @@ namespace CoverArt
|
||||
std::optional<Image> getFromDirectory(const std::filesystem::path& path) const;
|
||||
Image getDefaultCover(std::size_t size);
|
||||
|
||||
Image _defaultCover;
|
||||
|
||||
std::mutex _mutex;
|
||||
std::unique_ptr<Image> _defaultCover;
|
||||
std::map<std::size_t /* size */, Image> _defaultCovers;
|
||||
|
||||
static inline const std::vector<std::filesystem::path> _fileExtensions {".jpg", ".jpeg", ".png", ".bmp"}; // TODO parametrize
|
||||
|
||||
@@ -29,12 +29,6 @@ init(const std::filesystem::path& path)
|
||||
Magick::InitializeMagick(path.string().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
deinit()
|
||||
{
|
||||
MagickCore::MagickCoreTerminus();
|
||||
}
|
||||
|
||||
static
|
||||
std::string
|
||||
formatToMagick(Format format)
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace CoverArt
|
||||
{
|
||||
|
||||
void init(const std::filesystem::path& path);
|
||||
void deinit();
|
||||
|
||||
class Image
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ target_link_libraries(lmsdatabase PRIVATE
|
||||
|
||||
target_link_libraries(lmsdatabase PUBLIC
|
||||
lmsutils
|
||||
std::filesystem
|
||||
wtdbo
|
||||
)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
namespace Database {
|
||||
|
||||
#define LMS_DATABASE_VERSION 13
|
||||
#define LMS_DATABASE_VERSION 14
|
||||
|
||||
using Version = std::size_t;
|
||||
|
||||
@@ -153,7 +153,13 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" (
|
||||
}
|
||||
else if (version == 12)
|
||||
{
|
||||
// Artist and release that have a baddly parsed name but a MBID had no chance to updat the name
|
||||
// Artist and release that have a badly parsed name but a MBID had no chance to updat the name
|
||||
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||
ScanSettings::get(*this).modify()->incScanVersion();
|
||||
}
|
||||
else if (version == 13)
|
||||
{
|
||||
// Always store UUID in lower case + better WMA parsing
|
||||
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||
ScanSettings::get(*this).modify()->incScanVersion();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ target_link_libraries(lmsmetadata PRIVATE
|
||||
|
||||
target_link_libraries(lmsmetadata PUBLIC
|
||||
lmsutils
|
||||
std::filesystem
|
||||
)
|
||||
|
||||
install(TARGETS lmsmetadata DESTINATION lib)
|
||||
|
||||
@@ -154,12 +154,118 @@ getAlbum(const TagLib::PropertyMap& properties)
|
||||
return Album {std::move(albumName.front()), albumMBID.front()};
|
||||
}
|
||||
|
||||
void
|
||||
TagLibParser::processTag(Track& track, const std::string& tag, const TagLib::StringList& values, bool debug)
|
||||
{
|
||||
|
||||
// TODO validate MBID format
|
||||
if (debug)
|
||||
{
|
||||
std::vector<std::string> strs;
|
||||
std::transform(values.begin(), values.end(), std::back_inserter(strs), [](const auto& value) { return value.to8Bit(true); });
|
||||
|
||||
std::cout << "[" << tag << "] = " << StringUtils::joinStrings(strs, "*SEP*") << std::endl;
|
||||
}
|
||||
|
||||
if (tag.empty() || values.isEmpty() || values.front().isEmpty())
|
||||
return;
|
||||
|
||||
std::string value {StringUtils::stringTrim(values.front().to8Bit(true))};
|
||||
|
||||
if (tag == "TITLE")
|
||||
track.title = value;
|
||||
else if (tag == "MUSICBRAINZ_RELEASETRACKID"
|
||||
|| tag == "MUSICBRAINZ RELEASE TRACK ID")
|
||||
{
|
||||
track.musicBrainzTrackID = UUID::fromString(value);
|
||||
}
|
||||
else if (tag == "MUSICBRAINZ_TRACKID"
|
||||
|| tag == "MUSICBRAINZ TRACK ID")
|
||||
track.musicBrainzRecordID = UUID::fromString(value);
|
||||
else if (tag == "ACOUSTID_ID")
|
||||
track.acoustID = UUID::fromString(value);
|
||||
else if (tag == "TRACKTOTAL")
|
||||
{
|
||||
auto totalTrack = StringUtils::readAs<std::size_t>(value);
|
||||
if (totalTrack)
|
||||
track.totalTrack = totalTrack;
|
||||
}
|
||||
else if (tag == "TRACKNUMBER")
|
||||
{
|
||||
// Expecting 'Number/Total'
|
||||
std::vector<std::string> strings {splitAndTrimString(value, "/")};
|
||||
|
||||
if (!strings.empty())
|
||||
{
|
||||
track.trackNumber = StringUtils::readAs<std::size_t>(strings[0]);
|
||||
|
||||
// Lower priority than TRACKTOTAL
|
||||
if (strings.size() > 1 && !track.totalTrack)
|
||||
track.totalTrack = StringUtils::readAs<std::size_t>(strings[1]);
|
||||
}
|
||||
}
|
||||
else if (tag == "DISCTOTAL")
|
||||
{
|
||||
auto totalDisc = StringUtils::readAs<std::size_t>(value);
|
||||
if (totalDisc)
|
||||
track.totalDisc = totalDisc;
|
||||
}
|
||||
else if (tag == "DISCNUMBER")
|
||||
{
|
||||
// Expecting 'Number/Total'
|
||||
std::vector<std::string> strings {StringUtils::splitString(value, "/")};
|
||||
|
||||
if (!strings.empty())
|
||||
{
|
||||
track.discNumber = StringUtils::readAs<std::size_t>(strings[0]);
|
||||
|
||||
// Lower priority than DISCTOTAL
|
||||
if (strings.size() > 1 && !track.totalDisc)
|
||||
track.totalDisc = StringUtils::readAs<std::size_t>(strings[1]);
|
||||
}
|
||||
}
|
||||
else if (tag == "DATE")
|
||||
track.year = StringUtils::readAs<int>(value);
|
||||
else if (tag == "ORIGINALDATE" && !track.originalYear)
|
||||
{
|
||||
// Lower priority than ORIGINALYEAR
|
||||
track.originalYear = StringUtils::readAs<int>(value);
|
||||
}
|
||||
else if (tag == "ORIGINALYEAR")
|
||||
{
|
||||
// Higher priority than ORIGINALDATE
|
||||
auto originalYear = StringUtils::readAs<int>(value);
|
||||
if (originalYear)
|
||||
track.originalYear = originalYear;
|
||||
}
|
||||
else if (tag == "METADATA_BLOCK_PICTURE")
|
||||
track.hasCover = true;
|
||||
else if (tag == "COPYRIGHT")
|
||||
track.copyright = value;
|
||||
else if (tag == "COPYRIGHTURL")
|
||||
track.copyrightURL = value;
|
||||
else if (_clusterTypeNames.find(tag) != _clusterTypeNames.end())
|
||||
{
|
||||
std::set<std::string> clusterNames;
|
||||
for (const auto& valueList : values)
|
||||
{
|
||||
auto values = splitAndTrimString(valueList.to8Bit(true), "/,;");
|
||||
|
||||
for (const auto& value : values)
|
||||
clusterNames.insert(value);
|
||||
}
|
||||
|
||||
if (!clusterNames.empty())
|
||||
track.clusters[tag] = clusterNames;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Track>
|
||||
TagLibParser::parse(const std::filesystem::path& p, bool debug)
|
||||
{
|
||||
TagLib::FileRef f {p.string().c_str(),
|
||||
true, // read audio properties
|
||||
TagLib::AudioProperties::Fast};
|
||||
true, // read audio properties
|
||||
TagLib::AudioProperties::Fast};
|
||||
|
||||
if (f.isNull())
|
||||
{
|
||||
@@ -184,14 +290,40 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
|
||||
track.audioStreams = {std::move(audioStream)};
|
||||
}
|
||||
|
||||
TagLib::PropertyMap properties {f.file()->properties()};
|
||||
|
||||
// Not that good embedded pictures handling
|
||||
|
||||
// WMA
|
||||
if (TagLib::ASF::File* asfFile {dynamic_cast<TagLib::ASF::File*>(f.file())})
|
||||
{
|
||||
const TagLib::ASF::Tag* tag {asfFile->tag()};
|
||||
if (tag && tag->attributeListMap().contains("WM/Picture"))
|
||||
track.hasCover = true;
|
||||
if (tag)
|
||||
{
|
||||
if (tag->attributeListMap().contains("WM/Picture"))
|
||||
track.hasCover = true;
|
||||
|
||||
for (const auto& [name, attributeList] : tag->attributeListMap())
|
||||
{
|
||||
if (name.to8Bit().find("WM/") == 0 || properties.contains(name))
|
||||
continue;
|
||||
|
||||
TagLib::StringList stringAttributeList;
|
||||
for (const auto& attribute : attributeList)
|
||||
{
|
||||
if (attribute.type() == TagLib::ASF::Attribute::AttributeTypes::UnicodeType)
|
||||
stringAttributeList.append(attribute.toString());
|
||||
}
|
||||
|
||||
if (!stringAttributeList.isEmpty())
|
||||
{
|
||||
if (debug)
|
||||
std::cout << "Property: '" << name << "'" << std::endl;
|
||||
|
||||
properties.insert(name, stringAttributeList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// MP3
|
||||
else if (TagLib::MPEG::File* mp3File {dynamic_cast<TagLib::MPEG::File*>(f.file())})
|
||||
@@ -209,124 +341,18 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
|
||||
track.hasCover = true;
|
||||
}
|
||||
|
||||
if (f.tag())
|
||||
for(const auto& property : properties)
|
||||
{
|
||||
MetaData::Clusters clusters;
|
||||
const TagLib::PropertyMap& properties {f.file()->properties()};
|
||||
|
||||
for(const auto& property : properties)
|
||||
{
|
||||
const std::string tag {property.first.upper().to8Bit(true)};
|
||||
const TagLib::StringList& values {property.second};
|
||||
|
||||
// TODO validate MBID format
|
||||
if (debug)
|
||||
{
|
||||
std::vector<std::string> strs;
|
||||
std::transform(values.begin(), values.end(), std::back_inserter(strs), [](const auto& value) { return value.to8Bit(true); });
|
||||
|
||||
std::cout << "[" << tag << "] = " << StringUtils::joinStrings(strs, "*SEP*") << std::endl;
|
||||
}
|
||||
|
||||
if (tag.empty() || values.isEmpty() || values.front().isEmpty())
|
||||
continue;
|
||||
|
||||
std::string value {StringUtils::stringTrim(values.front().to8Bit(true))};
|
||||
|
||||
if (tag == "TITLE")
|
||||
track.title = value;
|
||||
else if (tag == "MUSICBRAINZ_RELEASETRACKID"
|
||||
|| tag == "MUSICBRAINZ RELEASE TRACK ID")
|
||||
{
|
||||
track.musicBrainzTrackID = UUID::fromString(value);
|
||||
}
|
||||
else if (tag == "MUSICBRAINZ_TRACKID"
|
||||
|| tag == "MUSICBRAINZ TRACK ID")
|
||||
track.musicBrainzRecordID = UUID::fromString(value);
|
||||
else if (tag == "ACOUSTID_ID")
|
||||
track.acoustID = UUID::fromString(value);
|
||||
else if (tag == "TRACKTOTAL")
|
||||
{
|
||||
auto totalTrack = StringUtils::readAs<std::size_t>(value);
|
||||
if (totalTrack)
|
||||
track.totalTrack = totalTrack;
|
||||
}
|
||||
else if (tag == "TRACKNUMBER")
|
||||
{
|
||||
// Expecting 'Number/Total'
|
||||
std::vector<std::string> strings {splitAndTrimString(value, "/")};
|
||||
|
||||
if (!strings.empty())
|
||||
{
|
||||
track.trackNumber = StringUtils::readAs<std::size_t>(strings[0]);
|
||||
|
||||
// Lower priority than TRACKTOTAL
|
||||
if (strings.size() > 1 && !track.totalTrack)
|
||||
track.totalTrack = StringUtils::readAs<std::size_t>(strings[1]);
|
||||
}
|
||||
}
|
||||
else if (tag == "DISCTOTAL")
|
||||
{
|
||||
auto totalDisc = StringUtils::readAs<std::size_t>(value);
|
||||
if (totalDisc)
|
||||
track.totalDisc = totalDisc;
|
||||
}
|
||||
else if (tag == "DISCNUMBER")
|
||||
{
|
||||
// Expecting 'Number/Total'
|
||||
std::vector<std::string> strings {StringUtils::splitString(value, "/")};
|
||||
|
||||
if (!strings.empty())
|
||||
{
|
||||
track.discNumber = StringUtils::readAs<std::size_t>(strings[0]);
|
||||
|
||||
// Lower priority than DISCTOTAL
|
||||
if (strings.size() > 1 && !track.totalDisc)
|
||||
track.totalDisc = StringUtils::readAs<std::size_t>(strings[1]);
|
||||
}
|
||||
}
|
||||
else if (tag == "DATE")
|
||||
track.year = StringUtils::readAs<int>(value);
|
||||
else if (tag == "ORIGINALDATE" && !track.originalYear)
|
||||
{
|
||||
// Lower priority than ORIGINALYEAR
|
||||
track.originalYear = StringUtils::readAs<int>(value);
|
||||
}
|
||||
else if (tag == "ORIGINALYEAR")
|
||||
{
|
||||
// Higher priority than ORIGINALDATE
|
||||
auto originalYear = StringUtils::readAs<int>(value);
|
||||
if (originalYear)
|
||||
track.originalYear = originalYear;
|
||||
}
|
||||
else if (tag == "METADATA_BLOCK_PICTURE")
|
||||
track.hasCover = true;
|
||||
else if (tag == "COPYRIGHT")
|
||||
track.copyright = value;
|
||||
else if (tag == "COPYRIGHTURL")
|
||||
track.copyrightURL = value;
|
||||
else if (_clusterTypeNames.find(tag) != _clusterTypeNames.end())
|
||||
{
|
||||
std::set<std::string> clusterNames;
|
||||
for (const auto& valueList : values)
|
||||
{
|
||||
auto values = splitAndTrimString(valueList.to8Bit(true), "/,;");
|
||||
|
||||
for (const auto& value : values)
|
||||
clusterNames.insert(value);
|
||||
}
|
||||
|
||||
if (!clusterNames.empty())
|
||||
track.clusters[tag] = clusterNames;
|
||||
}
|
||||
}
|
||||
|
||||
track.artists = getArtists(properties);
|
||||
track.albumArtists = getAlbumArtists(properties);
|
||||
track.album = getAlbum(properties);
|
||||
const std::string tag {property.first.upper().to8Bit(true)};
|
||||
const TagLib::StringList& values {property.second};
|
||||
|
||||
processTag(track, tag, values, debug);
|
||||
}
|
||||
|
||||
track.artists = getArtists(properties);
|
||||
track.albumArtists = getAlbumArtists(properties);
|
||||
track.album = getAlbum(properties);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,14 +21,21 @@
|
||||
|
||||
#include "metadata/IParser.hpp"
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
class StringList;
|
||||
}
|
||||
|
||||
namespace MetaData
|
||||
{
|
||||
|
||||
// Parse that makes use of AvFormat
|
||||
class TagLibParser : public IParser
|
||||
{
|
||||
public:
|
||||
private:
|
||||
std::optional<Track> parse(const std::filesystem::path& p, bool debug = false) override;
|
||||
|
||||
void processTag(Track& track, const std::string& tag, const TagLib::StringList& values, bool debug);
|
||||
};
|
||||
|
||||
} // namespace MetaData
|
||||
|
||||
@@ -18,6 +18,7 @@ target_include_directories(lmsrecommendation PRIVATE
|
||||
target_link_libraries(lmsrecommendation PRIVATE
|
||||
lmsdatabase
|
||||
lmssom
|
||||
std::filesystem
|
||||
wt
|
||||
)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Recommendation
|
||||
|
||||
private:
|
||||
|
||||
std::string_view getName() const { return "Clusters"; }
|
||||
std::string_view getName() const override { return "Clusters"; }
|
||||
|
||||
bool init(Database::Session&, bool) override {return true;}
|
||||
void requestCancelInit() override {}
|
||||
|
||||
@@ -55,15 +55,15 @@ class FeaturesClassifier : public IClassifier
|
||||
static const FeatureSettingsMap& getDefaultTrainFeatureSettings();
|
||||
private:
|
||||
|
||||
std::string_view getName() const { return "Features"; }
|
||||
std::string_view getName() const override { return "Features"; }
|
||||
|
||||
bool init(Database::Session& session, bool databaseChanged) override;
|
||||
void requestCancelInit() override;
|
||||
|
||||
std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) const override;
|
||||
std::vector<Database::IdType> getSimilarTracks(Database::Session& session, const std::unordered_set<Database::IdType>& tracksId, std::size_t maxCount) const override;
|
||||
std::vector<Database::IdType> getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) const;
|
||||
std::vector<Database::IdType> getSimilarArtists(Database::Session& session, Database::IdType artistId, std::size_t maxCount) const;
|
||||
std::vector<Database::IdType> getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount) const override;
|
||||
std::vector<Database::IdType> getSimilarArtists(Database::Session& session, Database::IdType artistId, std::size_t maxCount) const override;
|
||||
|
||||
bool initFromCache(Database::Session& session, const FeaturesClassifierCache& cache);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ target_link_libraries(lmsscanner PRIVATE
|
||||
)
|
||||
|
||||
target_link_libraries(lmsscanner PUBLIC
|
||||
std::filesystem
|
||||
wt
|
||||
)
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ target_link_libraries(lmssubsonic PRIVATE
|
||||
lmscover
|
||||
lmsrecommendation
|
||||
lmsutils
|
||||
std::filesystem
|
||||
)
|
||||
|
||||
target_link_libraries(lmssubsonic PUBLIC
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace StringUtils
|
||||
{
|
||||
template<>
|
||||
std::optional<API::Subsonic::Id>
|
||||
StringUtils::readAs(const std::string& str)
|
||||
readAs(const std::string& str)
|
||||
{
|
||||
return API::Subsonic::IdFromString(str);
|
||||
}
|
||||
|
||||
template<>
|
||||
std::optional<bool>
|
||||
StringUtils::readAs(const std::string& str)
|
||||
readAs(const std::string& str)
|
||||
{
|
||||
if (str == "true")
|
||||
return true;
|
||||
|
||||
@@ -90,10 +90,10 @@ namespace StringUtils
|
||||
{
|
||||
template<>
|
||||
std::optional<API::Subsonic::Id>
|
||||
StringUtils::readAs(const std::string& str);
|
||||
readAs(const std::string& str);
|
||||
|
||||
template<>
|
||||
std::optional<bool>
|
||||
StringUtils::readAs(const std::string& str);
|
||||
readAs(const std::string& str);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "subsonic/SubsonicResource.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <Wt/WLocalDateTime.h>
|
||||
|
||||
@@ -65,7 +66,7 @@ namespace StringUtils
|
||||
{
|
||||
template<>
|
||||
std::optional<API::Subsonic::ClientVersion>
|
||||
StringUtils::readAs(const std::string& str)
|
||||
readAs(const std::string& str)
|
||||
{
|
||||
// Expects "X.Y.Z"
|
||||
const auto numbers {StringUtils::splitString(str, ".")};
|
||||
|
||||
@@ -36,7 +36,7 @@ class SubsonicResource final : public Wt::WResource
|
||||
public:
|
||||
SubsonicResource(Database::Db& db);
|
||||
|
||||
static std::string getPath() { return "/rest/"; }
|
||||
static std::string getPath() { return "rest/"; }
|
||||
private:
|
||||
|
||||
void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) override;
|
||||
|
||||
@@ -26,7 +26,7 @@ target_link_libraries(lmsutils PRIVATE
|
||||
|
||||
target_link_libraries(lmsutils PUBLIC
|
||||
boost_system
|
||||
stdc++fs
|
||||
std::filesystem
|
||||
wt
|
||||
)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "utils/String.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
@@ -98,15 +99,25 @@ stringTrimEnd(const std::string& str, const std::string& whitespace)
|
||||
}
|
||||
|
||||
std::string
|
||||
stringToLower(const std::string& str)
|
||||
stringToLower(std::string_view str)
|
||||
{
|
||||
return boost::algorithm::to_lower_copy(str);
|
||||
std::string res;
|
||||
res.reserve(str.size());
|
||||
|
||||
std::transform(std::cbegin(str), std::cend(str), std::back_inserter(res), [](char c) { return std::tolower(c);});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string
|
||||
stringToUpper(const std::string& str)
|
||||
{
|
||||
return boost::to_upper_copy<std::string>(str);
|
||||
std::string res;
|
||||
res.reserve(str.size());
|
||||
|
||||
std::transform(std::cbegin(str), std::cend(str), std::back_inserter(res), [](char c) { return std::toupper(c);});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "utils/String.hpp"
|
||||
|
||||
namespace StringUtils
|
||||
{
|
||||
template <>
|
||||
@@ -40,6 +42,10 @@ stringIsUUID(std::string_view str)
|
||||
return std::regex_match(std::cbegin(str), std::cend(str), re);
|
||||
}
|
||||
|
||||
UUID::UUID(std::string_view str)
|
||||
: _value {StringUtils::stringToLower(str)}
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<UUID>
|
||||
UUID::fromString(std::string_view str)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
@@ -40,7 +41,7 @@ std::string
|
||||
stringTrimEnd(const std::string& str, const std::string& whitespaces = " \t");
|
||||
|
||||
std::string
|
||||
stringToLower(const std::string& str);
|
||||
stringToLower(std::string_view str);
|
||||
|
||||
std::string
|
||||
stringToUpper(const std::string& str);
|
||||
|
||||
@@ -34,7 +34,7 @@ class UUID
|
||||
std::string_view getAsString() const { return _value; }
|
||||
|
||||
private:
|
||||
UUID(std::string_view value) : _value {value} {}
|
||||
UUID(std::string_view value);
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ std::vector<std::string> generateWtConfig(std::string execPath)
|
||||
args.push_back("--config=" + wtConfigPath.string());
|
||||
args.push_back("--docroot=" + ServiceProvider<IConfig>::get()->getString("docroot"));
|
||||
args.push_back("--approot=" + ServiceProvider<IConfig>::get()->getString("approot"));
|
||||
args.push_back("--deploy-path=" + ServiceProvider<IConfig>::get()->getString("deploy-path", "/"));
|
||||
args.push_back("--resources-dir=" + ServiceProvider<IConfig>::get()->getString("wt-resources"));
|
||||
|
||||
if (ServiceProvider<IConfig>::get()->getBool("tls-enable", false))
|
||||
@@ -197,6 +198,8 @@ int main(int argc, char* argv[])
|
||||
LMS_LOG(MAIN, INFO) << "Stopping recommendation engine...";
|
||||
recommendationEngine.stop();
|
||||
|
||||
ServiceProvider<CoverArt::IGrabber>::clear();
|
||||
|
||||
LMS_LOG(MAIN, INFO) << "Clean stop!";
|
||||
res = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ dumpRecommendation(Database::Session session, Recommendation::IEngine& engine)
|
||||
};
|
||||
|
||||
std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl;
|
||||
for (Database::IdType similarReleaseId : engine.getSimilarReleases(session, {releaseId}, 3))
|
||||
for (Database::IdType similarReleaseId : engine.getSimilarReleases(session, releaseId, 3))
|
||||
std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ dumpRecommendation(Database::Session session, Recommendation::IEngine& engine)
|
||||
};
|
||||
|
||||
std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl;
|
||||
for (Database::IdType similarArtistId : engine.getSimilarArtists(session, {artistId}, 3))
|
||||
for (Database::IdType similarArtistId : engine.getSimilarArtists(session, artistId, 3))
|
||||
std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user