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

View File

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

View File

@ -29,6 +29,8 @@
#include "storage/StorageInterface.hxx"
#endif
#include <stdexcept>
static LocatedUri
LocateFileUri(const char *uri, const Client *client
#ifdef ENABLE_DATABASE

View File

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

View File

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

View File

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

View File

@ -33,6 +33,7 @@
#include <curl/curl.h>
#include <algorithm>
#include <stdexcept>
/**
* OO wrapper for "struct curl_slist *".

View File

@ -35,6 +35,8 @@
#include <array>
#include <stdint.h>
template<typename T> struct ConstBuffer;
namespace Gcrypt {

View File

@ -25,6 +25,7 @@
#include "util/ASCII.hxx"
#include <utility>
#include <stdexcept>
#include <assert.h>
#include <string.h>

View File

@ -52,6 +52,9 @@ xiph = static_library(
'VorbisPicture.cxx',
'XiphTags.cxx',
include_directories: inc,
dependencies: [
libvorbis_dep,
],
)
xiph_dep = declare_dependency(

View File

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

View File

@ -21,6 +21,8 @@
#include "ConfiguredResampler.hxx"
#include "util/ConstBuffer.hxx"
#include <stdexcept>
#include <assert.h>
void

View File

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

View File

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

View File

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

View File

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

View File

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

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