release v0.20.4
-----BEGIN PGP SIGNATURE----- iQJEBAABCAAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAliSTDkQHG1heEBtdXNp Y3BkLm9yZwAKCRAjbopYxttFEsIQD/4iNkylzLp8mID2aFT690MFzv4TRXi2XS7v r3Rlx3hpGyDdzeXZeFc5zxsc9Ei8OfLcdFC/Umj9LjQanXakOxwsdhagrW9cadNX YLxkyVcREXalmzUvoWeRnya1LjoTdYA8llvG2tAJosXVr0o/GHZi56aHcrYnW2a6 XD8kEk5k9beuEcBLk1rdZCTGPbVLwCFvMcpZ7j5Hd4kDGQjjw2aoEaPWchJdhLQh p+GRSU+A8hyTo9zy+aNO3cKvq6zqVxDLlHnIqh8XPWQoLPyWuD7ETvwERKmdmiPZ bSo0MR7azTlhkWbNZxjPHgZuacJDlwKvXPg1ofjn8VVcaVe5ONeX+1WP0ozUYqyU fmhLxMHKuwZcKo6do/jNhAVp//VBWhSwJHPA8kjBTuZHHc0HvgyTxlAgvzlrSswe dxc8vnvzgJfUKz5k7mf3amVg6Cu1CmNi59CkyL0NL+8N0inyMfdeQuQEYgbPoI6X jIRwACfXpMX75VtiDaNpnFLBjL5emE6u2bDoU2c5ezgpthaWjb0PqSmoLBBF8TNm k0ecXlIwCjT3pDcqmFdqgG3AJiYLTgX0rETC8PInl6toLzr2oVMVlijU3YK5PjMl nTvgs8TwprTWImgcBnidqRMb39p3AKs12pHfZ4Y5Iu82Bm60acZQMkv6sQh43Wyc +W2+T3D2IA== =b2Fz -----END PGP SIGNATURE----- Merge tag 'v0.20.4' release v0.20.4
This commit is contained in:
commit
df4af2b550
8
NEWS
8
NEWS
@ -1,5 +1,13 @@
|
|||||||
ver 0.21 (not yet released)
|
ver 0.21 (not yet released)
|
||||||
|
|
||||||
|
ver 0.20.4 (2017/02/01)
|
||||||
|
* input
|
||||||
|
- nfs: fix freeze after reconnect
|
||||||
|
* output
|
||||||
|
- sndio: work around a libroar C++ incompatibility
|
||||||
|
* workaround for GCC 4.9 "constexpr" bug
|
||||||
|
* fix FreeBSD build failure
|
||||||
|
|
||||||
ver 0.20.3 (2017/01/25)
|
ver 0.20.3 (2017/01/25)
|
||||||
* protocol
|
* protocol
|
||||||
- "playlistadd" creates new playlist if it does not exist, as documented
|
- "playlistadd" creates new playlist if it does not exist, as documented
|
||||||
|
@ -19,8 +19,8 @@ libvorbis = AutotoolsProject(
|
|||||||
)
|
)
|
||||||
|
|
||||||
opus = AutotoolsProject(
|
opus = AutotoolsProject(
|
||||||
'http://downloads.xiph.org/releases/opus/opus-1.1.3.tar.gz',
|
'http://downloads.xiph.org/releases/opus/opus-1.1.4.tar.gz',
|
||||||
'32bbb6b557fe1b6066adc0ae1f08b629',
|
'9122b6b380081dd2665189f97bfd777f04f92dc3ab6698eea1dbb27ad59d8692',
|
||||||
'lib/libopus.a',
|
'lib/libopus.a',
|
||||||
['--disable-shared', '--enable-static'],
|
['--disable-shared', '--enable-static'],
|
||||||
)
|
)
|
||||||
|
@ -62,7 +62,7 @@ Print(Response &r, TagType group, const TagCountMap &m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
stats_visitor_song(SearchStats &stats, const LightSong &song)
|
stats_visitor_song(SearchStats &stats, const LightSong &song)
|
||||||
{
|
{
|
||||||
stats.n_songs++;
|
stats.n_songs++;
|
||||||
@ -70,8 +70,6 @@ stats_visitor_song(SearchStats &stats, const LightSong &song)
|
|||||||
const auto duration = song.GetDuration();
|
const auto duration = song.GetDuration();
|
||||||
if (!duration.IsNegative())
|
if (!duration.IsNegative())
|
||||||
stats.total_duration += duration;
|
stats.total_duration += duration;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -94,7 +92,7 @@ CollectGroupCounts(TagCountMap &map, TagType group, const Tag &tag)
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song)
|
GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song)
|
||||||
{
|
{
|
||||||
assert(song.tag != nullptr);
|
assert(song.tag != nullptr);
|
||||||
@ -103,8 +101,6 @@ GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song)
|
|||||||
if (!CollectGroupCounts(map, group, tag) && group == TAG_ALBUM_ARTIST)
|
if (!CollectGroupCounts(map, group, tag) && group == TAG_ALBUM_ARTIST)
|
||||||
/* fall back to "Artist" if no "AlbumArtist" was found */
|
/* fall back to "Artist" if no "AlbumArtist" was found */
|
||||||
CollectGroupCounts(map, TAG_ARTIST, tag);
|
CollectGroupCounts(map, TAG_ARTIST, tag);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -27,13 +27,12 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
AddSong(const Storage &storage, const char *playlist_path_utf8,
|
AddSong(const Storage &storage, const char *playlist_path_utf8,
|
||||||
const LightSong &song)
|
const LightSong &song)
|
||||||
{
|
{
|
||||||
spl_append_song(playlist_path_utf8,
|
spl_append_song(playlist_path_utf8,
|
||||||
DatabaseDetachSong(storage, song));
|
DatabaseDetachSong(storage, song));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -49,16 +49,14 @@ PrintDirectoryURI(Response &r, bool base, const LightDirectory &directory)
|
|||||||
ApplyBaseFlag(directory.GetPath(), base));
|
ApplyBaseFlag(directory.GetPath(), base));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintDirectoryBrief(Response &r, bool base, const LightDirectory &directory)
|
PrintDirectoryBrief(Response &r, bool base, const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
if (!directory.IsRoot())
|
if (!directory.IsRoot())
|
||||||
PrintDirectoryURI(r, base, directory);
|
PrintDirectoryURI(r, base, directory);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory)
|
PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
if (!directory.IsRoot()) {
|
if (!directory.IsRoot()) {
|
||||||
@ -67,8 +65,6 @@ PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory)
|
|||||||
if (directory.mtime > 0)
|
if (directory.mtime > 0)
|
||||||
time_print(r, "Last-Modified", directory.mtime);
|
time_print(r, "Last-Modified", directory.mtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -96,7 +92,7 @@ print_playlist_in_directory(Response &r, bool base,
|
|||||||
directory->GetPath(), name_utf8);
|
directory->GetPath(), name_utf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintSongBrief(Response &r, Partition &partition,
|
PrintSongBrief(Response &r, Partition &partition,
|
||||||
bool base, const LightSong &song)
|
bool base, const LightSong &song)
|
||||||
{
|
{
|
||||||
@ -106,11 +102,9 @@ PrintSongBrief(Response &r, Partition &partition,
|
|||||||
/* this song file has an embedded CUE sheet */
|
/* this song file has an embedded CUE sheet */
|
||||||
print_playlist_in_directory(r, base,
|
print_playlist_in_directory(r, base,
|
||||||
song.directory, song.uri);
|
song.directory, song.uri);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintSongFull(Response &r, Partition &partition,
|
PrintSongFull(Response &r, Partition &partition,
|
||||||
bool base, const LightSong &song)
|
bool base, const LightSong &song)
|
||||||
{
|
{
|
||||||
@ -120,21 +114,18 @@ PrintSongFull(Response &r, Partition &partition,
|
|||||||
/* this song file has an embedded CUE sheet */
|
/* this song file has an embedded CUE sheet */
|
||||||
print_playlist_in_directory(r, base,
|
print_playlist_in_directory(r, base,
|
||||||
song.directory, song.uri);
|
song.directory, song.uri);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintPlaylistBrief(Response &r, bool base,
|
PrintPlaylistBrief(Response &r, bool base,
|
||||||
const PlaylistInfo &playlist,
|
const PlaylistInfo &playlist,
|
||||||
const LightDirectory &directory)
|
const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
print_playlist_in_directory(r, base,
|
print_playlist_in_directory(r, base,
|
||||||
&directory, playlist.name.c_str());
|
&directory, playlist.name.c_str());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintPlaylistFull(Response &r, bool base,
|
PrintPlaylistFull(Response &r, bool base,
|
||||||
const PlaylistInfo &playlist,
|
const PlaylistInfo &playlist,
|
||||||
const LightDirectory &directory)
|
const LightDirectory &directory)
|
||||||
@ -144,8 +135,6 @@ PrintPlaylistFull(Response &r, bool base,
|
|||||||
|
|
||||||
if (playlist.mtime > 0)
|
if (playlist.mtime > 0)
|
||||||
time_print(r, "Last-Modified", playlist.mtime);
|
time_print(r, "Last-Modified", playlist.mtime);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -191,15 +180,13 @@ db_selection_print(Response &r, Partition &partition,
|
|||||||
0, std::numeric_limits<int>::max());
|
0, std::numeric_limits<int>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintSongURIVisitor(Response &r, Partition &partition, const LightSong &song)
|
PrintSongURIVisitor(Response &r, Partition &partition, const LightSong &song)
|
||||||
{
|
{
|
||||||
song_print_uri(r, partition, song);
|
song_print_uri(r, partition, song);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
PrintUniqueTag(Response &r, TagType tag_type,
|
PrintUniqueTag(Response &r, TagType tag_type,
|
||||||
const Tag &tag)
|
const Tag &tag)
|
||||||
{
|
{
|
||||||
@ -211,8 +198,6 @@ PrintUniqueTag(Response &r, TagType tag_type,
|
|||||||
if (item.type != tag_type)
|
if (item.type != tag_type)
|
||||||
r.Format("%s: %s\n",
|
r.Format("%s: %s\n",
|
||||||
tag_item_names[item.type], item.value);
|
tag_item_names[item.type], item.value);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -27,14 +27,13 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
AddToQueue(Partition &partition, const LightSong &song)
|
AddToQueue(Partition &partition, const LightSong &song)
|
||||||
{
|
{
|
||||||
const Storage &storage = *partition.instance.storage;
|
const Storage &storage = *partition.instance.storage;
|
||||||
partition.playlist.AppendSong(partition.pc,
|
partition.playlist.AppendSong(partition.pc,
|
||||||
DatabaseDetachSong(storage,
|
DatabaseDetachSong(storage,
|
||||||
song));
|
song));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -67,15 +67,13 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
|
StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
|
||||||
const LightSong &song)
|
const LightSong &song)
|
||||||
{
|
{
|
||||||
++stats.song_count;
|
++stats.song_count;
|
||||||
|
|
||||||
StatsVisitTag(stats, artists, albums, *song.tag);
|
StatsVisitTag(stats, artists, albums, *song.tag);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseStats
|
DatabaseStats
|
||||||
|
@ -396,6 +396,17 @@ NfsConnection::ScheduleSocket()
|
|||||||
assert(GetEventLoop().IsInside());
|
assert(GetEventLoop().IsInside());
|
||||||
assert(context != nullptr);
|
assert(context != nullptr);
|
||||||
|
|
||||||
|
const int which_events = nfs_which_events(context);
|
||||||
|
|
||||||
|
if (which_events == POLLOUT && SocketMonitor::IsDefined())
|
||||||
|
/* kludge: if libnfs asks only for POLLOUT, it means
|
||||||
|
that it is currently waiting for the connect() to
|
||||||
|
finish - rpc_reconnect_requeue() may have been
|
||||||
|
called from inside nfs_service(); we must now
|
||||||
|
unregister the old socket and register the new one
|
||||||
|
instead */
|
||||||
|
SocketMonitor::Steal();
|
||||||
|
|
||||||
if (!SocketMonitor::IsDefined()) {
|
if (!SocketMonitor::IsDefined()) {
|
||||||
int _fd = nfs_get_fd(context);
|
int _fd = nfs_get_fd(context);
|
||||||
if (_fd < 0)
|
if (_fd < 0)
|
||||||
@ -405,7 +416,8 @@ NfsConnection::ScheduleSocket()
|
|||||||
SocketMonitor::Open(_fd);
|
SocketMonitor::Open(_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketMonitor::Schedule(libnfs_to_events(nfs_which_events(context)));
|
SocketMonitor::Schedule(libnfs_to_events(which_events)
|
||||||
|
| SocketMonitor::HANGUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int
|
inline int
|
||||||
@ -442,10 +454,14 @@ NfsConnection::OnSocketReady(unsigned flags)
|
|||||||
bool closed = false;
|
bool closed = false;
|
||||||
|
|
||||||
const bool was_mounted = mount_finished;
|
const bool was_mounted = mount_finished;
|
||||||
if (!mount_finished)
|
if (!mount_finished || (flags & SocketMonitor::HANGUP) != 0)
|
||||||
/* until the mount is finished, the NFS client may use
|
/* until the mount is finished, the NFS client may use
|
||||||
various sockets, therefore we unregister and
|
various sockets, therefore we unregister and
|
||||||
re-register it each time */
|
re-register it each time */
|
||||||
|
/* also re-register the socket if we got a HANGUP,
|
||||||
|
which is a sure sign that libnfs will close the
|
||||||
|
socket, which can lead to a race condition if
|
||||||
|
epoll_ctl() is called later */
|
||||||
SocketMonitor::Steal();
|
SocketMonitor::Steal();
|
||||||
|
|
||||||
const int result = Service(flags);
|
const int result = Service(flags);
|
||||||
|
@ -24,8 +24,16 @@
|
|||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
|
/* work around a C++ incompatibility if the sndio API is emulated by
|
||||||
|
libroar: libroar's "struct roar_service_stream" has a member named
|
||||||
|
"new", which is an illegal identifier in C++ */
|
||||||
|
#define new new_
|
||||||
|
|
||||||
#include <sndio.h>
|
#include <sndio.h>
|
||||||
|
|
||||||
|
/* undo the libroar workaround */
|
||||||
|
#undef new
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#ifndef SIO_DEVANY
|
#ifndef SIO_DEVANY
|
||||||
|
@ -64,7 +64,10 @@ enum class SampleFormat : uint8_t {
|
|||||||
/**
|
/**
|
||||||
* Checks whether the sample format is valid.
|
* Checks whether the sample format is valid.
|
||||||
*/
|
*/
|
||||||
static constexpr inline bool
|
#if !GCC_OLDER_THAN(5,0)
|
||||||
|
constexpr
|
||||||
|
#endif
|
||||||
|
static inline bool
|
||||||
audio_valid_sample_format(SampleFormat format)
|
audio_valid_sample_format(SampleFormat format)
|
||||||
{
|
{
|
||||||
switch (format) {
|
switch (format) {
|
||||||
@ -83,7 +86,10 @@ audio_valid_sample_format(SampleFormat format)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr inline unsigned
|
#if !GCC_OLDER_THAN(5,0)
|
||||||
|
constexpr
|
||||||
|
#endif
|
||||||
|
static inline unsigned
|
||||||
sample_format_size(SampleFormat format)
|
sample_format_size(SampleFormat format)
|
||||||
{
|
{
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate and construct a variable-size object. That is useful for
|
* Allocate and construct a variable-size object. That is useful for
|
||||||
|
Loading…
Reference in New Issue
Block a user