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:
Max Kellermann 2019-12-16 23:43:51 +01:00
commit 683d5848f4
18 changed files with 94 additions and 23 deletions

10
NEWS
View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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) {

View File

@ -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(

View File

@ -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 *".

View File

@ -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 {

View File

@ -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>

View File

@ -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(

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
{ {

View File

@ -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)