Upgraded code to switch to C++17. Tested on Debian Buster
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
Installation Instructions
|
Installation Instructions
|
||||||
*************************
|
*************************
|
||||||
|
|
||||||
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
|
Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software
|
||||||
Inc.
|
Foundation, Inc.
|
||||||
|
|
||||||
Copying and distribution of this file, with or without modification,
|
Copying and distribution of this file, with or without modification,
|
||||||
are permitted in any medium without royalty provided the copyright
|
are permitted in any medium without royalty provided the copyright
|
||||||
@@ -12,97 +12,96 @@ without warranty of any kind.
|
|||||||
Basic Installation
|
Basic Installation
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Briefly, the shell command `./configure && make && make install'
|
Briefly, the shell command './configure && make && make install'
|
||||||
should configure, build, and install this package. The following
|
should configure, build, and install this package. The following
|
||||||
more-detailed instructions are generic; see the `README' file for
|
more-detailed instructions are generic; see the 'README' file for
|
||||||
instructions specific to this package. Some packages provide this
|
instructions specific to this package. Some packages provide this
|
||||||
`INSTALL' file but do not implement all of the features documented
|
'INSTALL' file but do not implement all of the features documented
|
||||||
below. The lack of an optional feature in a given package is not
|
below. The lack of an optional feature in a given package is not
|
||||||
necessarily a bug. More recommendations for GNU packages can be found
|
necessarily a bug. More recommendations for GNU packages can be found
|
||||||
in *note Makefile Conventions: (standards)Makefile Conventions.
|
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||||
|
|
||||||
The `configure' shell script attempts to guess correct values for
|
The 'configure' shell script attempts to guess correct values for
|
||||||
various system-dependent variables used during compilation. It uses
|
various system-dependent variables used during compilation. It uses
|
||||||
those values to create a `Makefile' in each directory of the package.
|
those values to create a 'Makefile' in each directory of the package.
|
||||||
It may also create one or more `.h' files containing system-dependent
|
It may also create one or more '.h' files containing system-dependent
|
||||||
definitions. Finally, it creates a shell script `config.status' that
|
definitions. Finally, it creates a shell script 'config.status' that
|
||||||
you can run in the future to recreate the current configuration, and a
|
you can run in the future to recreate the current configuration, and a
|
||||||
file `config.log' containing compiler output (useful mainly for
|
file 'config.log' containing compiler output (useful mainly for
|
||||||
debugging `configure').
|
debugging 'configure').
|
||||||
|
|
||||||
It can also use an optional file (typically called `config.cache'
|
It can also use an optional file (typically called 'config.cache' and
|
||||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
enabled with '--cache-file=config.cache' or simply '-C') that saves the
|
||||||
the results of its tests to speed up reconfiguring. Caching is
|
results of its tests to speed up reconfiguring. Caching is disabled by
|
||||||
disabled by default to prevent problems with accidental use of stale
|
default to prevent problems with accidental use of stale cache files.
|
||||||
cache files.
|
|
||||||
|
|
||||||
If you need to do unusual things to compile the package, please try
|
If you need to do unusual things to compile the package, please try
|
||||||
to figure out how `configure' could check whether to do them, and mail
|
to figure out how 'configure' could check whether to do them, and mail
|
||||||
diffs or instructions to the address given in the `README' so they can
|
diffs or instructions to the address given in the 'README' so they can
|
||||||
be considered for the next release. If you are using the cache, and at
|
be considered for the next release. If you are using the cache, and at
|
||||||
some point `config.cache' contains results you don't want to keep, you
|
some point 'config.cache' contains results you don't want to keep, you
|
||||||
may remove or edit it.
|
may remove or edit it.
|
||||||
|
|
||||||
The file `configure.ac' (or `configure.in') is used to create
|
The file 'configure.ac' (or 'configure.in') is used to create
|
||||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
'configure' by a program called 'autoconf'. You need 'configure.ac' if
|
||||||
you want to change it or regenerate `configure' using a newer version
|
you want to change it or regenerate 'configure' using a newer version of
|
||||||
of `autoconf'.
|
'autoconf'.
|
||||||
|
|
||||||
The simplest way to compile this package is:
|
The simplest way to compile this package is:
|
||||||
|
|
||||||
1. `cd' to the directory containing the package's source code and type
|
1. 'cd' to the directory containing the package's source code and type
|
||||||
`./configure' to configure the package for your system.
|
'./configure' to configure the package for your system.
|
||||||
|
|
||||||
Running `configure' might take a while. While running, it prints
|
Running 'configure' might take a while. While running, it prints
|
||||||
some messages telling which features it is checking for.
|
some messages telling which features it is checking for.
|
||||||
|
|
||||||
2. Type `make' to compile the package.
|
2. Type 'make' to compile the package.
|
||||||
|
|
||||||
3. Optionally, type `make check' to run any self-tests that come with
|
3. Optionally, type 'make check' to run any self-tests that come with
|
||||||
the package, generally using the just-built uninstalled binaries.
|
the package, generally using the just-built uninstalled binaries.
|
||||||
|
|
||||||
4. Type `make install' to install the programs and any data files and
|
4. Type 'make install' to install the programs and any data files and
|
||||||
documentation. When installing into a prefix owned by root, it is
|
documentation. When installing into a prefix owned by root, it is
|
||||||
recommended that the package be configured and built as a regular
|
recommended that the package be configured and built as a regular
|
||||||
user, and only the `make install' phase executed with root
|
user, and only the 'make install' phase executed with root
|
||||||
privileges.
|
privileges.
|
||||||
|
|
||||||
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
5. Optionally, type 'make installcheck' to repeat any self-tests, but
|
||||||
this time using the binaries in their final installed location.
|
this time using the binaries in their final installed location.
|
||||||
This target does not install anything. Running this target as a
|
This target does not install anything. Running this target as a
|
||||||
regular user, particularly if the prior `make install' required
|
regular user, particularly if the prior 'make install' required
|
||||||
root privileges, verifies that the installation completed
|
root privileges, verifies that the installation completed
|
||||||
correctly.
|
correctly.
|
||||||
|
|
||||||
6. You can remove the program binaries and object files from the
|
6. You can remove the program binaries and object files from the
|
||||||
source code directory by typing `make clean'. To also remove the
|
source code directory by typing 'make clean'. To also remove the
|
||||||
files that `configure' created (so you can compile the package for
|
files that 'configure' created (so you can compile the package for
|
||||||
a different kind of computer), type `make distclean'. There is
|
a different kind of computer), type 'make distclean'. There is
|
||||||
also a `make maintainer-clean' target, but that is intended mainly
|
also a 'make maintainer-clean' target, but that is intended mainly
|
||||||
for the package's developers. If you use it, you may have to get
|
for the package's developers. If you use it, you may have to get
|
||||||
all sorts of other programs in order to regenerate files that came
|
all sorts of other programs in order to regenerate files that came
|
||||||
with the distribution.
|
with the distribution.
|
||||||
|
|
||||||
7. Often, you can also type `make uninstall' to remove the installed
|
7. Often, you can also type 'make uninstall' to remove the installed
|
||||||
files again. In practice, not all packages have tested that
|
files again. In practice, not all packages have tested that
|
||||||
uninstallation works correctly, even though it is required by the
|
uninstallation works correctly, even though it is required by the
|
||||||
GNU Coding Standards.
|
GNU Coding Standards.
|
||||||
|
|
||||||
8. Some packages, particularly those that use Automake, provide `make
|
8. Some packages, particularly those that use Automake, provide 'make
|
||||||
distcheck', which can by used by developers to test that all other
|
distcheck', which can by used by developers to test that all other
|
||||||
targets like `make install' and `make uninstall' work correctly.
|
targets like 'make install' and 'make uninstall' work correctly.
|
||||||
This target is generally not run by end users.
|
This target is generally not run by end users.
|
||||||
|
|
||||||
Compilers and Options
|
Compilers and Options
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
Some systems require unusual options for compilation or linking that
|
Some systems require unusual options for compilation or linking that
|
||||||
the `configure' script does not know about. Run `./configure --help'
|
the 'configure' script does not know about. Run './configure --help'
|
||||||
for details on some of the pertinent environment variables.
|
for details on some of the pertinent environment variables.
|
||||||
|
|
||||||
You can give `configure' initial values for configuration parameters
|
You can give 'configure' initial values for configuration parameters
|
||||||
by setting variables in the command line or in the environment. Here
|
by setting variables in the command line or in the environment. Here is
|
||||||
is an example:
|
an example:
|
||||||
|
|
||||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||||
|
|
||||||
@@ -113,21 +112,21 @@ Compiling For Multiple Architectures
|
|||||||
|
|
||||||
You can compile the package for more than one kind of computer at the
|
You can compile the package for more than one kind of computer at the
|
||||||
same time, by placing the object files for each architecture in their
|
same time, by placing the object files for each architecture in their
|
||||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
own directory. To do this, you can use GNU 'make'. 'cd' to the
|
||||||
directory where you want the object files and executables to go and run
|
directory where you want the object files and executables to go and run
|
||||||
the `configure' script. `configure' automatically checks for the
|
the 'configure' script. 'configure' automatically checks for the source
|
||||||
source code in the directory that `configure' is in and in `..'. This
|
code in the directory that 'configure' is in and in '..'. This is known
|
||||||
is known as a "VPATH" build.
|
as a "VPATH" build.
|
||||||
|
|
||||||
With a non-GNU `make', it is safer to compile the package for one
|
With a non-GNU 'make', it is safer to compile the package for one
|
||||||
architecture at a time in the source code directory. After you have
|
architecture at a time in the source code directory. After you have
|
||||||
installed the package for one architecture, use `make distclean' before
|
installed the package for one architecture, use 'make distclean' before
|
||||||
reconfiguring for another architecture.
|
reconfiguring for another architecture.
|
||||||
|
|
||||||
On MacOS X 10.5 and later systems, you can create libraries and
|
On MacOS X 10.5 and later systems, you can create libraries and
|
||||||
executables that work on multiple system types--known as "fat" or
|
executables that work on multiple system types--known as "fat" or
|
||||||
"universal" binaries--by specifying multiple `-arch' options to the
|
"universal" binaries--by specifying multiple '-arch' options to the
|
||||||
compiler but only a single `-arch' option to the preprocessor. Like
|
compiler but only a single '-arch' option to the preprocessor. Like
|
||||||
this:
|
this:
|
||||||
|
|
||||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
@@ -136,105 +135,104 @@ this:
|
|||||||
|
|
||||||
This is not guaranteed to produce working output in all cases, you
|
This is not guaranteed to produce working output in all cases, you
|
||||||
may have to build one architecture at a time and combine the results
|
may have to build one architecture at a time and combine the results
|
||||||
using the `lipo' tool if you have problems.
|
using the 'lipo' tool if you have problems.
|
||||||
|
|
||||||
Installation Names
|
Installation Names
|
||||||
==================
|
==================
|
||||||
|
|
||||||
By default, `make install' installs the package's commands under
|
By default, 'make install' installs the package's commands under
|
||||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
'/usr/local/bin', include files under '/usr/local/include', etc. You
|
||||||
can specify an installation prefix other than `/usr/local' by giving
|
can specify an installation prefix other than '/usr/local' by giving
|
||||||
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
'configure' the option '--prefix=PREFIX', where PREFIX must be an
|
||||||
absolute file name.
|
absolute file name.
|
||||||
|
|
||||||
You can specify separate installation prefixes for
|
You can specify separate installation prefixes for
|
||||||
architecture-specific files and architecture-independent files. If you
|
architecture-specific files and architecture-independent files. If you
|
||||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
|
||||||
PREFIX as the prefix for installing programs and libraries.
|
PREFIX as the prefix for installing programs and libraries.
|
||||||
Documentation and other data files still use the regular prefix.
|
Documentation and other data files still use the regular prefix.
|
||||||
|
|
||||||
In addition, if you use an unusual directory layout you can give
|
In addition, if you use an unusual directory layout you can give
|
||||||
options like `--bindir=DIR' to specify different values for particular
|
options like '--bindir=DIR' to specify different values for particular
|
||||||
kinds of files. Run `configure --help' for a list of the directories
|
kinds of files. Run 'configure --help' for a list of the directories
|
||||||
you can set and what kinds of files go in them. In general, the
|
you can set and what kinds of files go in them. In general, the default
|
||||||
default for these options is expressed in terms of `${prefix}', so that
|
for these options is expressed in terms of '${prefix}', so that
|
||||||
specifying just `--prefix' will affect all of the other directory
|
specifying just '--prefix' will affect all of the other directory
|
||||||
specifications that were not explicitly provided.
|
specifications that were not explicitly provided.
|
||||||
|
|
||||||
The most portable way to affect installation locations is to pass the
|
The most portable way to affect installation locations is to pass the
|
||||||
correct locations to `configure'; however, many packages provide one or
|
correct locations to 'configure'; however, many packages provide one or
|
||||||
both of the following shortcuts of passing variable assignments to the
|
both of the following shortcuts of passing variable assignments to the
|
||||||
`make install' command line to change installation locations without
|
'make install' command line to change installation locations without
|
||||||
having to reconfigure or recompile.
|
having to reconfigure or recompile.
|
||||||
|
|
||||||
The first method involves providing an override variable for each
|
The first method involves providing an override variable for each
|
||||||
affected directory. For example, `make install
|
affected directory. For example, 'make install
|
||||||
prefix=/alternate/directory' will choose an alternate location for all
|
prefix=/alternate/directory' will choose an alternate location for all
|
||||||
directory configuration variables that were expressed in terms of
|
directory configuration variables that were expressed in terms of
|
||||||
`${prefix}'. Any directories that were specified during `configure',
|
'${prefix}'. Any directories that were specified during 'configure',
|
||||||
but not in terms of `${prefix}', must each be overridden at install
|
but not in terms of '${prefix}', must each be overridden at install time
|
||||||
time for the entire installation to be relocated. The approach of
|
for the entire installation to be relocated. The approach of makefile
|
||||||
makefile variable overrides for each directory variable is required by
|
variable overrides for each directory variable is required by the GNU
|
||||||
the GNU Coding Standards, and ideally causes no recompilation.
|
Coding Standards, and ideally causes no recompilation. However, some
|
||||||
However, some platforms have known limitations with the semantics of
|
platforms have known limitations with the semantics of shared libraries
|
||||||
shared libraries that end up requiring recompilation when using this
|
that end up requiring recompilation when using this method, particularly
|
||||||
method, particularly noticeable in packages that use GNU Libtool.
|
noticeable in packages that use GNU Libtool.
|
||||||
|
|
||||||
The second method involves providing the `DESTDIR' variable. For
|
The second method involves providing the 'DESTDIR' variable. For
|
||||||
example, `make install DESTDIR=/alternate/directory' will prepend
|
example, 'make install DESTDIR=/alternate/directory' will prepend
|
||||||
`/alternate/directory' before all installation names. The approach of
|
'/alternate/directory' before all installation names. The approach of
|
||||||
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
'DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||||
does not work on platforms that have drive letters. On the other hand,
|
does not work on platforms that have drive letters. On the other hand,
|
||||||
it does better at avoiding recompilation issues, and works well even
|
it does better at avoiding recompilation issues, and works well even
|
||||||
when some directory options were not specified in terms of `${prefix}'
|
when some directory options were not specified in terms of '${prefix}'
|
||||||
at `configure' time.
|
at 'configure' time.
|
||||||
|
|
||||||
Optional Features
|
Optional Features
|
||||||
=================
|
=================
|
||||||
|
|
||||||
If the package supports it, you can cause programs to be installed
|
If the package supports it, you can cause programs to be installed
|
||||||
with an extra prefix or suffix on their names by giving `configure' the
|
with an extra prefix or suffix on their names by giving 'configure' the
|
||||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
|
||||||
|
|
||||||
Some packages pay attention to `--enable-FEATURE' options to
|
Some packages pay attention to '--enable-FEATURE' options to
|
||||||
`configure', where FEATURE indicates an optional part of the package.
|
'configure', where FEATURE indicates an optional part of the package.
|
||||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
They may also pay attention to '--with-PACKAGE' options, where PACKAGE
|
||||||
is something like `gnu-as' or `x' (for the X Window System). The
|
is something like 'gnu-as' or 'x' (for the X Window System). The
|
||||||
`README' should mention any `--enable-' and `--with-' options that the
|
'README' should mention any '--enable-' and '--with-' options that the
|
||||||
package recognizes.
|
package recognizes.
|
||||||
|
|
||||||
For packages that use the X Window System, `configure' can usually
|
For packages that use the X Window System, 'configure' can usually
|
||||||
find the X include and library files automatically, but if it doesn't,
|
find the X include and library files automatically, but if it doesn't,
|
||||||
you can use the `configure' options `--x-includes=DIR' and
|
you can use the 'configure' options '--x-includes=DIR' and
|
||||||
`--x-libraries=DIR' to specify their locations.
|
'--x-libraries=DIR' to specify their locations.
|
||||||
|
|
||||||
Some packages offer the ability to configure how verbose the
|
Some packages offer the ability to configure how verbose the
|
||||||
execution of `make' will be. For these packages, running `./configure
|
execution of 'make' will be. For these packages, running './configure
|
||||||
--enable-silent-rules' sets the default to minimal output, which can be
|
--enable-silent-rules' sets the default to minimal output, which can be
|
||||||
overridden with `make V=1'; while running `./configure
|
overridden with 'make V=1'; while running './configure
|
||||||
--disable-silent-rules' sets the default to verbose, which can be
|
--disable-silent-rules' sets the default to verbose, which can be
|
||||||
overridden with `make V=0'.
|
overridden with 'make V=0'.
|
||||||
|
|
||||||
Particular systems
|
Particular systems
|
||||||
==================
|
==================
|
||||||
|
|
||||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
|
||||||
CC is not installed, it is recommended to use the following options in
|
is not installed, it is recommended to use the following options in
|
||||||
order to use an ANSI C compiler:
|
order to use an ANSI C compiler:
|
||||||
|
|
||||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||||
|
|
||||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||||
|
|
||||||
HP-UX `make' updates targets which have the same time stamps as
|
HP-UX 'make' updates targets which have the same time stamps as their
|
||||||
their prerequisites, which makes it generally unusable when shipped
|
prerequisites, which makes it generally unusable when shipped generated
|
||||||
generated files such as `configure' are involved. Use GNU `make'
|
files such as 'configure' are involved. Use GNU 'make' instead.
|
||||||
instead.
|
|
||||||
|
|
||||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||||
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
|
||||||
a workaround. If GNU CC is not installed, it is therefore recommended
|
workaround. If GNU CC is not installed, it is therefore recommended to
|
||||||
to try
|
try
|
||||||
|
|
||||||
./configure CC="cc"
|
./configure CC="cc"
|
||||||
|
|
||||||
@@ -242,26 +240,26 @@ and if that doesn't work, try
|
|||||||
|
|
||||||
./configure CC="cc -nodtk"
|
./configure CC="cc -nodtk"
|
||||||
|
|
||||||
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
|
||||||
directory contains several dysfunctional programs; working variants of
|
directory contains several dysfunctional programs; working variants of
|
||||||
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
|
||||||
in your `PATH', put it _after_ `/usr/bin'.
|
in your 'PATH', put it _after_ '/usr/bin'.
|
||||||
|
|
||||||
On Haiku, software installed for all users goes in `/boot/common',
|
On Haiku, software installed for all users goes in '/boot/common',
|
||||||
not `/usr/local'. It is recommended to use the following options:
|
not '/usr/local'. It is recommended to use the following options:
|
||||||
|
|
||||||
./configure --prefix=/boot/common
|
./configure --prefix=/boot/common
|
||||||
|
|
||||||
Specifying the System Type
|
Specifying the System Type
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
There may be some features `configure' cannot figure out
|
There may be some features 'configure' cannot figure out
|
||||||
automatically, but needs to determine by the type of machine the package
|
automatically, but needs to determine by the type of machine the package
|
||||||
will run on. Usually, assuming the package is built to be run on the
|
will run on. Usually, assuming the package is built to be run on the
|
||||||
_same_ architectures, `configure' can figure that out, but if it prints
|
_same_ architectures, 'configure' can figure that out, but if it prints
|
||||||
a message saying it cannot guess the machine type, give it the
|
a message saying it cannot guess the machine type, give it the
|
||||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
'--build=TYPE' option. TYPE can either be a short name for the system
|
||||||
type, such as `sun4', or a canonical name which has the form:
|
type, such as 'sun4', or a canonical name which has the form:
|
||||||
|
|
||||||
CPU-COMPANY-SYSTEM
|
CPU-COMPANY-SYSTEM
|
||||||
|
|
||||||
@@ -270,101 +268,101 @@ where SYSTEM can have one of these forms:
|
|||||||
OS
|
OS
|
||||||
KERNEL-OS
|
KERNEL-OS
|
||||||
|
|
||||||
See the file `config.sub' for the possible values of each field. If
|
See the file 'config.sub' for the possible values of each field. If
|
||||||
`config.sub' isn't included in this package, then this package doesn't
|
'config.sub' isn't included in this package, then this package doesn't
|
||||||
need to know the machine type.
|
need to know the machine type.
|
||||||
|
|
||||||
If you are _building_ compiler tools for cross-compiling, you should
|
If you are _building_ compiler tools for cross-compiling, you should
|
||||||
use the option `--target=TYPE' to select the type of system they will
|
use the option '--target=TYPE' to select the type of system they will
|
||||||
produce code for.
|
produce code for.
|
||||||
|
|
||||||
If you want to _use_ a cross compiler, that generates code for a
|
If you want to _use_ a cross compiler, that generates code for a
|
||||||
platform different from the build platform, you should specify the
|
platform different from the build platform, you should specify the
|
||||||
"host" platform (i.e., that on which the generated programs will
|
"host" platform (i.e., that on which the generated programs will
|
||||||
eventually be run) with `--host=TYPE'.
|
eventually be run) with '--host=TYPE'.
|
||||||
|
|
||||||
Sharing Defaults
|
Sharing Defaults
|
||||||
================
|
================
|
||||||
|
|
||||||
If you want to set default values for `configure' scripts to share,
|
If you want to set default values for 'configure' scripts to share,
|
||||||
you can create a site shell script called `config.site' that gives
|
you can create a site shell script called 'config.site' that gives
|
||||||
default values for variables like `CC', `cache_file', and `prefix'.
|
default values for variables like 'CC', 'cache_file', and 'prefix'.
|
||||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
'configure' looks for 'PREFIX/share/config.site' if it exists, then
|
||||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
'PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||||
`CONFIG_SITE' environment variable to the location of the site script.
|
'CONFIG_SITE' environment variable to the location of the site script.
|
||||||
A warning: not all `configure' scripts look for a site script.
|
A warning: not all 'configure' scripts look for a site script.
|
||||||
|
|
||||||
Defining Variables
|
Defining Variables
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Variables not defined in a site shell script can be set in the
|
Variables not defined in a site shell script can be set in the
|
||||||
environment passed to `configure'. However, some packages may run
|
environment passed to 'configure'. However, some packages may run
|
||||||
configure again during the build, and the customized values of these
|
configure again during the build, and the customized values of these
|
||||||
variables may be lost. In order to avoid this problem, you should set
|
variables may be lost. In order to avoid this problem, you should set
|
||||||
them in the `configure' command line, using `VAR=value'. For example:
|
them in the 'configure' command line, using 'VAR=value'. For example:
|
||||||
|
|
||||||
./configure CC=/usr/local2/bin/gcc
|
./configure CC=/usr/local2/bin/gcc
|
||||||
|
|
||||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
causes the specified 'gcc' to be used as the C compiler (unless it is
|
||||||
overridden in the site shell script).
|
overridden in the site shell script).
|
||||||
|
|
||||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
|
||||||
an Autoconf limitation. Until the limitation is lifted, you can use
|
Autoconf limitation. Until the limitation is lifted, you can use this
|
||||||
this workaround:
|
workaround:
|
||||||
|
|
||||||
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||||
|
|
||||||
`configure' Invocation
|
'configure' Invocation
|
||||||
======================
|
======================
|
||||||
|
|
||||||
`configure' recognizes the following options to control how it
|
'configure' recognizes the following options to control how it
|
||||||
operates.
|
operates.
|
||||||
|
|
||||||
`--help'
|
'--help'
|
||||||
`-h'
|
'-h'
|
||||||
Print a summary of all of the options to `configure', and exit.
|
Print a summary of all of the options to 'configure', and exit.
|
||||||
|
|
||||||
`--help=short'
|
'--help=short'
|
||||||
`--help=recursive'
|
'--help=recursive'
|
||||||
Print a summary of the options unique to this package's
|
Print a summary of the options unique to this package's
|
||||||
`configure', and exit. The `short' variant lists options used
|
'configure', and exit. The 'short' variant lists options used only
|
||||||
only in the top level, while the `recursive' variant lists options
|
in the top level, while the 'recursive' variant lists options also
|
||||||
also present in any nested packages.
|
present in any nested packages.
|
||||||
|
|
||||||
`--version'
|
'--version'
|
||||||
`-V'
|
'-V'
|
||||||
Print the version of Autoconf used to generate the `configure'
|
Print the version of Autoconf used to generate the 'configure'
|
||||||
script, and exit.
|
script, and exit.
|
||||||
|
|
||||||
`--cache-file=FILE'
|
'--cache-file=FILE'
|
||||||
Enable the cache: use and save the results of the tests in FILE,
|
Enable the cache: use and save the results of the tests in FILE,
|
||||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
traditionally 'config.cache'. FILE defaults to '/dev/null' to
|
||||||
disable caching.
|
disable caching.
|
||||||
|
|
||||||
`--config-cache'
|
'--config-cache'
|
||||||
`-C'
|
'-C'
|
||||||
Alias for `--cache-file=config.cache'.
|
Alias for '--cache-file=config.cache'.
|
||||||
|
|
||||||
`--quiet'
|
'--quiet'
|
||||||
`--silent'
|
'--silent'
|
||||||
`-q'
|
'-q'
|
||||||
Do not print messages saying which checks are being made. To
|
Do not print messages saying which checks are being made. To
|
||||||
suppress all normal output, redirect it to `/dev/null' (any error
|
suppress all normal output, redirect it to '/dev/null' (any error
|
||||||
messages will still be shown).
|
messages will still be shown).
|
||||||
|
|
||||||
`--srcdir=DIR'
|
'--srcdir=DIR'
|
||||||
Look for the package's source code in directory DIR. Usually
|
Look for the package's source code in directory DIR. Usually
|
||||||
`configure' can determine that directory automatically.
|
'configure' can determine that directory automatically.
|
||||||
|
|
||||||
`--prefix=DIR'
|
'--prefix=DIR'
|
||||||
Use DIR as the installation prefix. *note Installation Names::
|
Use DIR as the installation prefix. *note Installation Names:: for
|
||||||
for more details, including other options available for fine-tuning
|
more details, including other options available for fine-tuning the
|
||||||
the installation locations.
|
installation locations.
|
||||||
|
|
||||||
`--no-create'
|
'--no-create'
|
||||||
`-n'
|
'-n'
|
||||||
Run the configure checks, but stop before creating any output
|
Run the configure checks, but stop before creating any output
|
||||||
files.
|
files.
|
||||||
|
|
||||||
`configure' also accepts some other, not widely useful, options. Run
|
'configure' also accepts some other, not widely useful, options. Run
|
||||||
`configure --help' for more details.
|
'configure --help' for more details.
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ __Note__: since _LMS_ stores hashed and salted passwords, it cannot handle the _
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
__Note__: this installation process and the default values of the configuration file and the _systemd_ service file have been written for _Debian Stretch_. Therefore, you may have to adapt commands and/or paths in order to fit to your distribution.
|
__Note__: this installation process and the default values of the configuration file and the _systemd_ service file have been written for _Debian Buster_. Therefore, you may have to adapt commands and/or paths in order to fit to your distribution.
|
||||||
|
|
||||||
### Build dependencies
|
### Build dependencies
|
||||||
|
|
||||||
|
__Note__: a C++17 compiler is needed to compile _LMS_
|
||||||
```sh
|
```sh
|
||||||
apt-get install g++ autoconf automake libboost-filesystem-dev libboost-system-dev libavcodec-dev libavutil-dev libavformat-dev libav-tools libmagick++-dev libpstreams-dev libconfig++-dev libpstreams-dev ffmpeg libtag1-dev
|
apt-get install g++ autoconf automake libboost-filesystem-dev libboost-system-dev libavcodec-dev libavutil-dev libavformat-dev libav-tools libmagick++-dev libpstreams-dev libconfig++-dev libpstreams-dev ffmpeg libtag1-dev
|
||||||
```
|
```
|
||||||
|
|||||||
+5
-6
@@ -5,7 +5,6 @@ AC_CONFIG_HEADER(src/config/config.h)
|
|||||||
|
|
||||||
AC_LANG_CPLUSPLUS
|
AC_LANG_CPLUSPLUS
|
||||||
|
|
||||||
# Checks for programs
|
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
|
|
||||||
PKG_CHECK_MODULES(IMAGEMAGICKXX, "ImageMagick++", [ HAVE_IMAGEMAGICKXX=yes ], [ ])
|
PKG_CHECK_MODULES(IMAGEMAGICKXX, "ImageMagick++", [ HAVE_IMAGEMAGICKXX=yes ], [ ])
|
||||||
@@ -20,6 +19,11 @@ AC_CHECK_HEADERS([Wt/WApplication.h pstreams/pstream.h],
|
|||||||
[],
|
[],
|
||||||
[AC_MSG_ERROR([Header not found or unusable !])])
|
[AC_MSG_ERROR([Header not found or unusable !])])
|
||||||
|
|
||||||
|
AC_CHECK_LIB([stdc++fs],
|
||||||
|
[main],
|
||||||
|
,
|
||||||
|
[AC_MSG_ERROR([lib filesystem not found!])])
|
||||||
|
|
||||||
AC_CHECK_LIB([pthread],
|
AC_CHECK_LIB([pthread],
|
||||||
[pthread_rwlock_unlock],
|
[pthread_rwlock_unlock],
|
||||||
,
|
,
|
||||||
@@ -66,11 +70,6 @@ AC_CHECK_LIB([boost_system],
|
|||||||
,
|
,
|
||||||
[AC_MSG_ERROR([libboost_system not found!])])
|
[AC_MSG_ERROR([libboost_system not found!])])
|
||||||
|
|
||||||
AC_CHECK_LIB( [boost_filesystem],
|
|
||||||
[main],
|
|
||||||
,
|
|
||||||
[AC_MSG_ERROR([libboost_filesystem not found!])])
|
|
||||||
|
|
||||||
AC_CHECK_LIB( [tag],
|
AC_CHECK_LIB( [tag],
|
||||||
[main],
|
[main],
|
||||||
,
|
,
|
||||||
|
|||||||
+3
-3
@@ -25,8 +25,8 @@ lms_SOURCES = \
|
|||||||
$(srcdir)/database/Artist.hpp \
|
$(srcdir)/database/Artist.hpp \
|
||||||
$(srcdir)/database/Cluster.cpp \
|
$(srcdir)/database/Cluster.cpp \
|
||||||
$(srcdir)/database/Cluster.hpp \
|
$(srcdir)/database/Cluster.hpp \
|
||||||
$(srcdir)/database/Database.cpp \
|
$(srcdir)/database/Db.cpp \
|
||||||
$(srcdir)/database/Database.hpp \
|
$(srcdir)/database/Db.hpp \
|
||||||
$(srcdir)/database/TrackArtistLink.cpp \
|
$(srcdir)/database/TrackArtistLink.cpp \
|
||||||
$(srcdir)/database/TrackArtistLink.hpp \
|
$(srcdir)/database/TrackArtistLink.hpp \
|
||||||
$(srcdir)/database/TrackFeatures.cpp \
|
$(srcdir)/database/TrackFeatures.cpp \
|
||||||
@@ -147,6 +147,6 @@ lms_SOURCES = \
|
|||||||
$(srcdir)/utils/Utils.cpp \
|
$(srcdir)/utils/Utils.cpp \
|
||||||
$(srcdir)/utils/Utils.hpp
|
$(srcdir)/utils/Utils.hpp
|
||||||
|
|
||||||
lms_CXXFLAGS=-std=c++14 -Wall -I$(srcdir)/ui $(MAGICKXX_CFLAGS) -D_REENTRANT
|
lms_CXXFLAGS=-std=c++17 -I$(srcdir)/ui $(MAGICKXX_CFLAGS) -D_REENTRANT
|
||||||
lms_LDADD=$(MAGICKXX_LIBS)
|
lms_LDADD=$(MAGICKXX_LIBS)
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
namespace API::Subsonic
|
namespace API::Subsonic
|
||||||
{
|
{
|
||||||
|
|
||||||
boost::optional<Id>
|
std::optional<Id>
|
||||||
IdFromString(const std::string& id)
|
IdFromString(const std::string& id)
|
||||||
{
|
{
|
||||||
if (id == "root")
|
if (id == "root")
|
||||||
@@ -35,7 +35,7 @@ IdFromString(const std::string& id)
|
|||||||
|
|
||||||
std::vector<std::string> values {splitString(id, "-")};
|
std::vector<std::string> values {splitString(id, "-")};
|
||||||
if (values.size() != 2)
|
if (values.size() != 2)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
Id res;
|
Id res;
|
||||||
|
|
||||||
@@ -49,11 +49,11 @@ IdFromString(const std::string& id)
|
|||||||
else if (type == "pl")
|
else if (type == "pl")
|
||||||
res.type = Id::Type::Playlist;
|
res.type = Id::Type::Playlist;
|
||||||
else
|
else
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
auto optId {readAs<Database::IdType>(values[1])};
|
auto optId {readAs<Database::IdType>(values[1])};
|
||||||
if (!optId)
|
if (!optId)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
res.value = *optId;
|
res.value = *optId;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ struct Id
|
|||||||
Database::IdType value {};
|
Database::IdType value {};
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::optional<Id> IdFromString(const std::string& id);
|
std::optional<Id> IdFromString(const std::string& id);
|
||||||
std::string IdToString(const Id& id);
|
std::string IdToString(const Id& id);
|
||||||
|
|
||||||
} // namespace API::Subsonic
|
} // namespace API::Subsonic
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "cover/CoverArtGrabber.hpp"
|
#include "cover/CoverArtGrabber.hpp"
|
||||||
#include "database/Artist.hpp"
|
#include "database/Artist.hpp"
|
||||||
#include "database/Cluster.hpp"
|
#include "database/Cluster.hpp"
|
||||||
|
#include "database/Db.hpp"
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/Track.hpp"
|
||||||
#include "database/TrackList.hpp"
|
#include "database/TrackList.hpp"
|
||||||
@@ -53,14 +54,14 @@ static const Av::Encoding transcodeEncoding {Av::Encoding::MP3};
|
|||||||
static const std::string reportedStarredDate {"2000-01-01T00:00:00"};
|
static const std::string reportedStarredDate {"2000-01-01T00:00:00"};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
boost::optional<API::Subsonic::Id>
|
std::optional<API::Subsonic::Id>
|
||||||
readAs(const std::string& str)
|
readAs(const std::string& str)
|
||||||
{
|
{
|
||||||
return API::Subsonic::IdFromString(str);
|
return API::Subsonic::IdFromString(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
boost::optional<bool>
|
std::optional<bool>
|
||||||
readAs(const std::string& str)
|
readAs(const std::string& str)
|
||||||
{
|
{
|
||||||
if (str == "true")
|
if (str == "true")
|
||||||
@@ -82,31 +83,31 @@ struct ClientVersion
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
boost::optional<API::Subsonic::ClientVersion>
|
std::optional<API::Subsonic::ClientVersion>
|
||||||
readAs(const std::string& str)
|
readAs(const std::string& str)
|
||||||
{
|
{
|
||||||
// Expects "X.Y.Z"
|
// Expects "X.Y.Z"
|
||||||
const auto numbers {splitString(str, ".")};
|
const auto numbers {splitString(str, ".")};
|
||||||
if (numbers.size() < 2 || numbers.size() > 3)
|
if (numbers.size() < 2 || numbers.size() > 3)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
API::Subsonic::ClientVersion version;
|
API::Subsonic::ClientVersion version;
|
||||||
|
|
||||||
auto number {readAs<unsigned>(numbers[0])};
|
auto number {readAs<unsigned>(numbers[0])};
|
||||||
if (!number)
|
if (!number)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
version.major = *number;
|
version.major = *number;
|
||||||
|
|
||||||
number = {readAs<unsigned>(numbers[1])};
|
number = {readAs<unsigned>(numbers[1])};
|
||||||
if (!number)
|
if (!number)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
version.minor = *number;
|
version.minor = *number;
|
||||||
|
|
||||||
if (numbers.size() == 3)
|
if (numbers.size() == 3)
|
||||||
{
|
{
|
||||||
number = {readAs<unsigned>(numbers[2])};
|
number = {readAs<unsigned>(numbers[2])};
|
||||||
if (!number)
|
if (!number)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
version.patch = *number;
|
version.patch = *number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,12 +133,12 @@ struct RequestContext
|
|||||||
std::string userName;
|
std::string userName;
|
||||||
};
|
};
|
||||||
|
|
||||||
using SessionMap = std::map<Database::Database*, std::unique_ptr<Session>>;
|
using SessionMap = std::map<Db*, std::unique_ptr<Session>>;
|
||||||
static std::map<std::thread::id, SessionMap> dbSessions;
|
static std::map<std::thread::id, SessionMap> dbSessions;
|
||||||
|
|
||||||
static
|
static
|
||||||
Session&
|
Session&
|
||||||
getOrCreateDbSession(Database::Database& db)
|
getOrCreateDbSession(Db& db)
|
||||||
{
|
{
|
||||||
static std::mutex mutex;
|
static std::mutex mutex;
|
||||||
|
|
||||||
@@ -152,12 +153,12 @@ getOrCreateDbSession(Database::Database& db)
|
|||||||
if (it != std::end(*sessionMap))
|
if (it != std::end(*sessionMap))
|
||||||
return *it->second;
|
return *it->second;
|
||||||
|
|
||||||
auto res {sessionMap->emplace(&db, db.createSession())};
|
auto res { sessionMap->try_emplace(&db, db.createSession())};
|
||||||
assert(res.second);
|
assert(res.second);
|
||||||
|
|
||||||
LMS_LOG(API_SUBSONIC, DEBUG) << "Created db session";
|
LMS_LOG(API_SUBSONIC, DEBUG) << "Created db session";
|
||||||
|
|
||||||
return *(res.first->second);
|
return *res.first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
@@ -209,7 +210,7 @@ getMandatoryMultiParametersAs(const Wt::Http::ParameterMap& parameterMap, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
boost::optional<T>
|
std::optional<T>
|
||||||
getParameterAs(const Wt::Http::ParameterMap& parameterMap, const std::string& param)
|
getParameterAs(const Wt::Http::ParameterMap& parameterMap, const std::string& param)
|
||||||
{
|
{
|
||||||
std::vector<T> params {getMultiParametersAs<T>(parameterMap, param)};
|
std::vector<T> params {getMultiParametersAs<T>(parameterMap, param)};
|
||||||
@@ -277,7 +278,7 @@ struct MediaRetrievalResult
|
|||||||
Wt::cpp17::any continuationData;
|
Wt::cpp17::any continuationData;
|
||||||
};
|
};
|
||||||
|
|
||||||
SubsonicResource::SubsonicResource(Database::Database& db)
|
SubsonicResource::SubsonicResource(Db& db)
|
||||||
: _db {db}
|
: _db {db}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -571,7 +572,7 @@ userToResponseNode(const User::pointer& user)
|
|||||||
|
|
||||||
static
|
static
|
||||||
Response
|
Response
|
||||||
handlePingRequest(RequestContext& context)
|
handlePingRequest(RequestContext&)
|
||||||
{
|
{
|
||||||
return Response::createOkResponse();
|
return Response::createOkResponse();
|
||||||
}
|
}
|
||||||
@@ -731,12 +732,12 @@ handleDeleteUserRequest(RequestContext& context)
|
|||||||
|
|
||||||
static
|
static
|
||||||
Response
|
Response
|
||||||
handleGetLicenseRequest(RequestContext& context)
|
handleGetLicenseRequest(RequestContext&)
|
||||||
{
|
{
|
||||||
Response response {Response::createOkResponse()};
|
Response response {Response::createOkResponse()};
|
||||||
|
|
||||||
Response::Node& licenseNode {response.createNode("license")};
|
Response::Node& licenseNode {response.createNode("license")};
|
||||||
licenseNode.setAttribute("licenseExpires", "2019-09-03T14:46:43");
|
licenseNode.setAttribute("licenseExpires", "2025-09-03T14:46:43");
|
||||||
licenseNode.setAttribute("email", "foo@bar.com");
|
licenseNode.setAttribute("email", "foo@bar.com");
|
||||||
licenseNode.setAttribute("valid", "true");
|
licenseNode.setAttribute("valid", "true");
|
||||||
|
|
||||||
@@ -748,7 +749,7 @@ Response
|
|||||||
handleGetRandomSongsRequest(RequestContext& context)
|
handleGetRandomSongsRequest(RequestContext& context)
|
||||||
{
|
{
|
||||||
// Optional params
|
// Optional params
|
||||||
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").get_value_or(50)};
|
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").value_or(50)};
|
||||||
size = std::min(size, std::size_t {500});
|
size = std::min(size, std::size_t {500});
|
||||||
|
|
||||||
auto transaction {context.dbSession.createSharedTransaction()};
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
@@ -776,8 +777,8 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3)
|
|||||||
std::string type {getMandatoryParameterAs<std::string>(context.parameters, "type")};
|
std::string type {getMandatoryParameterAs<std::string>(context.parameters, "type")};
|
||||||
|
|
||||||
// Optional params
|
// Optional params
|
||||||
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").get_value_or(10)};
|
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").value_or(10)};
|
||||||
std::size_t offset {getParameterAs<std::size_t>(context.parameters, "offset").get_value_or(0)};
|
std::size_t offset {getParameterAs<std::size_t>(context.parameters, "offset").value_or(0)};
|
||||||
|
|
||||||
std::vector<Release::pointer> releases;
|
std::vector<Release::pointer> releases;
|
||||||
|
|
||||||
@@ -932,7 +933,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3)
|
|||||||
throw BadParameterGenericError {"id"};
|
throw BadParameterGenericError {"id"};
|
||||||
|
|
||||||
// Optional params
|
// Optional params
|
||||||
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").get_value_or(20)};
|
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").value_or(20)};
|
||||||
|
|
||||||
Response response {Response::createOkResponse()};
|
Response response {Response::createOkResponse()};
|
||||||
Response::Node& artistInfoNode {response.createNode(id3 ? "artistInfo2" : "artistInfo")};
|
Response::Node& artistInfoNode {response.createNode(id3 ? "artistInfo2" : "artistInfo")};
|
||||||
@@ -1075,7 +1076,7 @@ handleGetMusicDirectoryRequest(RequestContext& context)
|
|||||||
|
|
||||||
static
|
static
|
||||||
Response
|
Response
|
||||||
handleGetMusicFoldersRequest(RequestContext& context)
|
handleGetMusicFoldersRequest(RequestContext&)
|
||||||
{
|
{
|
||||||
Response response {Response::createOkResponse()};
|
Response response {Response::createOkResponse()};
|
||||||
Response::Node& musicFoldersNode {response.createNode("musicFolders")};
|
Response::Node& musicFoldersNode {response.createNode("musicFolders")};
|
||||||
@@ -1142,7 +1143,7 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
|
|||||||
throw BadParameterGenericError {"id"};
|
throw BadParameterGenericError {"id"};
|
||||||
|
|
||||||
// Optional params
|
// Optional params
|
||||||
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").get_value_or(50)};
|
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").value_or(50)};
|
||||||
|
|
||||||
auto similarArtistsId {getService<Similarity::Searcher>()->getSimilarArtists(context.dbSession, id.value, 5)};
|
auto similarArtistsId {getService<Similarity::Searcher>()->getSimilarArtists(context.dbSession, id.value, 5)};
|
||||||
|
|
||||||
@@ -1248,7 +1249,7 @@ handleGetStarred2Request(RequestContext& context)
|
|||||||
|
|
||||||
static
|
static
|
||||||
Response::Node
|
Response::Node
|
||||||
tracklistToResponseNode(const TrackList::pointer& tracklist, Session& dbSession)
|
tracklistToResponseNode(const TrackList::pointer& tracklist, Session&)
|
||||||
{
|
{
|
||||||
Response::Node playlistNode;
|
Response::Node playlistNode;
|
||||||
|
|
||||||
@@ -1322,10 +1323,10 @@ handleGetSongsByGenreRequest(RequestContext& context)
|
|||||||
std::string genre {getMandatoryParameterAs<std::string>(context.parameters, "genre")};
|
std::string genre {getMandatoryParameterAs<std::string>(context.parameters, "genre")};
|
||||||
|
|
||||||
// Optional params
|
// Optional params
|
||||||
std::size_t size {getParameterAs<std::size_t>(context.parameters, "count").get_value_or(10)};
|
std::size_t size {getParameterAs<std::size_t>(context.parameters, "count").value_or(10)};
|
||||||
size = std::min(size, std::size_t {500});
|
size = std::min(size, std::size_t {500});
|
||||||
|
|
||||||
std::size_t offset {getParameterAs<std::size_t>(context.parameters, "offset").get_value_or(0)};
|
std::size_t offset {getParameterAs<std::size_t>(context.parameters, "offset").value_or(0)};
|
||||||
|
|
||||||
auto transaction {context.dbSession.createSharedTransaction()};
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
@@ -1398,12 +1399,12 @@ handleSearchRequestCommon(RequestContext& context, bool id3)
|
|||||||
std::vector<std::string> keywords {splitString(query, " ")};
|
std::vector<std::string> keywords {splitString(query, " ")};
|
||||||
|
|
||||||
// Optional params
|
// Optional params
|
||||||
std::size_t artistCount {getParameterAs<std::size_t>(context.parameters, "artistCount").get_value_or(20)};
|
std::size_t artistCount {getParameterAs<std::size_t>(context.parameters, "artistCount").value_or(20)};
|
||||||
std::size_t artistOffset {getParameterAs<std::size_t>(context.parameters, "artistOffset").get_value_or(0)};
|
std::size_t artistOffset {getParameterAs<std::size_t>(context.parameters, "artistOffset").value_or(0)};
|
||||||
std::size_t albumCount {getParameterAs<std::size_t>(context.parameters, "albumCount").get_value_or(20)};
|
std::size_t albumCount {getParameterAs<std::size_t>(context.parameters, "albumCount").value_or(20)};
|
||||||
std::size_t albumOffset {getParameterAs<std::size_t>(context.parameters, "albumOffset").get_value_or(0)};
|
std::size_t albumOffset {getParameterAs<std::size_t>(context.parameters, "albumOffset").value_or(0)};
|
||||||
std::size_t songCount {getParameterAs<std::size_t>(context.parameters, "songCount").get_value_or(20)};
|
std::size_t songCount {getParameterAs<std::size_t>(context.parameters, "songCount").value_or(20)};
|
||||||
std::size_t songOffset {getParameterAs<std::size_t>(context.parameters, "songOffset").get_value_or(0)};
|
std::size_t songOffset {getParameterAs<std::size_t>(context.parameters, "songOffset").value_or(0)};
|
||||||
|
|
||||||
auto transaction {context.dbSession.createSharedTransaction()};
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
@@ -1585,8 +1586,8 @@ Response
|
|||||||
handleUpdateUserRequest(RequestContext& context)
|
handleUpdateUserRequest(RequestContext& context)
|
||||||
{
|
{
|
||||||
std::string username {getMandatoryParameterAs<std::string>(context.parameters, "username")};
|
std::string username {getMandatoryParameterAs<std::string>(context.parameters, "username")};
|
||||||
boost::optional<std::string> password {getParameterAs<std::string>(context.parameters, "password")};
|
std::optional<std::string> password {getParameterAs<std::string>(context.parameters, "password")};
|
||||||
boost::optional<Bitrate> maxBitRate {getParameterAs<Bitrate>(context.parameters, "maxBitRate")};
|
std::optional<Bitrate> maxBitRate {getParameterAs<Bitrate>(context.parameters, "maxBitRate")};
|
||||||
|
|
||||||
User::PasswordHash hash;
|
User::PasswordHash hash;
|
||||||
if (password)
|
if (password)
|
||||||
@@ -1693,9 +1694,9 @@ createTranscoder(RequestContext& context)
|
|||||||
Id id {getMandatoryParameterAs<Id>(context.parameters, "id")};
|
Id id {getMandatoryParameterAs<Id>(context.parameters, "id")};
|
||||||
|
|
||||||
// Optional params
|
// Optional params
|
||||||
boost::optional<std::size_t> maxBitRate {getParameterAs<std::size_t>(context.parameters, "maxBitRate")};
|
std::optional<std::size_t> maxBitRate {getParameterAs<std::size_t>(context.parameters, "maxBitRate")};
|
||||||
|
|
||||||
boost::filesystem::path trackPath;
|
std::filesystem::path trackPath;
|
||||||
{
|
{
|
||||||
auto transaction {context.dbSession.createSharedTransaction()};
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
@@ -1771,7 +1772,7 @@ handleGetCoverArt(RequestContext& context, Wt::Http::ResponseContinuation*)
|
|||||||
// Mandatory params
|
// Mandatory params
|
||||||
Id id {getMandatoryParameterAs<Id>(context.parameters, "id")};
|
Id id {getMandatoryParameterAs<Id>(context.parameters, "id")};
|
||||||
|
|
||||||
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").get_value_or(256)};
|
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").value_or(256)};
|
||||||
size = clamp(size, std::size_t {32}, std::size_t {1024});
|
size = clamp(size, std::size_t {32}, std::size_t {1024});
|
||||||
|
|
||||||
MediaRetrievalResult res;
|
MediaRetrievalResult res;
|
||||||
@@ -1861,7 +1862,7 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
|
|||||||
const Wt::Http::ParameterMap& parameters {request.getParameterMap()};
|
const Wt::Http::ParameterMap& parameters {request.getParameterMap()};
|
||||||
|
|
||||||
// Optional parameters
|
// Optional parameters
|
||||||
ResponseFormat format {getParameterAs<std::string>(parameters, "f").get_value_or("xml") == "json" ? ResponseFormat::json : ResponseFormat::xml};
|
const ResponseFormat format {getParameterAs<std::string>(parameters, "f").value_or("xml") == "json" ? ResponseFormat::json : ResponseFormat::xml};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,12 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WResource.h>
|
#include <Wt/WResource.h>
|
||||||
#include <Wt/Http/Response.h>
|
#include <Wt/Http/Response.h>
|
||||||
|
|
||||||
#include "database/Database.hpp"
|
namespace Database
|
||||||
|
{
|
||||||
|
class Db;
|
||||||
|
}
|
||||||
|
|
||||||
namespace API::Subsonic
|
namespace API::Subsonic
|
||||||
{
|
{
|
||||||
@@ -31,7 +32,7 @@ namespace API::Subsonic
|
|||||||
class SubsonicResource final : public Wt::WResource
|
class SubsonicResource final : public Wt::WResource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SubsonicResource(Database::Database& db);
|
SubsonicResource(Database::Db& db);
|
||||||
~SubsonicResource();
|
~SubsonicResource();
|
||||||
|
|
||||||
static std::string getPath() { return "/rest/"; }
|
static std::string getPath() { return "/rest/"; }
|
||||||
@@ -39,7 +40,7 @@ class SubsonicResource final : public Wt::WResource
|
|||||||
|
|
||||||
void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) override;
|
void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) override;
|
||||||
|
|
||||||
Database::Database& _db;
|
Database::Db& _db;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ AuthTokenService::createAuthToken(Database::Session& session, Database::IdType u
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
boost::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo>
|
std::optional<AuthTokenService::AuthTokenProcessResult::AuthTokenInfo>
|
||||||
processAuthToken(Database::Session& session, const std::string& secret)
|
processAuthToken(Database::Session& session, const std::string& secret)
|
||||||
{
|
{
|
||||||
const std::string secretHash {sha1Function.compute(secret, {})};
|
const std::string secretHash {sha1Function.compute(secret, {})};
|
||||||
@@ -71,12 +71,12 @@ processAuthToken(Database::Session& session, const std::string& secret)
|
|||||||
|
|
||||||
Database::AuthToken::pointer authToken {Database::AuthToken::getByValue(session, secretHash)};
|
Database::AuthToken::pointer authToken {Database::AuthToken::getByValue(session, secretHash)};
|
||||||
if (!authToken)
|
if (!authToken)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
if (authToken->getExpiry() < Wt::WDateTime::currentDateTime())
|
if (authToken->getExpiry() < Wt::WDateTime::currentDateTime())
|
||||||
{
|
{
|
||||||
authToken.remove();
|
authToken.remove();
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(UI, DEBUG) << "Found auth token for user '" << authToken->getUser()->getLoginName() << "'!";
|
LMS_LOG(UI, DEBUG) << "Found auth token for user '" << authToken->getUser()->getLoginName() << "'!";
|
||||||
|
|||||||
@@ -21,9 +21,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <boost/asio/ip/address.hpp>
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
|
||||||
#include "LoginThrottler.hpp"
|
#include "LoginThrottler.hpp"
|
||||||
@@ -69,8 +69,8 @@ namespace Auth {
|
|||||||
Wt::WDateTime expiry;
|
Wt::WDateTime expiry;
|
||||||
};
|
};
|
||||||
|
|
||||||
State state;
|
State state {State::NotFound};
|
||||||
boost::optional<AuthTokenInfo> authTokenInfo;
|
std::optional<AuthTokenInfo> authTokenInfo {};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Removed if found
|
// Removed if found
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ LoginThrottler::onGoodClientAttempt(const boost::asio::ip::address& address)
|
|||||||
{
|
{
|
||||||
const boost::asio::ip::address clientAddress {getAddressToThrottle(address)};
|
const boost::asio::ip::address clientAddress {getAddressToThrottle(address)};
|
||||||
|
|
||||||
_attemptsInfo.erase(address);
|
_attemptsInfo.erase(clientAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -91,7 +91,7 @@ LoginThrottler::isClientThrottled(const boost::asio::ip::address& address) const
|
|||||||
{
|
{
|
||||||
const boost::asio::ip::address clientAddress {getAddressToThrottle(address)};
|
const boost::asio::ip::address clientAddress {getAddressToThrottle(address)};
|
||||||
|
|
||||||
auto it {_attemptsInfo.find(address)};
|
auto it {_attemptsInfo.find(clientAddress)};
|
||||||
if (it == _attemptsInfo.end())
|
if (it == _attemptsInfo.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <boost/asio/ip/address.hpp>
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
|
||||||
#include "LoginThrottler.hpp"
|
#include "LoginThrottler.hpp"
|
||||||
@@ -81,7 +80,7 @@ namespace Auth {
|
|||||||
};
|
};
|
||||||
|
|
||||||
State state;
|
State state;
|
||||||
boost::optional<AuthTokenInfo> authTokenInfo;
|
std::optional<AuthTokenInfo> authTokenInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Removed if found
|
// Removed if found
|
||||||
|
|||||||
+4
-12
@@ -43,16 +43,8 @@ MediaFileException::MediaFileException(int avError)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvInit()
|
|
||||||
{
|
|
||||||
/* register all the codecs */
|
|
||||||
avcodec_register_all();
|
|
||||||
av_register_all();
|
|
||||||
LMS_LOG(AV, INFO) << "avcodec version = " << avcodec_version();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
MediaFile::MediaFile(const std::filesystem::path& p)
|
||||||
MediaFile::MediaFile(const boost::filesystem::path& p)
|
|
||||||
: _p {p}
|
: _p {p}
|
||||||
{
|
{
|
||||||
int error = avformat_open_input(&_context, _p.string().c_str(), nullptr, nullptr);
|
int error = avformat_open_input(&_context, _p.string().c_str(), nullptr, nullptr);
|
||||||
@@ -155,7 +147,7 @@ MediaFile::getStreamInfo() const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
MediaFile::getBestStream() const
|
MediaFile::getBestStream() const
|
||||||
{
|
{
|
||||||
int res = av_find_best_stream(_context,
|
int res = av_find_best_stream(_context,
|
||||||
@@ -166,7 +158,7 @@ MediaFile::getBestStream() const
|
|||||||
0);
|
0);
|
||||||
|
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -238,7 +230,7 @@ MediaFile::getAttachedPictures(std::size_t nbMaxPictures) const
|
|||||||
return pictures;
|
return pictures;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<MediaFileFormat> guessMediaFileFormat(const boost::filesystem::path& file)
|
std::optional<MediaFileFormat> guessMediaFileFormat(const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
AVOutputFormat* format {av_guess_format(NULL,file.string().c_str(),NULL)};
|
AVOutputFormat* format {av_guess_format(NULL,file.string().c_str(),NULL)};
|
||||||
if (!format || !format->name)
|
if (!format || !format->name)
|
||||||
|
|||||||
+10
-12
@@ -29,14 +29,12 @@ extern "C"
|
|||||||
#include <libavutil/error.h>
|
#include <libavutil/error.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <map>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
#include <boost/optional.hpp>
|
#include <map>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "AvTypes.hpp"
|
#include "AvTypes.hpp"
|
||||||
|
|
||||||
@@ -66,7 +64,7 @@ class MediaFileException : public AvException
|
|||||||
class MediaFile
|
class MediaFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MediaFile(const boost::filesystem::path& p);
|
MediaFile(const std::filesystem::path& p);
|
||||||
~MediaFile();
|
~MediaFile();
|
||||||
|
|
||||||
MediaFile(const MediaFile&) = delete;
|
MediaFile(const MediaFile&) = delete;
|
||||||
@@ -76,19 +74,19 @@ class MediaFile
|
|||||||
|
|
||||||
std::string getFormatName() const;
|
std::string getFormatName() const;
|
||||||
|
|
||||||
const boost::filesystem::path& getPath() const {return _p;};
|
const std::filesystem::path& getPath() const {return _p;};
|
||||||
|
|
||||||
std::chrono::milliseconds getDuration() const;
|
std::chrono::milliseconds getDuration() const;
|
||||||
std::map<std::string, std::string> getMetaData(void);
|
std::map<std::string, std::string> getMetaData(void);
|
||||||
|
|
||||||
std::vector<StreamInfo> getStreamInfo() const;
|
std::vector<StreamInfo> getStreamInfo() const;
|
||||||
boost::optional<std::size_t> getBestStream() const; // none if failure/unknown
|
std::optional<std::size_t> getBestStream() const; // none if failure/unknown
|
||||||
bool hasAttachedPictures(void) const;
|
bool hasAttachedPictures(void) const;
|
||||||
std::vector<Picture> getAttachedPictures(std::size_t nbMaxPictures) const;
|
std::vector<Picture> getAttachedPictures(std::size_t nbMaxPictures) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
boost::filesystem::path _p;
|
std::filesystem::path _p;
|
||||||
AVFormatContext* _context {};
|
AVFormatContext* _context {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -99,7 +97,7 @@ struct MediaFileFormat
|
|||||||
std::string format;
|
std::string format;
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::optional<MediaFileFormat> guessMediaFileFormat(const boost::filesystem::path& file);
|
std::optional<MediaFileFormat> guessMediaFileFormat(const std::filesystem::path& file);
|
||||||
|
|
||||||
} // namespace Av
|
} // namespace Av
|
||||||
|
|
||||||
|
|||||||
+47
-47
@@ -37,16 +37,15 @@ static const std::vector<std::string> execNames =
|
|||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::mutex transcoderMutex;
|
static std::filesystem::path avConvPath = std::filesystem::path();
|
||||||
static boost::filesystem::path avConvPath = boost::filesystem::path();
|
|
||||||
static std::atomic<size_t> globalId = {0};
|
static std::atomic<size_t> globalId = {0};
|
||||||
|
|
||||||
void
|
void
|
||||||
Transcoder::init()
|
Transcoder::init()
|
||||||
{
|
{
|
||||||
for (std::string execName : execNames)
|
for (const std::string& execName : execNames)
|
||||||
{
|
{
|
||||||
boost::filesystem::path p = searchExecPath(execName);
|
const std::filesystem::path p {searchExecPath(execName)};
|
||||||
if (!p.empty())
|
if (!p.empty())
|
||||||
{
|
{
|
||||||
avConvPath = p;
|
avConvPath = p;
|
||||||
@@ -60,11 +59,10 @@ Transcoder::init()
|
|||||||
throw AvException("Cannot find any transcoder binary!");
|
throw AvException("Cannot find any transcoder binary!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Transcoder::Transcoder(boost::filesystem::path filePath, TranscodeParameters parameters)
|
Transcoder::Transcoder(const std::filesystem::path& filePath, const TranscodeParameters& parameters)
|
||||||
: _filePath(filePath),
|
: _filePath {filePath},
|
||||||
_parameters(parameters),
|
_parameters {parameters},
|
||||||
_isComplete(false),
|
_id {globalId++}
|
||||||
_id(globalId++)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -72,84 +70,84 @@ Transcoder::Transcoder(boost::filesystem::path filePath, TranscodeParameters par
|
|||||||
bool
|
bool
|
||||||
Transcoder::start()
|
Transcoder::start()
|
||||||
{
|
{
|
||||||
if (!boost::filesystem::exists(_filePath))
|
if (!std::filesystem::exists(_filePath))
|
||||||
return false;
|
return false;
|
||||||
else if (!boost::filesystem::is_regular( _filePath) )
|
else if (!std::filesystem::is_regular_file( _filePath) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
LMS_LOG_TRANSCODE(INFO) << "Transcoding file '" << _filePath.string() << "'";
|
LMS_LOG_TRANSCODE(INFO) << "Transcoding file '" << _filePath.string() << "'";
|
||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
|
||||||
args.push_back(avConvPath.string());
|
args.emplace_back(avConvPath.string());
|
||||||
|
|
||||||
// Make sure we do not produce anything in the stderr output
|
// Make sure we do not produce anything in the stderr output
|
||||||
// in order not to block the whole forked process
|
// in order not to block the whole forked process
|
||||||
args.push_back("-loglevel");
|
args.emplace_back("-loglevel");
|
||||||
args.push_back("quiet");
|
args.emplace_back("quiet");
|
||||||
args.push_back("-nostdin");
|
args.emplace_back("-nostdin");
|
||||||
|
|
||||||
// input Offset
|
// input Offset
|
||||||
if (_parameters.offset)
|
if (_parameters.offset)
|
||||||
{
|
{
|
||||||
args.push_back("-ss");
|
args.emplace_back("-ss");
|
||||||
args.push_back(std::to_string((*_parameters.offset).count()));
|
args.emplace_back(std::to_string((*_parameters.offset).count()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input file
|
// Input file
|
||||||
args.push_back("-i");
|
args.emplace_back("-i");
|
||||||
args.push_back(_filePath.string());
|
args.emplace_back(_filePath.string());
|
||||||
|
|
||||||
// Stream mapping, if set
|
// Stream mapping, if set
|
||||||
if (_parameters.stream)
|
if (_parameters.stream)
|
||||||
{
|
{
|
||||||
args.push_back("-map");
|
args.emplace_back("-map");
|
||||||
args.push_back("0:" + std::to_string(*_parameters.stream));
|
args.emplace_back("0:" + std::to_string(*_parameters.stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_parameters.stripMetadata)
|
if (_parameters.stripMetadata)
|
||||||
{
|
{
|
||||||
// Strip metadata
|
// Strip metadata
|
||||||
args.push_back("-map_metadata");
|
args.emplace_back("-map_metadata");
|
||||||
args.push_back("-1");
|
args.emplace_back("-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip video flows (including covers)
|
// Skip video flows (including covers)
|
||||||
args.push_back("-vn");
|
args.emplace_back("-vn");
|
||||||
|
|
||||||
// Codecs and formats
|
// Codecs and formats
|
||||||
if (_parameters.encoding)
|
if (_parameters.encoding)
|
||||||
{
|
{
|
||||||
// Output bitrates
|
// Output bitrates
|
||||||
args.push_back("-b:a");
|
args.emplace_back("-b:a");
|
||||||
args.push_back(std::to_string(_parameters.bitrate));
|
args.emplace_back(std::to_string(_parameters.bitrate));
|
||||||
|
|
||||||
switch (*_parameters.encoding)
|
switch (*_parameters.encoding)
|
||||||
{
|
{
|
||||||
case Encoding::MP3:
|
case Encoding::MP3:
|
||||||
args.push_back("-f");
|
args.emplace_back("-f");
|
||||||
args.push_back("mp3");
|
args.emplace_back("mp3");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Encoding::OGG_OPUS:
|
case Encoding::OGG_OPUS:
|
||||||
args.push_back("-acodec");
|
args.emplace_back("-acodec");
|
||||||
args.push_back("libopus");
|
args.emplace_back("libopus");
|
||||||
args.push_back("-f");
|
args.emplace_back("-f");
|
||||||
args.push_back("ogg");
|
args.emplace_back("ogg");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Encoding::OGG_VORBIS:
|
case Encoding::OGG_VORBIS:
|
||||||
args.push_back("-acodec");
|
args.emplace_back("-acodec");
|
||||||
args.push_back("libvorbis");
|
args.emplace_back("libvorbis");
|
||||||
args.push_back("-f");
|
args.emplace_back("-f");
|
||||||
args.push_back("ogg");
|
args.emplace_back("ogg");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Encoding::WEBM_VORBIS:
|
case Encoding::WEBM_VORBIS:
|
||||||
args.push_back("-acodec");
|
args.emplace_back("-acodec");
|
||||||
args.push_back("libvorbis");
|
args.emplace_back("libvorbis");
|
||||||
args.push_back("-f");
|
args.emplace_back("-f");
|
||||||
args.push_back("webm");
|
args.emplace_back("webm");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
@@ -169,23 +167,25 @@ Transcoder::start()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.push_back("-acodec");
|
args.emplace_back("-acodec");
|
||||||
args.push_back("copy");
|
args.emplace_back("copy");
|
||||||
args.push_back("-f");
|
args.emplace_back("-f");
|
||||||
args.push_back(mediaFileFormat->format);
|
args.emplace_back(mediaFileFormat->format);
|
||||||
|
|
||||||
_outputMimeType = mediaFileFormat->mimeType;
|
_outputMimeType = mediaFileFormat->mimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.push_back("pipe:1");
|
args.emplace_back("pipe:1");
|
||||||
|
|
||||||
LMS_LOG_TRANSCODE(DEBUG) << "Dumping args (" << args.size() << ")";
|
LMS_LOG_TRANSCODE(DEBUG) << "Dumping args (" << args.size() << ")";
|
||||||
for (std::string arg : args)
|
for (const std::string& arg : args)
|
||||||
LMS_LOG_TRANSCODE(DEBUG) << "Arg = '" << arg << "'";
|
LMS_LOG_TRANSCODE(DEBUG) << "Arg = '" << arg << "'";
|
||||||
|
|
||||||
// make sure only one thread is executing this part of code
|
// make sure only one thread is executing this part of code
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(transcoderMutex);
|
static std::mutex transcoderMutex;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock {transcoderMutex};
|
||||||
|
|
||||||
_child = std::make_shared<redi::ipstream>();
|
_child = std::make_shared<redi::ipstream>();
|
||||||
|
|
||||||
|
|||||||
+13
-16
@@ -20,12 +20,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <pstreams/pstream.h>
|
#include <pstreams/pstream.h>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include "AvTypes.hpp"
|
#include "AvTypes.hpp"
|
||||||
|
|
||||||
namespace Av {
|
namespace Av {
|
||||||
@@ -34,10 +33,10 @@ namespace Av {
|
|||||||
|
|
||||||
struct TranscodeParameters
|
struct TranscodeParameters
|
||||||
{
|
{
|
||||||
boost::optional<Encoding> encoding; // If not set, no transcoding is performed
|
std::optional<Encoding> encoding; // If not set, no transcoding is performed
|
||||||
std::size_t bitrate {128000};
|
std::size_t bitrate {128000};
|
||||||
boost::optional<std::size_t> stream; // Id of the stream to be transcoded (auto detect by default)
|
std::optional<std::size_t> stream; // Id of the stream to be transcoded (auto detect by default)
|
||||||
boost::optional<std::chrono::seconds> offset {};
|
std::optional<std::chrono::seconds> offset {};
|
||||||
bool stripMetadata {true};
|
bool stripMetadata {true};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,12 +45,13 @@ class Transcoder
|
|||||||
public:
|
public:
|
||||||
static void init();
|
static void init();
|
||||||
|
|
||||||
Transcoder(boost::filesystem::path file, TranscodeParameters parameters);
|
Transcoder(const std::filesystem::path& file, const TranscodeParameters& parameters);
|
||||||
~Transcoder();
|
~Transcoder();
|
||||||
|
|
||||||
// non copyable
|
|
||||||
Transcoder(const Transcoder&) = delete;
|
Transcoder(const Transcoder&) = delete;
|
||||||
Transcoder& operator=(const Transcoder&) = delete;
|
Transcoder& operator=(const Transcoder&) = delete;
|
||||||
|
Transcoder(Transcoder&&) = delete;
|
||||||
|
Transcoder& operator=(Transcoder&&) = delete;
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
const std::string& getOutputMimeType() const { return _outputMimeType; }
|
const std::string& getOutputMimeType() const { return _outputMimeType; }
|
||||||
@@ -60,18 +60,15 @@ class Transcoder
|
|||||||
|
|
||||||
const TranscodeParameters& getParameters() const { return _parameters; }
|
const TranscodeParameters& getParameters() const { return _parameters; }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Transcoder();
|
const std::filesystem::path _filePath;
|
||||||
|
const TranscodeParameters _parameters;
|
||||||
boost::filesystem::path _filePath;
|
|
||||||
TranscodeParameters _parameters;
|
|
||||||
|
|
||||||
std::shared_ptr<redi::ipstream> _child;
|
std::shared_ptr<redi::ipstream> _child;
|
||||||
|
|
||||||
bool _isComplete = false;
|
bool _isComplete {};
|
||||||
std::size_t _total = 0;
|
std::size_t _total {};
|
||||||
std::size_t _id;
|
const std::size_t _id {};
|
||||||
std::string _outputMimeType;
|
std::string _outputMimeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#include "utils/Exception.hpp"
|
#include "utils/Exception.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
#include "CoverArtGrabber.hpp"
|
#include "CoverArtGrabber.hpp"
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#include "av/AvInfo.hpp"
|
#include "av/AvInfo.hpp"
|
||||||
|
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
@@ -32,9 +30,9 @@
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isFileSupported(const boost::filesystem::path& file, const std::vector<boost::filesystem::path> extensions)
|
isFileSupported(const std::filesystem::path& file, const std::vector<std::filesystem::path> extensions)
|
||||||
{
|
{
|
||||||
boost::filesystem::path fileExtension = file.extension();
|
std::filesystem::path fileExtension = file.extension();
|
||||||
|
|
||||||
for (auto extension : extensions)
|
for (auto extension : extensions)
|
||||||
{
|
{
|
||||||
@@ -54,7 +52,7 @@ Grabber::Grabber()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Grabber::setDefaultCover(boost::filesystem::path p)
|
Grabber::setDefaultCover(const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
if (!_defaultCover.load(p))
|
if (!_defaultCover.load(p))
|
||||||
throw LmsException("Cannot read default cover file '" + p.string() + "'");
|
throw LmsException("Cannot read default cover file '" + p.string() + "'");
|
||||||
@@ -84,7 +82,7 @@ Grabber::getDefaultCover(std::size_t size)
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boost::optional<Image::Image>
|
static std::optional<Image::Image>
|
||||||
getFromAvMediaFile(const Av::MediaFile& input)
|
getFromAvMediaFile(const Av::MediaFile& input)
|
||||||
{
|
{
|
||||||
std::vector<Image::Image> res;
|
std::vector<Image::Image> res;
|
||||||
@@ -100,11 +98,11 @@ getFromAvMediaFile(const Av::MediaFile& input)
|
|||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(COVER, DEBUG) << "No cover found in media file '" << input.getPath().string() << "'";
|
LMS_LOG(COVER, DEBUG) << "No cover found in media file '" << input.getPath().string() << "'";
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Image::Image>
|
std::optional<Image::Image>
|
||||||
Grabber::getFromDirectory(const boost::filesystem::path& p) const
|
Grabber::getFromDirectory(const std::filesystem::path& p) const
|
||||||
{
|
{
|
||||||
for (auto coverPath : getCoverPaths(p))
|
for (auto coverPath : getCoverPaths(p))
|
||||||
{
|
{
|
||||||
@@ -117,33 +115,33 @@ Grabber::getFromDirectory(const boost::filesystem::path& p) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(COVER, DEBUG) << "No cover found in directory '" << p.string() << "'";
|
LMS_LOG(COVER, DEBUG) << "No cover found in directory '" << p.string() << "'";
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<boost::filesystem::path>
|
std::vector<std::filesystem::path>
|
||||||
Grabber::getCoverPaths(const boost::filesystem::path& directoryPath) const
|
Grabber::getCoverPaths(const std::filesystem::path& directoryPath) const
|
||||||
{
|
{
|
||||||
std::vector<boost::filesystem::path> res;
|
std::vector<std::filesystem::path> res;
|
||||||
boost::system::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
// TODO handle preferred file names
|
// TODO handle preferred file names
|
||||||
|
|
||||||
boost::filesystem::directory_iterator itPath(directoryPath, ec);
|
std::filesystem::directory_iterator itPath(directoryPath, ec);
|
||||||
boost::filesystem::directory_iterator itEnd;
|
std::filesystem::directory_iterator itEnd;
|
||||||
while (!ec && itPath != itEnd)
|
while (!ec && itPath != itEnd)
|
||||||
{
|
{
|
||||||
boost::filesystem::path path = *itPath;
|
std::filesystem::path path = *itPath;
|
||||||
itPath.increment(ec);
|
itPath.increment(ec);
|
||||||
|
|
||||||
if (!boost::filesystem::is_regular(path))
|
if (!std::filesystem::is_regular_file(path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!isFileSupported(path, _fileExtensions))
|
if (!isFileSupported(path, _fileExtensions))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (boost::filesystem::file_size(path) > _maxFileSize)
|
if (std::filesystem::file_size(path) > _maxFileSize)
|
||||||
{
|
{
|
||||||
LMS_LOG(COVER, INFO) << "Cover file '" << path.string() << " is too big (" << boost::filesystem::file_size(path) << "), limit is " << _maxFileSize;
|
LMS_LOG(COVER, INFO) << "Cover file '" << path.string() << " is too big (" << std::filesystem::file_size(path) << "), limit is " << _maxFileSize;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,8 +151,8 @@ Grabber::getCoverPaths(const boost::filesystem::path& directoryPath) const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Image::Image>
|
std::optional<Image::Image>
|
||||||
Grabber::getFromTrack(const boost::filesystem::path& p) const
|
Grabber::getFromTrack(const std::filesystem::path& p) const
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -165,7 +163,7 @@ Grabber::getFromTrack(const boost::filesystem::path& p) const
|
|||||||
catch (Av::MediaFileException& e)
|
catch (Av::MediaFileException& e)
|
||||||
{
|
{
|
||||||
LMS_LOG(COVER, ERROR) << "Cannot get covers from track " << p.string() << ": " << e.what();
|
LMS_LOG(COVER, ERROR) << "Cannot get covers from track " << p.string() << ": " << e.what();
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,10 +172,10 @@ Grabber::getFromTrack(Database::Session& dbSession, Database::IdType trackId, st
|
|||||||
{
|
{
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
boost::optional<Image::Image> cover;
|
std::optional<Image::Image> cover;
|
||||||
|
|
||||||
bool hasCover {};
|
bool hasCover {};
|
||||||
boost::filesystem::path trackPath;
|
std::filesystem::path trackPath;
|
||||||
|
|
||||||
{
|
{
|
||||||
auto transaction {dbSession.createSharedTransaction()};
|
auto transaction {dbSession.createSharedTransaction()};
|
||||||
@@ -208,9 +206,9 @@ Grabber::getFromTrack(Database::Session& dbSession, Database::IdType trackId, st
|
|||||||
Image::Image
|
Image::Image
|
||||||
Grabber::getFromRelease(Database::Session& session, Database::IdType releaseId, std::size_t size)
|
Grabber::getFromRelease(Database::Session& session, Database::IdType releaseId, std::size_t size)
|
||||||
{
|
{
|
||||||
boost::optional<Image::Image> cover;
|
std::optional<Image::Image> cover;
|
||||||
|
|
||||||
boost::optional<Database::IdType> trackId;
|
std::optional<Database::IdType> trackId;
|
||||||
{
|
{
|
||||||
auto transaction {session.createSharedTransaction()};
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
@@ -239,7 +237,8 @@ Grabber::getFromTrack(Database::Session& session, Database::IdType trackId, Imag
|
|||||||
{
|
{
|
||||||
const Image::Image cover {getFromTrack(session, trackId, size)};
|
const Image::Image cover {getFromTrack(session, trackId, size)};
|
||||||
|
|
||||||
return cover.save(Image::Format::JPEG);
|
assert(format == Image::Format::JPEG);
|
||||||
|
return cover.save(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t>
|
std::vector<uint8_t>
|
||||||
@@ -247,7 +246,8 @@ Grabber::getFromRelease(Database::Session& session, Database::IdType releaseId,
|
|||||||
{
|
{
|
||||||
const Image::Image cover {getFromRelease(session, releaseId, size)};
|
const Image::Image cover {getFromRelease(session, releaseId, size)};
|
||||||
|
|
||||||
return cover.save(Image::Format::JPEG);
|
assert(format == Image::Format::JPEG);
|
||||||
|
return cover.save(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CoverArt
|
} // namespace CoverArt
|
||||||
|
|||||||
@@ -19,12 +19,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
#include "image/Image.hpp"
|
#include "image/Image.hpp"
|
||||||
@@ -44,7 +44,7 @@ class Grabber
|
|||||||
Grabber(Grabber&&) = delete;
|
Grabber(Grabber&&) = delete;
|
||||||
Grabber& operator=(Grabber&&) = delete;
|
Grabber& operator=(Grabber&&) = delete;
|
||||||
|
|
||||||
void setDefaultCover(boost::filesystem::path defaultCoverPath);
|
void setDefaultCover(const std::filesystem::path& defaultCoverPath);
|
||||||
|
|
||||||
std::vector<uint8_t> getFromTrack(Database::Session& dbSession, Database::IdType trackId, Image::Format format, std::size_t size);
|
std::vector<uint8_t> getFromTrack(Database::Session& dbSession, Database::IdType trackId, Image::Format format, std::size_t size);
|
||||||
std::vector<uint8_t> getFromRelease(Database::Session& dbSession, Database::IdType releaseId, Image::Format format, std::size_t size);
|
std::vector<uint8_t> getFromRelease(Database::Session& dbSession, Database::IdType releaseId, Image::Format format, std::size_t size);
|
||||||
@@ -54,9 +54,9 @@ class Grabber
|
|||||||
Image::Image getFromTrack(Database::Session& dbSession, Database::IdType trackId, std::size_t size);
|
Image::Image getFromTrack(Database::Session& dbSession, Database::IdType trackId, std::size_t size);
|
||||||
Image::Image getFromRelease(Database::Session& dbSession, Database::IdType releaseId, std::size_t size);
|
Image::Image getFromRelease(Database::Session& dbSession, Database::IdType releaseId, std::size_t size);
|
||||||
|
|
||||||
boost::optional<Image::Image> getFromTrack(const boost::filesystem::path& path) const;
|
std::optional<Image::Image> getFromTrack(const std::filesystem::path& path) const;
|
||||||
std::vector<boost::filesystem::path> getCoverPaths(const boost::filesystem::path& directoryPath) const;
|
std::vector<std::filesystem::path> getCoverPaths(const std::filesystem::path& directoryPath) const;
|
||||||
boost::optional<Image::Image> getFromDirectory(const boost::filesystem::path& path) const;
|
std::optional<Image::Image> getFromDirectory(const std::filesystem::path& path) const;
|
||||||
|
|
||||||
Image::Image getDefaultCover(std::size_t size);
|
Image::Image getDefaultCover(std::size_t size);
|
||||||
|
|
||||||
@@ -65,12 +65,12 @@ class Grabber
|
|||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
std::map<std::size_t /* size */, Image::Image> _defaultCovers;
|
std::map<std::size_t /* size */, Image::Image> _defaultCovers;
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> _fileExtensions
|
std::vector<std::filesystem::path> _fileExtensions
|
||||||
= {".jpg", ".jpeg", ".png", ".bmp"}; // TODO parametrize
|
= {".jpg", ".jpeg", ".png", ".bmp"}; // TODO parametrize
|
||||||
|
|
||||||
std::size_t _maxFileSize = 5000000;
|
std::size_t _maxFileSize = 5000000;
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> _preferredFileNames
|
std::vector<std::filesystem::path> _preferredFileNames
|
||||||
= {"cover", "front"}; // TODO parametrize
|
= {"cover", "front"}; // TODO parametrize
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+13
-8
@@ -75,7 +75,7 @@ Artist::create(Session& session, const std::string& name, const std::string& MBI
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Artist::pointer>
|
std::vector<Artist::pointer>
|
||||||
Artist::getAll(Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
|
Artist::getAll(Session& session, std::optional<std::size_t> offset, std::optional<std::size_t> size)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Artist>()
|
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Artist>()
|
||||||
@@ -151,8 +151,8 @@ std::vector<Artist::pointer>
|
|||||||
Artist::getByFilter(Session& session,
|
Artist::getByFilter(Session& session,
|
||||||
const std::set<IdType>& clusters,
|
const std::set<IdType>& clusters,
|
||||||
const std::vector<std::string>& keywords,
|
const std::vector<std::string>& keywords,
|
||||||
boost::optional<std::size_t> offset,
|
std::optional<std::size_t> offset,
|
||||||
boost::optional<std::size_t> size,
|
std::optional<std::size_t> size,
|
||||||
bool& moreResults)
|
bool& moreResults)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
@@ -174,7 +174,7 @@ Artist::getByFilter(Session& session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Artist::pointer>
|
std::vector<Artist::pointer>
|
||||||
Artist::getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> limit)
|
Artist::getLastAdded(Session& session, Wt::WDateTime after, std::optional<std::size_t> limit)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
Wt::Dbo::collection<Artist::pointer> res = session.getDboSession().query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id")
|
Wt::Dbo::collection<Artist::pointer> res = session.getDboSession().query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id")
|
||||||
@@ -243,7 +243,7 @@ Artist::getReleaseCount() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Track>>
|
std::vector<Wt::Dbo::ptr<Track>>
|
||||||
Artist::getTracks(boost::optional<TrackArtistLink::Type> linkType) const
|
Artist::getTracks(std::optional<TrackArtistLink::Type> linkType) const
|
||||||
{
|
{
|
||||||
assert(self());
|
assert(self());
|
||||||
assert(IdIsValid(self()->id()));
|
assert(IdIsValid(self()->id()));
|
||||||
@@ -262,22 +262,27 @@ Artist::getTracks(boost::optional<TrackArtistLink::Type> linkType) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Track>>
|
std::vector<Wt::Dbo::ptr<Track>>
|
||||||
Artist::getTracksWithRelease(boost::optional<TrackArtistLink::Type> linkType) const
|
Artist::getTracksWithRelease(std::optional<TrackArtistLink::Type> linkType) const
|
||||||
{
|
{
|
||||||
assert(self());
|
assert(self());
|
||||||
assert(IdIsValid(self()->id()));
|
assert(IdIsValid(self()->id()));
|
||||||
assert(session());
|
assert(session());
|
||||||
|
|
||||||
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id INNER JOIN release r ON r.id = t.release_id")
|
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id INNER JOIN release r ON r.id = t.release_id")
|
||||||
.where("a.id = ?").bind(self()->id())
|
.where("a.id = ?").bind(self()->id())
|
||||||
.orderBy("t.year,r.name,t.disc_number,t.track_number")};
|
.orderBy("t.year,r.name,t.disc_number,t.track_number")};
|
||||||
|
|
||||||
|
if (linkType)
|
||||||
|
query.where("t_a_l.type = ?").bind(*linkType);
|
||||||
|
|
||||||
|
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {query.resultList()};
|
||||||
|
|
||||||
return std::vector<Wt::Dbo::ptr<Track>>(tracks.begin(), tracks.end());
|
return std::vector<Wt::Dbo::ptr<Track>>(tracks.begin(), tracks.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Track>>
|
std::vector<Wt::Dbo::ptr<Track>>
|
||||||
Artist::getRandomTracks(boost::optional<std::size_t> count) const
|
Artist::getRandomTracks(std::optional<std::size_t> count) const
|
||||||
{
|
{
|
||||||
assert(self());
|
assert(self());
|
||||||
assert(IdIsValid(self()->id()));
|
assert(IdIsValid(self()->id()));
|
||||||
|
|||||||
@@ -19,11 +19,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
|
|
||||||
@@ -58,13 +57,13 @@ class Artist : public Wt::Dbo::Dbo<Artist>
|
|||||||
static std::vector<pointer> getByFilter(Session& session,
|
static std::vector<pointer> getByFilter(Session& session,
|
||||||
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
||||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||||
boost::optional<std::size_t> offset,
|
std::optional<std::size_t> offset,
|
||||||
boost::optional<std::size_t> size,
|
std::optional<std::size_t> size,
|
||||||
bool& moreExpected);
|
bool& moreExpected);
|
||||||
|
|
||||||
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
static std::vector<pointer> getAll(Session& session, std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {});
|
||||||
static std::vector<pointer> getAllOrphans(Session& session); // No track related
|
static std::vector<pointer> getAllOrphans(Session& session); // No track related
|
||||||
static std::vector<pointer> getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> size = {});
|
static std::vector<pointer> getLastAdded(Session& session, Wt::WDateTime after, std::optional<std::size_t> size = {});
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
const std::string& getName(void) const { return _name; }
|
const std::string& getName(void) const { return _name; }
|
||||||
@@ -72,9 +71,9 @@ class Artist : public Wt::Dbo::Dbo<Artist>
|
|||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Release>> getReleases(const std::set<IdType>& clusterIds = std::set<IdType>()) const;
|
std::vector<Wt::Dbo::ptr<Release>> getReleases(const std::set<IdType>& clusterIds = std::set<IdType>()) const;
|
||||||
std::size_t getReleaseCount() const;
|
std::size_t getReleaseCount() const;
|
||||||
std::vector<Wt::Dbo::ptr<Track>> getTracks(boost::optional<TrackArtistLink::Type> linkType = {}) const;
|
std::vector<Wt::Dbo::ptr<Track>> getTracks(std::optional<TrackArtistLink::Type> linkType = {}) const;
|
||||||
std::vector<Wt::Dbo::ptr<Track>> getTracksWithRelease(boost::optional<TrackArtistLink::Type> linkType = {}) const;
|
std::vector<Wt::Dbo::ptr<Track>> getTracksWithRelease(std::optional<TrackArtistLink::Type> linkType = {}) const;
|
||||||
std::vector<Wt::Dbo::ptr<Track>> getRandomTracks(boost::optional<std::size_t> count) const;
|
std::vector<Wt::Dbo::ptr<Track>> getRandomTracks(std::optional<std::size_t> count) const;
|
||||||
|
|
||||||
// Get the cluster of the tracks made by this artist
|
// Get the cluster of the tracks made by this artist
|
||||||
// Each clusters are grouped by cluster type, sorted by the number of occurence
|
// Each clusters are grouped by cluster type, sorted by the number of occurence
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Database.hpp"
|
#include "Db.hpp"
|
||||||
|
|
||||||
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
||||||
#include <Wt/Dbo/backend/Sqlite3.h>
|
#include <Wt/Dbo/backend/Sqlite3.h>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
namespace Database {
|
namespace Database {
|
||||||
|
|
||||||
// Session living class handling the database and the login
|
// Session living class handling the database and the login
|
||||||
Database::Database(const boost::filesystem::path& dbPath)
|
Db::Db(const std::filesystem::path& dbPath)
|
||||||
{
|
{
|
||||||
LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string();
|
LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string();
|
||||||
|
|
||||||
@@ -49,9 +49,9 @@ Database::Database(const boost::filesystem::path& dbPath)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Session>
|
std::unique_ptr<Session>
|
||||||
Database::createSession()
|
Db::createSession()
|
||||||
{
|
{
|
||||||
return std::unique_ptr<Session>{new Session {_sharedMutex, *_connectionPool.get()}};
|
return std::unique_ptr<Session>(new Session {_sharedMutex, *_connectionPool.get()});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Database
|
} // namespace Database
|
||||||
@@ -19,10 +19,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||||
|
|
||||||
#include "Session.hpp"
|
#include "Session.hpp"
|
||||||
@@ -30,16 +29,16 @@
|
|||||||
namespace Database {
|
namespace Database {
|
||||||
|
|
||||||
// Session living class handling the database and the login
|
// Session living class handling the database and the login
|
||||||
class Database
|
class Db
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Database(const boost::filesystem::path& dbPath);
|
Db(const std::filesystem::path& dbPath);
|
||||||
|
|
||||||
std::unique_ptr<Session> createSession();
|
std::unique_ptr<Session> createSession();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_timed_mutex _sharedMutex;
|
std::shared_mutex _sharedMutex;
|
||||||
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
|
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
|
||||||
};
|
};
|
||||||
|
|
||||||
+18
-18
@@ -84,7 +84,7 @@ Release::getCount(Session& session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Release::pointer>
|
std::vector<Release::pointer>
|
||||||
Release::getAll(Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
|
Release::getAll(Session& session, std::optional<std::size_t> offset, std::optional<std::size_t> size)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ Release::getAll(Session& session, boost::optional<std::size_t> offset, boost::op
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Release::pointer>
|
std::vector<Release::pointer>
|
||||||
Release::getAllOrderedByArtist(Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
|
Release::getAllOrderedByArtist(Session& session, std::optional<std::size_t> offset, std::optional<std::size_t> size)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ Release::getAllOrderedByArtist(Session& session, boost::optional<std::size_t> of
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Release::pointer>
|
std::vector<Release::pointer>
|
||||||
Release::getAllRandom(Session& session, boost::optional<std::size_t> size)
|
Release::getAllRandom(Session& session, std::optional<std::size_t> size)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ Release::getAllOrphans(Session& session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Release::pointer>
|
std::vector<Release::pointer>
|
||||||
Release::getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> offset, boost::optional<std::size_t> limit)
|
Release::getLastAdded(Session& session, const Wt::WDateTime& after, std::optional<std::size_t> offset, std::optional<std::size_t> limit)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ Release::getLastAdded(Session& session, const Wt::WDateTime& after, boost::optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Release::pointer>
|
std::vector<Release::pointer>
|
||||||
Release::getByYear(Session& session, int yearFrom, int yearTo, boost::optional<std::size_t> offset, boost::optional<std::size_t> limit)
|
Release::getByYear(Session& session, int yearFrom, int yearTo, std::optional<std::size_t> offset, std::optional<std::size_t> limit)
|
||||||
{
|
{
|
||||||
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().query<Release::pointer>
|
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().query<Release::pointer>
|
||||||
("SELECT DISTINCT r from release r INNER JOIN track t ON r.id = t.release_id")
|
("SELECT DISTINCT r from release r INNER JOIN track t ON r.id = t.release_id")
|
||||||
@@ -216,8 +216,8 @@ std::vector<Release::pointer>
|
|||||||
Release::getByFilter(Session& session,
|
Release::getByFilter(Session& session,
|
||||||
const std::set<IdType>& clusterIds,
|
const std::set<IdType>& clusterIds,
|
||||||
const std::vector<std::string>& keywords,
|
const std::vector<std::string>& keywords,
|
||||||
boost::optional<std::size_t> offset,
|
std::optional<std::size_t> offset,
|
||||||
boost::optional<std::size_t> size,
|
std::optional<std::size_t> size,
|
||||||
bool& moreResults)
|
bool& moreResults)
|
||||||
{
|
{
|
||||||
Wt::Dbo::collection<pointer> collection = getQuery(session, clusterIds, keywords)
|
Wt::Dbo::collection<pointer> collection = getQuery(session, clusterIds, keywords)
|
||||||
@@ -237,19 +237,19 @@ Release::getByFilter(Session& session,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
Release::getTotalTrackNumber(void) const
|
Release::getTotalTrackNumber(void) const
|
||||||
{
|
{
|
||||||
return (_totalTrackNumber > 0) ? boost::make_optional<std::size_t>(_totalTrackNumber) : boost::none;
|
return (_totalTrackNumber > 0) ? std::make_optional<std::size_t>(_totalTrackNumber) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
Release::getTotalDiscNumber(void) const
|
Release::getTotalDiscNumber(void) const
|
||||||
{
|
{
|
||||||
return (_totalDiscNumber > 0) ? boost::make_optional<std::size_t>(_totalDiscNumber) : boost::none;
|
return (_totalDiscNumber > 0) ? std::make_optional<std::size_t>(_totalDiscNumber) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int>
|
std::optional<int>
|
||||||
Release::getReleaseYear(bool original) const
|
Release::getReleaseYear(bool original) const
|
||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
@@ -264,17 +264,17 @@ Release::getReleaseYear(bool original) const
|
|||||||
|
|
||||||
// various dates => no date
|
// various dates => no date
|
||||||
if (dates.empty() || dates.size() > 1)
|
if (dates.empty() || dates.size() > 1)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
auto date {dates.front()};
|
auto date {dates.front()};
|
||||||
|
|
||||||
if (date > 0)
|
if (date > 0)
|
||||||
return date;
|
return date;
|
||||||
else
|
else
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
Release::getCopyright() const
|
Release::getCopyright() const
|
||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
@@ -289,12 +289,12 @@ Release::getCopyright() const
|
|||||||
|
|
||||||
// various copyrights => no copyright
|
// various copyrights => no copyright
|
||||||
if (values.empty() || values.size() > 1 || values.front().empty())
|
if (values.empty() || values.size() > 1 || values.front().empty())
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
return values.front();
|
return values.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
Release::getCopyrightURL() const
|
Release::getCopyrightURL() const
|
||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
@@ -309,7 +309,7 @@ Release::getCopyrightURL() const
|
|||||||
|
|
||||||
// various copyright URLs => no copyright URL
|
// various copyright URLs => no copyright URL
|
||||||
if (values.empty() || values.size() > 1 || values.front().empty())
|
if (values.empty() || values.size() > 1 || values.front().empty())
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
return values.front();
|
return values.front();
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-13
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
@@ -51,18 +51,18 @@ class Release : public Wt::Dbo::Dbo<Release>
|
|||||||
static std::vector<pointer> getByName(Session& session, const std::string& name);
|
static std::vector<pointer> getByName(Session& session, const std::string& name);
|
||||||
static pointer getById(Session& session, IdType id);
|
static pointer getById(Session& session, IdType id);
|
||||||
static std::vector<pointer> getAllOrphans(Session& session); // no track related
|
static std::vector<pointer> getAllOrphans(Session& session); // no track related
|
||||||
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
static std::vector<pointer> getAll(Session& session, std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {});
|
||||||
static std::vector<pointer> getAllOrderedByArtist(Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
static std::vector<pointer> getAllOrderedByArtist(Session& session, std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {});
|
||||||
static std::vector<pointer> getAllRandom(Session& session, boost::optional<std::size_t> size = {});
|
static std::vector<pointer> getAllRandom(Session& session, std::optional<std::size_t> size = {});
|
||||||
static std::vector<pointer> getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
static std::vector<pointer> getLastAdded(Session& session, const Wt::WDateTime& after, std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {});
|
||||||
static std::vector<pointer> getByYear(Session& session, int yearFrom, int yearTo, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
static std::vector<pointer> getByYear(Session& session, int yearFrom, int yearTo, std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {});
|
||||||
|
|
||||||
static std::vector<pointer> getByFilter(Session& session, const std::set<IdType>& clusters);
|
static std::vector<pointer> getByFilter(Session& session, const std::set<IdType>& clusters);
|
||||||
static std::vector<pointer> getByFilter(Session& session,
|
static std::vector<pointer> getByFilter(Session& session,
|
||||||
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
||||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||||
boost::optional<std::size_t> offset,
|
std::optional<std::size_t> offset,
|
||||||
boost::optional<std::size_t> size,
|
std::optional<std::size_t> size,
|
||||||
bool& moreExpected);
|
bool& moreExpected);
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Track>> getTracks(const std::set<IdType>& clusters = std::set<IdType>()) const;
|
std::vector<Wt::Dbo::ptr<Track>> getTracks(const std::set<IdType>& clusters = std::set<IdType>()) const;
|
||||||
@@ -77,9 +77,9 @@ class Release : public Wt::Dbo::Dbo<Release>
|
|||||||
static pointer create(Session& session, const std::string& name, const std::string& MBID = "");
|
static pointer create(Session& session, const std::string& name, const std::string& MBID = "");
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
boost::optional<int> getReleaseYear(bool originalDate = false) const; // 0 if unknown or various
|
std::optional<int> getReleaseYear(bool originalDate = false) const; // 0 if unknown or various
|
||||||
boost::optional<std::string> getCopyright() const;
|
std::optional<std::string> getCopyright() const;
|
||||||
boost::optional<std::string> getCopyrightURL() const;
|
std::optional<std::string> getCopyrightURL() const;
|
||||||
|
|
||||||
// Modifiers
|
// Modifiers
|
||||||
void setTotalDiscNumber(std::size_t num) { _totalDiscNumber = static_cast<int>(num); }
|
void setTotalDiscNumber(std::size_t num) { _totalDiscNumber = static_cast<int>(num); }
|
||||||
@@ -88,8 +88,8 @@ class Release : public Wt::Dbo::Dbo<Release>
|
|||||||
// Accessors
|
// Accessors
|
||||||
std::string getName() const { return _name; }
|
std::string getName() const { return _name; }
|
||||||
std::string getMBID() const { return _MBID; }
|
std::string getMBID() const { return _MBID; }
|
||||||
boost::optional<std::size_t> getTotalTrackNumber() const;
|
std::optional<std::size_t> getTotalTrackNumber() const;
|
||||||
boost::optional<std::size_t> getTotalDiscNumber() const;
|
std::optional<std::size_t> getTotalDiscNumber() const;
|
||||||
std::chrono::milliseconds getDuration() const;
|
std::chrono::milliseconds getDuration() const;
|
||||||
|
|
||||||
// Get the artists of this release
|
// Get the artists of this release
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ ScanSettings::get(Session& session)
|
|||||||
return session.getDboSession().find<ScanSettings>();
|
return session.getDboSession().find<ScanSettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<boost::filesystem::path>
|
std::set<std::filesystem::path>
|
||||||
ScanSettings::getAudioFileExtensions() const
|
ScanSettings::getAudioFileExtensions() const
|
||||||
{
|
{
|
||||||
auto extensions = splitString(_audioFileExtensions, " ");
|
auto extensions = splitString(_audioFileExtensions, " ");
|
||||||
return std::set<boost::filesystem::path>(std::cbegin(extensions), std::cend(extensions));
|
return std::set<std::filesystem::path>(std::cbegin(extensions), std::cend(extensions));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ClusterType::pointer>
|
std::vector<ClusterType::pointer>
|
||||||
@@ -76,7 +76,7 @@ ScanSettings::getClusterTypes() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ScanSettings::setMediaDirectory(boost::filesystem::path p)
|
ScanSettings::setMediaDirectory(std::filesystem::path p)
|
||||||
{
|
{
|
||||||
_mediaDirectory = stringTrimEnd(p.string(), "/\\");
|
_mediaDirectory = stringTrimEnd(p.string(), "/\\");
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ template <typename It>
|
|||||||
std::set<std::string> getNames(It begin, It end)
|
std::set<std::string> getNames(It begin, It end)
|
||||||
{
|
{
|
||||||
std::set<std::string> names;
|
std::set<std::string> names;
|
||||||
std::transform(begin, end, std::inserter(names, std::begin(names)),
|
std::transform(begin, end, std::inserter(names, std::cbegin(names)),
|
||||||
[](const ClusterType::pointer& clusterType)
|
[](const ClusterType::pointer& clusterType)
|
||||||
{
|
{
|
||||||
return clusterType->getName();
|
return clusterType->getName();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <filesystem>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
#include <Wt/WTime.h>
|
#include <Wt/WTime.h>
|
||||||
@@ -47,18 +47,18 @@ class ScanSettings : public Wt::Dbo::Dbo<ScanSettings>
|
|||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
std::size_t getScanVersion() const { return _scanVersion; }
|
std::size_t getScanVersion() const { return _scanVersion; }
|
||||||
boost::filesystem::path getMediaDirectory() const { return _mediaDirectory; }
|
std::filesystem::path getMediaDirectory() const { return _mediaDirectory; }
|
||||||
Wt::WTime getUpdateStartTime() const { return _startTime; }
|
Wt::WTime getUpdateStartTime() const { return _startTime; }
|
||||||
UpdatePeriod getUpdatePeriod() const { return _updatePeriod; }
|
UpdatePeriod getUpdatePeriod() const { return _updatePeriod; }
|
||||||
std::vector<Wt::Dbo::ptr<ClusterType>> getClusterTypes() const;
|
std::vector<Wt::Dbo::ptr<ClusterType>> getClusterTypes() const;
|
||||||
std::set<boost::filesystem::path> getAudioFileExtensions() const;
|
std::set<std::filesystem::path> getAudioFileExtensions() const;
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
void setMediaDirectory(boost::filesystem::path p);
|
void setMediaDirectory(std::filesystem::path p);
|
||||||
void setUpdateStartTime(Wt::WTime t) { _startTime = t; }
|
void setUpdateStartTime(Wt::WTime t) { _startTime = t; }
|
||||||
void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; }
|
void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; }
|
||||||
void setClusterTypes(Session& session, const std::set<std::string>& clusterTypeNames);
|
void setClusterTypes(Session& session, const std::set<std::string>& clusterTypeNames);
|
||||||
void setAudioFileExtensions(std::set<boost::filesystem::path> fileExtensions);
|
void setAudioFileExtensions(std::set<std::filesystem::path> fileExtensions);
|
||||||
|
|
||||||
template<class Action>
|
template<class Action>
|
||||||
void persist(Action& a)
|
void persist(Action& a)
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ Session::doDatabaseMigrationIfNeeded()
|
|||||||
VersionInfo::get(*this).modify()->setVersion(LMS_DATABASE_VERSION);
|
VersionInfo::get(*this).modify()->setVersion(LMS_DATABASE_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
Session::Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool)
|
Session::Session(std::shared_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||||
: _mutex {mutex}
|
: _mutex {mutex}
|
||||||
{
|
{
|
||||||
_session.setConnectionPool(connectionPool);
|
_session.setConnectionPool(connectionPool);
|
||||||
@@ -140,9 +140,9 @@ enum class OwnedLock
|
|||||||
Unique,
|
Unique,
|
||||||
};
|
};
|
||||||
|
|
||||||
static thread_local std::map<std::shared_timed_mutex*, OwnedLock> lockDebug;
|
static thread_local std::map<std::shared_mutex*, OwnedLock> lockDebug;
|
||||||
|
|
||||||
UniqueTransaction::UniqueTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
|
UniqueTransaction::UniqueTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session)
|
||||||
: _lock {mutex},
|
: _lock {mutex},
|
||||||
_transaction {session}
|
_transaction {session}
|
||||||
{
|
{
|
||||||
@@ -156,7 +156,7 @@ UniqueTransaction::~UniqueTransaction()
|
|||||||
lockDebug[_lock.mutex()] = OwnedLock::None;
|
lockDebug[_lock.mutex()] = OwnedLock::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedTransaction::SharedTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
|
SharedTransaction::SharedTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session)
|
||||||
: _lock {mutex},
|
: _lock {mutex},
|
||||||
_transaction {session}
|
_transaction {session}
|
||||||
{
|
{
|
||||||
@@ -182,16 +182,16 @@ Session::checkSharedLocked()
|
|||||||
assert(lockDebug[&_mutex] != OwnedLock::None);
|
assert(lockDebug[&_mutex] != OwnedLock::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<UniqueTransaction>
|
UniqueTransaction
|
||||||
Session::createUniqueTransaction()
|
Session::createUniqueTransaction()
|
||||||
{
|
{
|
||||||
return std::unique_ptr<UniqueTransaction>(new UniqueTransaction{_mutex, _session});
|
return UniqueTransaction{_mutex, _session};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SharedTransaction>
|
SharedTransaction
|
||||||
Session::createSharedTransaction()
|
Session::createSharedTransaction()
|
||||||
{
|
{
|
||||||
return std::unique_ptr<SharedTransaction>(new SharedTransaction{_mutex, _session});
|
return SharedTransaction{_mutex, _session};
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
@@ -36,9 +35,9 @@ class UniqueTransaction
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Session;
|
friend class Session;
|
||||||
UniqueTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session);
|
UniqueTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session);
|
||||||
|
|
||||||
std::unique_lock<std::shared_timed_mutex> _lock;
|
std::unique_lock<std::shared_mutex> _lock;
|
||||||
Wt::Dbo::Transaction _transaction;
|
Wt::Dbo::Transaction _transaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,9 +48,9 @@ class SharedTransaction
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Session;
|
friend class Session;
|
||||||
SharedTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session);
|
SharedTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session);
|
||||||
|
|
||||||
std::shared_lock<std::shared_timed_mutex> _lock;
|
std::shared_lock<std::shared_mutex> _lock;
|
||||||
Wt::Dbo::Transaction _transaction;
|
Wt::Dbo::Transaction _transaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,8 +62,8 @@ class Session
|
|||||||
Session& operator=(const Session&) = delete;
|
Session& operator=(const Session&) = delete;
|
||||||
Session& operator=(Session&&) = delete;
|
Session& operator=(Session&&) = delete;
|
||||||
|
|
||||||
std::unique_ptr<UniqueTransaction> createUniqueTransaction();
|
UniqueTransaction createUniqueTransaction();
|
||||||
std::unique_ptr<SharedTransaction> createSharedTransaction();
|
SharedTransaction createSharedTransaction();
|
||||||
|
|
||||||
void checkUniqueLocked();
|
void checkUniqueLocked();
|
||||||
void checkSharedLocked();
|
void checkSharedLocked();
|
||||||
@@ -74,14 +73,14 @@ class Session
|
|||||||
Wt::Dbo::Session& getDboSession() { return _session; }
|
Wt::Dbo::Session& getDboSession() { return _session; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Database;
|
friend class Db;
|
||||||
|
|
||||||
Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool);
|
Session(std::shared_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool);
|
||||||
|
|
||||||
void doDatabaseMigrationIfNeeded();
|
void doDatabaseMigrationIfNeeded();
|
||||||
void prepareTables(); // need to run only once at startup
|
void prepareTables(); // need to run only once at startup
|
||||||
|
|
||||||
std::shared_timed_mutex& _mutex;
|
std::shared_mutex& _mutex;
|
||||||
Wt::Dbo::Session _session;
|
Wt::Dbo::Session _session;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+23
-23
@@ -32,14 +32,14 @@
|
|||||||
|
|
||||||
namespace Database {
|
namespace Database {
|
||||||
|
|
||||||
Track::Track(const boost::filesystem::path& p)
|
Track::Track(const std::filesystem::path& p)
|
||||||
:
|
:
|
||||||
_filePath( p.string() )
|
_filePath( p.string() )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Track::pointer>
|
std::vector<Track::pointer>
|
||||||
Track::getAll(Session& session, boost::optional<std::size_t> limit)
|
Track::getAll(Session& session, std::optional<std::size_t> limit)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ Track::getAll(Session& session, boost::optional<std::size_t> limit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Track::pointer>
|
std::vector<Track::pointer>
|
||||||
Track::getAllRandom(Session& session, boost::optional<std::size_t> limit)
|
Track::getAllRandom(Session& session, std::optional<std::size_t> limit)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ Track::getAllIds(Session& session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Track::pointer
|
Track::pointer
|
||||||
Track::getByPath(Session& session, const boost::filesystem::path& p)
|
Track::getByPath(Session& session, const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ Track::getByMBID(Session& session, const std::string& mbid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Track::pointer
|
Track::pointer
|
||||||
Track::create(Session& session, const boost::filesystem::path& p)
|
Track::create(Session& session, const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
session.checkUniqueLocked();
|
session.checkUniqueLocked();
|
||||||
|
|
||||||
@@ -107,13 +107,13 @@ Track::create(Session& session, const boost::filesystem::path& p)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<boost::filesystem::path>
|
std::vector<std::filesystem::path>
|
||||||
Track::getAllPaths(Session& session)
|
Track::getAllPaths(Session& session)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
Wt::Dbo::collection<std::string> res = session.getDboSession().query<std::string>("SELECT file_path FROM track");
|
Wt::Dbo::collection<std::string> res = session.getDboSession().query<std::string>("SELECT file_path FROM track");
|
||||||
return std::vector<boost::filesystem::path>(res.begin(), res.end());
|
return std::vector<std::filesystem::path>(res.begin(), res.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Track::pointer>
|
std::vector<Track::pointer>
|
||||||
@@ -126,7 +126,7 @@ Track::getMBIDDuplicates(Session& session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Track::pointer>
|
std::vector<Track::pointer>
|
||||||
Track::getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> limit)
|
Track::getLastAdded(Session& session, const Wt::WDateTime& after, std::optional<std::size_t> limit)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ Track::getAllWithMBIDAndMissingFeatures(Session& session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IdType>
|
std::vector<IdType>
|
||||||
Track::getAllIdsWithFeatures(Session& session, boost::optional<std::size_t> limit)
|
Track::getAllIdsWithFeatures(Session& session, std::optional<std::size_t> limit)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
@@ -224,8 +224,8 @@ std::vector<Track::pointer>
|
|||||||
Track::getByFilter(Session& session,
|
Track::getByFilter(Session& session,
|
||||||
const std::set<IdType>& clusterIds,
|
const std::set<IdType>& clusterIds,
|
||||||
const std::vector<std::string>& keywords,
|
const std::vector<std::string>& keywords,
|
||||||
boost::optional<std::size_t> offset,
|
std::optional<std::size_t> offset,
|
||||||
boost::optional<std::size_t> size,
|
std::optional<std::size_t> size,
|
||||||
bool& moreResults)
|
bool& moreResults)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
@@ -289,40 +289,40 @@ Track::setFeatures(const Wt::Dbo::ptr<TrackFeatures>& features)
|
|||||||
_trackFeatures = features;
|
_trackFeatures = features;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
Track::getTrackNumber(void) const
|
Track::getTrackNumber(void) const
|
||||||
{
|
{
|
||||||
return (_trackNumber > 0) ? boost::make_optional<std::size_t>(_trackNumber) : boost::none;
|
return (_trackNumber > 0) ? std::make_optional<std::size_t>(_trackNumber) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
Track::getDiscNumber(void) const
|
Track::getDiscNumber(void) const
|
||||||
{
|
{
|
||||||
return (_discNumber > 0) ? boost::make_optional<std::size_t>(_discNumber) : boost::none;
|
return (_discNumber > 0) ? std::make_optional<std::size_t>(_discNumber) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int>
|
std::optional<int>
|
||||||
Track::getYear() const
|
Track::getYear() const
|
||||||
{
|
{
|
||||||
return (_year > 0) ? boost::make_optional<int>(_year) : boost::none;
|
return (_year > 0) ? std::make_optional<int>(_year) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int>
|
std::optional<int>
|
||||||
Track::getOriginalYear() const
|
Track::getOriginalYear() const
|
||||||
{
|
{
|
||||||
return (_originalYear > 0) ? boost::make_optional<int>(_originalYear) : boost::none;
|
return (_originalYear > 0) ? std::make_optional<int>(_originalYear) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
Track::getCopyright() const
|
Track::getCopyright() const
|
||||||
{
|
{
|
||||||
return _copyright != "" ? boost::make_optional<std::string>(_copyright) : boost::none;
|
return _copyright != "" ? std::make_optional<std::string>(_copyright) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
Track::getCopyrightURL() const
|
Track::getCopyrightURL() const
|
||||||
{
|
{
|
||||||
return _copyrightURL != "" ? boost::make_optional<std::string>(_copyrightURL) : boost::none;
|
return _copyrightURL != "" ? std::make_optional<std::string>(_copyrightURL) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Artist>>
|
std::vector<Wt::Dbo::ptr<Artist>>
|
||||||
|
|||||||
+22
-23
@@ -19,12 +19,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
#include <boost/filesystem.hpp>
|
#include <optional>
|
||||||
#include <boost/optional.hpp>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
@@ -50,10 +49,10 @@ class Track : public Wt::Dbo::Dbo<Track>
|
|||||||
using pointer = Wt::Dbo::ptr<Track>;
|
using pointer = Wt::Dbo::ptr<Track>;
|
||||||
|
|
||||||
Track() {}
|
Track() {}
|
||||||
Track(const boost::filesystem::path& p);
|
Track(const std::filesystem::path& p);
|
||||||
|
|
||||||
// Find utility functions
|
// Find utility functions
|
||||||
static pointer getByPath(Session& session, const boost::filesystem::path& p);
|
static pointer getByPath(Session& session, const std::filesystem::path& p);
|
||||||
static pointer getById(Session& session, IdType id);
|
static pointer getById(Session& session, IdType id);
|
||||||
static pointer getByMBID(Session& session, const std::string& MBID);
|
static pointer getByMBID(Session& session, const std::string& MBID);
|
||||||
static std::vector<pointer> getByFilter(Session& session,
|
static std::vector<pointer> getByFilter(Session& session,
|
||||||
@@ -61,21 +60,21 @@ class Track : public Wt::Dbo::Dbo<Track>
|
|||||||
static std::vector<pointer> getByFilter(Session& session,
|
static std::vector<pointer> getByFilter(Session& session,
|
||||||
const std::set<IdType>& clusters, // tracks that belong to these clusters
|
const std::set<IdType>& clusters, // tracks that belong to these clusters
|
||||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||||
boost::optional<std::size_t> offset,
|
std::optional<std::size_t> offset,
|
||||||
boost::optional<std::size_t> size,
|
std::optional<std::size_t> size,
|
||||||
bool& moreExpected);
|
bool& moreExpected);
|
||||||
|
|
||||||
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> limit = {});
|
static std::vector<pointer> getAll(Session& session, std::optional<std::size_t> limit = {});
|
||||||
static std::vector<pointer> getAllRandom(Session& session, boost::optional<std::size_t> limit = {});
|
static std::vector<pointer> getAllRandom(Session& session, std::optional<std::size_t> limit = {});
|
||||||
static std::vector<IdType> getAllIds(Session& session); // nested transaction
|
static std::vector<IdType> getAllIds(Session& session); // nested transaction
|
||||||
static std::vector<boost::filesystem::path> getAllPaths(Session& session); // nested transaction
|
static std::vector<std::filesystem::path> getAllPaths(Session& session); // nested transaction
|
||||||
static std::vector<pointer> getMBIDDuplicates(Session& session);
|
static std::vector<pointer> getMBIDDuplicates(Session& session);
|
||||||
static std::vector<pointer> getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> size = 1);
|
static std::vector<pointer> getLastAdded(Session& session, const Wt::WDateTime& after, std::optional<std::size_t> size = 1);
|
||||||
static std::vector<pointer> getAllWithMBIDAndMissingFeatures(Session& session);
|
static std::vector<pointer> getAllWithMBIDAndMissingFeatures(Session& session);
|
||||||
static std::vector<IdType> getAllIdsWithFeatures(Session& session, boost::optional<std::size_t> limit = {});
|
static std::vector<IdType> getAllIdsWithFeatures(Session& session, std::optional<std::size_t> limit = {});
|
||||||
|
|
||||||
// Create utility
|
// Create utility
|
||||||
static pointer create(Session& session, const boost::filesystem::path& p);
|
static pointer create(Session& session, const std::filesystem::path& p);
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
void setScanVersion(std::size_t version) { _scanVersion = version; }
|
void setScanVersion(std::size_t version) { _scanVersion = version; }
|
||||||
@@ -98,20 +97,20 @@ class Track : public Wt::Dbo::Dbo<Track>
|
|||||||
void setFeatures(const Wt::Dbo::ptr<TrackFeatures>& features);
|
void setFeatures(const Wt::Dbo::ptr<TrackFeatures>& features);
|
||||||
|
|
||||||
std::size_t getScanVersion() const { return _scanVersion; }
|
std::size_t getScanVersion() const { return _scanVersion; }
|
||||||
boost::optional<std::size_t> getTrackNumber() const;
|
std::optional<std::size_t> getTrackNumber() const;
|
||||||
boost::optional<std::size_t> getDiscNumber() const;
|
std::optional<std::size_t> getDiscNumber() const;
|
||||||
std::string getName() const { return _name; }
|
std::string getName() const { return _name; }
|
||||||
boost::filesystem::path getPath() const { return _filePath; }
|
std::filesystem::path getPath() const { return _filePath; }
|
||||||
std::chrono::milliseconds getDuration() const { return _duration; }
|
std::chrono::milliseconds getDuration() const { return _duration; }
|
||||||
boost::optional<int> getYear() const;
|
std::optional<int> getYear() const;
|
||||||
boost::optional<int> getOriginalYear() const;
|
std::optional<int> getOriginalYear() const;
|
||||||
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
|
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
|
||||||
Wt::WDateTime getAddedTime() const { return _fileAdded; }
|
Wt::WDateTime getAddedTime() const { return _fileAdded; }
|
||||||
bool hasCover() const { return _hasCover; }
|
bool hasCover() const { return _hasCover; }
|
||||||
const std::string& getMBID() const { return _MBID; }
|
const std::string& getMBID() const { return _MBID; }
|
||||||
boost::optional<std::string> getCopyright() const;
|
std::optional<std::string> getCopyright() const;
|
||||||
boost::optional<std::string> getCopyrightURL() const;
|
std::optional<std::string> getCopyrightURL() const;
|
||||||
std::vector<Wt::Dbo::ptr<Artist>> getArtists(TrackArtistLink::Type type = {TrackArtistLink::Type::Artist}) const;
|
std::vector<Wt::Dbo::ptr<Artist>> getArtists(TrackArtistLink::Type type = TrackArtistLink::Type::Artist) const;
|
||||||
std::vector<Wt::Dbo::ptr<TrackArtistLink>> getArtistLinks() const;
|
std::vector<Wt::Dbo::ptr<TrackArtistLink>> getArtistLinks() const;
|
||||||
Wt::Dbo::ptr<Release> getRelease() const { return _release; }
|
Wt::Dbo::ptr<Release> getRelease() const { return _release; }
|
||||||
std::vector<Wt::Dbo::ptr<Cluster>> getClusters() const;
|
std::vector<Wt::Dbo::ptr<Cluster>> getClusters() const;
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ TrackList::getById(Session& session, IdType id)
|
|||||||
|
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<TrackListEntry>>
|
std::vector<Wt::Dbo::ptr<TrackListEntry>>
|
||||||
TrackList::getEntries(boost::optional<std::size_t> offset, boost::optional<std::size_t> size) const
|
TrackList::getEntries(std::optional<std::size_t> offset, std::optional<std::size_t> size) const
|
||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
assert(IdIsValid(self()->id()));
|
assert(IdIsValid(self()->id()));
|
||||||
@@ -116,7 +116,7 @@ TrackList::getEntries(boost::optional<std::size_t> offset, boost::optional<std::
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<TrackListEntry>>
|
std::vector<Wt::Dbo::ptr<TrackListEntry>>
|
||||||
TrackList::getEntriesReverse(boost::optional<std::size_t> offset, boost::optional<std::size_t> size) const
|
TrackList::getEntriesReverse(std::optional<std::size_t> offset, std::optional<std::size_t> size) const
|
||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
assert(IdIsValid(self()->id()));
|
assert(IdIsValid(self()->id()));
|
||||||
|
|||||||
@@ -19,12 +19,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "Types.hpp"
|
#include "Types.hpp"
|
||||||
|
|
||||||
namespace Database {
|
namespace Database {
|
||||||
@@ -79,8 +78,8 @@ class TrackList : public Wt::Dbo::Dbo<TrackList>
|
|||||||
// Get tracks, ordered by position
|
// Get tracks, ordered by position
|
||||||
std::size_t getCount() const;
|
std::size_t getCount() const;
|
||||||
Wt::Dbo::ptr<TrackListEntry> getEntry(std::size_t pos) const;
|
Wt::Dbo::ptr<TrackListEntry> getEntry(std::size_t pos) const;
|
||||||
std::vector<Wt::Dbo::ptr<TrackListEntry>> getEntries(boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {}) const;
|
std::vector<Wt::Dbo::ptr<TrackListEntry>> getEntries(std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {}) const;
|
||||||
std::vector<Wt::Dbo::ptr<TrackListEntry>> getEntriesReverse(boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {}) const;
|
std::vector<Wt::Dbo::ptr<TrackListEntry>> getEntriesReverse(std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {}) const;
|
||||||
|
|
||||||
std::vector<IdType> getTrackIds() const;
|
std::vector<IdType> getTrackIds() const;
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ User::hasStarredRelease(Wt::Dbo::ptr<Release> release) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Wt::Dbo::ptr<Release>>
|
std::vector<Wt::Dbo::ptr<Release>>
|
||||||
User::getStarredReleases(boost::optional<std::size_t> offset, boost::optional<std::size_t> limit) const
|
User::getStarredReleases(std::optional<std::size_t> offset, std::optional<std::size_t> limit) const
|
||||||
{
|
{
|
||||||
Wt::Dbo::collection<Wt::Dbo::ptr<Release>> res = _starredReleases.find()
|
Wt::Dbo::collection<Wt::Dbo::ptr<Release>> res = _starredReleases.find()
|
||||||
.offset(offset ? static_cast<int>(*offset) : -1)
|
.offset(offset ? static_cast<int>(*offset) : -1)
|
||||||
|
|||||||
@@ -19,10 +19,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
@@ -162,7 +161,7 @@ class User : public Wt::Dbo::Dbo<User>
|
|||||||
void starRelease(Wt::Dbo::ptr<Release> release);
|
void starRelease(Wt::Dbo::ptr<Release> release);
|
||||||
void unstarRelease(Wt::Dbo::ptr<Release> release);
|
void unstarRelease(Wt::Dbo::ptr<Release> release);
|
||||||
bool hasStarredRelease(Wt::Dbo::ptr<Release> release) const;
|
bool hasStarredRelease(Wt::Dbo::ptr<Release> release) const;
|
||||||
std::vector<Wt::Dbo::ptr<Release>> getStarredReleases(boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {}) const;
|
std::vector<Wt::Dbo::ptr<Release>> getStarredReleases(std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {}) const;
|
||||||
|
|
||||||
void starTrack(Wt::Dbo::ptr<Track> track);
|
void starTrack(Wt::Dbo::ptr<Track> track);
|
||||||
void unstarTrack(Wt::Dbo::ptr<Track> track);
|
void unstarTrack(Wt::Dbo::ptr<Track> track);
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ Image::load(const std::vector<unsigned char>& rawData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Image::load(boost::filesystem::path p)
|
Image::load(const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
+2
-3
@@ -19,10 +19,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
|
||||||
|
|
||||||
#include <Magick++.h>
|
#include <Magick++.h>
|
||||||
|
|
||||||
namespace Image
|
namespace Image
|
||||||
@@ -49,7 +48,7 @@ class Image
|
|||||||
|
|
||||||
// input
|
// input
|
||||||
bool load(const std::vector<unsigned char>& rawData);
|
bool load(const std::vector<unsigned char>& rawData);
|
||||||
bool load(boost::filesystem::path p);
|
bool load(const std::filesystem::path& p);
|
||||||
|
|
||||||
Geometry getSize() const;
|
Geometry getSize() const;
|
||||||
|
|
||||||
|
|||||||
+8
-9
@@ -17,7 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/property_tree/xml_parser.hpp>
|
#include <boost/property_tree/xml_parser.hpp>
|
||||||
|
|
||||||
#include <Wt/WServer.h>
|
#include <Wt/WServer.h>
|
||||||
@@ -29,6 +28,7 @@
|
|||||||
#include "auth/AuthTokenService.hpp"
|
#include "auth/AuthTokenService.hpp"
|
||||||
#include "auth/PasswordService.hpp"
|
#include "auth/PasswordService.hpp"
|
||||||
#include "cover/CoverArtGrabber.hpp"
|
#include "cover/CoverArtGrabber.hpp"
|
||||||
|
#include "database/Db.hpp"
|
||||||
#include "image/Image.hpp"
|
#include "image/Image.hpp"
|
||||||
#include "scanner/MediaScanner.hpp"
|
#include "scanner/MediaScanner.hpp"
|
||||||
#include "similarity/features/SimilarityFeaturesScannerAddon.hpp"
|
#include "similarity/features/SimilarityFeaturesScannerAddon.hpp"
|
||||||
@@ -42,9 +42,9 @@ std::vector<std::string> generateWtConfig(std::string execPath)
|
|||||||
{
|
{
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
|
||||||
const boost::filesystem::path wtConfigPath {getService<Config>()->getPath("working-dir") / "wt_config.xml"};
|
const std::filesystem::path wtConfigPath {getService<Config>()->getPath("working-dir") / "wt_config.xml"};
|
||||||
const boost::filesystem::path wtLogFilePath {getService<Config>()->getPath("log-file", "/var/log/lms.log")};
|
const std::filesystem::path wtLogFilePath {getService<Config>()->getPath("log-file", "/var/log/lms.log")};
|
||||||
const boost::filesystem::path wtAccessLogFilePath {getService<Config>()->getPath("access-log-file", "/var/log/lms.access.log")};
|
const std::filesystem::path wtAccessLogFilePath {getService<Config>()->getPath("access-log-file", "/var/log/lms.access.log")};
|
||||||
|
|
||||||
args.push_back(execPath);
|
args.push_back(execPath);
|
||||||
args.push_back("--config=" + wtConfigPath.string());
|
args.push_back("--config=" + wtConfigPath.string());
|
||||||
@@ -86,7 +86,7 @@ std::vector<std::string> generateWtConfig(std::string execPath)
|
|||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
boost::filesystem::path configFilePath {"/etc/lms.conf"};
|
std::filesystem::path configFilePath {"/etc/lms.conf"};
|
||||||
int res = EXIT_FAILURE;
|
int res = EXIT_FAILURE;
|
||||||
|
|
||||||
assert(argc > 0);
|
assert(argc > 0);
|
||||||
@@ -110,8 +110,8 @@ int main(int argc, char* argv[])
|
|||||||
ServiceProvider<Config>::create(configFilePath);
|
ServiceProvider<Config>::create(configFilePath);
|
||||||
|
|
||||||
// Make sure the working directory exists
|
// Make sure the working directory exists
|
||||||
boost::filesystem::create_directories(getService<Config>()->getPath("working-dir"));
|
std::filesystem::create_directories(getService<Config>()->getPath("working-dir"));
|
||||||
boost::filesystem::create_directories(getService<Config>()->getPath("working-dir") / "cache");
|
std::filesystem::create_directories(getService<Config>()->getPath("working-dir") / "cache");
|
||||||
|
|
||||||
// Construct WT configuration and get the argc/argv back
|
// Construct WT configuration and get the argc/argv back
|
||||||
std::vector<std::string> wtServerArgs = generateWtConfig(argv[0]);
|
std::vector<std::string> wtServerArgs = generateWtConfig(argv[0]);
|
||||||
@@ -128,11 +128,10 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
// lib init
|
// lib init
|
||||||
Image::init(argv[0]);
|
Image::init(argv[0]);
|
||||||
Av::AvInit();
|
|
||||||
Av::Transcoder::init();
|
Av::Transcoder::init();
|
||||||
|
|
||||||
// Initializing a connection pool to the database that will be shared along services
|
// Initializing a connection pool to the database that will be shared along services
|
||||||
Database::Database database {getService<Config>()->getPath("working-dir") / "lms.db"};
|
Database::Db database {getService<Config>()->getPath("working-dir") / "lms.db"};
|
||||||
|
|
||||||
UserInterface::LmsApplicationGroupContainer appGroups;
|
UserInterface::LmsApplicationGroupContainer appGroups;
|
||||||
|
|
||||||
|
|||||||
@@ -29,21 +29,21 @@ namespace MetaData
|
|||||||
|
|
||||||
using MetadataMap = std::map<std::string, std::string>;
|
using MetadataMap = std::map<std::string, std::string>;
|
||||||
|
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
findFirstValueOf(const MetadataMap& metadataMap, std::initializer_list<std::string> tags)
|
findFirstValueOf(const MetadataMap& metadataMap, std::initializer_list<std::string> tags)
|
||||||
{
|
{
|
||||||
auto it = std::find_first_of(std::cbegin(metadataMap), std::cend(metadataMap), std::cbegin(tags), std::cend(tags), [](const auto& it, const auto& str) { return it.first == str; });
|
auto it = std::find_first_of(std::cbegin(metadataMap), std::cend(metadataMap), std::cbegin(tags), std::cend(tags), [](const auto& it, const auto& str) { return it.first == str; });
|
||||||
if (it == std::cend(metadataMap))
|
if (it == std::cend(metadataMap))
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
return stringTrim(it->second);
|
return stringTrim(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
boost::optional<Album>
|
std::optional<Album>
|
||||||
getAlbum(const MetadataMap& metadataMap)
|
getAlbum(const MetadataMap& metadataMap)
|
||||||
{
|
{
|
||||||
boost::optional<Album> res;
|
std::optional<Album> res;
|
||||||
|
|
||||||
auto album {findFirstValueOf(metadataMap, {"ALBUM"})};
|
auto album {findFirstValueOf(metadataMap, {"ALBUM"})};
|
||||||
if (!album)
|
if (!album)
|
||||||
@@ -115,8 +115,8 @@ getArtists(const MetadataMap& metadataMap)
|
|||||||
return artists;
|
return artists;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Track>
|
std::optional<Track>
|
||||||
AvFormat::parse(const boost::filesystem::path& p, bool debug)
|
AvFormat::parse(const std::filesystem::path& p, bool debug)
|
||||||
{
|
{
|
||||||
Track track;
|
Track track;
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ AvFormat::parse(const boost::filesystem::path& p, bool debug)
|
|||||||
}
|
}
|
||||||
catch(Av::MediaFileException& e)
|
catch(Av::MediaFileException& e)
|
||||||
{
|
{
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return track;
|
return track;
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "MetaData.hpp"
|
#include "MetaData.hpp"
|
||||||
|
|
||||||
namespace MetaData
|
namespace MetaData
|
||||||
@@ -31,7 +28,7 @@ namespace MetaData
|
|||||||
class AvFormat : public Parser
|
class AvFormat : public Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
boost::optional<Track> parse(const boost::filesystem::path& p, bool debug = false) override;
|
std::optional<Track> parse(const std::filesystem::path& p, bool debug = false) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MetaData
|
} // namespace MetaData
|
||||||
|
|||||||
+10
-11
@@ -20,12 +20,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
namespace MetaData
|
namespace MetaData
|
||||||
{
|
{
|
||||||
using Clusters = std::map<std::string /* type */, std::set<std::string> /* names */>;
|
using Clusters = std::map<std::string /* type */, std::set<std::string> /* names */>;
|
||||||
@@ -54,15 +53,15 @@ namespace MetaData
|
|||||||
std::string title;
|
std::string title;
|
||||||
std::string musicBrainzTrackID;
|
std::string musicBrainzTrackID;
|
||||||
std::string musicBrainzRecordID;
|
std::string musicBrainzRecordID;
|
||||||
boost::optional<Album> album;
|
std::optional<Album> album;
|
||||||
Clusters clusters;
|
Clusters clusters;
|
||||||
std::chrono::milliseconds duration {};
|
std::chrono::milliseconds duration {};
|
||||||
boost::optional<std::size_t> trackNumber;
|
std::optional<std::size_t> trackNumber;
|
||||||
boost::optional<std::size_t> totalTrack;
|
std::optional<std::size_t> totalTrack;
|
||||||
boost::optional<std::size_t> discNumber;
|
std::optional<std::size_t> discNumber;
|
||||||
boost::optional<std::size_t> totalDisc;
|
std::optional<std::size_t> totalDisc;
|
||||||
boost::optional<int> year;
|
std::optional<int> year;
|
||||||
boost::optional<int> originalYear;
|
std::optional<int> originalYear;
|
||||||
bool hasCover {false};
|
bool hasCover {false};
|
||||||
std::vector<AudioStream> audioStreams;
|
std::vector<AudioStream> audioStreams;
|
||||||
std::string acoustID;
|
std::string acoustID;
|
||||||
@@ -73,7 +72,7 @@ namespace MetaData
|
|||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual boost::optional<Track> parse(const boost::filesystem::path& p, bool debug = false) = 0;
|
virtual std::optional<Track> parse(const std::filesystem::path& p, bool debug = false) = 0;
|
||||||
|
|
||||||
void setClusterTypeNames(const std::set<std::string>& clusterTypeNames) { _clusterTypeNames = clusterTypeNames; }
|
void setClusterTypeNames(const std::set<std::string>& clusterTypeNames) { _clusterTypeNames = clusterTypeNames; }
|
||||||
|
|
||||||
|
|||||||
@@ -126,10 +126,10 @@ getAlbumArtists(const TagLib::PropertyMap& properties)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
boost::optional<Album>
|
std::optional<Album>
|
||||||
getAlbum(const TagLib::PropertyMap& properties)
|
getAlbum(const TagLib::PropertyMap& properties)
|
||||||
{
|
{
|
||||||
boost::optional<Album> res;
|
std::optional<Album> res;
|
||||||
|
|
||||||
std::vector<std::string> albumName {getPropertyValues(properties, "ALBUM")};
|
std::vector<std::string> albumName {getPropertyValues(properties, "ALBUM")};
|
||||||
if (albumName.empty())
|
if (albumName.empty())
|
||||||
@@ -145,8 +145,8 @@ getAlbum(const TagLib::PropertyMap& properties)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Track>
|
std::optional<Track>
|
||||||
TagLibParser::parse(const boost::filesystem::path& p, bool debug)
|
TagLibParser::parse(const std::filesystem::path& p, bool debug)
|
||||||
{
|
{
|
||||||
TagLib::FileRef f {p.string().c_str(),
|
TagLib::FileRef f {p.string().c_str(),
|
||||||
true, // read audio properties
|
true, // read audio properties
|
||||||
@@ -155,13 +155,13 @@ TagLibParser::parse(const boost::filesystem::path& p, bool debug)
|
|||||||
if (f.isNull())
|
if (f.isNull())
|
||||||
{
|
{
|
||||||
LMS_LOG(METADATA, ERROR) << "File '" << p.string() << "': parsing failed";
|
LMS_LOG(METADATA, ERROR) << "File '" << p.string() << "': parsing failed";
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!f.audioProperties())
|
if (!f.audioProperties())
|
||||||
{
|
{
|
||||||
LMS_LOG(METADATA, INFO) << "File '" << p.string() << "': no audio properties";
|
LMS_LOG(METADATA, INFO) << "File '" << p.string() << "': no audio properties";
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
Track track;
|
Track track;
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "MetaData.hpp"
|
#include "MetaData.hpp"
|
||||||
|
|
||||||
namespace MetaData
|
namespace MetaData
|
||||||
@@ -31,7 +28,7 @@ namespace MetaData
|
|||||||
class TagLibParser : public Parser
|
class TagLibParser : public Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
boost::optional<Track> parse(const boost::filesystem::path& p, bool debug = false) override;
|
std::optional<Track> parse(const std::filesystem::path& p, bool debug = false) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MetaData
|
} // namespace MetaData
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/asio/placeholders.hpp>
|
#include <boost/asio/placeholders.hpp>
|
||||||
|
|
||||||
#include <Wt/WLocalDateTime.h>
|
#include <Wt/WLocalDateTime.h>
|
||||||
@@ -63,15 +62,15 @@ getNextFirstOfMonth(Wt::WDate current)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isFileSupported(const boost::filesystem::path& file, const std::set<boost::filesystem::path>& extensions)
|
isFileSupported(const std::filesystem::path& file, const std::set<std::filesystem::path>& extensions)
|
||||||
{
|
{
|
||||||
return (extensions.find(file.extension()) != extensions.end());
|
return (extensions.find(file.extension()) != extensions.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isPathInParentPath(const boost::filesystem::path& path, const boost::filesystem::path& parentPath)
|
isPathInParentPath(const std::filesystem::path& path, const std::filesystem::path& parentPath)
|
||||||
{
|
{
|
||||||
boost::filesystem::path curPath = path;
|
std::filesystem::path curPath = path;
|
||||||
|
|
||||||
while (curPath.has_parent_path())
|
while (curPath.has_parent_path())
|
||||||
{
|
{
|
||||||
@@ -327,25 +326,25 @@ MediaScanner::scheduleNextScan()
|
|||||||
void
|
void
|
||||||
MediaScanner::countAllFiles(Stats& stats)
|
MediaScanner::countAllFiles(Stats& stats)
|
||||||
{
|
{
|
||||||
boost::system::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
stats.totalFiles = 0;
|
stats.totalFiles = 0;
|
||||||
|
|
||||||
boost::filesystem::recursive_directory_iterator itPath {_mediaDirectory, ec};
|
std::filesystem::recursive_directory_iterator itPath {_mediaDirectory, ec};
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
LMS_LOG(DBUPDATER, ERROR) << "Cannot iterate over '" << _mediaDirectory.string() << "': " << ec.message();
|
LMS_LOG(DBUPDATER, ERROR) << "Cannot iterate over '" << _mediaDirectory.string() << "': " << ec.message();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::filesystem::recursive_directory_iterator itEnd;
|
std::filesystem::recursive_directory_iterator itEnd;
|
||||||
while (_running && itPath != itEnd)
|
while (_running && itPath != itEnd)
|
||||||
{
|
{
|
||||||
const boost::filesystem::path& path {*itPath};
|
const std::filesystem::path& path {*itPath};
|
||||||
|
|
||||||
if (!ec)
|
if (!ec)
|
||||||
{
|
{
|
||||||
if (boost::filesystem::is_regular(path) && isFileSupported(path, _fileExtensions))
|
if (std::filesystem::is_regular_file(path) && isFileSupported(path, _fileExtensions))
|
||||||
stats.totalFiles++;
|
stats.totalFiles++;
|
||||||
|
|
||||||
if (stats.totalFiles % 250 == 0)
|
if (stats.totalFiles % 250 == 0)
|
||||||
@@ -498,11 +497,11 @@ void MediaScanner::notifyInProgressIfNeeded(Stats& stats)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan, Stats& stats)
|
MediaScanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, Stats& stats)
|
||||||
{
|
{
|
||||||
notifyInProgressIfNeeded(stats);
|
notifyInProgressIfNeeded(stats);
|
||||||
|
|
||||||
auto lastWriteTime = Wt::WDateTime::fromTime_t(boost::filesystem::last_write_time(file));
|
const auto lastWriteTime {Wt::WDateTime::fromTimePoint(std::filesystem::last_write_time(file))};
|
||||||
|
|
||||||
if (!forceScan)
|
if (!forceScan)
|
||||||
{
|
{
|
||||||
@@ -518,7 +517,7 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<MetaData::Track> trackInfo {_metadataParser.parse(file)};
|
std::optional<MetaData::Track> trackInfo {_metadataParser.parse(file)};
|
||||||
if (!trackInfo)
|
if (!trackInfo)
|
||||||
{
|
{
|
||||||
stats.scanErrors++;
|
stats.scanErrors++;
|
||||||
@@ -642,11 +641,11 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaScanner::scanMediaDirectory(boost::filesystem::path mediaDirectory, bool forceScan, Stats& stats)
|
MediaScanner::scanMediaDirectory(const std::filesystem::path& mediaDirectory, bool forceScan, Stats& stats)
|
||||||
{
|
{
|
||||||
boost::system::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
boost::filesystem::recursive_directory_iterator itPath(mediaDirectory, ec);
|
std::filesystem::recursive_directory_iterator itPath {mediaDirectory, ec};
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
LMS_LOG(DBUPDATER, ERROR) << "Cannot iterate over '" << mediaDirectory.string() << "': " << ec.message();
|
LMS_LOG(DBUPDATER, ERROR) << "Cannot iterate over '" << mediaDirectory.string() << "': " << ec.message();
|
||||||
@@ -654,17 +653,17 @@ MediaScanner::scanMediaDirectory(boost::filesystem::path mediaDirectory, bool fo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::filesystem::recursive_directory_iterator itEnd;
|
std::filesystem::recursive_directory_iterator itEnd;
|
||||||
while (_running && itPath != itEnd)
|
while (_running && itPath != itEnd)
|
||||||
{
|
{
|
||||||
const boost::filesystem::path& path {*itPath};
|
const std::filesystem::path& path {*itPath};
|
||||||
|
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
LMS_LOG(DBUPDATER, ERROR) << "Cannot process entry '" << path.string() << "': " << ec.message();
|
LMS_LOG(DBUPDATER, ERROR) << "Cannot process entry '" << path.string() << "': " << ec.message();
|
||||||
stats.scanErrors++;
|
stats.scanErrors++;
|
||||||
}
|
}
|
||||||
else if (boost::filesystem::is_regular(path))
|
else if (std::filesystem::is_regular_file(path))
|
||||||
{
|
{
|
||||||
if (isFileSupported(path, _fileExtensions))
|
if (isFileSupported(path, _fileExtensions))
|
||||||
scanAudioFile(path, forceScan, stats );
|
scanAudioFile(path, forceScan, stats );
|
||||||
@@ -678,14 +677,14 @@ MediaScanner::scanMediaDirectory(boost::filesystem::path mediaDirectory, bool fo
|
|||||||
|
|
||||||
// Check if a file exists and is still in a media directory
|
// Check if a file exists and is still in a media directory
|
||||||
static bool
|
static bool
|
||||||
checkFile(const boost::filesystem::path& p, const boost::filesystem::path& mediaDirectory, const std::set<boost::filesystem::path>& extensions)
|
checkFile(const std::filesystem::path& p, const std::filesystem::path& mediaDirectory, const std::set<std::filesystem::path>& extensions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// For each track, make sure the the file still exists
|
// For each track, make sure the the file still exists
|
||||||
// and still belongs to a media directory
|
// and still belongs to a media directory
|
||||||
if (!boost::filesystem::exists( p )
|
if (!std::filesystem::exists( p )
|
||||||
|| !boost::filesystem::is_regular( p ) )
|
|| !std::filesystem::is_regular_file( p ) )
|
||||||
{
|
{
|
||||||
LMS_LOG(DBUPDATER, INFO) << "Removing '" << p.string() << "': missing";
|
LMS_LOG(DBUPDATER, INFO) << "Removing '" << p.string() << "': missing";
|
||||||
return false;
|
return false;
|
||||||
@@ -706,7 +705,7 @@ checkFile(const boost::filesystem::path& p, const boost::filesystem::path& media
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (boost::filesystem::filesystem_error& e)
|
catch (std::filesystem::filesystem_error& e)
|
||||||
{
|
{
|
||||||
LMS_LOG(DBUPDATER, ERROR) << "Caught exception while checking file '" << p.string() << "': " << e.what();
|
LMS_LOG(DBUPDATER, ERROR) << "Caught exception while checking file '" << p.string() << "': " << e.what();
|
||||||
return false;
|
return false;
|
||||||
@@ -716,7 +715,7 @@ checkFile(const boost::filesystem::path& p, const boost::filesystem::path& media
|
|||||||
void
|
void
|
||||||
MediaScanner::removeMissingTracks(Stats& stats)
|
MediaScanner::removeMissingTracks(Stats& stats)
|
||||||
{
|
{
|
||||||
std::vector<boost::filesystem::path> trackPaths;
|
std::vector<std::filesystem::path> trackPaths;
|
||||||
{
|
{
|
||||||
auto transaction {_dbSession->createSharedTransaction()};
|
auto transaction {_dbSession->createSharedTransaction()};
|
||||||
trackPaths = Track::getAllPaths(*_dbSession);;
|
trackPaths = Track::getAllPaths(*_dbSession);;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
#include <Wt/WIOService.h>
|
#include <Wt/WIOService.h>
|
||||||
@@ -86,8 +87,8 @@ class MediaScanner
|
|||||||
{
|
{
|
||||||
State currentState {State::NotScheduled};
|
State currentState {State::NotScheduled};
|
||||||
Wt::WDateTime nextScheduledScan;
|
Wt::WDateTime nextScheduledScan;
|
||||||
boost::optional<Stats> lastScanStats;
|
std::optional<Stats> lastScanStats;
|
||||||
boost::optional<Stats> inProgressStats;
|
std::optional<Stats> inProgressStats;
|
||||||
};
|
};
|
||||||
|
|
||||||
Status getStatus();
|
Status getStatus();
|
||||||
@@ -110,7 +111,7 @@ class MediaScanner
|
|||||||
// Update database (scheduled callback)
|
// Update database (scheduled callback)
|
||||||
void scan(boost::system::error_code ec);
|
void scan(boost::system::error_code ec);
|
||||||
|
|
||||||
void scanMediaDirectory( boost::filesystem::path mediaDirectory, bool forceScan, Stats& stats);
|
void scanMediaDirectory( const std::filesystem::path& mediaDirectory, bool forceScan, Stats& stats);
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
void refreshScanSettings();
|
void refreshScanSettings();
|
||||||
@@ -119,8 +120,8 @@ class MediaScanner
|
|||||||
void removeMissingTracks(Stats& stats);
|
void removeMissingTracks(Stats& stats);
|
||||||
void removeOrphanEntries();
|
void removeOrphanEntries();
|
||||||
void checkDuplicatedAudioFiles(Stats& stats);
|
void checkDuplicatedAudioFiles(Stats& stats);
|
||||||
void scanAudioFile(const boost::filesystem::path& file, bool forceScan, Stats& stats);
|
void scanAudioFile(const std::filesystem::path& file, bool forceScan, Stats& stats);
|
||||||
Database::IdType doScanAudioFile(const boost::filesystem::path& file, Stats& stats);
|
Database::IdType doScanAudioFile(const std::filesystem::path& file, Stats& stats);
|
||||||
void notifyInProgressIfNeeded(Stats& stats);
|
void notifyInProgressIfNeeded(Stats& stats);
|
||||||
void notifyInProgress(Stats& stats);
|
void notifyInProgress(Stats& stats);
|
||||||
|
|
||||||
@@ -137,16 +138,16 @@ class MediaScanner
|
|||||||
|
|
||||||
std::mutex _statusMutex;
|
std::mutex _statusMutex;
|
||||||
State _curState {State::NotScheduled};
|
State _curState {State::NotScheduled};
|
||||||
boost::optional<Stats> _inProgressStats;
|
std::optional<Stats> _inProgressStats;
|
||||||
boost::optional<Stats> _lastScanStats;
|
std::optional<Stats> _lastScanStats;
|
||||||
Wt::WDateTime _nextScheduledScan;
|
Wt::WDateTime _nextScheduledScan;
|
||||||
|
|
||||||
// Current scan settings
|
// Current scan settings
|
||||||
std::size_t _scanVersion {};
|
std::size_t _scanVersion {};
|
||||||
Wt::WTime _startTime;
|
Wt::WTime _startTime;
|
||||||
Database::ScanSettings::UpdatePeriod _updatePeriod {Database::ScanSettings::UpdatePeriod::Never};
|
Database::ScanSettings::UpdatePeriod _updatePeriod {Database::ScanSettings::UpdatePeriod::Never};
|
||||||
std::set<boost::filesystem::path> _fileExtensions;
|
std::set<std::filesystem::path> _fileExtensions;
|
||||||
boost::filesystem::path _mediaDirectory;
|
std::filesystem::path _mediaDirectory;
|
||||||
|
|
||||||
|
|
||||||
}; // class MediaScanner
|
}; // class MediaScanner
|
||||||
|
|||||||
@@ -31,24 +31,24 @@ namespace Similarity {
|
|||||||
|
|
||||||
|
|
||||||
static
|
static
|
||||||
boost::filesystem::path getCacheDirectory()
|
std::filesystem::path getCacheDirectory()
|
||||||
{
|
{
|
||||||
return getService<Config>()->getPath("working-dir") / "cache" / "features";
|
return getService<Config>()->getPath("working-dir") / "cache" / "features";
|
||||||
}
|
}
|
||||||
|
|
||||||
static boost::filesystem::path getCacheNetworkFilePath()
|
static std::filesystem::path getCacheNetworkFilePath()
|
||||||
{
|
{
|
||||||
return getCacheDirectory() / "network";
|
return getCacheDirectory() / "network";
|
||||||
};
|
};
|
||||||
|
|
||||||
static boost::filesystem::path getCacheTrackPositionsFilePath()
|
static std::filesystem::path getCacheTrackPositionsFilePath()
|
||||||
{
|
{
|
||||||
return getCacheDirectory() / "track_positions";
|
return getCacheDirectory() / "track_positions";
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
bool
|
bool
|
||||||
networkToCacheFile(const SOM::Network& network, boost::filesystem::path path)
|
networkToCacheFile(const SOM::Network& network, std::filesystem::path path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -91,8 +91,8 @@ networkToCacheFile(const SOM::Network& network, boost::filesystem::path path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
boost::optional<SOM::Network>
|
std::optional<SOM::Network>
|
||||||
createNetworkFromCacheFile(boost::filesystem::path path)
|
createNetworkFromCacheFile(std::filesystem::path path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -137,13 +137,13 @@ createNetworkFromCacheFile(boost::filesystem::path path)
|
|||||||
catch (boost::property_tree::ptree_error& error)
|
catch (boost::property_tree::ptree_error& error)
|
||||||
{
|
{
|
||||||
LMS_LOG(SIMILARITY, ERROR) << "Cannot read network cache: " << error.what();
|
LMS_LOG(SIMILARITY, ERROR) << "Cannot read network cache: " << error.what();
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
bool
|
bool
|
||||||
objectPositionToCacheFile(const std::map<Database::IdType, std::set<SOM::Position>>& objectsPosition, boost::filesystem::path path)
|
objectPositionToCacheFile(const std::map<Database::IdType, std::set<SOM::Position>>& objectsPosition, std::filesystem::path path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -178,8 +178,8 @@ objectPositionToCacheFile(const std::map<Database::IdType, std::set<SOM::Positio
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
boost::optional<std::map<Database::IdType, std::set<SOM::Position>>>
|
std::optional<std::map<Database::IdType, std::set<SOM::Position>>>
|
||||||
createObjectPositionsFromCacheFile(boost::filesystem::path path)
|
createObjectPositionsFromCacheFile(std::filesystem::path path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -210,21 +210,21 @@ createObjectPositionsFromCacheFile(boost::filesystem::path path)
|
|||||||
catch (boost::property_tree::ptree_error& error)
|
catch (boost::property_tree::ptree_error& error)
|
||||||
{
|
{
|
||||||
LMS_LOG(SIMILARITY, ERROR) << "Cannot create object position from cache file: " << error.what();
|
LMS_LOG(SIMILARITY, ERROR) << "Cannot create object position from cache file: " << error.what();
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FeaturesCache::invalidate()
|
FeaturesCache::invalidate()
|
||||||
{
|
{
|
||||||
boost::filesystem::remove(getCacheNetworkFilePath());
|
std::filesystem::remove(getCacheNetworkFilePath());
|
||||||
boost::filesystem::remove(getCacheTrackPositionsFilePath());
|
std::filesystem::remove(getCacheTrackPositionsFilePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<FeaturesCache>
|
std::optional<FeaturesCache>
|
||||||
FeaturesCache::read()
|
FeaturesCache::read()
|
||||||
{
|
{
|
||||||
boost::optional<FeaturesCache> res;
|
std::optional<FeaturesCache> res;
|
||||||
|
|
||||||
auto network{createNetworkFromCacheFile(getCacheNetworkFilePath())};
|
auto network{createNetworkFromCacheFile(getCacheNetworkFilePath())};
|
||||||
if (!network)
|
if (!network)
|
||||||
@@ -240,7 +240,7 @@ FeaturesCache::read()
|
|||||||
void
|
void
|
||||||
FeaturesCache::write()
|
FeaturesCache::write()
|
||||||
{
|
{
|
||||||
boost::filesystem::create_directories(getService<Config>()->getPath("working-dir") / "cache" / "features");
|
std::filesystem::create_directories(getService<Config>()->getPath("working-dir") / "cache" / "features");
|
||||||
|
|
||||||
if (!networkToCacheFile(_network, getCacheNetworkFilePath())
|
if (!networkToCacheFile(_network, getCacheNetworkFilePath())
|
||||||
|| !objectPositionToCacheFile(_trackPositions, getCacheTrackPositionsFilePath()))
|
|| !objectPositionToCacheFile(_trackPositions, getCacheTrackPositionsFilePath()))
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
@@ -33,7 +34,7 @@ class FeaturesCache
|
|||||||
|
|
||||||
static void invalidate();
|
static void invalidate();
|
||||||
|
|
||||||
static boost::optional<FeaturesCache> read();
|
static std::optional<FeaturesCache> read();
|
||||||
void write();
|
void write();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ getTracksWithMBIDAndMissingFeatures(Database::Session& dbSession)
|
|||||||
FeaturesScannerAddon::FeaturesScannerAddon(std::unique_ptr<Database::Session> dbSession)
|
FeaturesScannerAddon::FeaturesScannerAddon(std::unique_ptr<Database::Session> dbSession)
|
||||||
: _dbSession {std::move(dbSession)}
|
: _dbSession {std::move(dbSession)}
|
||||||
{
|
{
|
||||||
boost::optional<Similarity::FeaturesCache> cache {Similarity::FeaturesCache::read()};
|
std::optional<Similarity::FeaturesCache> cache {Similarity::FeaturesCache::read()};
|
||||||
if (cache)
|
if (cache)
|
||||||
{
|
{
|
||||||
auto searcher {std::make_shared<Similarity::FeaturesSearcher>(*_dbSession.get(), *cache, [&]() { return _stopRequested; })};
|
auto searcher {std::make_shared<Similarity::FeaturesSearcher>(*_dbSession.get(), *cache, [&]() { return _stopRequested; })};
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ class FeaturesScannerAddon final : public Scanner::MediaScannerAddon
|
|||||||
void requestStop() override;
|
void requestStop() override;
|
||||||
void preScanComplete() override;
|
void preScanComplete() override;
|
||||||
|
|
||||||
void trackAdded(Database::IdType trackId) override {}
|
void trackAdded(Database::IdType) override {}
|
||||||
void trackToRemove(Database::IdType trackId) override {}
|
void trackToRemove(Database::IdType) override {}
|
||||||
void trackUpdated(Database::IdType trackId) override;
|
void trackUpdated(Database::IdType trackId) override;
|
||||||
|
|
||||||
bool fetchFeatures(Database::IdType trackId, const std::string& MBID);
|
bool fetchFeatures(Database::IdType trackId, const std::string& MBID);
|
||||||
|
|||||||
@@ -68,10 +68,10 @@ getFeatureInfoMapNbDimensions(const FeatureInfoMap& featureInfoMap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
boost::optional<SOM::InputVector>
|
std::optional<SOM::InputVector>
|
||||||
getInputVectorFromTrack(Database::Session& session, Database::IdType trackId, const FeatureInfoMap& featuresInfo, std::size_t nbDimensions)
|
getInputVectorFromTrack(Database::Session& session, Database::IdType trackId, const FeatureInfoMap& featuresInfo, std::size_t nbDimensions)
|
||||||
{
|
{
|
||||||
boost::optional<SOM::InputVector> res {SOM::InputVector {nbDimensions}};
|
std::optional<SOM::InputVector> res {SOM::InputVector {nbDimensions}};
|
||||||
|
|
||||||
std::map<std::string, std::vector<double>> features;
|
std::map<std::string, std::vector<double>> features;
|
||||||
for (auto itFeatureInfo : featuresInfo)
|
for (auto itFeatureInfo : featuresInfo)
|
||||||
@@ -150,7 +150,7 @@ FeaturesSearcher::FeaturesSearcher(Database::Session& session, std::function<boo
|
|||||||
if (stopRequested())
|
if (stopRequested())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
boost::optional<SOM::InputVector> inputVector {getInputVectorFromTrack(session, trackId, featuresInfo, nbDimensions)};
|
std::optional<SOM::InputVector> inputVector {getInputVectorFromTrack(session, trackId, featuresInfo, nbDimensions)};
|
||||||
if (!inputVector)
|
if (!inputVector)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -456,7 +456,7 @@ FeaturesSearcher::getSimilarObjects(const std::set<Database::IdType>& ids,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// If there is not enough objects, try again with closest neighbour until there is too much distance
|
// If there is not enough objects, try again with closest neighbour until there is too much distance
|
||||||
boost::optional<SOM::Position> closestRefVectorPosition {_network->getClosestRefVectorPosition(searchedRefVectorsPosition, _networkRefVectorsDistanceMedian * 0.75)};
|
std::optional<SOM::Position> closestRefVectorPosition {_network->getClosestRefVectorPosition(searchedRefVectorsPosition, _networkRefVectorsDistanceMedian * 0.75)};
|
||||||
if (!closestRefVectorPosition)
|
if (!closestRefVectorPosition)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -189,10 +189,10 @@ Network::getClosestRefVectorPosition(const InputVector& data) const
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Position>
|
std::optional<Position>
|
||||||
Network::getClosestRefVectorPosition(const InputVector& data, InputVector::Distance maxDistance) const
|
Network::getClosestRefVectorPosition(const InputVector& data, InputVector::Distance maxDistance) const
|
||||||
{
|
{
|
||||||
boost::optional<Position> position {getClosestRefVectorPosition(data)};
|
std::optional<Position> position {getClosestRefVectorPosition(data)};
|
||||||
|
|
||||||
if (_distanceFunc(data, _refVectors.get(*position), _weights) > maxDistance)
|
if (_distanceFunc(data, _refVectors.get(*position), _weights) > maxDistance)
|
||||||
position.reset();
|
position.reset();
|
||||||
@@ -200,7 +200,7 @@ Network::getClosestRefVectorPosition(const InputVector& data, InputVector::Dista
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Position>
|
std::optional<Position>
|
||||||
Network::getClosestRefVectorPosition(const std::set<Position>& refVectorsPosition, InputVector::Distance maxDistance) const
|
Network::getClosestRefVectorPosition(const std::set<Position>& refVectorsPosition, InputVector::Distance maxDistance) const
|
||||||
{
|
{
|
||||||
std::set<Position> neighboursPosition;
|
std::set<Position> neighboursPosition;
|
||||||
@@ -221,7 +221,7 @@ Network::getClosestRefVectorPosition(const std::set<Position>& refVectorsPositio
|
|||||||
neighboursPosition.erase(refVectorPosition);
|
neighboursPosition.erase(refVectorPosition);
|
||||||
|
|
||||||
if (neighboursPosition.empty())
|
if (neighboursPosition.empty())
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
// Now compute the distance for each neighbour
|
// Now compute the distance for each neighbour
|
||||||
struct NeighbourInfo
|
struct NeighbourInfo
|
||||||
@@ -247,9 +247,9 @@ Network::getClosestRefVectorPosition(const std::set<Position>& refVectorsPositio
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (neighboursInfo.empty())
|
if (neighboursInfo.empty())
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
auto min {std::min_element(neighboursInfo.begin(), neighboursInfo.end(),
|
auto min {std::min_element(std::cbegin(neighboursInfo), std::cend(neighboursInfo),
|
||||||
[&](const auto& a, const auto& b)
|
[&](const auto& a, const auto& b)
|
||||||
{
|
{
|
||||||
return a.distance < b.distance;
|
return a.distance < b.distance;
|
||||||
|
|||||||
@@ -21,11 +21,10 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include "utils/Exception.hpp"
|
#include "utils/Exception.hpp"
|
||||||
#include "InputVector.hpp"
|
#include "InputVector.hpp"
|
||||||
#include "Matrix.hpp"
|
#include "Matrix.hpp"
|
||||||
@@ -70,9 +69,9 @@ class Network
|
|||||||
|
|
||||||
const InputVector& getRefVector(const Position& position) const;
|
const InputVector& getRefVector(const Position& position) const;
|
||||||
Position getClosestRefVectorPosition(const InputVector& data) const;
|
Position getClosestRefVectorPosition(const InputVector& data) const;
|
||||||
boost::optional<Position> getClosestRefVectorPosition(const InputVector& data, InputVector::Distance maxDistance) const;
|
std::optional<Position> getClosestRefVectorPosition(const InputVector& data, InputVector::Distance maxDistance) const;
|
||||||
|
|
||||||
boost::optional<Position> getClosestRefVectorPosition(const std::set<Position>& refVectorsPosition, InputVector::Distance maxDistance) const;
|
std::optional<Position> getClosestRefVectorPosition(const std::set<Position>& refVectorsPosition, InputVector::Distance maxDistance) const;
|
||||||
|
|
||||||
InputVector::Distance getRefVectorsDistance(const Position& position1, const Position& position2) const;
|
InputVector::Distance getRefVectorsDistance(const Position& position1, const Position& position2) const;
|
||||||
|
|
||||||
|
|||||||
+5
-6
@@ -54,12 +54,12 @@ createAuthToken(Database::IdType userId, const Wt::WDateTime& expiry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
boost::optional<Database::IdType>
|
std::optional<Database::IdType>
|
||||||
processAuthToken(const Wt::WEnvironment& env)
|
processAuthToken(const Wt::WEnvironment& env)
|
||||||
{
|
{
|
||||||
const std::string* authCookie {env.getCookie(authCookieName)};
|
const std::string* authCookie {env.getCookie(authCookieName)};
|
||||||
if (!authCookie)
|
if (!authCookie)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
const auto res {getService<::Auth::AuthTokenService>()->processAuthToken(LmsApp->getDbSession(), boost::asio::ip::address::from_string(env.clientAddress()), *authCookie)};
|
const auto res {getService<::Auth::AuthTokenService>()->processAuthToken(LmsApp->getDbSession(), boost::asio::ip::address::from_string(env.clientAddress()), *authCookie)};
|
||||||
switch (res.state)
|
switch (res.state)
|
||||||
@@ -67,7 +67,7 @@ processAuthToken(const Wt::WEnvironment& env)
|
|||||||
case ::Auth::AuthTokenService::AuthTokenProcessResult::State::NotFound:
|
case ::Auth::AuthTokenService::AuthTokenProcessResult::State::NotFound:
|
||||||
case ::Auth::AuthTokenService::AuthTokenProcessResult::State::Throttled:
|
case ::Auth::AuthTokenService::AuthTokenProcessResult::State::Throttled:
|
||||||
LmsApp->setCookie(authCookieName, std::string {}, 0, "", "", env.urlScheme() == "https");
|
LmsApp->setCookie(authCookieName, std::string {}, 0, "", "", env.urlScheme() == "https");
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
case ::Auth::AuthTokenService::AuthTokenProcessResult::State::Found:
|
case ::Auth::AuthTokenService::AuthTokenProcessResult::State::Found:
|
||||||
createAuthToken(res.authTokenInfo->userId, res.authTokenInfo->expiry);
|
createAuthToken(res.authTokenInfo->userId, res.authTokenInfo->expiry);
|
||||||
@@ -107,7 +107,6 @@ class AuthModel : public Wt::WFormModel
|
|||||||
user.modify()->setLastLogin(Wt::WDateTime::currentDateTime());
|
user.modify()->setLastLogin(Wt::WDateTime::currentDateTime());
|
||||||
_userId = user.id();
|
_userId = user.id();
|
||||||
|
|
||||||
const Wt::WDateTime now {Wt::WDateTime::currentDateTime()};
|
|
||||||
isDemo = user->isDemo();
|
isDemo = user->isDemo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,11 +150,11 @@ class AuthModel : public Wt::WFormModel
|
|||||||
return (validation(field).state() == Wt::ValidationState::Valid);
|
return (validation(field).state() == Wt::ValidationState::Valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<Database::IdType> getUserId() const { return _userId; }
|
std::optional<Database::IdType> getUserId() const { return _userId; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
boost::optional<Database::IdType> _userId;
|
std::optional<Database::IdType> _userId;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AuthModel::Field AuthModel::LoginNameField {"login-name"};
|
const AuthModel::Field AuthModel::LoginNameField {"login-name"};
|
||||||
|
|||||||
+1
-3
@@ -19,15 +19,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WTemplateFormView.h>
|
#include <Wt/WTemplateFormView.h>
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
boost::optional<Database::IdType>
|
std::optional<Database::IdType>
|
||||||
processAuthToken(const Wt::WEnvironment& env);
|
processAuthToken(const Wt::WEnvironment& env);
|
||||||
|
|
||||||
class Auth : public Wt::WTemplateFormView
|
class Auth : public Wt::WTemplateFormView
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "cover/CoverArtGrabber.hpp"
|
#include "cover/CoverArtGrabber.hpp"
|
||||||
#include "database/Artist.hpp"
|
#include "database/Artist.hpp"
|
||||||
#include "database/Cluster.hpp"
|
#include "database/Cluster.hpp"
|
||||||
|
#include "database/Db.hpp"
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/User.hpp"
|
||||||
#include "explore/Explore.hpp"
|
#include "explore/Explore.hpp"
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
std::unique_ptr<Wt::WApplication>
|
std::unique_ptr<Wt::WApplication>
|
||||||
LmsApplication::create(const Wt::WEnvironment& env, Database::Database& db, LmsApplicationGroupContainer& appGroups)
|
LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups)
|
||||||
{
|
{
|
||||||
return std::make_unique<LmsApplication>(env, db.createSession(), appGroups);
|
return std::make_unique<LmsApplication>(env, db.createSession(), appGroups);
|
||||||
}
|
}
|
||||||
@@ -496,13 +497,13 @@ LmsApplication::createHome()
|
|||||||
playqueue->playNext();
|
playqueue->playNext();
|
||||||
});
|
});
|
||||||
|
|
||||||
playqueue->loadTrack.connect([=] (Database::IdType trackId, bool play)
|
playqueue->trackSelected.connect([=] (Database::IdType trackId, bool play)
|
||||||
{
|
{
|
||||||
_events.lastLoadedTrackId = trackId;
|
_events.lastLoadedTrackId = trackId;
|
||||||
_events.trackLoaded(trackId, play);
|
_events.trackLoaded(trackId, play);
|
||||||
});
|
});
|
||||||
|
|
||||||
playqueue->trackUnload.connect([=]
|
playqueue->trackUnselected.connect([=]
|
||||||
{
|
{
|
||||||
_events.lastLoadedTrackId.reset();
|
_events.lastLoadedTrackId.reset();
|
||||||
_events.trackUnloaded();
|
_events.trackUnloaded();
|
||||||
@@ -555,7 +556,7 @@ LmsApplication::createHome()
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Events from Application group
|
// Events from Application group
|
||||||
_events.appOpen.connect([=] (LmsApplicationInfo info)
|
_events.appOpen.connect([=] (LmsApplicationInfo)
|
||||||
{
|
{
|
||||||
// Only one active session by user
|
// Only one active session by user
|
||||||
if (!LmsApp->isUserDemo())
|
if (!LmsApp->isUserDemo())
|
||||||
@@ -565,10 +566,10 @@ LmsApplication::createHome()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
internalPathChanged().connect(std::bind([=]
|
internalPathChanged().connect([=]
|
||||||
{
|
{
|
||||||
handlePathChange(mainStack, isUserAdmin());
|
handlePathChange(mainStack, isUserAdmin());
|
||||||
}));
|
});
|
||||||
|
|
||||||
handlePathChange(mainStack, isUserAdmin());
|
handlePathChange(mainStack, isUserAdmin());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,12 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LMS_APPLICATION_HPP
|
#pragma once
|
||||||
#define LMS_APPLICATION_HPP
|
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WApplication.h>
|
#include <Wt/WApplication.h>
|
||||||
|
|
||||||
#include "database/Database.hpp"
|
|
||||||
#include "scanner/MediaScanner.hpp"
|
#include "scanner/MediaScanner.hpp"
|
||||||
|
|
||||||
#include "LmsApplicationGroup.hpp"
|
#include "LmsApplicationGroup.hpp"
|
||||||
@@ -32,6 +30,7 @@
|
|||||||
namespace Database {
|
namespace Database {
|
||||||
class Artist;
|
class Artist;
|
||||||
class Cluster;
|
class Cluster;
|
||||||
|
class Db;
|
||||||
class Release;
|
class Release;
|
||||||
class User;
|
class User;
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,7 @@ struct Events
|
|||||||
|
|
||||||
// A track is being loaded
|
// A track is being loaded
|
||||||
Wt::Signal<Database::IdType /* trackId */, bool /* play */> trackLoaded;
|
Wt::Signal<Database::IdType /* trackId */, bool /* play */> trackLoaded;
|
||||||
boost::optional<Database::IdType> lastLoadedTrackId;
|
std::optional<Database::IdType> lastLoadedTrackId;
|
||||||
// Unload current track
|
// Unload current track
|
||||||
Wt::Signal<> trackUnloaded;
|
Wt::Signal<> trackUnloaded;
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ class LmsApplication : public Wt::WApplication
|
|||||||
public:
|
public:
|
||||||
LmsApplication(const Wt::WEnvironment& env, std::unique_ptr<Database::Session> dbSession, LmsApplicationGroupContainer& appGroups);
|
LmsApplication(const Wt::WEnvironment& env, std::unique_ptr<Database::Session> dbSession, LmsApplicationGroupContainer& appGroups);
|
||||||
|
|
||||||
static std::unique_ptr<Wt::WApplication> create(const Wt::WEnvironment& env, Database::Database& db, LmsApplicationGroupContainer& appGroups);
|
static std::unique_ptr<Wt::WApplication> create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups);
|
||||||
static LmsApplication* instance();
|
static LmsApplication* instance();
|
||||||
|
|
||||||
// Session application data
|
// Session application data
|
||||||
@@ -124,8 +123,8 @@ class LmsApplication : public Wt::WApplication
|
|||||||
std::unique_ptr<Database::Session> _dbSession;
|
std::unique_ptr<Database::Session> _dbSession;
|
||||||
LmsApplicationGroupContainer& _appGroups;
|
LmsApplicationGroupContainer& _appGroups;
|
||||||
Events _events;
|
Events _events;
|
||||||
boost::optional<Database::IdType> _userId;
|
std::optional<Database::IdType> _userId;
|
||||||
boost::optional<bool> _userAuthStrong;
|
std::optional<bool> _userAuthStrong;
|
||||||
std::shared_ptr<ImageResource> _imageResource;
|
std::shared_ptr<ImageResource> _imageResource;
|
||||||
std::shared_ptr<AudioResource> _audioResource;
|
std::shared_ptr<AudioResource> _audioResource;
|
||||||
};
|
};
|
||||||
@@ -136,5 +135,3 @@ class LmsApplication : public Wt::WApplication
|
|||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace UserInterface {
|
|||||||
LmsApplicationInfo
|
LmsApplicationInfo
|
||||||
LmsApplicationInfo::fromEnvironment(const Wt::WEnvironment& env)
|
LmsApplicationInfo::fromEnvironment(const Wt::WEnvironment& env)
|
||||||
{
|
{
|
||||||
LmsApplicationInfo info = {.userAgent = wApp->environment().agent()};
|
LmsApplicationInfo info = {.userAgent = env.agent()};
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ PlayQueue::PlayQueue()
|
|||||||
trackPos = LmsApp->getUser()->getCurPlayingTrackPos();
|
trackPos = LmsApp->getUser()->getCurPlayingTrackPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
load(trackPos, false);
|
loadTrack(trackPos, false);
|
||||||
});
|
});
|
||||||
trackList = LmsApp->getUser()->getQueuedTrackList(LmsApp->getDbSession());
|
trackList = LmsApp->getUser()->getQueuedTrackList(LmsApp->getDbSession());
|
||||||
}
|
}
|
||||||
@@ -192,11 +192,11 @@ PlayQueue::stop()
|
|||||||
{
|
{
|
||||||
updateCurrentTrack(false);
|
updateCurrentTrack(false);
|
||||||
_trackPos.reset();
|
_trackPos.reset();
|
||||||
trackUnload.emit();
|
trackUnselected.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PlayQueue::load(std::size_t pos, bool play)
|
PlayQueue::loadTrack(std::size_t pos, bool play)
|
||||||
{
|
{
|
||||||
updateCurrentTrack(false);
|
updateCurrentTrack(false);
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ PlayQueue::load(std::size_t pos, bool play)
|
|||||||
|
|
||||||
updateCurrentTrack(true);
|
updateCurrentTrack(true);
|
||||||
|
|
||||||
loadTrack.emit(trackId, play);
|
trackSelected.emit(trackId, play);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -249,7 +249,7 @@ PlayQueue::playPrevious()
|
|||||||
if (*_trackPos == 0)
|
if (*_trackPos == 0)
|
||||||
stop();
|
stop();
|
||||||
else
|
else
|
||||||
load(*_trackPos - 1, true);
|
loadTrack(*_trackPos - 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -257,11 +257,11 @@ PlayQueue::playNext()
|
|||||||
{
|
{
|
||||||
if (!_trackPos)
|
if (!_trackPos)
|
||||||
{
|
{
|
||||||
load(0, true);
|
loadTrack(0, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
load(*_trackPos + 1, true);
|
loadTrack(*_trackPos + 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -327,7 +327,7 @@ PlayQueue::playTracks(const std::vector<Database::IdType>& trackIds)
|
|||||||
{
|
{
|
||||||
clearTracks();
|
clearTracks();
|
||||||
enqueueTracks(trackIds);
|
enqueueTracks(trackIds);
|
||||||
load(0, true);
|
loadTrack(0, true);
|
||||||
|
|
||||||
LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-playing", trackIds.size()).arg(trackIds.size()), std::chrono::milliseconds(2000));
|
LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-playing", trackIds.size()).arg(trackIds.size()), std::chrono::milliseconds(2000));
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ PlayQueue::addSome()
|
|||||||
{
|
{
|
||||||
auto pos = _entriesContainer->indexOf(entry);
|
auto pos = _entriesContainer->indexOf(entry);
|
||||||
if (pos >= 0)
|
if (pos >= 0)
|
||||||
load(pos, true);
|
loadTrack(pos, true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Wt::WText* delBtn = entry->bindNew<Wt::WText>("del-btn", Wt::WString::tr("Lms.PlayQueue.template.delete-btn"), Wt::TextFormat::XHTML);
|
Wt::WText* delBtn = entry->bindNew<Wt::WText>("del-btn", Wt::WString::tr("Lms.PlayQueue.template.delete-btn"), Wt::TextFormat::XHTML);
|
||||||
|
|||||||
@@ -19,13 +19,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WContainerWidget.h>
|
#include <Wt/WContainerWidget.h>
|
||||||
#include <Wt/WPushButton.h>
|
#include <Wt/WPushButton.h>
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
#include <Wt/WText.h>
|
#include <Wt/WText.h>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
namespace Similarity {
|
namespace Similarity {
|
||||||
@@ -53,10 +53,10 @@ class PlayQueue : public Wt::WTemplate
|
|||||||
void playPrevious();
|
void playPrevious();
|
||||||
|
|
||||||
// Signal emitted when a track is to be load(and optionally played)
|
// Signal emitted when a track is to be load(and optionally played)
|
||||||
Wt::Signal<Database::IdType /*trackId*/, bool /*play*/> loadTrack;
|
Wt::Signal<Database::IdType /*trackId*/, bool /*play*/> trackSelected;
|
||||||
|
|
||||||
// Signal emitted when play has to be stopped
|
// Signal emitted when track is unselected (has to be stopped)
|
||||||
Wt::Signal<> trackUnload;
|
Wt::Signal<> trackUnselected;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Wt::Dbo::ptr<Database::TrackList> getTrackList();
|
Wt::Dbo::ptr<Database::TrackList> getTrackList();
|
||||||
@@ -71,7 +71,7 @@ class PlayQueue : public Wt::WTemplate
|
|||||||
void updateRepeatBtn();
|
void updateRepeatBtn();
|
||||||
void updateRadioBtn();
|
void updateRadioBtn();
|
||||||
|
|
||||||
void load(std::size_t pos, bool play);
|
void loadTrack(std::size_t pos, bool play);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void addRadioTrackFromSimilarity(std::shared_ptr<Similarity::Finder> similarityFinder);
|
void addRadioTrackFromSimilarity(std::shared_ptr<Similarity::Finder> similarityFinder);
|
||||||
@@ -85,7 +85,7 @@ class PlayQueue : public Wt::WTemplate
|
|||||||
Wt::WText* _nbTracks {};
|
Wt::WText* _nbTracks {};
|
||||||
Wt::WText* _repeatBtn {};
|
Wt::WText* _repeatBtn {};
|
||||||
Wt::WText* _radioBtn {};
|
Wt::WText* _radioBtn {};
|
||||||
boost::optional<std::size_t> _trackPos; // current track position, if set
|
std::optional<std::size_t> _trackPos; // current track position, if set
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
static const Field AudioTranscodeBitrateLimitField;
|
static const Field AudioTranscodeBitrateLimitField;
|
||||||
static const Field DemoField;
|
static const Field DemoField;
|
||||||
|
|
||||||
UserModel(boost::optional<Database::IdType> userId)
|
UserModel(std::optional<Database::IdType> userId)
|
||||||
: Wt::WFormModel(),
|
: Wt::WFormModel(),
|
||||||
_userId(userId)
|
_userId(userId)
|
||||||
{
|
{
|
||||||
@@ -80,7 +80,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
|
|
||||||
void saveData()
|
void saveData()
|
||||||
{
|
{
|
||||||
boost::optional<Database::User::PasswordHash> passwordHash;
|
std::optional<Database::User::PasswordHash> passwordHash;
|
||||||
if (!valueText(PasswordField).empty())
|
if (!valueText(PasswordField).empty())
|
||||||
passwordHash = getService<::Auth::PasswordService>()->hashPassword(valueText(PasswordField).toUTF8());
|
passwordHash = getService<::Auth::PasswordService>()->hashPassword(valueText(PasswordField).toUTF8());
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ValueStringModel<Bitrate>> _bitrateModel;
|
std::shared_ptr<ValueStringModel<Bitrate>> _bitrateModel;
|
||||||
boost::optional<Database::IdType> _userId;
|
std::optional<Database::IdType> _userId;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Wt::WFormModel::Field UserModel::LoginField = "login";
|
const Wt::WFormModel::Field UserModel::LoginField = "login";
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "Validators.hpp"
|
#include "Validators.hpp"
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <filesystem>
|
||||||
|
|
||||||
#include <Wt/WLengthValidator.h>
|
#include <Wt/WLengthValidator.h>
|
||||||
|
|
||||||
@@ -53,11 +53,11 @@ DirectoryValidator::validate(const Wt::WString& input) const
|
|||||||
if (input.empty())
|
if (input.empty())
|
||||||
return Wt::WValidator::validate(input);
|
return Wt::WValidator::validate(input);
|
||||||
|
|
||||||
boost::filesystem::path p(input.toUTF8());
|
const std::filesystem::path p {input.toUTF8()};
|
||||||
boost::system::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
// TODO check rights
|
// TODO check rights
|
||||||
bool res = boost::filesystem::is_directory(p, ec);
|
bool res = std::filesystem::is_directory(p, ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
return Wt::WValidator::Result(Wt::ValidationState::Invalid, ec.message()); // TODO translate common errors
|
return Wt::WValidator::Result(Wt::ValidationState::Invalid, ec.message()); // TODO translate common errors
|
||||||
else if (res)
|
else if (res)
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WStringListModel.h>
|
#include <Wt/WStringListModel.h>
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
@@ -41,7 +42,7 @@ class ValueStringModel : public Wt::WStringListModel
|
|||||||
return Wt::cpp17::any_cast<Wt::WString>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::Display));
|
return Wt::cpp17::any_cast<Wt::WString>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::Display));
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
getRowFromString(const Wt::WString& value)
|
getRowFromString(const Wt::WString& value)
|
||||||
{
|
{
|
||||||
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
||||||
@@ -50,10 +51,10 @@ class ValueStringModel : public Wt::WStringListModel
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
getRowFromValue(const T& value)
|
getRowFromValue(const T& value)
|
||||||
{
|
{
|
||||||
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
|
||||||
@@ -62,7 +63,7 @@ class ValueStringModel : public Wt::WStringListModel
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -42,16 +42,16 @@ namespace UserInterface {
|
|||||||
Artist::Artist(Filters* filters)
|
Artist::Artist(Filters* filters)
|
||||||
: _filters(filters)
|
: _filters(filters)
|
||||||
{
|
{
|
||||||
wApp->internalPathChanged().connect(std::bind([=]
|
wApp->internalPathChanged().connect([=]
|
||||||
{
|
{
|
||||||
refresh();
|
refresh();
|
||||||
}));
|
});
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
filters->updated().connect(std::bind([=] {
|
filters->updated().connect([=] {
|
||||||
refresh();
|
refresh();
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -162,13 +162,13 @@ Artist::refresh()
|
|||||||
entry->bindWidget("artist", LmsApplication::createArtistAnchor(artists.front()));
|
entry->bindWidget("artist", LmsApplication::createArtistAnchor(artists.front()));
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int> year = release->getReleaseYear();
|
std::optional<int> year {release->getReleaseYear()};
|
||||||
if (year)
|
if (year)
|
||||||
{
|
{
|
||||||
entry->setCondition("if-has-year", true);
|
entry->setCondition("if-has-year", true);
|
||||||
entry->bindInt("year", *year);
|
entry->bindInt("year", *year);
|
||||||
|
|
||||||
boost::optional<int> originalYear = release->getReleaseYear(true);
|
std::optional<int> originalYear {release->getReleaseYear(true)};
|
||||||
if (originalYear && *originalYear != *year)
|
if (originalYear && *originalYear != *year)
|
||||||
{
|
{
|
||||||
entry->setCondition("if-has-orig-year", true);
|
entry->setCondition("if-has-orig-year", true);
|
||||||
@@ -177,16 +177,16 @@ Artist::refresh()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.template.play-btn"), Wt::TextFormat::XHTML);
|
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.template.play-btn"), Wt::TextFormat::XHTML);
|
||||||
playBtn->clicked().connect(std::bind([=]
|
playBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
releasesPlay.emit({releaseId});
|
releasesPlay.emit({releaseId});
|
||||||
}));
|
});
|
||||||
|
|
||||||
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
||||||
addBtn->clicked().connect(std::bind([=]
|
addBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
releasesAdd.emit({releaseId});
|
releasesAdd.emit({releaseId});
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ ReleaseInfo::refresh()
|
|||||||
if (!release)
|
if (!release)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
boost::optional<std::string> copyright {release->getCopyright()};
|
std::optional<std::string> copyright {release->getCopyright()};
|
||||||
boost::optional<std::string> copyrightURL {release->getCopyrightURL()};
|
std::optional<std::string> copyrightURL {release->getCopyrightURL()};
|
||||||
|
|
||||||
setCondition("if-has-copyright-or-copyright-url", copyright || copyrightURL);
|
setCondition("if-has-copyright-or-copyright-url", copyright || copyrightURL);
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,13 @@ Release::refresh()
|
|||||||
|
|
||||||
t->bindString("name", Wt::WString::fromUTF8(release->getName()), Wt::TextFormat::Plain);
|
t->bindString("name", Wt::WString::fromUTF8(release->getName()), Wt::TextFormat::Plain);
|
||||||
|
|
||||||
boost::optional<int> year {release->getReleaseYear()};
|
std::optional<int> year {release->getReleaseYear()};
|
||||||
if (year)
|
if (year)
|
||||||
{
|
{
|
||||||
t->setCondition("if-has-year", true);
|
t->setCondition("if-has-year", true);
|
||||||
t->bindInt("year", *year);
|
t->bindInt("year", *year);
|
||||||
|
|
||||||
boost::optional<int> originalYear {release->getReleaseYear(true)};
|
std::optional<int> originalYear {release->getReleaseYear(true)};
|
||||||
if (originalYear && *originalYear != *year)
|
if (originalYear && *originalYear != *year)
|
||||||
{
|
{
|
||||||
t->setCondition("if-has-orig-year", true);
|
t->setCondition("if-has-orig-year", true);
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ Releases::addSome()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Database::IdType>
|
std::vector<Database::IdType>
|
||||||
Releases::getReleases(boost::optional<std::size_t> offset, boost::optional<std::size_t> limit, bool& moreResults) const
|
Releases::getReleases(std::optional<std::size_t> offset, std::optional<std::size_t> limit, bool& moreResults) const
|
||||||
{
|
{
|
||||||
const auto searchKeywords {splitString(_search->text().toUTF8(), " ")};
|
const auto searchKeywords {splitString(_search->text().toUTF8(), " ")};
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WContainerWidget.h>
|
#include <Wt/WContainerWidget.h>
|
||||||
#include <Wt/WLineEdit.h>
|
#include <Wt/WLineEdit.h>
|
||||||
@@ -44,7 +44,7 @@ class Releases : public Wt::WTemplate
|
|||||||
void refresh();
|
void refresh();
|
||||||
void addSome();
|
void addSome();
|
||||||
|
|
||||||
std::vector<Database::IdType> getReleases(boost::optional<std::size_t> offset, boost::optional<std::size_t> limit, bool& moreResults) const;
|
std::vector<Database::IdType> getReleases(std::optional<std::size_t> offset, std::optional<std::size_t> limit, bool& moreResults) const;
|
||||||
std::vector<Database::IdType> getReleases() const;
|
std::vector<Database::IdType> getReleases() const;
|
||||||
|
|
||||||
Filters* _filters;
|
Filters* _filters;
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ using namespace Database;
|
|||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
Tracks::Tracks(Filters* filters)
|
Tracks::Tracks(Filters* filters)
|
||||||
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.Tracks.template")),
|
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Tracks.template")},
|
||||||
_filters(filters)
|
_filters {filters}
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
|
|
||||||
@@ -56,18 +56,18 @@ _filters(filters)
|
|||||||
});
|
});
|
||||||
|
|
||||||
Wt::WText* addBtn = bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
Wt::WText* addBtn = bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
||||||
addBtn->clicked().connect(std::bind([=]
|
addBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
tracksAdd.emit(getTracks());
|
tracksAdd.emit(getTracks());
|
||||||
}));
|
});
|
||||||
|
|
||||||
_tracksContainer = bindNew<Wt::WContainerWidget>("tracks");
|
_tracksContainer = bindNew<Wt::WContainerWidget>("tracks");
|
||||||
|
|
||||||
_showMore = bindNew<Wt::WPushButton>("show-more", Wt::WString::tr("Lms.Explore.show-more"));
|
_showMore = bindNew<Wt::WPushButton>("show-more", Wt::WString::tr("Lms.Explore.show-more"));
|
||||||
_showMore->clicked().connect(std::bind([=]
|
_showMore->clicked().connect([=]
|
||||||
{
|
{
|
||||||
addSome();
|
addSome();
|
||||||
}));
|
});
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ _filters(filters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Database::IdType>
|
std::vector<Database::IdType>
|
||||||
Tracks::getTracks(boost::optional<std::size_t> offset, boost::optional<std::size_t> size, bool& moreResults)
|
Tracks::getTracks(std::optional<std::size_t> offset, std::optional<std::size_t> size, bool& moreResults)
|
||||||
{
|
{
|
||||||
const auto searchKeywords {splitString(_search->text().toUTF8(), " ")};
|
const auto searchKeywords {splitString(_search->text().toUTF8(), " ")};
|
||||||
const auto clusterIds {_filters->getClusterIds()};
|
const auto clusterIds {_filters->getClusterIds()};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WContainerWidget.h>
|
#include <Wt/WContainerWidget.h>
|
||||||
#include <Wt/WLineEdit.h>
|
#include <Wt/WLineEdit.h>
|
||||||
@@ -43,7 +43,7 @@ class Tracks : public Wt::WTemplate
|
|||||||
void refresh();
|
void refresh();
|
||||||
void addSome();
|
void addSome();
|
||||||
|
|
||||||
std::vector<Database::IdType> getTracks(boost::optional<std::size_t> offset, boost::optional<std::size_t> size, bool& moreResults);
|
std::vector<Database::IdType> getTracks(std::optional<std::size_t> offset, std::optional<std::size_t> size, bool& moreResults);
|
||||||
std::vector<Database::IdType> getTracks();
|
std::vector<Database::IdType> getTracks();
|
||||||
|
|
||||||
Wt::WContainerWidget* _tracksContainer;
|
Wt::WContainerWidget* _tracksContainer;
|
||||||
|
|||||||
@@ -19,12 +19,10 @@
|
|||||||
|
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
|
|
||||||
|
|
||||||
Config::Config(const boost::filesystem::path& p)
|
Config::Config(const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
_config.readFile(p.string().c_str());
|
_config.readFile(p.string().c_str());
|
||||||
}
|
}
|
||||||
@@ -35,7 +33,7 @@ Config::getString(const std::string& setting, const std::string& def, const std:
|
|||||||
try {
|
try {
|
||||||
std::string res {(const char*)_config.lookup(setting)};
|
std::string res {(const char*)_config.lookup(setting)};
|
||||||
|
|
||||||
if (!allowedValues.empty() && allowedValues.find(res) == allowedValues.end())
|
if (!allowedValues.empty() && allowedValues.find(res) == std::cend(allowedValues))
|
||||||
{
|
{
|
||||||
LMS_LOG(MAIN, ERROR) << "Invalid setting for '" << setting << "', using default value '" << def << "'";
|
LMS_LOG(MAIN, ERROR) << "Invalid setting for '" << setting << "', using default value '" << def << "'";
|
||||||
return def;
|
return def;
|
||||||
@@ -49,12 +47,12 @@ Config::getString(const std::string& setting, const std::string& def, const std:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::filesystem::path
|
std::filesystem::path
|
||||||
Config::getPath(const std::string& setting, const boost::filesystem::path& path)
|
Config::getPath(const std::string& setting, const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const char* res = _config.lookup(setting);
|
const char* res = _config.lookup(setting);
|
||||||
return boost::filesystem::path(std::string(res));
|
return std::filesystem::path {std::string(res)};
|
||||||
}
|
}
|
||||||
catch (std::exception &e)
|
catch (std::exception &e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,16 +18,16 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <libconfig.h++>
|
#include <libconfig.h++>
|
||||||
|
|
||||||
// Used to get config values from configuration files
|
// Used to get config values from configuration files
|
||||||
class Config final
|
class Config final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Config(const boost::filesystem::path& p);
|
Config(const std::filesystem::path& p);
|
||||||
~Config() = default;
|
~Config() = default;
|
||||||
|
|
||||||
Config(const Config&) = delete;
|
Config(const Config&) = delete;
|
||||||
@@ -37,7 +37,7 @@ class Config final
|
|||||||
|
|
||||||
// Default values are returned in case of setting not found
|
// Default values are returned in case of setting not found
|
||||||
std::string getString(const std::string& setting, const std::string& def = "", const std::set<std::string>& allowedValues = {});
|
std::string getString(const std::string& setting, const std::string& def = "", const std::set<std::string>& allowedValues = {});
|
||||||
boost::filesystem::path getPath(const std::string& setting, const boost::filesystem::path& def = boost::filesystem::path());
|
std::filesystem::path getPath(const std::string& setting, const std::filesystem::path& def = std::filesystem::path());
|
||||||
unsigned long getULong(const std::string& setting, unsigned long def = 0);
|
unsigned long getULong(const std::string& setting, unsigned long def = 0);
|
||||||
long getLong(const std::string& setting, long def = 0);
|
long getLong(const std::string& setting, long def = 0);
|
||||||
bool getBool(const std::string& setting, bool def = false);
|
bool getBool(const std::string& setting, bool def = false);
|
||||||
|
|||||||
+7
-7
@@ -28,7 +28,7 @@
|
|||||||
#include "utils/Exception.hpp"
|
#include "utils/Exception.hpp"
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
|
|
||||||
boost::filesystem::path searchExecPath(std::string filename)
|
std::filesystem::path searchExecPath(std::string filename)
|
||||||
{
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ boost::filesystem::path searchExecPath(std::string filename)
|
|||||||
tokenizer tok(path, sep);
|
tokenizer tok(path, sep);
|
||||||
for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it)
|
for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it)
|
||||||
{
|
{
|
||||||
boost::filesystem::path p = *it;
|
std::filesystem::path p = *it;
|
||||||
p /= filename;
|
p /= filename;
|
||||||
if (!::access(p.c_str(), X_OK))
|
if (!::access(p.c_str(), X_OK))
|
||||||
{
|
{
|
||||||
@@ -53,7 +53,7 @@ boost::filesystem::path searchExecPath(std::string filename)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void computeCrc(const boost::filesystem::path& p, std::vector<unsigned char>& crc)
|
void computeCrc(const std::filesystem::path& p, std::vector<unsigned char>& crc)
|
||||||
{
|
{
|
||||||
using crc_type = boost::crc_32_type;
|
using crc_type = boost::crc_32_type;
|
||||||
crc_type result;
|
crc_type result;
|
||||||
@@ -87,11 +87,11 @@ void computeCrc(const boost::filesystem::path& p, std::vector<unsigned char>& cr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ensureDirectory(boost::filesystem::path dir)
|
bool ensureDirectory(const std::filesystem::path& dir)
|
||||||
{
|
{
|
||||||
if (boost::filesystem::exists(dir))
|
if (std::filesystem::exists(dir))
|
||||||
return boost::filesystem::is_directory(dir);
|
return std::filesystem::is_directory(dir);
|
||||||
else
|
else
|
||||||
return boost::filesystem::create_directory(dir);
|
return std::filesystem::create_directory(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -19,15 +19,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
boost::filesystem::path searchExecPath(std::string filename);
|
std::filesystem::path searchExecPath(std::string filename);
|
||||||
|
|
||||||
void computeCrc(const boost::filesystem::path& p, std::vector<unsigned char>& checksum);
|
void computeCrc(const std::filesystem::path& p, std::vector<unsigned char>& checksum);
|
||||||
|
|
||||||
// Make sure the given path is a directory
|
// Make sure the given path is a directory
|
||||||
// Create it if needed
|
// Create it if needed
|
||||||
bool ensureDirectory(boost::filesystem::path dir);
|
bool ensureDirectory(const std::filesystem::path& dir);
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -55,7 +55,7 @@ bool readList(const std::string& str, const std::string& separators, std::list<s
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
readAs(const std::string& str)
|
readAs(const std::string& str)
|
||||||
{
|
{
|
||||||
return str;
|
return str;
|
||||||
@@ -137,13 +137,13 @@ stringEndsWith(const std::string& str, const std::string& ending)
|
|||||||
return boost::algorithm::ends_with(str, ending);
|
return boost::algorithm::ends_with(str, ending);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
stringFromHex(const std::string& str)
|
stringFromHex(const std::string& str)
|
||||||
{
|
{
|
||||||
static const char lut[] {"0123456789ABCDEF"};
|
static const char lut[] {"0123456789ABCDEF"};
|
||||||
|
|
||||||
if (str.length() % 2 != 0)
|
if (str.length() % 2 != 0)
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
std::string res;
|
std::string res;
|
||||||
res.reserve(str.length() / 2);
|
res.reserve(str.length() / 2);
|
||||||
|
|||||||
+5
-6
@@ -22,13 +22,12 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WDate.h>
|
#include <Wt/WDate.h>
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -53,14 +52,14 @@ std::string
|
|||||||
bufferToString(const std::vector<unsigned char>& data);
|
bufferToString(const std::vector<unsigned char>& data);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
boost::optional<T> readAs(const std::string& str)
|
std::optional<T> readAs(const std::string& str)
|
||||||
{
|
{
|
||||||
T res;
|
T res;
|
||||||
|
|
||||||
std::istringstream iss ( str );
|
std::istringstream iss ( str );
|
||||||
iss >> res;
|
iss >> res;
|
||||||
if (iss.fail())
|
if (iss.fail())
|
||||||
return boost::none;
|
return std::nullopt;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -71,7 +70,7 @@ replaceInString(const std::string& str, const std::string& from, const std::stri
|
|||||||
bool
|
bool
|
||||||
stringEndsWith(const std::string& str, const std::string& ending);
|
stringEndsWith(const std::string& str, const std::string& ending);
|
||||||
|
|
||||||
boost::optional<std::string>
|
std::optional<std::string>
|
||||||
stringFromHex(const std::string& str);
|
stringFromHex(const std::string& str);
|
||||||
|
|
||||||
// warning: not efficient
|
// warning: not efficient
|
||||||
@@ -105,7 +104,7 @@ void uniqueAndSortedByOccurence(In first, In last, Out out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class T, class Compare = std::less<>>
|
template<class T, class Compare = std::less<>>
|
||||||
constexpr const T& clamp( T v, T lo, T hi, Compare comp = {})
|
constexpr T clamp(T v, T lo, T hi, Compare comp = {})
|
||||||
{
|
{
|
||||||
assert(!comp(hi, lo));
|
assert(!comp(hi, lo));
|
||||||
return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
|
return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
|
||||||
|
|||||||
+3
-3
@@ -8,14 +8,14 @@ som_SOURCES = \
|
|||||||
$(top_srcdir)/src/similarity/features/som/DataNormalizer.cpp \
|
$(top_srcdir)/src/similarity/features/som/DataNormalizer.cpp \
|
||||||
$(top_srcdir)/src/similarity/features/som/Network.cpp
|
$(top_srcdir)/src/similarity/features/som/Network.cpp
|
||||||
|
|
||||||
som_CXXFLAGS=-std=c++14 -Wall -I${top_srcdir}/src/ -I${top_srcdir}/src/similarity/features/som/
|
som_CXXFLAGS=-std=c++17 -I${top_srcdir}/src/ -I${top_srcdir}/src/similarity/features/som/
|
||||||
|
|
||||||
|
|
||||||
database_SOURCES = \
|
database_SOURCES = \
|
||||||
$(srcdir)/database/DatabaseTest.cpp \
|
$(srcdir)/database/DatabaseTest.cpp \
|
||||||
$(top_srcdir)/src/database/Artist.cpp \
|
$(top_srcdir)/src/database/Artist.cpp \
|
||||||
$(top_srcdir)/src/database/Cluster.cpp \
|
$(top_srcdir)/src/database/Cluster.cpp \
|
||||||
$(top_srcdir)/src/database/Database.cpp \
|
$(top_srcdir)/src/database/Db.cpp \
|
||||||
$(top_srcdir)/src/database/TrackArtistLink.cpp \
|
$(top_srcdir)/src/database/TrackArtistLink.cpp \
|
||||||
$(top_srcdir)/src/database/TrackFeatures.cpp \
|
$(top_srcdir)/src/database/TrackFeatures.cpp \
|
||||||
$(top_srcdir)/src/database/TrackList.cpp \
|
$(top_srcdir)/src/database/TrackList.cpp \
|
||||||
@@ -29,5 +29,5 @@ database_SOURCES = \
|
|||||||
$(top_srcdir)/src/utils/Logger.cpp \
|
$(top_srcdir)/src/utils/Logger.cpp \
|
||||||
$(top_srcdir)/src/utils/Utils.cpp
|
$(top_srcdir)/src/utils/Utils.cpp
|
||||||
|
|
||||||
database_CXXFLAGS=-std=c++14 -Wall -I${top_srcdir}/src/
|
database_CXXFLAGS=-std=c++17 -I${top_srcdir}/src/
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,11 @@
|
|||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <filesystem>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
#include "database/Artist.hpp"
|
||||||
#include "database/Cluster.hpp"
|
#include "database/Cluster.hpp"
|
||||||
#include "database/Database.hpp"
|
#include "database/Db.hpp"
|
||||||
#include "database/TrackList.hpp"
|
#include "database/TrackList.hpp"
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/Track.hpp"
|
||||||
@@ -34,10 +34,10 @@ using namespace Database;
|
|||||||
class ScopedFileDeleter final
|
class ScopedFileDeleter final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ScopedFileDeleter(const boost::filesystem::path& path) : _path {path} {}
|
ScopedFileDeleter(const std::filesystem::path& path) : _path {path} {}
|
||||||
~ScopedFileDeleter() { boost::filesystem::remove(_path); }
|
~ScopedFileDeleter() { std::filesystem::remove(_path); }
|
||||||
private:
|
private:
|
||||||
boost::filesystem::path _path;
|
std::filesystem::path _path;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CHECK(PRED) \
|
#define CHECK(PRED) \
|
||||||
@@ -1247,19 +1247,19 @@ testDatabaseEmpty(Session& session)
|
|||||||
CHECK(Track::getAll(session).empty());
|
CHECK(Track::getAll(session).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main()
|
||||||
{
|
{
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const boost::filesystem::path tmpFile {boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()};
|
const std::filesystem::path tmpFile {std::tmpnam(nullptr)};
|
||||||
ScopedFileDeleter tmpFileDeleter {tmpFile};
|
ScopedFileDeleter tmpFileDeleter {tmpFile};
|
||||||
|
|
||||||
std::cout << "Database test file: '" << tmpFile.string() << "'" << std::endl;
|
std::cout << "Database test file: '" << tmpFile.string() << "'" << std::endl;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < 2; ++i)
|
for (std::size_t i = 0; i < 2; ++i)
|
||||||
{
|
{
|
||||||
Database::Database db {tmpFile};
|
Database::Db db {tmpFile};
|
||||||
std::unique_ptr<Session> session {db.createSession()};
|
std::unique_ptr<Session> session {db.createSession()};
|
||||||
|
|
||||||
auto runTest = [&session](const std::string& name, std::function<void(Session&)> testFunc)
|
auto runTest = [&session](const std::string& name, std::function<void(Session&)> testFunc)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
using namespace SOM;
|
using namespace SOM;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main()
|
||||||
{
|
{
|
||||||
static const InputVector::value_type EPSILON = 0.01;
|
static const InputVector::value_type EPSILON = 0.01;
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -31,13 +32,13 @@ std::ostream& operator<<(std::ostream& os, const MetaData::Album& album)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void parse(MetaData::Parser& parser, const boost::filesystem::path& file)
|
void parse(MetaData::Parser& parser, const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
using namespace MetaData;
|
using namespace MetaData;
|
||||||
|
|
||||||
parser.setClusterTypeNames( {"MOOD", "GENRE"} );
|
parser.setClusterTypeNames( {"MOOD", "GENRE"} );
|
||||||
|
|
||||||
boost::optional<Track> track {parser.parse(file, true)};
|
std::optional<Track> track {parser.parse(file, true)};
|
||||||
if (!track)
|
if (!track)
|
||||||
{
|
{
|
||||||
std::cerr << "Parsing failed" << std::endl;
|
std::cerr << "Parsing failed" << std::endl;
|
||||||
@@ -119,11 +120,9 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Av::AvInit();
|
|
||||||
|
|
||||||
for (std::size_t i {}; i < static_cast<std::size_t>(argc - 1); ++i)
|
for (std::size_t i {}; i < static_cast<std::size_t>(argc - 1); ++i)
|
||||||
{
|
{
|
||||||
boost::filesystem::path file {argv[i + 1]};
|
std::filesystem::path file {argv[i + 1]};
|
||||||
|
|
||||||
std::cout << "Parsing file '" << file << "'" << std::endl;
|
std::cout << "Parsing file '" << file << "'" << std::endl;
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ lms_metadata_SOURCES = \
|
|||||||
$(top_srcdir)/src/utils/Logger.cpp \
|
$(top_srcdir)/src/utils/Logger.cpp \
|
||||||
$(top_srcdir)/src/utils/Utils.cpp
|
$(top_srcdir)/src/utils/Utils.cpp
|
||||||
|
|
||||||
lms_metadata_CXXFLAGS=-std=c++14 -Wall -I$(top_srcdir)/src -D_REENTRANT
|
lms_metadata_CXXFLAGS=-std=c++17 -I$(top_srcdir)/src -D_REENTRANT
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <random>
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "database/Database.hpp"
|
#include "database/Db.hpp"
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/Track.hpp"
|
||||||
#include "database/Artist.hpp"
|
#include "database/Artist.hpp"
|
||||||
@@ -32,7 +32,7 @@ std::ostream& operator<<(std::ostream& os, const Database::Track::pointer& track
|
|||||||
|
|
||||||
static
|
static
|
||||||
bool
|
bool
|
||||||
getTrackFeatures(Database::Session &session, const Database::Track::pointer& track, const std::map<std::string, std::size_t>& featuresSettings, SOM::InputVector& res)
|
getTrackFeatures(Database::Session&, const Database::Track::pointer& track, const std::map<std::string, std::size_t>& featuresSettings, SOM::InputVector& res)
|
||||||
{
|
{
|
||||||
std::map<std::string, std::vector<double>> features;
|
std::map<std::string, std::vector<double>> features;
|
||||||
for (const auto& featureSettings : featuresSettings)
|
for (const auto& featureSettings : featuresSettings)
|
||||||
@@ -84,13 +84,13 @@ int main(int argc, char *argv[])
|
|||||||
for (const auto& featureSettings : featuresSettings)
|
for (const auto& featureSettings : featuresSettings)
|
||||||
nbDims += featureSettings.second;
|
nbDims += featureSettings.second;
|
||||||
|
|
||||||
boost::filesystem::path configFilePath = "/etc/lms.conf";
|
std::filesystem::path configFilePath {"/etc/lms.conf"};
|
||||||
if (argc >= 2)
|
if (argc >= 2)
|
||||||
configFilePath = std::string(argv[1], 0, 256);
|
configFilePath = std::string(argv[1], 0, 256);
|
||||||
|
|
||||||
ServiceProvider<Config>::create(configFilePath);
|
ServiceProvider<Config>::create(configFilePath);
|
||||||
|
|
||||||
Database::Database db {getService<Config>()->getPath("working-dir") / "lms.db"};
|
Database::Db db {getService<Config>()->getPath("working-dir") / "lms.db"};
|
||||||
auto session {db.createSession()};
|
auto session {db.createSession()};
|
||||||
|
|
||||||
std::cout << "Getting all features..." << std::endl;
|
std::cout << "Getting all features..." << std::endl;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ lms_similarity_SOURCES = \
|
|||||||
$(srcdir)/LmsSimilarity.cpp \
|
$(srcdir)/LmsSimilarity.cpp \
|
||||||
$(top_srcdir)/src/database/Artist.cpp \
|
$(top_srcdir)/src/database/Artist.cpp \
|
||||||
$(top_srcdir)/src/database/Cluster.cpp \
|
$(top_srcdir)/src/database/Cluster.cpp \
|
||||||
$(top_srcdir)/src/database/Database.cpp \
|
$(top_srcdir)/src/database/Db.cpp \
|
||||||
$(top_srcdir)/src/database/TrackFeatures.cpp \
|
$(top_srcdir)/src/database/TrackFeatures.cpp \
|
||||||
$(top_srcdir)/src/database/TrackList.cpp \
|
$(top_srcdir)/src/database/TrackList.cpp \
|
||||||
$(top_srcdir)/src/database/Release.cpp \
|
$(top_srcdir)/src/database/Release.cpp \
|
||||||
@@ -20,5 +20,5 @@ lms_similarity_SOURCES = \
|
|||||||
$(top_srcdir)/src/utils/Logger.cpp \
|
$(top_srcdir)/src/utils/Logger.cpp \
|
||||||
$(top_srcdir)/src/utils/Utils.cpp
|
$(top_srcdir)/src/utils/Utils.cpp
|
||||||
|
|
||||||
lms_similarity_CXXFLAGS=-std=c++14 -Wall -I$(top_srcdir)/src -D_REENTRANT
|
lms_similarity_CXXFLAGS=-std=c++17 -I$(top_srcdir)/src -D_REENTRANT
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user