release v0.23.13
-----BEGIN PGP SIGNATURE----- iQJBBAABCgArFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAmRrqn4NHG1heEBibGFy Zy5kZQAKCRAjbopYxttFErXiD/9QIaeO5d+U7BJK1yGkQgu5vHo4Pm34ewP8LTxs K2MYxQQ2jZc7PXqi5Z04wmUw8ymq1BVA2D0ytMdl1Ejcbrste7UrP+1TuD8L9JBj WCE+4otNVYDpx1PB32BN+gxCmJqiFtAQS+eMIz/SXdEXZ9sJvIgYRRkSiSFd0WHM dT3OeeZ0V8cANbXpgI+RLghRGkVdpZJo4uXh3c0OHog1fjEBWPw6+7tH/mkotM0n /hQjI5j14wnzZuIpkDR1kuzvGYmxJKG6LkuUqcm1WexNFbxMqDSbAlXeCmzJn7eK NQvtROOGNjQ84thxRPLlDlYa6vbxoTqdYgZsVHzsQZWejhqw8zsPsF+Ea0A4tkLd UKDQFTphZMVqx74+1u7IjQyW7x+k02iXWLwH2IheEl+BJhvEB7zEflN9TR81jek4 RrGx7jcTArdpt7okAe2ONkHvTYtIpaK5E2voza8K4TGOMzpkVlOkyvJ+rpn8CDRf w0faVvmCQdSrgHKfwUUY8Z47a7d+kkKpFLlvvzNoDV8drQXfgIj5t9HOLATSVVux l2pbgauXFsKyd4IsByXj+m8cBSvmy2C4eLJCsmguiZEvcobdJtzwOr4KuwQDjBsC 8HW0BD0t9S1dSL59vJdMG+CfK/QU5rmAIkxdydD42uo9UmSS78xw1uQgi9oDaPa8 XNCGdQ== =nOGh -----END PGP SIGNATURE----- Merge tag 'v0.23.13' release v0.23.13
This commit is contained in:
commit
9027e5c5bb
5
NEWS
5
NEWS
|
@ -43,11 +43,14 @@ ver 0.24 (not yet released)
|
||||||
* remove Boost dependency
|
* remove Boost dependency
|
||||||
* require libfmt 7 or later
|
* require libfmt 7 or later
|
||||||
|
|
||||||
ver 0.23.13 (not yet released)
|
ver 0.23.13 (2023/05/22)
|
||||||
* input
|
* input
|
||||||
- curl: fix busy loop after connection failed
|
- curl: fix busy loop after connection failed
|
||||||
|
- curl: hide "404" log messages for non-existent ".mpdignore" files
|
||||||
* archive
|
* archive
|
||||||
- zzip: fix crash bug
|
- zzip: fix crash bug
|
||||||
|
* database
|
||||||
|
- simple: reveal hidden songs after deleting containing CUE
|
||||||
* decoder
|
* decoder
|
||||||
- ffmpeg: reorder to a lower priority than "gme"
|
- ffmpeg: reorder to a lower priority than "gme"
|
||||||
- gme: require GME 0.6 or later
|
- gme: require GME 0.6 or later
|
||||||
|
|
|
@ -670,6 +670,11 @@ If ReplayGain is enabled, then the setting ``replaygain_preamp`` is
|
||||||
set to a value (in dB) between ``-15`` and ``15``. This is the gain
|
set to a value (in dB) between ``-15`` and ``15``. This is the gain
|
||||||
applied to songs with ReplayGain tags.
|
applied to songs with ReplayGain tags.
|
||||||
|
|
||||||
|
On songs without ReplayGain tags, the setting
|
||||||
|
``replaygain_missing_preamp`` is used instead. If this setting is not
|
||||||
|
configured, then no ReplayGain is applied to such songs, and they will
|
||||||
|
appear too loud.
|
||||||
|
|
||||||
ReplayGain is usually implemented with a software volume filter (which
|
ReplayGain is usually implemented with a software volume filter (which
|
||||||
prevents `Bit-perfect playback`_). To use a hardware mixer, set
|
prevents `Bit-perfect playback`_). To use a hardware mixer, set
|
||||||
``replay_gain_handler`` to ``mixer`` in the ``audio_output`` section
|
``replay_gain_handler`` to ``mixer`` in the ``audio_output`` section
|
||||||
|
|
|
@ -111,6 +111,18 @@ Directory::LookupTargetSong(std::string_view target) noexcept
|
||||||
return lr.directory->FindSong(lr.rest);
|
return lr.directory->FindSong(lr.rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Directory::ClearInPlaylist() noexcept
|
||||||
|
{
|
||||||
|
assert(holding_db_lock());
|
||||||
|
|
||||||
|
for (auto &child : children)
|
||||||
|
child.ClearInPlaylist();
|
||||||
|
|
||||||
|
for (auto &song : songs)
|
||||||
|
song.in_playlist = false;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Directory::PruneEmpty() noexcept
|
Directory::PruneEmpty() noexcept
|
||||||
{
|
{
|
||||||
|
|
|
@ -257,6 +257,14 @@ public:
|
||||||
*/
|
*/
|
||||||
SongPtr RemoveSong(Song *song) noexcept;
|
SongPtr RemoveSong(Song *song) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively walk through the whole tree and set all
|
||||||
|
* `Song::in_playlist` fields to `false`.
|
||||||
|
*
|
||||||
|
* Caller must lock the #db_mutex.
|
||||||
|
*/
|
||||||
|
void ClearInPlaylist() noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caller must lock the #db_mutex.
|
* Caller must lock the #db_mutex.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -515,6 +515,7 @@ UpdateWalk::Walk(Directory &root, const char *path, bool discard) noexcept
|
||||||
|
|
||||||
{
|
{
|
||||||
const ScopeDatabaseLock protect;
|
const ScopeDatabaseLock protect;
|
||||||
|
root.ClearInPlaylist();
|
||||||
PurgeDanglingFromPlaylists(root);
|
PurgeDanglingFromPlaylists(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,10 +275,8 @@ EventLoop::Run() noexcept
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
assert(IsInside());
|
assert(IsInside());
|
||||||
assert(!quit);
|
|
||||||
#ifdef HAVE_THREADED_EVENT_LOOP
|
#ifdef HAVE_THREADED_EVENT_LOOP
|
||||||
assert(!quit_injected);
|
assert(alive || quit_injected);
|
||||||
assert(alive);
|
|
||||||
assert(busy);
|
assert(busy);
|
||||||
|
|
||||||
wake_event.Schedule(SocketEvent::READ);
|
wake_event.Schedule(SocketEvent::READ);
|
||||||
|
@ -303,7 +301,7 @@ EventLoop::Run() noexcept
|
||||||
|
|
||||||
FlushClockCaches();
|
FlushClockCaches();
|
||||||
|
|
||||||
do {
|
while (!quit) {
|
||||||
again = false;
|
again = false;
|
||||||
|
|
||||||
/* invoke timers */
|
/* invoke timers */
|
||||||
|
@ -365,7 +363,7 @@ EventLoop::Run() noexcept
|
||||||
|
|
||||||
socket_event.Dispatch();
|
socket_event.Dispatch();
|
||||||
}
|
}
|
||||||
} while (!quit);
|
}
|
||||||
|
|
||||||
#ifdef HAVE_THREADED_EVENT_LOOP
|
#ifdef HAVE_THREADED_EVENT_LOOP
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
|
@ -463,7 +463,6 @@ CurlInputStream::InitEasy()
|
||||||
request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases);
|
request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases);
|
||||||
request->SetOption(CURLOPT_FOLLOWLOCATION, 1L);
|
request->SetOption(CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
request->SetOption(CURLOPT_MAXREDIRS, 5L);
|
request->SetOption(CURLOPT_MAXREDIRS, 5L);
|
||||||
request->SetOption(CURLOPT_FAILONERROR, 1L);
|
|
||||||
|
|
||||||
/* this option eliminates the probe request when
|
/* this option eliminates the probe request when
|
||||||
username/password are specified */
|
username/password are specified */
|
||||||
|
|
|
@ -18,13 +18,13 @@ endif
|
||||||
|
|
||||||
conf.set('HAVE_MD5', crypto_md5_dep.found())
|
conf.set('HAVE_MD5', crypto_md5_dep.found())
|
||||||
|
|
||||||
if libavutil_dep.found()
|
if ffmpeg_util_dep.found()
|
||||||
crypto_base64 = static_library(
|
crypto_base64 = static_library(
|
||||||
'crypto_base64',
|
'crypto_base64',
|
||||||
'Base64.cxx',
|
'Base64.cxx',
|
||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
libavutil_dep,
|
ffmpeg_util_dep,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
// Copyright The Music Player Daemon Project
|
|
||||||
|
|
||||||
#include "LogError.hxx"
|
|
||||||
#include "Domain.hxx"
|
|
||||||
#include "Log.hxx"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <libavutil/error.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LogFfmpegError(int errnum)
|
|
||||||
{
|
|
||||||
char msg[256];
|
|
||||||
av_strerror(errnum, msg, sizeof(msg));
|
|
||||||
LogError(ffmpeg_domain, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LogFfmpegError(int errnum, const char *prefix)
|
|
||||||
{
|
|
||||||
char msg[256];
|
|
||||||
av_strerror(errnum, msg, sizeof(msg));
|
|
||||||
FmtError(ffmpeg_domain, "{}: {}", prefix, msg);
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
// Copyright The Music Player Daemon Project
|
|
||||||
|
|
||||||
#ifndef MPD_FFMPEG_LOG_ERROR_HXX
|
|
||||||
#define MPD_FFMPEG_LOG_ERROR_HXX
|
|
||||||
|
|
||||||
void
|
|
||||||
LogFfmpegError(int errnum);
|
|
||||||
|
|
||||||
void
|
|
||||||
LogFfmpegError(int errnum, const char *prefix);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -13,6 +13,30 @@ else
|
||||||
endif
|
endif
|
||||||
conf.set('HAVE_LIBAVFILTER', libavfilter_dep.found())
|
conf.set('HAVE_LIBAVFILTER', libavfilter_dep.found())
|
||||||
|
|
||||||
|
if not libavutil_dep.found()
|
||||||
|
ffmpeg_util_dep = dependency('', required: false)
|
||||||
|
ffmpeg_dep = dependency('', required: false)
|
||||||
|
subdir_done()
|
||||||
|
endif
|
||||||
|
|
||||||
|
ffmpeg_util = static_library(
|
||||||
|
'ffmpeg_util',
|
||||||
|
'Interleave.cxx',
|
||||||
|
'Error.cxx',
|
||||||
|
include_directories: inc,
|
||||||
|
dependencies: [
|
||||||
|
fmt_dep,
|
||||||
|
libavutil_dep,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
ffmpeg_util_dep = declare_dependency(
|
||||||
|
link_with: ffmpeg_util,
|
||||||
|
dependencies: [
|
||||||
|
libavutil_dep,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
if not enable_ffmpeg
|
if not enable_ffmpeg
|
||||||
ffmpeg_dep = dependency('', required: false)
|
ffmpeg_dep = dependency('', required: false)
|
||||||
subdir_done()
|
subdir_done()
|
||||||
|
@ -30,17 +54,16 @@ ffmpeg = static_library(
|
||||||
'ffmpeg',
|
'ffmpeg',
|
||||||
'Init.cxx',
|
'Init.cxx',
|
||||||
'Interleave.cxx',
|
'Interleave.cxx',
|
||||||
'LogError.cxx',
|
|
||||||
'LogCallback.cxx',
|
'LogCallback.cxx',
|
||||||
'Error.cxx',
|
'Error.cxx',
|
||||||
'Domain.cxx',
|
'Domain.cxx',
|
||||||
ffmpeg_sources,
|
ffmpeg_sources,
|
||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
ffmpeg_util_dep,
|
||||||
libavformat_dep,
|
libavformat_dep,
|
||||||
libavcodec_dep,
|
libavcodec_dep,
|
||||||
libavfilter_dep,
|
libavfilter_dep,
|
||||||
libavutil_dep,
|
|
||||||
log_dep,
|
log_dep,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -48,9 +71,9 @@ ffmpeg = static_library(
|
||||||
ffmpeg_dep = declare_dependency(
|
ffmpeg_dep = declare_dependency(
|
||||||
link_with: ffmpeg,
|
link_with: ffmpeg,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
ffmpeg_util_dep,
|
||||||
libavformat_dep,
|
libavformat_dep,
|
||||||
libavcodec_dep,
|
libavcodec_dep,
|
||||||
libavfilter_dep,
|
libavfilter_dep,
|
||||||
libavutil_dep,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
#include "storage/FileInfo.hxx"
|
#include "storage/FileInfo.hxx"
|
||||||
#include "storage/MemoryDirectoryReader.hxx"
|
#include "storage/MemoryDirectoryReader.hxx"
|
||||||
|
#include "lib/curl/HttpStatusError.hxx"
|
||||||
#include "lib/curl/Init.hxx"
|
#include "lib/curl/Init.hxx"
|
||||||
#include "lib/curl/Global.hxx"
|
#include "lib/curl/Global.hxx"
|
||||||
#include "lib/curl/Slist.hxx"
|
#include "lib/curl/Slist.hxx"
|
||||||
|
@ -14,7 +15,6 @@
|
||||||
#include "lib/curl/Handler.hxx"
|
#include "lib/curl/Handler.hxx"
|
||||||
#include "lib/curl/Escape.hxx"
|
#include "lib/curl/Escape.hxx"
|
||||||
#include "lib/expat/ExpatParser.hxx"
|
#include "lib/expat/ExpatParser.hxx"
|
||||||
#include "lib/fmt/RuntimeError.hxx"
|
|
||||||
#include "lib/fmt/ToBuffer.hxx"
|
#include "lib/fmt/ToBuffer.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "event/InjectEvent.hxx"
|
#include "event/InjectEvent.hxx"
|
||||||
|
@ -286,8 +286,9 @@ private:
|
||||||
/* virtual methods from CurlResponseHandler */
|
/* virtual methods from CurlResponseHandler */
|
||||||
void OnHeaders(unsigned status, Curl::Headers &&headers) final {
|
void OnHeaders(unsigned status, Curl::Headers &&headers) final {
|
||||||
if (status != 207)
|
if (status != 207)
|
||||||
throw FmtRuntimeError("Status {} from WebDAV server; expected \"207 Multi-Status\"",
|
throw HttpStatusError(status,
|
||||||
status);
|
FmtBuffer<80>("Status {} from WebDAV server; expected \"207 Multi-Status\"",
|
||||||
|
status));
|
||||||
|
|
||||||
if (!IsXmlContentType(headers))
|
if (!IsXmlContentType(headers))
|
||||||
throw std::runtime_error("Unexpected Content-Type from WebDAV server");
|
throw std::runtime_error("Unexpected Content-Type from WebDAV server");
|
||||||
|
|
|
@ -14,14 +14,17 @@ class ScopeExitGuard : F {
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ScopeExitGuard(F &&f):F(std::forward<F>(f)) {}
|
explicit ScopeExitGuard(F &&f) noexcept:F(std::forward<F>(f)) {}
|
||||||
|
|
||||||
ScopeExitGuard(ScopeExitGuard &&src)
|
ScopeExitGuard(ScopeExitGuard &&src) noexcept
|
||||||
:F(std::move(src)), enabled(src.enabled) {
|
:F(std::move(src)),
|
||||||
src.enabled = false;
|
enabled(std::exchange(src.enabled, false)) {}
|
||||||
}
|
|
||||||
|
|
||||||
~ScopeExitGuard() {
|
/* destructors are "noexcept" by default; this explicit
|
||||||
|
"noexcept" declaration allows the destructor to throw if
|
||||||
|
the function can throw; without this, a throwing function
|
||||||
|
would std::terminate() */
|
||||||
|
~ScopeExitGuard() noexcept(noexcept(std::declval<F>()())) {
|
||||||
if (enabled)
|
if (enabled)
|
||||||
F::operator()();
|
F::operator()();
|
||||||
}
|
}
|
||||||
|
@ -38,7 +41,7 @@ struct ScopeExitTag {
|
||||||
parantheses at the end of the expression AtScopeExit()
|
parantheses at the end of the expression AtScopeExit()
|
||||||
call */
|
call */
|
||||||
template<typename F>
|
template<typename F>
|
||||||
ScopeExitGuard<F> operator+(F &&f) {
|
ScopeExitGuard<F> operator+(F &&f) noexcept {
|
||||||
return ScopeExitGuard<F>(std::forward<F>(f));
|
return ScopeExitGuard<F>(std::forward<F>(f));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue