Merge branch 'v0.21.x' into master
This commit is contained in:
commit
0acc398c52
6
NEWS
6
NEWS
|
@ -40,6 +40,8 @@ ver 0.22 (not yet released)
|
||||||
- GCC 7 or clang 4 (or newer) recommended
|
- GCC 7 or clang 4 (or newer) recommended
|
||||||
|
|
||||||
ver 0.21.26 (not yet released)
|
ver 0.21.26 (not yet released)
|
||||||
|
* database
|
||||||
|
- inotify: obey ".mpdignore" files
|
||||||
* output
|
* output
|
||||||
- osx: fix crash bug
|
- osx: fix crash bug
|
||||||
- sles: support floating point samples
|
- sles: support floating point samples
|
||||||
|
@ -50,7 +52,11 @@ ver 0.21.26 (not yet released)
|
||||||
- iso9660: support seeking
|
- iso9660: support seeking
|
||||||
- zzip: fix crash on corrupt ZIP file
|
- zzip: fix crash on corrupt ZIP file
|
||||||
* decoder
|
* decoder
|
||||||
|
- ffmpeg: remove "rtsp://" from the list of supported protocols
|
||||||
|
- ffmpeg: add "hls+http://" to the list of supported protocols
|
||||||
- sndfile: fix lost samples at end of file
|
- sndfile: fix lost samples at end of file
|
||||||
|
* fix "single" mode bug after resuming playback
|
||||||
|
* the default log_level is "default", not "info"
|
||||||
|
|
||||||
ver 0.21.25 (2020/07/06)
|
ver 0.21.25 (2020/07/06)
|
||||||
* protocol:
|
* protocol:
|
||||||
|
|
|
@ -50,9 +50,7 @@ gcc_pure
|
||||||
static bool
|
static bool
|
||||||
SkipNameFS(PathTraitsFS::const_pointer name_fs) noexcept
|
SkipNameFS(PathTraitsFS::const_pointer name_fs) noexcept
|
||||||
{
|
{
|
||||||
return name_fs[0] == '.' &&
|
return PathTraitsFS::IsSpecialFilename(name_fs);
|
||||||
(name_fs[1] == 0 ||
|
|
||||||
(name_fs[1] == '.' && name_fs[2] == 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
|
|
|
@ -21,19 +21,25 @@
|
||||||
#include "InotifySource.hxx"
|
#include "InotifySource.hxx"
|
||||||
#include "InotifyQueue.hxx"
|
#include "InotifyQueue.hxx"
|
||||||
#include "InotifyDomain.hxx"
|
#include "InotifyDomain.hxx"
|
||||||
|
#include "ExcludeList.hxx"
|
||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
|
#include "input/InputStream.hxx"
|
||||||
|
#include "input/Error.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
|
#include "fs/DirectoryReader.hxx"
|
||||||
#include "fs/FileInfo.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
|
#include "thread/Mutex.hxx"
|
||||||
|
#include "util/Compiler.h"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
#include <forward_list>
|
#include <forward_list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <string.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
|
||||||
static constexpr unsigned IN_MASK =
|
static constexpr unsigned IN_MASK =
|
||||||
|
@ -50,17 +56,28 @@ struct WatchDirectory {
|
||||||
|
|
||||||
int descriptor;
|
int descriptor;
|
||||||
|
|
||||||
|
ExcludeList exclude_list;
|
||||||
|
|
||||||
std::forward_list<WatchDirectory> children;
|
std::forward_list<WatchDirectory> children;
|
||||||
|
|
||||||
template<typename N>
|
template<typename N>
|
||||||
WatchDirectory(WatchDirectory *_parent, N &&_name,
|
WatchDirectory(N &&_name,
|
||||||
int _descriptor)
|
int _descriptor)
|
||||||
:parent(_parent), name(std::forward<N>(_name)),
|
:parent(nullptr), name(std::forward<N>(_name)),
|
||||||
descriptor(_descriptor) {}
|
descriptor(_descriptor) {}
|
||||||
|
|
||||||
|
template<typename N>
|
||||||
|
WatchDirectory(WatchDirectory &_parent, N &&_name,
|
||||||
|
int _descriptor)
|
||||||
|
:parent(&_parent), name(std::forward<N>(_name)),
|
||||||
|
descriptor(_descriptor),
|
||||||
|
exclude_list(_parent.exclude_list) {}
|
||||||
|
|
||||||
WatchDirectory(const WatchDirectory &) = delete;
|
WatchDirectory(const WatchDirectory &) = delete;
|
||||||
WatchDirectory &operator=(const WatchDirectory &) = delete;
|
WatchDirectory &operator=(const WatchDirectory &) = delete;
|
||||||
|
|
||||||
|
void LoadExcludeList(Path directory_path) noexcept;
|
||||||
|
|
||||||
[[nodiscard]] gcc_pure
|
[[nodiscard]] gcc_pure
|
||||||
unsigned GetDepth() const noexcept;
|
unsigned GetDepth() const noexcept;
|
||||||
|
|
||||||
|
@ -68,6 +85,18 @@ struct WatchDirectory {
|
||||||
AllocatedPath GetUriFS() const noexcept;
|
AllocatedPath GetUriFS() const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
WatchDirectory::LoadExcludeList(Path directory_path) noexcept
|
||||||
|
try {
|
||||||
|
Mutex mutex;
|
||||||
|
auto is = InputStream::OpenReady((directory_path / Path::FromFS(".mpdignore")).c_str(),
|
||||||
|
mutex);
|
||||||
|
exclude_list.Load(std::move(is));
|
||||||
|
} catch (...) {
|
||||||
|
if (!IsFileNotFound(std::current_exception()))
|
||||||
|
LogError(std::current_exception());
|
||||||
|
}
|
||||||
|
|
||||||
static InotifySource *inotify_source;
|
static InotifySource *inotify_source;
|
||||||
static InotifyQueue *inotify_queue;
|
static InotifyQueue *inotify_queue;
|
||||||
|
|
||||||
|
@ -145,20 +174,19 @@ WatchDirectory::GetUriFS() const noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we don't look at "." / ".." nor files with newlines in their name */
|
/* we don't look at "." / ".." nor files with newlines in their name */
|
||||||
static bool skip_path(const char *path)
|
gcc_pure
|
||||||
|
static bool
|
||||||
|
SkipFilename(Path name) noexcept
|
||||||
{
|
{
|
||||||
return PathTraitsFS::IsSpecialFilename(path) ||
|
return PathTraitsFS::IsSpecialFilename(name.c_str()) ||
|
||||||
std::strchr(path, '\n') != nullptr;
|
name.HasNewline();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
recursive_watch_subdirectories(WatchDirectory *directory,
|
recursive_watch_subdirectories(WatchDirectory &parent,
|
||||||
const AllocatedPath &path_fs, unsigned depth)
|
const Path path_fs,
|
||||||
{
|
unsigned depth)
|
||||||
DIR *dir;
|
try {
|
||||||
struct dirent *ent;
|
|
||||||
|
|
||||||
assert(directory != nullptr);
|
|
||||||
assert(depth <= inotify_max_depth);
|
assert(depth <= inotify_max_depth);
|
||||||
assert(!path_fs.IsNull());
|
assert(!path_fs.IsNull());
|
||||||
|
|
||||||
|
@ -167,20 +195,17 @@ recursive_watch_subdirectories(WatchDirectory *directory,
|
||||||
if (depth > inotify_max_depth)
|
if (depth > inotify_max_depth)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dir = opendir(path_fs.c_str());
|
DirectoryReader dir(path_fs);
|
||||||
if (dir == nullptr) {
|
while (dir.ReadEntry()) {
|
||||||
FormatErrno(inotify_domain,
|
|
||||||
"Failed to open directory %s", path_fs.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((ent = readdir(dir))) {
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (skip_path(ent->d_name))
|
const Path name_fs = dir.GetEntry();
|
||||||
|
if (SkipFilename(name_fs))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (parent.exclude_list.Check(name_fs))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto name_fs = Path::FromFS(ent->d_name);
|
|
||||||
const auto child_path_fs = path_fs / name_fs;
|
const auto child_path_fs = path_fs / name_fs;
|
||||||
|
|
||||||
FileInfo fi;
|
FileInfo fi;
|
||||||
|
@ -209,17 +234,18 @@ recursive_watch_subdirectories(WatchDirectory *directory,
|
||||||
/* already being watched */
|
/* already being watched */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
directory->children.emplace_front(directory,
|
parent.children.emplace_front(parent,
|
||||||
name_fs,
|
name_fs,
|
||||||
ret);
|
ret);
|
||||||
child = &directory->children.front();
|
child = &parent.children.front();
|
||||||
|
child->LoadExcludeList(child_path_fs);
|
||||||
|
|
||||||
tree_add_watch_directory(child);
|
tree_add_watch_directory(child);
|
||||||
|
|
||||||
recursive_watch_subdirectories(child, child_path_fs, depth);
|
recursive_watch_subdirectories(*child, child_path_fs, depth);
|
||||||
}
|
}
|
||||||
|
} catch (...) {
|
||||||
closedir(dir);
|
LogError(std::current_exception());
|
||||||
}
|
}
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
|
@ -240,8 +266,6 @@ mpd_inotify_callback(int wd, unsigned mask,
|
||||||
{
|
{
|
||||||
WatchDirectory *directory;
|
WatchDirectory *directory;
|
||||||
|
|
||||||
/*FormatDebug(inotify_domain, "wd=%d mask=0x%x name='%s'", wd, mask, name);*/
|
|
||||||
|
|
||||||
directory = tree_find_watch_directory(wd);
|
directory = tree_find_watch_directory(wd);
|
||||||
if (directory == nullptr)
|
if (directory == nullptr)
|
||||||
return;
|
return;
|
||||||
|
@ -263,7 +287,7 @@ mpd_inotify_callback(int wd, unsigned mask,
|
||||||
? root
|
? root
|
||||||
: (root / uri_fs);
|
: (root / uri_fs);
|
||||||
|
|
||||||
recursive_watch_subdirectories(directory, path_fs,
|
recursive_watch_subdirectories(*directory, path_fs,
|
||||||
directory->GetDepth());
|
directory->GetDepth());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,11 +342,12 @@ mpd_inotify_init(EventLoop &loop, Storage &storage, UpdateService &update,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inotify_root = new WatchDirectory(nullptr, path, descriptor);
|
inotify_root = new WatchDirectory(path, descriptor);
|
||||||
|
inotify_root->LoadExcludeList(path);
|
||||||
|
|
||||||
tree_add_watch_directory(inotify_root);
|
tree_add_watch_directory(inotify_root);
|
||||||
|
|
||||||
recursive_watch_subdirectories(inotify_root, path, 0);
|
recursive_watch_subdirectories(*inotify_root, path, 0);
|
||||||
|
|
||||||
inotify_queue = new InotifyQueue(loop, update);
|
inotify_queue = new InotifyQueue(loop, update);
|
||||||
|
|
||||||
|
|
|
@ -443,7 +443,7 @@ Player::ActivateDecoder() noexcept
|
||||||
pc.audio_format.Clear();
|
pc.audio_format.Clear();
|
||||||
|
|
||||||
{
|
{
|
||||||
/* call syncPlaylistWithQueue() in the main thread */
|
/* call playlist::SyncWithPlayer() in the main thread */
|
||||||
const ScopeUnlock unlock(pc.mutex);
|
const ScopeUnlock unlock(pc.mutex);
|
||||||
pc.listener.OnPlayerSync();
|
pc.listener.OnPlayerSync();
|
||||||
}
|
}
|
||||||
|
@ -684,6 +684,12 @@ Player::SeekDecoder(std::unique_lock<Mutex> &lock) noexcept
|
||||||
/* re-fill the buffer after seeking */
|
/* re-fill the buffer after seeking */
|
||||||
buffering = true;
|
buffering = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
/* call syncPlaylistWithQueue() in the main thread */
|
||||||
|
const ScopeUnlock unlock(pc.mutex);
|
||||||
|
pc.listener.OnPlayerSync();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1175,6 +1181,11 @@ try {
|
||||||
{
|
{
|
||||||
const ScopeUnlock unlock(mutex);
|
const ScopeUnlock unlock(mutex);
|
||||||
do_play(*this, dc, buffer);
|
do_play(*this, dc, buffer);
|
||||||
|
|
||||||
|
/* give the main thread a chance to
|
||||||
|
queue another song, just in case
|
||||||
|
we've stopped playback
|
||||||
|
spuriously */
|
||||||
listener.OnPlayerSync();
|
listener.OnPlayerSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -305,9 +305,7 @@ gcc_pure
|
||||||
static bool
|
static bool
|
||||||
SkipNameFS(PathTraitsFS::const_pointer name) noexcept
|
SkipNameFS(PathTraitsFS::const_pointer name) noexcept
|
||||||
{
|
{
|
||||||
return name[0] == '.' &&
|
return PathTraitsFS::IsSpecialFilename(name);
|
||||||
(name[1] == 0 ||
|
|
||||||
(name[1] == '.' && name[2] == 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -151,11 +151,9 @@ SmbclientStorage::OpenDirectory(std::string_view uri_utf8)
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
static bool
|
static bool
|
||||||
SkipNameFS(const char *name) noexcept
|
SkipNameFS(PathTraitsFS::const_pointer name) noexcept
|
||||||
{
|
{
|
||||||
return name[0] == '.' &&
|
return PathTraitsFS::IsSpecialFilename(name);
|
||||||
(name[1] == 0 ||
|
|
||||||
(name[1] == '.' && name[2] == 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SmbclientDirectoryReader::~SmbclientDirectoryReader()
|
SmbclientDirectoryReader::~SmbclientDirectoryReader()
|
||||||
|
|
Loading…
Reference in New Issue