Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1930d5774d | ||
|
|
7220a76be0 | ||
|
|
83f7610dd1 | ||
|
|
30e0644722 | ||
|
|
3ada464020 | ||
|
|
d5983dd362 | ||
|
|
98258acc37 | ||
|
|
8002bc752f | ||
|
|
834ad7a58f | ||
|
|
e8f2f98048 | ||
|
|
c672b60d07 | ||
|
|
ea269c9c92 | ||
|
|
1fe3a77640 | ||
|
|
bbaeea1ab7 | ||
|
|
0a3aee9d82 | ||
|
|
2434020971 | ||
|
|
41e0eb7378 | ||
|
|
6adf964c81 | ||
|
|
b59f37bc0a | ||
|
|
cf2d171ccc | ||
|
|
cc28a7b67f | ||
|
|
8b5c33cecd | ||
|
|
6c28adbcd2 | ||
|
|
2125e3ed57 | ||
|
|
3da7ecfadf | ||
|
|
5bb02bbd39 | ||
|
|
f11aa09f7c | ||
|
|
02eb4752d3 | ||
|
|
d9c3215584 | ||
|
|
110e6d026b | ||
|
|
c0f57b8a8b | ||
|
|
57633fbcb3 | ||
|
|
864c87e6c0 | ||
|
|
1a516cf3c0 | ||
|
|
5c25499c5e | ||
|
|
da4bb4c298 | ||
|
|
5b8ff61799 | ||
|
|
56bded07b1 | ||
|
|
db144a43ad | ||
|
|
5965f62b56 | ||
|
|
05aa9f72a9 | ||
|
|
281461f0f0 | ||
|
|
f70eb63879 | ||
|
|
99c23cf139 | ||
|
|
9aa75e738c | ||
|
|
e9c45a9140 | ||
|
|
a065c6e6b9 | ||
|
|
feb5ff9bd2 | ||
|
|
92ec3f0881 | ||
|
|
98c47d9d36 | ||
|
|
6c67408944 | ||
|
|
261a816b21 | ||
|
|
7a23c123c8 | ||
|
|
e85b24bee0 | ||
|
|
9e73ea77b4 | ||
|
|
b0739eca87 | ||
|
|
848f6aa5ab | ||
|
|
c9ba4f3f9c | ||
|
|
c0e9246a66 | ||
|
|
096c23f27d | ||
|
|
40bde1eac9 | ||
|
|
4b55ed17a9 | ||
|
|
4f757a5add | ||
|
|
674c137e5f | ||
|
|
ff1ff1e54a | ||
|
|
42b22187c8 | ||
|
|
cfe22502ab | ||
|
|
d77b0c7dcd | ||
|
|
5cf889b676 | ||
|
|
ffc36d5255 | ||
|
|
0126276e2f | ||
|
|
58d6ddab9e | ||
|
|
05db6934eb | ||
|
|
02c68c5cdb | ||
|
|
b02fee7309 | ||
|
|
424f75c9e1 | ||
|
|
f6e1176f97 | ||
|
|
e4700c0a27 | ||
|
|
cf23fd8774 | ||
|
|
dee8872395 | ||
|
|
4ba9357a9c | ||
|
|
48ec09ab1e | ||
|
|
754f4048a8 | ||
|
|
037bb07d08 | ||
|
|
87635c5268 | ||
|
|
528b4338f4 | ||
|
|
c780b8bba9 | ||
|
|
ca34f3250b | ||
|
|
6a68e1c3f3 | ||
|
|
85f77ec81d | ||
|
|
37debed0b8 | ||
|
|
008383f24a | ||
|
|
4f7d52dbf2 | ||
|
|
c7848da8f2 | ||
|
|
10a6c5c57d | ||
|
|
2cc2bab309 | ||
|
|
701fd1d939 | ||
|
|
d1bdea8edb | ||
|
|
0cea67ee70 | ||
|
|
3a0480a482 | ||
|
|
1fa99da3c2 | ||
|
|
22d669da18 | ||
|
|
772681f23d | ||
|
|
1862a98a44 |
20
NEWS
20
NEWS
@@ -1,3 +1,23 @@
|
||||
ver 0.21.5 (2019/02/22)
|
||||
* protocol
|
||||
- fix deadlock in "albumart" command
|
||||
- fix "tagtypes disable" command
|
||||
* database
|
||||
- simple: fix assertion failure
|
||||
- fix assertion failures with mount points
|
||||
* storage
|
||||
- udisks: fix "AlreadyMounted" error
|
||||
- udisks: use relative path from mount URI
|
||||
- fix memory leak
|
||||
* input
|
||||
- buffer: fix crash bug when playing remote WAV file
|
||||
* tags
|
||||
- ape: map "Album Artist"
|
||||
* output
|
||||
- shout: add support for TLS
|
||||
* mixer
|
||||
- pulse: add "scale_volume" setting
|
||||
|
||||
ver 0.21.4 (2019/01/04)
|
||||
* database
|
||||
- inotify: fix crash bug "terminate called after throwing ..."
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="26"
|
||||
android:versionName="0.21.4">
|
||||
android:versionCode="27"
|
||||
android:versionName="0.21.5">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# This is a wrapper for pkg-config which helps with cross-compiling;
|
||||
# it sets up environment variables to pkg-config searches for
|
||||
# libraries in the sysroot where a copy of this script is located.
|
||||
|
||||
BIN=`dirname $0`
|
||||
ROOT=`dirname "$BIN"`
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.21.4'
|
||||
version = '0.21.5'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
||||
@@ -87,6 +87,22 @@ Mount file systems (e.g. USB sticks or other removable media) using
|
||||
the udisks2 daemon via D-Bus. To obtain a valid udisks2 URI, consult
|
||||
:ref:`the according neighbor plugin <neighbor_plugin>`.
|
||||
|
||||
It might be necessary to grant :program:`MPD` privileges to control
|
||||
:program:`udisks2` through :program:`policykit`. To do this, create a
|
||||
file called :file:`/usr/share/polkit-1/rules.d/mpd-udisks.rules` with
|
||||
the following text::
|
||||
|
||||
polkit.addRule(function(action, subject) {
|
||||
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
||||
action.id == "org.freedesktop.udisks2.filesystem-mount-other-seat") &&
|
||||
subject.user == "mpd") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
|
||||
If you run MPD as a different user, change ``mpd`` to the name of your
|
||||
MPD user.
|
||||
|
||||
.. _neighbor_plugin:
|
||||
|
||||
Neighbor plugins
|
||||
@@ -935,6 +951,8 @@ The pulse plugin connects to a `PulseAudio <http://www.freedesktop.org/wiki/Soft
|
||||
- Sets the host name of the PulseAudio server. By default, :program:`MPD` connects to the local PulseAudio server.
|
||||
* - **sink NAME**
|
||||
- Specifies the name of the PulseAudio sink :program:`MPD` should play on.
|
||||
* - **scale_volume FACTOR**
|
||||
- Specifies a linear scaling coefficient (ranging from 0.5 to 5.0) to apply when adjusting volume through :program:`MPD`. For example, chosing a factor equal to ``"0.7"`` means that setting the volume to 100 in :program:`MPD` will set the PulseAudio volume to 70%, and a factor equal to ``"3.5"`` means that volume 100 in :program:`MPD` corresponds to a 350% PulseAudio volume.
|
||||
|
||||
recorder
|
||||
~~~~~~~~
|
||||
@@ -974,6 +992,8 @@ You must set a format.
|
||||
- Set the timeout for the shout connection in seconds. Defaults to 2 seconds.
|
||||
* - **protocol icecast2|icecast1|shoutcast**
|
||||
- Specifies the protocol that wil be used to connect to the server. The default is "icecast2".
|
||||
* - **tls disabled|auto|auto_no_plain|rfc2818|rfc2817**
|
||||
- Specifies what kind of TLS to use. The default is "disabled" (no TLS).
|
||||
* - **mount URI**
|
||||
- Mounts the :program:`MPD` stream in the specified URI.
|
||||
* - **user USERNAME**
|
||||
|
||||
@@ -909,7 +909,7 @@ The music database
|
||||
|
||||
.. _command_lsinfo:
|
||||
|
||||
:command:`lsinfo {URI}`
|
||||
:command:`lsinfo [URI]`
|
||||
Lists the contents of the directory
|
||||
``URI``. The response contains records
|
||||
starting with ``file``,
|
||||
@@ -1131,7 +1131,7 @@ Connection settings
|
||||
``tagtypes`` sub commands configure this
|
||||
list.
|
||||
|
||||
:command:`tagtypes disable {NAME...]`
|
||||
:command:`tagtypes disable {NAME...}`
|
||||
Remove one or more tags from the list of tag types the
|
||||
client is interested in. These will be omitted from
|
||||
responses to this client.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21.4',
|
||||
version: '0.21.5',
|
||||
meson_version: '>= 0.47.2',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
|
||||
@@ -128,6 +128,7 @@ option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
|
||||
option('opus', type: 'feature', description: 'Opus decoder plugin')
|
||||
option('sidplay', type: 'feature', description: 'C64 SID support via libsidplayfp or libsidplay2')
|
||||
option('sndfile', type: 'feature', description: 'libsndfile decoder plugin')
|
||||
option('tremor', type: 'feature', description: 'Fixed-point vorbis decoder plugin')
|
||||
option('vorbis', type: 'feature', description: 'Vorbis decoder plugin')
|
||||
option('wavpack', type: 'feature', description: 'WavPack decoder plugin')
|
||||
option('wildmidi', type: 'feature', description: 'WildMidi decoder plugin')
|
||||
|
||||
@@ -112,8 +112,8 @@ liblame = AutotoolsProject(
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.1.tar.xz',
|
||||
'a38ec4d026efb58506a99ad5cd23d5a9793b4bf415f2c4c2e9c1bb444acd1994',
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.1.1.tar.xz',
|
||||
'373749824dfd334d84e55dff406729edfd1606575ee44dd485d97d45ea4d2d86',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.62.0.tar.xz',
|
||||
'dab5643a5fe775ae92570b9f3df6b0ef4bc2a827a959361fb130c73b721275c1',
|
||||
'http://curl.haxx.se/download/curl-7.64.0.tar.xz',
|
||||
'2f2f13fa34d44aa29cb444077ad7dc4dc6d189584ad552e0aaeb06e608af6001',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -392,7 +392,7 @@ libnfs = AutotoolsProject(
|
||||
)
|
||||
|
||||
boost = BoostProject(
|
||||
'http://downloads.sourceforge.net/project/boost/boost/1.68.0/boost_1_68_0.tar.bz2',
|
||||
'7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7',
|
||||
'http://downloads.sourceforge.net/project/boost/boost/1.69.0/boost_1_69_0.tar.bz2',
|
||||
'8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406',
|
||||
'include/boost/version.hpp',
|
||||
)
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/DatabaseError.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
#include "db/update/Service.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
#include "sticker/StickerDatabase.hxx"
|
||||
@@ -48,7 +51,19 @@ Instance::Instance()
|
||||
{
|
||||
}
|
||||
|
||||
Instance::~Instance() noexcept = default;
|
||||
Instance::~Instance() noexcept
|
||||
{
|
||||
#ifdef ENABLE_DATABASE
|
||||
delete update;
|
||||
|
||||
if (database != nullptr) {
|
||||
database->Close();
|
||||
database.reset();
|
||||
}
|
||||
|
||||
delete storage;
|
||||
#endif
|
||||
}
|
||||
|
||||
Partition *
|
||||
Instance::FindPartition(const char *name) noexcept
|
||||
|
||||
@@ -41,7 +41,7 @@ class NeighborGlue;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/DatabaseListener.hxx"
|
||||
class Database;
|
||||
#include "db/Ptr.hxx"
|
||||
class Storage;
|
||||
class UpdateService;
|
||||
#endif
|
||||
@@ -104,7 +104,7 @@ struct Instance final
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
Database *database;
|
||||
DatabasePtr database;
|
||||
|
||||
/**
|
||||
* This is really a #CompositeStorage. To avoid heavy include
|
||||
@@ -147,7 +147,6 @@ struct Instance final
|
||||
Partition *FindPartition(const char *name) noexcept;
|
||||
|
||||
void BeginShutdownPartitions() noexcept;
|
||||
void FinishShutdownPartitions() noexcept;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
/**
|
||||
@@ -156,7 +155,7 @@ struct Instance final
|
||||
* music_directory was configured).
|
||||
*/
|
||||
Database *GetDatabase() {
|
||||
return database;
|
||||
return database.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,8 +167,6 @@ struct Instance final
|
||||
#endif
|
||||
|
||||
void BeginShutdownUpdate() noexcept;
|
||||
void FinishShutdownUpdate() noexcept;
|
||||
void ShutdownDatabase() noexcept;
|
||||
|
||||
#ifdef ENABLE_CURL
|
||||
void LookupRemoteTag(const char *uri) noexcept;
|
||||
|
||||
@@ -109,7 +109,7 @@ parse_log_level(const char *value, int line)
|
||||
#endif
|
||||
|
||||
void
|
||||
log_early_init(bool verbose)
|
||||
log_early_init(bool verbose) noexcept
|
||||
{
|
||||
#ifdef ANDROID
|
||||
(void)verbose;
|
||||
@@ -171,7 +171,7 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
|
||||
#ifndef ANDROID
|
||||
|
||||
static void
|
||||
close_log_files(void)
|
||||
close_log_files() noexcept
|
||||
{
|
||||
#ifdef HAVE_SYSLOG
|
||||
LogFinishSysLog();
|
||||
@@ -181,7 +181,7 @@ close_log_files(void)
|
||||
#endif
|
||||
|
||||
void
|
||||
log_deinit(void)
|
||||
log_deinit() noexcept
|
||||
{
|
||||
#ifndef ANDROID
|
||||
close_log_files();
|
||||
@@ -213,7 +213,8 @@ void setup_log_output()
|
||||
#endif
|
||||
}
|
||||
|
||||
int cycle_log_files(void)
|
||||
int
|
||||
cycle_log_files() noexcept
|
||||
{
|
||||
#ifdef ANDROID
|
||||
return 0;
|
||||
|
||||
@@ -31,7 +31,7 @@ struct ConfigData;
|
||||
* @param verbose true when the program is started with --verbose
|
||||
*/
|
||||
void
|
||||
log_early_init(bool verbose);
|
||||
log_early_init(bool verbose) noexcept;
|
||||
|
||||
/**
|
||||
* Throws #std::runtime_error on error.
|
||||
@@ -40,12 +40,12 @@ void
|
||||
log_init(const ConfigData &config, bool verbose, bool use_stdout);
|
||||
|
||||
void
|
||||
log_deinit();
|
||||
log_deinit() noexcept;
|
||||
|
||||
void
|
||||
setup_log_output();
|
||||
|
||||
int
|
||||
cycle_log_files();
|
||||
cycle_log_files() noexcept;
|
||||
|
||||
#endif /* LOG_H */
|
||||
|
||||
89
src/Main.cxx
89
src/Main.cxx
@@ -185,19 +185,16 @@ InitStorage(const ConfigData &config, EventLoop &event_loop)
|
||||
static bool
|
||||
glue_db_init_and_load(const ConfigData &config)
|
||||
{
|
||||
instance->database =
|
||||
CreateConfiguredDatabase(config, instance->event_loop,
|
||||
instance->io_thread.GetEventLoop(),
|
||||
*instance);
|
||||
if (instance->database == nullptr)
|
||||
auto db = CreateConfiguredDatabase(config, instance->event_loop,
|
||||
instance->io_thread.GetEventLoop(),
|
||||
*instance);
|
||||
if (!db)
|
||||
return true;
|
||||
|
||||
if (instance->database->GetPlugin().RequireStorage()) {
|
||||
if (db->GetPlugin().RequireStorage()) {
|
||||
InitStorage(config, instance->io_thread.GetEventLoop());
|
||||
|
||||
if (instance->storage == nullptr) {
|
||||
delete instance->database;
|
||||
instance->database = nullptr;
|
||||
LogDefault(config_domain,
|
||||
"Found database setting without "
|
||||
"music_directory - disabling database");
|
||||
@@ -211,22 +208,24 @@ glue_db_init_and_load(const ConfigData &config)
|
||||
}
|
||||
|
||||
try {
|
||||
instance->database->Open();
|
||||
db->Open();
|
||||
} catch (...) {
|
||||
std::throw_with_nested(std::runtime_error("Failed to open database plugin"));
|
||||
}
|
||||
|
||||
auto *db = dynamic_cast<SimpleDatabase *>(instance->database);
|
||||
if (db == nullptr)
|
||||
instance->database = std::move(db);
|
||||
|
||||
auto *sdb = dynamic_cast<SimpleDatabase *>(instance->database.get());
|
||||
if (sdb == nullptr)
|
||||
return true;
|
||||
|
||||
instance->update = new UpdateService(config,
|
||||
instance->event_loop, *db,
|
||||
instance->event_loop, *sdb,
|
||||
static_cast<CompositeStorage &>(*instance->storage),
|
||||
*instance);
|
||||
|
||||
/* run database update after daemonization? */
|
||||
return db->FileExists();
|
||||
return sdb->FileExists();
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -351,27 +350,6 @@ Instance::BeginShutdownUpdate() noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::FinishShutdownUpdate() noexcept
|
||||
{
|
||||
#ifdef ENABLE_DATABASE
|
||||
delete update;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::ShutdownDatabase() noexcept
|
||||
{
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (instance->database != nullptr) {
|
||||
instance->database->Close();
|
||||
delete instance->database;
|
||||
}
|
||||
|
||||
delete instance->storage;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::BeginShutdownPartitions() noexcept
|
||||
{
|
||||
@@ -381,12 +359,6 @@ Instance::BeginShutdownPartitions() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
Instance::FinishShutdownPartitions() noexcept
|
||||
{
|
||||
partitions.clear();
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnIdle(unsigned flags)
|
||||
{
|
||||
@@ -523,18 +495,19 @@ static int
|
||||
mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
{
|
||||
ConfigureFS(raw_config);
|
||||
AtScopeExit() { DeinitFS(); };
|
||||
|
||||
glue_mapper_init(raw_config);
|
||||
|
||||
initPermissions(raw_config);
|
||||
spl_global_init(raw_config);
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
archive_plugin_init_all();
|
||||
const ScopeArchivePluginsInit archive_plugins_init;
|
||||
#endif
|
||||
|
||||
pcm_convert_global_init(raw_config);
|
||||
|
||||
decoder_plugin_init_all(raw_config);
|
||||
const ScopeDecoderPluginsInit decoder_plugins_init(raw_config);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
const bool create_db = InitDatabaseAndStorage(raw_config);
|
||||
@@ -553,9 +526,9 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
}
|
||||
|
||||
client_manager_init(raw_config);
|
||||
input_stream_global_init(raw_config,
|
||||
instance->io_thread.GetEventLoop());
|
||||
playlist_list_global_init(raw_config);
|
||||
const ScopeInputPluginsInit input_plugins_init(raw_config,
|
||||
instance->io_thread.GetEventLoop());
|
||||
const ScopePlaylistPluginsInit playlist_plugins_init(raw_config);
|
||||
|
||||
#ifdef ENABLE_DAEMON
|
||||
daemonize_commit();
|
||||
@@ -564,7 +537,7 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
#ifndef ANDROID
|
||||
setup_log_output();
|
||||
|
||||
SignalHandlersInit(instance->event_loop);
|
||||
const ScopeSignalHandlersInit signal_handlers_init(instance->event_loop);
|
||||
#endif
|
||||
|
||||
instance->io_thread.Start();
|
||||
@@ -652,34 +625,10 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
|
||||
}
|
||||
#endif
|
||||
|
||||
instance->FinishShutdownUpdate();
|
||||
instance->ShutdownDatabase();
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
sticker_global_finish();
|
||||
#endif
|
||||
|
||||
playlist_list_global_finish();
|
||||
input_stream_global_finish();
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
mapper_finish();
|
||||
#endif
|
||||
|
||||
DeinitFS();
|
||||
|
||||
instance->FinishShutdownPartitions();
|
||||
command_finish();
|
||||
decoder_plugin_deinit_all();
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
archive_plugin_deinit_all();
|
||||
#endif
|
||||
instance->rtio_thread.Stop();
|
||||
instance->io_thread.Stop();
|
||||
#ifndef ANDROID
|
||||
SignalHandlersFinish();
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,11 +58,6 @@ mapper_init(AllocatedPath &&_playlist_dir)
|
||||
mapper_set_playlist_dir(std::move(_playlist_dir));
|
||||
}
|
||||
|
||||
void
|
||||
mapper_finish() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
AllocatedPath
|
||||
|
||||
@@ -37,9 +37,6 @@ class AllocatedPath;
|
||||
void
|
||||
mapper_init(AllocatedPath &&playlist_dir);
|
||||
|
||||
void
|
||||
mapper_finish() noexcept;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
/**
|
||||
|
||||
@@ -119,7 +119,7 @@ try {
|
||||
TextFile file(config.path);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
const SongLoader song_loader(partition.instance.database,
|
||||
const SongLoader song_loader(partition.instance.GetDatabase(),
|
||||
partition.instance.storage);
|
||||
#else
|
||||
const SongLoader song_loader(nullptr, nullptr);
|
||||
|
||||
@@ -124,7 +124,7 @@ stats_print(Response &r, const Partition &partition)
|
||||
std::lround(partition.pc.GetTotalPlayTime().count()));
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
const Database *db = partition.instance.database;
|
||||
const Database *db = partition.instance.GetDatabase();
|
||||
if (db != nullptr)
|
||||
db_stats_print(r, *db);
|
||||
#endif
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
void
|
||||
tag_print_types(Response &r) noexcept
|
||||
{
|
||||
const auto tag_mask = global_tag_mask & r.GetTagMask();
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++)
|
||||
if (IsTagEnabled(i))
|
||||
if (tag_mask.Test(TagType(i)))
|
||||
r.Format("tagtype: %s\n", tag_item_names[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ static bool archive_plugins_enabled[ARRAY_SIZE(archive_plugins) - 1];
|
||||
if (archive_plugins_enabled[archive_plugin_iterator - archive_plugins])
|
||||
|
||||
const ArchivePlugin *
|
||||
archive_plugin_from_suffix(const char *suffix)
|
||||
archive_plugin_from_suffix(const char *suffix) noexcept
|
||||
{
|
||||
if (suffix == nullptr)
|
||||
return nullptr;
|
||||
@@ -63,7 +63,7 @@ archive_plugin_from_suffix(const char *suffix)
|
||||
}
|
||||
|
||||
const ArchivePlugin *
|
||||
archive_plugin_from_name(const char *name)
|
||||
archive_plugin_from_name(const char *name) noexcept
|
||||
{
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (strcmp(plugin->name, name) == 0)
|
||||
@@ -81,7 +81,8 @@ void archive_plugin_init_all(void)
|
||||
}
|
||||
}
|
||||
|
||||
void archive_plugin_deinit_all(void)
|
||||
void
|
||||
archive_plugin_deinit_all() noexcept
|
||||
{
|
||||
archive_plugins_for_each_enabled(plugin)
|
||||
if (plugin->finish != nullptr)
|
||||
|
||||
@@ -33,10 +33,10 @@ extern const ArchivePlugin *const archive_plugins[];
|
||||
/* interface for using plugins */
|
||||
|
||||
const ArchivePlugin *
|
||||
archive_plugin_from_suffix(const char *suffix);
|
||||
archive_plugin_from_suffix(const char *suffix) noexcept;
|
||||
|
||||
const ArchivePlugin *
|
||||
archive_plugin_from_name(const char *name);
|
||||
archive_plugin_from_name(const char *name) noexcept;
|
||||
|
||||
/* this is where we "load" all the "plugins" ;-) */
|
||||
void
|
||||
@@ -44,6 +44,17 @@ archive_plugin_init_all();
|
||||
|
||||
/* this is where we "unload" all the "plugins" */
|
||||
void
|
||||
archive_plugin_deinit_all();
|
||||
archive_plugin_deinit_all() noexcept;
|
||||
|
||||
class ScopeArchivePluginsInit {
|
||||
public:
|
||||
ScopeArchivePluginsInit() {
|
||||
archive_plugin_init_all();
|
||||
}
|
||||
|
||||
~ScopeArchivePluginsInit() noexcept {
|
||||
archive_plugin_deinit_all();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -285,11 +285,6 @@ command_init()
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
command_finish()
|
||||
{
|
||||
}
|
||||
|
||||
static const struct command *
|
||||
command_lookup(const char *name)
|
||||
{
|
||||
|
||||
@@ -37,9 +37,11 @@
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/DirectoryReader.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "input/Error.hxx"
|
||||
#include "LocateUri.hxx"
|
||||
#include "TimePrint.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h> /* for PRIu64 */
|
||||
@@ -256,7 +258,11 @@ find_stream_art(const char *directory, Mutex &mutex)
|
||||
|
||||
try {
|
||||
return InputStream::OpenReady(art_file.c_str(), mutex);
|
||||
} catch (const std::exception &e) {}
|
||||
} catch (...) {
|
||||
auto e = std::current_exception();
|
||||
if (!IsFileNotFound(e))
|
||||
LogError(e);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -285,8 +291,11 @@ read_stream_art(Response &r, const char *uri, size_t offset)
|
||||
uint8_t buffer[CHUNK_SIZE];
|
||||
size_t read_size;
|
||||
|
||||
is->Seek(offset);
|
||||
read_size = is->Read(&buffer, CHUNK_SIZE);
|
||||
{
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
is->Seek(offset);
|
||||
read_size = is->Read(&buffer, CHUNK_SIZE);
|
||||
}
|
||||
|
||||
r.Format("size: %" PRIoffset "\n"
|
||||
"binary: %u\n",
|
||||
|
||||
@@ -294,7 +294,7 @@ handle_update(Client &client, Request args, Response &r, bool discard)
|
||||
if (update != nullptr)
|
||||
return handle_update(r, *update, path, discard);
|
||||
|
||||
Database *db = client.GetInstance().database;
|
||||
Database *db = client.GetInstance().GetDatabase();
|
||||
if (db != nullptr)
|
||||
return handle_update(r, *db, path, discard);
|
||||
#else
|
||||
|
||||
@@ -209,7 +209,7 @@ handle_mount(Client &client, Request args, Response &r)
|
||||
instance.EmitIdle(IDLE_MOUNT);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.database)) {
|
||||
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.GetDatabase())) {
|
||||
try {
|
||||
db->Mount(local_uri, remote_uri);
|
||||
} catch (...) {
|
||||
@@ -253,7 +253,7 @@ handle_unmount(Client &client, Request args, Response &r)
|
||||
destroy here */
|
||||
instance.update->CancelMount(local_uri);
|
||||
|
||||
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.database)) {
|
||||
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.GetDatabase())) {
|
||||
if (db->Unmount(local_uri))
|
||||
// TODO: call Instance::OnDatabaseModified()?
|
||||
instance.EmitIdle(IDLE_DATABASE);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "Configured.hxx"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "Interface.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "config/Param.hxx"
|
||||
#include "config/Block.hxx"
|
||||
@@ -26,7 +27,7 @@
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
Database *
|
||||
DatabasePtr
|
||||
CreateConfiguredDatabase(const ConfigData &config,
|
||||
EventLoop &main_event_loop, EventLoop &io_event_loop,
|
||||
DatabaseListener &listener)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,10 +20,11 @@
|
||||
#ifndef MPD_DB_CONFIG_HXX
|
||||
#define MPD_DB_CONFIG_HXX
|
||||
|
||||
#include "Ptr.hxx"
|
||||
|
||||
struct ConfigData;
|
||||
class EventLoop;
|
||||
class DatabaseListener;
|
||||
class Database;
|
||||
|
||||
/**
|
||||
* Read database configuration settings and create a #Database
|
||||
@@ -32,7 +33,7 @@ class Database;
|
||||
*
|
||||
* Throws #std::runtime_error on error.
|
||||
*/
|
||||
Database *
|
||||
DatabasePtr
|
||||
CreateConfiguredDatabase(const ConfigData &config,
|
||||
EventLoop &main_event_loop, EventLoop &io_event_loop,
|
||||
DatabaseListener &listener);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,13 +18,14 @@
|
||||
*/
|
||||
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "Interface.hxx"
|
||||
#include "Registry.hxx"
|
||||
#include "DatabaseError.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
|
||||
Database *
|
||||
DatabasePtr
|
||||
DatabaseGlobalInit(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,12 +20,11 @@
|
||||
#ifndef MPD_DATABASE_GLUE_HXX
|
||||
#define MPD_DATABASE_GLUE_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
#include "Ptr.hxx"
|
||||
|
||||
struct ConfigBlock;
|
||||
class EventLoop;
|
||||
class DatabaseListener;
|
||||
class Database;
|
||||
|
||||
/**
|
||||
* Initialize the database library.
|
||||
@@ -34,7 +33,7 @@ class Database;
|
||||
*
|
||||
* @param block the database configuration block
|
||||
*/
|
||||
Database *
|
||||
DatabasePtr
|
||||
DatabaseGlobalInit(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -26,10 +26,11 @@
|
||||
#ifndef MPD_DATABASE_PLUGIN_HXX
|
||||
#define MPD_DATABASE_PLUGIN_HXX
|
||||
|
||||
#include "Ptr.hxx"
|
||||
|
||||
struct ConfigBlock;
|
||||
class EventLoop;
|
||||
class DatabaseListener;
|
||||
class Database;
|
||||
|
||||
struct DatabasePlugin {
|
||||
/**
|
||||
@@ -52,10 +53,10 @@ struct DatabasePlugin {
|
||||
* @param io_event_loop the #EventLoop running on the
|
||||
* #EventThread, i.e. the one used for background I/O
|
||||
*/
|
||||
Database *(*create)(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block);
|
||||
DatabasePtr (*create)(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block);
|
||||
|
||||
constexpr bool RequireStorage() const {
|
||||
return flags & FLAG_REQUIRE_STORAGE;
|
||||
|
||||
29
src/db/Ptr.hxx
Normal file
29
src/db/Ptr.hxx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DATABASE_PTR_HXX
|
||||
#define MPD_DATABASE_PTR_HXX
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Database;
|
||||
|
||||
typedef std::unique_ptr<Database> DatabasePtr;
|
||||
|
||||
#endif
|
||||
@@ -18,11 +18,11 @@
|
||||
*/
|
||||
|
||||
#include "VHelper.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "song/LightSong.hxx"
|
||||
#include "song/Filter.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
DatabaseVisitorHelper::DatabaseVisitorHelper(const DatabaseSelection &_selection,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "Visitor.hxx"
|
||||
#include "Selection.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -112,10 +112,10 @@ public:
|
||||
ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
|
||||
const ConfigBlock &block);
|
||||
|
||||
static Database *Create(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block);
|
||||
static DatabasePtr Create(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block);
|
||||
|
||||
void Open() override;
|
||||
void Close() noexcept override;
|
||||
@@ -440,12 +440,12 @@ ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
|
||||
{
|
||||
}
|
||||
|
||||
Database *
|
||||
DatabasePtr
|
||||
ProxyDatabase::Create(EventLoop &loop, EventLoop &,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block)
|
||||
{
|
||||
return new ProxyDatabase(loop, listener, block);
|
||||
return std::make_unique<ProxyDatabase>(loop, listener, block);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -37,22 +37,25 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
Directory::Directory(std::string &&_path_utf8, Directory *_parent)
|
||||
Directory::Directory(std::string &&_path_utf8, Directory *_parent) noexcept
|
||||
:parent(_parent),
|
||||
path(std::move(_path_utf8))
|
||||
{
|
||||
}
|
||||
|
||||
Directory::~Directory()
|
||||
Directory::~Directory() noexcept
|
||||
{
|
||||
delete mounted_database;
|
||||
if (mounted_database != nullptr) {
|
||||
mounted_database->Close();
|
||||
mounted_database.reset();
|
||||
}
|
||||
|
||||
songs.clear_and_dispose(Song::Disposer());
|
||||
children.clear_and_dispose(DeleteDisposer());
|
||||
}
|
||||
|
||||
void
|
||||
Directory::Delete()
|
||||
Directory::Delete() noexcept
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(parent != nullptr);
|
||||
@@ -70,7 +73,7 @@ Directory::GetName() const noexcept
|
||||
}
|
||||
|
||||
Directory *
|
||||
Directory::CreateChild(const char *name_utf8)
|
||||
Directory::CreateChild(const char *name_utf8) noexcept
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(name_utf8 != nullptr);
|
||||
@@ -160,7 +163,7 @@ Directory::LookupDirectory(const char *uri) noexcept
|
||||
}
|
||||
|
||||
void
|
||||
Directory::AddSong(Song *song)
|
||||
Directory::AddSong(Song *song) noexcept
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(song != nullptr);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "util/Compiler.h"
|
||||
#include "db/Visitor.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "db/Ptr.hxx"
|
||||
#include "Song.hxx"
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
@@ -43,7 +44,6 @@ static constexpr unsigned DEVICE_INARCHIVE = -1;
|
||||
static constexpr unsigned DEVICE_CONTAINER = -2;
|
||||
|
||||
class SongFilter;
|
||||
class Database;
|
||||
|
||||
struct Directory {
|
||||
static constexpr auto link_mode = boost::intrusive::normal_link;
|
||||
@@ -96,21 +96,21 @@ struct Directory {
|
||||
* If this is not nullptr, then this directory does not really
|
||||
* exist, but is a mount point for another #Database.
|
||||
*/
|
||||
Database *mounted_database = nullptr;
|
||||
DatabasePtr mounted_database;
|
||||
|
||||
public:
|
||||
Directory(std::string &&_path_utf8, Directory *_parent);
|
||||
~Directory();
|
||||
Directory(std::string &&_path_utf8, Directory *_parent) noexcept;
|
||||
~Directory() noexcept;
|
||||
|
||||
/**
|
||||
* Create a new root #Directory object.
|
||||
*/
|
||||
gcc_malloc gcc_returns_nonnull
|
||||
static Directory *NewRoot() {
|
||||
static Directory *NewRoot() noexcept {
|
||||
return new Directory(std::string(), nullptr);
|
||||
}
|
||||
|
||||
bool IsMount() const {
|
||||
bool IsMount() const noexcept {
|
||||
return mounted_database != nullptr;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
*
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
void Delete();
|
||||
void Delete() noexcept;
|
||||
|
||||
/**
|
||||
* Create a new #Directory object as a child of the given one.
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
*
|
||||
* @param name_utf8 the UTF-8 encoded name of the new sub directory
|
||||
*/
|
||||
Directory *CreateChild(const char *name_utf8);
|
||||
Directory *CreateChild(const char *name_utf8) noexcept;
|
||||
|
||||
/**
|
||||
* Caller must lock the #db_mutex.
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
*
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
Directory *MakeChild(const char *name_utf8) {
|
||||
Directory *MakeChild(const char *name_utf8) noexcept {
|
||||
Directory *child = FindChild(name_utf8);
|
||||
if (child == nullptr)
|
||||
child = CreateChild(name_utf8);
|
||||
@@ -242,7 +242,7 @@ public:
|
||||
* Add a song object to this directory. Its "parent" attribute must
|
||||
* be set already.
|
||||
*/
|
||||
void AddSong(Song *song);
|
||||
void AddSong(Song *song) noexcept;
|
||||
|
||||
/**
|
||||
* Remove a song object from this directory (which effectively
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -60,8 +60,7 @@ inline SimpleDatabase::SimpleDatabase(const ConfigBlock &block)
|
||||
#ifdef ENABLE_ZLIB
|
||||
compress(block.GetBlockValue("compress", true)),
|
||||
#endif
|
||||
cache_path(block.GetPath("cache_directory")),
|
||||
prefixed_light_song(nullptr)
|
||||
cache_path(block.GetPath("cache_directory"))
|
||||
{
|
||||
if (path.IsNull())
|
||||
throw std::runtime_error("No \"path\" parameter specified");
|
||||
@@ -84,12 +83,12 @@ inline SimpleDatabase::SimpleDatabase(AllocatedPath &&_path,
|
||||
prefixed_light_song(nullptr) {
|
||||
}
|
||||
|
||||
Database *
|
||||
DatabasePtr
|
||||
SimpleDatabase::Create(EventLoop &, EventLoop &,
|
||||
gcc_unused DatabaseListener &listener,
|
||||
const ConfigBlock &block)
|
||||
{
|
||||
return new SimpleDatabase(block);
|
||||
return std::make_unique<SimpleDatabase>(block);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -219,6 +218,7 @@ SimpleDatabase::GetSong(const char *uri) const
|
||||
|
||||
prefixed_light_song =
|
||||
new PrefixedLightSong(*song, r.directory->GetPath());
|
||||
r.directory->mounted_database->ReturnSong(song);
|
||||
return prefixed_light_song;
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ void
|
||||
SimpleDatabase::ReturnSong(gcc_unused const LightSong *song) const noexcept
|
||||
{
|
||||
assert(song != nullptr);
|
||||
assert(song == &light_song.Get() || song == prefixed_light_song);
|
||||
assert(song == prefixed_light_song || song == &light_song.Get());
|
||||
|
||||
if (prefixed_light_song != nullptr) {
|
||||
delete prefixed_light_song;
|
||||
@@ -390,13 +390,13 @@ SimpleDatabase::Save()
|
||||
}
|
||||
|
||||
void
|
||||
SimpleDatabase::Mount(const char *uri, Database *db)
|
||||
SimpleDatabase::Mount(const char *uri, DatabasePtr db)
|
||||
{
|
||||
#if !CLANG_CHECK_VERSION(3,6)
|
||||
/* disabled on clang due to -Wtautological-pointer-compare */
|
||||
assert(uri != nullptr);
|
||||
assert(db != nullptr);
|
||||
#endif
|
||||
assert(db != nullptr);
|
||||
assert(*uri != 0);
|
||||
|
||||
ScopeDatabaseLock protect;
|
||||
@@ -411,7 +411,7 @@ SimpleDatabase::Mount(const char *uri, Database *db)
|
||||
"Parent not found");
|
||||
|
||||
Directory *mnt = r.directory->CreateChild(r.uri);
|
||||
mnt->mounted_database = db;
|
||||
mnt->mounted_database = std::move(db);
|
||||
}
|
||||
|
||||
static constexpr bool
|
||||
@@ -441,27 +441,21 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri)
|
||||
#ifndef ENABLE_ZLIB
|
||||
constexpr bool compress = false;
|
||||
#endif
|
||||
auto db = new SimpleDatabase(cache_path / name_fs,
|
||||
compress);
|
||||
try {
|
||||
db->Open();
|
||||
} catch (...) {
|
||||
delete db;
|
||||
throw;
|
||||
}
|
||||
auto db = std::make_unique<SimpleDatabase>(cache_path / name_fs,
|
||||
compress);
|
||||
db->Open();
|
||||
|
||||
// TODO: update the new database instance?
|
||||
|
||||
try {
|
||||
Mount(local_uri, db);
|
||||
Mount(local_uri, std::move(db));
|
||||
} catch (...) {
|
||||
db->Close();
|
||||
delete db;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
inline Database *
|
||||
inline DatabasePtr
|
||||
SimpleDatabase::LockUmountSteal(const char *uri) noexcept
|
||||
{
|
||||
ScopeDatabaseLock protect;
|
||||
@@ -470,8 +464,7 @@ SimpleDatabase::LockUmountSteal(const char *uri) noexcept
|
||||
if (r.uri != nullptr || !r.directory->IsMount())
|
||||
return nullptr;
|
||||
|
||||
Database *db = r.directory->mounted_database;
|
||||
r.directory->mounted_database = nullptr;
|
||||
auto db = std::move(r.directory->mounted_database);
|
||||
r.directory->Delete();
|
||||
|
||||
return db;
|
||||
@@ -480,12 +473,11 @@ SimpleDatabase::LockUmountSteal(const char *uri) noexcept
|
||||
bool
|
||||
SimpleDatabase::Unmount(const char *uri) noexcept
|
||||
{
|
||||
Database *db = LockUmountSteal(uri);
|
||||
auto db = LockUmountSteal(uri);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
db->Close();
|
||||
delete db;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,6 +21,7 @@
|
||||
#define MPD_SIMPLE_DATABASE_PLUGIN_HXX
|
||||
|
||||
#include "db/Interface.hxx"
|
||||
#include "db/Ptr.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "song/LightSong.hxx"
|
||||
#include "util/Manual.hxx"
|
||||
@@ -57,7 +58,7 @@ class SimpleDatabase : public Database {
|
||||
* A buffer for GetSong() when prefixing the #LightSong
|
||||
* instance from a mounted #Database.
|
||||
*/
|
||||
mutable PrefixedLightSong *prefixed_light_song;
|
||||
mutable PrefixedLightSong *prefixed_light_song = nullptr;
|
||||
|
||||
/**
|
||||
* A buffer for GetSong().
|
||||
@@ -68,15 +69,14 @@ class SimpleDatabase : public Database {
|
||||
mutable unsigned borrowed_song_count;
|
||||
#endif
|
||||
|
||||
public:
|
||||
SimpleDatabase(const ConfigBlock &block);
|
||||
|
||||
SimpleDatabase(AllocatedPath &&_path, bool _compress) noexcept;
|
||||
|
||||
public:
|
||||
static Database *Create(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block);
|
||||
static DatabasePtr Create(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block);
|
||||
|
||||
gcc_pure
|
||||
Directory &GetRoot() noexcept {
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
* success, this object gains ownership of the given #Database
|
||||
*/
|
||||
gcc_nonnull_all
|
||||
void Mount(const char *uri, Database *db);
|
||||
void Mount(const char *uri, DatabasePtr db);
|
||||
|
||||
/**
|
||||
* Throws #std::runtime_error on error.
|
||||
@@ -142,7 +142,7 @@ private:
|
||||
*/
|
||||
void Load();
|
||||
|
||||
Database *LockUmountSteal(const char *uri) noexcept;
|
||||
DatabasePtr LockUmountSteal(const char *uri) noexcept;
|
||||
};
|
||||
|
||||
extern const DatabasePlugin simple_db_plugin;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -82,10 +82,10 @@ public:
|
||||
:Database(upnp_db_plugin),
|
||||
event_loop(_event_loop) {}
|
||||
|
||||
static Database *Create(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block) noexcept;
|
||||
static DatabasePtr Create(EventLoop &main_event_loop,
|
||||
EventLoop &io_event_loop,
|
||||
DatabaseListener &listener,
|
||||
const ConfigBlock &block) noexcept;
|
||||
|
||||
void Open() override;
|
||||
void Close() noexcept override;
|
||||
@@ -146,12 +146,12 @@ private:
|
||||
const UPnPDirObject& dirent) const;
|
||||
};
|
||||
|
||||
Database *
|
||||
DatabasePtr
|
||||
UpnpDatabase::Create(EventLoop &, EventLoop &io_event_loop,
|
||||
gcc_unused DatabaseListener &listener,
|
||||
const ConfigBlock &) noexcept
|
||||
{
|
||||
return new UpnpDatabase(io_event_loop);
|
||||
return std::make_unique<UpnpDatabase>(io_event_loop);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -94,7 +94,7 @@ UpdateService::CancelMount(const char *uri)
|
||||
cancel_current = next.IsDefined() && next.storage == storage2;
|
||||
}
|
||||
|
||||
if (auto *db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database)) {
|
||||
if (auto *db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database.get())) {
|
||||
queue.Erase(*db2);
|
||||
cancel_current |= next.IsDefined() && next.db == db2;
|
||||
}
|
||||
@@ -188,7 +188,7 @@ UpdateService::Enqueue(const char *path, bool discard)
|
||||
/* follow the mountpoint, update the mounted
|
||||
database */
|
||||
|
||||
db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database);
|
||||
db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database.get());
|
||||
if (db2 == nullptr)
|
||||
throw std::runtime_error("Cannot update this type of database");
|
||||
|
||||
|
||||
@@ -152,7 +152,8 @@ decoder_plugin_init_all(const ConfigData &config)
|
||||
}
|
||||
}
|
||||
|
||||
void decoder_plugin_deinit_all(void)
|
||||
void
|
||||
decoder_plugin_deinit_all() noexcept
|
||||
{
|
||||
decoder_plugins_for_each_enabled([=](const DecoderPlugin &plugin){
|
||||
plugin.Finish();
|
||||
|
||||
@@ -40,11 +40,22 @@ decoder_plugin_init_all(const ConfigData &config);
|
||||
|
||||
/* this is where we "unload" all the "plugins" */
|
||||
void
|
||||
decoder_plugin_deinit_all();
|
||||
decoder_plugin_deinit_all() noexcept;
|
||||
|
||||
class ScopeDecoderPluginsInit {
|
||||
public:
|
||||
explicit ScopeDecoderPluginsInit(const ConfigData &config) {
|
||||
decoder_plugin_init_all(config);
|
||||
}
|
||||
|
||||
~ScopeDecoderPluginsInit() noexcept {
|
||||
decoder_plugin_deinit_all();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
static inline const DecoderPlugin *
|
||||
decoder_plugins_find(F f)
|
||||
decoder_plugins_find(F f) noexcept
|
||||
{
|
||||
for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i)
|
||||
if (decoder_plugins_enabled[i] && f(*decoder_plugins[i]))
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static constexpr Domain audiofile_domain("audiofile");
|
||||
|
||||
|
||||
@@ -36,8 +36,9 @@ if flac_dep.found()
|
||||
]
|
||||
endif
|
||||
|
||||
conf.set('ENABLE_VORBIS_DECODER', libvorbis_dep.found())
|
||||
if libvorbis_dep.found()
|
||||
conf.set('ENABLE_VORBIS_DECODER', libvorbis_dep.found() or libvorbisidec_dep.found())
|
||||
conf.set('HAVE_TREMOR', libvorbisidec_dep.found())
|
||||
if libvorbis_dep.found() or libvorbisidec_dep.found()
|
||||
decoder_plugins_sources += [
|
||||
'VorbisDecoderPlugin.cxx',
|
||||
'VorbisDomain.cxx',
|
||||
@@ -181,6 +182,7 @@ decoder_plugins = static_library(
|
||||
libsidplay_dep,
|
||||
libsndfile_dep,
|
||||
libvorbis_dep,
|
||||
libvorbisidec_dep,
|
||||
ogg_dep,
|
||||
wavpack_dep,
|
||||
wildmidi_dep,
|
||||
|
||||
@@ -80,7 +80,7 @@ private:
|
||||
void
|
||||
BlockingCall(EventLoop &loop, std::function<void()> &&f)
|
||||
{
|
||||
if (loop.IsDead() || loop.IsInside()) {
|
||||
if (!loop.IsAlive() || loop.IsInside()) {
|
||||
/* we're already inside the loop - we can simply call
|
||||
the function */
|
||||
f();
|
||||
|
||||
@@ -25,7 +25,13 @@
|
||||
|
||||
EventLoop::EventLoop(ThreadId _thread)
|
||||
:SocketMonitor(*this),
|
||||
quit(false), dead(false),
|
||||
/* if this instance is hosted by an EventThread (no ThreadId
|
||||
known yet) then we're not yet alive until the thread is
|
||||
started; for the main EventLoop instance, we assume it's
|
||||
already alive, because nobody but EventThread will call
|
||||
SetAlive() */
|
||||
alive(!_thread.IsNull()),
|
||||
quit(false),
|
||||
thread(_thread)
|
||||
{
|
||||
SocketMonitor::Open(SocketDescriptor(wake_fd.Get()));
|
||||
@@ -143,12 +149,11 @@ EventLoop::Run() noexcept
|
||||
|
||||
assert(IsInside());
|
||||
assert(!quit);
|
||||
assert(!dead);
|
||||
assert(alive);
|
||||
assert(busy);
|
||||
|
||||
SocketMonitor::Schedule(SocketMonitor::READ);
|
||||
AtScopeExit(this) {
|
||||
dead = true;
|
||||
SocketMonitor::Cancel();
|
||||
};
|
||||
|
||||
@@ -215,7 +220,6 @@ EventLoop::Run() noexcept
|
||||
} while (!quit);
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(!dead);
|
||||
assert(busy);
|
||||
assert(thread.IsInside());
|
||||
#endif
|
||||
|
||||
@@ -85,12 +85,15 @@ class EventLoop final : SocketMonitor
|
||||
|
||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
|
||||
|
||||
std::atomic_bool quit;
|
||||
|
||||
/**
|
||||
* If this is true, then Run() has returned.
|
||||
* Is this #EventLoop alive, i.e. can events be scheduled?
|
||||
* This is used by BlockingCall() to determine whether
|
||||
* schedule in the #EventThread or to call directly (if
|
||||
* there's no #EventThread yet/anymore).
|
||||
*/
|
||||
std::atomic_bool dead;
|
||||
bool alive;
|
||||
|
||||
std::atomic_bool quit;
|
||||
|
||||
/**
|
||||
* True when the object has been modified and another check is
|
||||
@@ -207,9 +210,12 @@ private:
|
||||
bool OnSocketReady(unsigned flags) noexcept override;
|
||||
|
||||
public:
|
||||
gcc_pure
|
||||
bool IsDead() const noexcept {
|
||||
return dead;
|
||||
void SetAlive(bool _alive) noexcept {
|
||||
alive = _alive;
|
||||
}
|
||||
|
||||
bool IsAlive() const noexcept {
|
||||
return alive;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,8 +26,11 @@
|
||||
void
|
||||
EventThread::Start()
|
||||
{
|
||||
assert(!event_loop.IsAlive());
|
||||
assert(!thread.IsDefined());
|
||||
|
||||
event_loop.SetAlive(true);
|
||||
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
@@ -35,6 +38,9 @@ void
|
||||
EventThread::Stop() noexcept
|
||||
{
|
||||
if (thread.IsDefined()) {
|
||||
assert(event_loop.IsAlive());
|
||||
event_loop.SetAlive(false);
|
||||
|
||||
event_loop.Break();
|
||||
thread.Join();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -88,4 +88,13 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
void
|
||||
WithBufferedOutputStream(OutputStream &os, F &&f)
|
||||
{
|
||||
BufferedOutputStream bos(os);
|
||||
f(bos);
|
||||
bos.Flush();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,8 +31,33 @@
|
||||
#include "system/Error.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
FileOutputStream::FileOutputStream(FileDescriptor _directory_fd,
|
||||
Path _path, Mode _mode)
|
||||
:path(_path),
|
||||
directory_fd(_directory_fd),
|
||||
mode(_mode)
|
||||
{
|
||||
Open();
|
||||
}
|
||||
#endif
|
||||
|
||||
FileOutputStream::FileOutputStream(Path _path, Mode _mode)
|
||||
:path(_path), mode(_mode)
|
||||
:path(_path),
|
||||
#ifdef __linux__
|
||||
directory_fd(AT_FDCWD),
|
||||
#endif
|
||||
mode(_mode)
|
||||
{
|
||||
Open();
|
||||
}
|
||||
|
||||
inline void
|
||||
FileOutputStream::Open()
|
||||
{
|
||||
switch (mode) {
|
||||
case Mode::CREATE:
|
||||
@@ -149,8 +174,12 @@ FileOutputStream::Cancel() noexcept
|
||||
* Open a file using Linux's O_TMPFILE for writing the given file.
|
||||
*/
|
||||
static bool
|
||||
OpenTempFile(FileDescriptor &fd, Path path)
|
||||
OpenTempFile(FileDescriptor directory_fd,
|
||||
FileDescriptor &fd, Path path)
|
||||
{
|
||||
if (directory_fd != FileDescriptor(AT_FDCWD))
|
||||
return fd.Open(directory_fd, ".", O_TMPFILE|O_WRONLY, 0666);
|
||||
|
||||
const auto directory = path.GetDirectoryName();
|
||||
if (directory.IsNull())
|
||||
return false;
|
||||
@@ -165,11 +194,15 @@ FileOutputStream::OpenCreate(bool visible)
|
||||
{
|
||||
#ifdef HAVE_O_TMPFILE
|
||||
/* try Linux's O_TMPFILE first */
|
||||
is_tmpfile = !visible && OpenTempFile(fd, GetPath());
|
||||
is_tmpfile = !visible && OpenTempFile(directory_fd, fd, GetPath());
|
||||
if (!is_tmpfile) {
|
||||
#endif
|
||||
/* fall back to plain POSIX */
|
||||
if (!fd.Open(GetPath().c_str(),
|
||||
if (!fd.Open(
|
||||
#ifdef __linux__
|
||||
directory_fd,
|
||||
#endif
|
||||
GetPath().c_str(),
|
||||
O_WRONLY|O_CREAT|O_TRUNC,
|
||||
0666))
|
||||
throw FormatErrno("Failed to create %s",
|
||||
@@ -188,7 +221,11 @@ FileOutputStream::OpenAppend(bool create)
|
||||
if (create)
|
||||
flags |= O_CREAT;
|
||||
|
||||
if (!fd.Open(path.c_str(), flags))
|
||||
if (!fd.Open(
|
||||
#ifdef __linux__
|
||||
directory_fd,
|
||||
#endif
|
||||
path.c_str(), flags))
|
||||
throw FormatErrno("Failed to append to %s",
|
||||
path.c_str());
|
||||
}
|
||||
@@ -219,12 +256,12 @@ FileOutputStream::Commit()
|
||||
|
||||
#ifdef HAVE_O_TMPFILE
|
||||
if (is_tmpfile) {
|
||||
unlink(GetPath().c_str());
|
||||
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
|
||||
|
||||
/* hard-link the temporary file to the final path */
|
||||
if (linkat(AT_FDCWD,
|
||||
StringFormat<64>("/proc/self/fd/%d", fd.Get()),
|
||||
AT_FDCWD, path.c_str(),
|
||||
directory_fd.Get(), path.c_str(),
|
||||
AT_SYMLINK_FOLLOW) < 0)
|
||||
throw FormatErrno("Failed to commit %s",
|
||||
path.c_str());
|
||||
@@ -253,7 +290,11 @@ FileOutputStream::Cancel() noexcept
|
||||
#ifdef HAVE_O_TMPFILE
|
||||
if (!is_tmpfile)
|
||||
#endif
|
||||
unlink(GetPath().c_str());
|
||||
#ifdef __linux__
|
||||
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
|
||||
#else
|
||||
unlink(GetPath().c_str());
|
||||
#endif
|
||||
break;
|
||||
|
||||
case Mode::CREATE_VISIBLE:
|
||||
|
||||
@@ -59,6 +59,10 @@ class Path;
|
||||
class FileOutputStream final : public OutputStream {
|
||||
const AllocatedPath path;
|
||||
|
||||
#ifdef __linux__
|
||||
const FileDescriptor directory_fd;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
@@ -108,6 +112,11 @@ private:
|
||||
public:
|
||||
explicit FileOutputStream(Path _path, Mode _mode=Mode::CREATE);
|
||||
|
||||
#ifdef __linux__
|
||||
FileOutputStream(FileDescriptor _directory_fd, Path _path,
|
||||
Mode _mode=Mode::CREATE);
|
||||
#endif
|
||||
|
||||
~FileOutputStream() noexcept {
|
||||
if (IsDefined())
|
||||
Cancel();
|
||||
@@ -133,6 +142,7 @@ public:
|
||||
private:
|
||||
void OpenCreate(bool visible);
|
||||
void OpenAppend(bool create);
|
||||
void Open();
|
||||
|
||||
bool Close() noexcept {
|
||||
assert(IsDefined());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -27,8 +27,8 @@
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MPD_OUTPUT_STREAM_HXX
|
||||
#define MPD_OUTPUT_STREAM_HXX
|
||||
#ifndef OUTPUT_STREAM_HXX
|
||||
#define OUTPUT_STREAM_HXX
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class StdioOutputStream final : public OutputStream {
|
||||
FILE *const file;
|
||||
|
||||
public:
|
||||
explicit StdioOutputStream(FILE *_file):file(_file) {}
|
||||
explicit StdioOutputStream(FILE *_file) noexcept:file(_file) {}
|
||||
|
||||
/* virtual methods from class OutputStream */
|
||||
void Write(const void *data, size_t size) override {
|
||||
|
||||
@@ -66,6 +66,11 @@ BufferedInputStream::Check()
|
||||
void
|
||||
BufferedInputStream::Seek(offset_type new_offset)
|
||||
{
|
||||
if (new_offset >= size) {
|
||||
offset = size;
|
||||
return;
|
||||
}
|
||||
|
||||
auto r = buffer.Read(new_offset);
|
||||
if (r.HasData()) {
|
||||
/* nice, we already have some data at the desired
|
||||
|
||||
@@ -35,4 +35,16 @@ input_stream_global_init(const ConfigData &config, EventLoop &event_loop);
|
||||
void
|
||||
input_stream_global_finish() noexcept;
|
||||
|
||||
class ScopeInputPluginsInit {
|
||||
public:
|
||||
ScopeInputPluginsInit(const ConfigData &config,
|
||||
EventLoop &event_loop) {
|
||||
input_stream_global_init(config, event_loop);
|
||||
}
|
||||
|
||||
~ScopeInputPluginsInit() noexcept {
|
||||
input_stream_global_finish();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "CdioParanoiaInputPlugin.hxx"
|
||||
#include "lib/cdio/Paranoia.hxx"
|
||||
#include "../InputStream.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "util/TruncateString.hxx"
|
||||
@@ -41,18 +42,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAVE_CDIO_PARANOIA_PARANOIA_H
|
||||
#include <cdio/paranoia/paranoia.h>
|
||||
#else
|
||||
#include <cdio/paranoia.h>
|
||||
#endif
|
||||
|
||||
#include <cdio/cd_types.h>
|
||||
|
||||
class CdioParanoiaInputStream final : public InputStream {
|
||||
cdrom_drive_t *const drv;
|
||||
CdIo_t *const cdio;
|
||||
cdrom_paranoia_t *const para;
|
||||
CdromParanoia para;
|
||||
|
||||
const lsn_t lsn_from, lsn_to;
|
||||
int lsn_relofs;
|
||||
@@ -66,18 +61,17 @@ class CdioParanoiaInputStream final : public InputStream {
|
||||
bool reverse_endian,
|
||||
lsn_t _lsn_from, lsn_t _lsn_to)
|
||||
:InputStream(_uri, _mutex),
|
||||
drv(_drv), cdio(_cdio), para(cdio_paranoia_init(drv)),
|
||||
drv(_drv), cdio(_cdio), para(drv),
|
||||
lsn_from(_lsn_from), lsn_to(_lsn_to),
|
||||
lsn_relofs(0),
|
||||
buffer_lsn(-1)
|
||||
{
|
||||
/* Set reading mode for full paranoia, but allow
|
||||
skipping sectors. */
|
||||
paranoia_modeset(para,
|
||||
PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
|
||||
para.SetMode(PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
|
||||
|
||||
/* seek to beginning of the track */
|
||||
cdio_paranoia_seek(para, lsn_from, SEEK_SET);
|
||||
para.Seek(lsn_from);
|
||||
|
||||
seekable = true;
|
||||
size = (lsn_to - lsn_from + 1) * CDIO_CD_FRAMESIZE_RAW;
|
||||
@@ -90,7 +84,7 @@ class CdioParanoiaInputStream final : public InputStream {
|
||||
}
|
||||
|
||||
~CdioParanoiaInputStream() {
|
||||
cdio_paranoia_free(para);
|
||||
para = {};
|
||||
cdio_cddap_close_no_free_cdio(drv);
|
||||
cdio_destroy(cdio);
|
||||
}
|
||||
@@ -208,10 +202,10 @@ input_cdio_open(const char *uri,
|
||||
throw std::runtime_error("Unable to identify audio CD disc.");
|
||||
}
|
||||
|
||||
cdda_verbose_set(drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
|
||||
cdio_cddap_verbose_set(drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
|
||||
if (speed > 0) {
|
||||
FormatDebug(cdio_domain,"Attempting to set CD speed to %dx",speed);
|
||||
cdda_speed_set(drv,speed);
|
||||
cdio_cddap_speed_set(drv,speed);
|
||||
}
|
||||
|
||||
if (0 != cdio_cddap_open(drv)) {
|
||||
@@ -277,7 +271,7 @@ CdioParanoiaInputStream::Seek(offset_type new_offset)
|
||||
|
||||
{
|
||||
const ScopeUnlock unlock(mutex);
|
||||
cdio_paranoia_seek(para, lsn_from + lsn_relofs, SEEK_SET);
|
||||
para.Seek(lsn_from + lsn_relofs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,56 +279,53 @@ size_t
|
||||
CdioParanoiaInputStream::Read(void *ptr, size_t length)
|
||||
{
|
||||
size_t nbytes = 0;
|
||||
int diff;
|
||||
size_t len, maxwrite;
|
||||
int16_t *rbuf;
|
||||
char *s_err, *s_mess;
|
||||
char *wptr = (char *) ptr;
|
||||
|
||||
while (length > 0) {
|
||||
|
||||
|
||||
/* end of track ? */
|
||||
if (lsn_from + lsn_relofs > lsn_to)
|
||||
break;
|
||||
|
||||
//current sector was changed ?
|
||||
const int16_t *rbuf;
|
||||
if (lsn_relofs != buffer_lsn) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
||||
rbuf = cdio_paranoia_read(para, nullptr);
|
||||
try {
|
||||
rbuf = para.Read().data;
|
||||
} catch (...) {
|
||||
char *s_err = cdio_cddap_errors(drv);
|
||||
if (s_err) {
|
||||
FormatError(cdio_domain,
|
||||
"paranoia_read: %s", s_err);
|
||||
#if LIBCDIO_VERSION_NUM >= 90
|
||||
cdio_cddap_free_messages(s_err);
|
||||
#else
|
||||
free(s_err);
|
||||
#endif
|
||||
}
|
||||
|
||||
s_err = cdda_errors(drv);
|
||||
if (s_err) {
|
||||
FormatError(cdio_domain,
|
||||
"paranoia_read: %s", s_err);
|
||||
free(s_err);
|
||||
throw;
|
||||
}
|
||||
s_mess = cdda_messages(drv);
|
||||
if (s_mess) {
|
||||
free(s_mess);
|
||||
}
|
||||
if (!rbuf)
|
||||
throw std::runtime_error("paranoia read error");
|
||||
|
||||
//store current buffer
|
||||
memcpy(buffer, rbuf, CDIO_CD_FRAMESIZE_RAW);
|
||||
buffer_lsn = lsn_relofs;
|
||||
} else {
|
||||
//use cached sector
|
||||
rbuf = (int16_t *)buffer;
|
||||
rbuf = (const int16_t *)buffer;
|
||||
}
|
||||
|
||||
//correct offset
|
||||
diff = offset - lsn_relofs * CDIO_CD_FRAMESIZE_RAW;
|
||||
const int diff = offset - lsn_relofs * CDIO_CD_FRAMESIZE_RAW;
|
||||
|
||||
assert(diff >= 0 && diff < CDIO_CD_FRAMESIZE_RAW);
|
||||
|
||||
maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
|
||||
len = (length < maxwrite? length : maxwrite);
|
||||
const size_t maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
|
||||
const size_t len = (length < maxwrite? length : maxwrite);
|
||||
|
||||
//skip diff bytes from this lsn
|
||||
memcpy(wptr, ((char*)rbuf) + diff, len);
|
||||
memcpy(wptr, ((const char *)rbuf) + diff, len);
|
||||
//update pointer
|
||||
wptr += len;
|
||||
nbytes += len;
|
||||
|
||||
@@ -10,10 +10,6 @@ libcdio_paranoia_dep = dependency('libcdio_paranoia', version: '>= 0.4', require
|
||||
conf.set('ENABLE_CDIO_PARANOIA', libcdio_paranoia_dep.found())
|
||||
if libcdio_paranoia_dep.found()
|
||||
input_plugins_sources += 'CdioParanoiaInputPlugin.cxx'
|
||||
|
||||
conf.set('HAVE_CDIO_PARANOIA_PARANOIA_H',
|
||||
compiler.has_header('cdio/paranoia/paranoia.h',
|
||||
dependencies: libcdio_paranoia_dep))
|
||||
endif
|
||||
|
||||
if curl_dep.found()
|
||||
|
||||
164
src/lib/cdio/Paranoia.hxx
Normal file
164
src/lib/cdio/Paranoia.hxx
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef CDIO_PARANOIA_HXX
|
||||
#define CDIO_PARANOIA_HXX
|
||||
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <cdio/version.h>
|
||||
#if LIBCDIO_VERSION_NUM >= 90
|
||||
#include <cdio/paranoia/paranoia.h>
|
||||
#else
|
||||
#include <cdio/paranoia.h>
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
class CdromDrive {
|
||||
cdrom_drive_t *drv = nullptr;
|
||||
|
||||
public:
|
||||
CdromDrive() = default;
|
||||
|
||||
explicit CdromDrive(CdIo_t *cdio)
|
||||
:drv(cdio_cddap_identify_cdio(cdio, 1, nullptr))
|
||||
{
|
||||
if (drv == nullptr)
|
||||
throw std::runtime_error("Failed to identify audio CD");
|
||||
|
||||
cdda_verbose_set(drv, CDDA_MESSAGE_FORGETIT,
|
||||
CDDA_MESSAGE_FORGETIT);
|
||||
}
|
||||
|
||||
~CdromDrive() noexcept {
|
||||
if (drv != nullptr)
|
||||
cdio_cddap_close_no_free_cdio(drv);
|
||||
}
|
||||
|
||||
CdromDrive(CdromDrive &&src) noexcept
|
||||
:drv(std::exchange(src.drv, nullptr)) {}
|
||||
|
||||
CdromDrive &operator=(CdromDrive &&src) noexcept {
|
||||
using std::swap;
|
||||
swap(drv, src.drv);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto get() const noexcept {
|
||||
return drv;
|
||||
}
|
||||
|
||||
void Open() {
|
||||
if (cdio_cddap_open(drv) != 0)
|
||||
throw std::runtime_error("Failed to open disc");
|
||||
}
|
||||
|
||||
auto GetDiscSectorRange() const {
|
||||
auto first = cdio_cddap_disc_firstsector(drv);
|
||||
auto last = cdio_cddap_disc_lastsector(drv);
|
||||
if (first < 0 || last < 0)
|
||||
throw std::runtime_error("Failed to get disc audio sectors");
|
||||
return std::make_pair(first, last);
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool IsAudioTrack(track_t i) const noexcept {
|
||||
return cdio_cddap_track_audiop(drv, i);
|
||||
}
|
||||
|
||||
auto GetTrackSectorRange(track_t i) const {
|
||||
auto first = cdio_cddap_track_firstsector(drv, i);
|
||||
auto last = cdio_cddap_track_lastsector(drv, i);
|
||||
if (first < 0 || last < 0)
|
||||
throw std::runtime_error("Invalid track number");
|
||||
return std::make_pair(first, last);
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
unsigned GetTrackCount() const noexcept {
|
||||
return cdio_cddap_tracks(drv);
|
||||
}
|
||||
|
||||
unsigned GetTrackChannels(track_t i) const {
|
||||
auto value = cdio_cddap_track_channels(drv, i);
|
||||
if (value < 0)
|
||||
throw std::runtime_error("cdio_cddap_track_channels() failed");
|
||||
return unsigned(value);
|
||||
}
|
||||
};
|
||||
|
||||
class CdromParanoia {
|
||||
cdrom_paranoia_t *paranoia = nullptr;
|
||||
|
||||
public:
|
||||
CdromParanoia() = default;
|
||||
|
||||
explicit CdromParanoia(cdrom_drive_t *drv) noexcept
|
||||
:paranoia(cdio_paranoia_init(drv)) {}
|
||||
|
||||
~CdromParanoia() noexcept {
|
||||
if (paranoia != nullptr)
|
||||
cdio_paranoia_free(paranoia);
|
||||
}
|
||||
|
||||
CdromParanoia(CdromParanoia &&src) noexcept
|
||||
:paranoia(std::exchange(src.paranoia, nullptr)) {}
|
||||
|
||||
CdromParanoia &operator=(CdromParanoia &&src) noexcept {
|
||||
using std::swap;
|
||||
swap(paranoia, src.paranoia);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto get() const noexcept {
|
||||
return paranoia;
|
||||
}
|
||||
|
||||
void SetMode(int mode_flags) noexcept {
|
||||
paranoia_modeset(paranoia, mode_flags);
|
||||
}
|
||||
|
||||
void Seek(int32_t seek, int whence=SEEK_SET) {
|
||||
if (cdio_paranoia_seek(paranoia, seek, whence) < 0)
|
||||
throw std::runtime_error("Failed to seek disc");
|
||||
}
|
||||
|
||||
ConstBuffer<int16_t> Read() {
|
||||
const int16_t *data = cdio_paranoia_read(paranoia, nullptr);
|
||||
if (data == nullptr)
|
||||
throw std::runtime_error("Read from audio CD failed");
|
||||
|
||||
return {data, CDIO_CD_FRAMESIZE_RAW / sizeof(int16_t)};
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,20 +1,19 @@
|
||||
Index: curl-7.58.0/lib/url.c
|
||||
===================================================================
|
||||
--- curl-7.58.0.orig/lib/url.c
|
||||
+++ curl-7.58.0/lib/url.c
|
||||
@@ -3503,6 +3503,7 @@ static CURLcode override_login(struct Cu
|
||||
diff -ur curl-7.63.0.orig/lib/url.c curl-7.63.0/lib/url.c
|
||||
--- curl-7.63.0.orig/lib/url.c 2019-01-21 10:15:51.368019445 +0100
|
||||
+++ curl-7.63.0/lib/url.c 2019-01-21 10:19:16.307523984 +0100
|
||||
@@ -3057,6 +3057,7 @@
|
||||
}
|
||||
|
||||
conn->bits.netrc = FALSE;
|
||||
+#ifndef __BIONIC__
|
||||
if(data->set.use_netrc != CURL_NETRC_IGNORED) {
|
||||
int ret = Curl_parsenetrc(conn->host.name,
|
||||
userp, passwdp,
|
||||
@@ -3524,6 +3525,7 @@ static CURLcode override_login(struct Cu
|
||||
conn->bits.user_passwd = TRUE; /* enable user+password */
|
||||
if(data->set.use_netrc != CURL_NETRC_IGNORED &&
|
||||
(!*userp || !**userp || !*passwdp || !**passwdp)) {
|
||||
bool netrc_user_changed = FALSE;
|
||||
@@ -3090,6 +3091,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
+#endif
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* for updated strings, we update them in the URL */
|
||||
if(user_changed) {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "Iter.hxx"
|
||||
#include "util/Compiler.h"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#if GCC_OLDER_THAN(8,0)
|
||||
/* switch off completely bogus shadow warnings in older GCC
|
||||
@@ -81,6 +82,14 @@ public:
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ConstBuffer<T> GetFixedArray() noexcept {
|
||||
void *value;
|
||||
int n_elements;
|
||||
dbus_message_iter_get_fixed_array(&iter, &value, &n_elements);
|
||||
return {(const T *)value, size_t(n_elements)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new iterator which recurses into a container
|
||||
* value.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ReadIter.hxx"
|
||||
#include "ObjectManager.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <functional>
|
||||
@@ -39,6 +40,40 @@ CheckString(I &&i) noexcept
|
||||
return i.GetString();
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
gcc_pure
|
||||
static StringView
|
||||
CheckRecursedByteArrayToString(I &&i) noexcept
|
||||
{
|
||||
if (i.GetArgType() != DBUS_TYPE_BYTE)
|
||||
return nullptr;
|
||||
|
||||
auto value = i.template GetFixedArray<char>();
|
||||
return { value.data, value.size };
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
gcc_pure
|
||||
static StringView
|
||||
CheckByteArrayToString(I &&i) noexcept
|
||||
{
|
||||
if (i.GetArgType() != DBUS_TYPE_ARRAY)
|
||||
return nullptr;
|
||||
|
||||
return CheckRecursedByteArrayToString(i.Recurse());
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
gcc_pure
|
||||
static StringView
|
||||
CheckByteArrayArrayFrontToString(I &&i) noexcept
|
||||
{
|
||||
if (i.GetArgType() != DBUS_TYPE_ARRAY)
|
||||
return nullptr;
|
||||
|
||||
return CheckByteArrayToString(i.Recurse());
|
||||
}
|
||||
|
||||
static void
|
||||
ParseDriveDictEntry(Object &o, const char *name,
|
||||
ODBus::ReadMessageIter &&value_i) noexcept
|
||||
@@ -61,6 +96,25 @@ ParseBlockDictEntry(Object &o, const char *name,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ParseFileesystemDictEntry(Object &o, const char *name,
|
||||
ODBus::ReadMessageIter &&value_i) noexcept
|
||||
{
|
||||
if (StringIsEqual(name, "MountPoints")) {
|
||||
if (!o.mount_point.empty())
|
||||
/* we already know one mount point, and we're
|
||||
not interested in more */
|
||||
return;
|
||||
|
||||
/* get the first string in the array */
|
||||
auto value = CheckByteArrayArrayFrontToString(value_i);
|
||||
if (value != nullptr)
|
||||
o.mount_point = {value.data, value.size};
|
||||
|
||||
// TODO: check whether the string is a valid filesystem path
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ParseInterface(Object &o, const char *interface,
|
||||
ODBus::ReadMessageIter &&i) noexcept
|
||||
@@ -74,6 +128,10 @@ ParseInterface(Object &o, const char *interface,
|
||||
std::ref(o), _1, _2));
|
||||
} else if (StringIsEqual(interface, "org.freedesktop.UDisks2.Filesystem")) {
|
||||
o.is_filesystem = true;
|
||||
|
||||
i.ForEachProperty(std::bind(ParseFileesystemDictEntry,
|
||||
std::ref(o), _1, _2));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,13 @@ struct Object {
|
||||
|
||||
std::string drive_id, block_id;
|
||||
|
||||
/**
|
||||
* The first element of the "MountPoints" array of the
|
||||
* "Filesystem" interface. Empty if no "MountPoints" property
|
||||
* exists.
|
||||
*/
|
||||
std::string mount_point;
|
||||
|
||||
bool is_filesystem = false;
|
||||
|
||||
explicit Object(const char *_path) noexcept
|
||||
|
||||
@@ -73,7 +73,7 @@ NfsManager::Compare::operator()(const ManagedConnection &a,
|
||||
|
||||
NfsManager::~NfsManager() noexcept
|
||||
{
|
||||
assert(GetEventLoop().IsInside());
|
||||
assert(!GetEventLoop().IsAlive() || GetEventLoop().IsInside());
|
||||
|
||||
CollectGarbage();
|
||||
|
||||
@@ -103,7 +103,7 @@ NfsManager::GetConnection(const char *server, const char *export_name) noexcept
|
||||
void
|
||||
NfsManager::CollectGarbage() noexcept
|
||||
{
|
||||
assert(GetEventLoop().IsInside());
|
||||
assert(!GetEventLoop().IsAlive() || GetEventLoop().IsInside());
|
||||
|
||||
garbage.clear_and_dispose(DeleteDisposer());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
libflac_dep = dependency('flac', version: '>= 1.2', required: get_option('flac'))
|
||||
libopus_dep = dependency('opus', required: get_option('opus'))
|
||||
libvorbis_dep = dependency('vorbis', required: get_option('vorbis'))
|
||||
libvorbisidec_dep = dependency('vorbisidec', required: get_option('tremor'))
|
||||
|
||||
if get_option('vorbis').enabled() and get_option('tremor').enabled()
|
||||
error('Cannot build both, the Vorbis decoder AND the Tremor (Vorbis fixed-point) decoder')
|
||||
endif
|
||||
|
||||
libvorbisenc_dep = dependency('', required: false)
|
||||
if need_encoder and not get_option('vorbisenc').disabled()
|
||||
@@ -13,7 +18,7 @@ if need_encoder and not get_option('vorbisenc').disabled()
|
||||
endif
|
||||
endif
|
||||
|
||||
if libopus_dep.found() or libvorbis_dep.found() or libvorbisenc_dep.found()
|
||||
if libopus_dep.found() or libvorbis_dep.found() or libvorbisenc_dep.found() or libvorbisidec_dep.found()
|
||||
libogg_dep = dependency('ogg')
|
||||
else
|
||||
libogg_dep = dependency('', required: false)
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include "mixer/MixerInternal.hxx"
|
||||
#include "mixer/Listener.hxx"
|
||||
#include "output/plugins/PulseOutputPlugin.hxx"
|
||||
#include "util/NumberParser.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "config/Block.hxx"
|
||||
|
||||
#include <pulse/context.h>
|
||||
#include <pulse/introspect.h>
|
||||
@@ -37,13 +40,18 @@
|
||||
class PulseMixer final : public Mixer {
|
||||
PulseOutput &output;
|
||||
|
||||
bool online;
|
||||
const float volume_scale_factor;
|
||||
|
||||
bool online = false;
|
||||
|
||||
struct pa_cvolume volume;
|
||||
|
||||
public:
|
||||
PulseMixer(PulseOutput &_output, MixerListener &_listener)
|
||||
PulseMixer(PulseOutput &_output, MixerListener &_listener,
|
||||
double _volume_scale_factor)
|
||||
:Mixer(pulse_mixer_plugin, _listener),
|
||||
output(_output), online(false)
|
||||
output(_output),
|
||||
volume_scale_factor(_volume_scale_factor)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -159,13 +167,30 @@ pulse_mixer_on_change(PulseMixer &pm,
|
||||
pm.Update(context, stream);
|
||||
}
|
||||
|
||||
static float
|
||||
parse_volume_scale_factor(const char *value) {
|
||||
if (value == nullptr)
|
||||
return 1.0;
|
||||
|
||||
char *endptr;
|
||||
float factor = ParseFloat(value, &endptr);
|
||||
|
||||
if (endptr == value || *endptr != '\0' || factor < 0.5 || factor > 5.0)
|
||||
throw FormatRuntimeError("\"%s\" is not a number in the "
|
||||
"range 0.5 to 5.0",
|
||||
value);
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
static Mixer *
|
||||
pulse_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
|
||||
MixerListener &listener,
|
||||
gcc_unused const ConfigBlock &block)
|
||||
const ConfigBlock &block)
|
||||
{
|
||||
PulseOutput &po = (PulseOutput &)ao;
|
||||
PulseMixer *pm = new PulseMixer(po, listener);
|
||||
float scale = parse_volume_scale_factor(block.GetBlockValue("scale_volume"));
|
||||
PulseMixer *pm = new PulseMixer(po, listener, scale);
|
||||
|
||||
pulse_output_set_mixer(po, *pm);
|
||||
|
||||
@@ -191,8 +216,9 @@ PulseMixer::GetVolume()
|
||||
int
|
||||
PulseMixer::GetVolumeInternal()
|
||||
{
|
||||
pa_volume_t max_pa_volume = volume_scale_factor * PA_VOLUME_NORM;
|
||||
return online ?
|
||||
(int)((100 * (pa_cvolume_avg(&volume) + 1)) / PA_VOLUME_NORM)
|
||||
(int)((100 * (pa_cvolume_avg(&volume) + 1)) / max_pa_volume)
|
||||
: -1;
|
||||
}
|
||||
|
||||
@@ -204,9 +230,11 @@ PulseMixer::SetVolume(unsigned new_volume)
|
||||
if (!online)
|
||||
throw std::runtime_error("disconnected");
|
||||
|
||||
pa_volume_t max_pa_volume = volume_scale_factor * PA_VOLUME_NORM;
|
||||
|
||||
struct pa_cvolume cvolume;
|
||||
pa_cvolume_set(&cvolume, volume.channels,
|
||||
(new_volume * PA_VOLUME_NORM + 50) / 100);
|
||||
(new_volume * max_pa_volume + 50) / 100);
|
||||
pulse_output_set_volume(output, &cvolume);
|
||||
volume = cvolume;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "AllocatedSocketAddress.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -70,6 +71,12 @@ AllocatedSocketAddress::SetSize(size_type new_size) noexcept
|
||||
|
||||
#ifdef HAVE_UN
|
||||
|
||||
StringView
|
||||
AllocatedSocketAddress::GetLocalRaw() const noexcept
|
||||
{
|
||||
return SocketAddress(*this).GetLocalRaw();
|
||||
}
|
||||
|
||||
void
|
||||
AllocatedSocketAddress::SetLocal(const char *path) noexcept
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -27,8 +27,8 @@
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ALLOCATED_SOCKET_ADDRESS_HPP
|
||||
#define ALLOCATED_SOCKET_ADDRESS_HPP
|
||||
#ifndef ALLOCATED_SOCKET_ADDRESS_HXX
|
||||
#define ALLOCATED_SOCKET_ADDRESS_HXX
|
||||
|
||||
#include "SocketAddress.hxx"
|
||||
#include "Features.hxx"
|
||||
@@ -140,6 +140,20 @@ public:
|
||||
}
|
||||
|
||||
#ifdef HAVE_UN
|
||||
/**
|
||||
* @see SocketAddress::GetLocalRaw()
|
||||
*/
|
||||
gcc_pure
|
||||
StringView GetLocalRaw() const noexcept;
|
||||
|
||||
/**
|
||||
* @see SocketAddress::GetLocalPath()
|
||||
*/
|
||||
gcc_pure
|
||||
const char *GetLocalPath() const noexcept {
|
||||
return ((SocketAddress)*this).GetLocalPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this a "local" address (UNIX domain socket). If the path
|
||||
* begins with a '@', then the rest specifies an "abstract" local
|
||||
@@ -149,6 +163,14 @@ public:
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
bool IsV6Any() const noexcept {
|
||||
return ((SocketAddress)*this).IsV6Any();
|
||||
}
|
||||
|
||||
bool IsV4Mapped() const noexcept {
|
||||
return ((SocketAddress)*this).IsV4Mapped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the port number. Returns 0 if not applicable.
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include <string.h>
|
||||
|
||||
static inline bool
|
||||
IsValidHostnameChar(char ch)
|
||||
IsValidHostnameChar(char ch) noexcept
|
||||
{
|
||||
return IsAlphaNumericASCII(ch) ||
|
||||
ch == '-' || ch == '.' ||
|
||||
@@ -44,14 +44,14 @@ IsValidHostnameChar(char ch)
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsValidScopeChar(char ch)
|
||||
IsValidScopeChar(char ch) noexcept
|
||||
{
|
||||
return IsAlphaNumericASCII(ch) ||
|
||||
ch == '-' || ch == '_';
|
||||
}
|
||||
|
||||
static const char *
|
||||
FindScopeEnd(const char *p)
|
||||
FindScopeEnd(const char *p) noexcept
|
||||
{
|
||||
if (*p == '%' && IsValidScopeChar(p[1])) {
|
||||
p += 2;
|
||||
@@ -63,7 +63,7 @@ FindScopeEnd(const char *p)
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsValidIPv6Char(char ch)
|
||||
IsValidIPv6Char(char ch) noexcept
|
||||
{
|
||||
return IsDigitASCII(ch) ||
|
||||
(ch >= 'a' && ch <= 'f') ||
|
||||
@@ -72,7 +72,7 @@ IsValidIPv6Char(char ch)
|
||||
}
|
||||
|
||||
static const char *
|
||||
FindIPv6End(const char *p)
|
||||
FindIPv6End(const char *p) noexcept
|
||||
{
|
||||
while (IsValidIPv6Char(*p))
|
||||
++p;
|
||||
@@ -84,7 +84,7 @@ FindIPv6End(const char *p)
|
||||
}
|
||||
|
||||
ExtractHostResult
|
||||
ExtractHost(const char *src)
|
||||
ExtractHost(const char *src) noexcept
|
||||
{
|
||||
ExtractHostResult result{nullptr, src};
|
||||
const char *hostname;
|
||||
|
||||
@@ -57,7 +57,7 @@ struct ExtractHostResult {
|
||||
*/
|
||||
const char *end;
|
||||
|
||||
constexpr bool HasFailed() const {
|
||||
constexpr bool HasFailed() const noexcept {
|
||||
return host == nullptr;
|
||||
}
|
||||
};
|
||||
@@ -71,6 +71,6 @@ struct ExtractHostResult {
|
||||
*/
|
||||
gcc_pure
|
||||
ExtractHostResult
|
||||
ExtractHost(const char *src);
|
||||
ExtractHost(const char *src) noexcept;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -51,7 +51,12 @@ class IPv4Address {
|
||||
#ifdef _WIN32
|
||||
static constexpr struct in_addr ConstructInAddr(uint8_t a, uint8_t b,
|
||||
uint8_t c, uint8_t d) noexcept {
|
||||
return {{{ a, b, c, d }}};
|
||||
struct in_addr result{};
|
||||
result.s_net = a;
|
||||
result.s_host = b;
|
||||
result.s_lh = c;
|
||||
result.s_impno = d;
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -66,7 +71,7 @@ class IPv4Address {
|
||||
|
||||
static constexpr struct in_addr ConstructInAddr(uint8_t a, uint8_t b,
|
||||
uint8_t c, uint8_t d) noexcept {
|
||||
return { ConstructInAddrT(a, b, c, d) };
|
||||
return ConstructInAddrBE(ConstructInAddrT(a, b, c, d));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -158,7 +163,7 @@ public:
|
||||
*/
|
||||
static constexpr const IPv4Address &Cast(const SocketAddress &src) noexcept {
|
||||
/* this reinterpret_cast works because this class is
|
||||
just a wrapper for struct sockaddr_in6 */
|
||||
just a wrapper for struct sockaddr_in */
|
||||
return *(const IPv4Address *)(const void *)src.GetAddress();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -53,22 +53,24 @@ class IPv6Address {
|
||||
uint16_t c, uint16_t d,
|
||||
uint16_t e, uint16_t f,
|
||||
uint16_t g, uint16_t h) noexcept {
|
||||
return {{
|
||||
#ifndef __HAIKU__
|
||||
{
|
||||
#endif
|
||||
uint8_t(a >> 8), uint8_t(a),
|
||||
uint8_t(b >> 8), uint8_t(b),
|
||||
uint8_t(c >> 8), uint8_t(c),
|
||||
uint8_t(d >> 8), uint8_t(d),
|
||||
uint8_t(e >> 8), uint8_t(e),
|
||||
uint8_t(f >> 8), uint8_t(f),
|
||||
uint8_t(g >> 8), uint8_t(g),
|
||||
uint8_t(h >> 8), uint8_t(h),
|
||||
#ifndef __HAIKU__
|
||||
}
|
||||
#endif
|
||||
}};
|
||||
struct in6_addr result{};
|
||||
result.s6_addr[0] = a >> 8;
|
||||
result.s6_addr[1] = a;
|
||||
result.s6_addr[2] = b >> 8;
|
||||
result.s6_addr[3] = b;
|
||||
result.s6_addr[4] = c >> 8;
|
||||
result.s6_addr[5] = c;
|
||||
result.s6_addr[6] = d >> 8;
|
||||
result.s6_addr[7] = d;
|
||||
result.s6_addr[8] = e >> 8;
|
||||
result.s6_addr[9] = e;
|
||||
result.s6_addr[10] = f >> 8;
|
||||
result.s6_addr[11] = f;
|
||||
result.s6_addr[12] = g >> 8;
|
||||
result.s6_addr[13] = g;
|
||||
result.s6_addr[14] = h >> 8;
|
||||
result.s6_addr[15] = h;
|
||||
return result;
|
||||
}
|
||||
|
||||
static constexpr struct sockaddr_in6 Construct(struct in6_addr address,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -73,6 +73,21 @@ SocketAddress::GetLocalRaw() const noexcept
|
||||
return {path, size - header_size};
|
||||
}
|
||||
|
||||
const char *
|
||||
SocketAddress::GetLocalPath() const noexcept
|
||||
{
|
||||
const auto raw = GetLocalRaw();
|
||||
return !raw.empty() &&
|
||||
/* must be an absolute path */
|
||||
raw.front() == '/' &&
|
||||
/* must be null-terminated */
|
||||
raw.back() == 0 &&
|
||||
/* there must not be any other null byte */
|
||||
memchr(raw.data, 0, raw.size - 1) == nullptr
|
||||
? raw.data
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
* Does the object have a well-defined address? Check !IsNull()
|
||||
* before calling this method.
|
||||
*/
|
||||
bool IsDefined() const noexcept {
|
||||
constexpr bool IsDefined() const noexcept {
|
||||
return GetFamily() != AF_UNSPEC;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,13 @@ public:
|
||||
*/
|
||||
gcc_pure
|
||||
StringView GetLocalRaw() const noexcept;
|
||||
|
||||
/**
|
||||
* Returns the local socket path or nullptr if not applicable
|
||||
* (or if the path is corrupt).
|
||||
*/
|
||||
gcc_pure
|
||||
const char *GetLocalPath() const noexcept;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
|
||||
@@ -637,11 +637,8 @@ osx_output_set_device(OSXOutput *oo)
|
||||
}
|
||||
}
|
||||
if (i == numdevices) {
|
||||
FormatWarning(osx_output_domain,
|
||||
"Found no audio device with name '%s' "
|
||||
"(will use default audio device)",
|
||||
throw FormatRuntimeError("Found no audio device with name '%s' ",
|
||||
oo->device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
status = AudioUnitSetProperty(oo->au,
|
||||
|
||||
@@ -141,6 +141,27 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
|
||||
protocol = SHOUT_PROTOCOL_HTTP;
|
||||
}
|
||||
|
||||
#ifdef SHOUT_TLS
|
||||
unsigned tls;
|
||||
value = block.GetBlockValue("tls");
|
||||
if (value != nullptr) {
|
||||
if (0 == strcmp(value, "disabled"))
|
||||
tls = SHOUT_TLS_DISABLED;
|
||||
else if(0 == strcmp(value, "auto"))
|
||||
tls = SHOUT_TLS_AUTO;
|
||||
else if(0 == strcmp(value, "auto_no_plain"))
|
||||
tls = SHOUT_TLS_AUTO_NO_PLAIN;
|
||||
else if(0 == strcmp(value, "rfc2818"))
|
||||
tls = SHOUT_TLS_RFC2818;
|
||||
else if(0 == strcmp(value, "rfc2817"))
|
||||
tls = SHOUT_TLS_RFC2817;
|
||||
else
|
||||
throw FormatRuntimeError("invalid shout TLS option \"%s\"", value);
|
||||
} else {
|
||||
tls = SHOUT_TLS_DISABLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (shout_set_host(shout_conn, host) != SHOUTERR_SUCCESS ||
|
||||
shout_set_port(shout_conn, port) != SHOUTERR_SUCCESS ||
|
||||
shout_set_password(shout_conn, passwd) != SHOUTERR_SUCCESS ||
|
||||
@@ -151,6 +172,9 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
|
||||
shout_set_format(shout_conn, shout_format)
|
||||
!= SHOUTERR_SUCCESS ||
|
||||
shout_set_protocol(shout_conn, protocol) != SHOUTERR_SUCCESS ||
|
||||
#ifdef SHOUT_TLS
|
||||
shout_set_tls(shout_conn, tls) != SHOUTERR_SUCCESS ||
|
||||
#endif
|
||||
shout_set_agent(shout_conn, "MPD") != SHOUTERR_SUCCESS)
|
||||
throw std::runtime_error(shout_get_error(shout_conn));
|
||||
|
||||
|
||||
@@ -47,6 +47,17 @@ playlist_list_global_init(const ConfigData &config);
|
||||
void
|
||||
playlist_list_global_finish() noexcept;
|
||||
|
||||
class ScopePlaylistPluginsInit {
|
||||
public:
|
||||
explicit ScopePlaylistPluginsInit(const ConfigData &config) {
|
||||
playlist_list_global_init(config);
|
||||
}
|
||||
|
||||
~ScopePlaylistPluginsInit() noexcept {
|
||||
playlist_list_global_finish();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens a playlist by its URI.
|
||||
*/
|
||||
|
||||
@@ -106,7 +106,7 @@ storage_state_restore(const char *line, TextFile &file, Instance &instance)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.database)) {
|
||||
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.GetDatabase())) {
|
||||
try {
|
||||
db->Mount(uri.c_str(), url.c_str());
|
||||
} catch (...) {
|
||||
|
||||
@@ -192,7 +192,7 @@ private:
|
||||
}
|
||||
|
||||
void Disconnect() noexcept {
|
||||
assert(GetEventLoop().IsInside());
|
||||
assert(!GetEventLoop().IsAlive() || GetEventLoop().IsInside());
|
||||
|
||||
switch (state) {
|
||||
case State::INITIAL:
|
||||
|
||||
@@ -46,6 +46,8 @@ class UdisksStorage final : public Storage {
|
||||
const std::string base_uri;
|
||||
const std::string id;
|
||||
|
||||
const AllocatedPath inside_path;
|
||||
|
||||
std::string dbus_path;
|
||||
|
||||
SafeSingleton<ODBus::Glue> dbus_glue;
|
||||
@@ -64,10 +66,12 @@ class UdisksStorage final : public Storage {
|
||||
DeferEvent defer_mount, defer_unmount;
|
||||
|
||||
public:
|
||||
template<typename B, typename I>
|
||||
UdisksStorage(EventLoop &_event_loop, B &&_base_uri, I &&_id)
|
||||
template<typename B, typename I, typename IP>
|
||||
UdisksStorage(EventLoop &_event_loop, B &&_base_uri, I &&_id,
|
||||
IP &&_inside_path)
|
||||
:base_uri(std::forward<B>(_base_uri)),
|
||||
id(std::forward<I>(_id)),
|
||||
inside_path(std::forward<IP>(_inside_path)),
|
||||
dbus_glue(_event_loop),
|
||||
defer_mount(_event_loop, BIND_THIS_METHOD(DeferredMount)),
|
||||
defer_unmount(_event_loop, BIND_THIS_METHOD(DeferredUnmount)) {}
|
||||
@@ -120,6 +124,9 @@ public:
|
||||
const char *MapToRelativeUTF8(const char *uri_utf8) const noexcept override;
|
||||
|
||||
private:
|
||||
void SetMountPoint(Path mount_point);
|
||||
void LockSetMountPoint(Path mount_point);
|
||||
|
||||
void OnListReply(ODBus::Message reply) noexcept;
|
||||
|
||||
void MountWait();
|
||||
@@ -131,20 +138,52 @@ private:
|
||||
void OnUnmountNotify(ODBus::Message reply) noexcept;
|
||||
};
|
||||
|
||||
inline void
|
||||
UdisksStorage::SetMountPoint(Path mount_point)
|
||||
{
|
||||
mounted_storage = inside_path.IsNull()
|
||||
? CreateLocalStorage(mount_point)
|
||||
: CreateLocalStorage(mount_point / inside_path);
|
||||
|
||||
mount_error = {};
|
||||
want_mount = false;
|
||||
cond.broadcast();
|
||||
}
|
||||
|
||||
void
|
||||
UdisksStorage::LockSetMountPoint(Path mount_point)
|
||||
{
|
||||
const std::lock_guard<Mutex> lock(mutex);
|
||||
SetMountPoint(mount_point);
|
||||
}
|
||||
|
||||
void
|
||||
UdisksStorage::OnListReply(ODBus::Message reply) noexcept
|
||||
{
|
||||
using namespace UDisks2;
|
||||
|
||||
try {
|
||||
ParseObjects(reply, [this](Object &&o) {
|
||||
if (o.IsId(id))
|
||||
dbus_path = std::move(o.path);
|
||||
});
|
||||
std::string mount_point;
|
||||
|
||||
ParseObjects(reply, [this, &mount_point](Object &&o) {
|
||||
if (!o.IsId(id))
|
||||
return;
|
||||
|
||||
dbus_path = std::move(o.path);
|
||||
mount_point = std::move(o.mount_point);
|
||||
});
|
||||
|
||||
if (dbus_path.empty())
|
||||
throw FormatRuntimeError("No such UDisks2 object: %s",
|
||||
id.c_str());
|
||||
|
||||
if (!mount_point.empty()) {
|
||||
/* already mounted: don't attempt to mount
|
||||
again, because this would result in
|
||||
org.freedesktop.UDisks2.Error.AlreadyMounted */
|
||||
LockSetMountPoint(Path::FromFS(mount_point.c_str()));
|
||||
return;
|
||||
}
|
||||
} catch (...) {
|
||||
const std::lock_guard<Mutex> lock(mutex);
|
||||
mount_error = std::current_exception();
|
||||
@@ -222,12 +261,7 @@ try {
|
||||
throw std::runtime_error("Malformed 'Mount' response");
|
||||
|
||||
const char *mount_path = i.GetString();
|
||||
|
||||
const std::lock_guard<Mutex> lock(mutex);
|
||||
mounted_storage = CreateLocalStorage(Path::FromFS(mount_path));
|
||||
mount_error = {};
|
||||
want_mount = false;
|
||||
cond.broadcast();
|
||||
LockSetMountPoint(Path::FromFS(mount_path));
|
||||
} catch (...) {
|
||||
const std::lock_guard<Mutex> lock(mutex);
|
||||
mount_error = std::current_exception();
|
||||
@@ -297,16 +331,18 @@ UdisksStorage::MapUTF8(const char *uri_utf8) const noexcept
|
||||
{
|
||||
assert(uri_utf8 != nullptr);
|
||||
|
||||
if (StringIsEmpty(uri_utf8))
|
||||
/* kludge for a special case: return the "udisks://"
|
||||
URI if the parameter is an empty string to fix the
|
||||
mount URIs in the state file */
|
||||
return base_uri;
|
||||
|
||||
try {
|
||||
const_cast<UdisksStorage *>(this)->MountWait();
|
||||
|
||||
return mounted_storage->MapUTF8(uri_utf8);
|
||||
} catch (...) {
|
||||
/* fallback - not usable but the best we can do */
|
||||
|
||||
if (StringIsEmpty(uri_utf8))
|
||||
return base_uri;
|
||||
|
||||
return PathTraitsUTF8::Build(base_uri.c_str(), uri_utf8);
|
||||
}
|
||||
}
|
||||
@@ -333,12 +369,17 @@ CreateUdisksStorageURI(EventLoop &event_loop, const char *base_uri)
|
||||
} else {
|
||||
id = {id_begin, relative_path};
|
||||
++relative_path;
|
||||
while (*relative_path == '/')
|
||||
++relative_path;
|
||||
}
|
||||
|
||||
// TODO: use relative_path
|
||||
auto inside_path = *relative_path != 0
|
||||
? AllocatedPath::FromUTF8Throw(relative_path)
|
||||
: nullptr;
|
||||
|
||||
return std::make_unique<UdisksStorage>(event_loop, base_uri,
|
||||
std::move(id));
|
||||
std::move(id),
|
||||
std::move(inside_path));
|
||||
}
|
||||
|
||||
const StoragePlugin udisks_storage_plugin = {
|
||||
|
||||
@@ -32,9 +32,7 @@
|
||||
|
||||
#include "UniqueFileDescriptor.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct epoll_event;
|
||||
|
||||
@@ -43,6 +43,30 @@ OpenReadOnly(const char *path)
|
||||
return fd;
|
||||
}
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenWriteOnly(const char *path, int flags)
|
||||
{
|
||||
UniqueFileDescriptor fd;
|
||||
if (!fd.Open(path, O_WRONLY|flags))
|
||||
throw FormatErrno("Failed to open '%s'", path);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenDirectory(const char *path, int flags)
|
||||
{
|
||||
UniqueFileDescriptor fd;
|
||||
if (!fd.Open(path, O_DIRECTORY|O_RDONLY|flags))
|
||||
throw FormatErrno("Failed to open '%s'", path);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
UniqueFileDescriptor
|
||||
@@ -75,6 +99,16 @@ OpenReadOnly(FileDescriptor directory, const char *name, int flags)
|
||||
return fd;
|
||||
}
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenWriteOnly(FileDescriptor directory, const char *name, int flags)
|
||||
{
|
||||
UniqueFileDescriptor fd;
|
||||
if (!fd.Open(directory, name, O_WRONLY|flags))
|
||||
throw FormatErrno("Failed to open '%s'", name);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenDirectory(FileDescriptor directory, const char *name, int flags)
|
||||
{
|
||||
|
||||
@@ -36,6 +36,16 @@ class UniqueFileDescriptor;
|
||||
UniqueFileDescriptor
|
||||
OpenReadOnly(const char *path);
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenWriteOnly(const char *path, int flags=0);
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenDirectory(const char *name, int flags=0);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
UniqueFileDescriptor
|
||||
@@ -47,6 +57,9 @@ OpenPath(FileDescriptor directory, const char *name, int flags=0);
|
||||
UniqueFileDescriptor
|
||||
OpenReadOnly(FileDescriptor directory, const char *name, int flags=0);
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenWriteOnly(FileDescriptor directory, const char *name, int flags=0);
|
||||
|
||||
UniqueFileDescriptor
|
||||
OpenDirectory(FileDescriptor directory, const char *name, int flags=0);
|
||||
|
||||
|
||||
@@ -60,7 +60,8 @@ public:
|
||||
}
|
||||
|
||||
UniqueFileDescriptor &operator=(UniqueFileDescriptor &&other) noexcept {
|
||||
std::swap(fd, other.fd);
|
||||
using std::swap;
|
||||
swap(fd, other.fd);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <string.h>
|
||||
|
||||
static constexpr struct tag_table ape_tags[] = {
|
||||
{ "album artist", TAG_ALBUM_ARTIST },
|
||||
{ "year", TAG_DATE },
|
||||
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
|
||||
};
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
}
|
||||
|
||||
TagMask &operator&=(TagMask other) {
|
||||
value |= other.value;
|
||||
value &= other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
}
|
||||
|
||||
TagMask &operator^=(TagMask other) {
|
||||
value |= other.value;
|
||||
value ^= other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
static constexpr Domain signal_handlers_domain("signal_handlers");
|
||||
|
||||
static void
|
||||
HandleShutdownSignal(void *ctx)
|
||||
HandleShutdownSignal(void *ctx) noexcept
|
||||
{
|
||||
auto &loop = *(EventLoop *)ctx;
|
||||
loop.Break();
|
||||
@@ -47,7 +47,7 @@ x_sigaction(int signum, const struct sigaction *act)
|
||||
}
|
||||
|
||||
static void
|
||||
handle_reload_event(void *)
|
||||
handle_reload_event(void *) noexcept
|
||||
{
|
||||
LogDebug(signal_handlers_domain, "got SIGHUP, reopening log files");
|
||||
cycle_log_files();
|
||||
@@ -76,7 +76,7 @@ SignalHandlersInit(EventLoop &loop)
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandlersFinish()
|
||||
SignalHandlersFinish() noexcept
|
||||
{
|
||||
SignalMonitorFinish();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,17 @@ void
|
||||
SignalHandlersInit(EventLoop &loop);
|
||||
|
||||
void
|
||||
SignalHandlersFinish();
|
||||
SignalHandlersFinish() noexcept;
|
||||
|
||||
class ScopeSignalHandlersInit {
|
||||
public:
|
||||
ScopeSignalHandlersInit(EventLoop &loop) {
|
||||
SignalHandlersInit(loop);
|
||||
}
|
||||
|
||||
~ScopeSignalHandlersInit() noexcept {
|
||||
SignalHandlersFinish();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,28 +36,28 @@
|
||||
#include <stddef.h>
|
||||
|
||||
template<typename T, typename U>
|
||||
static inline constexpr T *
|
||||
constexpr T *
|
||||
OffsetCast(U *p, ptrdiff_t offset)
|
||||
{
|
||||
return reinterpret_cast<T *>(OffsetPointer(p, offset));
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static inline constexpr T *
|
||||
constexpr T *
|
||||
OffsetCast(const U *p, ptrdiff_t offset)
|
||||
{
|
||||
return reinterpret_cast<const T *>(OffsetPointer(p, offset));
|
||||
}
|
||||
|
||||
template<class C, class A>
|
||||
static constexpr inline ptrdiff_t
|
||||
constexpr ptrdiff_t
|
||||
ContainerAttributeOffset(const C *null_c, const A C::*p)
|
||||
{
|
||||
return ptrdiff_t((const char *)&(null_c->*p) - (const char *)null_c);
|
||||
}
|
||||
|
||||
template<class C, class A>
|
||||
static constexpr inline ptrdiff_t
|
||||
constexpr ptrdiff_t
|
||||
ContainerAttributeOffset(const A C::*p)
|
||||
{
|
||||
return ContainerAttributeOffset<C, A>(nullptr, p);
|
||||
@@ -67,7 +67,7 @@ ContainerAttributeOffset(const A C::*p)
|
||||
* Cast the given pointer to a struct member to its parent structure.
|
||||
*/
|
||||
template<class C, class A>
|
||||
static inline constexpr C &
|
||||
constexpr C &
|
||||
ContainerCast(A &a, const A C::*member)
|
||||
{
|
||||
return *OffsetCast<C, A>(&a, -ContainerAttributeOffset<C, A>(member));
|
||||
@@ -77,7 +77,7 @@ ContainerCast(A &a, const A C::*member)
|
||||
* Cast the given pointer to a struct member to its parent structure.
|
||||
*/
|
||||
template<class C, class A>
|
||||
static inline constexpr const C &
|
||||
constexpr const C &
|
||||
ContainerCast(const A &a, const A C::*member)
|
||||
{
|
||||
return *OffsetCast<const C, const A>(&a, -ContainerAttributeOffset<C, A>(member));
|
||||
|
||||
@@ -34,29 +34,25 @@
|
||||
#include "WCharUtil.hxx"
|
||||
#endif
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsASCII(const unsigned char ch)
|
||||
{
|
||||
return ch < 0x80;
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsASCII(const char ch)
|
||||
{
|
||||
return IsASCII((unsigned char)ch);
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsWhitespaceOrNull(const char ch)
|
||||
{
|
||||
return (unsigned char)ch <= 0x20;
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsWhitespaceNotNull(const char ch)
|
||||
{
|
||||
return ch > 0 && ch <= 0x20;
|
||||
@@ -68,50 +64,43 @@ IsWhitespaceNotNull(const char ch)
|
||||
* want the fastest implementation, and you don't care if a null byte
|
||||
* matches.
|
||||
*/
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsWhitespaceFast(const char ch)
|
||||
{
|
||||
return IsWhitespaceOrNull(ch);
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsPrintableASCII(char ch)
|
||||
{
|
||||
return (signed char)ch >= 0x20;
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsDigitASCII(char ch)
|
||||
{
|
||||
return ch >= '0' && ch <= '9';
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsUpperAlphaASCII(char ch)
|
||||
{
|
||||
return ch >= 'A' && ch <= 'Z';
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsLowerAlphaASCII(char ch)
|
||||
{
|
||||
return ch >= 'a' && ch <= 'z';
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsAlphaASCII(char ch)
|
||||
{
|
||||
return IsUpperAlphaASCII(ch) || IsLowerAlphaASCII(ch);
|
||||
}
|
||||
|
||||
constexpr
|
||||
static inline bool
|
||||
constexpr bool
|
||||
IsAlphaNumericASCII(char ch)
|
||||
{
|
||||
return IsAlphaASCII(ch) || IsDigitASCII(ch);
|
||||
@@ -121,8 +110,7 @@ IsAlphaNumericASCII(char ch)
|
||||
* Convert the specified ASCII character (0x00..0x7f) to upper case.
|
||||
* Unlike toupper(), it ignores the system locale.
|
||||
*/
|
||||
constexpr
|
||||
static inline char
|
||||
constexpr char
|
||||
ToUpperASCII(char ch)
|
||||
{
|
||||
return ch >= 'a' && ch <= 'z'
|
||||
@@ -134,8 +122,7 @@ ToUpperASCII(char ch)
|
||||
* Convert the specified ASCII character (0x00..0x7f) to lower case.
|
||||
* Unlike tolower(), it ignores the system locale.
|
||||
*/
|
||||
constexpr
|
||||
static inline char
|
||||
constexpr char
|
||||
ToLowerASCII(char ch)
|
||||
{
|
||||
return ch >= 'A' && ch <= 'Z'
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
/**
|
||||
* Offset the given pointer by the specified number of bytes.
|
||||
*/
|
||||
static inline constexpr void *
|
||||
constexpr void *
|
||||
OffsetPointer(void *p, ptrdiff_t offset)
|
||||
{
|
||||
return (char *)p + offset;
|
||||
@@ -43,7 +43,7 @@ OffsetPointer(void *p, ptrdiff_t offset)
|
||||
/**
|
||||
* Offset the given pointer by the specified number of bytes.
|
||||
*/
|
||||
static inline constexpr const void *
|
||||
constexpr const void *
|
||||
OffsetPointer(const void *p, ptrdiff_t offset)
|
||||
{
|
||||
return (const char *)p + offset;
|
||||
|
||||
@@ -42,6 +42,8 @@ PrintException(const std::exception &e) noexcept
|
||||
std::rethrow_if_nested(e);
|
||||
} catch (const std::exception &nested) {
|
||||
PrintException(nested);
|
||||
} catch (const char *s) {
|
||||
fprintf(stderr, "%s\n", s);
|
||||
} catch (...) {
|
||||
fprintf(stderr, "Unrecognized nested exception\n");
|
||||
}
|
||||
@@ -54,6 +56,8 @@ PrintException(const std::exception_ptr &ep) noexcept
|
||||
std::rethrow_exception(ep);
|
||||
} catch (const std::exception &e) {
|
||||
PrintException(e);
|
||||
} catch (const char *s) {
|
||||
fprintf(stderr, "%s\n", s);
|
||||
} catch (...) {
|
||||
fprintf(stderr, "Unrecognized exception\n");
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ struct BasicStringView : ConstBuffer<T> {
|
||||
using ConstBuffer<T>::back;
|
||||
using ConstBuffer<T>::pop_front;
|
||||
using ConstBuffer<T>::pop_back;
|
||||
using ConstBuffer<T>::skip_front;
|
||||
|
||||
gcc_pure
|
||||
pointer_type Find(value_type ch) const noexcept {
|
||||
@@ -114,6 +115,20 @@ struct BasicStringView : ConstBuffer<T> {
|
||||
StripLeft();
|
||||
StripRight();
|
||||
}
|
||||
|
||||
bool SkipPrefix(BasicStringView<T> needle) noexcept {
|
||||
bool match = StartsWith(needle);
|
||||
if (match)
|
||||
skip_front(needle.size);
|
||||
return match;
|
||||
}
|
||||
|
||||
bool RemoveSuffix(BasicStringView<T> needle) noexcept {
|
||||
bool match = EndsWith(needle);
|
||||
if (match)
|
||||
size -= needle.size;
|
||||
return match;
|
||||
}
|
||||
};
|
||||
|
||||
struct StringView : BasicStringView<char> {
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
#include "StringView.hxx"
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
struct WStringView : BasicStringView<wchar_t> {
|
||||
using BasicStringView::BasicStringView;
|
||||
|
||||
|
||||
@@ -30,10 +30,14 @@ if zeroconf_option == 'bonjour'
|
||||
if not compiler.has_header('dns_sd.h')
|
||||
error('dns_sd.h not found')
|
||||
endif
|
||||
|
||||
bonjour_dep = declare_dependency(link_args: ['-framework', 'dnssd'])
|
||||
|
||||
if is_darwin
|
||||
bonjour_dep = declare_dependency(link_args: ['-framework', 'dnssd'])
|
||||
else
|
||||
bonjour_dep = declare_dependency(link_args: ['-ldns_sd'])
|
||||
endif
|
||||
conf.set('HAVE_BONJOUR', true)
|
||||
|
||||
|
||||
zeroconf = static_library(
|
||||
'zeroconf_bonjour',
|
||||
'ZeroconfGlue.cxx',
|
||||
|
||||
41
test/ConfigGlue.hxx
Normal file
41
test/ConfigGlue.hxx
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_TEST_CONFIG_GLUE_HXX
|
||||
#define MPD_TEST_CONFIG_GLUE_HXX
|
||||
|
||||
#include "config/File.hxx"
|
||||
#include "config/Migrate.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
|
||||
inline ConfigData
|
||||
AutoLoadConfigFile(Path path)
|
||||
{
|
||||
ConfigData data;
|
||||
|
||||
if (!path.IsNull()) {
|
||||
ReadConfigFile(data, path);
|
||||
Migrate(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -65,7 +65,7 @@ try {
|
||||
|
||||
const Path path = Path::FromFS(argv[1]);
|
||||
|
||||
decoder_plugin_init_all(ConfigData());
|
||||
const ScopeDecoderPluginsInit decoder_plugins_init({});
|
||||
|
||||
const auto *plugin = FindContainerDecoderPlugin(path);
|
||||
if (plugin == nullptr) {
|
||||
@@ -87,8 +87,6 @@ try {
|
||||
|
||||
bos.Flush();
|
||||
|
||||
decoder_plugin_deinit_all();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
} catch (...) {
|
||||
PrintException(std::current_exception());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -26,11 +26,7 @@
|
||||
#include "db/LightDirectory.hxx"
|
||||
#include "song/LightSong.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "config/File.hxx"
|
||||
#include "config/Migrate.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "config/Param.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "ConfigGlue.hxx"
|
||||
#include "tag/Config.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "event/Thread.hxx"
|
||||
@@ -124,9 +120,7 @@ try {
|
||||
|
||||
GlobalInit init;
|
||||
|
||||
ConfigData config;
|
||||
ReadConfigFile(config, config_path);
|
||||
Migrate(config);
|
||||
const auto config = AutoLoadConfigFile(config_path);
|
||||
|
||||
TagLoadConfig(config);
|
||||
|
||||
@@ -139,15 +133,13 @@ try {
|
||||
if (path != nullptr)
|
||||
block.AddBlockParam("path", path->value, path->line);
|
||||
|
||||
Database *db = plugin->create(init.GetEventLoop(),
|
||||
init.GetEventLoop(),
|
||||
database_listener, block);
|
||||
|
||||
AtScopeExit(db) { delete db; };
|
||||
auto db = plugin->create(init.GetEventLoop(),
|
||||
init.GetEventLoop(),
|
||||
database_listener, block);
|
||||
|
||||
db->Open();
|
||||
|
||||
AtScopeExit(db) { db->Close(); };
|
||||
AtScopeExit(&db) { db->Close(); };
|
||||
|
||||
const DatabaseSelection selection("", true);
|
||||
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config/File.hxx"
|
||||
#include "config/Migrate.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "ConfigGlue.hxx"
|
||||
#include "tag/Chromaprint.hxx"
|
||||
#include "pcm/PcmConvert.hxx"
|
||||
#include "event/Thread.hxx"
|
||||
@@ -90,31 +88,21 @@ ParseCommandLine(int argc, char **argv)
|
||||
}
|
||||
|
||||
class GlobalInit {
|
||||
ConfigData config;
|
||||
const ConfigData config;
|
||||
EventThread io_thread;
|
||||
const ScopeInputPluginsInit input_plugins_init;
|
||||
const ScopeDecoderPluginsInit decoder_plugins_init;
|
||||
|
||||
public:
|
||||
GlobalInit(Path config_path, bool verbose) {
|
||||
SetLogThreshold(verbose ? LogLevel::DEBUG : LogLevel::INFO);
|
||||
|
||||
if (!config_path.IsNull()) {
|
||||
ReadConfigFile(config, config_path);
|
||||
Migrate(config);
|
||||
}
|
||||
|
||||
explicit GlobalInit(Path config_path)
|
||||
:config(AutoLoadConfigFile(config_path)),
|
||||
input_plugins_init(config, io_thread.GetEventLoop()),
|
||||
decoder_plugins_init(config)
|
||||
{
|
||||
io_thread.Start();
|
||||
|
||||
input_stream_global_init(config,
|
||||
io_thread.GetEventLoop());
|
||||
decoder_plugin_init_all(config);
|
||||
|
||||
pcm_convert_global_init(config);
|
||||
}
|
||||
|
||||
~GlobalInit() {
|
||||
decoder_plugin_deinit_all();
|
||||
input_stream_global_finish();
|
||||
}
|
||||
};
|
||||
|
||||
class ChromaprintDecoderClient final : public DecoderClient {
|
||||
@@ -245,7 +233,8 @@ int main(int argc, char **argv)
|
||||
try {
|
||||
const auto c = ParseCommandLine(argc, argv);
|
||||
|
||||
const GlobalInit init(c.config_path, c.verbose);
|
||||
SetLogThreshold(c.verbose ? LogLevel::DEBUG : LogLevel::INFO);
|
||||
const GlobalInit init(c.config_path);
|
||||
|
||||
const DecoderPlugin *plugin = decoder_plugin_from_name(c.decoder);
|
||||
if (plugin == nullptr) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user