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
|
||||
"window" parameters
|
||||
- add command "readpicture" to download embedded pictures
|
||||
- relax the ISO 8601 parser: allow omitting the time of day and the "Z"
|
||||
suffix
|
||||
* tags
|
||||
- new tags "Grouping" (for ID3 "TIT1"), "Work" and "Conductor"
|
||||
* input
|
||||
@ -32,9 +30,15 @@ ver 0.22 (not yet released)
|
||||
* switch to C++17
|
||||
- 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
|
||||
- jack: mark ports as terminal
|
||||
- shout: declare metadata as UTF-8
|
||||
* fix build failure with -Ddatabase=false
|
||||
|
||||
ver 0.21.16 (2019/10/16)
|
||||
|
@ -42,7 +42,7 @@ Provides access to the database of another :program:`MPD` instance using libmpdc
|
||||
* - **password**
|
||||
- The password used to log in to the "master" :program:`MPD` instance.
|
||||
* - **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
|
||||
----
|
||||
@ -1120,7 +1120,7 @@ This plugin requires building with ``libavfilter`` (FFmpeg).
|
||||
normalize
|
||||
---------
|
||||
|
||||
Normalize the volume during playback (at the expensve of quality).
|
||||
Normalize the volume during playback (at the expense of quality).
|
||||
|
||||
|
||||
null
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
static LocatedUri
|
||||
LocateFileUri(const char *uri, const Client *client
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "../ArchiveVisitor.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <zzip/zzip.h>
|
||||
@ -121,9 +122,19 @@ ZzipArchiveFile::OpenStream(const char *pathname,
|
||||
Mutex &mutex)
|
||||
{
|
||||
ZZIP_FILE *_file = zzip_file_open(dir->dir, pathname, 0);
|
||||
if (_file == nullptr)
|
||||
throw FormatRuntimeError("not found in the ZIP file: %s",
|
||||
pathname);
|
||||
if (_file == nullptr) {
|
||||
const auto error = (zzip_error_t)zzip_error(dir->dir);
|
||||
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,
|
||||
mutex,
|
||||
|
@ -686,6 +686,11 @@ MadDecoder::DecodeFirstFrame(Tag *tag) noexcept
|
||||
{
|
||||
struct xing xing;
|
||||
|
||||
#if GCC_CHECK_VERSION(10,0)
|
||||
/* work around bogus -Wuninitialized in GCC 10 */
|
||||
xing.frames = 0;
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
const auto action = DecodeNextFrame(false, tag);
|
||||
switch (action) {
|
||||
|
@ -40,6 +40,9 @@ input_glue = static_library(
|
||||
'cache/Item.cxx',
|
||||
'cache/Stream.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
boost_dep,
|
||||
],
|
||||
)
|
||||
|
||||
input_glue_dep = declare_dependency(
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
/**
|
||||
* OO wrapper for "struct curl_slist *".
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
template<typename T> struct ConstBuffer;
|
||||
|
||||
namespace Gcrypt {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
@ -52,6 +52,9 @@ xiph = static_library(
|
||||
'VorbisPicture.cxx',
|
||||
'XiphTags.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
libvorbis_dep,
|
||||
],
|
||||
)
|
||||
|
||||
xiph_dep = declare_dependency(
|
||||
|
@ -381,6 +381,7 @@ ShoutOutput::SendTag(const Tag &tag)
|
||||
shout_tag_to_metadata(tag, song, sizeof(song));
|
||||
|
||||
shout_metadata_add(meta, "song", song);
|
||||
shout_metadata_add(meta, "charset", "UTF-8");
|
||||
if (SHOUTERR_SUCCESS != shout_set_metadata(shout_conn, meta)) {
|
||||
LogWarning(shout_output_domain,
|
||||
"error setting shout metadata");
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include "ConfiguredResampler.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void
|
||||
|
@ -106,14 +106,19 @@ ParseTimeStamp(const char *s)
|
||||
{
|
||||
assert(s != nullptr);
|
||||
|
||||
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 {
|
||||
/* try ISO 8601 */
|
||||
return ParseISO8601(s).first;
|
||||
} catch (...) {
|
||||
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 */
|
||||
return ParseISO8601(s).first;
|
||||
/* rethrow the ParseISO8601() error */
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool
|
||||
|
@ -147,6 +147,18 @@ FormatErrno(const char *fmt, Args&&... args) noexcept
|
||||
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
|
||||
inline bool
|
||||
IsErrno(const std::system_error &e, int code) noexcept
|
||||
|
@ -123,8 +123,12 @@ ParseISO8601(const char *s)
|
||||
|
||||
/* parse the date */
|
||||
const char *end = strptime(s, "%F", &tm);
|
||||
if (end == nullptr)
|
||||
throw std::runtime_error("Failed to parse date");
|
||||
if (end == nullptr) {
|
||||
/* try without field separators */
|
||||
end = strptime(s, "%Y%m%d", &tm);
|
||||
if (end == nullptr)
|
||||
throw std::runtime_error("Failed to parse date");
|
||||
}
|
||||
|
||||
s = end;
|
||||
|
||||
@ -136,6 +140,12 @@ ParseISO8601(const char *s)
|
||||
|
||||
if ((end = strptime(s, "%T", &tm)) != nullptr)
|
||||
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)
|
||||
precision = std::chrono::minutes(1);
|
||||
else if ((end = strptime(s, "%H", &tm)) != nullptr)
|
||||
|
@ -35,14 +35,14 @@
|
||||
/**
|
||||
* A statically allocated string buffer.
|
||||
*/
|
||||
template<typename T, size_t CAPACITY>
|
||||
template<typename T, std::size_t CAPACITY>
|
||||
class BasicStringBuffer {
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T &reference;
|
||||
typedef T *pointer;
|
||||
typedef const T *const_pointer;
|
||||
typedef size_t size_type;
|
||||
typedef std::size_t size_type;
|
||||
|
||||
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> {};
|
||||
|
||||
#endif
|
||||
|
@ -36,13 +36,13 @@
|
||||
|
||||
template<typename... Args>
|
||||
static inline void
|
||||
StringFormat(char *buffer, size_t size,
|
||||
StringFormat(char *buffer, std::size_t size,
|
||||
const char *fmt, Args&&... args) noexcept
|
||||
{
|
||||
snprintf(buffer, size, fmt, args...);
|
||||
}
|
||||
|
||||
template<size_t CAPACITY, typename... Args>
|
||||
template<std::size_t CAPACITY, typename... Args>
|
||||
static inline void
|
||||
StringFormat(StringBuffer<CAPACITY> &buffer,
|
||||
const char *fmt, Args&&... args) noexcept
|
||||
@ -50,7 +50,7 @@ StringFormat(StringBuffer<CAPACITY> &buffer,
|
||||
StringFormat(buffer.data(), buffer.capacity(), fmt, args...);
|
||||
}
|
||||
|
||||
template<size_t CAPACITY, typename... Args>
|
||||
template<std::size_t CAPACITY, typename... Args>
|
||||
static inline StringBuffer<CAPACITY>
|
||||
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+02:00", 1549291601, 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)
|
||||
|
Loading…
Reference in New Issue
Block a user