release v0.21.17
-----BEGIN PGP SIGNATURE----- iQJEBAABCgAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAl34Bg0QHG1heEBtdXNp Y3BkLm9yZwAKCRAjbopYxttFEmOvD/4/gBb1kIOQduz+ZV79LHPGeqDoIJBm8VWg t9R1Mpt0flqC8+RcXFK/P0kUifBO5fu+a1DbHHijUvDNLYpUsCiXyxTpES9gGh54 7djEeqi1suoLpMUt4zkUEHGTp2dGUTmyewE1TuF1sZi2xlPgOeWXEX7a0xWs4zdG WI4CojUSkQGVAI2XZ7xIKP1v368B9F6AbqTK4zkzjNbTCv5VfESGlxdde0NXqU2e /bjil1aT2kdcks3ddeT4llyGIUzn1PzOyA6LQvmVXLSAtz9Rr/hHA+qkR19a4CCt PXxE0xClhhmZglzPuES0sRqBbwG99dPzA1ajTOtielwMrlFx+OceRfUnfdgqpPqd HTkEzMGsQ2PMMbE8R9DF5GU8B9xAcnKMh2g3/9KGStbocGSFWahLRObkHEhC5uJu g5ncQio3o/AtlBirywn/lkd6CYRDvMheMVOTHtv4/ZN0hIONMXCb6u1SQfqnX7qb fRP2UP9VUpPI9MUfOn/0uqM8/zXb5SMRe78p9UIYCF+mEDST190VxsdMjiBp/Xug Pktlec72iyiNHCavp04nXkt2kBrfSIFFaAQ0Qlp4huRV1k3Ur672NHtwuPLv8ORH xLec8On6nWVuuu5kC0F+4KxTlu6u2CMwH+wVr7t/D4rrD1iVo39MtElTBawZZC6+ 8EapnNHyGg== =A+Dp -----END PGP SIGNATURE----- Merge tag 'v0.21.17' release v0.21.17
This commit is contained in:
commit
683d5848f4
10
NEWS
10
NEWS
@ -3,8 +3,6 @@ ver 0.22 (not yet released)
|
|||||||
- "findadd"/"searchadd"/"searchaddpl" support the "sort" and
|
- "findadd"/"searchadd"/"searchaddpl" support the "sort" and
|
||||||
"window" parameters
|
"window" parameters
|
||||||
- add command "readpicture" to download embedded pictures
|
- add command "readpicture" to download embedded pictures
|
||||||
- relax the ISO 8601 parser: allow omitting the time of day and the "Z"
|
|
||||||
suffix
|
|
||||||
* tags
|
* tags
|
||||||
- new tags "Grouping" (for ID3 "TIT1"), "Work" and "Conductor"
|
- new tags "Grouping" (for ID3 "TIT1"), "Work" and "Conductor"
|
||||||
* input
|
* input
|
||||||
@ -32,9 +30,15 @@ ver 0.22 (not yet released)
|
|||||||
* switch to C++17
|
* switch to C++17
|
||||||
- GCC 7 or clang 4 (or newer) recommended
|
- GCC 7 or clang 4 (or newer) recommended
|
||||||
|
|
||||||
ver 0.21.17 (not yet released)
|
ver 0.21.17 (2019/12/16)
|
||||||
|
* protocol
|
||||||
|
- relax the ISO 8601 parser: allow omitting field separators, the
|
||||||
|
time of day and the "Z" suffix
|
||||||
|
* archive
|
||||||
|
- zzip: improve error reporting
|
||||||
* outputs
|
* outputs
|
||||||
- jack: mark ports as terminal
|
- jack: mark ports as terminal
|
||||||
|
- shout: declare metadata as UTF-8
|
||||||
* fix build failure with -Ddatabase=false
|
* fix build failure with -Ddatabase=false
|
||||||
|
|
||||||
ver 0.21.16 (2019/10/16)
|
ver 0.21.16 (2019/10/16)
|
||||||
|
@ -42,7 +42,7 @@ Provides access to the database of another :program:`MPD` instance using libmpdc
|
|||||||
* - **password**
|
* - **password**
|
||||||
- The password used to log in to the "master" :program:`MPD` instance.
|
- The password used to log in to the "master" :program:`MPD` instance.
|
||||||
* - **keepalive yes|no**
|
* - **keepalive yes|no**
|
||||||
- Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expensive of a very small amount of additional network traffic. Disabled by default.
|
- Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expense of a very small amount of additional network traffic. Disabled by default.
|
||||||
|
|
||||||
upnp
|
upnp
|
||||||
----
|
----
|
||||||
@ -1120,7 +1120,7 @@ This plugin requires building with ``libavfilter`` (FFmpeg).
|
|||||||
normalize
|
normalize
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Normalize the volume during playback (at the expensve of quality).
|
Normalize the volume during playback (at the expense of quality).
|
||||||
|
|
||||||
|
|
||||||
null
|
null
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
static LocatedUri
|
static LocatedUri
|
||||||
LocateFileUri(const char *uri, const Client *client
|
LocateFileUri(const char *uri, const Client *client
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "../ArchiveVisitor.hxx"
|
#include "../ArchiveVisitor.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "system/Error.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
|
|
||||||
#include <zzip/zzip.h>
|
#include <zzip/zzip.h>
|
||||||
@ -121,9 +122,19 @@ ZzipArchiveFile::OpenStream(const char *pathname,
|
|||||||
Mutex &mutex)
|
Mutex &mutex)
|
||||||
{
|
{
|
||||||
ZZIP_FILE *_file = zzip_file_open(dir->dir, pathname, 0);
|
ZZIP_FILE *_file = zzip_file_open(dir->dir, pathname, 0);
|
||||||
if (_file == nullptr)
|
if (_file == nullptr) {
|
||||||
throw FormatRuntimeError("not found in the ZIP file: %s",
|
const auto error = (zzip_error_t)zzip_error(dir->dir);
|
||||||
pathname);
|
switch (error) {
|
||||||
|
case ZZIP_ENOENT:
|
||||||
|
throw FormatFileNotFound("Failed to open '%s' in ZIP file",
|
||||||
|
pathname);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw FormatRuntimeError("Failed to open '%s' in ZIP file: %s",
|
||||||
|
pathname,
|
||||||
|
zzip_strerror(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return std::make_unique<ZzipInputStream>(dir, pathname,
|
return std::make_unique<ZzipInputStream>(dir, pathname,
|
||||||
mutex,
|
mutex,
|
||||||
|
@ -686,6 +686,11 @@ MadDecoder::DecodeFirstFrame(Tag *tag) noexcept
|
|||||||
{
|
{
|
||||||
struct xing xing;
|
struct xing xing;
|
||||||
|
|
||||||
|
#if GCC_CHECK_VERSION(10,0)
|
||||||
|
/* work around bogus -Wuninitialized in GCC 10 */
|
||||||
|
xing.frames = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const auto action = DecodeNextFrame(false, tag);
|
const auto action = DecodeNextFrame(false, tag);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
@ -40,6 +40,9 @@ input_glue = static_library(
|
|||||||
'cache/Item.cxx',
|
'cache/Item.cxx',
|
||||||
'cache/Stream.cxx',
|
'cache/Stream.cxx',
|
||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
|
dependencies: [
|
||||||
|
boost_dep,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
input_glue_dep = declare_dependency(
|
input_glue_dep = declare_dependency(
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OO wrapper for "struct curl_slist *".
|
* OO wrapper for "struct curl_slist *".
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
template<typename T> struct ConstBuffer;
|
template<typename T> struct ConstBuffer;
|
||||||
|
|
||||||
namespace Gcrypt {
|
namespace Gcrypt {
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "util/ASCII.hxx"
|
#include "util/ASCII.hxx"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -52,6 +52,9 @@ xiph = static_library(
|
|||||||
'VorbisPicture.cxx',
|
'VorbisPicture.cxx',
|
||||||
'XiphTags.cxx',
|
'XiphTags.cxx',
|
||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
|
dependencies: [
|
||||||
|
libvorbis_dep,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
xiph_dep = declare_dependency(
|
xiph_dep = declare_dependency(
|
||||||
|
@ -381,6 +381,7 @@ ShoutOutput::SendTag(const Tag &tag)
|
|||||||
shout_tag_to_metadata(tag, song, sizeof(song));
|
shout_tag_to_metadata(tag, song, sizeof(song));
|
||||||
|
|
||||||
shout_metadata_add(meta, "song", song);
|
shout_metadata_add(meta, "song", song);
|
||||||
|
shout_metadata_add(meta, "charset", "UTF-8");
|
||||||
if (SHOUTERR_SUCCESS != shout_set_metadata(shout_conn, meta)) {
|
if (SHOUTERR_SUCCESS != shout_set_metadata(shout_conn, meta)) {
|
||||||
LogWarning(shout_output_domain,
|
LogWarning(shout_output_domain,
|
||||||
"error setting shout metadata");
|
"error setting shout metadata");
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include "ConfiguredResampler.hxx"
|
#include "ConfiguredResampler.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -106,14 +106,19 @@ ParseTimeStamp(const char *s)
|
|||||||
{
|
{
|
||||||
assert(s != nullptr);
|
assert(s != nullptr);
|
||||||
|
|
||||||
char *endptr;
|
try {
|
||||||
unsigned long long value = strtoull(s, &endptr, 10);
|
/* try ISO 8601 */
|
||||||
if (*endptr == 0 && endptr > s)
|
return ParseISO8601(s).first;
|
||||||
/* it's an integral UNIX time stamp */
|
} catch (...) {
|
||||||
return std::chrono::system_clock::from_time_t((time_t)value);
|
char *endptr;
|
||||||
|
unsigned long long value = strtoull(s, &endptr, 10);
|
||||||
|
if (*endptr == 0 && endptr > s)
|
||||||
|
/* it's an integral UNIX time stamp */
|
||||||
|
return std::chrono::system_clock::from_time_t((time_t)value);
|
||||||
|
|
||||||
/* try ISO 8601 */
|
/* rethrow the ParseISO8601() error */
|
||||||
return ParseISO8601(s).first;
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr bool
|
static constexpr bool
|
||||||
|
@ -147,6 +147,18 @@ FormatErrno(const char *fmt, Args&&... args) noexcept
|
|||||||
return FormatErrno(errno, fmt, std::forward<Args>(args)...);
|
return FormatErrno(errno, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static inline std::system_error
|
||||||
|
FormatFileNotFound(const char *fmt, Args&&... args) noexcept
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return FormatLastError(ERROR_FILE_NOT_FOUND, fmt,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
#else
|
||||||
|
return FormatErrno(ENOENT, fmt, std::forward<Args>(args)...);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
inline bool
|
inline bool
|
||||||
IsErrno(const std::system_error &e, int code) noexcept
|
IsErrno(const std::system_error &e, int code) noexcept
|
||||||
|
@ -123,8 +123,12 @@ ParseISO8601(const char *s)
|
|||||||
|
|
||||||
/* parse the date */
|
/* parse the date */
|
||||||
const char *end = strptime(s, "%F", &tm);
|
const char *end = strptime(s, "%F", &tm);
|
||||||
if (end == nullptr)
|
if (end == nullptr) {
|
||||||
throw std::runtime_error("Failed to parse date");
|
/* try without field separators */
|
||||||
|
end = strptime(s, "%Y%m%d", &tm);
|
||||||
|
if (end == nullptr)
|
||||||
|
throw std::runtime_error("Failed to parse date");
|
||||||
|
}
|
||||||
|
|
||||||
s = end;
|
s = end;
|
||||||
|
|
||||||
@ -136,6 +140,12 @@ ParseISO8601(const char *s)
|
|||||||
|
|
||||||
if ((end = strptime(s, "%T", &tm)) != nullptr)
|
if ((end = strptime(s, "%T", &tm)) != nullptr)
|
||||||
precision = std::chrono::seconds(1);
|
precision = std::chrono::seconds(1);
|
||||||
|
else if ((end = strptime(s, "%H%M%S", &tm)) != nullptr)
|
||||||
|
/* no field separators */
|
||||||
|
precision = std::chrono::seconds(1);
|
||||||
|
else if ((end = strptime(s, "%H%M", &tm)) != nullptr)
|
||||||
|
/* no field separators */
|
||||||
|
precision = std::chrono::minutes(1);
|
||||||
else if ((end = strptime(s, "%H:%M", &tm)) != nullptr)
|
else if ((end = strptime(s, "%H:%M", &tm)) != nullptr)
|
||||||
precision = std::chrono::minutes(1);
|
precision = std::chrono::minutes(1);
|
||||||
else if ((end = strptime(s, "%H", &tm)) != nullptr)
|
else if ((end = strptime(s, "%H", &tm)) != nullptr)
|
||||||
|
@ -35,14 +35,14 @@
|
|||||||
/**
|
/**
|
||||||
* A statically allocated string buffer.
|
* A statically allocated string buffer.
|
||||||
*/
|
*/
|
||||||
template<typename T, size_t CAPACITY>
|
template<typename T, std::size_t CAPACITY>
|
||||||
class BasicStringBuffer {
|
class BasicStringBuffer {
|
||||||
public:
|
public:
|
||||||
typedef T value_type;
|
typedef T value_type;
|
||||||
typedef T &reference;
|
typedef T &reference;
|
||||||
typedef T *pointer;
|
typedef T *pointer;
|
||||||
typedef const T *const_pointer;
|
typedef const T *const_pointer;
|
||||||
typedef size_t size_type;
|
typedef std::size_t size_type;
|
||||||
|
|
||||||
static constexpr value_type SENTINEL = '\0';
|
static constexpr value_type SENTINEL = '\0';
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<size_t CAPACITY>
|
template<std::size_t CAPACITY>
|
||||||
class StringBuffer : public BasicStringBuffer<char, CAPACITY> {};
|
class StringBuffer : public BasicStringBuffer<char, CAPACITY> {};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,13 +36,13 @@
|
|||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
static inline void
|
static inline void
|
||||||
StringFormat(char *buffer, size_t size,
|
StringFormat(char *buffer, std::size_t size,
|
||||||
const char *fmt, Args&&... args) noexcept
|
const char *fmt, Args&&... args) noexcept
|
||||||
{
|
{
|
||||||
snprintf(buffer, size, fmt, args...);
|
snprintf(buffer, size, fmt, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t CAPACITY, typename... Args>
|
template<std::size_t CAPACITY, typename... Args>
|
||||||
static inline void
|
static inline void
|
||||||
StringFormat(StringBuffer<CAPACITY> &buffer,
|
StringFormat(StringBuffer<CAPACITY> &buffer,
|
||||||
const char *fmt, Args&&... args) noexcept
|
const char *fmt, Args&&... args) noexcept
|
||||||
@ -50,7 +50,7 @@ StringFormat(StringBuffer<CAPACITY> &buffer,
|
|||||||
StringFormat(buffer.data(), buffer.capacity(), fmt, args...);
|
StringFormat(buffer.data(), buffer.capacity(), fmt, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t CAPACITY, typename... Args>
|
template<std::size_t CAPACITY, typename... Args>
|
||||||
static inline StringBuffer<CAPACITY>
|
static inline StringBuffer<CAPACITY>
|
||||||
StringFormat(const char *fmt, Args&&... args) noexcept
|
StringFormat(const char *fmt, Args&&... args) noexcept
|
||||||
{
|
{
|
||||||
|
@ -71,6 +71,15 @@ static constexpr struct {
|
|||||||
{ "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) },
|
{ "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) },
|
||||||
{ "2019-02-04T16:46:41+02:00", 1549291601, std::chrono::seconds(1) },
|
{ "2019-02-04T16:46:41+02:00", 1549291601, std::chrono::seconds(1) },
|
||||||
{ "2019-02-04T16:46:41-0200", 1549306001, std::chrono::seconds(1) },
|
{ "2019-02-04T16:46:41-0200", 1549306001, std::chrono::seconds(1) },
|
||||||
|
|
||||||
|
/* without field separators */
|
||||||
|
{ "19700101T000000Z", 0, std::chrono::seconds(1) },
|
||||||
|
{ "19700101T000001Z", 1, std::chrono::seconds(1) },
|
||||||
|
{ "20190204T164641Z", 1549298801, std::chrono::seconds(1) },
|
||||||
|
{ "19700101", 0, std::chrono::hours(24) },
|
||||||
|
{ "20190204", 1549238400, std::chrono::hours(24) },
|
||||||
|
{ "20190204T1646", 1549298760, std::chrono::minutes(1) },
|
||||||
|
{ "20190204T16", 1549296000, std::chrono::hours(1) },
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(ISO8601, Parse)
|
TEST(ISO8601, Parse)
|
||||||
|
Loading…
Reference in New Issue
Block a user